質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.62%

使いにくい時系列データをスマートに処理したい

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 870

musuka

score 10

質問は①~④です。
ゴチャゴチャしていて読みづらく申し訳ございませんが、
意見、回答、いただけると幸いです。

# 実行待ちのタスクが直近1カ月でどれくらい予約されてるかの辞書です。
# 時と分が逆になってます。
# 曜日や日を表す数字が最後についたりつかなかったりします。
# period 1=日次, 2=週次, 3=月次
# time mm:hh:(曜日dow|日day)
# 曜日(1:日曜-7:土曜)|日(1-31)]。
lis = [
{'period': 2  'time': '00:01:7'} # 毎週土曜の01:00に実行
{'period': 1  'time': '10:07'} # 毎日07:10に実行
{'period': 2  'time': '00:01:2'} # 毎週月曜の01:00に実行
{'period': 1  'time': '00:03'} # 毎日の03:00に実行
{'period': 1  'time': '00:02'}
{'period': 1  'time': '00:01'}
{'period': 2  'time': '00:01:1'}
{'period': 3  'time': '00:01:1'}
{'period': 1  'time': '00:02'}
{'period': 2  'time': '00:03:1'}
{'period': 3  'time': '00:01:1'}
{'period': 1  'time': '00:04'}
{'period': 1  'time': '30:02'}
{'period': 1  'time': '00:02'}
{'period': 3  'time': '00:03:20'} # 毎月20日の03:00に実行
] 
lis2 = []
# mm:hhをhh:mmに変更したlis2を再作成
for row in lis:
    sp = row['time'].split(':') # 00:01 -> ['00', '01']
    time = "{}:{}".format(sp[1].zfill(2), sp[0].zfill(2))
    row['time'] = time
    lis2.append(row)

# periodごとにカウントするための辞書を用意
daily = {}
weekly = {}
monthly = {}

# periodごとのtimeごとに件数を数える
for row in lis2:
    if row['period'] == 1:
        daily.setdefault(row['time'], 0)
        daily[row['time']] += 1
pprint(daily)
"""
 '01:02': 1,
 '01:05': 2,
 '01:10': 1,
 '01:14': 1, ...
"""
df = pd.DataFrame(list(daily.items()))
df.plot(kind='area', stacked=True, alpha=0.4)
plt.show()
plt.savefig("image.png")

上記のような使いにくい時系列データがあります。

このデータを加工し、periodごとのtimeごとに件数をカウントし、
pandasで横軸:time, 縦軸:件数のグラフを書きたいのですが、
スマートな方法はありますか。

現在はlis[x]['time']をmm:hhからhh:mmに変更し、
件数を数えるための辞書を3種類用意し、
ループして数えてます。

①hh:mmに変更せずとも、datetimeのstrtimeやら
pandasのなんやらで、上手いことできるんじゃなかろうか。

②mm:hh→hh:mmにする処理(splitで実装)、
もっと読みやすくて短い書き方はないのか。

③件数数える部分の処理(ループでインクリメントして実装)、
countメソッド?collections.Counter関数?その他?で上手くできるんじゃないか。

④件数を数えるなら、辞書かリストのどちらがいいか。
pandasで使うなら、リストの方がなんとなく使いやすそう。

⑤直近1カ月のグラフにするとして、weeklyとmonthlyの時系列データは同生成するか。
仮にtimeを'2018 12-07 02:30'のようなフォーマットにするとして、
weekly直近の4週間(4件分の辞書)やmonthly(1件分の辞書)は、
時間の加算をどうやって実装し、生成するのか(datetime,pandasで上手くできないか?)

-============================================
↓↓↓追記(2018_1203_0123)↓↓↓
-============================================
追記①
⑤の週次、月次グラフのイメージ画像を追加しました。
(画像では11-21から出てしまってますが)
現在から直近30日のperiodごとのtimeごとの推移を見たいです。

追記②

{'period': 2  'time': '00:01:2'} # WEEKLY 毎週月曜の01:00に実行

また、WEEKLYの分がまた厄介で可能なら落としたくないと考えています。
つまり、最後の:2を落として、
現在から4回先までのtimeデータを
4回 lis.append(データ)する?必要があります。
(12-03 01:00, 12-10 01:00, 12-17 01:00, 12-24 01:00)

追記③
groupbyの使い方がいまいちぴんと来てなくて、
groupbyの関連でvalue_countsを知ったので、一旦実装してみました。
MySQLでいうところの、
SELECT period, time, COUNT(*) FROM lis GROUP BY period, time ;
的な結果を得たいと考えています。

