2015-10-15 4 views
3

J'ai des données sur 3 types d'événements et je veux estimer les probabilités de transition Pij (1). Ceux-ci indiquent la probabilité qu'un certain événement i soit suivi par l'événement j, étant donné que cet événement est survenu (j'ai donc besoin de probabilités conditionnelles). Je veux aussi savoir Pij (2) et Pij (3), qui est la probabilité conditionnelle que le deuxième (troisième) événement après l'événement i soit l'événement j.Estimer les probabilités de transition (pandas)

Jetez un oeil à certaines données maquette:

import pandas as pd 
import numpy as np 
np.random.seed(5) 
strings=list('ABC') 
events=[strings[i] for i in np.random.randint(0,3,20)] 
groups=[1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2] 
index=pd.date_range('2/2/2012',periods=20,freq='T') 
dfm=pd.DataFrame(data={'event':events,'group':groups},index=index) 
dfm.head() 

        event group 
2012-02-02 00:00:00 C 1 
2012-02-02 00:01:00 B 1 
2012-02-02 00:02:00 C 1 
2012-02-02 00:03:00 C 1 
2012-02-02 00:04:00 A 1 

Jusqu'à présent, j'ai suivi une stratégie très inélégante et naïve et utilisé shift pour voir quels événements se sont produits au cours des prochaines périodes:

#Create new columns containing the shifted values 
for i in range(1,4): 
    dfm['event_t%i'%i]=dfm.event.groupby(dfm.group).shift(-i) 
#Combine the columns with current and shifted values into one 
for i in range(1,4): 
    dfm['NEWevent_t%i'%i]=dfm['event']+' '+dfm['event_t%i'%i] 
    dfm = dfm.drop('event_t%i'%i, 1) 

#Count the number of times each combination occurs 
A=dfm['NEWevent_t1'].groupby(dfm.group).value_counts() 
B=dfm['NEWevent_t2'].groupby(dfm.group).value_counts() 
C=dfm['NEWevent_t3'].groupby(dfm.group).value_counts() 

merged=pd.concat([A, B, C], axis=1) 

Cela donne en effet le nombre de fois qu'une combinaison d'événements spécifique (par exemple AA, AB, ..) se produit pour chaque groupe. Pour ce faire, je peux faire un groupement en utilisant à la fois la variable de groupe et la première lettre de la paire à deux lettres comme variables de regroupement. Cette solution de force brute pourrait ressembler à:

merged=merged.reset_index() 
merged['first']=merged['level_1'].apply(lambda x: x[0]) 
merged.columns=['group','i j','t1','t2','t3','first'] 
merged.groupby(['group','first'])['t1','t2','t3'].sum() 
sums=merged.groupby(['group','first'])['t1','t2','t3'].sum() 
merged=pd.merge(merged,sums,left_on=['group','first'],right_index=True) 
merged['Pij(1)']=merged.t1_x/merged.t1_y 
merged['Pij(2)']=merged.t2_x/merged.t2_y 
merged['Pij(3)']=merged.t3_x/merged.t3_y 
merged[['group','i j','Pij(1)','Pij(2)','Pij(3)']] 
merged.head() 

    group i j Pij(1) Pij(2)  Pij(3) 
0 1 A A 0.25 0.666667 0.666667 
1 1 A B 0.25 NaN   NaN 
2 1 A C 0.50 0.333333 0.333333 
3 1 B A 0.50 0.500000 0.500000 
4 1 B C 0.50 0.500000 0.500000 

Je crois qu'il doit y avoir un moyen beaucoup plus facile d'y parvenir? Des suggestions sur la façon de rendre cela plus efficace?

Remarque: mon jeu de données contient 5 millions de lignes, 10 types d'événements et 100 groupes.

Répondre

4

La meilleure façon de présenter les probabilités de transition est dans une matrice de transition où T (i, j) est la probabilité que Ti passe à Tj. Commençons par vos données:

import pandas as pd 
import numpy as np 

np.random.seed(5) 
strings=list('ABC') 
events=[strings[i] for i in np.random.randint(0,3,20)] 
groups=[1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2] 
index=pd.date_range('2/2/2012',periods=20,freq='T') 
dfm=pd.DataFrame(data={'event':events,'group':groups},index=index) 
for i in range(1,4): 
    dfm['event_t%i'%i]=dfm.event.groupby(dfm.group).shift(-i) 

Je pense que votre commande de changement est acceptable, mais c'est juste moi. Quoi qu'il en soit, à partir d'ici, vous vous limiterez à 'group' == 1 et remplissez la matrice de transition. À la fin, vous divisez par les colonnes pour obtenir les probabilités de transition.

trans = pd.DataFrame(columns=strings, index=strings) 
g_dfm = dfm[dfm['group']==1] 

for s1 in strings: 
    for s2 in strings: 
     events = g_dfm[(g_dfm['event']==s1) & (g_dfm['event_t1']==s2)] 
     trans.ix[s1, s2] = len(events) 

trans = trans.astype(float).div(trans.sum(axis=1), axis=0) 
trans = trans.fillna(0) 

À partir de là, vous pouvez faire une heatmap:

import matplotlib.pyplot as plt 

fig, ax = plt.subplots(figsize=(3,3)) 
ax.pcolormesh(trans.values, cmap=plt.get_cmap('Blues'), vmin=0, vmax=1) 
ax.invert_yaxis() 
ax.set_yticks(np.arange(0, len(trans.index))+0.5) 
ax.set_xticks(np.arange(0, len(trans.columns))+0.5) 
ax.set_yticklabels(trans.index, fontsize=16, color='k') 
ax.set_xticklabels(trans.columns, fontsize=16, color='k') 
ax.tick_params(direction='out', pad=10) 
ax.set_frame_on(True) 

for tk1, tk2 in zip(ax.xaxis.get_major_ticks(), ax.yaxis.get_major_ticks()): 
    tk1.tick1On, tk2.tick1On, tk1.tick2On, tk2.tick2On = [False]*4 

plt.show() 

enter image description here

Rincez et répétez l'opération pour tous vos groupes et deuxième et troisième transitions.

+1

Merci. Mais si j'ai raison, il y a un problème avec la ligne 'trans = trans/trans.sum()'. (1) cela semble donner de mauvais résultats et (2) ne peut pas gérer la division par zéro. Une solution serait 'trans = trans.astype (float). Div (trans.sum (axe = 1), axe = 0) trans = trans.fillna (0)'. – Pilik

+0

Merci, je vais y remédier. Cela a fonctionné pour moi, ou je ne l'afficherais pas, mais le vôtre pourrait être plus généralisable. – thefourtheye