【问题标题】:Pandas complex math in groupby+aggregationgroupby+aggregation 中的 Pandas 复杂数学
【发布时间】:2021-02-28 19:41:25
【问题描述】:

我想在聚合时运行一些复杂的数学运算。我写了聚合函数:

import math as mt
# convert calc cols to float from object
cols = dfg_dom_smry.columns
cols = cols[2:]
for col in cols:
    df[col] = df[col].astype(float)

# groupby fields
index = ['PA']

#aggregate
df = dfdom.groupby(index).agg({'newcol1': (mt.sqrt(sum('savings'*'rp')**2))/sum('savings')})

我收到一个错误:TypeError: can't multiply sequence by non-int of type 'str'

这是我的数据的摘录。完整的数据有许多组储蓄和 rp 列。所以理想情况下,我想为每组储蓄和 rp 列运行一个 for 循环

PA   domain          savings           rp 
 M   M-RET-COM       383,895.36      0.14 
 P   P-RET-AG        14,302,804.19   0.16 
 P   P-RET-COM       56,074,119.28   0.33 
 P   P-RET-IND       46,677,610.00   0.27 
 P   P-SBD/NC-AG     1,411,905.00    -   
 P   P-SBD/NC-COM    4,255,891.25    0.36 
 P   P-SBD/NC-IND    295,365.00      -   
 S   S-RET-AG        2,391,504.33    0.72 
 S   S-RET-COM       19,195,073.84   0.18 
 S   S-RET-IND       17,677,708.38   0.13 
 S   S-SBD/NC-COM    6,116,407.07    0.05 
 D   D-RET-COM       11,944,490.39   0.15 
 D   D-RET-IND       1,213,117.63    -   
 D   D-SBD/NC-COM    2,708,153.57    0.69 
 C   C-RET-AG       
 C   C-RET-COM      
 C   C-RET-IND      

对于上述数据,这将是最终结果:

 PA  newcol1
 M   0.143027374757981 
 P   0.18601700701305
 S   0.0979541706738756
 D   0.166192684106493
 C  

感谢您的帮助

【问题讨论】:

  • 斯科特,这就是它的作用:for col in cols: df[col] = df[col].astype(float)。之后我确认节省是 float64 格式。另外,我注释掉了我在下面发布的解决方案的浮点转换,它运行良好。还有其他建议吗?
  • 抛开表达式的可操作性:mt.sqrt(sum('savings'*'rp')**2) 有什么意义?起初,我以为你想要的是平方和。但看起来你想要总和的平方......然后是平方根......如果没有负值,这没有什么意义......我认为你想要的是平方值的总和......
  • 说得对,它是用来处理绝对数字的。这是相对精度的汇总。您希望在计算中包含负节省,因此平方首先会处理负数。
  • 那么为什么不使用abs呢?我认为这会提高可读性。
  • 好问题。我认为这可能是其他原因,因为 (a2 + b2 + c2)^1/2 不等于 a+b+c,即使没有负数。我不是统计学家。会回到我的同事那里,找出答案。

标签: python pandas pandas-groupby aggregate


【解决方案1】:

怎么样

o = dfdom.groupby(index).apply(
    lambda s: pow(pow(s.savings*s.rp, 2).sum(), .5)/(s.savings.sum() or 1)
)

?

上面的s 代表pandas.Series

另外,请注意opandas.Series 的一个实例,这意味着您必须将其转换为pandas.DataFrame,至少要证明您给它的名称是正确的,即df。你可以这样做:

df = o.to_frame('the column name you want')

不同地/参数地
def rollup(df, index, svgs, rp, col_name):
    return df.groupby(index).apply(
        lambda s: pow(pow(s[svgs]*s[rp], 2).sum(), .5)/(s[svgs].sum() or 1)
    ).to_frame(col_name)

# rollup(dfdom, index, 'savings', 'rp', 'svgs_rp')

【讨论】:

  • 谢谢我在我的问题中添加了预期的结果。当分母 s.savings.sum() 为 0 时,您的解决方案是否有效?
  • @Smodi 是的,请参阅最后的编辑,即(s[svgs].sum() or 1) 块。实际上,0 or 1 返回 1。这样的 表达式 称为 Short-Circuit Evaluation
  • 太棒了。刚刚合并到我的代码中,并给出了与我的第一个代码相同的结果。我也不知道战俘。更整洁。谢谢。
  • 如果我不想分组索引,这段代码将如何工作?我试过return df.apply(lambda s: pow(pow((s[svgs]*s[rp]).sum(), 2), .5)/(s[svgs].sum() or 1)).to_frame(col_name)我认为问题是输出不再是一个系列。
  • 仅供参考,我可以这样做:return pd.DataFrame([pow(pow(dfg_pa_smry[svgs]*dfg_pa_smry[rp],2).sum(),.5)/dfg_pa_smry[svgs].sum()], columns=[colname]),但想看看是否也可以使用上述方法。
【解决方案2】:

更新:我用接受的答案中的代码替换了下面的代码。

这就是我最终所做的。我创建了一个函数来逐步完成每个计算,并为每组储蓄和 rp cols 调用该函数。

# the function
def rollup(df, index, svgs, rp):
    df['svgs_rp'] = (df[svgs]*df[rp])**2
    df2 = df.groupby(index).agg({'svgs_rp':'sum',
                                 svgs:'sum'})
    df2['temp'] = np.where((df2[svgs] == 0), '', ((df2['svgs_rp']**(1/2))/df2[svgs]))
    df2 = df2['temp']
    return df2

#calling the function
index = ['PA']

# the next part is within a for loop to go through all the savings and rp column sets. For simplicity I've removed the loop.
svgs = 'savings1' 
rp = 'rp1'
dftemp = rollup(dfdom, index, svgs, rp)
dftemp.rename({'temp': newcol1}, axis=1, inplace=True)
df = pd.merge(df, dftemp, on=index, how = 'left') # this last step was not put in my original question. I've provided so the r-code below makes sense.

烦人的是,我必须先在新列中进行数学运算,然后再进行汇总。这是 R 中的等效代码:

# the function
roll_up <- function(savings,rp){
   return(sqrt(sum((savings*rp)^2,na.rm=T))/sum(savings,na.rm=T))

# calling the function
df <- df[dfdom[,.(newcol1=roll_up(savings1,rp1),
                  newcol2=roll_up(savings2,rp2),...
                  newcol_n=roll_up(savings_n,rp_n)),by='PA'],on='PA']

我对 python 编程比较陌生,这是我能想到的最好的。如果有更好的方法可以做到这一点,请分享。谢谢。

【讨论】:

    【解决方案3】:

    你的 groupby 应该有 () 然后是 [] 像这样:

    df = dfdom.groupby([index]).agg({'newcol1': (mt.sqrt(sum('savings'*'rp')^2))/sum('savings')})
    

    【讨论】:

    • 谢谢我解决了这个问题。还为此更新了我的问题。仍然得到以下错误:TypeError: can't multiply sequence by non-int of type 'str'
    • 对不起,我更改了我的代码,但这里没有。现在变了。更新的错误仍然正确。它不起作用
    • 另外我认为 () 中不需要 []。因为索引是作为列表创建的
    • 是的,当您使用多列进行分组时需要 []。
    猜你喜欢
    • 2020-01-24
    • 1970-01-01
    • 2021-11-07
    • 2016-09-11
    • 2017-03-12
    • 1970-01-01
    • 1970-01-01
    • 2018-10-10
    • 2020-07-28
    相关资源
    最近更新 更多