11

我正在使用 MultiIndexed pandas DataFrame 并希望将 DataFrame 的子集乘以某个数字。

它与此相同,但具有 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)

我不知道这个错误是什么意思,但我觉得我正在从鼹鼠山中造出一座山。有谁知道这样做的正确方法?

在此先感谢,罗布

4

2 回答 2

12

注意:在即将发布的 0.13中,已向 xs 添加了一个drop_level参数(感谢这个问题!):

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

另一种选择是将“天”级别提升为列,然后使用应用。

于 2013-07-09T16:21:17.160 回答
9

详细的多索引说明

您可以使用.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级别可以引用为0flavourwith1dayas 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 以外的任何级别进行选择都比较复杂。让我们首先选择一个特定的组合,例如2008yearbananasat。为此,您将组合作为元组传递给.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
于 2017-11-19T20:15:33.540 回答