Pythonのデータフレームについて質問があります。
Pandasのコラムに正の整数が入っています。
このコラムから10,10,10,...,10,12,12.....,12,13....13と連続している数字を抽出したいです。
10,12,13の並びになっていてかつ10,12,13はどれくらい連続しているか分からないです。
抽出数字にidの番号を振りたいです。
また、可能であればなるべくifやfor文を使わずに高速な処理が出来れば理想です。
下記のようなデータフレームを作りたいです。
num id
10
:
12 1
:
13
10
: 2
13
キーワード
プログラミング Python Pandas Numpy Jupyter
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答2件
0
作ってみました。
あんまり洗練されてなくてごちゃごちゃしてますが、遅くはないはずです……。
python
1import numpy as np 2import pandas as pd 3 4def function(a, v): 5 v_size = v.size 6 7 cut_index, = np.r_[True, a[1:] != a[:-1], True].nonzero() 8 short_a = a[cut_index[:-1]] 9 slide_index = np.arange(short_a.size-v_size+1)[:, None] + np.arange(v_size) 10 match = (short_a[slide_index] == v).all(1) 11 all_match = np.convolve(match, np.ones(v_size, dtype=int)) 12 index, = np.repeat(all_match, np.ediff1d(cut_index)).nonzero() 13 14 result_array = a[index] 15 result_index = np.isin(index, cut_index[match.nonzero()[0]]).cumsum() 16 17 return pd.DataFrame({'num': result_array, 'id': result_index})
第一引数には対象のシリーズをnumpy配列に変換したものを、
第二引数にはnp.array([10, 12, 13])
を渡してください。
たとえば、
python
1s = pd.Series([10, 10, 10, 12, 13, 13, 0, 0, 10, 12, 13, 10, 12, 10, 12, 13, 13]) 2# [10, 10, 10, 12, 13, 13, 10, 12, 13, , 10, 12, 13, 13] 3v = np.array([10, 12, 13]) 4 5function(s.to_numpy(), v) 6# num id 7# 0 10 1 8# 1 10 1 9# 2 10 1 10# 3 12 1 11# 4 13 1 12# 5 13 1 13# 6 10 2 14# 7 12 2 15# 8 13 2 16# 9 10 3 17# 10 12 3 18# 11 13 3 19# 12 13 3
挙動の解説
1. 同じ値が連続する部分の除外
「10,12,13
はどれくらい連続しているか分からない」ということなので、同じ数が連続する部分をひとまとめにします。これは、インデックスを一つずらした配列と比較することで取得できます。
python
1cut_index, = np.r_[True, a[1:] != a[:-1], True].nonzero() 2short_a = a[cut_index[:-1]] 3 4""" 5a : [10, 10, 10, 12, 13, 13, 0, 0, 10, 12, 13, 10, 12, 10, 12, 13, 13] 6 7a[1:] : 10 [10, 10, 12, 13, 13, 0, 0, 10, 12, 13, 10, 12, 10, 12, 13, 13] 8a[:-1] : [10, 10, 10, 12, 13, 13, 0, 0, 10, 12, 13, 10, 12, 10, 12, 13] 13 9a[1:] != a[:-1] : [ F, F, T, T, F, T, F, T, T, T, T, T, T, T, T, F] 10(concatenate) : [ T, F, F, T, T, F, T, F, T, T, T, T, T, T, T, T, F, T] 11 12cut_index : [ 0, 3, 4, 6, 8, 9, 10, 11, 12, 13, 14, 15, 17] 13short_a : [10, 12, 13, 0, 10, 12, 13, 10, 12, 10, 12, 13, ] 14"""
2. 一致箇所の検索
前節で作成した配列short_a
から、10,12,13
のまとまりを探します。
そのためにまず、short_a
を3つずつ区切るためのインデックスを作成します。
python
1slide_index = np.arange(short_a.size-v_size+1)[:, None] + np.arange(v_size) 2print(slide_index) 3# [[ 0 1 2] 4# [ 1 2 3] 5# [ 2 3 4] 6# ... 7# [ 7 8 9] 8# [ 8 9 10] 9# [ 9 10 11]]
v
と比較することで、10,12,13
と一致する箇所を探します。
python
1match = (short_a[slide_index] == v).all(1) 2 3""" 4slide_a : [[10, 12, 13], [12, 13, 0], [13, 0, 10], [ 0, 10, 12], [10, 12, 13], ...] 5v : [[10, 12, 13], [ > ], [ > ], [ > ], [ > ], ...] 6match : [ True, False, False, False, True, ...] 7"""
match
で得られたTrue
は、10,12,13
の10
の位置のみなので、np.convolve()
を利用して10,12,13
の位置がTrue
になるようにします。
python
1all_match = np.convolve(match, np.ones(v_size, dtype=int)) 2 3""" 4short_a : [10, 12, 13, 0, 10, 12, 13, 10, 12, 10, 12, 13] 5match : [ T, F, F, F, T, F, F, F, F, T] 6all_match : [ 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1] 7"""
3. 一致箇所の拡大復元
前節で求めたall_match
は、10,10,10
のように同じ値が連続する部分がまとまった配列に対する位置なので、np.repeat()
を用いて1.の逆を行います。
python
1index, = np.repeat(all_match, np.ediff1d(cut_index)).nonzero() 2 3""" 4a : [10, 10, 10, 12, 13, 13, 0, 0, 10, 12, 13, 10, 12, 10, 12, 13, 13] 5cut_index : [ 0, 3, 4, 6, 8, 9, 10, 11, 12, 13, 14, 15, 17] 6 7all_match : [ 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, ] 8(repeat) : [ 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1] 9index : [ 0, 1, 2, 3, 4, 5, 8, 9, 10, 13, 14, 15, 16] 10"""
投稿2020/03/18 23:52
編集2020/03/27 02:35総合スコア1399
0
仕様が若干不明瞭なところがありますが。。。
同じ数値でも出現箇所が違う場合はid を分けたいのでしょうかね。
でしたらこんな感じで書けます。
Python
1import pandas as pd 2df = pd.DataFrame({'num': [10,10,10,10,12,12,12,12,10,10,13,13,13]}) 3 4# idを振る 5df['id'] = (df['num'].diff()!=0).cumsum() 6print(df) 7# num id 8#0 10 1 9#1 10 1 10#2 10 1 11#3 10 1 12#4 12 2 13#5 12 2 14#6 12 2 15#7 12 2 16#8 10 3 17#9 10 3 18#10 13 4 19#11 13 4 20#12 13 4 21 22# 何個連続しているか 23ret = df.groupby('id').size() 24print(ret) 25#id 26#1 4 27#2 4 28#3 2 29#4 3
【修正回答】
kirara0048 さんと回答により仕様を完全に間違って解釈していたことが判明しましたので・・・
10,12,13 の周期毎に id
を振りたいのであれば num
値の 13 から 10 への変化点だけを見て積算するとよろしいかと
Python
1import pandas as pd 2df = pd.DataFrame({'num': [10,10,10,10,12,12,12,12,13,13,13,10,12,12,13,13,10,10,10,10,12,12,13]}) 3 4# idを振る 5df['id'] = ((df['num'].shift()!=10) & (df['num']==10)).cumsum() 6print(df) 7# num id 8#0 10 1 9#1 10 1 10#2 10 1 11#3 10 1 12#4 12 1 13#5 12 1 14#6 12 1 15#7 12 1 16#8 13 1 17#9 13 1 18#10 13 1 19#11 10 2 20#12 12 2 21#13 12 2 22#14 13 2 23#15 13 2 24#16 10 3 25#17 10 3 26#18 10 3 27#19 10 3 28#20 12 3 29#21 12 3 30#22 13 3
投稿2020/03/18 23:29
編集2020/03/19 00:30総合スコア15898
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/03/18 23:55