Все курсы > Анализ и обработка данных > Занятие 1
Мы начинаем курс анализа и обработки данных. На двух предыдущих курсах мы, во-первых, получили общее представление о том, как устроено машинное обучение, и, во-вторых, приобрели достаточно продвинутые навыки в программировании на Питоне.
Курс анализа данных — это первый курс, в рамках которого мы переходим непосредственно к работе над построением моделей.
Для того чтобы лучше понимать содержание этого курса давайте вначале рассмотрим этапы построения модели.
Задача машинного обучения
Ниже представлена общая последовательность работы над задачей машинного обучения.

Описанный процесс принято называть пайплайном, то есть порядком действий (от англ. pipeline, трубопровод, конвейер), которые необходимо выполнять для построения модели. Подробнее рассмотрим некоторые из этих этапов.
Этап 1. Постановка задачи и определение метрики
Первый этап может показаться тривиальным, однако во многих случах, особенно в задачах классификации, выбор правильной метрики является ключевым для построения качественной модели. Про важность классификационной метрики мы начали говорить в рамках вводного курса и продолжим этот разговор в дальнейшем на гораздо более детальном уровне.
Этап 2. Получение данных
Важный этап. Хотя на этом курсе мы будем использовать уже готовые (зачастую учебные) датасеты, стоит помнить, что получение данных (data gathering) не происходит само собой и во многом от того как и какие данные собраны будет зависеть конечный результат (на который не смогут повлиять ни качественный EDA, ни сложный алгоритм машинного обучения).
Этап 3. Исследовательский анализ данных
В рамках EDA нам нужно решить три основных задачи: описать данные, найти отличия и выявить взаимосвязи. Описание данных позволяет понять, о каких данных идет речь, а также выявить недостатки в данных (с которыми мы справляемся на этапе обработки). Отличия и взаимосвязи в данных — основа для построения модели, это то, за что модель «цепляется», чтобы выдать верный числовой результат, правильную классификацию или кластер.
Для решения этих задач наилучшим образом подходят средства визуализации и описательная статистика. Этим мы займемся во втором разделе.
Отдельно хочется сказать про baseline models, простые модели, которые мы строим в самом начале работы. Они позволяют понять какой результат мы можем получить, не вкладывая дополнительных усилий в работу с данными, а затем отталкиваться от этого результата для обработки данных и построения более сложных моделей.
Базовые модели мы начнем строить на курсе по оптимизации.
Этап 4. Обработка данных
Как уже было сказано, на этапе EDA зачастую становится очевидно, что в данных есть недостатки, которые сильно повляют на качество модели или в целом не позволят ее обучить.
Очистка данных: ошибки и пропуски
Во-первых, в данных могут встречаться ошибки: дубликаты, неверные значения или неподходящий формат данных. Кроме того, данные могут содержать пропуски, и с ними также нужно что-то делать. Этим вопросам посвящен третий раздел курса.
Преобразование данных
Во-вторых, зачастую количественные данные нужно привести к одному масштабу и/или нормальному распределению. Кроме того, числовые признаки могут содержать сильно отличающиеся от остальных данных значения или выбросы, которые также повляют на конечный результат. Категориальные данные необходимо закодировать с помощью чисел. Если категориальные данные выражены строками, это может воспрепятствовать обучению алгоритма.
Преобразование количественных и категориальных данных рассматривается в четвертом разделе.
Конструирование и отбор признаков, понижение размерности
Еще одним важным этапом является конструирование признаков, а также отбор признаков и понижение размерности. В рамках этого курса мы затронем лишь базовые способы конструирования признаков. Более сложные вопросы отбора признаков и понижения размерности мы отложим на потом.
Этап 5. Моделирование и оценка результата
Когда данные готовы, их можно загружать в модель, обучать эту модель и оценивать результат.
Здесь важно сказать, что это итеративный (iterative) или циклический процесс. Многие из описанных выше шагов могут повторяться. В частности, построение модели может привести к необходимости дополнительной обработки данных. Кроме того, разные алгоритмы требуют разной подготовки данных (например, линейные модели требуют масштабирования данных, а для деревьев решений этого не нужно).
При этом прежде чем приступить к анализу и обработке данных важно освоить библиотеку Pandas. Именно этим мы и займемся в начале курса.
Про библиотеку Pandas

Библиотека Pandas — это ключевой инструмент для анализа данных на Питоне. Она позволяет работать с данными, представленными в табличной форме, а также временными рядами. Как вы уже видели, Pandas легко интегрируется с matplotlib, seaborn, sklearn и другими библиотеками.
Кроме того, структурно изучение Pandas можно разделить на два больших раздела: преобразование данных и статистический анализ. В этом разделе (текущее и следующее занятие) мы начнем знакомиться с первой частью, а в следующем (занятия три и четыре) перейдем ко второй.
Откроем ноутбук к этому занятию⧉
Объекты DataFrame и Series
В Pandas есть два основных объекта: DataFrame и Series. Слегка упрощая, можно сказать, что DataFrame — это таблица (и соответственно двумерный массив), а Series — столбец этой таблицы (а значит одномерный массив).

