【问题标题】:Sorting groups by the first value without changing the group order按第一个值对组进行排序而不更改组顺序
【发布时间】:2019-01-12 01:27:51
【问题描述】:

我正在尝试在不更改块内顺序的情况下按块对 pandas 数据帧进行排序。

数据框包含论坛帖子、时间戳和主题名称。我已经对数据框进行了排序,以便属于同一线程的所有帖子都使用df.sort_values(['thread', 'timestamp'], inplace=True) 以正确的顺序排列。我现在想根据每个块中第一个帖子的时间戳对属于同一线程的数据块进行排序。块内的顺序应保持不变。

我目前拥有的:

    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

我想要什么:

    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

有没有办法做到这一点?

【问题讨论】:

    标签: python pandas dataframe group-by pandas-groupby


    【解决方案1】:

    让我们先尝试groupby线程,然后获取第一条记录,将这些记录按时间排序,然后使用DataFrameGroupBy的groups属性获取每个组中索引的当前顺序。最后,使用pd.concat 和列表推导以第一条记录的排序顺序重建数据框。

    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()])
    

    输出:

       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
    

    【讨论】:

      【解决方案2】:
      1. 首先,获取每个组的第一个“时间戳”和argsort
      2. 接下来,使用groupby,利用groupby 按键对组进行排序,但不会更改组内的顺序这一事实。
      3. 最后,concat 按排序的结果组。

      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
      

      【讨论】:

        【解决方案3】:

        使用sort_valuesdrop_duplicates 得到最小值,然后我们使用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
        

        【讨论】:

          【解决方案4】:

          一种方法是创建一个临时列,例如在“线程”上使用groupbytransform 创建一个名为“first_ts”的临时列,以便在每个线程的“时间戳”列上获取min(即第一个日期)。现在您可以在此列sort_values 和在临时列drop

          # 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)
          

          你得到了预期的结果

          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
          

          或者如果你不想创建列,你也可以使用reindexgroupby的排序值的索引如:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-06-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-11-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多