如何更新 MultiIndex pandas DataFrame 的子集
Posted
技术标签:
【中文标题】如何更新 MultiIndex pandas DataFrame 的子集【英文标题】:How to update a subset of a MultiIndexed pandas DataFrame 【发布时间】:2013-07-07 08:11:33 【问题描述】:我正在使用 MultiIndexed pandas DataFrame,并希望将 DataFrame 的子集乘以某个数字。
它与 this 相同,但具有 MultiIndex。
>>> d = pd.DataFrame('year':[2008,2008,2008,2008,2009,2009,2009,2009],
'flavour':['strawberry','strawberry','banana','banana',
'strawberry','strawberry','banana','banana'],
'day':['sat','sun','sat','sun','sat','sun','sat','sun'],
'sales':[10,12,22,23,11,13,23,24])
>>> d = d.set_index(['year','flavour','day'])
>>> d
sales
year flavour day
2008 strawberry sat 10
sun 12
banana sat 22
sun 23
2009 strawberry sat 11
sun 13
banana sat 23
sun 24
到目前为止,一切都很好。但是,假设我发现所有星期六的数据都只有应有的一半!我想将所有 sat
销售额乘以 2。
我的第一次尝试是:
sat = d.xs('sat', level='day')
sat = sat * 2
d.update(sat)
但这不起作用,因为变量sat
已经失去了索引的day
级别:
>>> sat
sales
year flavour
2008 strawberry 20
banana 44
2009 strawberry 22
banana 46
所以 pandas 不知道如何将新的销售数据加入旧数据框。
我很快就发现了:
>>> sat = d.xs('sat', level='day', copy=False)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python27\lib\site-packages\pandas\core\frame.py", line 2248, in xs
raise ValueError('Cannot retrieve view (copy=False)')
ValueError: Cannot retrieve view (copy=False)
我不知道这个错误是什么意思,但我觉得我是在鼹鼠山上造山。有谁知道这样做的正确方法?
提前致谢, 抢
【问题讨论】:
对于那些在 MultiIndexing 上寻找更详细解决方案的用户,check this answer below 如果您有兴趣了解更多关于切片和过滤多索引数据帧的信息,请查看我的帖子:How do I slice or filter MultiIndex DataFrame levels?。 【参考方案1】:注意:即将发布 0.13 a drop_level
argument has been added to xs(感谢这个问题!):
In [42]: df.xs('sat', level='day', drop_level=False)
Out[42]:
sales
year flavour day
2008 strawberry sat 10
另一种选择是使用 select(它提取相同数据的子 DataFrame(副本),即它具有相同的索引,因此可以正确更新):
In [11]: d.select(lambda x: x[2] == 'sat') * 2
Out[11]:
sales
year flavour day
2008 strawberry sat 20
banana sat 44
2009 strawberry sat 22
banana sat 46
In [12]: d.update(d.select(lambda x: x[2] == 'sat') * 2)
另一个选项是使用应用:
In [21]: d.apply(lambda x: x*2 if x.name[2] == 'sat' else x, axis=1)
另一个选项是使用get_level_values
(这可能是其中最有效的方法):
In [22]: d[d.index.get_level_values('day') == 'sat'] *= 2
另一个选项是将“天”级别提升为列,然后使用应用。
【讨论】:
是的,这是一个可行的解决方案,但使用lambda
s 处理这样一个(看似)简单的任务感觉不对。 SQL 等效项是UPDATE table SET col = col * 2 WHERE day = 'sat'
。我想知道xs
是否应该包含一个选项来保持选中的索引级别。
@LondonRob 是的(更新是我不太喜欢的东西,lambdas 也不错),我想知道同样的事情,也许值得作为一个问题添加(如果我们'再快一点,下周可能会在 0.12 出)。绝对有一些改进的余地。
作为旁注,似乎使用 MultiIndex 会使一切变得更加困难。我真的无法弄清楚它使什么变得更容易!
哈哈!是的。好吧,那太棒了,但我想开发人员会争先恐后地完成工作,而不是想要添加新功能。
@LondonRob d[d.index.get_level_values('day') == 'sat'] *= 2
我认为更快...【参考方案2】:
多索引详解
您可以使用.loc
索引器从具有MultiIndex 的DataFrame 中选择数据子集。假设我们有原始问题的数据:
sales
year flavour day
2008 strawberry sat 10
sun 12
banana sat 22
sun 23
2009 strawberry sat 11
sun 13
banana sat 23
sun 24
此 DataFrame 在其索引中有 3 个级别,每个级别都有一个名称(year
、flavour
和 day
)。级别也隐式地给出了从外部从 0 开始的整数位置。因此,year
级别可以引用为0
,flavour
与1
,以及day
为2
。
从 0 级选择 - 最外层
级别0
是最容易进行选择的级别。例如,如果我们只想选择 2008 年,我们可以执行以下操作:
df.loc[2008]
sales
flavour day
strawberry sat 10
sun 12
banana sat 22
sun 23
这会降低外部索引级别。如果您想保持外层,您可以将您的选择作为列表(或切片)传递:
df.loc[[2008]] # df.loc[2008:2008] gets the same result
sales
year flavour day
2008 strawberry sat 10
sun 12
banana sat 22
sun 23
从其他级别进行选择
从 0 级以外的任何级别进行选择都比较复杂。让我们首先选择一个特定的组合,例如年份2008
、banana
和sat
。为此,您将组合作为元组传递给.loc
:
df.loc[(2008, 'banana', 'sat')]
sales 22
Name: (2008, banana, sat), dtype: int64
我总是像上面那样使用括号,但 Python 会自动将任何以逗号分隔的值集解释为元组,因此以下将得到相同的结果:
df.loc[2008, 'banana', 'sat']
所有级别都被删除并返回一个系列。我们可以通过在列表中传递元组来保持级别:
df.loc[[(2008, 'banana', 'sat')]]
sales
year flavour day
2008 banana sat 22
从特定级别选择多个值
前面的示例从每个级别中进行了一次选择。可以使用列表来包含您想要的级别的所有值。例如,如果我们想选择 2008 年和 2009 年的所有行,香蕉味的,周六和周日,我们可以执行以下操作:
df.loc[([2008, 2009], 'banana', ('sat','sun'))]
sales
year flavour day
2008 banana sat 22
sun 23
2009 banana sat 23
sun 24
同样,您不必将整个选择括在括号中来表示元组,并且可以简单地这样做:
df.loc[[2008, 2009], 'banana', ('sat','sun')]
从特定级别选择所有值。
您可能希望选择特定级别的所有值。例如,让我们尝试选择所有年份、所有口味和星期六。您可能认为以下方法可行:
df.loc[:, :, 'sat']
但是,这会遇到“索引器过多的 IndexError”。可以通过三种不同的方式从特定级别选择所有值。
df.loc[(slice(None), slice(None), 'sat'), :]
df.loc(axis=0)[:, :, 'sat']
df.loc[pd.IndexSlice[:, :, 'sat'], :]
所有三个都产生以下结果:
sales
year flavour day
2008 strawberry sat 10
banana sat 22
2009 strawberry sat 11
banana sat 23
【讨论】:
df.loc(axis=0)[:, :, 'sat']
哇......好吧。您能否解释或指出有关在loc
中使用轴参数的文档? +1
@ScottBoston 是的,在我 read the advanced indexing docs again 之前我也不知道。你必须从那里往下走一页左右。看起来它是从 2014 年 5 月开始添加的 way back in 0.14
@TedPetrou 你能添加选择器来允许在特定级别设置所有值吗?
我正在尝试df.loc[('2008', 'banana', 'sat'), 'sales']
并收到此错误:KeyError: "Passing list-likes to .loc or [] with any missing labels。缺少以下标签:索引( ['00'], dtype='object', name='master_part_no')。见pandas.pydata.org/pandas-docs/stable/user_guide/…"以上是关于如何更新 MultiIndex pandas DataFrame 的子集的主要内容,如果未能解决你的问题,请参考以下文章
Pandas DataFrame - 如何检索 MultiIndex 级别的特定组合
合并pandas DataFrames时如何保留列MultiIndex值
使用 MultiIndex 时如何在 Pandas 中使用转换器
pandas:将两个 DataFrame 与已排序的 MultiIndex 连接起来,使得结果具有已排序的 MultiIndex