Индекс (index), столбцы (columns) и значения (values) датафрейма мы разберем чуть позднее, а сейчас давайте посмотрим на способы создания датафрейма.
Создание датафрейма
Способ 1. Создание датафрейма из файла
Датафрейм можно создать, импортировав файлы различных форматов. Мы уже делали так с файлами в формате .csv с помощью функции pd.read_csv(). Аналогичным образом можно импортировать файлы и в других форматах, например MS Excel или html. Рассмотрим несколько примеров.
Файл .csv в zip-архиве. Если в zip-архиве содержится только один файл, его можно напрямую подгрузить в Pandas. Вначале скачаем необходимые данные.
После скачивания не забудьте подгрузить данные в сессионное хранилище Google Colab.
1 2 3 |
# испортируем файл из папки content и выведем первые три строки csv_zip = pd.read_csv('/content/train.zip') csv_zip.head(3) |

Как вы наверное узнали, речь идет о датасете «Титаник».
MS Excel. Похожим образом мы можем импортировать файлы в формате Excel.
Теперь используем функцию pd.read_excel() для импорта файла.
1 2 3 |
# импортируем данные в формате Excel, указав номер листа, который хотим использовать excel_data = pd.read_excel('/content/iris.xlsx', sheet_name = 0) excel_data.head(3) |

С датасетом про цветы ириса мы также уже знакомы.
Дополнительно зачему, что файлы можно хранить локально на своем компьютере (если вы используете Jupyter Notebook), импортировать в сессионное хранилище Google Colab или передать в функцию импорта ссылку на файл в Интернете.
Файл в формате html. Данные можно загружать не только из файла, но и брать из Интернета. Для этого подойдет функция read_html(). По большому счету речь идет о примере несложного веб-скрапинга (web scraping), то есть получения информации с веб-страниц в автоматизированном режиме.
Возьмем страницу Википедии про население Земли⧉. В частности предположим, что нас будет интересовать вот эта таблица.

1 2 3 4 5 6 7 |
# передадим соответствующую ссылку в функцию pd.read_html() # в параметре match укажем ключевые слова, которые помогут найти нужную таблицу html_data = pd.read_html('https://en.wikipedia.org/wiki/World_population', match = 'World population') # мы получили пять результатов len(html_data) |
1 |
5 |
Посмотрим на второй полученный результат.
1 |
html_data[1] |

Полученный датафрейм требует форматирования. Подробнее приведенные ниже методы мы будем разбирать в течение занятия.
1 2 3 4 5 6 7 8 9 10 |
# удалим столбец # и строки с индексом 10 и 11 html_data[1].drop(labels = '#', axis = 1, inplace = True) html_data[1].drop(labels = [10, 11], axis = 0, inplace = True) # удалим символы [A] и [B] html_data[1].rename(columns = {'2030[A]' : '2030'}, inplace = True) html_data[1].iloc[0, 0] = 'China' # заново посмотрим на результат html_data[1] |

Примечание. Приведенный выше код работает без ошибок на момент написания статьи. Возможно со временем эта таблица исчезнет со страницы Википедии или будет добавлена новая, в этом случае код нужно будет модифицировать.
Способ 2. Подключение к базе данных SQL
Библиотека Pandas позволяет получать информацию напрямую из базы данных SQL. В качестве примера возьмем популярную учебную базу данных Chinook, в которой содержится информация о магазине, торгующем цифровыми записями (digital media store).
Ниже представлена схема этой реляционной базы данных (relational database schema).

Тема реляционных баз данных и SQL выходит за рамки сегодняшнего занятия, однако для полноты изложения скажу, что такая база данных называется реляционной (от англ. relation — «отношение, зависимость»), потому что ее таблицы взаимосвязаны.
Например, в таблице tracks (записи) в поле GenreId содержится идентификатор жанра. По этому идентификатору мы можем определить, к какому жанру принадлежит та или иная композиция, заглянув в таблицу genres, где каждому GenreId соответствует название определенного жанра.
SQL же расшифровывается как structured query language (язык структурированных запросов) и позволяет достаточно гибко создавать, извлекать и изменять данные из реляционных баз данных.
Попробуем подключить базу данных к нашему ноутбуку и написать очень простой запрос на SQL внутри кода на Питоне. Вначале скачаем базу данных chinook (архив нужно распаковать и подгрузить файл в формате .db).
1 2 3 4 5 6 7 8 9 10 11 |
# импортируем модуль sqlite3 для работы с базой данных SQL import sqlite3 as sql # создадим соединение с базой данных chinook conn = sql.connect('/content/chinook.db') # выберем все строки из таблицы tracks sql_data = pd.read_sql('SELECT * FROM tracks', conn) # vs. read_sql_query # посмотрим на результат sql_data.head(3) |

