如何更新MultiIndex pandas DataFrame的子集

时间:2013-07-09 16:05:26

标签: python pandas

我正在使用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

所以大熊猫不知道如何将新的销售数据加入到旧的数据框中。

我快速刺伤了:

>>> 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)

我不知道那个错误意味着什么,但我觉得我正在用一个小山丘制造一座山。有谁知道这样做的正确方法?

提前致谢, 罗布

2 个答案:

答案 0 :(得分:11)

注意:即将发布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)

另一个选项是使用apply:

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

另一个选项是将“日”级别提升为列,然后使用“应用”。

答案 1 :(得分:6)

详细的多索引说明

您可以使用.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个级别,每个级别都有一个名称(yearflavourday)。这些级别也隐含地给出从外部以0开始的整数位置。因此,year级别可以引用为0flavour引用1day引用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级以外的任何级别进行选择都比较复杂。我们首先选择特定组合,例如年2008bananasat。为此,您将组合作为元组传递给.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

同样,你不必将整个选择包装在paraentheses中以表示一个元组,并且可以简单地执行:

df.loc[[2008, 2009], 'banana', ('sat','sun')]

选择特定级别的所有值。

您可能希望从特定级别中选择所有值。例如,让我们尝试选择所有年份,所有口味和周六。您可能认为以下方法有效:

df.loc[:, :, 'sat']

但是,这会遇到索引器索引错误太多的问题。有三种不同的方法可以从特定级别中选择所有值。

  • 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