日時月ごとのパンダデータフレームグループ


98

csvファイルについて考えてみます。

string,date,number
a string,2/5/11 9:16am,1.0
a string,3/5/11 10:44pm,2.0
a string,4/22/11 12:07pm,3.0
a string,4/22/11 12:10pm,4.0
a string,4/29/11 11:59am,1.0
a string,5/2/11 1:41pm,2.0
a string,5/2/11 2:02pm,3.0
a string,5/2/11 2:56pm,4.0
a string,5/2/11 3:00pm,5.0
a string,5/2/14 3:02pm,6.0
a string,5/2/14 3:18pm,7.0

これを読み込んで、日付列を日時形式に再フォーマットできます。

b=pd.read_csv('b.dat')
b['date']=pd.to_datetime(b['date'],format='%m/%d/%y %I:%M%p')

私は月ごとにデータをグループ化しようとしています。月にアクセスし、それによってグループ化する明白な方法があるはずのようです。しかし、私はそれをすることができないようです。誰かが方法を知っていますか?

私が現在試しているのは、日付によるインデックスの再作成です。

b.index=b['date']

私は次のように月にアクセスできます:

b.index.month

ただ、月ごとにまとめる機能が見つからないようです。


回答のいずれかを適用するのに苦労している場合は、この質問(したがって回答)では、日時値がデータフレームのインデックスに割り当てられていることに注意してください。簡単なヒント/リマインダーは次のようになります。日時列がある場合は、次のようにするだけで、実際には1つのYeay / Month / Day / Hour / Minute値にアクセスできますmy_df.my_column.dt.month
FedericoDorato20年

回答:


179

なんとかやった:

b = pd.read_csv('b.dat')
b.index = pd.to_datetime(b['date'],format='%m/%d/%y %I:%M%p')
b.groupby(by=[b.index.month, b.index.year])

または

b.groupby(pd.Grouper(freq='M'))  # update for v0.21+

54
よりパンドニックな方法は、resample(必要な機能を提供する場合に)使用するか、TimeGrouperdf.groupby(pd.TimeGrouper(freq='M'))
Karl D.

10
結果のDataFrameの合計または平均を取得するには、df.groupby(pd.TimeGrouper(freq='M')).sum()またはdf.groupby(pd.TimeGrouper(freq='M')).mean()
Alexandre

9
pd.TimeGrouperは非推奨になりましたpd.Grouper。これはもう少し柔軟性がfreqありlevelますが、それでも引数を取ります。
BallpointBen 2018

最初の方法は機能していないようです。を介して作成されたシリーズの「シリーズオブジェクトには属性「月」がありません」というエラーが表示されto_datetimeます。
エリー

1
@ely答えbは、CSVから読み取られた後にインデックスが与えられる元の質問の行に暗黙的に依存しています。b.index = pd.to_datetime(b['date'],format='%m/%d/%y %I:%M%p')行の後に追加しますb = pd.read_csv('b.dat')。[私も今答えを編集しました。]
goodside 2010年

77

(更新:2018)

pd.Timegrouperは減価償却され、削除されることに注意してください。代わりに使用してください:

 df.groupby(pd.Grouper(freq='M'))

2
Grouperのドキュメントはこちら、周波数の仕様(freq=...)はこちらをご覧ください。いくつかの例はあるfreq=Dのための日freq=Bのための営業日freq=Wのために数週間あるいはfreq=Q宿舎
キム

3
次のように、dfのインデックスを再作成する必要がないように、「key」を使用すると便利です。df.groupby(pd.Grouper(key = 'your_date_column'、freq = 'M'))
Edward

14

MultiIndexを回避する1つの解決策は、datetimeday = 1を設定する新しい列を作成することです。次にこの列でグループ化します。

曜日を正規化する

df = pd.DataFrame({'Date': pd.to_datetime(['2017-10-05', '2017-10-20', '2017-10-01', '2017-09-01']),
                   'Values': [5, 10, 15, 20]})

# normalize day to beginning of month, 4 alternative methods below
df['YearMonth'] = df['Date'] + pd.offsets.MonthEnd(-1) + pd.offsets.Day(1)
df['YearMonth'] = df['Date'] - pd.to_timedelta(df['Date'].dt.day-1, unit='D')
df['YearMonth'] = df['Date'].map(lambda dt: dt.replace(day=1))
df['YearMonth'] = df['Date'].dt.normalize().map(pd.tseries.offsets.MonthBegin().rollback)

次にgroupby、通常どおりに使用します。

g = df.groupby('YearMonth')

res = g['Values'].sum()

# YearMonth
# 2017-09-01    20
# 2017-10-01    30
# Name: Values, dtype: int64

比較 pd.Grouper

このソリューションの微妙な利点は、とは異なりpd.Grouper、ハタのインデックスが月末ではなく毎月の初めに正規化されることです。したがって、次の方法でグループを簡単に抽出できますget_group

some_group = g.get_group('2017-10-01')

10月の最終日を計算するのは少し面倒です。pd.Grouper、v0.23以降、conventionパラメータをサポートしますが、これはPeriodIndexハタにのみ適用されます。

文字列変換との比較

上記のアイデアの代わりに、文字列に変換する2017-10-XXこともできます'2017-10'。たとえば、日時を文字列に変換します。ただし、一連の文字列(ポインタの配列として格納されるdatetime)とobject一連の文字列(連続したメモリブロックに数値データとして内部的に格納される)の効率上の利点がすべて失われるため、これはお勧めしません。


すでにday = 1の値がある場合にオフセットを利用する適切な方法については、この回答を参照してください:stackoverflow.com/a/45831333/9987623
AlexK

@AlexK、よりもpd.tseries.offsets利点がありpd.tseries.MonthBeginますか?
JPP

申し訳ありませんが、それらを区別するのに十分なことはわかりません。df['YearMonth'] = df['Date'] - pd.offsets.MonthBegin(1)上記のコードは、すでに月の最初の日付を前の月の最初に変更するため、コメントを追加しました。
AlexK

@AlexK、グッドスポット、それに応じて回答を更新しました。
JPP

8

@jppのやや代替の解決策ですが、YearMonth文字列を出力します。

df['YearMonth'] = pd.to_datetime(df['Date']).apply(lambda x: '{year}-{month}'.format(year=x.year, month=x.month))

res = df.groupby('YearMonth')['Values'].sum()
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.