Позднее в рамках курса мы поговорим еще про два формата: json и pickle.
Способ 3. Создание датафрейма из словаря
Кроме этого, и мы уже много раз так делали, библиотека Pandas позволяет создать датафрейм из словаря с помощью функции pd.DataFrame(). В этом случае ключи словаря будут названиями столбцов, а значения — элементами строк. Последние можно передать с помощью списков или массивов Numpy. Рассмотрим на примере.
Вначале подготовим данные.
1 2 3 4 5 6 |
# создадим несколько списков и массивов Numpy с информацией о семи странах мира country = np.array(['China', 'Vietnam', 'United Kingdom', 'Russia', 'Argentina', 'Bolivia', 'South Africa']) capital = ['Beijing', 'Hanoi', 'London', 'Moscow', 'Buenos Aires', 'Sucre', 'Pretoria'] population = [1400, 97, 67, 144, 45, 12, 59] # млн. человек area = [9.6, 0.3, 0.2, 17.1, 2.8, 1.1, 1.2] # млн. кв. км. sea = [1] * 5 + [0, 1] # выход к морю (в этом списке его нет только у Боливии) |
Теперь создадим пустой словарь и поместим в него наши данные.
1 2 3 4 5 6 7 8 9 10 |
# создадим пустой словарь countries_dict = {} # превратим эти списки в значения словаря, # одновременно снабдив необходимыми ключами countries_dict['country'] = country countries_dict['capital'] = capital countries_dict['population'] = population countries_dict['area'] = area countries_dict['sea'] = sea |
Наконец, создадим датафрейм с помощью функции pd.DataFrame().
1 2 |
countries = pd.DataFrame(countries_dict) countries |

Способ 4. Создание датафрейма из 2D массива Numpy
Помимо этого, датафрейм можно создать из двумерного массива Numpy.
1 2 3 4 5 6 |
# внешнее измерение будет столбцами, внутренее - строками arr = np.array([[1, 1, 1], [2, 2, 2], [3, 3, 3]]) pd.DataFrame(arr) |

Перейдем к структуре и свойствам датафрейма.
Структура и свойства датафрейма
Датафрейм библиотеки Pandas состоит из трех основных компонентов: строк (index), столбцов (columns) и значений (values).
Получить доступ к названиям столбцов можно с помощью атрибута columns.
1 2 |
# выведем столбцы датафрейма countries countries.columns |
1 |
Index(['country', 'capital', 'population', 'area', 'sea'], dtype='object') |
Атрибут index описывает, каким образом идентифицируются строки.
1 2 |
# в нашем случае индекс это числовая последовательность countries.index |
1 |
RangeIndex(start=0, stop=7, step=1) |
С помощью атрибута values мы получаем доступ к значениям датафрейма.
1 2 |
# значения выводятся в формате массива Numpy countries.values |
1 2 3 4 5 6 7 |
array([['China', 'Beijing', 1400, 9.6, 1], ['Vietnam', 'Hanoi', 97, 0.3, 1], ['United Kingdom', 'London', 67, 0.2, 1], ['Russia', 'Moscow', 144, 17.1, 1], ['Argentina', 'Buenos Aires', 45, 2.8, 1], ['Bolivia', 'Sucre', 12, 1.1, 0], ['South Africa', 'Pretoria', 59, 1.2, 1]], dtype=object) |
Описание индекса и названия столбцов мы также можем получить с помощью атрибута axes (множественное от axis, ось) с индексами [0] и [1] соответственно.
1 2 |
# выведем описание индекса датафрейма через атрибут axes[0] countries.axes[0] |
1 |
RangeIndex(start=0, stop=7, step=1) |
1 2 |
# axes[1] выводит названия столбцов countries.axes[1] |
1 |
Index(['country', 'capital', 'population', 'area', 'sea'], dtype='object') |
В целом, полезно запомнить, что axis = 0 позволяет применить какой-либо метод к строкам датафрейма, а axis = 1, соответственно к столбцам.
Мы также можем посмотреть количество измерений, размерность и общее количество элементов датафрейма с помощью атрибутов ndim, shape и size соответственно.
1 |
countries.ndim, countries.shape, countries.size |
1 |
(2, (7, 5), 35) |
Атрибут dtypes выдает тип данных каждого столбца.
1 |
countries.dtypes |
1 2 3 4 5 6 |
country object capital object population int64 area float64 sea int64 dtype: object |
Кроме того, с помощью метода .memory_usage() мы можем посмотреть объем занимаемой памяти по столбцам в байтах.
1 |
countries.memory_usage() |
1 2 3 4 5 6 7 |
Index 128 country 56 capital 56 population 56 area 56 sea 56 dtype: int64 |
Индекс датафрейма
По умолчанию при создании датафрейма библиотека Pandas снабжает его числовым индексом, который начинается с нуля. При этом мы можем присвоить датафрейму собственный индекс.
Присвоение индекса
Более того, этот индекс не обязательно должен быть числовым.
1 2 3 4 5 6 7 8 9 |
# создадим список с кодами стран custom_index = ['CN', 'VN', 'GB', 'RU', 'AR', 'BO', 'ZA'] # создадим датафрейм из словаря и передадим список через параметр index countries = pd.DataFrame(countries_dict, index = custom_index) # посмотрим на результат countries |

