如果列名不同,如何将一个pandas dataframe(pivot表)划分为另一个?

yebdmbv4  于 2023-05-21  发布在  其他
关注(0)|答案(1)|浏览(133)

我想做的是
我正在用pandas计算某些透视表,我想用一个除以另一个。
下面是一个示例(使用虚构的数据),其中我有某些项目的销售数据,我想计算总销售额(以美元为单位)、销售项目总数、单价等。
另一个例子是计算加权平均值,在最后一步中,您需要除以权重。
数据具有多索引,因为它是通过各种分类(例如,分类)进行切片和切割的结果。你在伦敦和纽约卖出了多少高质量和低质量的小部件)。

最小可重复示例

在下面的例子中,我创建了一个尺寸为7 x8的数据框piv_all_sales

  • 7行:2个区域x 3个产品+ 1行用于总计
  • 8列:2个指标(总销售额和净销售额)x(3种质量(低、中、高)+ 1列用于总计)

piv_all_sales看起来像这样:

piv_count计算我卖出了多少件商品,尺寸为7 x4:

我想将前者除以后者--但是DataFrame.div()方法不起作用--大概是因为两个 Dataframe 具有不同的列名。
另一个复杂之处是piv_all_sales有8列,而piv_count有4列

import numpy as np
import pandas as pd

rng = np.random.default_rng(seed=42)
df=pd.DataFrame()
df['region'] = np.repeat(['USA','Canada'],12)
df['product'] = np.tile(['apples','strawberries','bananas'],8)
df['quality'] = np.repeat(['high','medium','low'],8)

df['net sales'] = rng.integers(low=0, high=100, size=24)
df['gross sales'] = rng.integers(low=50, high=150, size=24)
df['# items sold'] = rng.integers(low=1, high=20, size=24)

piv_net_sales = pd.pivot_table(data=df,
                           values=['net sales'],
                           index=['region','product'],
                           columns=['quality'],
                           aggfunc='sum',
                           margins=True)

piv_all_sales = pd.pivot_table(data=df,
                           values=['net sales','gross sales'],
                           index=['region','product'],
                           columns=['quality'],
                           aggfunc='sum',
                           margins=True)

piv_count = pd.pivot_table(data=df,
                           values=['# items sold'],
                           index=['region','product'],
                           columns=['quality'],
                           aggfunc='sum',
                           margins=True)

我所尝试的

我不知道如何将(7 x8) Dataframe 除以(7 x4) Dataframe 。
因此,我开始试图划分一个7 x4的7 x4,即使用dataframe只有净销售额,而不是净和毛在一起。然而,两者都不起作用:

out1 = piv_net_sales / piv_count
out2 = piv_net_sales.div(piv_count)

大概是因为pandas查找但没有找到同名的列?
两者都不起作用,因为两者都产生所有nan的7 x8 Dataframe

部分、低效的解决方案

唯一有效的方法是将每个 Dataframe 转换为一个numpy数组,然后将两个数组分开。这样,列名不同就不再重要了。然而:

  • 这是非常不优雅和乏味的,因为我必须将 Dataframe 转换为numpy数组,然后用正确的索引重新创建 Dataframe
  • 我仍然不知道如何将7 x8 Dataframe 除以7 x4;也许把7 x8分成2个(7 x4)数组,计算每个数组,然后再把它们组合起来?

工作,但不是很优雅,也没有效率
out3 = piv_net_sales.to_numpy()/ piv_count.to_numpy()

类似问题

我发现了一些类似的问题,例如:
How to divide two Pandas Pivoted Tables
但我无法将这些答案应用到我的案子上

8i9zcol2

8i9zcol21#

按照您的方法,如果我理解正确,您可以用途:

out = (
    pd.concat([piv_all_sales, piv_count], axis=1).stack(1)
        .assign(**{"gross sales": lambda x: x["gross sales"].div(x["# items sold"]),
                   "net sales": lambda x: x["net sales"].div(x["# items sold"])})
        .unstack(2)[["gross sales", "net sales"]]
        .reindex_like(piv_all_sales) #to restore back the initial order
)

输出:

print(out)

                    gross sales                    net sales                   
quality                    high   low medium   All      high   low medium   All
region product                                                                 
Canada apples               NaN  9.89   6.70  8.05       NaN  4.44   4.08  4.23
       bananas              NaN 24.62  11.00 19.85       NaN 11.85  10.14 11.25
       strawberries         NaN  7.66   8.80  8.02       NaN  3.56   5.07  4.04
USA    apples             13.15   NaN  35.00 15.33      2.19   NaN   3.00  2.27
       bananas             7.67   NaN   4.79  6.23      6.25   NaN   4.88  5.56
       strawberries       12.61   NaN   9.20 11.26      8.22   NaN   3.47  6.34
All                       11.20 11.56   8.07 10.02      5.38  5.39   4.71  5.11

分步说明:
concat创建如下输出:

.stack(1)取消透视第二个索引(质量:中、高、低)将其从列移动到行:

assign计算字段除以售出的商品数量-注意**表示关键字参数:

unstack重做一个pivot,再次将质量作为列索引

reindex_like按照与初始 Dataframe 相同的顺序排列列

相关问题