按索引级别为 Pandas Multiindex DataFrame 分配值

10

我有一个Pandas多级索引数据框,需要从系列中为其中一列分配值。该系列与数据框的第一级索引共享其索引。

import pandas as pd
import numpy as np
idx0 = np.array(['bar', 'bar', 'bar', 'baz', 'foo', 'foo'])
idx1 = np.array(['one', 'two', 'three', 'one', 'one', 'two'])
df = pd.DataFrame(index = [idx0, idx1], columns = ['A', 'B'])
s = pd.Series([True, False, True],index = np.unique(idx0))
print df
print s

输出:

             A    B
bar one    NaN  NaN
    two    NaN  NaN
    three  NaN  NaN
baz one    NaN  NaN
foo one    NaN  NaN
    two    NaN  NaN

bar     True
baz    False
foo     True
dtype: bool

这些不起作用:

df.A = s # does not raise an error, but does nothing
df.loc[s.index,'A'] = s # raises an error

期望输出:

             A     B
bar one    True   NaN
    two    True   NaN
    three  True   NaN
baz one    False  NaN
foo one    True   NaN
    two    True   NaN
3个回答

9

系列(以及字典)可以像函数一样使用map和apply(感谢@normanius改进语法):

df['A'] = pd.Series(df.index.get_level_values(0)).map(s).values

或者类似地:
df['A'] = df.reset_index(level=0)['level_0'].map(s).values

结果:

A    B
bar one     True  NaN
    two     True  NaN
    three   True  NaN
baz one    False  NaN
foo one     True  NaN
    two     True  NaN

我确实想知道这是否是一个错误,如果传递的值具有可以对齐的索引值,则无法正常工作,无论如何+1。 - EdChum
1
我也无法弄清楚使用.loc分配值的语法应该是什么,希望有更好的pandas专家能够出现来回答这个问题。对我来说,这应该可以正常工作,因此必须有一种方法可以在不使用map的情况下完成此操作。 - EdChum
哦,我以为你指的是其他的事情。我认为使用map来做这件事情是一个不错的选择。也可以通过合并来实现,但我觉得那可能会慢一些(但可能更容易阅读)。 - JohnE
2
@JohnE:我建议写成 df['A'] = pd.Series(df.index.get_level_values(0)).map(s).values,这样更健壮,比你的示例更清晰。 - normanius
1
@normanius 谢谢!我甚至不记得回答过这个问题,但完全同意你的评论,并已编辑以包括你的建议。 - JohnE

2

df.A = s 不会引发错误,但也不会有任何作用。

事实上,这应该是有效的。你的观点实际上与我的问题相关。

ᐊᐊ 解决方法 ᐊᐊ

>>> s.index = pd.Index((c,) for c in s.index)  # ᐊᐊᐊᐊᐊᐊᐊᐊ
>>> df.A = s
>>> df
               A    B
bar one     True  NaN
    two     True  NaN
    three   True  NaN
baz one    False  NaN
foo one     True  NaN
    two     True  NaN

为什么上述方法有效?

因为如果你直接使用df.A = s 而没有采用解决方法,实际上你是试图将pandas.Index包含的坐标赋值给一个子类实例,这在某种程度上看起来像是与LS原则相反的“对抗”,也就是说,它是pandas.MultiIndex的一个实例。我的意思是,你可以自己看一下:

>>> type(s.index).__name__
'Index'

>>> type(df.index).__name__
'MultiIndex'

因此,这个解决方法是将s的索引转换为一个一维的pandas.MultiIndex实例。
>>> s.index = pd.Index((c,) for c in s.index)
>>> type(s.index).__name__
'MultiIndex'

目前没有明显的变化。

>>> s
bar     True
baz    False
foo     True
dtype: bool

一种想法: 从许多角度(数学,本体论)来看,所有这些都表明pandas.Index应该被设计为pandas.MultiIndex的子类,而不是当前的相反。


@EdChum 上述解决方法可能会让您了解当前存在的错误类型。 - keepAlive

1

您可以在DataFrame的df上使用join方法,但需要相应地命名索引和系列:

>>> df.index.names = ('lvl0', 'lvl1')
>>> s.index.name = 'lvl0'
>>> s.name = 'new_col'

然后join方法在DataFrame中创建一个新列:

>>> df.join(s)
              A    B  new_col
lvl0 lvl1
bar  one    NaN  NaN     True
     two    NaN  NaN     True
     three  NaN  NaN     True
baz  one    NaN  NaN    False
foo  one    NaN  NaN     True
     two    NaN  NaN     True

将其分配给现有列:
>>> df['A'] = df.join(s)['new_col']
>>> df
                A    B
lvl0 lvl1
bar  one     True  NaN
     two     True  NaN
     three   True  NaN
baz  one    False  NaN
foo  one     True  NaN
     two     True  NaN

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接