Python: серия Pandas - зачем использовать loc?

Почему мы используем 'loc' для панелей данных панд? Кажется, следующий код с использованием или без использования loc обоих компиляции и запуска с одновременной скоростью

%timeit df_user1 = df.loc[df.user_id=='5561']

100 loops, best of 3: 11.9 ms per loop

или же

%timeit df_user1_noloc = df[df.user_id=='5561']

100 loops, best of 3: 12 ms per loop

Так зачем использовать loc?

Изменить: это было помечено как дубликат вопроса. Но хотя объяснение панд iloc vs ix vs loc? упоминает что *

Вы можете получить столбец, просто используя getitem фрейма данных:

*

df['time']    # equivalent to df.loc[:, 'time']

он не говорит, почему мы используем loc, хотя он объясняет множество особенностей loc, мой конкретный вопрос: "почему бы просто не пропустить loc вообще"? за что я принял очень подробный ответ ниже.

Кроме того, этот пост (ответ, который я не считаю ответом) очень скрыт в обсуждении, и любому человеку, который ищет то, что я искал, было бы трудно найти информацию, и ответ на него был бы намного лучше. на мой вопрос.

3 ответа

Решение
  • Явное лучше, чем неявное.

    df[boolean_mask] выбирает строки где boolean_mask Верно, но есть угловой случай, когда вы можете не захотеть: когда df имеет логические значения столбцов:

    In [229]: df = pd.DataFrame({True:[1,2,3],False:[3,4,5]}); df
    Out[229]: 
       False  True 
    0      3      1
    1      4      2
    2      5      3
    

    Вы можете использовать df[[True]] выбрать True колонка. Вместо этого это поднимает ValueError:

    In [230]: df[[True]]
    ValueError: Item wrong length 1 instead of 3.
    

    По сравнению с использованием loc:

    In [231]: df.loc[[True]]
    Out[231]: 
       False  True 
    0      3      1
    

    Напротив, следующее не поднимает ValueError хотя структура df2 почти так же, как df1 выше:

    In [258]: df2 = pd.DataFrame({'A':[1,2,3],'B':[3,4,5]}); df2
    Out[258]: 
       A  B
    0  1  3
    1  2  4
    2  3  5
    
    In [259]: df2[['B']]
    Out[259]: 
       B
    0  3
    1  4
    2  5
    

    Таким образом, df[boolean_mask] не всегда ведет себя так же, как df.loc[boolean_mask], Даже если это маловероятно, я бы рекомендовал всегда использовать df.loc[boolean_mask] вместо df[boolean_mask] потому что смысл df.locСинтаксис явно. С df.loc[indexer] вы автоматически знаете, что df.loc выбирает строки. В отличие от этого, не ясно, если df[indexer] выберет строки или столбцы (или поднять ValueErrorне зная подробностей о indexer а также df,

  • df.loc[row_indexer, column_index] Можно выбрать строки и столбцы. df[indexer] можно выбрать только строки или столбцы в зависимости от типа значений в indexer и тип значений столбца df имеет (опять же, они булевы?).

    In [237]: df2.loc[[True,False,True], 'B']
    Out[237]: 
    0    3
    2    5
    Name: B, dtype: int64
    
  • Когда часть передается df.loc конечные точки включены в диапазон. Когда часть передается df[...], срез интерпретируется как полуоткрытый интервал:

    In [239]: df2.loc[1:2]
    Out[239]: 
       A  B
    1  2  4
    2  3  5
    
    In [271]: df2[1:2]
    Out[271]: 
       A  B
    1  2  4
    

Рассмотрение производительности для нескольких столбцов «Сцепленное назначение» с использованием и без использования .loc

Позвольте мне дополнить и без того очень хорошие ответы рассмотрением производительности системы.

Сам вопрос включает в себя сравнение производительности системы (время выполнения) 2-х кусков кода с использованием .loc и без него. Время выполнения примерно одинаково для приведенных примеров кода. Однако для некоторых других примеров кода может быть значительная разница во времени выполнения с использованием и без использования .loc : например, разница в несколько раз или больше!

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

      df[df['mark'] >= 50]['text_rating'] = 'Pass'

Однако этот тип «цепочечного назначения» не работает, поскольку он может создать «копию» вместо «представления», и назначение нового столбца на основе этой «копии» не будет обновлять исходный фрейм данных.

Доступны 2 варианта:

    1. Мы можем либо использовать .loc, либо
    1. Закодируйте это по-другому, не используя .loc

2-й случай, например:

      df['text_rating'][df['mark'] >= 50] = 'Pass'

Поместив фильтрацию в последнюю очередь (после указания нового имени столбца), назначение хорошо работает с обновленным исходным фреймом данных.

Решение с использованием .loc выглядит следующим образом:

      df.loc[df['mark'] >= 50, 'text_rating'] = 'Pass'

Теперь давайте посмотрим на время их выполнения:

Без использования .loc :

      %%timeit 
df['text_rating'][df['mark'] >= 50] = 'Pass'

2.01 ms ± 105 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

С использованием .loc :

      %%timeit 
df.loc[df['mark'] >= 50, 'text_rating'] = 'Pass'

577 µs ± 5.13 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Как мы видим, при использовании .loc время выполнения увеличивается более чем в 3 раза!

Для более подробного объяснения «цепного назначения» вы можете обратиться к другому связанному сообщению . Как бороться с SettingWithCopyWarning в pandas?и, в частности , ответ cs95. Пост отлично объясняет функциональные различия использования .loc. Я просто дополняю здесь разницу в производительности системы (время выполнения).

В дополнение к тому, что уже было сказано (проблемы с наличием True, False в качестве имени столбца без использования loc и возможностью выбора строк и столбцов с помощью loc и возможностью нарезки для выбора строк и столбцов), еще одно большое отличие заключается в том, что вы можете использовать loc для присвоения значений определенным строкам и столбцам. Если вы попытаетесь выбрать подмножество фрейма данных, используя логический ряд, и попытаетесь изменить значение этого выбора подмножества, вы, вероятно, получите предупреждение SettingWithCopy.

Допустим, вы пытаетесь изменить столбец «Высшее руководство» для всех строк, чья зарплата больше 60000.

Этот:

      mask = df["salary"] > 60000
df[mask]["upper management"] = True

выдает предупреждение о том, что «значение пытается быть установлено для копии среза из кадра данных» и не будет работать, потому что df[mask] создает копию и попытка обновить «верхнее управление» этой копии не имеет никакого эффекта на оригинальном дф.

Но это удается:

      mask = df["salary"] > 60000
df.loc[mask,"upper management"] = True

Обратите внимание, что в обоих случаях вы можете сделать df[df["salary"] > 60000]или же df.loc[df["salary"] > 60000], но я думаю, что сначала хранить логическое условие в переменной чище.

Другие вопросы по тегам