2017-08-11 5 views
2

this questionをPythonで解決しようとしています。Pythonでベクトルのすべての値をフィルタリングするデータフレーム

ID = np.concatenate((np.repeat("A",5), 
        np.repeat("B",4), 
        np.repeat("C",2))) 
Hour = np.array([0,2,5,6,9,0,2,5,6,0,2]) 
testVector = [0,2,5]  
df = pd.DataFrame({'ID' : ID, 'Hour': Hour}) 

当社グループ行IDすることにより、我々はtestVectorでないすべての値がそのグループの列Hourで発見されdfからすべての行を削除します。

def all_in(x,y): 
    return all([z in list(x) for z in y]) 

to_keep = df.groupby(by='ID')['Hour'].aggregate(lambda x: all_in(x,testVector)) 
to_keep = list(to_keep[to_keep].index) 
df = df[df['ID'].isin(to_keep)] 

このコードをできるだけ短く効率的にしたいと考えています。改善のための提案または代替ソリューションのアプローチ?

+0

ええ、最速のソリューションをお望みですか?あなたのデータフレームのサイズは? – jezrael

+0

@Jezrael、この努力の主な目的は私のPythonコーディングを改善することです。だからあなたの答えをありがとう、何かから新しい何かを学んだ); – Florian

+0

私はタイミングをテストし、 'ayhan'の解決策が最も速いようです:) – jezrael

答えて

4
In [99]: test_set = set(testVector) 

In [100]: df.loc[df.groupby('ID').Hour.transform(lambda x: set(x) & test_set == test_set)] 
Out[100]: 
    Hour ID 
0  0 A 
1  2 A 
2  5 A 
3  6 A 
4  9 A 
5  0 B 
6  2 B 
7  5 B 
8  6 B 

説明:私たちは、グループごとにHour値のセットを作成lambda x: set(x) & test_set == test_set)機能で

In [104]: df.groupby('ID').Hour.apply(lambda x: set(x)) 
Out[104]: 
ID 
A {0, 2, 5, 6, 9} 
B  {0, 2, 5, 6} 
C    {0, 2} 
Name: Hour, dtype: object 

その後、我々はtest_setで交差点を設定します:

In [105]: df.groupby('ID').Hour.apply(lambda x: set(x) & test_set) 
Out[105]: 
ID 
A {0, 2, 5} 
B {0, 2, 5} 
C  {0, 2} 
Name: Hour, dtype: object 

test_setもう一度と比較:私はちょうどそれがどのように動作するかを示すために.apply()の代わり.transformを使用

In [106]: df.groupby('ID').Hour.apply(lambda x: set(x) & test_set == test_set) 
Out[106]: 
ID 
A  True 
B  True 
C False 
Name: Hour, dtype: bool 

PS。

しかし、我々は、後にブールインデックスを使用するために変換を使用する必要があります。

In [107]: df.groupby('ID').Hour.transform(lambda x: set(x) & test_set == test_set) 
Out[107]: 
0  True 
1  True 
2  True 
3  True 
4  True 
5  True 
6  True 
7  True 
8  True 
9  False 
10 False 
Name: Hour, dtype: bool 
+0

ありがとう!ここのラムダ関数が正確に何を説明してくれますか? – Florian

+1

@Florian、今は良くなったと思っています... ;-) – MaxU

+0

ありがとう!私は演算子のシーケンスのために混乱していました。 'test_set == test_set'は奇妙に見えます。 ;) – Florian

2

は最初Hour列から各IDについてset Sを作成します。その後、ベクターと比較して、新たなSeriesためmap

df = df[df['ID'].map(df.groupby(by='ID')['Hour'].apply(set)) >= set(testVector)] 
print (df) 
    Hour ID 
0  0 A 
1  2 A 
2  5 A 
3  6 A 
4  9 A 
5  0 B 
6  2 B 
7  5 B 
8  6 B 

タイミング:MaxUのソリューションと同様に

np.random.seed(123) 
N = 1000000 

df = pd.DataFrame({'ID': np.random.randint(200, size=N), 
        'Hour': np.random.choice(range(10000),N)}) 
print (df) 
testVector = [0,2,5] 

test_set = set(testVector) 
s = pd.Series(testVector) 

#maxu sol 
In [259]: %timeit (df.loc[df.groupby('ID').Hour.transform(lambda x: set(x) & test_set == test_set)]) 
1 loop, best of 3: 356 ms per loop 

#jez sol 
In [260]: %timeit (df[df['ID'].map(df.groupby(by='ID')['Hour'].apply(set)) >= set(testVector)]) 
1 loop, best of 3: 462 ms per loop 

#ayhan sol1 
In [261]: %timeit (df[df.groupby('ID')['Hour'].transform(lambda x: s.isin(x).all())]) 
1 loop, best of 3: 300 ms per loop 

#ayhan sol2 
In [263]: %timeit (df.groupby('ID').filter(lambda x: s.isin(x['Hour']).all())) 
1 loop, best of 3: 211 ms per loop 
2

が、私はシリーズを使用する代わりに、一連の:

testVector = pd.Series(testVector) 
df[df.groupby('ID')['Hour'].transform(lambda x: testVector.isin(x).all())] 
Out: 
    Hour ID 
0  0 A 
1  2 A 
2  5 A 
3  6 A 
4  9 A 
5  0 B 
6  2 B 
7  5 B 
8  6 B 

にフィルタはここでもっと慣用的かもしれません:

df.groupby('ID').filter(lambda x: testVector.isin(x['Hour']).all()) 
Out: 
    Hour ID 
0  0 A 
1  2 A 
2  5 A 
3  6 A 
4  9 A 
5  0 B 
6  2 B 
7  5 B 
8  6 B 
+1

ありがとう!素晴らしい解決策。 – Florian

+1

最速の解決策が最後のようです;) – jezrael

関連する問題