LoginSignup
142
161

More than 5 years have passed since last update.

pandasのDataFrameから期間を範囲指定して抽出する

Posted at

記事の概要

pandasで時系列データを扱う時に長時間の欠損値や異常値があった場合、補間ではどうにもできないので、既にあるDataFrameから日付を指定して抽出したい事がありました。
本記事はDataFrameのカラムに時間情報がある時に日付(あるいは時刻)で範囲指定して時系列データを抽出する方法を備忘録がてら記述したものになります。
また扱うテーブルの情報によって変換の仕方が違うためそれに関しても言及しています。

DataFrameから日時データの抽出

データの下準備になります。長いので必要ない場合この節は飛ばしても大丈夫だと思います。

import pandas as pd
import numpy as np
import datetime as dt

本記事では上記の3つのライブラリを使用しています。

標準形式で記載された日付カラムが存在する場合

用いるDataFrame

df1 = pd.DataFrame({
    '来店者数' : [120, 114, np.nan, 105, 128, 98],
    '仕入れ数' : [140, np.nan, 100, 130, 120, np.nan],
    '日付' : ['2019-05-01', '2019-05-02', '2019-05-03', '2019-05-04', '2019-05-05', '2019-05-06']},
)
来店者数 仕入れ数 日付
0 120.0 140.0 2019-05-01
1 114.0 NaN 2019-05-02
2 NaN 100.0 2019-05-03
3 105.0 130.0 2019-05-04
4 128.0 120.0 2019-05-05
5 98.0 NaN 2019-05-06

"日付"カラムに日付情報が埋め込まれています。DataFrameは列ごとにSeriesを保持し、各Seriesにデータ型が設定されています。各列のデータ型を確認してみます。
ちなみにNaNが入っている特別な意味はないです。

print(df1.dtypes)
来店者数    float64
仕入れ数    float64
日付       object
dtype: object

"日付"カラムののデータ型はobjectです。
通常csvやtsvファイルをpandasのread_csvで読み込んだ場合はこのようなDataFrameになっていると思います。

object型からdatetime型への変換

df1['日付'] = pd.to_datetime(df1['日付'])
print(df1.dtypes)
来店者数           float64
仕入れ数           float64
日付      datetime64[ns]
dtype: object

ちゃんと"日付"カラムがdatetime64[ns]型になっていますね。
datetimeの標準な形になっていればpandasのto_datetimeメソッドをそのまま使えばよいです。

標準形式でない日付カラムが存在する場合

用いるDataFrame

df2 = pd.DataFrame({
    '乗車人数' : [150, 180, 140, 160, 161, 145],
    '日時' : ['2019/5月1日-12:00', '2019/5月1日-12:50', '2019/5月1日-13:30', '2019/5月1日-13:59', '2019/5月1日-14:00', '2019/5月1日-14:30']},
)
乗車人数 日時
0 150 2019/5月1日-12:00
1 180 2019/5月1日-12:50
2 140 2019/5月1日-13:30
3 160 2019/5月1日-13:59
4 161 2019/5月1日-14:00
5 145 2019/5月1日-14:30

まずこんな形式の日時データはないと思いますが、"日時"カラムの中身の形式がぐちゃぐちゃの場合でも変換可能です。

object型からdatetime型への変換

df2['日時'] = pd.to_datetime(df2['日時'], format='%Y/%m月%d日-%H:%M')
print(df2.dtypes)
乗車人数     int64
日時      datetime64[ns]
dtype: object

フォーマットを指定して変換するにはto_datetimeメソッドのformat引数に対応する文字列を渡してあげればよいです。
主なDirective(指示語)は以下の表のとおりです。1

Directive 意味
%Y 西暦4桁の10進法表記
%y 西暦下2桁の10進法表記
%m 月の10進法表記
%d 日の10進法表記
%H 時(24時間表記)の10進法表記
%I 時(12時間表記)の10進法表記
%M 分の10進法表記
%S 秒の10進法表記
%p 'AM'か'PM'かの文字列
%w 曜日の10進法表記 [0(日曜), 6]
%A 曜日名の文字列を返す ['Sunday', 'Saturday']
%% '%'を表す

年月日等の情報が別々のカラムに保持されている場合

用いるDataFrame

df3 = pd.DataFrame({
    '来店者数' : [120, 114, 123, 105, 128, 98],
    '仕入れ数' : [140, 110, 100, 130, 120, 100],
    '年' : [2019, 2019, 2019, 2019, 2019, 2019],
    '月' : [5, 5 ,5 ,6, 6, 6],
    '日' : [29, 30, 31, 1, 2, 3]}
)
来店者数 仕入れ数
0 120 140 2019 5 29
1 114 110 2019 5 30
2 123 100 2019 5 31
3 105 130 2019 6 1
4 128 120 2019 6 2
5 98 100 2019 6 3

