7 votes

Une façon efficace de filtrer par date dans groupby

Compte tenu de la DataFrame généré par :

import numpy as np
import pandas as pd
from datetime import timedelta

np.random.seed(0)
rng = pd.date_range('2015-02-24', periods=14, freq='9H')
ids = [1]*5 + [2]*2 + [3]*7
df = pd.DataFrame({'id': ids, 'time_entered': rng, 'val': np.random.randn(len(rng))})

df :

    id  time_entered        val
0   1   2015-02-24 00:00:00 1.764052
1   1   2015-02-24 09:00:00 0.400157
2   1   2015-02-24 18:00:00 0.978738
3   1   2015-02-25 03:00:00 2.240893
4   1   2015-02-25 12:00:00 1.867558
5   2   2015-02-25 21:00:00 -0.977278
6   2   2015-02-26 06:00:00 0.950088
7   3   2015-02-26 15:00:00 -0.151357
8   3   2015-02-27 00:00:00 -0.103219
9   3   2015-02-27 09:00:00 0.410599
10  3   2015-02-27 18:00:00 0.144044
11  3   2015-02-28 03:00:00 1.454274
12  3   2015-02-28 12:00:00 0.761038
13  3   2015-02-28 21:00:00 0.121675

Je dois, pour chaque id supprimez les lignes qui se trouvent à plus de 24 heures (1 jour) de la dernière date d'enregistrement. time_entered pour cela id . Ma solution actuelle :

def custom_transform(x):
    datetime_from = x["time_entered"].max() - timedelta(days=1)
    x = x[x["time_entered"] > datetime_from]
    return x

df.groupby("id").apply(lambda x: custom_transform(x)).reset_index(drop=True)

qui donne la sortie correcte et attendue :

    id  time_entered        val
0   1   2015-02-24 18:00:00 0.978738
1   1   2015-02-25 03:00:00 2.240893
2   1   2015-02-25 12:00:00 1.867558
3   2   2015-02-25 21:00:00 -0.977278
4   2   2015-02-26 06:00:00 0.950088
5   3   2015-02-28 03:00:00 1.454274
6   3   2015-02-28 12:00:00 0.761038
7   3   2015-02-28 21:00:00 0.121675

Cependant, mes données réelles sont des dizaines de millions de lignes, et des centaines de milliers d'identifiants uniques, pour cette raison, cette solution est infaisable (prend beaucoup de temps).

Existe-t-il un moyen plus efficace de filtrer les données ? J'apprécie toutes les idées !

4voto

Quang Hoang Points 191

En général, il faut éviter groupby().apply() puisqu'il n'est pas vectorisé à travers les groupes, sans parler de l'overhead pour l'allocation de mémoire si vous retournez de nouveaux dataframes comme dans votre cas.

Que diriez-vous de trouver le seuil de temps avec groupby().transform puis utiliser l'indexation booléenne sur l'ensemble des données :

time_max_by_id = df.groupby('id')['time_entered'].transform('max') - pd.Timedelta('1D')
df[df['time_entered'] > time_max_by_id]

Sortie :

    id        time_entered       val
2    1 2015-02-24 18:00:00  0.978738
3    1 2015-02-25 03:00:00  2.240893
4    1 2015-02-25 12:00:00  1.867558
5    2 2015-02-25 21:00:00 -0.977278
6    2 2015-02-26 06:00:00  0.950088
11   3 2015-02-28 03:00:00  1.454274
12   3 2015-02-28 12:00:00  0.761038
13   3 2015-02-28 21:00:00  0.121675

0voto

W-B Points 94428
df.groupby('id').apply(lambda x : x[(x['time_entered'].max()-x['time_entered'])<pd.Timedelta('1D')]).reset_index(drop=True)
Out[322]: 
   id        time_entered       val
0   1 2015-02-24 18:00:00  0.978738
1   1 2015-02-25 03:00:00  2.240893
2   1 2015-02-25 12:00:00  1.867558
3   2 2015-02-25 21:00:00 -0.977278
4   2 2015-02-26 06:00:00  0.950088
5   3 2015-02-28 03:00:00  1.454274
6   3 2015-02-28 12:00:00  0.761038
7   3 2015-02-28 21:00:00  0.121675

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