3
votes

Tri des groupes par la première valeur sans changer l'ordre des groupes

J'essaie de trier un dataframe pandas par bloc sans changer l'ordre dans les blocs.

Le dataframe contient des messages de forum, des horodatages et des noms de threads. J'ai déjà trié le dataframe de sorte que tous les messages appartenant au même fil soient dans le bon ordre en utilisant df.sort_values ​​(['thread', 'timestamp'], inplace = True) . Je veux maintenant trier les blocs de données appartenant au même thread en fonction de l'horodatage du premier message de chaque bloc. L'ordre dans les blocs doit rester inchangé.

Ce que j'ai actuellement:

    post   timestamp         thread
6   some   2004/09/01 09:32  game
3   text   2007/04/22 14:11  question
4   this   2007/04/24 11:03  question
5   be     2007/05/03 17:55  question
2   some   2008/07/10 15:23  nice
0   this   2009/10/30 16:51  hello   
1   be     2009/11/02 17:11  hello
7   text   2010/01/01 03:32  wheather

Ce que je veux:

    post   timestamp         thread
0   this   2009/10/30 16:51  hello   
1   be     2009/11/02 17:11  hello
2   some   2008/07/10 15:23  nice
3   text   2007/04/22 14:11  question
4   this   2007/04/24 11:03  question
5   be     2007/05/03 17:55  question
6   some   2004/09/01 09:32  game
7   text   2010/01/01 03:32  wheather


0 commentaires

4 Réponses :


4
votes
  1. Commencez par obtenir le premier «horodatage» de chaque groupe et argsort .
  2. Ensuite, utilisez groupby , en profitant du fait que groupby trie les groupes par clé, mais ne change pas l'ordre dans les groupes.
  3. Enfin, concatre les groupes résultants dans un ordre trié.
  4. idx = df['thread'].map(df.groupby('thread')['timestamp'].first().argsort())
    idx
    
    0    3
    1    3
    2    2
    3    1
    4    1
    5    1
    6    0
    7    4
    Name: thread, dtype: int64
    
    pd.concat([g for _, g in df.groupby(idx)])
    
       post         timestamp    thread
    6  some  2004/09/01 09:32      game
    3  text  2007/04/22 14:11  question
    4  this  2007/04/24 11:03  question
    5    is  2007/05/03 17:55  question
    2  some  2008/07/10 15:23      nice
    0  this  2009/10/30 16:51     hello
    1    is  2009/11/02 17:11     hello
    7  text  2010/01/01 03:32  wheather
    

0 commentaires

3
votes

une façon est de créer une colonne temporaire par exemple nommée 'first_ts' avec groupby sur 'thread' et transform pour obtenir le min (donc la première date) sur la colonne 'timestamp' pour chaque fil. Vous pouvez maintenant trier les valeurs par cette colonne et supprimer la colonne temporaire.

df = df.reindex(df.groupby('thread').timestamp.transform(min)
                  .sort_values().index)

et vous obtenez comme prévu

print(df)
   post           timestamp    thread
6  some 2004-09-01 09:32:00      game
3  text 2007-04-22 14:11:00  question
4  this 2007-04-24 11:03:00  question
5    be 2007-05-03 17:55:00  question
2  some 2008-07-10 15:23:00      nice
0  this 2009-10-30 16:51:00     hello
1    be 2009-11-02 17:11:00     hello
7  text 2010-01-01 03:32:00  wheather

ou si vous ne souhaitez pas créer la colonne, vous pouvez également utiliser reindex avec l'index de la valeur triée de groupby telle que:

# you might need to convert timestamp to datetime 
df.timestamp = pd.to_datetime(df.timestamp)
#create the column
df['first_ts'] = df.groupby('thread').timestamp.transform(min)
#sort and drop
df = df.sort_values(['first_ts']).drop('first_ts',axis=1)


0 commentaires

4
votes

Essayons d'abord de thread groupby puis récupérons le premier enregistrement, trions ces enregistrements par heure, puis utilisons l'attribut groups de DataFrameGroupBy pour obtenir l'ordre actuel de l'index dans chaque groupe. Enfin, utilisez pd.concat et la compréhension de liste pour reconstruire le dataframe dans l'ordre trié des premiers enregistrements.

   post           timestamp    thread
6  some 2004-09-01 09:32:00      game
3  text 2007-04-22 14:11:00  question
4  this 2007-04-24 11:03:00  question
5    be 2007-05-03 17:55:00  question
2  some 2008-07-10 15:23:00      nice
0  this 2009-10-30 16:51:00     hello
1    be 2009-11-02 17:11:00     hello
7  text 2010-01-01 03:32:00  wheather

Sortie:

g = df.groupby('thread')
s = g.head(1).sort_values('timestamp')['thread']
dg = g.groups

pd.concat([df.reindex(dg[i[1]]) for i in s.iteritems()])


0 commentaires

4
votes

En utilisant sort_values ​​ avec drop_duplicates obtenir le min, puis nous utilisons Categorical

cate=df.sort_values('timestamp').drop_duplicates('thread')
df.thread=pd.Categorical(df.thread,ordered=True,categories=cate.thread.tolist())
df=df.sort_values('thread')
df
   post           timestamp    thread
6  some 2004-09-01 09:32:00      game
3  text 2007-04-22 14:11:00  question
4  this 2007-04-24 11:03:00  question
5    be 2007-05-03 17:55:00  question
2  some 2008-07-10 15:23:00      nice
0  this 2009-10-30 16:51:00     hello
1    be 2009-11-02 17:11:00     hello
7  text 2010-01-01 03:32:00  wheather


0 commentaires