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

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

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

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

2回答

988閲覧

Pythonのデータフレームについて質問があります。

13orphans88

総合スコア4

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2020/03/18 12:14

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ページで確認できます。

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

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

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

guest

回答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,1310の位置のみなので、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
kirara0048

総合スコア1399

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

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

magichan

2020/03/18 23:55

あーなるほど、[10,12,13] の周期毎に id を振るって事か・・完全に仕様を間違って解釈していた
guest

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
magichan

総合スコア15898

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問