本文を読み飛ばす

pandas での時系列データ処理に関するメモ

はじめに

Python で時系列データを扱う場合、pandas を使うと良い。 ある程度は NumPy でも扱えるけれど、総合的にみて少々機能不足の印象がぬぐえない。 NumPy を使えて pandas を使えない状況は滅多に無いと思うので、 基本的に Python では「時系列= pandas」ぐらいの感覚で良いのではないかと思う。

随時更新。思い付いたり教わったり、実際に使ったタイミングで書き出していく。

日時文字列の解釈

  • とにかく pandas.to_datetime() が便利

    • >>> import pandas as pd
      >>> pd.to_datetime([
      ...     # 色々な表記法に対応している
      ...     "2000-03-01",
      ...     "3/1/2000",
      ...     "May 10, 2000",
      ...
      ...     # 月以下が不足していても補完してくれる
      ...     "2000",
      ...     "May, 2000",
      ...     "2000/3",
      ...     "2/2000",
      ... ])
      DatetimeIndex(['2000-03-01', '2000-03-01', '2000-05-10', '2000-01-01',
                      '2000-05-01', '2000-03-01', '2000-02-01'],
                      dtype='datetime64[ns]', freq=None)
      
  • CSV ファイルのロードと同時に日時文字列を解釈したければ pandas.read_csv() の引数 parse_dates を使う

    • 特定の文字列値を欠損値と扱いたければ引数 na_values に指定する
    • >>> import io
      >>> import pandas as pd
      >>>
      >>> csv_data = """
      ... Branch,Schedule,Status,First release,End-of-life,Release manager
      ... master,PEP 619,features,2021-10-04,TBD,Pablo Galindo Salgado
      ... 3.9,PEP 596,bugfix,2020-10-05,TBD,Łukasz Langa
      ... 3.8,PEP 569,bugfix,2019-10-14,2024-10,Łukasz Langa
      ... 3.7,PEP 537,security,2018-06-27,2023-06-27,Ned Deily
      ... 3.6,PEP 494,security,2016-12-23,2021-12-23,Ned Deily
      ... """  # https://devguide.python.org/#status-of-python-branches
      >>>
      >>> with io.StringIO(csv_data) as f:
      ...     df = pd.read_csv(f,
      ...                      parse_dates=["First release", "End-of-life"],
      ...                      na_values={"End-of-life": "TBD"})
      ...
      >>> df
          Branch Schedule    Status First release End-of-life        Release manager
      0  master  PEP 619  features    2021-10-04         NaT  Pablo Galindo Salgado
      1     3.9  PEP 596    bugfix    2020-10-05         NaT           Łukasz Langa
      2     3.8  PEP 569    bugfix    2019-10-14  2024-10-01           Łukasz Langa
      3     3.7  PEP 537  security    2018-06-27  2023-06-27              Ned Deily
      4     3.6  PEP 494  security    2016-12-23  2021-12-23              Ned Deily
      

日時の配列を生成する

  • pandas.date_range() を使う

    • 生成される日時データの間隔は引数 freqOffset Alias を与えて指定する
    • 生成方法は引数 startendperiods の組み合わせで柔軟に指定できる

      引数の組み合わせ 生成される日時の配列
      start + end start 以後 end 以前の日時を生成
      start + periods start から periods 個の日時を生成
      end + periods end が最後となる、periods 個の日時を生成
      start + end + periods end が最後となる、periods 個の日時を生成
    • >>> import pandas as pd
      >>>
      >>> # 長さ8、間隔2で月初を選択
      >>> pd.date_range(start="2020-01-01", periods=8, freq="2MS")
      DatetimeIndex(['2020-01-01', '2020-03-01', '2020-05-01', '2020-07-01',
                     '2020-09-01', '2020-11-01', '2021-01-01', '2021-03-01'],
                    dtype='datetime64[ns]', freq='2MS')
      >>>
      >>> # 2020年の日をすべて選択
      >>> pd.date_range("2020", "2021", freq="D")
      DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',
                     '2020-01-05', '2020-01-06', '2020-01-07', '2020-01-08',
                     '2020-01-09', '2020-01-10',
                     ...
                     '2020-12-23', '2020-12-24', '2020-12-25', '2020-12-26',
                     '2020-12-27', '2020-12-28', '2020-12-29', '2020-12-30',
                     '2020-12-31', '2021-01-01'],
                    dtype='datetime64[ns]', length=367, freq='D')
      

日時単位での集約 (Group By)

  • 特定の日時単位(年、月、分など)でデータを集約するには、groupby() ではなく DataFrame.resample()Series.resample() を使う

    • >>> import pandas as pd
      >>>
      >>> # 日本の COVID-19 の PCR 検査陽性者数をロード
      >>> # 出典:厚生労働省ホームページ 新型コロナウイルス感染症について オープンデータ
      >>> #       https://www.mhlw.go.jp/stf/covid-19/open-data.html
      >>> df = pd.read_csv("https://sgryjp.gitlab.io/datasets/pcr_positive_daily.csv",
      ...                  parse_dates=True,
      ...                  index_col="date")
      >>> df
                  num_positives
      date
      2020-01-16              1
      2020-01-17              0
      2020-01-18              0
      2020-01-19              0
      2020-01-20              0
      ...                   ...
      2021-01-05           4885
      2021-01-06           5946
      2021-01-07           7537
      2021-01-08           7844
      2021-01-09           7278
      
      [360 rows x 1 columns]
      
      >>>
      >>> df.resample("M").sum()  # 月ごと("M"=月末で集計)の陽性者数合計を計算
                  num_positives
      date
      2020-01-31             12
      2020-02-29            212
      2020-03-31           1900
      2020-04-30          12361
      2020-05-31           2488
      2020-06-30           1748
      2020-07-31          17367
      2020-08-31          32000
      2020-09-30          15091
      2020-10-31          17583
      2020-11-30          47132
      2020-12-31          85891
      2021-01-31          46070
      

時系列の窓関数処理

窓関数処理 (Windowing Operation) ついては pandas 公式のユーザーガイドも参照されたい。

  • 単純な窓関数処理は DataFrame.rolling()Series.rolling() で行える。

    • >>> import pandas as pd
      >>>
      >>> # 日本の COVID-19 の PCR 検査陽性者数をロード
      >>> # 出典:厚生労働省ホームページ 新型コロナウイルス感染症について オープンデータ
      >>> #       https://www.mhlw.go.jp/stf/covid-19/open-data.html
      >>> df = pd.read_csv("https://sgryjp.gitlab.io/datasets/pcr_positive_daily.csv",
      ...                  parse_dates=True,
      ...                  index_col="date")
      >>> df["moving_average"] = df.num_positives.rolling(7).mean()  # 週単位の移動平均を計算
      >>> df.plot()
      

      プロット