3 votes

Compter toutes les occurrences passées d'un élément dans un grand ensemble de données

J'ai un cadre de données assez grand (3 millions de lignes) qui ressemble à ceci :

df = pd.DataFrame({'user_id' : ['100','101','102','103','104'],
           'service_id' : ['73', '73', '46', '12', '12'],
           'date_of_service' : ['2015-06-10 17:00:00', 
                               '2014-09-27 17:00:00', 
                               '2015-01-12 17:00:00', 
                               '2012-08-22 17:00:00', 
                               '2013-03-07 17:00:00']})
df

J'ai une colonne pour l'identification de l'utilisateur. Les utilisateurs fournissent des services, chaque service ayant un identifiant (service_id). Chaque service a une date (date_of_service).

Je voudrais créer une colonne qui calcule, pour chaque ligne, le nombre de services que l'utilisateur a effectués dans le passé (y compris le présent), c'est-à-dire que je voudrais le dataframe de résultat suivant :

df = pd.DataFrame({'user_id' : ['100','101','102','103','104'],
           'service_id' : ['73', '73', '46', '12', '12'],
           'date_of_service' : ['2015-06-10 17:00:00', 
                               '2014-09-27 17:00:00', 
                               '2015-01-12 17:00:00', 
                               '2012-08-22 17:00:00', 
                               '2013-03-07 17:00:00'],
              'number_of_past_services' : [2, 1, 1, 1, 2]})
df

Ce que j'ai fait

J'ai utilisé un groupby et un count :

df['count_services'] = df.ix[:, 1:].groupby('user_id').transform('count')

Le problème est que, ici, je compte toutes les occurrences sur l'ensemble des données. Ce que je veux, c'est avoir les occurrences passées !

J'ai essayé de filtrer en utilisant des opérations de type sql, par exemple :

len(df[df.date_of_service < df['date_of_service'][0]][df.user_id == df.user_id[0]])

ce qui donne le bon résultat pour la première ligne. Cependant, ce calcul prend environ 1 seconde pour une seule ligne !

J'aimerais savoir comment mettre cela à l'échelle afin de pouvoir créer une colonne de manière efficace.

2voto

MaxU Points 5284

IIUC vous pouvez le faire de cette façon :

In [69]: df['number_of_past_services'] = df.sort_values('date_of_service') \
                                           .assign(x=1) \
                                           .groupby('service_id')['x'].cumsum()

In [70]: df
Out[70]:
      date_of_service service_id user_id  number_of_past_services
0 2015-06-10 17:00:00         73     100                        2
1 2014-09-27 17:00:00         73     101                        1
2 2015-01-12 17:00:00         46     102                        1
3 2012-08-22 17:00:00         12     103                        1
4 2013-03-07 17:00:00         12     104                        2

0voto

Eric Duminil Points 38857

Si j'ai bien compris, vous pourriez :

  • trier vos données par date_of_service
  • créer un dictionnaire vide d'occurrences passées
  • itérer sur toutes les lignes triées :
    • past_occurences.get(user_id, 0) contient les informations dont vous avez besoin
    • past_occurences[user_id] = past_occurences.get(user_id, 0) + 1 mettra à jour le dictionnaire.

De toutes les étapes, le tri sera le plus lent. Le reste devrait être raisonnablement rapide.

PS : Vous pouvez également utiliser un defaultdict . Voici un exemple .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X