こんな感じのデータは結構ありそう。
この例の場合"年","月","日"それぞれのカラムの型はint64になっているかと思います。実際

print(df3.dtypes)
来店者数    int64
仕入れ数    int64
年       int64
月       int64
日       int64
dtype: object

このような結果となりました。

object型からdatetime型への変換

データ型がint64なので、一旦string(object)に変換してからまとめてto_datetimeメソッドに引き渡してdatetime64型にします。

df3['日付'] = pd.to_datetime(df3['年'].astype(str)+'-'+ df3['月'].astype(str)+'-'+ df3['日'].astype(str))
来店者数 仕入れ数 日付
0 120 140 2019 5 29 2019-05-29
1 114 110 2019 5 30 2019-05-30
2 123 100 2019 5 31 2019-05-31
3 105 130 2019 6 1 2019-06-01
4 128 120 2019 6 2 2019-06-02
5 98 100 2019 6 3 2019-06-03
print(df3.dtypes)
来店者数             int64
仕入れ数             int64
年                int64
月                int64
日                int64
日付      datetime64[ns]
dtype: object

変換出来ました。新たに"日付"カラムが生成されているのがわかります。
データ型もちゃんとdatetime64[ns]型になっています。
データ分析において日時の情報を入力する場合はいいですが、もし使わないのであれば"年","月","日"の各カラムは削除してしまってもいいと思います。

df3 = df3.drop(columns=['年','月','日'])
来店者数 仕入れ数 日付
0 120 140 2019-05-29
1 114 110 2019-05-30
2 123 100 2019-05-31
3 105 130 2019-06-01
4 128 120 2019-06-02
5 98 100 2019-06-03

以上の処理でdrop可能です。

期間を範囲指定する

ここから本題となります。

df1から2019/5/3より最近のレコードだけ取り出す場合は

df1[df1['日付'] > dt.datetime(2019,5,3)]

のように記述します。抽出結果は以下の通りになります。

来店者数 仕入れ数 日付
3 105.0 130.0 2019-05-04
4 128.0 120.0 2019-05-05
5 98.0 NaN 2019-05-06

上界と下界を指定する場合は、それぞれの条件を括弧でくくり、'&'で繋げます。
たとえばdf1から2019/5/3以降かつ2019/5/6より前のレコードだけ取り出す場合は

df1[(df1['日付'] >= dt.datetime(2019,5,3)) & (df1['日付'] < dt.datetime(2019,5,6))]

のように記述すればよいです。抽出結果は以下の通りになります。

来店者数 仕入れ数 日付
2 NaN 100.0 2019-05-03
3 105.0 130.0 2019-05-04
4 128.0 120.0 2019-05-05

もちろん時間や分、秒まで指定可能です。
df2から2019/5/1の12:30より後から同日の14:00より前までのレコードだけ取り出す場合は

df2[(df2['日時'] > dt.datetime(2019,5,1,12,30)) & (df2['日時'] < dt.datetime(2019,5,1,14))]

のように書きます。抽出結果は以下の通りです。

乗車人数 日時
1 180 2019-05-01 12:50:00
2 140 2019-05-01 13:30:00
3 160 2019-05-01 13:59:00

他の方法(index)

これまではカラムに保存された日時のデータをdatetimeの型に変換してカラムの上書きをしてきました。
これらとは別の方法として、DataFrameのindexに変換後の日時を保持してインデックス参照するものがあります。
この時期間の範囲指定の方法が少し違います。
日時の情報を保持しつつカラムから削除したい場合にこの方法は有効かと思われます。

たとえばdf2において

df2.index = pd.to_datetime(df2['日時'], format='%Y/%m月%d日-%H:%M').values
df2 = df2.drop(columns='日時')

とすればindexに日時データが保持され、元の"日時"カラムは削除されます。

乗車人数
2019-05-01 12:00:00 150
2019-05-01 12:50:00 180
2019-05-01 13:30:00 140
2019-05-01 13:59:00 160
2019-05-01 14:00:00 161
2019-05-01 14:30:00 145

この時indexで機関の範囲指定をして抽出する方法は以下の通りです。

df2['2019-05-01 12:30' : '2019-05-01 14:00']
乗車人数
2019-05-01 12:50:00 180
2019-05-01 13:30:00 140
2019-05-01 13:59:00 160
2019-05-01 14:00:00 161

上記の方法を用いる場合、条件に境界値は含まれるので注意!

142
161
0

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
142
161