Применение функции к столбцу MultiIndex pandas.DataFrame

У меня есть 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

но я хотел бы понять, почему такое поведение происходит при выполнении нарезки, подобной диктовке (например, df['L']['Five']), а не при использовании нарезки .loc.

ПРИМЕЧАНИЕ: DataFrame взят из файла HDF, который не был мультииндексирован. Возможно, это причина странного поведения?

EDIT: я использую Pandas v.0.13.1 и NumPy v.1.8.0


person VGonPa    schedule 08.04.2014    source источник


Ответы (1)


df['L']['Five'] выбирает уровень 0 со значением «L» и возвращает DataFrame, который затем выбирает столбец «Пять», возвращая доступную серию.

Средство доступа __getitem__ для Dataframe ([]) попытается сделать правильно и даст вам правильный столбец. Однако это цепная индексация, см. здесь

Чтобы получить доступ к мультииндексу, используйте запись кортежа, ('a','b') и .loc, которая является однозначной, например. df.loc[:,('a','b')]. Кроме того, это позволяет одновременно индексировать несколько осей (например, строки И столбцы).

Итак, почему это не работает, когда вы выполняете цепочку индексации и назначения, например. df['L']['Five'] = value.

df['L'] возвращает фрейм данных с однократной индексацией. Затем происходит другая операция Python df_with_L['Five'], которая выбирает индекс серии с помощью «пяти». Я указал это другой переменной. Поскольку pandas рассматривает эти операции как отдельные события (например, отдельные вызовы __getitem__, поэтому он должен рассматривать их как линейные операции, они происходят одна за другой.

Сравните это с df.loc[:,('L','Five')], который передает вложенный кортеж (:,('L','Five')) одному вызову __getitem__. Это позволяет pandas справляться с этим как с единым объектом (и, к вашему сведению, быть немного быстрее, потому что он может напрямую индексировать фрейм).

Почему это важно? Поскольку цепочечное индексирование состоит из двух вызовов, возможно, что любой вызов может вернуть копию данных из-за того, как они разделены. Таким образом, устанавливая это, вы фактически устанавливаете копию, а не исходный кадр. Панды не могут понять это, потому что это две отдельные операции Python, которые не связаны.

Предупреждение SettingWithCopy является «эвристикой» для обнаружения этого (это означает, что в большинстве случаев оно имеет тенденцию обнаруживать просто легкую проверку). Выяснить это на самом деле очень сложно.

Операция .loc — это отдельная операция python, и, таким образом, она может выбрать срез (который все еще может быть копией), но позволяет pandas назначать этот срез обратно во фрейм после его изменения, таким образом устанавливая значения, как вы думаете.

Причина предупреждения вот в чем. Иногда, когда вы нарезаете массив, вы просто возвращаете представление, а это значит, что вы можете установить его без проблем. Однако даже один типизированный массив может создать копию, если он нарезан определенным образом. DataFrame с несколькими типами (это означает, что он имеет данные с плавающей запятой и объектные данные) почти всегда будет давать копию. Создается ли представление, зависит от расположения памяти массива.

Примечание: это не имеет ничего общего с источником данных.

person Jeff    schedule 08.04.2014