将函数应用于MultiIndex pandas.DataFrame列

时间:2014-04-08 09:38:55

标签: python pandas apply multi-index

我有一个MultiIndex pandas DataFrame,我想在其中一个列中应用一个函数,并将结果分配给同一列。

In [1]:
    import numpy as np
    import pandas as pd
    cols = ['One', 'Two', 'Three', 'Four', 'Five']
    df = pd.DataFrame(np.array(list('ABCDEFGHIJKLMNO'), dtype='object').reshape(3,5), index = list('ABC'), columns=cols)
    df.to_hdf('/tmp/test.h5', 'df')
    df = pd.read_hdf('/tmp/test.h5', 'df')
    df
Out[1]:
         One     Two     Three  Four    Five
    A    A       B       C      D       E
    B    F       G       H      I       J
    C    K       L       M      N       O
    3 rows × 5 columns

In [2]:
    df.columns = pd.MultiIndex.from_arrays([list('UUULL'), ['One', 'Two', 'Three', 'Four', 'Five']])
    df['L']['Five'] = df['L']['Five'].apply(lambda x: x.lower())
    df
-c:2: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead 
Out[2]:
         U                      L
         One    Two     Three   Four    Five
    A    A      B       C       D       E
    B    F      G       H       I       J
    C    K      L       M       N       O
    3 rows × 5 columns

In [3]:
    df.columns = ['One', 'Two', 'Three', 'Four', 'Five']
    df    
Out[3]:
         One    Two     Three   Four    Five
    A    A      B       C       D       E
    B    F      G       H       I       J
    C    K      L       M       N       O
    3 rows × 5 columns

In [4]:
    df['Five'] = df['Five'].apply(lambda x: x.upper())
    df
Out[4]:
         One    Two     Three   Four    Five
    A    A      B       C       D       E
    B    F      G       H       I       J
    C    K      L       M       N       O
    3 rows × 5 columns

正如您所看到的,该功能未应用于该列,我猜是因为我收到此警告:

-c:2: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead

奇怪的是,这个错误有时只会发生,而我却无法理解它何时发生,何时发生。

我设法应用函数切片数据框.loc作为建议的警告:

In [5]:
    df.columns = pd.MultiIndex.from_arrays([list('UUULL'), ['One', 'Two', 'Three', 'Four', 'Five']])
    df.loc[:,('L','Five')] = df.loc[:,('L','Five')].apply(lambda x: x.lower())
    df

Out[5]:
         U                      L
         One    Two     Three   Four    Five
    A    A      B       C       D       e
    B    F      G       H       I       j
    C    K      L       M       N       o
    3 rows × 5 columns

但是我想理解为什么在进行类似dict的切片(例如df['L']['Five'])而不是使用.loc切片时会发生这种行为。

注意:DataFrame来自一个没有多索引的HDF文件,这可能是造成这种奇怪行为的原因吗?

编辑:我正在使用Pandas v.0.13.1NumPy v.1.8.0

1 个答案:

答案 0 :(得分:6)

df['L']['Five']正在选择等级0,其值为' L'并返回一个DataFrame,然后是列' Five'被选中,返回被访问的系列。

Dataframe的__getitem__访问者([])将尝试做正确的事情,并为您提供正确的列。但是,这是链式索引see here

要访问多索引,请使用明确的元组符号('a','b').loc,例如: df.loc[:,('a','b')]。此外,这允许同时进行多轴索引(例如行和列)。

那么,当你进行链式索引和分配时,为什么这不起作用,例如: df['L']['Five'] = value

df['L']重新生成单索引的数据框。然后另一个python操作df_with_L['Five']通过' Five'选择系列索引。发生。我用另一个变量指出了这个。因为pandas将这些操作视为单独的事件(例如,单独调用__getitem__,所以它必须将它们视为线性操作,它们会一个接一个地发生。

将此与df.loc[:,('L','Five')]对比,后者将(:,('L','Five'))的嵌套元组传递给__getitem__的单个调用。这允许pandas将其作为一个单独的实体处理(并且因为它可以直接索引到帧中,所以fyi会快得多)。

为什么这很重要?由于链式索引是2次调用,因此,由于切片的方式,任一调用都可能返回数据的副本。因此,在设置此项时,您实际上是在设置副本,而不是原始帧。大熊猫不可能弄清楚这一点,因为它们是两个没有连接的独立python操作。

SettingWithCopy警告是一种启发式'检测这个(意味着它往往会捕获大多数情况只是一个轻量级检查)。把它弄清楚是真的很复杂。

.loc操作是单个python操作,因此可以选择切片(仍然可以是副本),但允许pandas在修改后将该切片分配回帧中,从而设置值正如你想的那样。

警告的原因是这样的。有时当您对数组进行切片时,您只需返回一个视图,这意味着您可以设置它没有问题。但是,即使单个 dtyped数组可以生成副本(如果以特定方式切片)。多重数据格式(意味着它说浮点数和对象数据)几乎总是会产生副本。是否创建视图取决于数组的内存布局。

注意:这与数据来源没有任何关系。