如何使用 Pandas 将多索引系列加入单个索引数据框?

Posted

技术标签:

【中文标题】如何使用 Pandas 将多索引系列加入单个索引数据框?【英文标题】:How to join a multi-index series to a single index dataframe with Pandas? 【发布时间】:2018-04-19 02:10:01 【问题描述】:

考虑以下单索引DataFrame:

      energy    fat
1      2000      28
2      1900      17
3      2200      30
4      1750      15
5      1800      18
6      1600      12

我也有一个多索引系列:

1  vitamin-c    0.0004
   vitamin-a    0.0150
2  vitamin-c    0.0030
3  vitamin-d    1.2000
   vitamin-e    1.0007
   vitamin-c    1.2020
4  vitamin-a    0.0780
5  vitamin-b    0.9650
6  vitamin-e    1.9801
   vitamin-c    1.0011

我怎样才能加入这两者,结果看起来像这样:

      energy    fat          vitamins
1      2000      28     vitamin-c    0.0004
                        vitamin-a    0.0150
2      1900      17     vitamin-c    0.0030
3      2200      30     vitamin-d    1.2000
                        vitamin-e    1.0007
                        vitamin-c    1.2020
4      1750      15     vitamin-a    0.0780
5      1800      18     vitamin-b    0.9650
6      1600      12     vitamin-e    1.9801
                        vitamin-c    1.0011

我尝试了df.join(series, how = 'inner'),但得到的只是以下错误消息:

"ValueError: cannot join with no level specified and no overlapping names"

有人可以解释一下我在这里做错了什么以及如何实现两者的结合吗?谢谢!

【问题讨论】:

你能提供一个可重现的例子吗?生成Series 的代码会很有帮助。 【参考方案1】:

源集:

In [96]: s
Out[96]:
id   vitamins
1.0  vitamin-c    0.0004
     vitamin-a    0.0150
2.0  vitamin-c    0.0030
3.0  vitamin-d    1.2000
     vitamin-e    1.0007
     vitamin-c    1.2020
4.0  vitamin-a    0.0780
5.0  vitamin-b    0.9650
6.0  vitamin-e    1.9801
     vitamin-c    1.0011
Name: val, dtype: float64

In [97]: df
Out[97]:
   energy  fat
1    2000   28
2    1900   17
3    2200   30
4    1750   15
5    1800   18
6    1600   12

解决方案:

In [99]: s.reset_index() \
          .merge(df, left_on='id', right_index=True) \
          .set_index(['id','energy','fat','vitamins'])
Out[99]:
                             val
id  energy fat vitamins
1.0 2000   28  vitamin-c  0.0004
               vitamin-a  0.0150
2.0 1900   17  vitamin-c  0.0030
3.0 2200   30  vitamin-d  1.2000
               vitamin-e  1.0007
               vitamin-c  1.2020
4.0 1750   15  vitamin-a  0.0780
5.0 1800   18  vitamin-b  0.9650
6.0 1600   12  vitamin-e  1.9801
               vitamin-c  1.0011

【讨论】:

【参考方案2】:

选项 1 我不建议将不应该存在的内容移到索引中。 也就是说,如果您的索引级别被适当命名,或者它们匹配,那么您可以使用 pd.DataFrame.join,以便 pandas 知道要加入的内容。

df.rename_axis('ord').join(s.rename_axis(['ord', 'vit']).rename('val'))

               energy  fat     val
ord vit                           
1   vitamin-c    2000   28  0.0004
    vitamin-a    2000   28  0.0150
2   vitamin-c    1900   17  0.0030
3   vitamin-d    2200   30  1.2000
    vitamin-e    2200   30  1.0007
    vitamin-c    2200   30  1.2020
4   vitamin-a    1750   15  0.0780
5   vitamin-b    1800   18  0.9650
6   vitamin-e    1600   12  1.9801
    vitamin-c    1600   12  1.0011

多写几行来增加可读性

s = s.rename_axis(['ord', 'vit']).rename('val')
df = df.rename_axis('ord')

df.join(s)

               energy  fat     val
ord vit                           
1   vitamin-c    2000   28  0.0004
    vitamin-a    2000   28  0.0150
2   vitamin-c    1900   17  0.0030
3   vitamin-d    2200   30  1.2000
    vitamin-e    2200   30  1.0007
    vitamin-c    2200   30  1.2020
4   vitamin-a    1750   15  0.0780
5   vitamin-b    1800   18  0.9650
6   vitamin-e    1600   12  1.9801
    vitamin-c    1600   12  1.0011

选项 2 我们还可以将pd.concatlocpd.Index.get_level_values 一起使用

pd.concat(
    [df.loc[s.index.get_level_values(0)].set_index(s.index), s.rename('val')],
    axis=1
)

             energy  fat     val
1 vitamin-c    2000   28  0.0004
  vitamin-a    2000   28  0.0150
2 vitamin-c    1900   17  0.0030
3 vitamin-d    2200   30  1.2000
  vitamin-e    2200   30  1.0007
  vitamin-c    2200   30  1.2020
4 vitamin-a    1750   15  0.0780
5 vitamin-b    1800   18  0.9650
6 vitamin-e    1600   12  1.9801
  vitamin-c    1600   12  1.0011

【讨论】:

这很聪明! rename_axis 是个好方法! 非常感谢大家的回答和@piRSquared 的全面解释。 'rename_axis()' 比 'index.names = ()' 好吗? (安迪·海登建议的解决方案) @solub 它具有相同的目的。不同之处在于使用rename_axis 允许您将索引级别重命名为“内联”。这有助于一些流水线练习,用更少的代码行编写代码,可读性......关键是这是一个主观的调用,完全取决于你。【参考方案3】:

如果您将名称添加到索引/多索引中,您可以使用连接:

In [11]: df
Out[11]:
   energy  fat
n
1    2000   28
2    1900   17
3    2200   30
4    1750   15
5    1800   18
6    1600   12

In [12]: df2
Out[12]:
                val
n vitamin
1 vitamin-c  0.0004
  vitamin-a  0.0150
2 vitamin-c  0.0030
3 vitamin-d  1.2000
  vitamin-e  1.0007
  vitamin-c  1.2020
4 vitamin-a  0.0780
5 vitamin-b  0.9650
6 vitamin-e  1.9801
  vitamin-c  1.0011

In [13]: df.join(df2)
Out[13]:
             energy  fat     val
n vitamin
1 vitamin-c    2000   28  0.0004
  vitamin-a    2000   28  0.0150
2 vitamin-c    1900   17  0.0030
3 vitamin-d    2200   30  1.2000
  vitamin-e    2200   30  1.0007
  vitamin-c    2200   30  1.2020
4 vitamin-a    1750   15  0.0780
5 vitamin-b    1800   18  0.9650
6 vitamin-e    1600   12  1.9801
  vitamin-c    1600   12  1.0011

注意:通过设置 .index.names 来做到这一点:

In [21]: df.index.names = ["n"]  # or .name = "n"

In [22]: df2.index.names = ["n", "vitamin"]

【讨论】:

以上是关于如何使用 Pandas 将多索引系列加入单个索引数据框?的主要内容,如果未能解决你的问题,请参考以下文章

加入/合并两个 Pandas 数据框并将列用作多索引

Pandas 重置系列索引以删除多索引

Pandas 重置系列索引以删除多索引

Pandas:在多索引数据帧中重新索引和插值

pandas:如何使用多索引运行数据透视?

如何将 2 个未对齐的 Pandas 系列相乘并接收具有多索引的产品系列