2017-08-11 54 views
2

我想用Python解决this question在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 

说明:

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 

在我们为每个组创建一组 Hour值的 lambda x: set(x) & test_set == test_set)功能

并再次与test_set比较吧:

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我以前.apply()代替.transform只是为了展示它是如何工作的。

但是我们需要用才能使用布尔索引以后变换:

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

谢谢!你介意解释一下这里的lambda函数到底做了什么吗? – Florian

+1

@弗洛里安,我希望现在好起来...... ;-) – MaxU

+0

谢谢一堆!由于运算符的顺序,我感到困惑,'test_set == test_set'看起来很奇怪。 ;) – Florian

2

每个IDHour柱首先创建set秒。

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 

时序:然后mapSeries这与载体相比

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

类似MaxU的解决方案,但我用了一个系列,而不是一组:

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

相关问题