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

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

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

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

pandas

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

Q&A

解決済

4回答

2943閲覧

Pandasで4行以上連続して条件を満たす行のみ残したい

miraMirar

総合スコア10

Python

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

pandas

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

0グッド

0クリップ

投稿2021/11/09 05:35

編集2021/11/09 05:40

前提・実現したいこと

Pandasで時系列データの処理をしています。
OFFのときは0で、記録中はプラスの値が入っているのですが、たまにOFFなのに値が入っていることがあり、これを0にしたいです。4行以上連続で値が入っていれば記録中とみなします。

以下のような表で、「記録値」を「処理後」にしたいです。
|時刻|記録値|処理後|
|12:00|0|0|
|12:01|0|0|
|12:03|1|0|
|12:04|0|1|
|12:05|3|3|
|12:06|2|2|
|12:07|4|4|
|12:08|1|1|
|12:09|2|2|
|12:10|0|0|
|12:11|0|0|
|12:12|1|0|
|12:13|3|0|
|12:14|1|0|
|12:15|0|0|

発生している問題・エラーメッセージ

「該当行の上2行のいずれかが0かつ下2行のいずれかが0なら0にする」という処理をすると1点のみと2点連続は0になり、3点連続は1点のみになります。
(1点のみ:010→000 2点連続:0220→0000  3点連続:01110→01010)
これに続いて「上の行も下の行も0なら0にする」という処理をすることを考えました。
ただ、表全体に2段階の処理を適用することになるのでより速いやり方にしたいのですが思いつきません。

該当のソースコード

Python

