按特定顺序排序(情境:pandas DataFrame Groupby)

16

我想要更改下面代码给出的订单日期。
我想要的结果是按照(星期一,星期二,星期三,星期四,星期五,星期六,星期日)的顺序排序,是否应该按照预定义的顺序键进行排序?


这是我需要微调的代码:

f8 = df_toy_indoor2.groupby(['device_id', 'day'])['dwell_time'].sum()

print(f8)

当前结果:

device_id                         day
device_112                        Thu     436518
                                  Wed     636451
                                  Fri     770307
                                  Tue     792066
                                  Mon     826862
                                  Sat     953503
                                  Sun    1019298
device_223                        Mon    2534895
                                  Thu    2857429
                                  Tue    3303173
                                  Fri    3548178
                                  Wed    3822616
                                  Sun    4213633
                                  Sat    4475221

期望的结果:

device_id                         day
device_112                        Mon     826862  
                                  Tue     792066
                                  Wed     636451 
                                  Thu     436518
                                  Fri     770307
                                  Sat     953503
                                  Sun    1019298
device_223                        Mon    2534895
                                  Tue    3303173
                                  Wed    3822616
                                  Thu    2857429
                                  Fri    3548178
                                  Sat    4475221
                                  Sun    4213633

这里,type(df_toy_indoor2.groupby(['device_id', 'day'])['dwell_time']) 是一个类 'pandas.core.groupby.SeriesGroupBy'。

我找到了.sort_values(),但它是按值内建的排序函数。
我想得到一些指针来设置一些顺序以进一步进行数据操作。
提前致谢。


不清楚你在问什么,请提供一个 [MCVE]。 - IanS
我认为这很清楚。他希望在打印df时对日期进行排序。 - PdevG
@IanS 我刚刚在我的问题中添加了期望的结果。你明白我的问题吗? - SUNDONG
@Skirrebattie,你还没有看到原始问题 ;) - IanS
4个回答

25
花了我一些时间,但我找到了解决方案。 reindex 可以实现你想要的功能。请看我的代码示例:
a = [1, 2] * 2 + [2, 1] * 3 + [1, 2]
b = ['Mon', 'Wed', 'Thu', 'Fri'] * 3
c = list(range(12))
df = pd.DataFrame(data=[a,b,c]).T
df.columns = ['device', 'day', 'value']
df = df.groupby(['device', 'day']).sum()

提供:

            value
device day       
1      Fri      7
       Mon      0
       Thu     12
       Wed     14
2      Fri     14
       Mon     12
       Thu      6
       Wed      1

然后进行重新索引:

df.reindex(['Mon', 'Wed', 'Thu', 'Fri'], level='day')

更加方便的是(感谢burhan)
df.reindex(list(calendar.day_abbr), level='day')

给出:

            value
device day       
1      Mon      0
       Wed     14
       Thu     12
       Fri      7
2      Mon     12
       Wed      1
       Thu      6
       Fri     14

2
你可以使用calendar模块,并将重新索引更改为:df.reindex(list(calendar.day_abbr), level='day'),以避免明确定义工作日。 - burhan
@Skirrebattie 太棒了!这是我想知道的最简单和最清晰的解决方案。 - SUNDONG
@SUNDONG 很高兴能帮忙,我之前也不知道这个函数,所以我也学到了新东西 :) - PdevG

6

'day'列设置为分类类型,确保在设置类别时,您的日期列表按您想要的方式排序。执行groupby将自动为您排序,但如果您尝试对列进行其他排序,则会按您指定的正确顺序进行排序。

# Initial setup.
np.random.seed([3,1415])
n = 100
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
df = pd.DataFrame({
    'device_id': np.random.randint(1,3,n),
    'day': np.random.choice(days, n),
    'dwell_time':np.random.random(n)
    })


# Set as category, groupby, and sort.
df['day'] = df['day'].astype("category", categories=days, ordered=True)
df = df.groupby(['device_id', 'day']).sum()

更新: astype 不再接受 categories 参数,请使用:

category_day = pd.api.types.CategoricalDtype(categories=days, ordered=True)
df['day'] = df['day'].astype(category_day)

生成的输出结果:
               dwell_time
device_id day            
1         Mon    4.428626
          Tue    3.259319
          Wed    2.436024
          Thu    0.909724
          Fri    4.974137
          Sat    5.583778
          Sun    2.687258
2         Mon    3.117923
          Tue    2.427154
          Wed    1.943927
          Thu    4.599547
          Fri    2.628887
          Sat    6.247520
          Sun    2.716886

请注意,此方法适用于任何类型的自定义排序。例如,如果您有一列条目为'a','b','c',并希望按非标准顺序排序,例如'c','a','b',则只需执行相同类型的过程:将该列指定为分类列,并将您想要的类别按非标准顺序排列。

这样更有意义。不过你需要 ordered=True 吗? - ayhan
1
我从阅读文档中也是这么认为的,但实际上它可以不需要。为了保险起见,我会添加它。另外,在进行一些测试后,groupby 会自动对输出进行排序,因此告诉它对索引进行排序是不必要的。 - root
感谢@root。这也将在预处理dfs和显示结果时有很大帮助。 - SUNDONG

1

可能不是最好的方法,但据我所知,您无法将函数/映射传递给 sort_values。作为解决方法,我通常使用 assign 添加一个新列,并按该列进行排序。在您的示例中,这还需要首先重置索引(并将其设置回来)。

days = {'Mon': 1, 'Tue': 2, 'Wed': 3, 'Thu': 4, 'Fri': 5, 'Sun': 6, 'Sat': 7}
f8 = f8.reset_index()
(f8.assign(day_num=f8['day'].map(days))
   .sort_values(['device_id', 'day_num'])
   .set_index(['device_id', 'day'])
   .drop('day_num', axis=1))
Out: 
                                            0
device_id                        day         
0d4fd55bb363bf6f6f7f8b3342cd0467 Mon   826862
                                 Tue   792066
                                 Wed   636451
                                 Thu   436518
                                 Fri   770307
                                 Sun  1019298
                                 Sat   953503
f6258edf9145d1c0404e6f3d7a27a29d Mon  2534895
                                 Tue  3303173
                                 Wed  3822616
                                 Thu  2857429
                                 Fri  3548178
                                 Sun  4213633
                                 Sat  4475221

1
这就像为此任务添加另一列,完成我的任务,然后将其删除。 感谢您分享有用的技巧,我希望有人能够添加Pandas“按预定义顺序排序键”的库。 - SUNDONG
是的,它正在完全做到这一点。 - ayhan
1
@SUNDONG 我的解决方案按照预定义的顺序进行排序 :) - PdevG

1

如果在groupby之前对数据框进行排序,pandas会保持你的排序顺序。首先,你需要想出一种好的方法来对一个星期中的日子进行排序。其中一种方法是给每行分配表示该星期几的整数,然后按照该列进行排序。例如:

import pandas

df = pandas.DataFrame(
    columns=['device_id', 'day', 'dwell_time'], 
    data=[[1, 'Wed', 35], [1, 'Mon', 63], [2, 'Sat', 83], [2, 'Fri', 82]]
)

df['day_of_week'] = df.apply(
    lambda x: ['Mon', 'Tues', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'].index(x.day), 
    1
)

print(df.sort(['device_id', 'day_of_week']).groupby(['device_id', 'day'])['dwell_time'].sum())

产生:

device_id  day    dwell_time
1          Mon    63
           Wed    35
2          Fri    82
           Sat    83

看起来你的解决方案在groupby之前需要DataFrame排序。排序可以在对值求和后进行 - 我认为这样更快。 - SUNDONG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接