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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

Q&A

解決済

1回答

1472閲覧

pandasでresampleしたデータの各開始時刻を、元データと揃えたい

samr

総合スコア9

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

0グッド

2クリップ

投稿2018/06/16 07:50

編集2018/06/18 12:55

前提・実現したいこと

timedata
2018-06-01 00:03:000
2018-06-01 00:04:001
2018-06-01 01:05:000
2018-06-01 01:06:001

この様な抜けのある時系列データを、一時間単位でDataFrame.resampleすると

timedata
2018-06-01 00:00:001
2018-06-01 01:00:001

この様に最初のデータのタイムスタンプが00始まりになります。
これを元のデータと同じ開始時刻に揃え、

timedata
2018-06-01 00:03:001
2018-06-01 01:05:001

この様にしたいのです。

該当のソースコード

python

1import pandas as pd 2 3df = pd.DataFrame( 4 {'data':[0,1,0,1]}, 5 index = ["2018-06-01 00:03:00", 6 "2018-06-01 00:04:00", 7 "2018-06-01 01:05:00", 8 "2018-06-01 01:06:00",]) 9df.index = pd.to_datetime(df.index) 10 11print(df) 12# data 13#2018-06-01 00:03:00 0 14#2018-06-01 00:04:00 1 15#2018-06-01 01:05:00 0 16#2018-06-01 01:06:00 1 17 18print(df.resample('1H').sum()) 19# data 20#2018-06-01 00:00:00 1 21#2018-06-01 01:00:00 1

試したこと

実際のデータは巨大で、for文ではかなり時間が掛かってしまいました。
出来るだけ高速に実現する方法を探しています。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

こんな感じでどうでしょうか。

やっていることは、Resample後の Datetime と Resample前の Dateteime を関連付けたテーブル (Seriesデータ)を作成しておき、Resample後のデータに対して rename() を行っております。

Python

1import pandas as pd 2 3df = pd.DataFrame( 4 {'data':[0,1,0,1]}, 5 index = ["2018-06-01 00:03:00", 6 "2018-06-01 00:04:00", 7 "2018-06-01 01:05:00", 8 "2018-06-01 01:06:00"]) 9df.index = pd.to_datetime(df.index) 10 11 12conv = df.groupby(pd.Grouper(freq='1H')).apply(lambda d:d.index[0]) 13print(conv) 14#2018-06-01 00:00:00 2018-06-01 00:03:00 15#2018-06-01 01:00:00 2018-06-01 01:05:00 16#Freq: H, dtype: datetime64[ns] 17 18res = df.resample('1H').first().rename(index=conv) 19print(res) 20# data 21#2018-06-01 00:03:00 0 22#2018-06-01 01:05:00 0

投稿2018/06/18 10:47

magichan

総合スコア15898

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

samr

2018/06/18 12:49

magichan様、ご回答ありがとうございます。 部分データで試してみたところ、希望通りのデータになり喜んだのですが、 実データで試してみると、applyで以下のエラーが出てしまいます。 IndexError: index 0 is out of bounds for axis 0 with size 0 エラーが発生するデータを調べてみると、1時間を超える欠損が続く場合にエラーが発生しているようです。 もう少しだけ知恵をお貸しください。 以下はエラーが出るコードです。 ```python df = pd.DataFrame( {'data':[0,1,0,1,1,1]}, index = ["2018-06-01 00:03:00", "2018-06-01 00:04:00", "2018-06-01 01:05:00", "2018-06-01 01:06:00", "2018-06-03 02:01:00", "2018-06-03 02:06:00"]) df.index = pd.to_datetime(df.index) conv = df.groupby(pd.Grouper(freq='1H')).apply(lambda d:d.index[0]) ```
magichan

2018/06/18 13:13

多分原因は、groupbyの結果として空のDataframeが渡された際、lambda式のd.index[0] が存在しないためエラーになっているのかと思います。 今手元に実行環境が無いので確認出来ないのですが、d.index.get(0) などで逃げれませんか?
samr

2018/06/18 13:21