1For i in range(2, (len(df)): #i-2がエラーにならないように3行目からループ(はじめの2行は常に0) 2 if ((df.iat[(i+1),1] == 0)|(df.iat[(i+2),1] == 0))&((df.iat[(i-1),1] == 0)|(df.iat[(i-2),1] == 0)): 3 df.iat[i, 1] = 0 4 5For i in range(2, (len(df)): 6 if (df.iat[(i+1),1] == 0)&(df.iat[(i-1),1] == 0): 7 df.iat[i, 1] = 0

試したこと

新しい列を作って、0ならFalse、0より大きければTrueを入れて、TrueかFalseが連続しているところに同じ番号をつけるということもしてみたのですが、その先で「4行以上連続」をどう認識させればいいのか思いつきませんでした。

Python

1df['over0'] = df['記録値']>0 2df['group'] = (df['over0'] != df['over0'].shift(1)).cumsum()

補足情報(FW/ツールのバージョンなど)

Python3.8

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

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

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

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

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

melian

2021/11/09 06:31 編集

間違えて投稿してしまいました。すみません。
guest

回答4

0

ベストアンサー

|12:04|0|1|は|12:04|0|0|の書き間違いのように見えます。

それで良ければ、以下のようにすればできます。

python

1df['flag'] = ((df['記録値']==0)|(df['記録値']==0).shift()).cumsum() 2def f(df): 3 if len(df)>=4: 4 df['処理後'] = df['記録値'] 5 else: 6 df['処理後'] = 0 7 return df 8 9print(df.groupby('flag').apply(f))

実行結果

python

1>>> print(df) 2 時刻 記録値 30 12:00 0 41 12:01 0 52 12:03 1 63 12:04 0 74 12:05 3 85 12:06 2 96 12:07 4 107 12:08 1 118 12:09 2 129 12:10 0 1310 12:11 0 1411 12:12 1 1512 12:13 3 1613 12:14 1 1714 12:15 0 18>>> df['flag'] = ((df['記録値']==0)|(df['記録値']==0).shift()).cumsum() 19>>> def f(df): 20... if len(df)>=4: 21... df['処理後'] = df['記録値'] 22... else: 23... df['処理後'] = 0 24... return df 25... 26>>> print(df.groupby('flag').apply(f)) 27 時刻 記録値 flag 処理後 280 12:00 0 1 0 291 12:01 0 2 0 302 12:03 1 3 0 313 12:04 0 4 0 324 12:05 3 5 3 335 12:06 2 5 2 346 12:07 4 5 4 357 12:08 1 5 1 368 12:09 2 5 2 379 12:10 0 6 0 3810 12:11 0 7 0 3911 12:12 1 8 0 4012 12:13 3 8 0 4113 12:14 1 8 0 4214 12:15 0 9 0

投稿2021/11/09 06:44

ppaul

総合スコア24666

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

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

miraMirar

2021/11/10 06:51

綺麗なコードありがとうございます! できました! ただ勉強不足でどう動いているのかよくわからないので、これから勉強しようと思います。 ((df['記録値']==0)|(df['記録値']==0).shift()でBool値が返ってきた時点で自分の知識を超えていました。) 12:04の「処理後」はご指摘の通り間違いです。
guest

0

解決済になっていますが、ずばり、連続で値が入っているかどうかの判定ができるbinary_opening()という関数がありますので、これを使うのが簡単ですよ。
scipy.ndimage.binary_opening — SciPy v1.7.1 Manual

python

1import pandas as pd 2from scipy.ndimage import binary_opening 3 4df = pd.DataFrame({ 5 '時刻': ['12:00', '12:01', '12:03', '12:04', '12:05', '12:06', '12:07', '12:08', '12:09', '12:10', '12:11', '12:12', '12:13', '12:14', '12:15'], 6 '記録値': [0, 0, 1, 0, 3, 2, 4, 1, 2, 0, 0, 1, 3, 1, 0]}) 7 8N = 4 9 10df['処理後'] = df['記録値'] * binary_opening(df['記録値'], [1]*N)

投稿2021/11/15 04:34

編集2021/11/15 04:36
kirara0048

総合スコア1399

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

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

0

窓関数(rolling)を使ってもできそうです。

2回rollingをやっていて、
1回目(consecutive_fwd)は、その行以降で4行連続して記録値が正である行でTrueになります。
(rollingの窓が後ろ方向になるのであとでshiftしています)
2回目(consecutive)は、1回目でTrueになった行が4行目以内に含まれる行でTrueになります。

rollingは特段遅い処理でもないので、2回やってもそれほど問題ないと思います。

python

1import pandas as pd 2 3df = pd.DataFrame({ 4 '時刻': ['12:00', '12:01', '12:03', '12:04', '12:05', '12:06', '12:07', '12:08', '12:09', '12:10', '12:11', '12:12', '12:13', '12:14', '12:15'], 5 '記録値': [0, 0, 1, 0, 3, 2, 4, 1, 2, 0, 0, 1, 3, 1, 0]}) 6 7N = 4 8 9consecutive_fwd = (df['記録値'] > 0).rolling(N).sum().shift(1 - N) == N 10consecutive = consecutive_fwd.rolling(N, min_periods=1).max() > 0 11df['処理後'] = df['記録値'].where(consecutive, 0)

余談

上でやっていることは畳み込みと同じなので、速度を求めるなら numpy.convolve を使うと、かなり改善できると思います。

python

1import numpy as np 2 3window = np.ones(N, dtype=int) 4consecutive_fwd = np.convolve(df['記録値'] > 0, window, mode='valid') == N 5consecutive = np.convolve(consecutive_fwd, window) > 0 6df['処理後'] = np.where(consecutive, df['記録値'], 0)

投稿2021/11/10 02:07

編集2021/11/10 02:07
bsdfan

総合スコア4599

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

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

miraMirar

2021/11/12 14:17

回答ありがとうございます。 どちらも使ったことのない関数で勉強になりました! 時系列データの処理に役立ちそうなので色々試してみたいと思います。
guest

0

ただ、表全体に2段階の処理を適用することになるのでより速いやり方にしたいのですが思いつきません。

その 2 段階の処理を Pandas ではなくてリストで行ってみてはどうでしょうか。

python

1import pandas as pd 2 3df = pd.DataFrame({ 4 '時刻': pd.date_range("12:00", "12:14", freq="min").time, 5 '記録値': [0, 0, 1, 0, 3, 2, 4, 1, 2, 0, 0, 1, 3, 1, 0], 6}) 7 8r = df['記録値'].tolist() 9l = len(r) 10 11# 該当行の上2行のいずれかが0かつ下2行のいずれかが0なら0にする 12r = r[:2] + [ 13 r[i+2] * any((all(r[i:(i+2)]), all(r[(i+3):(i+5)]))) 14 for i in range(l-4) 15 ] + r[-2:] 16 17# 上の行も下の行も0なら0にする 18r = r[:1] + [r[i+1] * any((r[i], r[i+2])) for i in range(l-2)] + r[-1:] 19 20# データフレームに反映 21df['記録値'] = r 22 23print(df.to_markdown(index=False))
時刻記録値
12:00:000
12:01:000
12:02:000
12:03:000
12:04:003
12:05:002
12:06:004
12:07:001
12:08:002
12:09:000
12:10:000
12:11:000
12:12:000
12:13:000
12:14:000

投稿2021/11/09 09:45

編集2021/11/09 16:55
melian

総合スコア19865

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

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

miraMirar

2021/11/12 13:30

ありがとうございます! リストにするとループよりスマートですね。 Bool値をかけて0にするのは思いつきませんでした。 勉強になります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問