2014-08-30 61 views
1

我的情况如下。我有A类赛事之一系列(a_series),由是PersonID和另一个ID无关的问题索引:熊猫:矢量化时间间隔中的行计数

PersonID  AnotherID 
19   768   2013-02-03 13:39:00 
       767   2013-02-03 14:03:00 
       766   2013-02-03 15:35:00 
       765   2013-02-03 22:32:00 
       764   2013-02-04 11:36:00 
       763   2013-02-04 12:07:00 
26   762   2013-02-18 13:21:00 
... 
730   66901   2014-08-21 21:09:00 
       67078   2014-08-22 23:44:00 
       67141   2014-08-23 11:16:00 
       67168   2014-08-23 14:53:00 
       67216   2014-08-23 21:45:00 
Name: Timestamp, Length: 34175, dtype: datetime64[ns] 

而且我得到了另一个系列(b_series),正是建立在同一的,但B型的描述事件:

PersonID  AnotherID 
26   939  2013-02-18 06:01:00 
       940  2013-02-18 06:47:00 
       941  2013-02-19 07:02:00 
... 
728   65159  2014-08-14 18:40:00 
729   66104  2014-08-18 09:08:00 
       66229  2014-08-18 17:31:00 
Name: Timestamp, Length: 1886, dtype: datetime64[ns] 

注意的是,虽然结构是一样的,指数是不一样的 - 这意味着一个人可以有更多的事件比事件B,可能不会有一定的活动键入。

我想创建一个具有相同结构a_series的系列,但是对于每一行,计算发生在A事件之前12小时内的b_series的事件数量。因此,举例来说,如果我们走线26 762 2013-02-18 13:21:00series_a,它的价值应该是2

我已经成功地做到这一点与应用,如:

def apply_func(x, series_b): 
    try: 
     return series_b.loc[x['PersonID']].\ 
      between(x['Timestamp'] - timedelta(hours = 12), x['Timestamp']).sum() 
    except KeyError: 
     return 0 

new_series = series_a.apply(apply_func, axis = 1, args = (seriesb,)) 
new_series.index = series_a.index 

但我不禁觉得必须有更高效的“熊猫”方式。也许用groupby或lookup?

回答

1

根据框架和比赛的数量的大小,它可能更有效地使用连接操作:

首先,给系列名称,将其更改为数据帧:

>>> a.name, b.name = 'a', 'b' 
>>> xb = b.reset_index(level=-1).filter('b') 
>>> xa = a.reset_index() 

然后,加入他们的'PersonID'

>>> df = xa.join(xb, on='PersonID', how='inner') 
>>> df 
    PersonID AnotherID     a     b 
6  26  762 2013-02-18 13:21:00 2013-02-18 06:01:00 
6  26  762 2013-02-18 13:21:00 2013-02-18 06:47:00 
6  26  762 2013-02-18 13:21:00 2013-02-19 07:02:00 

现在,算上命中数:

>>> lag = np.timedelta64(12, 'h') 
>>> df['cnt'] = (df['b'] < df['a']) & (df['a'] < df['b'] + lag) 
>>> ts = df.groupby(['PersonID', 'AnotherID', 'a'])['cnt'].sum() 
>>> ts 
PersonID AnotherID a     
26  762  2013-02-18 13:21:00 2 
Name: cnt, dtype: float64 

,并与原系列一致:

>>> xcol = ['PersonID', 'AnotherID', 'a'] 
>>> xa.join(ts, on=xcol).fillna(0).set_index(xcol[:-1]) 
            a cnt 
PersonID AnotherID       
19  768  2013-02-03 13:39:00 0 
     767  2013-02-03 14:03:00 0 
     766  2013-02-03 15:35:00 0 
     765  2013-02-03 22:32:00 0 
     764  2013-02-04 11:36:00 0 
     763  2013-02-04 12:07:00 0 
26  762  2013-02-18 13:21:00 2 
730  66901  2014-08-21 21:09:00 0 
     67078  2014-08-22 23:44:00 0 
     67141  2014-08-23 11:16:00 0 
     67168  2014-08-23 14:53:00 0 
     67216  2014-08-23 21:45:00 0 
+0

第一个加入实际上是笛卡尔积,对不对?我会尽力在真实数据上计时。谢谢。 – Korem 2014-08-31 15:40:38

+1

@Korem它是笛卡尔产品只有匹配的'PersonID'行,而不是两个框架。它仍然是上述代码的瓶颈,如果两个系列中每个“PersonID”有很多行,则效率可能不高。 – 2014-08-31 15:46:42

+0

是的,但我的解决方案在这些条款中并没有更好的解决方案。它遍历完全相同数量的“行”。 – Korem 2014-08-31 15:54:45