Этот индекс можно сбросить с помощью метода .reset_index().
1 2 3 |
# при этом параметр inplace = True сделает изменения постоянными countries.reset_index(inplace = True) countries |

Как мы видим, прошлый индекс стал отдельным столбцом. Снова сделаем этот столбец индексом с помощью метода .set_index().
1 2 3 |
# передадим методу название столбца, который хотим сделать индексом countries.set_index('index', inplace = True) countries |

Еще раз сбросим индекс, но на этот раз не будем делать его отдельным столбцом. Для этого передадим методу .reset_index() параметр drop = True.
1 2 |
countries.reset_index(drop = True, inplace = True) countries |

Собственный индекс можно создать, просто поместив новые значения в атрибут index.
1 2 |
countries.index = custom_index countries |

Многоуровневый индекс
Многоуровневый (MultiIndex) или иерархический (hierarchical) индекс позволяет задать несколько уровней (levels) для индексации строк или столбцов. «Внешний» индекс объединяет несколько элементов «внутреннего» индекса.
Вначале подготовим данные для многоуровневого индекса строк.
1 2 3 4 5 6 7 8 |
# создадим список из кортежей с названием континента и кодом страны rows = [('Asia', 'CN'), ('Asia', 'VN'), ('Europe', 'GB'), ('Europe', 'RU'), ('S. America', 'AR'), ('S. America', 'BO'), ('Africa', 'ZA')] |
Проделаем аналогичную работу для столбцов.
1 2 3 4 5 6 7 |
# в столбцах название страны и столицы мы объединим в категорию names # а размер населения, площадь и выход к морю в data cols = [('names', 'country'), ('names', 'capital'), ('data', 'population'), ('data', 'area'), ('data', 'sea')] |
Теперь создадим иерархический индекс для строк и столбцов с помощью функции pd.MultiIndex.from_tuples().
1 2 3 4 5 6 |
# создадим многоуровневый индекс для строк # индексам присвоим названия через names = ['region', 'code'] custom_multindex = pd.MultiIndex.from_tuples(rows, names = ['region', 'code']) # сделаем то же самое для столбцов custom_multicols = pd.MultiIndex.from_tuples(cols) |
Наконец остается передать эти индексы в атрибуты index и columns и вывести результат.
1 2 3 4 |
countries.index = custom_multindex countries.columns = custom_multicols countries |

Как мы видим страны (code) теперь разбиты на континенты (region), а информация о них — на текстовые (names) и числовые (data) данные.
1 2 3 4 5 6 7 |
# вернемся к обычному индексу и названиям столбцов custom_cols = ['country', 'capital', 'population', 'area', 'sea'] countries.index = custom_index countries.columns = custom_cols countries |

