36
37

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Pandasの日付が縦に並んでいるデータフレームで、欠けている日付の行を補う方法と気をつけること

Last updated at Posted at 2018-02-14

解決するのに少し手間取ったのでメモ

環境設定

import pandas as pd
from datetime import date, timedelta

PCはmacです。

何をやりたいのか

日付が並んでいるデータフレームに対し、欠けている日付を補います。
例えば、2/14から2/18の売上データがあるとします。

tmp = pd.DataFrame(
    {"売上個数": [2, 3, 1]},
    index=[pd.to_datetime("2018-2-14"), pd.to_datetime("2018-2-16"), pd.to_datetime("2018-2-17")])
tmp
スクリーンショット 2018-02-15 0.05.30.png

このデータフレームでは2/15と2/18のデータが欠けています(2/14-2/18の売上データなので)。
売上個数が欠測した理由はさておき、2/15と2/18に売上個数NAの行を追加したい場合、どのようにすれば良いでしょうか。

どうすればできるのか

補完したい期間の日付を入れたデータフレームを作ってマージしましょう。
日付を入れる場所はindexでも列でも大丈夫です。

実際にやってみます。

'''
補完したい日付のデータフレームを作る
'''
# indexに日付を入れる場合
dates_DF = pd.DataFrame(index=[pd.to_datetime("2018-2-14") + timedelta(days=i) for i in range(5)])
# okadateさんのコメントを反映、こちらの方が綺麗
dates_DF = pd.DataFrame(index=pd.date_range('2018-2-14', periods=5, freq='D'))

# 列に日付を入れる場合
dates_DF2 = pd.DataFrame({"日付": [pd.to_datetime("2018-2-14") + timedelta(days=i) for i in range(5)]})

'''
マージ
'''
# index同士を結合させる場合
tmp.merge(dates_DF, how="outer", left_index=True, right_index=True)

# indexと列を結合させる場合
tmp.merge(dates_DF2, how="outer", left_index=True, right_on="日付")
スクリーンショット 2018-02-15 0.25.09.png

2/15と2/18の行を補うことが出来ました。
indexしかない空の日付データフレームを作るのがポイントです。
(列に日付を入れたデータフレームを作っても結果は変わりません)

追記

欠測している日付がデータの間である場合はpd.resampleで補完が出来るそうです。

# resampleは時系列データの解像度(頻度)を変更する関数
# データの圧縮などに使えるが、この場合は欠けている日付を補う事も出来る
tmp.resample('D').mean()

スクリーンショット 2018-02-15 12.25.48.png

気をつけること

pandas.to_datetime()はdatetimeではなくTimestampを返すことに注意しましょう。
indexでマージする場合は問題ありませんが、列でマージする場合、Timestampとdatetime.dateをマージするとTimestampが日付からUNIX時間(1970年1月1日 午前0時0分0秒からの経過秒数)になったりエラーを吐いたりします。
特に理由がなければマージはindexで行うか、列で行う場合は元の日付の型がTimestampなのかdatetime.dateなのか確認して、それに合わせた型を準備しましょう。

まず、型を確認しましょう。

type(pd.to_datetime("2018-2-14")) # => pandas._libs.tslib.Timestamp
type(date(2018,2,14)) # => datetime.date

pd.to_datetime()はTimestamp、date()はdatetime.dateであることがわかります。

次に、datetime.dateとTimestampでマージをしてみましょう。

'''
前処理
'''
# datetime.dateの売上データを作る
tmp_dt = pd.DataFrame(
    {"売上個数": [2, 3, 1]},
    index=[date(2018, 2, 14), date(2018, 2, 16), date(2018, 2, 17)])

# datetime.dateの日付データを作る
# timedeltaはTimestampに対してもdatetime.dateに対しても計算出来る
dates_DF_dt = pd.DataFrame(index=[date(2018, 2, 14) + timedelta(days=i) for i in range(5)])

# Timestamp型であることを明示する
tmp_ts = tmp
dates_DF_ts = dates_DF_ts

売上:Timestamp, 日付: datetime.dateの場合

# indexでマージ
tmp_ts.merge(dates_DF_dt, how="outer", left_index=True, right_index=True) # 問題ない

# 列でマージ
# reset_index()でindexにある日付を列に持ってこられる
# その場合列名は"index"となる
tmp_ts.reset_index().merge(dates_DF_dt.reset_index(), how="outer", on="index") # エラー

indexでマージをする場合は問題ありませんが、列でマージをするとエラーを吐きます。

売上: datetime.date, 日付: Timestampの場合

# indexでマージ
tmp_dt.merge(dates_DF_ts, how="outer", left_index=True, right_index=True) # 問題ない

# 列でマージ
tmp_dt.reset_index().merge(dates_DF_ts.reset_index(), how="outer", on="index") # エラーは吐かないが上手くマージ出来ない

indexでマージする場合は問題ありませんが、列でマージした場合出力は以下のようになります。

スクリーンショット 2018-02-15 1.03.24.png

日付データフレームのTimestampがUNIX時刻(int)になっていることがわかります。

まとめ

  • 日付が縦に並んでいるデータフレームに対し欠けている日付の行を補いたいときは、日付のデータフレームを作ってマージする
  • resampledate_rangeを使った方が綺麗
  • pandas.to_datetime()はTimestampを返すので注意
  • マージはindexで行った方が安全
36
37
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
36
37

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?