создайте массив дат как пересечение дат, которые содержат John
и даты, которые содержат Susan
:
dates = np.intersect1d(
df.A.values[df.B.values == 'John'],
df.A.values[df.B.values == 'Susan']
)
, затем используйте массив дат для фильтрации кадра данных
df[df.A.isin(dates)]
# outputs:
A B C
0 2002-01-12 Sarah 39
1 2002-01-12 John 17
2 2002-01-12 Susan 30
8 2002-02-20 John 20
9 2002-02-20 Susan 40
Время:
Сравнение решений, предоставленных jpp , ALollz и выше:
Решение на основе numpy в несколько раз эффективнеечем другие.
In [288]: def hal(df):
...: dates = np.intersect1d(
...: df.A.values[df.B.values == 'John'],
...: df.A.values[df.B.values == 'Susan']
...: )
...: return df[df.A.isin(dates)]
...:
In [289]: def jpp(df):
...: s = df.groupby('A')['B'].apply(set)
...: return df[df['A'].map(s) >= {'John', 'Susan'}]
...:
In [290]: def alollz(df):
...: flag = df.groupby('A').B.transform(lambda x: ((x=='Susan').any() & (x == 'John').any()).sum().astype('boo
...: l'))
...: return df[flag==True]
...:
In [291]: %timeit hal(df)
394 µs ± 6.42 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [292]: %timeit jpp(df)
1.46 ms ± 27.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [293]: %timeit alollz(df)
4.9 ms ± 75 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Однако решение, предложенное ALollz, можно ускорить в 2 раза, исключив некоторые лишние ненужные операции и сократившись до пустых массивов для сравнения.
In [294]: def alollz_improved(df):
...: v = df.groupby('A').B.transform(lambda x: (x.values=='Susan').any() & (x.values=='John').any())
...: return df[v]
...:
In [295]: %timeit alollz_improved(df)
2.2 ms ± 38.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)