过滤与Python中的矢量的所有值匹配的数据帧

时间:2017-08-11 10:14:15

标签: python pandas dataframe

我正在尝试用Python解决this question

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对行进行分组,然后我们要删除df中的所有行,其中testVector中的所有值都未在该组的Hour列中找到。我们可以实现如下:

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)]

我希望尽可能简短有效地使用此代码。有任何改进建议或替代解决方案吗?

3 个答案:

答案 0 :(得分: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

<强>解释

lambda x: set(x) & test_set == test_set)函数中,我们为每个组创建了一组Hour值:

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进行比较:

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来展示它是如何工作的。

但我们需要使用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

答案 1 :(得分:2)

首先从set列为每个ID创建Hour个。然后map用于新的Series,与vector:

进行比较
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

<强>计时

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 :(得分:2)

与MaxU的解决方案类似,但我使用的是Series而不是set:

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