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 варианта:
-
- Мы можем либо использовать .loc, либо
-
- Закодируйте это по-другому, не используя .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]
, но я думаю, что сначала хранить логическое условие в переменной чище.