【问题标题】:Pandas pivoted dataframe with the agg() functionPandas 使用 agg() 函数旋转数据框
【发布时间】:2017-12-15 05:14:18
【问题描述】:

假设我有一个表单的旋转数据框

           Value             Qty            Code           
Color       Blue Green  Red Blue Green  Red Blue Green  Red
Date                                                       
2017-07-01   0.0   1.1  0.0  0.0  12.0  0.0    0   abc    0
2017-07-03   2.3   1.3  0.0  3.0   1.0  0.0  cde   abc    0
2017-07-06   0.0   0.0  1.4  0.0   0.0  1.0    0     0  cde

我有兴趣将日期重新采样为每周频率。我想对主列的每个子列执行以下转换,Value: max, Qty: sum, Code = last。在普通的非 MultiIndex 数据帧 df 中,可以通过 agg() 函数执行以下操作。

df.resample('W').agg({"Value":"max", "Qty":"sum", "Code":"last"})

但是当我尝试使用旋转数据框时,它不喜欢键。如果没有明确指定所有子列,在多索引数据帧的情况下我该怎么做?

预期的输出是

           Value             Qty             Code           
Color       Blue Green  Red Blue Green  Red  Blue Green  Red
Date                                       
2017-07-02   0.0   1.1  0.0  0.0  12.0  0.0     0   abc    0
2017-07-09   2.3   1.3  1.4  3.0   1.0  1.0     0     0  cde

要生成上述起始数据帧,请使用以下代码

from collections import OrderedDict
import pandas as pd

table = OrderedDict((
    ("Date", ["2017-07-01", "2017-07-03", "2017-07-03", "2017-07-6"]),
    ('Color',['Green', 'Blue', 'Green', 'Red']),
    ('Value',  [1.1, 2.3, 1.3, 1.4]),
    ('Qty', [12, 3, 1, 1]),
    ('Code',   ['abc', 'cde', 'abc', 'cde'])
))
d = pd.DataFrame(table)
p = d.pivot(index='Date', columns='Color')
p.index = pd.to_datetime(p.index)
p.fillna(0, inplace=True)

编辑:添加了所需的结果。

编辑 2:我还尝试创建一个字典来输入 agg() 函数,但它带有 4 级列标题。

dc = dict(zip(p.columns, map({'Value': 'max', 'Qty': 'sum', 'Code': 'last'}.get, [x[0] for x in p.columns])))

newp = p.resample('W').agg(dc)

【问题讨论】:

  • 你的预期输出是什么?

标签: python pandas pivot aggregate resampling


【解决方案1】:

考虑首先组合分层列并按不同的列类型运行每周聚合:ValueQtyCode

# COMBINE THE LIST OF MULTI-LEVEL COLUMN (LIST OF TUPLES)
p.columns = [i[0]+i[1] for i in p.columns]
p.columns = p.columns.get_level_values(0)

# HORIZONTAL MERGE
out = pd.concat([p.resample('W').max()[[c for c in p.columns if 'Value' in c]],
                 p.resample('W').sum()[[c for c in p.columns if 'Qty' in c]],
                 p.resample('W').last()[[c for c in p.columns if 'Code' in c]]], axis=1)
print(out)
#             ValueBlue  ValueGreen  ValueRed  QtyBlue  QtyGreen  QtyRed  CodeBlue CodeGreen CodeRed
# Date                                                                                              
# 2017-07-02        0.0         1.1       0.0      0.0      12.0     0.0         0       abc       0
# 2017-07-09        2.3         1.3       1.4      3.0       1.0     1.0         0         0     cde

要保留原始分层列,请在展平列级别之前保存列对象,然后在重新采样过程后重新分配回列:

pvtcolumns = p.columns

# ...same code as above

out.columns = pvtcolumns
print(df)

#             Value           Qty             Code           
# Color       Blue Green  Red Blue Green  Red Blue Green  Red
# Date                                                       
# 2017-07-02   0.0   1.1  0.0  0.0  12.0  0.0    0   abc    0
# 2017-07-09   2.3   1.3  1.4  3.0   1.0  1.0    0     0  cde

【讨论】:

  • 感谢您的回答。我给出的示例起始数据帧 (p) 代表了我在多个操作(例如 cumsum、跨主要 x 轴的代数操作)之后所拥有的内容。我不知道如何轻松地取消它。是否可以从上面的 p 开始并连接到您的上述解决方案?
【解决方案2】:

我相信你需要stack() 来避免MultiIndex。似乎没有办法在groupbyresample 对象的agg 方法中指定level=0,所以这是我能弄清楚的唯一方法(如果不是,请告诉我准确):

p.stack().reset_index(level=1).groupby(pd.Grouper(freq='w')).agg({'Value': 'max', 'Qty': 'sum', 'Code': 'last'})

            Qty  Value Code
Date                        
2017-07-02  12.0    1.1    0
2017-07-09   5.0    2.3  code

堆栈会将颜色沿轴 0 带到index,重置索引以将MultiIndex 转换为DateTimeIndex,其余部分非常简单。

编辑

这行得通吗?

dic = {'Value': 'max', 'Qty': 'sum', 'Code': 'last'}
df = pd.DataFrame()
for i in p.columns.get_level_values(0).unique():
    temp = p.xs(i, axis=1, level=0, drop_level=False).resample('W').agg(dic[i])
    df = pd.concat([df, temp], axis=1)
df.columns=p.columns

df
           Value             Qty            Code           
Color       Blue Green  Red Blue Green  Red Blue Green  Red
Date                                                       
2017-07-02   0.0   1.1  0.0  0.0  12.0  0.0    0   abc    0
2017-07-09   2.3   1.3  1.4  3.0   1.0  1.0    0     0  cde

我不知道这种方法是如何“防故障”的,所以要小心。设置df.columns=p.columns 似乎很粗略,但保持多索引一直是主要挑战。如果我在pd.concat() 中设置levels=p.columns.levels(这似乎更安全),它会将索引扁平化为元组,这些元组也可以解压缩为多索引。我用几种不同的方式对此进行了测试,似乎没问题。

【讨论】:

  • 感谢您的尝试。我希望保留相同的列结构,但将索引重新采样为每周频率。本质上, p.resample('W').max() 给出了 Value 列的正确答案。 p.resample('W').sum() 给出了 Qty 列的正确答案。和 p.resample('W').last() 用于 Code 列。我想我可以单独完成所有这些并将正确的列合并在一起,但我希望有一个更通用的方法。
  • 另一种选择是展平 MultiIndex 列并以这种方式执行计算。我尝试了几种不同的方法,但看起来并不干净。
  • 您介意分享一下您是如何展平 MultiIndex 列的吗?我是 MultiIndex 数据帧的新手,我一直在寻找有趣的技术。 :-)
  • 当然,试试这个-df.columns = [' '.join(col).strip() for col in df.columns.values]。归功于安迪·海登-stackoverflow.com/questions/14507794/…
猜你喜欢
  • 2017-04-06
  • 2021-02-16
  • 2022-12-04
  • 1970-01-01
  • 2019-08-02
  • 2020-05-11
  • 2017-11-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多