从粘贴的示例开始:
df = pd.DataFrame(columns=['a','b','date'],
data=[['foo','blue','2018-02-17'],['foo','blue','2018-02-22'],['foo','red','2018-04-28'],['foo','blue','2018-04-29'],['foo','blue','2018-05-02'],
['foo','red','2018-08-01'],['bar','yellow','2018-01-25'],['bar','red','2018-04-07'],['bar','yellow','2018-07-11'],
['bar','yellow','2018-07-14'],['baz','red','2018-03-11'],['baz','blue','2018-04-14'],['baz','red','2018-07-05'],['baz','blue','2018-10-01']])
df['date'] = pd.to_datetime(df['date'])
df
a b date
0 foo blue 2018-02-17
1 foo blue 2018-02-22
2 foo red 2018-04-28
3 foo blue 2018-04-29
4 foo blue 2018-05-02
5 foo red 2018-08-01
6 bar yellow 2018-01-25
7 bar red 2018-04-07
8 bar yellow 2018-07-11
9 bar yellow 2018-07-14
10 baz red 2018-03-11
11 baz blue 2018-04-14
12 baz red 2018-07-05
13 baz blue 2018-10-01
我确定了在给定时间范围的情况下,df中需要搜索的行的子集。我使用与您的示例相同的60天(+/-30天)时间框架。
win_sz = pd.Timedelta(days=60)
start = df['date'].min() + win_sz/2
end = df['date'].max() - win_sz/2
to_search_over = df[(df['date'] > start) & (df['date'] <= end)]['date']
下一步,
-
对于中的每一行
to_search_over
上面的列表中,我抓取了df的一个子集,其中包含日期在与日期窗口大小(在本例中为60天)相对应的日期范围内的所有行。
-
我用这个窗户
groupby()
和
nunique()
检查列中的给定元素
a
列中有多个值
b
与之相关。
-
最后,我将满足此条件的行的任何索引添加到列表中(
res
)
res = []
for d in to_search_over:
mask = (df['date'] > d-(win_sz/2)) & (df['date'] <= d+(win_sz/2))
window = df.loc[mask]
a = window.groupby('a')['b'].nunique()
a = a[a>1].index.values
if a.any():
res += list(window[window['a'].isin(a)].index)
我将这个索引列表转换为一个集合,然后再转换回一个列表,以便只保留唯一的行值。然后,我可以对df进行切片,以返回所有符合我们标准的行:
df.iloc[list(set(res))]
a b date
2 foo red 2018-04-28
3 foo blue 2018-04-29
4 foo blue 2018-05-02
10 baz red 2018-03-11
11 baz blue 2018-04-14
我很高兴看到是否有人有更优雅的方法来实现这一点(这种方法不需要显式地逐行迭代df的一个子集的行)。我花了一段时间试图找到一种方法
pd.rolling
但无济于事。