В этом Блокноте мы обсудим несколько советов по использованию iloc
для работы с набором данных, содержащим большое количество столбцов. Даже если у вас есть некоторый опыт использования iloc
, следует изучить пару полезных приемов, чтобы ускорить анализ и избежать ввода большого количества имен столбцов в коде.
Оригинал статьи Криса тут
Во многих стандартных примерах, встречающихся в науке о данных, относительно небольшое число столбцов. Например, в наборе данных Titanic
их 8, у Iris
- 4, а у Boston Housing
- 14. Реальные же наборы данных - грязные и часто включают множество дополнительных (потенциально ненужных) столбцов.
В процессе анализа данных вам может потребоваться выбрать подмножество столбцов по следующим причинам:
Описанные ниже приемы помогут сократить время, которое вы тратите на обработку столбцов данных.
Чтобы проиллюстрировать некоторые примеры, я собираюсь использовать необычный набор данных из переписи белок Центрального парка. Да, видимо, в Центральном парке пытались подсчитать и занести в каталог белок. Я подумал, что это будет забавный пример для работы.
Этот набор данных включает 3023 строки данных и 31 столбец. Хотя 31 столбец не является огромным количеством столбцов, это полезный пример для иллюстрации концепций, которые вы можете применить к данным с большим количеством столбцов.
Прим. переводчика: на сайте Центрального парка содержится подробная инструкция по работе с данными. Разберем ее подробно:
В октябре 2018 года с помощью добровольцев-охотников за белками подсчитали количество белок в Центральном парке Нью-Йорка. В результате переписи белок был выпущен отчет. Параметры, включенные в отчет:
X
: координата долготы точки наблюдения за белкойY
: Координата широты точки наблюдения за белкойUnique Squirrel ID
: идентификационный ярлык для каждой обнаруженной белки. Тег состоит из Hectare ID
+ Shift
+ Date
(MMDD) + Hectare Squirrel Number
.Hectare
: ID тег, полученный из сетки гектаров, используемой для разделения и подсчета парковой зоны. Одна ось, которая проходит преимущественно с севера на юг, является числовой (1-42), а ось, которая проходит преимущественно с востока на запад, является алфавитной (A-I).Shift
: значение - AM
или PM
, чтобы указать, когда произошло наблюдение - утром или поздно вечером.Date
: объединение месяца, дня и года наблюдения (MMDDYYYY).Hectare Squirrel Number
: число в хронологической последовательности наблюдений за белками для отдельного наблюдения.Age
: значение Adult
(Взрослый) or Juvenile
(Несовершеннолетний).Primary Fur Color
: Gray
, Cinnamon
или Black
.Highlight Fur Color
: дискретное значение или строковые значения, состоящие из Gray
, Cinnamon
, Black
или White
.Combination of Primary and Highlight Color
: комбинация двух предыдущих столбцов; в этом столбце приведены общие наблюдаемые перестановки основных цветов и оттенков.Color Notes
: иногда наблюдатели добавляли комментарии о состоянии беличьего меха. Location
: Ground Plane
или Above Ground
. Наблюдателям было дано указание отметить, где была белка, когда ее впервые заметили.Above Ground Sighter Measurement
: FALSE
- для наблюдений за белками на плоскости земли.Specific Location
: Иногда наблюдатели добавляли комментарии о местонахождении белки.Running
: была замечена бегущая белка.Chasing
: белка, преследующая другую белку.Climbing
: белка, взбирающаяся на дерево или другой природный объект.Eating
: белка за едой.Foraging
: белка в поисках пищи.OtherActivities
: другая активность белки. Kuks
: веселое голосовое общение, используемое белками по разным причинам.Quaas
: удлиненное голосовое общение, которое может указывать на присутствие наземного хищника, такого как собака.Moans
: высокий голос, который может указывать на присутствие воздушного хищника, такого как ястреб.Tail Flags
: белка, ловящая хвост. Используется для увеличения размера белки и сбивания с толку соперников или хищников. Tail Twitches
: используется белкой для выражения интереса, любопытства.Approaches
: белка, приближающаяся к человеку в поисках еды.Indifferent
: белке было безразлично присутствие человека.Runs From
: белка убегает от людей, считая их угрозой.Other Interactions
: наблюдатель отмечает другие типы взаимодействий между белками и людьми.Уверен, теперь вы узнали много нового о поведении белок!
Давайте начнем с чтения данных:
import pandas as pd
import numpy as np
# прямая ссылка на данные: 'https://data.cityofnewyork.us/api/views/vfnx-vebw/rows.csv?accessType=DOWNLOAD&bom=true&format=true'
# скачал набор на случай изменений в исходном:
df = pd.read_csv('https://raw.githubusercontent.com/dm-fedorov/pandas_basic/master/%D0%B1%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5%20%D0%B2%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%B2%20pandas/data/2018_Central_Park_Squirrel_Census_-_Squirrel_Data.csv')
df.head()
X | Y | Unique Squirrel ID | Hectare | Shift | Date | Hectare Squirrel Number | Age | Primary Fur Color | Highlight Fur Color | ... | Kuks | Quaas | Moans | Tail flags | Tail twitches | Approaches | Indifferent | Runs from | Other Interactions | Lat/Long | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | -73.956134 | 40.794082 | 37F-PM-1014-03 | 37F | PM | 10142018 | 3 | NaN | NaN | NaN | ... | False | False | False | False | False | False | False | False | NaN | POINT (-73.9561344937861 40.7940823884086) |
1 | -73.957044 | 40.794851 | 37E-PM-1006-03 | 37E | PM | 10062018 | 3 | Adult | Gray | Cinnamon | ... | False | False | False | False | False | False | False | True | me | POINT (-73.9570437717691 40.794850940803904) |
2 | -73.976831 | 40.766718 | 2E-AM-1010-03 | 02E | AM | 10102018 | 3 | Adult | Cinnamon | NaN | ... | False | False | False | False | False | False | True | False | NaN | POINT (-73.9768311751004 40.76671780725581) |
3 | -73.975725 | 40.769703 | 5D-PM-1018-05 | 05D | PM | 10182018 | 5 | Juvenile | Gray | NaN | ... | False | False | False | False | False | False | False | True | NaN | POINT (-73.9757249834141 40.7697032606755) |
4 | -73.959313 | 40.797533 | 39B-AM-1018-01 | 39B | AM | 10182018 | 1 | NaN | NaN | NaN | ... | True | False | False | False | False | False | False | False | NaN | POINT (-73.9593126695714 40.797533370163) |
5 rows × 31 columns
Иногда бывает сложно запомнить имена всех столбцов и их индекс.
Вот простое решение:
col_mapping = [f"{c[0]}:{c[1]}" for c in enumerate(df.columns)]
Получился такой список:
col_mapping
['0:X', '1:Y', '2:Unique Squirrel ID', '3:Hectare', '4:Shift', '5:Date', '6:Hectare Squirrel Number', '7:Age', '8:Primary Fur Color', '9:Highlight Fur Color', '10:Combination of Primary and Highlight Color', '11:Color notes', '12:Location', '13:Above Ground Sighter Measurement', '14:Specific Location', '15:Running', '16:Chasing', '17:Climbing', '18:Eating', '19:Foraging', '20:Other Activities', '21:Kuks', '22:Quaas', '23:Moans', '24:Tail flags', '25:Tail twitches', '26:Approaches', '27:Indifferent', '28:Runs from', '29:Other Interactions', '30:Lat/Long']
Основная функция, которую мы рассмотрим, - это iloc
.
Она используется для индексации на основе целых чисел. Поскольку функции iloc
и loc
могут принимать в качестве входных данных логический массив, бывают случаи, когда эти функции производят одинаковый вывод. Однако в рамках этого Блокнота я сосредоточусь только на выборе столбца с помощью iloc
.
Вот простой рисунок, иллюстрирующий основное использование iloc
:
Например, если вы хотите посмотреть столбец данных Unique Squirrel ID
для всех строк:
df.iloc[:, 2]
0 37F-PM-1014-03 1 37E-PM-1006-03 2 2E-AM-1010-03 3 5D-PM-1018-05 4 39B-AM-1018-01 ... 3018 30B-AM-1007-04 3019 19A-PM-1013-05 3020 22D-PM-1012-07 3021 29B-PM-1010-02 3022 5E-PM-1012-01 Name: Unique Squirrel ID, Length: 3023, dtype: object
Посмотреть в дополнение к Unique Squirrel ID
местоположение X
и Y
:
df.iloc[:, [0, 1, 2]]
X | Y | Unique Squirrel ID | |
---|---|---|---|
0 | -73.956134 | 40.794082 | 37F-PM-1014-03 |
1 | -73.957044 | 40.794851 | 37E-PM-1006-03 |
2 | -73.976831 | 40.766718 | 2E-AM-1010-03 |
3 | -73.975725 | 40.769703 | 5D-PM-1018-05 |
4 | -73.959313 | 40.797533 | 39B-AM-1018-01 |
... | ... | ... | ... |
3018 | -73.963943 | 40.790868 | 30B-AM-1007-04 |
3019 | -73.970402 | 40.782560 | 19A-PM-1013-05 |
3020 | -73.966587 | 40.783678 | 22D-PM-1012-07 |
3021 | -73.963994 | 40.789915 | 29B-PM-1010-02 |
3022 | -73.975479 | 40.769640 | 5E-PM-1012-01 |
3023 rows × 3 columns
Ввод всех столбцов не самый эффективный способ, поэтому можем использовать нотацию срезов:
df.iloc[:, 0:3] # df.iloc[:, :3]
X | Y | Unique Squirrel ID | |
---|---|---|---|
0 | -73.956134 | 40.794082 | 37F-PM-1014-03 |
1 | -73.957044 | 40.794851 | 37E-PM-1006-03 |
2 | -73.976831 | 40.766718 | 2E-AM-1010-03 |
3 | -73.975725 | 40.769703 | 5D-PM-1018-05 |
4 | -73.959313 | 40.797533 | 39B-AM-1018-01 |
... | ... | ... | ... |
3018 | -73.963943 | 40.790868 | 30B-AM-1007-04 |
3019 | -73.970402 | 40.782560 | 19A-PM-1013-05 |
3020 | -73.966587 | 40.783678 | 22D-PM-1012-07 |
3021 | -73.963994 | 40.789915 | 29B-PM-1010-02 |
3022 | -73.975479 | 40.769640 | 5E-PM-1012-01 |
3023 rows × 3 columns
Это даст тот же результат, что и выше.
Если хочется объединить список целых чисел с нотацией среза?
Можно попробовать что-то вроде такого:
# произойдет ошибка: invalid syntax
#df.iloc[:, [0:3, 15:19]]
или такого:
# произойдет ошибка: Too many indexers
#df.iloc[:, 0:3,15:19]
Хммм... очевидно, это не работает.
К счастью, есть объект NumPy r_
, который может нам помочь.
Объект r_
"преобразует объекты срезов в конкатенацию по первой оси".
Вот немного более сложный пример, демонстрирующий, как это работает:
np.r_[0:3, 15:19, 24, 25]
array([ 0, 1, 2, 15, 16, 17, 18, 24, 25])
Это круто!
Объект r_
преобразовал комбинацию целочисленных списков и нотации срезов в единый список, который мы можем передать iloc
:
df.iloc[:, np.r_[0:3, 15:19, 24, 25]]
X | Y | Unique Squirrel ID | Running | Chasing | Climbing | Eating | Tail flags | Tail twitches | |
---|---|---|---|---|---|---|---|---|---|
0 | -73.956134 | 40.794082 | 37F-PM-1014-03 | False | False | False | False | False | False |
1 | -73.957044 | 40.794851 | 37E-PM-1006-03 | True | False | False | False | False | False |
2 | -73.976831 | 40.766718 | 2E-AM-1010-03 | False | False | True | False | False | False |
3 | -73.975725 | 40.769703 | 5D-PM-1018-05 | False | False | True | False | False | False |
4 | -73.959313 | 40.797533 | 39B-AM-1018-01 | False | False | False | False | False | False |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
3018 | -73.963943 | 40.790868 | 30B-AM-1007-04 | False | False | False | True | False | False |
3019 | -73.970402 | 40.782560 | 19A-PM-1013-05 | False | False | False | False | False | False |
3020 | -73.966587 | 40.783678 | 22D-PM-1012-07 | False | False | False | True | False | False |
3021 | -73.963994 | 40.789915 | 29B-PM-1010-02 | False | False | False | True | False | False |
3022 | -73.975479 | 40.769640 | 5E-PM-1012-01 | False | False | False | True | False | False |
3023 rows × 9 columns
Вот еще один совет: вы можете использовать эту нотацию при чтении данных с помощью read_csv
:
df_2 = pd.read_csv(
'https://raw.githubusercontent.com/dm-fedorov/pandas_basic/master/%D0%B1%D1%8B%D1%81%D1%82%D1%80%D0%BE%D0%B5%20%D0%B2%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%B2%20pandas/data/2018_Central_Park_Squirrel_Census_-_Squirrel_Data.csv',
usecols=np.r_[1, 2, 5:8, 15:25]
)
df_2.head()
Y | Unique Squirrel ID | Date | Hectare Squirrel Number | Age | Running | Chasing | Climbing | Eating | Foraging | Other Activities | Kuks | Quaas | Moans | Tail flags | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 40.794082 | 37F-PM-1014-03 | 10142018 | 3 | NaN | False | False | False | False | False | NaN | False | False | False | False |
1 | 40.794851 | 37E-PM-1006-03 | 10062018 | 3 | Adult | True | False | False | False | False | NaN | False | False | False | False |
2 | 40.766718 | 2E-AM-1010-03 | 10102018 | 3 | Adult | False | False | True | False | False | NaN | False | False | False | False |
3 | 40.769703 | 5D-PM-1018-05 | 10182018 | 5 | Juvenile | False | False | True | False | False | NaN | False | False | False | False |
4 | 40.797533 | 39B-AM-1018-01 | 10182018 | 1 | NaN | False | False | False | False | False | unknown | True | False | False | False |
Я считаю эту нотацию полезной, когда есть набор данных, в котором вы хотите оставить столбцы и не хотите вводить их полные имена.
Нужно быть осторожным при использовании нотации среза и помнить, что последнее число в диапазоне не включается в сгенерированный список чисел.
Например, если мы укажем диапазон 2:4
, мы получим только список из 2
и 3
:
np.r_[2:4]
array([2, 3])
Если вы хотите включить индекс столбца 4
, используйте np.r_[2:5]
.
У np.r_
есть необязательный аргумент step
.
В следующем примере можем указать, что список будет увеличиваться на 2:
np.r_[2:10:2]
array([2, 4, 6, 8])
Один из наиболее эффективных способов фильтрации столбцов - передать в iloc
логический массив.
Самая важная идея заключается в том, что мы не создаем логический массив вручную, а используем вывод другой функции pandas для генерации массива и передачи его в iloc
.
В данном случае можем использовать метод доступа str
для индекса столбца, как и любой другой столбец данных pandas. Это сгенерирует необходимый логический массив, который ожидает iloc
.
Например, хотим увидеть, название каких столбцов содержит слово run
:
# https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.str.contains.html
run_cols = df.columns.str.contains('run', case=False) # не чувствительный к регистру
run_cols
array([False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False])
Передадим новый массив логических значений в iloc
, чтобы выбрать два столбца:
df.iloc[:, run_cols].head()
Running | Runs from | |
---|---|---|
0 | False | False |
1 | True | True |
2 | False | False |
3 | False | True |
4 | False | False |
На практике чаще используют лямбда-функцию:
df.iloc[:, lambda df:df.columns.str.contains('run',
case=False)].head()
Running | Runs from | |
---|---|---|
0 | False | False |
1 | True | True |
2 | False | False |
3 | False | True |
4 | False | False |
Преимущество в использовании функций str
заключаются в том, что вы можете усложнить работу с потенциальными параметрами фильтрации.
Например, если мы хотим, чтобы все столбцы содержали в названии Color
или Tail
:
df.iloc[:, lambda df: df.columns.str.contains('Color|Tail',
case=False)].head()
Primary Fur Color | Highlight Fur Color | Combination of Primary and Highlight Color | Color notes | Tail flags | Tail twitches | |
---|---|---|---|---|---|---|
0 | NaN | NaN | + | NaN | False | False |
1 | Gray | Cinnamon | Gray+Cinnamon | NaN | False | False |
2 | Cinnamon | NaN | Cinnamon+ | NaN | False | False |
3 | Gray | NaN | Gray+ | NaN | False | False |
4 | NaN | NaN | + | NaN | False | False |
Мы можем объединить все эти концепции вместе, используя результаты логического массива для получения индекса, а затем использовать np.r_
для объединения списков.
Пример ниже можно упростить, используя
filter
.
Вот пример, в котором мы хотим получить все столбцы, связанные с Color
или Tail
, а также Unique Squirrel ID
белки:
color_cols = df.columns.str.contains('Color|Tail', case=False)
color_cols
array([False, False, False, False, False, False, False, False, True, True, True, True, False, False, False, False, False, False, False, False, False, False, False, False, True, True, False, False, False, False, False])
color_indices = [i for i, col in enumerate(color_cols) if col]
color_indices
[8, 9, 10, 11, 24, 25]
df.iloc[:, np.r_[0:3, color_indices]].head()
X | Y | Unique Squirrel ID | Primary Fur Color | Highlight Fur Color | Combination of Primary and Highlight Color | Color notes | Tail flags | Tail twitches | |
---|---|---|---|---|---|---|---|---|---|
0 | -73.956134 | 40.794082 | 37F-PM-1014-03 | NaN | NaN | + | NaN | False | False |
1 | -73.957044 | 40.794851 | 37E-PM-1006-03 | Gray | Cinnamon | Gray+Cinnamon | NaN | False | False |
2 | -73.976831 | 40.766718 | 2E-AM-1010-03 | Cinnamon | NaN | Cinnamon+ | NaN | False | False |
3 | -73.975725 | 40.769703 | 5D-PM-1018-05 | Gray | NaN | Gray+ | NaN | False | False |
4 | -73.959313 | 40.797533 | 39B-AM-1018-01 | NaN | NaN | + | NaN | False | False |
В исходном Блокноте я не включил никакой информации об использовании filter
для выбора столбцов. filter
звучит так, будто его следует использовать для фильтрации данных, а не имен столбцов. К счастью, в pandas вы можете использовать filter
для выбора столбцов!
Если вы хотите выбрать столбцы, в названии которых встречается Color
, то можете использовать следующий код:
df.filter(like='Color')
Primary Fur Color | Highlight Fur Color | Combination of Primary and Highlight Color | Color notes | |
---|---|---|---|---|
0 | NaN | NaN | + | NaN |
1 | Gray | Cinnamon | Gray+Cinnamon | NaN |
2 | Cinnamon | NaN | Cinnamon+ | NaN |
3 | Gray | NaN | Gray+ | NaN |
4 | NaN | NaN | + | NaN |
... | ... | ... | ... | ... |
3018 | Gray | NaN | Gray+ | NaN |
3019 | Gray | White | Gray+White | NaN |
3020 | Gray | Black, Cinnamon, White | Gray+Black, Cinnamon, White | NaN |
3021 | Gray | Cinnamon, White | Gray+Cinnamon, White | NaN |
3022 | Cinnamon | Gray, White | Cinnamon+Gray, White | NaN |
3023 rows × 4 columns
Вы можете использовать регулярное выражение, чтобы найти столбцы, содержащие один или несколько шаблонов:
df.filter(regex='ing|Date')
Date | Running | Chasing | Climbing | Eating | Foraging | |
---|---|---|---|---|---|---|
0 | 10142018 | False | False | False | False | False |
1 | 10062018 | True | False | False | False | False |
2 | 10102018 | False | False | True | False | False |
3 | 10182018 | False | False | True | False | False |
4 | 10182018 | False | False | False | False | False |
... | ... | ... | ... | ... | ... | ... |
3018 | 10072018 | False | False | False | True | True |
3019 | 10132018 | False | False | False | False | True |
3020 | 10122018 | False | False | False | True | True |
3021 | 10102018 | False | False | False | True | False |
3022 | 10122018 | False | False | False | True | True |
3023 rows × 6 columns
Пример, показанный выше, можно более лаконично записать с помощью filter
:
df.filter(regex='Color|Tail')
Primary Fur Color | Highlight Fur Color | Combination of Primary and Highlight Color | Color notes | Tail flags | Tail twitches | |
---|---|---|---|---|---|---|
0 | NaN | NaN | + | NaN | False | False |
1 | Gray | Cinnamon | Gray+Cinnamon | NaN | False | False |
2 | Cinnamon | NaN | Cinnamon+ | NaN | False | False |
3 | Gray | NaN | Gray+ | NaN | False | False |
4 | NaN | NaN | + | NaN | False | False |
... | ... | ... | ... | ... | ... | ... |
3018 | Gray | NaN | Gray+ | NaN | False | False |
3019 | Gray | White | Gray+White | NaN | False | False |
3020 | Gray | Black, Cinnamon, White | Gray+Black, Cinnamon, White | NaN | False | False |
3021 | Gray | Cinnamon, White | Gray+Cinnamon, White | NaN | False | False |
3022 | Cinnamon | Gray, White | Cinnamon+Gray, White | NaN | False | False |
3023 rows × 6 columns
Предостережение: имейте в виду, что при изменении порядка следования столбцов могут возникнуть сложности при обработке данных показанным выше способом.