在Pandas中,元组能否正常作为索引?

2 投票
2 回答
35 浏览
提问于 2025-04-14 18:25

我想在Pandas中使用一个多重索引(MultiIndex),在每一层都有一个嵌套的元组。我知道理论上可以把这些元组拆开,但这样会让代码看起来不太清晰,也挺麻烦的。一般来说,元组里的元素(一个类名和一些参数)只有放在一起才有意义,我想让它们更难组合成没有意义的对,而且这些元组的长度各不相同,我想用MultiIndex.from_product来实现。

在创建数据框(DataFrame)和访问值的时候一切都正常,但在写入数据时,我得到了意想不到的结果。

在一个简单的例子中,下面的代码:

import pandas as pd
index=pd.MultiIndex.from_arrays([[("foo","spam"),("foo","spam")],[("bar","egg"),("bar","egg")],[("baz","bacon"),("pam","bacon")]])
this_index = (("foo","spam"),("bar","egg"),("baz","bacon"))
df = pd.DataFrame(index=index, columns=["value"])
print(df)
print(df.loc[this_index])
df.loc[this_index]=0
# df.loc[this_index,"value"]=0
print(df)

首先打印出我预期的表格(三个元组作为索引,列值为NaN),然后打印出正确获取的值NaN,但最后一行却显示了两个额外的列,分别叫“bar”和“egg”,它们的值都是0:

                                    value  bar  egg
(foo, spam) (bar, egg) (baz, bacon)     0  0.0  0.0
                       (pam, bacon)   NaN  NaN  NaN

在这种情况下,使用注释掉的那行代码进行赋值可以得到预期的结果。

不过,在我的情况下,我需要“spam”、“egg”和“bacon”也作为元组。如果我把上面代码的第2行和第3行改成:

index=pd.MultiIndex.from_arrays([[("foo",("spam",)),("foo",("spam",))],[("bar",("egg",)),("bar",("egg",))],[("baz",("bacon",)),("pam",("bacon",))]])
this_index = (("foo",("spam",)),("bar",("egg",)),("baz",("bacon",)))

我再次得到了前两次打印的预期行为,第三次打印的结果(现在也算是预期的):

                                             value  bar  (egg,)
(foo, (spam,)) (bar, (egg,)) (baz, (bacon,))     0  0.0     0.0
                             (pam, (bacon,))   NaN  NaN     NaN

但是尝试上面提到的同样的解决方法却得到了:

ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 2 dimensions. The detected shape was (3, 2) + inhomogeneous part.

我找不到任何方法来调整这个技巧。

目前我找到的最佳解决方法是对元组使用str(),然后在需要的时候再解析内容,但我觉得应该有更好的办法。我在这里找到的唯一线索是对这个答案的一个未回答的评论。

2 个回答

1

你可以使用 pd.IndexSlice 这个工具:

index=pd.MultiIndex.from_arrays([[("foo",("spam",)),("foo",("spam",))],[("bar",("egg",)),("bar",("egg",))],[("baz",("bacon",)),("pam",("bacon",))]])
this_index = (
    (
        "foo",
        ("spam",),
    ),
    ("bar", ("egg",)),
    ("baz", ("bacon",)),
)
df = pd.DataFrame(index=index, columns=["value"])

df.loc[pd.IndexSlice[this_index], :] = 0  # note the `:` for column selector (this will set 0 for all columns)
print(df)

输出结果是:

                                             value
(foo, (spam,)) (bar, (egg,)) (baz, (bacon,))     0
                             (pam, (bacon,))   NaN
1

如果我理解得没错,你的问题出在这个赋值上:

index=pd.MultiIndex.from_arrays([[("foo",("spam",)),("foo",("spam",))],[("bar",("egg",)),("bar",("egg",))],[("baz",("bacon",)),("pam",("bacon",))]])
this_index = (("foo",("spam",)),("bar",("egg",)),("baz",("bacon",)))

df = pd.DataFrame(index=index, columns=["value"])
df.loc[this_index, 'value']=0

你可以通过使用列表来解决列或索引的问题:

df.loc[this_index, ['value']] = 0

# or
df.loc[[this_index], 'value'] = 0

输出结果:

                                             value
(foo, (spam,)) (bar, (egg,)) (baz, (bacon,))     0
                             (pam, (bacon,))   NaN

撰写回答