Материал объясняет на простом примере принцип работы логической индексации в
pandas
для отбора и фильтрации данных.
Для выполнения примеров загрузим данные:
>>> import pandas as pd url = ( "https://raw.githubusercontent.com/pandas-dev" "/pandas/main/pandas/tests/io/data/csv/tips.csv" ) >>> tips = pd.read_csv(url) >>> tips # total_bill tip sex smoker day time size # 0 16.99 1.01 Female No Sun Dinner 2 # 1 10.34 1.66 Male No Sun Dinner 3 # 2 21.01 3.50 Male No Sun Dinner 3 # 3 23.68 3.31 Male No Sun Dinner 2 # 4 24.59 3.61 Female No Sun Dinner 4 # .. ... ... ... ... ... ... ... # 239 29.03 5.92 Male No Sat Dinner 3 # 240 27.18 2.00 Female Yes Sat Dinner 2 # 241 22.67 2.00 Male Yes Sat Dinner 2 # 242 17.82 1.75 Male No Sat Dinner 2 # 243 18.78 3.00 Female No Thur Dinner 2 # # [244 rows x 7 columns]
Допустим, необходимо из загруженного DataFrame
отобрать все строки, в которых столбец sex
имеет значение "Male"
, т.е. соблюдается условие "sex" == "Male"
. Например:
>>> tips[tips["sex"] == "Male"].head() # total_bill tip sex smoker day time size # 1 10.34 1.66 Male No Sun Dinner 3 # 2 21.01 3.50 Male No Sun Dinner 3 # 3 23.68 3.31 Male No Sun Dinner 2 # 5 25.29 4.71 Male No Sun Dinner 4 # 6 8.77 2.00 Male No Sun Dinner 2
Обратите внимание на индексные метки строк - строка 0 и 4 пропущены. В них столбец sex
имеет значение Female
Приведенный выше оператор tips["sex"] == "Male"
просто передает в DataFrame
серию Series
из логических объектов True
/False
, тем самым отбирая и возвращая все строки с True
.
# смотрим, что выводит выражение >>> tips["sex"] == "Male" # 0 False # 1 True # 2 True # 3 True # 4 False # ... # 239 True # 240 False # 241 True # 242 True # 243 False # Name: sex, Length: 244, dtype: bool >>> type(tips["sex"] == "Male") # <class 'pandas.core.series.Series'>
Можно с уверенностью сказать, что это Series
со значениями bool
, которая имеет те-же индексные метки, что и исходный DataFrame
.
Сохраним полученную серию в переменную, а ее передадим в исходный DataFrame
:
# присваиваем выражение переменной >>> ser = tips["sex"] == "Male" # убедимся, что это серия >>> type(ser) # <class 'pandas.core.series.Series'> # смотрим еще раз, как она выглядит # (первые 5 строк) >>> ser.head() # 0 False # 1 True # 2 True # 3 True # 4 False # Name: sex, dtype: bool
Подсчитаем количество одинаковых значений True
/False
в полученной серии ser
:
>>> ser.value_counts() # sex # True 157 # False 87 # Name: count, dtype: int64
Получили 157 строк со значениями True
Теперь передадим переменную ser
в исходный DataFrame
. В свою очередь DataFrame
выведет те индексные метки строк, которые соответствуют True
в серии ser
. Обратите внимание на индексные метки строк полученного DataFrame
и общее количество записей (157 rows):
>>> tips[ser] # total_bill tip sex smoker day time size new_bill # 1 8.34 1.66 Male No Sun Dinner 3 4.170 # 2 19.01 3.50 Male No Sun Dinner 3 9.505 # 3 21.68 3.31 Male No Sun Dinner 2 10.840 # 5 23.29 4.71 Male No Sun Dinner 4 11.645 # 6 6.77 2.00 Male No Sun Dinner 2 3.385 # .. ... ... ... ... ... ... ... ... # 236 10.60 1.00 Male Yes Sat Dinner 2 5.300 # 237 30.83 1.17 Male Yes Sat Dinner 2 15.415 # 239 27.03 5.92 Male No Sat Dinner 3 13.515 # 241 20.67 2.00 Male Yes Sat Dinner 2 10.335 # 242 15.82 1.75 Male No Sat Dinner 2 7.910 # [157 rows x 8 columns]
А дает она буквально следующие. Если передать оператор отрицания ~
с полученной серией ser
, то получим противоположный результат. То есть массив данных, где столбец sex
имеет значение Female
:
>>> tips[~ser] # total_bill tip sex smoker day time size # 0 16.99 1.01 Female No Sun Dinner 2 # 4 24.59 3.61 Female No Sun Dinner 4 # 11 35.26 5.00 Female No Sun Dinner 4 # 14 14.83 3.02 Female No Sun Dinner 2 # 16 10.33 1.67 Female No Sun Dinner 3 # .. ... ... ... ... ... ... ... # 226 10.09 2.00 Female Yes Fri Lunch 2 # 229 22.12 2.88 Female Yes Sat Dinner 2 # 238 35.83 4.67 Female No Sat Dinner 3 # 240 27.18 2.00 Female Yes Sat Dinner 2 # 243 18.78 3.00 Female No Thur Dinner 2 # # [87 rows x 7 columns]
Обратите внимание на индексные метки строк полученного DataFrame
и общее количество записей (87 rows).
К тому же в pandas
есть много методов, которые на выходе возвращают серии с логической индексацией, а начинающие пользователи не знают что с этим делать.
Например в pandas
есть метод DataFrame.duplicated()
, который возвращает логическую индексацию найденных дубликатов и метод DataFrame.drop_duplicates()
, возвращающий исходный DataFrame
с уже удаленными дубликатами. В таких случаях начинающих пользователей pandas
всегда возникает вопрос: - "А как же посмотреть/вывести на печать эти дубликаты?" На помощь приходит "логическая индексация"
Для примера возьмем следующий DataFrame
:
>>> import pandas as pd df = pd.DataFrame({ 'brand': ['Yum Yum', 'Yum Yum', 'Indomie', 'Indomie', 'Indomie'], 'style': ['cup', 'cup', 'cup', 'pack', 'pack'], 'rating': [4, 4, 3.5, 15, 5] }) >>> df # brand style rating # 0 Yum Yum cup 4.0 # 1 Yum Yum cup 4.0 # 2 Indomie cup 3.5 # 3 Indomie pack 15.0 # 4 Indomie pack 5.0
Метод DataFrame.duplicated()
возвратит Series
со значениями bool
:
>>> df.duplicated() # 0 False # 1 True # 2 False # 3 False # 4 False # dtype: bool
Что бы посмотреть, какие дублирующие строки отбросил метод df.duplicated()
используем логическую индексацию:
>>> df[df.duplicated()] # brand style rating # 1 Yum Yum cup 4.0
Теперь, что бы посмотреть как данные выглядят без дубликатов, необходимо в исходный DataFrame
передать отрицание ~df.duplicated()
или использовать метод DataFrame.drop_duplicates()
:
>>> df[~df.duplicated()] # brand style rating # 0 Yum Yum cup 4.0 # 2 Indomie cup 3.5 # 3 Indomie pack 15.0 # 4 Indomie pack 5.0 # или >>> df.drop_duplicates() # brand style rating # 0 Yum Yum cup 4.0 # 2 Indomie cup 3.5 # 3 Indomie pack 15.0 # 4 Indomie pack 5.0