如何使用pandas groupby对一些行降序和一些行升序排序

Posted

技术标签:

【中文标题】如何使用pandas groupby对一些行降序和一些行升序排序【英文标题】:How to use pandas groupby to sort some rows descending and some rows ascending 【发布时间】:2018-11-16 17:56:28 【问题描述】:

我有一个这样的数据框:

import pandas as pd
df = pd.DataFrame(
    [
              ["EUR"          , 0.031        , 61.170000 , "ask" ],
              ["EUR"          , 7.642        , 61.360000 , "ask" ],
              ["EUR"          , 0.266        , 61.370000 , "ask" ],
              ["EUR"          , 0.364        , 61.410000 , "ask" ],
              ["EUR"          , 0.101        , 61.100000 , "bid" ],
              ["EUR"          , 23.090       , 60.470000 , "bid" ],
              ["EUR"          , 2.061        , 60.460000 , "bid" ],
              ["EUR"          , 0.133        , 60.450000 , "bid" ],
              ["USD"          , 3.031        , 161.170000, "ask" ],
              ["USD"          , 10.642       , 161.360000, "ask" ],
              ["USD"          , 3.266        , 161.370000, "ask" ],
              ["USD"          , 3.364        , 161.410000, "ask" ],
              ["USD"          , 3.101        , 161.100000, "bid" ],
              ["USD"          , 26.090       , 160.470000, "bid" ],
              ["USD"          , 5.061        , 160.460000, "bid" ],
              ["USD"          , 3.133        , 160.450000, "bid" ],
    ],
    columns = ["base_currency", "base_volume", "price"  , "type"]
)
df

我想对行重新排序。具体来说,我想将货币(所有“EUR”行放在一起,所有“USD”行放在一起)然后,对于每种货币,我希望“询问”按降序排序,并且我希望“出价”为升序排列。现在,如果 DataFrame 仅包含一种货币,我可以通过以下方式正常工作:

_df = df.groupby("type").apply(lambda row: row.sort_values(by = "price"))
_df

我必须处理多种货币,但是当我尝试按以下方式对它们进行分组时

_df = df.groupby(["base_currency", "type"]).apply(
    lambda row:
        row.sort_values(by = "price") if row['type'] == 'ask' else\
        row.sort_values(by = "price", ascending = False),
    axis = 1
)
_df

我收到以下错误:

TypeError: <lambda>() got an unexpected keyword argument 'axis'

这里可能发生了什么?我该如何解决?

【问题讨论】:

【参考方案1】:

发生的情况是 groupby.apply 没有错误提示的 axis 参数,您可以这样做:

df.groupby(
    ['base_currency', 'type'], group_keys=False
).apply(
    lambda g: g.sort_values('price', ascending=g.name[1] == 'bid')     
    # check the type for each group and sort accordingly
)

#   base_currency  base_volume   price type
#3            EUR        0.364   61.41  ask
#2            EUR        0.266   61.37  ask
#1            EUR        7.642   61.36  ask
#0            EUR        0.031   61.17  ask
#7            EUR        0.133   60.45  bid
#6            EUR        2.061   60.46  bid
#5            EUR       23.090   60.47  bid
#4            EUR        0.101   61.10  bid
#11           USD        3.364  161.41  ask
#10           USD        3.266  161.37  ask
#9            USD       10.642  161.36  ask
#8            USD        3.031  161.17  ask
#15           USD        3.133  160.45  bid
#14           USD        5.061  160.46  bid
#13           USD       26.090  160.47  bid
#12           USD        3.101  161.10  bid

【讨论】:

【参考方案2】:

您可以创建一个辅助列以根据另一个列进行升序或降序排序。

这里我们的辅助列在type == 'ask' 时否定price。然后,当我们按升序sort_values 的默认值)排序时,我们会得到预期的结果,即按降序对这些行进行排序price

import numpy as np

df['price_helper'] = df['price'] * np.where(df['type'] == 'ask', -1, 1)

df_sorted = df.sort_values(['base_currency', 'type', 'price_helper'])\
              .drop('price_helper', 1)

print(df_sorted)

   base_currency  base_volume   price type
3            EUR        0.364   61.41  ask
2            EUR        0.266   61.37  ask
1            EUR        7.642   61.36  ask
0            EUR        0.031   61.17  ask
7            EUR        0.133   60.45  bid
6            EUR        2.061   60.46  bid
5            EUR       23.090   60.47  bid
4            EUR        0.101   61.10  bid
11           USD        3.364  161.41  ask
10           USD        3.266  161.37  ask
9            USD       10.642  161.36  ask
8            USD        3.031  161.17  ask
15           USD        3.133  160.45  bid
14           USD        5.061  160.46  bid
13           USD       26.090  160.47  bid
12           USD        3.101  161.10  bid

性能

基于lambda 的方法和“帮助列”方法的性能相似。在我看来,选择你认为可维护的。

def psi(df):
    return df.groupby(['base_currency', 'type'], group_keys=False)\
             .apply(lambda g: g.sort_values('price', ascending=g.name[1] == 'bid'))

def jp(df):
    df['price_helper'] = df['price'] * np.where(df['type'] == 'ask', -1, 1)
    return df.sort_values(['base_currency', 'type', 'price_helper'])\
             .drop('price_helper', 1)

data = pd.concat([data]*100000, ignore_index=True)

%timeit psi(data)  # 969 ms
%timeit jp(data)   # 899 ms

【讨论】:

以上是关于如何使用pandas groupby对一些行降序和一些行升序排序的主要内容,如果未能解决你的问题,请参考以下文章

pandas 如何使用 groupby 在标签中按日期对列进行分组?

pandas如何对value列数据进行分组groupby?

Pandas groupby:如何在使用两列创建 groupby 时以正确的顺序对工作日进行排序?

如何使用 groupby 调整 pandas 中的小计列?

sql查询降序和升序的问题

如何对不同长度的 Python Pandas groupby 对象进行切片?