在多级熊猫数据框架python中设置值



我最近一直在使用多级数据范围,我发现它们可以大大减少大型数据集的计算时间。例如,考虑简单的数据框架:

df = pd.DataFrame([
        [1, 111, 0], [2, 222, 0], [1, 111, 0],
        [2, 222, 1], [1, 111, 1], [2, 222, 2]
    ], columns=["ID", "A", "B"], index=[1, 1, 2, 2, 3, 3]
)
df.head(6)
    ID   A    B
1   1   111   0
1   2   222   0
2   1   111   0
2   2   222   1
3   1   111   1
3   2   222   2

可以通过ID旋转以创建多级数据框架:

pivot_df = df.pivot(columns="ID")
pivot_df.head()
     A        B
ID   1   2    1   2
1   111 222   0   0
2   111 222   0   1
3   111 222   1   2

以这种格式拥有我的数据的好处是,我可以简单地引用0级列来执行所有ID的"向量"操作:

pivot_df["A"] * (1 + pivot_df["B"])**2
ID  1   2
1   111 222
2   111 888
3   444 999

这些操作对我真的很有帮助!在现实生活中,我的计算要复杂得多,需要以> 1000个ID进行执行。我使用的一个常见数据框架包含10列(在0级(,带有1000个ID(在1级(,带有350行。

我有兴趣弄清楚做两件事:在此枢纽数据框架中更新特定字段的值;为此数据框创建一个新列。像

pivot_df["A"] = pivot_df["A"] * (1 + pivot_df["B"])**2

pivot_df["C"] = pivot_df["A"] * (1 + pivot_df["B"])**2

我执行其中的任何一个时都不会遇到任何错误,但是数据帧保持不变。我也尝试使用.loc和.iloc,但我没有成功。

我认为问题是维护计算出的数据范围的多级结构,但是我对使用多级数据框架的新手是新手,并且不确定如何有效地解决此问题。我有一个笨拙的解决方法,这是不高效的(创建计算出的数据框的字典,然后将它们合并在一起...

df_dict = OrderedDict()
df_dict["A"] = pivot_df["A"]
df_dict["B"] = pivot_df["B"]
df_dict["C"] = pivot_df["A"] * (1 + pivot_df["B"])**2
dfs = [val.T.set_index(np.repeat(key, val.shape[1]), append=True).T for key, val in df_dict.iteritems()]
final_df = reduce(lambda x, y: pd.merge(x, y, left_index=True, right_index=True), dfs)
final_df.columns = final_df.columns.swaplevel(0, 1)

或类似地,

df_dict = OrderedDict()
df_dict["A"] = pivot_df["A"] * (1 + pivot_df["B"])**2
df_dict["B"] = pivot_df["B"]
dfs = [val.T.set_index(np.repeat(key, val.shape[1]), append=True).T for key, val in df_dict.iteritems()]
final_df = reduce(lambda x, y: pd.merge(x, y, left_index=True, right_index=True), dfs)
final_df.columns = final_df.columns.swaplevel(0, 1)

这不一定是笨拙的(我为解决方法感到自豪(,但这肯定不是有效或计算优化的。有人有任何建议吗?

选项1
不要首先旋转!
您说这很方便,因为您可以以新的枢轴形式执行矢量计算。这是一个错误的代表,因为您可以在枢轴之前很容易执行这些计算。

df['C'] = df["A"] * (1 + df["B"]) ** 2
df.pivot(columns='ID')
      A       B       C      
ID    1    2  1  2    1     2
1   111  222  0  0  111   222
2   111  222  0  1  111   888
3   111  222  1  2  444  1998

或在管道的单线中,如果您喜欢

df.assign(C=df.A * (1 + df.B) ** 2).pivot(columns='ID')
      A       B       C      
ID    1    2  1  2    1     2
1   111  222  0  0  111   222
2   111  222  0  1  111   888
3   111  222  1  2  444  1998

选项2
pd.concat
但是要回答您的问题...

pdf = df.pivot(columns='ID')
pd.concat([
        pdf.A, pdf.B, pdf.A * (1 + pdf.B) ** 2
    ], axis=1, keys=['A', 'B', 'C'])
      A       B       C      
ID    1    2  1  2    1     2
1   111  222  0  0  111   222
2   111  222  0  1  111   888
3   111  222  1  2  444  1998

选项3
更多pd.concat
在Concat

之前,将另一个级别添加到列中
pdf = df.pivot(columns='ID')
c = pdf.A * (1 + pdf.B) ** 2
c.columns = [['C'] * len(c.columns), c.columns]
pd.concat([pdf, c], axis=1)
      A       B       C      
ID    1    2  1  2    1     2
1   111  222  0  0  111   222
2   111  222  0  1  111   888
3   111  222  1  2  444  1998

最新更新