2014-08-30 3 views
1

Моя ситуация следующая. У меня есть одна серия (a_series) событий типа А, индексируются PersonId и другим ID не имеет отношения к вопросу:pandas: векторизация строк подсчета во временном интервале

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, но для каждой строки, подсчитывая количество событий из b_series, которые произошли за 12 часов до события A. Так, например, если мы возьмем линию 26 762 2013-02-18 13:21:00 от series_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 

Но я не могу помочь, но чувствую, что должен быть более эффективный «панда-и-й» способ. Возможно, с группой или поиском?

ответ

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

+1

@Korem - это декартово произведение только для строк 'PersonID', а не для двух фреймов. Он по-прежнему является узким местом для кода выше и может быть неэффективным, если в каждой серии есть несколько строк на 'PersonID'. –

+0

Да, но мое решение не лучше в этих терминах. Он пересекает то же самое количество «строк». – Korem

Смежные вопросы