Преобразование в другие форматы
Получившийся датафрейм можно преобразовать в словарь с помощью метода .to_dict().
1 |
print(countries.to_dict()) |
1 |
{'country': {'CN': 'China', 'VN': 'Vietnam', 'GB': 'United Kingdom', 'RU': 'Russia', 'AR': 'Argentina', 'BO': 'Bolivia', 'ZA': 'South Africa'}, 'capital': {'CN': 'Beijing', 'VN': 'Hanoi', 'GB': 'London', 'RU': 'Moscow', 'AR': 'Buenos Aires', 'BO': 'Sucre', 'ZA': 'Pretoria'}, 'population': {'CN': 1400, 'VN': 97, 'GB': 67, 'RU': 144, 'AR': 45, 'BO': 12, 'ZA': 59}, 'area': {'CN': 9.6, 'VN': 0.3, 'GB': 0.2, 'RU': 17.1, 'AR': 2.8, 'BO': 1.1, 'ZA': 1.2}, 'sea': {'CN': 1, 'VN': 1, 'GB': 1, 'RU': 1, 'AR': 1, 'BO': 0, 'ZA': 1}} |
Аналогично, метод .to_numpy() преобразовывает данные в массив Numpy.
1 |
countries.to_numpy() |
1 2 3 4 5 6 7 |
array([['China', 'Beijing', 1400, 9.6, 1], ['Vietnam', 'Hanoi', 97, 0.3, 1], ['United Kingdom', 'London', 67, 0.2, 1], ['Russia', 'Moscow', 144, 17.1, 1], ['Argentina', 'Buenos Aires', 45, 2.8, 1], ['Bolivia', 'Sucre', 12, 1.1, 0], ['South Africa', 'Pretoria', 59, 1.2, 1]], dtype=object) |
Кроме того, мы можем сохранить датафрейм в файл (в Google Colab он появится в сессионном хранилище). Например, это может быть файл в формате .csv и тогда необходимо использовать функцию .to_csv().
1 2 3 |
# по умолчанию, индекс также станет частью .csv файла # параметр index = False позволит этого избежать countries.to_csv('countries.csv', index = False) |

Метод .to_list() позволяет преобразовать объект Series (столбец датафрейма) в список.
1 |
print(countries.country.to_list()) |
1 |
['China', 'Vietnam', 'United Kingdom', 'Russia', 'Argentina', 'Bolivia', 'South Africa'] |
Применить метод .to_list() к датафрейму не получится.
Создание Series
Создание Series из списка
Объект Series можно создать из списка. Возьмем следующий список с названиями стран.
1 |
country_list = ['China', 'South Africa', 'United Kingdom', 'Russia', 'Argentina', 'Vietnam', 'Australia'] |
Передадим его в функцию pd.Series().
1 2 |
country_Series = pd.Series(country_list) country_Series |
1 2 3 4 5 6 7 8 |
0 China 1 South Africa 2 United Kingdom 3 Russia 4 Argentina 5 Vietnam 6 Australia dtype: object |
Обратите внимание, что для объекта Series был автоматически создан числовой индекс. С его помощью мы можем получить доступ к элементам этого объекта.
1 2 |
# например, выведем первый элемент country_Series[0] |
1 |
'China' |
Создание Series из словаря
Помимо списка, объект Series можно создать из словаря. Создадим словарь, в котором коды стран будут ключами, а их названия — значениями.
1 2 3 4 5 6 7 8 |
country_dict = {'CN' : 'China', 'ZA' : 'South Africa', 'GB' : 'United Kingdom', 'RU' : 'Russia', 'AR' : 'Argentina', 'VN' : 'Vietnam', 'AU' : 'Australia' } |
Передадим этот словарь в функцию pd.Series(). Ключи станут индексами, а значения — элементами объекта.
1 2 |
country_Series = pd.Series(country_dict) country_Series |
1 2 3 4 5 6 7 8 |
CN China ZA South Africa GB United Kingdom RU Russia AR Argentina VN Vietnam AU Australia dtype: object |
Доступ к элементам будет осуществляться теперь уже по коду отдельной страны.
1 |
country_Series['AU'] |
1 |
'Australia' |
Доступ к строкам, столбцам и элементам
Поговорим про способы обращения к строкам, столбцам и элементам датафрейма.
Циклы в датафрейме
Цикл for позволяет получить доступ к названиям столбцов датафрейма.
1 2 |
for column in countries: print(column) |
1 2 3 4 5 |
country capital population area sea |
Метод .iterrows() возвращает индекс строки и ее содержимое в формате Series.
1 2 3 4 5 6 7 |
# прервем цикл после первой итерации с помощью цикла for for index, row in countries.iterrows(): print(index) print(row) print('...') print(type(row)) break |
1 2 3 4 5 6 7 8 9 |
CN country China capital Beijing population 1400 area 9.6 sea 1 Name: CN, dtype: object ... <class 'pandas.core.series.Series'> |
Получить доступ к элементам одной строки можно по индексу объекта Series (то есть по названиям столбцов исходного датафрейма).
1 2 3 4 |
for _, row in countries.iterrows(): # например, сформируем вот такое предложение print(row['capital'] + ' is the capital of ' + row['country']) break |
1 |
Beijing is the capital of China |
Доступ к столбцам
В отличие от объекта Series в датафрейме через квадратные скобки мы получаем доступ к его столбцам.
1 2 |
# выведем столбец capital датафрейма countries countries['capital'] |
1 2 3 4 5 6 7 8 |
CN Beijing VN Hanoi GB London RU Moscow AR Buenos Aires BO Sucre ZA Pretoria Name: capital, dtype: object |
Доступ к столбцу можно также получить, указав название датафрейма и через точку название искомого столбца.
1 2 |
# однако в этом случае название не должно содержать пробелов countries.capital |
1 2 3 4 5 6 7 8 |
CN Beijing VN Hanoi GB London RU Moscow AR Buenos Aires BO Sucre ZA Pretoria Name: capital, dtype: object |
Как уже было сказано, отдельные столбцы в датафрейме имеют тип данных Series.
1 |
type(countries.capital) |
1 |
pandas.core.series.Series |
Для того чтобы получить доступ к столбцам и при этом на выходе получить датафрейм необходимо использовать двойные скобки.
1 2 3 |
# логика здесь в том, что внутрениие скобки - это список, # внешние - оператор индексации countries[['capital']] |

