【问题标题】:How to replicate excel sumproduct function correctly in python?如何在 python 中正确复制 excel sumproduct 函数?
【发布时间】:2023-01-08 08:40:16
【问题描述】:

我有一个如下所示的数据框,我需要计算平均权重。在 Excel 中,如果我使用 sumproduct 函数,我将得到 15.25 的结果。但是,当我使用以下代码时,它给了我 0。如何在代码中更正此问题?

import pandas as pd
df1 = { 'product1':['N/A'],
  'product2':[15.25],
  'p1 weight':[0],
  'p2 weight':[4]}

df1=pd.DataFrame(df1)
df1.fillna(0,inplace=True)


cols_left = [c for c in df1.columns if 'product' in c]
cols_right = [c for c in df1.columns if 'weight' in c]

result = (df1[cols_left] * df1[cols_right]).sum(axis=1) / df1[cols_right].sum(axis=1)
df1['result'] = result

结果如下

请注意,我必须使用 cols_left 和 cols_right 方法,因为在我的实际工作情况下,我有 +100 列需要根据相应的列进行加权平均计算。

我没有像df1['result1'] = (df1['product1'] * df1['p1 weight'] + df1['product2'] * df1['p2 weight'] )/ df1['p1 weight'] + df1['p2 weight'] 那样进行硬编码,而是在和积计算之前将 cols_left 和 cols_right 中的所有相应列分别分组。

非常感谢任何建议。

【问题讨论】:

  • 使用 numpy 和 pandas 有几种方法可以做到这一点。 Numpy 会更干净,但如果你想要一个纯粹的 pandas 解决方案,你可能需要编写更多代码。查看下面我的答案以获取两者的示例。

标签: python pandas numpy


【解决方案1】:

麻木的方法:

以下示例的 df1 在我的答案的最后一部分中定义

解决这个问题的 numpy 方法是先使用 np.nanprod,然后使用 .sum()。这个答案的灵感来自this StackOverflow solution

A = df1.iloc[:,:2].values
B = df1.iloc[:,2:].values

num = np.nanprod(np.dstack((A,B)),2).sum(1)
den = df1.iloc[:,2:].sum(1)

df1['sumproduct'] = num/den
print(df1)
   product1  product2  p1 weight  p2 weight  sumproduct
0       NaN     15.25          0          4       15.25
1      10.0     10.00          2          3       10.00
2       8.0      2.00          5          1        7.00

熊猫方法:

Pandas 的方式可能有点复杂,因为 pandas.DataFrame.dot(这基本上是您想要的总和)不会为您提供具有不同列名的数据框的很大灵活性。但是,您可以将 pandas.groupby 与自定义石斑鱼一起使用来轻松完成产品。

尝试这种受this StackOverflow answer启发的方法-

num_base_cols = 2 #number of columns per group
num_repeat = 2 #number of such groups

col_groups = np.tile(np.arange(num_base_cols), num_repeat) #[0,1,0,1] grouper

num = df1.groupby(col_groups, axis=1).prod().sum(1)
den = df1.iloc[:,2:].sum(1)

df1['sumproduct'] = num/den
print(df1)
   product1  product2  p1 weight  p2 weight  sumproduct
0       NaN     15.25          0          4       15.25
1      10.0     10.00          2          3       10.00
2       8.0      2.00          5          1        7.00

聚苯乙烯- 我将此数据框与一些添加的行一起使用来演示上述解决方案。此外,我使用正确的 np.nan 而不是 NAN 字符串来实现真实的虚拟输入。

import pandas as pd
import numpy as np   #for adding proper Nans instead of strings

## added a few more rows for testing ##
#######################################
df1 = { 'product1':[np.nan,10,8],
        'product2':[15.25,10,2],
        'p1 weight':[0,2,5],
        'p2 weight':[4,3,1]}

df1=pd.DataFrame(df1)
#######################################

【讨论】:

  • 非常感谢 Akshy Sehgal 提供了 pandas 和 numpy 的解决方案。我看到了那个 stackoverflow 答案,但不确定如何将该方法应用到我的代码中,所以非常感谢这个演示。问题是我并不总是每组有 2 列,有时是 1、3、4 甚至每组不同的数字,但组数应该固定,始终为 2(产品和重量)。如果我使用这种方法,我将需要考虑如何使其动态化而不是对每组的列数进行硬编码。
  • 您可以选择正确的列数和组大小来创建石斑鱼。对于您的示例,您需要一个 [0,1,0,1] 石斑鱼 .. 其中带有 0 的列获得 prod(),带有 1 的列获得 prod()。如果你有更多的组(比如 4 个组),你需要有一个看起来像 [0,1,2,3,0,1,2,3] 的石斑鱼,它会相应地表现。为此只需更改num_base_cols = 4
  • 谢谢阿克谢。这种方法对我来说有点先进,你介意给我快速演示一下吗?无论哪种方式,非常感谢您的帮助。
【解决方案2】:

Pandas 支持(并强制执行)数据对齐。当您将一个操作应用于两个数据框时,该操作将应用于具有相同索引(名称)的行和列,而不是在相同的位置。要将操作应用于具有不同名称的一对列,您应该从它们中提取底层的 numpy 数组:

# Clean the NAs
import numpy as np
df1.replace("N/A", np.nan, inplace=True)

(df1[cols_left].fillna(0).values * df1[cols_right].values).sum() / df1[cols_right].sum(1)
#0    15.25

请注意,nan * 0 仍然是 nan。您必须将 nans 转换为有限数字(例如,转换为 0)以获得数字结果。

【讨论】:

  • 谢谢 DYZ,但是这段代码有效吗?我跑了但它给了我错误..
  • 我假设您在第一列中有 nan,而不是 "N/A"。已为您修复。
  • 谢谢,这很完美。我试图弄清楚您的解决方案与我的原始代码有何不同。在我看来,您在 cols_left 和 cols_right 之后添加了 .values,并将 N/A 替换为 np.nan,然后将 fillna 替换为 0。我试图理解这有何不同。但是非常感谢您的解决方案。这很棒。
  • 正确,.values 是关键。
  • .values 是对原始 numpy 数组的引用。 Numpy 列没有名称,它们只有索引。当您将两个 numpy 数组相乘时,第一个数组的第一列乘以另一个数组的第一列。当您将两个 pandas 数据框相乘时,第一个数据框的 A 列乘以第二个数据框的 A 列,无论它们的位置如何。
猜你喜欢
  • 2013-08-05
  • 1970-01-01
  • 2016-10-09
  • 2023-03-31
  • 1970-01-01
  • 2019-05-11
  • 2017-09-26
  • 1970-01-01
  • 2016-11-07
相关资源
最近更新 更多