2014-09-17 3 views
0

Я пытаюсь выполнить некоторый рудиментарный анализ воронки с помощью блока данных pandas. Смысл, у меня есть dataframe, содержащий пользователя sessions, который состоит из серии events. Я хотел бы иметь возможность группировать по session, определить, какие sessions содержат заданный event ordering (eventA, за которым следует eventB), а затем группировать по date и получать их с течением времени.анализ воронки с пандами (большой набор данных)

, например, учитывая мой dataframe:

sessions = ['a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'd', 'd', 'd'] 
events = ['dog', 'cat', 'tree', 'tree', 'dog', 'frog', 'cat', 'dog', 'cat', 'tree', 'cat', 'dog'] 
d1 = datetime(2014,8,1) 
d2 = datetime(2014,8,2) 
d3 = datetime(2014,8,3) 
dates = [d1, d1, d1, d1, d1, d1, d1, d2, d2, d1, d1, d1] 
dic = {'sessions':sessions, 'events':events, 'dates':dates} 
df_tot = pd.DataFrame(dic) 

производства:

sessionDate events sessions 
0 2014-08-01 dog  a 
1 2014-08-01 cat  a 
2 2014-08-01 tree a 
3 2014-08-01 tree b 
4 2014-08-01 dog  b 
5 2014-08-01 frog b 
6 2014-08-01 cat  b 
7 2014-08-02 dog  c 
8 2014-08-02 cat  c 
9 2014-08-01 tree d 
10 2014-08-01 cat  d 
11 2014-08-01 dog  d 

Я хотел бы получить следующий за заказ событие dog затем cat:

   reachedFirstEvent reachedSecondEvent total 
2014-08-01 1     2     3 
2014-08-02 0     1     1 

Мой второй проблема в том, что у меня 3 миллиона строк в моем фактическом фрейме. Поэтому я создал взломанное решение. Он работает, но довольно медленный. Любые мысли о том, как это сделать или ускорить мой код?

def find_funnels_ex(dlist,event_list): 

     m = -1 
     for i in range(0,len(event_list)): 

      j = np.where(dlist == event_list[i])[0] #get all indices where cat 
      j = j[j>=m] #select only indices greater than min dog index 
      if j.size == 0: 
       return i 
      else: 
       m = np.min(j) 

     return i+1 

sessions = ['a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'd', 'd', 'd'] 
events = ['dog', 'cat', 'tree', 'tree', 'dog', 'frog', 'cat', 'dog', 'cat', 'tree', 'cat', 'dog'] 
d1 = datetime(2014,8,1) 
d2 = datetime(2014,8,2) 
d3 = datetime(2014,8,3) 
dates = [d1, d1, d1, d1, d1, d1, d1, d2, d2, d1, d1, d1] 
dic = {'sessions':sessions, 'events':events, 'dates':dates} 
df_tot = pd.DataFrame(dic) 


#get only groups that have at least first event 
gb_tot = df_tot.groupby('sessions') 
df_filt = gb_tot.filter(lambda x: 'dog' in x['events'].values) #changes to dataframe 

#get funnel position for each session 
#returns a 1 if first event is reached, returns a 2 if second event is reached, etc 
gb_filt = df_filt.groupby('sessions') 
gb_funn = gb_filt.aggregate(lambda x: find_funnels_ex(
           x['events'].values, 
           ['dog','cat'] 
           ) 
        ) 

#join this to funnel to get date events funnel was started 
gb_filt = gb_filt.aggregate({'dates':np.min}) 
gb_filt['funnel'] = gb_funn['events'] 
df_funn = gb_filt.reset_index() #change back to dataframe 

#pivot to get columns of funnel position indicators 
df_piv = pd.pivot_table(df_funn,'funnel', cols='funnel', rows=['sessions','dates'], aggfunc=np.sum) #pivot 
df_piv = df_piv.reset_index() #reset 

#group by date and sum 
df_piv = df_piv.set_index('dates') #set index 
gb_piv = df_piv.groupby(lambda x: x) #groupby date 
gb_final = gb_piv.aggregate({1:np.sum,2:np.sum}) 

#get totals 
gb_tot = df_tot.groupby('sessions') 
gb_tot = gb_tot.aggregate({'dates':np.min}) 
gb_tot = gb_tot.set_index('dates') #set index 
gb_tot = gb_tot.groupby(lambda x: x).size() #groupby date 
gb_final['total'] = gb_tot 


gb_final[2] = gb_final.apply(lambda x: x[2]/2.0,axis=1) 
+0

Можете ли вы добавить немного подробностей в свой пример о том, почему 'reachFirstEv ent' является '0' для' 2014-08-02'? Если критерии, чтобы достичь первого, даже «собака» в сеансе, то не разрешается ли сессия 'c'? – cwharland

+0

Правильно, так что это происходит из моей функции find_funnels_ex(). Если и только если достигнута вторая часть воронки, она возвращает 2. Если только первая часть достигнута, она возвращает 1. Итак, вы правы, фактический счет firstEventReached должен быть столбцами 1 + 2 – user3439329

+0

Звучит неплохо , работая над записью чего-то, что может помочь. Чтобы лучше понять, ваша цель состоит в том, чтобы сделать две вещи: 1) определить сеансы, которые достигают первого и/или второго события, и 2) для каждой даты суммировать количество сеансов, которые соответствуют этим логическим точкам? Кроме того, массив даты, который у вас есть, не создает список df. он должен читать '[d1, d1, d1, d1, d1, d1, d1, d2, d2, d1, d1, d1]' – cwharland

ответ

0

Вот еще одна версия, которую я придумал. Точно так же медленно с большим количеством данных, хотя :(

Эта функция определяет, включает ли сеанс текущую воронку:

def find_funnels(dlist,event_list): 

    m = -1 
    for i in range(0,len(event_list)): 

     j = np.where(dlist == event_list[i])[0] 
     j = j[j>=m] 
     if j.size == 0: 
      return False 
     else: 
      m = np.min(j) 

    return True 

Данная функция применяет текущую воронку к ДФ:

def funnelz(df, eventList, groups, dates, events): 

    dfz = pd.DataFrame() 
    count = 1 
    eventList2 = [eventList[0]] 

    while eventList: 

     if not dfz.empty: 
      gb = df.groupby(groups) 
      df = gb.filter(lambda x: find_funnels(x[events].values, eventList2)) 

     gb = df.groupby(groups) 
     gb = gb.aggregate({dates:np.min}) 
     gb = gb.set_index(dates) 
     gb = gb.groupby(lambda x: x).size() 

     if not dfz.empty: 
      dfz['event'+str(count)+'Reached'] = gb 
      count += 1 
      eventList = eventList[1:] 
      if eventList: 
       eventList2.append(eventList[0]) 

     else: 
      dfz['total'] = gb 

    return dfz 

кажется для работы, но медленный:

funnelz(df_funn, eventList=['dog', 'cat'], groups = 'sessions', dates = 'dates', events = 'events') 
Смежные вопросы