如何在 MultiIndex Groupby 中高效地执行乘法
Posted
技术标签:
【中文标题】如何在 MultiIndex Groupby 中高效地执行乘法【英文标题】:How to Efficiently Perform Multiplication within MultiIndex Groupby 【发布时间】:2021-06-20 12:25:28 【问题描述】:我正在尝试使用我的两个二级索引来计算第三个索引。但是,我找不到一种惯用的方式来做到这一点。
如何从其他两个二级索引中计算一个二级索引?每个组都有相同的二级索引。
我的代码
import numpy as np
import pandas as pd
# Create index
tickers = ['A', 'B', 'C']
line_items = ['sales', 'ebitda_margin', 'ebitda', 'other_field']
index = pd.MultiIndex.from_product([tickers, line_items], names=['ticker', 'line_item'])
df = pd.DataFrame([100, 0.3, np.nan, 7, 200, 0.2, np.nan, 8, 300, 0.1, np.nan, 9],
index=index,
columns=[0])
# Let's assume 10% sales growth for all companies
# This is included to show that I am doing this calculation for multiple years (columns)
df[1] = df[0]
df.loc[pd.IndexSlice[:, 'sales'], 1] *= 1.1
这会产生以下数据框:
0 1
ticker line_item
A sales 100.0 110.0
ebitda_margin 0.3 0.3
ebitda NaN NaN
other_field 7.0 7.0
B sales 200.0 220.0
ebitda_margin 0.2 0.2
ebitda NaN NaN
other_field 8.0 8.0
C sales 300.0 330.0
ebitda_margin 0.1 0.1
ebitda NaN NaN
other_field 9.0 9.0
我有什么
请注意,我知道我需要对索引进行一些工作才能使以下内容正常工作,但如果存在的话,我宁愿找到更好的方法,而不是使用此代码。
df.apply(lambda x: x.loc[pd.IndexSlice[:, 'sales']] * x.loc[pd.IndexSlice[:, 'ebitda_margin']])
有没有更好的方法来做到这一点?
【问题讨论】:
df.stack().unstack('line_item').eval('ebitda = ebitda_margin * sales').stack().unstack(1)
?
这很好用@piRSquared。有没有办法让它变成多行并用其他东西替换 eval 语句?编辑:我在几行中找到了一种方法。感谢您的洞察力。
是的!我想。请发布您希望最终数据框的外观。另外,不要害怕 pd.DataFrame.eval
这不是你的普通 Python eval
。
我发布了一个引用您评论的答案。再次感谢您,@piRSquared。
【参考方案1】:
尝试xs
作为pd.IndexSlice
的替代品,您可以在其中删除一个级别,然后mul
在相乘时允许级别对齐:
df.loc[pd.IndexSlice[:, 'sales'],:] = (df.loc[pd.IndexSlice[:, 'sales'],:]
.mul(df.xs('ebitda_margin', level='line_item'), level=0)
)
输出:
0
ticker line_item
A sales 30.0
ebitda_margin 0.3
ebitda NaN
other_field 7.0
B sales 40.0
ebitda_margin 0.2
ebitda NaN
other_field 8.0
C sales 30.0
ebitda_margin 0.1
ebitda NaN
other_field 9.0
【讨论】:
有没有办法将 ebitda 设置为 sales * ebitda_margin。我认为这是弄乱了索引,导致设置不正确。 没关系,如果我只是获取值并使用索引切片进行设置,则可以。谢谢!【参考方案2】:df.loc[df.index.get_level_values(1) == "sales"] = (
df.loc[df.index.get_level_values(1) == "sales"].values
* df.loc[df.index.get_level_values(1) == "ebitda_margin"].values
)
print(df)
打印:
0 1
ticker line_item
A sales 30.0 33.0
ebitda_margin 0.3 0.3
ebitda NaN NaN
other_field 7.0 7.0
B sales 40.0 44.0
ebitda_margin 0.2 0.2
ebitda NaN NaN
other_field 8.0 8.0
C sales 30.0 33.0
ebitda_margin 0.1 0.1
ebitda NaN NaN
other_field 9.0 9.0
【讨论】:
有没有办法让我将 ebitda 指数设置为 sales * ebitda_margin? @JackMoody 只需将“sales”更改为“ebidta”即可。试试df.loc[df.index.get_level_values(1) == "ebitda"] = ...the same...
【参考方案3】:
感谢@piRSquared 帮助我解决这个问题。以下为我做了:
piR cmets:
关键是将行项目放入列中以使数学更容易。如果数据框是面向演示的并且很小,那么stack
/unstack
/pivot
可以更方便地进行计算,然后stack
/unstack
/pivot
就足够了。
df_restack = df.stack().unstack('line_item')
df_restack['ebitda'] = df_restack['sales'] * df_restack['ebitda_margin']
df = df_restack.stack().unstack(1)
这会产生以下数据框:
0 1
ticker line_item
A ebitda 30.0 33.0
ebitda_margin 0.3 0.3
other_field 7.0 7.0
sales 100.0 110.0
B ebitda 40.0 44.0
ebitda_margin 0.2 0.2
other_field 8.0 8.0
sales 200.0 220.0
C ebitda 30.0 33.0
ebitda_margin 0.1 0.1
other_field 9.0 9.0
sales 300.0 330.0
我会注意到,当数据框较大时,Quang 和 Andrej 的答案要快得多,所以我选择了 Quang 的答案,因为他首先回答。
【讨论】:
以上是关于如何在 MultiIndex Groupby 中高效地执行乘法的主要内容,如果未能解决你的问题,请参考以下文章
带有 pandas groupby multiindex 的箱线图,用于来自 multiindex 的指定子级别
不同长度的pandas groupby元组-ValueError:在通过级别中找不到值:MultiIndex
Pandas Multiindex 和 Groupby 返回奇怪的行为