Так мы можем получить доступ к нескольким столбцам.
1 |
countries[['capital', 'area']] |

Доступ к столбцам можно также получить с помощью метода .filter(), передав параметру items список с необходимыми нам столбцами.
1 |
countries.filter(items = ['capital', 'population']) |

Доступ к строкам
Получить доступ к строкам можно через срез индекса.
1 2 |
# выведем строки со второй по пятую (не включительно) countries[1:5] |

Методы .loc[] и .iloc[]
Метод .loc[]
Метод .loc[] позволяет получить доступ к строкам и столбцам через их названия (label-based location). Например, выведем первые три строки и первые три столбца датафрейма про страны.
1 2 3 |
# для этого передадим методу .loc[] два списка: # с индексами строк и названиями столбцов countries.loc[['CN', 'RU', 'VN'], ['capital', 'population', 'area']] |
![библиотека Pandas, метод .loc[]](https://www.dmitrymakarov.ru/wp-content/uploads/2022/06/loc_method.jpg)
Через двоеточие, как и в Numpy, мы можем вывести все строки или все столбцы датафрейма.
1 2 |
# например, выведем все строки датафрейма countries.loc[:, ['capital', 'population', 'area']] |
![двоеточие и метод .loc[]](https://www.dmitrymakarov.ru/wp-content/uploads/2022/06/loc_method_all_rows.jpg)
Метод .loc[] также поддерживает значения Boolean.
1 2 3 |
# например, выведем все строки и только последний столбец, # передав список соответствующих логических значений countries.loc[:, [False, False, False, False, True]] |
![логические значения и метод .loc[]](https://www.dmitrymakarov.ru/wp-content/uploads/2022/06/loc_method_boolean.jpg)
Метод .get_loc() позволяет узнать порядковый номер (начиная с нуля) строки или столбца по их индексу и названию соответственно. Для того чтобы узнать порядковый номер строки, метод .get_loc() нужно использовать совместно с атрибутом index.
1 2 |
# выведем номер строки с индексом 'RU' countries.index.get_loc('RU') |
1 |
'3' |
Теперь выведем номер столбца country. Для этого нам понадобится атрибут columns.
1 |
countries.columns.get_loc('country') |
1 |
'0' |
Метод .iloc[]
Метод .iloc[] действует примерно также, как и .loc[], с тем отличием, что он основывается на числовом индексе (integer-based location).
1 2 3 |
# теперь в списки мы передаем номера строк и столбцов, # нумерация начинается с нуля countries.iloc[[0, 3, 5], [0, 1, 2]] |
![библиотека Pandas, метод .iloc[]](https://www.dmitrymakarov.ru/wp-content/uploads/2022/06/iloc_method.jpg)
В методе .iloc[] можно использовать срезы.
1 2 |
# выведем первые три строки и последние два столбца countries.iloc[:3, -2:] |
![метод .iloc[] и срезы](https://www.dmitrymakarov.ru/wp-content/uploads/2022/06/iloc_method_slices.jpg)
К столбцам удобно обращаться по их названиям, к строкам — по порядковому номеру (числовому индексу). Для того чтобы это реализовать, мы можем объединить двойные квадратные скобки и метод .iloc[].
1 2 3 |
# вначале передадим названия столбцов в двойных скобках, # затем номера строк через метод .iloc[] countries[['population', 'area']].iloc[[0, 3]] |
![двойные скобки для столбцов и метод .iloc[] для строк](https://www.dmitrymakarov.ru/wp-content/uploads/2022/06/brackets_iloc.jpg)
Многоуровневый индекс и методы .loc[] и .iloc[]
Поговорим про применение методов .loc[] и .iloc[] к датафрейму с многоуровневым индексом.
1 2 3 4 5 |
# вновь создадим датафрейм с многоуровневым индексом по строкам и столбцам countries.index = custom_multindex countries.columns = custom_multicols countries |
![библиотека Pandas, многоуровневый индекс и методы .loc[] и .iloc[]](https://www.dmitrymakarov.ru/wp-content/uploads/2022/06/MultiIndexDataFrame-1024x638.jpg)
Для доступа к первой строке передадим методу .loc[] соответствующий двойной индекс.
1 |
countries.loc['Asia', 'CN'] |
1 2 3 4 5 6 |
names country China capital Beijing data population 1400 area 9.6 sea 1 Name: (Asia, CN), dtype: object |
Мы также можем передать значения в форме кортежей как для строк, так и для столбцов.
1 2 |
# выведем первую строку и столбцы с числовыми данными countries.loc[('Asia', 'CN'), [('data', 'population'), ('data', 'area'), ('data', 'sea')]] |
1 2 3 4 |
data population 1400.0 area 9.6 sea 1.0 Name: (Asia, CN), dtype: float64 |
Доступ к строкам можно получить, указав внутри кортежа название региона и список с кодами стран.
1 2 |
# например, выведем только азиатские страны countries.loc[('Asia', ['CN', 'VN']), :] |

Внутри кортежа можно указать только регион. В этом случае мы получим все находящиеся в нем страны.
1 |
countries.loc[('Asia'), :] |

Аналогичным образом мы можем получить доступ к столбцам.
1 |
countries.loc[:, [('names', 'country'), ('data', 'population')]] |
![доступ по многоуровневому индексу к столбцам с помощью метода .loc[]](https://www.dmitrymakarov.ru/wp-content/uploads/2022/06/mult_loc_columns.jpg)
Метод .iloc[], при этом, игнорирует структуру иерархического индекса и использует простой числовой индекс.
1 2 |
# получим доступ к четвертой строке и третьему, четвертому и пятому столбцам countries.iloc[3, [2, 3, 4]] |
1 2 3 4 |
data population 144.0 area 17.1 sea 1.0 Name: (Europe, RU), dtype: float64 |
Метод .xs()
Метод .xs() (от англ. cross-section, срез) позволяет получить доступ к определенному уровню иерархического индекса. Начнем со строк.
1 2 3 |
# выберем Европу из уровня region # axis = 0 указывает, что мы берем строки countries.xs('Europe', level = 'region', axis = 0) |

Перейдем к столбцам и выберем names и country (по сути мы выбираем столбец country) на первом и втором уровнях соответственно.
1 2 3 |
# levels указывает, на каких уровнях искать названия столбцов # параметр axis = 1 говорит о том, что мы имеем дело со столбцами countries.xs(('names', 'country'), level = [0, 1], axis = 1) |

Кроме того, мы можем соединить два метода .xs() и одновременно получить доступ и к строкам, и к столбцам.
1 2 3 |
# в данном случае мы можем не указывать level, потому что # Europe и names находятся во внешних индексах, которые в level указаны по умолчанию countries.xs('Europe', axis = 0).xs(('names'), axis = 1) |

Вернем датафрейму одноуровневый индекс.
1 2 3 4 5 6 |
# обновим атрибуты index и columns countries.index = custom_index countries.columns = custom_cols # посмотрим на исходный датафрейм countries |

Метод .at[]
Метод .at[] подходит для извлечения или записи одного значения датафрейма.
1 |
countries.at['CN', 'capital'] |
1 |
'Beijing' |
Фильтры
Логическая маска
Как и в случае с массивом Numpy, мы можем создать логическую маску (Boolean mask) датафрейма, в которой нежелательные значения будут помечены как False.
1 2 |
# создадим логическую маску для стран с населением больше миллиарда человек countries.population > 1000 |
1 2 3 4 5 6 7 8 |
CN True VN False GB False RU False AR False BO False ZA False Name: population, dtype: bool |
Применив эту маску к исходному датафрейму, мы получим только те значения, которые помечены как True.
1 2 |
# применим логическую маску к исходному датафрейму countries[countries.population > 1000] |

Мы можем применить к датафрейму две маски, объединенные, например, логическим оператором И.
1 2 |
# отфильтруем датафрейм по критериям численности населения и площади countries[(countries.population > 50) & (countries.area < 2)] |

Приведу еще один вариант синтаксиса. Его удобно использовать, если у вас есть множество условий, по которым вы хотите отфильтровать датафрейм.
1 2 3 4 5 6 7 8 |
# вначале создаем нужные нам маски population_mask = countries.population > 70 area_mask = countries.population < 50 # затем объединяем их по необходимым условиям (в данном случае ИЛИ) mask = population_mask | area_mask # и применяем маску к исходному датафрейму countries[mask] |

Метод .query()
Метод .query() позволяет задавать условие фильтрации «своими словами».
1 2 3 |
# например, выберем страны с населением более 50 млн. человек И # площадью менее двух млн. кв. километров countries.query('population > 50 and area < 2') |

При фильтрации по строковым значениям (которые записываются в кавычках), необходимо использовать различающиеся кавычки: двойные для метода .query() и одинарные для строкового значения или наоборот.
1 2 |
# выведем данные по Великобритании countries.query("country == 'United Kingdom'") |

Другие способы фильтрации
С помощью метода .isin() мы можем проверить наличие нескольких значений в определенном столбце, а затем использовать результат в качестве логической маски.
1 2 3 4 |
# найдем строки, в которых в столбце capital присутствуют следующие значения keyword_list = ['Beijing', 'Moscow', 'Hanoi'] countries[countries.capital.isin(keyword_list)] |

Похожим образом можно использовать метод .startswith().
1 2 |
# например, для нахождения стран, НЕ начинающихся с буквы "A" countries[ ~ countries.country.str.startswith('A')] |

Метод .nlargest() позволяет найти строки с наибольшим значением в определенном столбце.
1 2 |
# например, возьмем три строки с наибольшими значением по столбцу population countries.nlargest(3, 'population') |

Метод .nsmallest() аналогичным образом находит наименьшие значения.
Метод .argmax() выводит индекс строки, в которой в определенном столбце содержится наибольшее значение.
1 2 |
# например, предположим, что мы хотим найти индекс страны с наибольшей площадью countries.area.argmax() |
1 |
3 |
Посмотрим, какой стране соответствует этот индекс.
1 2 |
# напомню, что двойные скобки позволяют вывести DataFrame, а не Series countries.iloc[[countries.area.argmax()]] |

Метод .argmin() выполняет обратное действие.
Вспомним, что в метод .loc[] можно передать тип данных Boolean. Используем это свойство для создания фильтра.
1 2 |
# выведем страны с населением более 90 млн. человек countries.loc[countries.population > 90, :] |
![метод .loc[] для фильтрации значений в датафрейме библиотеки Pandas](https://www.dmitrymakarov.ru/wp-content/uploads/2022/07/loc_boolean_filter.jpg)
Метод .filter(), если использовать параметр like, ищет совпадения с искомой фразой в индексе (если axis = 0) или названии столбцов (если axis = 1).
1 2 |
# найдем строки, в которых в индексе есть буквосочетание "ZA" countries.filter(like = 'ZA', axis = 0) |

Очевидно, если указать параметр axis = 1, выведется индекс, потому что именно в нем есть буквосочетание ZA.
Сортировка
Для сортировки значений в датафрейме библиотеки Pandas используется метод .sort_values(). Этому методу мы можем передать, среди прочих, следующие параметры:
- by — по какому столбцу или столбцам вести сортировку
- inplace — сохранять ли результат
- ascending — в восходящем или нисходящем порядке сортировать
Рассмотрим два примера.
1 2 3 |
# выполним сортировку по столбцу population, не сохраняя изменений, # в возрастающем порядке (значение по умолчанию) countries.sort_values(by = 'population', inplace = False, ascending = True) |

1 2 |
# теперь отсортируем по двум столбцам в нисходящем порядке countries.sort_values(by = ['area', 'population'], inplace = False, ascending = False) |

Кроме того, можно отсортировать строки по индексу с помощью метода .sort_index().
1 |
countries.sort_index() |

На этом завершим сегодняшнее занятие.
Подведем итог
На первом занятии по анализу и обработке данных мы рассмотрели из каких этапов состоит решение задачи машинного обучения и начали углубленное изучение библиотеки Pandas.
В частности, мы узнали про типы объектов библиотеки Pandas (датафрейм и Series), способы их создания, структуру и свойства. Кроме того, мы рассмотрели возможности доступа к строкам, столбцам и элементам датафрейма.
Вопросы для закрепления
Вопрос. Зачем нужны baseline-модели при решении ML задачи?
Посмотреть правильный ответ
Ответ: слово baseline, в частности, переводится как базовый или исходный уровень и baseline-модель — это своего рода точка отсчета, простая модель, которая позволяет сравнить, насколько нам удается улучшить результат, применяя различные преобразования данных и настраивая алгоритмы.
Вопрос. Чем объект DataFrame отличается от Series?
Посмотреть правильный ответ
Ответ: ключевое отличие в том, что DataFrame — это двумерный массив (даже если в нем только один столбец), Series, соответственно, одномерный.
Методы многих классов (например, класс LinearRegression в Sklearn) требуют на входе именно DataFrame (или двумерный массив Numpy), а не Series.
Вопрос. В чем отличие между методами .loc[] и .iloc[]?
Посмотреть правильный ответ
Ответ: метод .loc[] позволяет найти строки или столбцы датафрейма, основываясь на их названии, метод .iloc[] выполняет ту же функцию, однако на входе принимает числовой индекс.
На следующем занятии мы разберем возможности изменения, соединения и группировки датафреймов.