取り急ぎ conv = df.groupby(pd.Grouper(freq='1H')).apply(lambda d:d.index.get(0) とやってみましたが、 AttributeError: 'DatetimeIndex' object has no attribute 'get' とエラーが出てしまいました。
samr

2018/06/18 13:38

試行錯誤してみましたが、d.index[0]部を関数化しtryで逃げる事で出来そうな感じです。
samr

2018/06/18 13:44

magichan様、ありがとうございました! tryで逃げ、dropna()で余分なデータを消すことで希望通りのデータが出来ました。 詳細は解決方法に記載しておきます。
samr

2018/06/18 13:48

すみません、解決方法は自己解決の場合だけなんですね。 見づらいですが、ここにコードを記載しておきます。 def func(d): try: res = d.index[0] except: return None return res df = pd.DataFrame( {'data':[0,1,0,1,1,1]}, index = ["2018-06-01 00:03:00", "2018-06-01 00:04:00", "2018-06-01 01:05:00", "2018-06-01 01:06:00", "2018-06-03 02:01:00", "2018-06-03 02:06:00"]) df.index = pd.to_datetime(df.index) conv = df.groupby(pd.Grouper(freq='1H')).apply(lambda d:func(d)) res = df.resample('1H').first().rename(index=conv) res = res.dropna() print(res) # data #2018-06-01 00:03:00 0.0 #2018-06-01 01:05:00 0.0 #2018-06-03 02:01:00 1.0
samr

2018/06/18 14:16

上記のコードのresample集計関数をsum()にしたところ、 dropna()では上手く行かなかったので、以下のように修正しました。 何度も申し訳ありません。 ... res = df.resample('1H').sum().rename(index=conv) res = res[~pd.isnull(res.index)] print(res) # data #2018-06-01 00:03:00 1 #2018-06-01 01:05:00 1 #2018-06-03 02:01:00 2
magichan

2018/06/18 16:13 編集

連絡遅くなりました。 うっ、DatetimeIndex には get() がありませんでしたか・・。やっぱりちゃんと確認しないと駄目ですね。申し訳ありませんでした。 とはいえ、解決できたようでよかったです。 一応私が書くとしたら ですが、DatetimeIndex には to_series() という method があるようなので conv = df.groupby(pd.Grouper(freq='1H')).apply(lambda d:d.index.to_series().get(0)).dropna() とするか、三項演算子をつかって conv = df.groupby(pd.Grouper(freq='1H')).apply(lambda d:None if d.empty else d.index[0]).dropna() とするかのどちらかかと思います。 ここで最後に dropna() をしているのは、Index を Nan に rename するのを防ぐためとなります。
samr

2018/06/18 16:52

ありがとうございます。 色々な書き方が有るのですね、スマートで良いですね。勉強になります。 ですが、どちらも欠損間隔が1時間を超えるデータで実行すると、以下の様な欠損した時間を埋めたデータが出てきてしまいました。 # data #2018-06-01 00:03:00 1 #2018-06-01 01:05:00 1 #2018-06-01 02:00:00 0 #... #2018-06-03 01:00:00 0 #2018-06-03 02:01:00 2 一応、conv部のdropna()をせず、res[~pd.isnull(res.index)]することでfunc無しで期待通りのデータにすることは出来ました。 resampleの方をどうにかすれば、もっとスマートに出来るでしょうか?
magichan

2018/06/19 04:23

現状の方法でも全く問題ないかと思いますが、 dropna した conv データを使うなら res = df.resample('1H').sum().rename(index=conv) res = res[res.index.isin(conv)] のように書けるかと思います。 あと join を使って res = conv.to_frame().join(df.resample('1H').sum()).set_index(0) のように1行で書くことも出来そうですね。
samr

2018/06/19 11:44

何度もありがとうございます。 教えて頂いた方法を色々組み合わせて試してみました。conv部に関しては、 1. funcを使う方法 速度は一番速い(4.69 ms ± 32.4 µs)が、コードが冗長。 2. to_seriesを使う方法 速度が遅い(8.47 ms~8.62 ms ± 64.9 µs) 3. 三項演算子を使う方法 速度はfuncより少し遅くなる(4.81 ms ± 28.5 µs)が、スマート。 res部に関してはどの方法でも速度への影響は然程ありませんでしたが、 joinを使った方法だとインデックスの列名が変わってしまったので、 最終的に分かりやすさを優先して、三項演算子を使った以下の方法に決めました。 conv = df.groupby(pd.Grouper(freq='1H')).apply(lambda d:None if d.empty else d.index[0]).dropna() res = df.resample('1H').sum().rename(index=conv) res = res[res.index.isin(conv)] お付き合いくださいまして、どうもありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問