df = pd.DataFrame({'time': [row['time'] for row in lis]})
# >>> df[:]
#      time
# 0    2018-12-31 21:00:00
# 1    2018-12-07 20:30:00
# 2    2018-12-02 12:10:00
#pprint(df, width=273)

df_count = df['time'].value_counts()
# 2018-12-02 16:00:00    74
# 2018-12-03 06:10:00    40
# 2018-12-03 04:10:00    39
#pprint(df_count)

df = pd.DataFrame({'time': list(df_count.index.values), 'num': list(df_count.values)})
#    time                  num
#0   2018-12-03 01:00:00   74
#1   2018-12-03 15:10:00   40
#2   2018-12-03 04:00:00   39
#pprint(df)

↓月次のイメージ↓
月次のイメージ

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • can110

    2018/12/02 05:32

    ⑤の週次、月次グラフのイメージが不明です。手書きでもよいので図示ください。

    キャンセル

回答 1

+1

⑤週次、月次グラフイメージが不明ですが、おそらくこのようなものだろうとアタリをつけて作成してみました。
①②についてはスマートな方法はないと思います。
③④についてはpandasのgroupbyにまかせると楽できます。

import pandas as pd
import matplotlib.pyplot as plt

lis = [
    {'period': 2 , 'time': '00:01:7'}, # 毎週土曜の01:00に実行
    {'period': 1 , 'time': '10:07'}, # 毎日07:10に実行
    {'period': 2 , 'time': '00:01:2'}, # 毎週月曜の01:00に実行
    {'period': 1 , 'time': '00:03'}, # 毎日の03:00に実行
    {'period': 1 , 'time': '00:02'},
    {'period': 1 , 'time': '00:01'},
    {'period': 2 , 'time': '00:01:1'},
    {'period': 3 , 'time': '00:01:1'},
    {'period': 1 , 'time': '00:02'},
    {'period': 2 , 'time': '00:03:1'},
    {'period': 3 , 'time': '00:01:1'},
    {'period': 1 , 'time': '00:04'},
    {'period': 1 , 'time': '30:02'},
    {'period': 1 , 'time': '00:02'},
    {'period': 3 , 'time': '00:03:20'} # 毎月20日の03:00に実行
]

# time列を日時に変換
def toDT(row):
    src = row['time'].split(':')

    # 何日目かを特定
    day = 1
    if row['period'] == 2:
        day = int(src[2]) # 曜日の情報は抜け落ちるが不要だろう
    elif row['period'] == 3:
        day = int(src[2])

    # 2018年1月基準の日時に変換
    from datetime import datetime
    dt = datetime(2018,1,day,int(src[1]),int(src[0]))
    row['time'] = dt

    return row

df = pd.DataFrame( {'period':[v['period'] for v in lis], 'time':[v['time'] for v in lis]})
df = df.apply(toDT,axis=1) # time列を日時に変換
print(df)

for per in [1,2,3]: # 日、週、月次
    grp = df[df['period'] == per].groupby('time')
    df2 = grp.count().reset_index()

    df2.plot(kind='area', stacked=True, alpha=0.4,x='time') # x軸はtimeとする
    plt.savefig('{}.png'.format(per))
    plt.show()


イメージ説明
イメージ説明
イメージ説明

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/12/03 01:39

    回答ありがとうございます。
    > ①②についてはスマートな方法はないと思います。
    残念ですが、splitで実装してみます。

    > ③④についてはpandasのgroupbyにまかせると楽できます。
    groupbyの使い方がイマイチ理解できていません。
    pandas3日くらい勉強してみましたが、全然理解が進まず、
    良さげな参考書でも買って読んでみようかと思います。

    > ⑤の週次、月次グラフのイメージ
    イメージの画像を添付しました。
    まだ、日、週、月次のデータをどう可視化するかまとまってないですが、
    グラフを2個(あるいは3個?)用意して、
    グラフ1はDAILY(period: [1])を
    X軸: 00:00-23:59の時系列を1時間単位で
    Y軸: (period=[1]の)timeごとの件数
    グラフ2はMONTHKY(period: [1, 2, 3]を
    X軸: 現在から直近1カ月の時系列を1時間単位で
    Y軸: (period=[1, 2, 3]の)timeごとの件数を
    表示しようと考えてます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.62%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る