Что из себя представляет объект series

от admin

Введение в pandas: анализ данных на Python

4 Март 2017 , Python, 679012 просмотров, Introduction to pandas: data analytics in Python

pandas это высокоуровневая Python библиотека для анализа данных. Почему я её называю высокоуровневой, потому что построена она поверх более низкоуровневой библиотеки NumPy (написана на Си), что является большим плюсом в производительности. В экосистеме Python, pandas является наиболее продвинутой и быстроразвивающейся библиотекой для обработки и анализа данных. В своей работе мне приходится пользоваться ею практически каждый день, поэтому я пишу эту краткую заметку для того, чтобы в будущем ссылаться к ней, если вдруг что-то забуду. Также надеюсь, что читателям блога заметка поможет в решении их собственных задач с помощью pandas, и послужит небольшим введением в возможности этой библиотеки.

DataFrame и Series

Чтобы эффективно работать с pandas, необходимо освоить самые главные структуры данных библиотеки: DataFrame и Series. Без понимания что они из себя представляют, невозможно в дальнейшем проводить качественный анализ.

Series

Структура/объект Series представляет из себя объект, похожий на одномерный массив (питоновский список, например), но отличительной его чертой является наличие ассоциированных меток, т.н. индексов, вдоль каждого элемента из списка. Такая особенность превращает его в ассоциативный массив или словарь в Python.

В строковом представлении объекта Series, индекс находится слева, а сам элемент справа. Если индекс явно не задан, то pandas автоматически создаёт RangeIndex от 0 до N-1, где N общее количество элементов. Также стоит обратить, что у Series есть тип хранимых элементов, в нашем случае это int64, т.к. мы передали целочисленные значения.

У объекта Series есть атрибуты через которые можно получить список элементов и индексы, это values и index соответственно.

Доступ к элементам объекта Series возможны по их индексу (вспоминается аналогия со словарем и доступом по ключу).

Индексы можно задавать явно:

Делать выборку по нескольким индексам и осуществлять групповое присваивание:

Фильтровать Series как душе заблагорассудится, а также применять математические операции и многое другое:

Если Series напоминает нам словарь, где ключом является индекс, а значением сам элемент, то можно сделать так:

У объекта Series и его индекса есть атрибут name, задающий имя объекту и индексу соответственно.

Индекс можно поменять «на лету», присвоив список атрибуту index объекта Series

Имейте в виду, что список с индексами по длине должен совпадать с количеством элементов в Series.

DataFrame

Объект DataFrame лучше всего представлять себе в виде обычной таблицы и это правильно, ведь DataFrame является табличной структурой данных. В любой таблице всегда присутствуют строки и столбцы. Столбцами в объекте DataFrame выступают объекты Series, строки которых являются их непосредственными элементами.

DataFrame проще всего сконструировать на примере питоновского словаря:

Чтобы убедиться, что столбец в DataFrame это Series, извлекаем любой:

Объект DataFrame имеет 2 индекса: по строкам и по столбцам. Если индекс по строкам явно не задан (например, колонка по которой нужно их строить), то pandas задаёт целочисленный индекс RangeIndex от 0 до N-1, где N это количество строк в таблице.

В таблице у нас 4 элемента от 0 до 3.

Доступ по индексу в DataFrame

Индекс по строкам можно задать разными способами, например, при формировании самого объекта DataFrame или «на лету»:

Как видно, индексу было задано имя — Country Code. Отмечу, что объекты Series из DataFrame будут иметь те же индексы, что и объект DataFrame:

Доступ к строкам по индексу возможен несколькими способами:

  • .loc — используется для доступа по строковой метке
  • .iloc — используется для доступа по числовому значению (начиная от 0)

Можно делать выборку по индексу и интересующим колонкам:

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

Фильтровать DataFrame с помощью т.н. булевых массивов:

Кстати, к столбцам можно обращаться, используя атрибут или нотацию словарей Python, т.е. df.population и df[‘population’] это одно и то же.

Сбросить индексы можно вот так:

pandas при операциях над DataFrame, возвращает новый объект DataFrame.

Добавим новый столбец, в котором население (в миллионах) поделим на площадь страны, получив тем самым плотность:

Не нравится новый столбец? Не проблема, удалим его:

Особо ленивые могут просто написать del df[‘density’].

Переименовывать столбцы нужно через метод rename:

В этом примере перед тем как переименовать столбец Country Code, убедитесь, что с него сброшен индекс, иначе не будет никакого эффекта.

Чтение и запись данных

pandas поддерживает все самые популярные форматы хранения данных: csv, excel, sql, буфер обмена, html и многое другое:

Чаще всего приходится работать с csv-файлами. Например, чтобы сохранить наш DataFrame со странами, достаточно написать:

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

Считать данные из csv-файла и превратить в DataFrame можно функцией read_csv.

Аргумент sep указывает разделитесь столбцов. Существует ещё масса способов сформировать DataFrame из различных источников, но наиболее часто используют CSV, Excel и SQL. Например, с помощью функции read_sql, pandas может выполнить SQL запрос и на основе ответа от базы данных сформировать необходимый DataFrame. За более подробной информацией стоит обратиться к официальной документации.

Группировка и агрегирование в pandas

Группировка данных один из самых часто используемых методов при анализе данных. В pandas за группировку отвечает метод .groupby. Я долго думал какой пример будет наиболее наглядным, чтобы продемонстрировать группировку, решил взять стандартный набор данных (dataset), использующийся во всех курсах про анализ данных — данные о пассажирах Титаника. Скачать CSV файл можно тут.

Необходимо подсчитать, сколько женщин и мужчин выжило, а сколько нет. В этом нам поможет метод .groupby.

А теперь проанализируем в разрезе класса кабины:

Сводные таблицы в pandas

Термин «сводная таблица» хорошо известен тем, кто не по наслышке знаком с инструментом Microsoft Excel или любым иным, предназначенным для обработки и анализа данных. В pandas сводные таблицы строятся через метод .pivot_table. За основу возьмём всё тот же пример с Титаником. Например, перед нами стоит задача посчитать сколько всего женщин и мужчин было в конкретном классе корабля:

В качестве индекса теперь у нас будет пол человека, колонками станут значения из PClass, функцией агрегирования будет count (подсчёт количества записей) по колонке Name.

Всё очень просто.

Анализ временных рядов

В pandas очень удобно анализировать временные ряды. В качестве показательного примера я буду использовать цену на акции корпорации Apple за 5 лет по дням. Файл с данными можно скачать тут.

Здесь мы формируем DataFrame с DatetimeIndex по колонке Date и сортируем новый индекс в правильном порядке для работы с выборками. Если колонка имеет формат даты и времени отличный от ISO8601, то для правильного перевода строки в нужный тип, можно использовать метод pandas.to_datetime.

Давайте теперь узнаем среднюю цену акции (mean) на закрытии (Close):

А если взять промежуток с февраля 2012 по февраль 2015 и посчитать среднее:

А что если нам нужно узнать среднюю цену закрытия по неделям?!

Resampling мощный инструмент при работе с временными рядами (time series), помогающий переформировать выборку так, как удобно вам. Метод resample первым аргументом принимает строку rule. Все доступные значения можно найти в документации.

Визуализация данных в pandas

Для визуального анализа данных, pandas использует библиотеку matplotlib. Продемонстрирую простейший способ визуализации в pandas на примере с акциями Apple.

Берём цену закрытия в промежутке между 2012 и 2017.

И видим вот такую картину:

По оси X, если не задано явно, всегда будет индекс. По оси Y в нашем случае цена закрытия. Если внимательно посмотреть, то в 2014 году цена на акцию резко упала, это событие было связано с тем, что Apple проводила сплит 7 к 1. Так мало кода и уже более-менее наглядный анализ 😉

Эта заметка демонстрирует лишь малую часть возможностей pandas. Со своей стороны я постараюсь по мере своих сил обновлять и дополнять её.

Анализируем данные: структура Series в Pandas

Обработка и анализ данных – важнейшая часть работы специалиста по системному анализу. В этом может помочь Pandas — программная высокоуровневая библиотека, написанная на Python. Главными структурами данных в ней являются Series и DataFrame. Не понимая их работу, выполнить качественный анализ будет невозможно. Сегодня поговорим про Series.

Series — объект, напоминающий одномерный массив (к примеру, список в Python). Отличительная черта — ассоциированные метки или так называемые индексы, расположенные, вдоль каждого элемента списка. Это особенность и превращает Series в ассоциативный массив либо словарь в Python.

1-1801-c9680f.png

В строковом представлении Series индекс находится слева, а элемент справа. Если же индекс не задан явно, pandas автоматически создаст RangeIndex от 0 до N-1, где N — общее число элементов. Кроме того, следует учесть, что в Series существует тип хранимых элементов (в примере это int64, ведь мы передавали целочисленные значения).

Также у объекта Series есть атрибуты, посредством которых вы сможете получить список элементов и индексы — values и index соответственно.

2-1801-64f981.png

Доступ к элементам Series осуществляется по индексу этих элементов.

3-1801-af1860.png

При этом мы можем задавать индексы явно:

4-1801-9f59e5.png

Также есть возможность осуществлять выборку по нескольким индексам и выполнять групповое присваивание:

5-1801-03e558.png

Что касается фильтрации, то это тоже не проблема для Series, плюс можно применять математические операции и т. д.:

6-1801-468a09.png

Но если сам объект Series напоминает словарь, где ключ — это индекс, а значение — сам элемент, то мы можем сделать следующее:

7-1801-64fcac.png

У объекта Series и его индекса присутствует атрибут name, который задает имя объекту и индексу.

8-1801-73e1ad.png

Индекс мы можем поменять без проблем, присвоив список атрибуту index объекта Series.

9-1801-55f1a4.png

Также имейте в виду, что список с индексами по длине в обязательном порядке должен совпадать с числом элементов в Series.

Структуры данных

Основными рабочими структурами данных в Pandas являются объекты Series и DataFrame, но прежде чем переходить к их изучению, давайте сделаем необходимые импорты:

Series

Series — это структура данных, которая сочетает свойства одномерного массивы NumPy и словаря Python, т.е. доступ к каждому элементу может быть получен, либо с помощью индекса, либо с помощью идентификатора (ключа). Создать объект Series можно с помощью следующей команды:

Аргументом data может быть массив NumPy, словарь Python или скалярное значение. Аргумент index не является обязательным и может быть последовательностью, содержащей идентификаторы каждого элемента в data . Если index не указан, то он будет создан автоматически, а его значения будут целыми числами из интервала [0, 1, 2, . len(data) — 1] , т.е. будут совпадать с индексами одномерного массива.

Очень часто объекты Series называют, просто — сериями, аргумент data — данными, а index — индексом.

Давайте создадим серию из массива NumPy:

Атрибут .index позволяет взглянуть на идентификаторы элементов (их иногда еще называют метками элементов):

Если индекс не указан, как здесь:

то он будет создан автоматически, а его значения будут совпадать с значениями индексов массива data

Важно отметить, что элементы индекса могут иметь одинаковые значения:

Серии также могут быть созданы из словарей:

Если вместе со словарем указан еще и индекс, то из данных будут извлечены только те элементы, ключи которых присутствуют в index :

Для ключей, которых нет в data , но есть в index создаются значения NaN, которые обозначают отсутствующие элементы.

В качестве data может выступать скалярное значение, которое будет сопоставлено каждому индексу:

Серии являются массиво-подобными объектами, поэтому они могут быть переданы в качестве аргументов, практически во все функции NumPy и в этом ракурсе ведут себя практически точно так же как и array объекты:

Например, вот обычное извлечение разреженного среза (среза с заданным шагом):

Результат булевой индексации (применения логической маски):

Извлечение элементов с помощью массива индексов:

Применение функции NumPy к серии и выполнение векторизованной арифметической операции:

При этом серии подобны словарю в том смысле, что доступ к элементам может быть выполнен по идентификаторам (меткам) в index :

Как и в NumPy, все операции над сериями векторизованы, но все они выполняются с выравниванием индекса:

Выравнивание означает, что операции выполняются только над элементами с одинаковым индексом, остальные индексы попадают в результат со значениями NaN.

Зачастую, данные в сериях являются значениями некоторой переменной из конкретной прикладной области, поэтому полезно давать сериям осмысленные названия:

Сменить название можно с помощью метода rename() :

DataFrame

DataFrame — это объект, который сочетает свойства структурированных массивов NumPy и словарей Python. Самая простая аналогия для DataFrame — это таблица, столбцами которой являются объекты Series. Обычно, объекты DataFrame называют, просто — датафреймами.

Создать датафрейм можно несколькими способами, например из словаря, значениями элементов которого являются объекты Series, а ключами — строки с названиями будующих столбцов:

first_column second_column third_column
a 4.0 NaN 2.0
b 3.0 NaN NaN
c 2.0 9.0 3.0
d 1.0 8.0 4.0
e 0.0 7.0 NaN
f NaN 6.0 5.0
g NaN 5.0 6.0

Можно указать необходимые индексы и столбцы:

first_column second_column third_column
c 2 5 3
d 3 6 4
second_column third_column fourth_column
c 5.0 3.0 NaN
d 6.0 4.0 NaN
f 8.0 5.0 NaN
g 9.0 6.0 NaN
z NaN NaN NaN

Датафрейм может быть создан из структурированного массива NumPy:

col_1 col_2 col_3
0 a 11 0.899902
1 b 22 0.799805
2 c 33 0.700195
col_1 col_2 col_3
one a 11 0.899902
two b 22 0.799805
three c 33 0.700195
col_2 col_1 col_4
one 11 a NaN
two 22 b NaN
three 33 c NaN

Датафрейм может быть создан из списка словарей:

c a b
one NaN 11.0 22.0
two 44.0 NaN 33.0
three 66.0 55.0 NaN

Датафрейм может быть создан из словаря словарей:

X Y
a 1.0 11.0
b 2.0 NaN
c NaN 22.0

Датафрейм может быть создан из словаря словарей с ключами-кортежами, в результате чего может быть получена иерархическая индексация как строк так и столбцов::

group_1 group_2
x y z x y z
i m 11 33 55 11 33 55
n 22 44 66 22 44 66
j m 11 33 55 11 33 55
n 22 44 66 22 44 66

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

col_1 col_2
0 9 3
1 9 5
2 5 1
3 6 5
4 3 8

Например, добавить новый столбец в датафрейм можно точно так же как и новый элемент в словарь, т.е. просто указать ключ и присвоить ему новую серию:

col_1 col_2 col_3
0 9 3 900
1 9 5 900
2 5 1 500
3 6 5 600
4 3 8 300

Наверное вы обратили внимание на то, что новыми столбцами датафрейма могут быть результаты вычислений над имеющимися столбцами. В примере выше мы взяли столбец col_1, умножили его на 100, а результат присвоили новому столбцу, который стал частью исходного датафрейма. Это очень удобно и мы можем этим успешно и часто пользоваться. А в самих вычислениях может учавствовать несколько столбцов, например, мы можем выполнить сравнение значений нескольких столбцов, а результат добавить в тот же самый датафрейм:

col_1 col_2 col_3 col_4
0 9 3 900 True
1 9 5 900 True
2 5 1 500 True
3 6 5 600 True
4 3 8 300 False

Удалить столбец можно с помощью, уже знакомого по словарям, метода pop() :

Данный метод удаляет указанный элемент, но как вы обратили внимание, возвращает его значение в виде серии. Тем не менее, столбец действительно удалился:

col_2 col_3 col_4
0 3 900 True
1 5 900 True
2 1 500 True
3 5 600 True
4 8 300 False

Вставить столбец в нужное место можно с помощью метода insert :

col_1 col_2 col_3 col_4
0 0.003333 3 900 True
1 0.005556 5 900 True
2 0.002000 1 500 True
3 0.008333 5 600 True
4 0.026667 8 300 False

Обратите внимание, как я вставляю комментарии в код. Так лучше не делать. Но делаю я это для того что бы было проще понять код. По хорошему, если вы выполняете примеры, то просто удалите все, что после # . Комментарии — это классно, но лучше не вносить их, так как это делаю я, а выделять им отдельное место, рядом с конструкциями языка.

Если новому столбцу присваивается скалярное значение, то им будет заполнен весь столбец:

col_1 col_2 col_3 col_4 col_5
0 0.003333 3 900 True 10
1 0.005556 5 900 True 10
2 0.002000 1 500 True 10
3 0.008333 5 600 True 10
4 0.026667 8 300 False 10

Если вставить серию у которой значения индекса не совпадают с индексами датафрейма, то произойдет выравнивание индексов:

col_1 col_2 col_3 col_4 col_5 col_6
0 0.003333 3 900 True 10 10.0
1 0.005556 5 900 True 10 NaN
2 0.002000 1 500 True 10 10.0
3 0.008333 5 600 True 10 NaN
4 0.026667 8 300 False 10 10.0

Метод assign() позволяет создавать столбцы, которые могут быть получены в результате вычислений:

a b c
0 8 4 6
1 3 1 4
2 7 9 0
3 0 4 2
4 2 4 5

При этом возвращается новый датафрейм, а исходный остается без изменений:

a b c
0 8 4 6
1 3 1 4
2 7 9 0
3 0 4 2
4 2 4 5

Метод assign() так же позволяет использовать лямбда-функции:

a b c d
0 8 4 6 38
1 3 1 4 7
2 7 9 0 63
3 0 4 2 2
4 2 4 5 13

Индексация строк датафреймов

Допустим у нас есть следующий датафрейм:

X Y Z
a 2 8 5
b 1 7 6
c 4 6 6
d 3 4 5
e 6 7 3

Что бы обратиться к его строкам по их индексу (меткам) в index перед оператором [] нужно указать индексатор loc :

А что бы обратиться к той же строке, но по целочисленному индексу, нужен индексатор iloc :

Возвращаемые индексаторами, объекты являются сериями:

Причем индексы этих серий, совпадают с названиями столбцов датафрейма, что в общем-то более чем логично:

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

Индексаторами loc и iloc можно воспользоваться для выделения срезов (фрагментов таблиц):

X Y Z
b 1 7 6
c 4 6 6
d 3 4 5
X Y Z
b 1 7 6
c 4 6 6
d 3 4 5

Добиться аналогичного результата можно, просто, указав начальный и конечный индекс среза:

X Y Z
b 1 7 6
c 4 6 6
d 3 4 5

Так же как и в NumPy получить датафрейм из нужных строк можно с помощью логических массивов:

X Y Z
a 2 8 5
c 4 6 6
e 6 7 3

Выравнивание данных

При выполнении арифметических операций с участием датафреймов происходит выравнивание данных, как по строкам, так и по столбцам, а результатом операции будет датафрейм с объединением меток строк и столбцов. Для примера сделаем два датафрейма:

col_0 col_1 col_2 col_3
row_0 0 1 2 3
row_1 4 5 6 7
row_2 8 9 10 11
row_3 12 13 14 15
row_4 16 17 18 19
row_5 20 21 22 23
row_6 24 25 26 27
col_0 col_1 col_2 col_3 col_4 col_5 col_6
row_0 0 1 2 3 4 5 6
row_1 7 8 9 10 11 12 13
row_2 14 15 16 17 18 19 20
row_3 21 22 23 24 25 26 27

А теперь выполним их произведение:

col_0 col_1 col_2 col_3 col_4 col_5 col_6
row_0 0.0 1.0 4.0 9.0 NaN NaN NaN
row_1 28.0 40.0 54.0 70.0 NaN NaN NaN
row_2 112.0 135.0 160.0 187.0 NaN NaN NaN
row_3 252.0 286.0 322.0 360.0 NaN NaN NaN
row_4 NaN NaN NaN NaN NaN NaN NaN
row_5 NaN NaN NaN NaN NaN NaN NaN
row_6 NaN NaN NaN NaN NaN NaN NaN

Если выполняется операция между датафреймом и серией, то индексы серии выравниваются по столбцам датафрейма, а затем происходит транслирование серии по всему датафрейму, например

col_0 col_1 col_2 col_3 col_4 col_5 col_6
row_0 0 1 4 9 16 25 36
row_1 0 8 18 30 44 60 78
row_2 0 15 32 51 72 95 120
row_3 0 22 46 72 100 130 162
col_0 col_1 col_2 col_3 col_4 col_5 col_6
row_0 0.0 NaN 4.0 NaN 8.0 NaN 12.0
row_1 7.0 NaN 11.0 NaN 15.0 NaN 19.0
row_2 14.0 NaN 18.0 NaN 22.0 NaN 26.0
row_3 21.0 NaN 25.0 NaN 29.0 NaN 33.0

Иногда бывает нужно чтобы транслирование выполнялось не по столбцам а построчно, например, из значений элементов каждого столбца вычесть соответствующие значения серии:

Но если попытаться выполнить это, то мы получим далеко не то что нужно:

col_0 col_1 col_2 col_3 col_4 col_5 col_6 row_0 row_1 row_2 row_3
row_0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
row_1 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
row_2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
row_3 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

Что бы выполнить подобную операцию нужно воспользоваться методом sub() :

col_0 col_1 col_2 col_3 col_4 col_5 col_6
row_0 -4 -3 -2 -1 0 1 2
row_1 -4 -3 -2 -1 0 1 2
row_2 -4 -3 -2 -1 0 1 2
row_3 -4 -3 -2 -1 0 1 2

Использование обычных чисел (скаляров) в выражениях, так же выполняется с выполнением трансляции по всему датафрейму:

col_0 col_1 col_2 col_3 col_4 col_5 col_6
row_0 1 2 4 8 16 32 64
row_1 128 256 512 1024 2048 4096 8192
row_2 16384 32768 65536 131072 262144 524288 1048576
row_3 2097152 4194304 8388608 16777216 33554432 67108864 134217728
col_0 col_1 col_2 col_3 col_4 col_5 col_6
row_0 1 3 5 7 9 11 13
row_1 15 17 19 21 23 25 27
row_2 29 31 33 35 37 39 41
row_3 43 45 47 49 51 53 55

Логические операции, так же выполняются с выполнением транслирования:

col_0 col_1 col_2 col_3
row_0 0 0 0 1
row_1 2 0 0 0
row_2 0 1 1 1
row_3 2 1 0 0
row_4 2 1 1 0
row_5 2 2 2 1
row_6 1 2 2 1
col_0 col_1 col_2 col_3
row_0 True False False True
row_1 False False False False
row_2 True True True True
row_3 False True False False
row_4 False True True False
row_5 False False False True
row_6 False False False True
col_0 col_1 col_2 col_3
row_0 2 1 2 0
row_1 2 2 2 0
row_2 1 1 1 2
row_3 0 1 1 2
row_4 1 1 1 0
row_5 2 2 2 2
row_6 2 0 1 0
col_0 col_1 col_2 col_3
row_0 False False False False
row_1 True False False True
row_2 False True True False
row_3 False True False False
row_4 False True True True
row_5 True True True False
row_6 False False False False

Тоже самое касается и побитовых логических операций:

col_0 col_1 col_2 col_3
row_0 False False False True
row_1 False True False False
row_2 False False False True
row_3 False False False False
row_4 True True False False
row_5 True False False True
row_6 False False True True

Что бы выполнить транспонирование датафрейма, т.е. сделать так что бы строки стали столбцами, а столбцы строками, достаточно воспользоваться атрибутом .T :

col_0 col_1 col_2 col_3
row_0 0 1 2 3
row_1 4 5 6 7
row_2 8 9 10 11
row_3 12 13 14 15
row_4 16 17 18 19
row_5 20 21 22 23
row_6 24 25 26 27
row_0 row_1 row_2 row_3 row_4 row_5 row_6
col_0 0 4 8 12 16 20 24
col_1 1 5 9 13 17 21 25
col_2 2 6 10 14 18 22 26
col_3 3 7 11 15 19 23 27

Все поэлементные функции NumPy могут выполняться над сериями и датафреймами, при условии, что они состоят из числовых значений:

col_0 col_1 col_2 col_3
row_0 1.0 2.0 4.0 8.0
row_1 16.0 32.0 64.0 128.0
row_2 256.0 512.0 1024.0 2048.0
row_3 4096.0 8192.0 16384.0 32768.0
row_4 65536.0 131072.0 262144.0 524288.0
row_5 1048576.0 2097152.0 4194304.0 8388608.0
row_6 16777216.0 33554432.0 67108864.0 134217728.0

Но это не значит, что датафреймы могут заменить собой массивы NumPy, так как они отличаются внутренней структурой и принципом индексации, например, сравните результат вычисления среднего значения для датафрейма с результатом вычислений для массива NumPy с теми же самыми элементами:

Тем не менее, серии полностью поддерживают универсальные функции NumPy, причем выравнивание индексов происходит перед применением данных функций:

Вывод на экран

Чаще всего датафреймы содержат очень много строк и столбцов, которые просто не могут поместиться на экран. В качестве примера, мы можем воспользоваться наборами данных из пакета Seaborn. Скорее всего вы работаете в дистрибутиве Anaconda или в Гугловском колабе, так что Seaborn наверняка у вас уже установлен. А если нет, то быстрее пересаживайтесь на анаконду и колаб!

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-х строк, понятно, что выводить все эти строки на экран не имеет смысла, т.к. получится очень длинная таблица. Что бы получить какое-то представление о таблице можно воспользоваться методом info() :

Да, чаще всего серии и датафреймы являются очень большими и их вывод просто не помещается на экран, поэтому, они выводятся в «урезанной» форме:

Что бы просмотреть их небольшой фрагмент, можно воспользоваться методами head() или tail() :

По умолчанию выводится всего 5 строк, но мы можем указать столько строк сколько нам нужно:

Атрибуты объектов Pandas

Допустим у нас есть следующий датафрейм:

A B C D
2020-01-01 0.291797 0.898429 0.471657 0.170232
2020-01-02 0.781077 0.088060 0.056392 0.110569
2020-01-03 0.949604 0.781641 0.281778 0.488396
2020-01-04 0.730843 0.055508 0.513162 0.109554
2020-01-05 0.703591 0.060644 0.542054 0.995164

Что бы получить доступ к его метаданным, таким как форма, названия столбцов и индексам строк, можно воспользоваться его атрибутами:

Объекты с именами столбцов и индексами строк являются неизменяемыми (немутирующими), так что если попробовать переименовать имя одного столбца или изменить индекс одной из строк, то мы увидим ошибку:

Но ничто нам не запрещает спокойно назначать датафреймам новые объекты columns и index :

col_0 col_1 col_2 col_3
2020-01-01 0.291797 0.898429 0.471657 0.170232
2020-01-02 0.781077 0.088060 0.056392 0.110569
2020-01-03 0.949604 0.781641 0.281778 0.488396
2020-01-04 0.730843 0.055508 0.513162 0.109554
2020-01-05 0.703591 0.060644 0.542054 0.995164
col_0 col_1 col_2 col_3
id_0 0.291797 0.898429 0.471657 0.170232
id_1 0.781077 0.088060 0.056392 0.110569
id_2 0.949604 0.781641 0.281778 0.488396
id_3 0.730843 0.055508 0.513162 0.109554
id_4 0.703591 0.060644 0.542054 0.995164

В тоже время объекты Pandas (Series, DataFrame, Index, Columns) можно рассматривать как контейнеры для обычных массивов, которые содержат фактические данные и над которыми можно выполнять действия и вычисления. Основным типом используемых массивов являются массивы NumPy, но Pandas идет несколько дальше этих массивов и расширяет множество типов данных, которые могут в них содержаться.

Что бы преобразовать серии и их индексы в массивы NumPy можно воспользоваться их атрибутом array :

Вы можете заметить, что возвращаемые объекты не очень-то и похожи на массивы NumPy, так и есть — это одномерные объекты, базового класса ExtensionArray, которые очень похожи на обычные массивы NumPy, но для Pandas они позволяют добиться большей функциональности за счет расширения типов. Эта информация, конечно вряд ли является очень полезной, но она дает хорошее представление о том, что серии и датафреймы Pandas это не просто смесь свойств словарей и массивов в буквальном смысле этого слова, а нечто более сложное.

Иногда, знание таких тонкостей, очень сильно помогает. Например, вследствии того, что Pandas использует свои типы данных, корректное преобразование в массивы NumPy бывает просто невозможно без дополнительных манипуляций. Но мы пока не будем углубляться в эту тему и двинемся дальше.

Если нам нужен именно массив NumPy то можно воспользоваться методом Pandas to_numpy() или методом NumPy asarray() :

Когда серии и объекты индексов основаны на классе ExtensionArray, то иногда бывает полезно включать копирование данных и преобразование их типа с помощью аргументов copy и dtype . Например, в NumPy нет типа данных для хранения даты и времени с учетом часовых поясов, а в Pandas есть. Допустим, у нас есть вот такая серия, которая хранит дату с московским временем:

Если мы перобразуем эту серию в массив NumPy, то увидим что его данные будут типа object :

К данному типу преобразуется все что выходит за пределы базовых типов NumPy (строки, словари и многое другое тоже будет иметь тип данных object ). Конечно, это теперь массив NumPy который содержит информацию о дате и времени с указанием часового пояса. Но тем не менее, иногда нам все равно нужно получить именно массив NumPy с типом даты и времени. В этом случае мы можем воспользоваться параметром dtype :

С преобразованием датафреймов в массивы все немного сложнее. Если все столбцы датафрейма имеют единый тип данных, то метод to_numpy вернет массив с тем же самым типом данных:

Но если данные в столбцах разного типа, то тип данных результирующего масива будет преобразован к наиболее общему типу NumPy:

col_0 col_1 col_2 col_3 col_4
id_0 0.291797 0.898429 0.471657 0.170232 a
id_1 0.781077 0.088060 0.056392 0.110569 b
id_2 0.949604 0.781641 0.281778 0.488396 c
id_3 0.730843 0.055508 0.513162 0.109554 d
id_4 0.703591 0.060644 0.542054 0.995164 e

Раньше для этих целей рекомендовалось использовать метод values() , но сейчас данный метод считается устаревшим. Так что если вам нужно преобразовать объекты Pandas в массивы NumPy, то пользуйтесь атрибутом array или методом to_numpy() .

Введение

По работе на данный момент приходится много работать с Pandas. Для закрепления своих знаний и для помощи другим в освоении, решил написать серию статей про работу с этой замечательной библиотекой, которая позволяет выполнять максимально быстро множество различных операций над большим объемом данных.

Первые шаги

Для начала, скачаем набор данных, который будем использовать в работе с Pandas. Данные доступны по ссылке https://github.com/PacktPublishing/Pandas-Cookbook/tree/master/data

Работать будем в Jupiter Notebook. Если ставили его в комплекте с Anaconda, то, скорее всего, путь к исходникам, с которыми работает Jupiter Notebook будет вида: C:/Users/your_user_name/ — можно создать там какую-нибудь папку, сохранить data из ссылки выше, распаковать и, вернувшись в Notebook создать новый документ для работы.

Для подгрузки данных из CSV в DataFrame (DF) используется метод read_csv. Можно загружать CSV через дефолтный модули Python — csv, но read_csv намного быстрее и эффективнее справляется с поставленной задачей.

Для отображения первых строк DF и базовой информации — используется метод head().

В данном примере мы используем movie.csv из набора данных с github-а.

axis = 0 — т.е. ось 0 — олицетворяет данные, сгруппированные вертикально, используя столбцы. А axis = 1, ось 1 — данные, сгруппированные горизонтально, используя элементы индекса.

В метод head можно передавать число строк, которое хотим увидеть, при выводе. Например, movie.head(7) выведет первые 7 строк DF, по аналогии — movie.tail(7) — выведет последние 7 строк.

В общем и целом, DF стоит на “трех китах”:

  • индексы
  • столбцы
  • данные

К каждому вышеописанному компоненту можно обратиться непосредственно из DF. Каждый компонент — обособленный Python объект со своими методами и свойствами. Зачастую, операции производятся непосредственно над этими компонентами, а не над DF в целом.

Типы данных

Для хранения данных Pandas использует различные типы данных в зависимости от того, какие значения используются в том или ином столбце. Ниже можно рассмотреть таблицу с основными типами данными.

Чтобы точно узнать, какой тип данных используется в каждом из столбце необходимо написать: move.dtypes — в результате мы получим объект, свойствами которого являются наименования столбцов DF, а значениями — соответствующие типы данных.

В Pandas существует строгое правило: один тип данных для столбца. Исключением является лишь один тип — Object. Object — это некое универсальное решение для всех возможных типов. Т.е., если Pandas не может определить точно, какой же тип используется в столбце, то будет выбран тип Object, потому что он может хранить в себе все что угодно — любой Python объект.

Выборка единственного столбца или работа с Series

Объект типа Series представляет собой в Pandas единственный столбец, состоящий из двух компонент: индекс и данные. Чтобы получить данные того или иного столбца можно использовать как квадратные скобки, так и точку (точку лучше не использовать в продакшн коде, потому что далеко не все наименования столбцов можно использовать в такого рода выборке). Рассмотрим пример ниже:

Слева у нас индексы, справа — значения. Name: country — наименование объекта типа Series. dtype: object — указывает, какого типа данные хранятся в данном столбце.

Существует возможность превратить объект типа Series в DF, вызвав метод to_frame(): country.to_frame() . В качестве имени столбца Pandas возьмет наименование объекта Series:

Series — методы и свойства

И Series, и DF — имеют очень много свойств и методов. Причем, множество из них совпадают друг с другом. Рассмотрим наиболее популярные и важные методы работы с объектами типа Series.

  • .head() — по аналогии с DF, мы можем использовать данный метод с Series, чтобы получить первые строки объекта
  • .value_counts() — один из наиболее популярных методов для Series с типом данных Object — возвращает количество уникальных записей в столбце. Таким образом, вызвав country.value_counts() получим следующий результат:
  • Метод value_counts может получить параметр normalize=True и вернуть нормализованные данные — country.value_counts(normalize=True):
  • Для того, чтобы подсчитать количество элементов в Series, можно использовать один из трех доступных параметров: country.size (вернет int), country.shape (вернет tuple) и len(country) (вернет int).
    Есть еще и метод country.count() , который, несмотря на свое название, возвращает не количество элементов в Series, а количество НЕ пустых элементов.
  • “Подбить” базовую статистику по Series можно используя следующие доступные методы: min, max, mean, median, std и sum.
    А можно просто вызвать метод describe, который вернет новый Series с индексами в виде наименований различной статистики объекта (min, max, mean и пр) и с их соответствующими значениями. Причем, следует иметь ввиду, что describe() для Series с типом данных Object и для Series с типом данных, к примеру, Float, вернет различную статистику:
  • country.isnull() — вернет Series с типом bool, в котором True будут те элементы, которые НЕ заданы в исходном объекте типа Series.
  • country.notnull() — вернет Series с типом bool, в котором True будут те элементы, которые заданы в исходной объекте типа Series, т.е. не являются нулевыми.
  • country.fillna(‘Not available’) — заполняет переданным значением все нулевые объекты у Series.
  • country.dropna() — вернет Series с удаленными нулевыми значениями.
  • если у нас стоит задача просто узнать, есть ли в Series нулевые значения, то мы можем использовать свойство: country.hasnans — в ответ получим True/False.

Series — операторы

Как Series, так и DF в Pandas работают с большинство операций из Python. Рассмотрим основные операторы, возвращающие новые объекты типа Series с новыми значениями:

  • imdb = movie[‘imdb_score’]
    imdb + 1
    Вернет нам Series со оценками IMDB, увеличенными на 1:
  • По аналогии с плюсом, мы можем использовать и другие арифметические операции с Series: -, *, **, /, //, %.
  • С Series мы можем также использовать любой оператор сравнения: >, <, >=, <=, !=, ==. В результате вернется Series с типом данных bool, где значениями будут True/False в зависимости от поставленного условия.

Все операторы, описанные выше, применяются к каждому элементу Series. В Python нам бы пришлось организовывать for loop, чтобы обойти каждый элемент, Pandas же опирается на NumPy библиотеку, которая предоставляет возможности как для векторизированных вычислений, так и для проведения операция над всей последовательностью данных разом, без использования циклических конструкций. Результатом всех вышеописанных операторов будет новый объект типа Series с такими же индексами как у исходного, но со значениями, соответствующими результату работы оператора.

Pandas всем вышеописанным операторам предоставляет методы-аналогии, которые описаны в таблице ниже:

В Pandas большое количество методов возвращают новые Series или DF, поэтому Pandas прекрасно дружит с таким понятием как chaining method, используя dot-нотацию. Method chaining удобно применять, когда над одним объектом требуется совершить сразу множество тех или иных действий (при условии, что каждое действие — читай: метод, возвращает Series или DF, пригодный для вызова следующего метода). Предположим, мы хотим вывести первые 3 строчки результата метода value_counts:

Еще один хороший пример method chaining — подсчитать количество нулевых элементов в Series. Как мы помним, isnull() возвращает серию, где нулевые элементы представлены как True, а все прочие как False. При вызове метода sum(), Pandas преобразует True/False в 1/0 и вернет корректное число.

Создание понятных индексов

Индекс в DF представляет label для каждой из строк. Если для DF индекс не задан принудительно, то для индексирования Pandas по умолчанию использует RageIndex, который присвоит строкам DF label-ы в виде чисел от 0 до n-1, где n — количество строк в DF.

Мы можем присвоить индексу DF любое его поле. Делается это с помощью метода set_index() следующим образом:

Кроме вышеописанного метода, Pandas предоставляет возможность сразу же задать индекс для DF на этапе чтения данных, указав параметр index_col у соответствующего метода. В нашем случае — это read_csv:

Оба вышеописанных метода установки индекса по умолчанию удаляют столбец с изначальным индексом DF, поэтому, если есть необходимость изначальный столбец с индексом, то необходимо использовать метод set_index с параметром drop=False.

Если есть необходимо сбросить ранее установленный индекс, то можно использовать метод reset_index, который поставит ранее установленный индекс на место первого столбца DF, а индексом сделает штатный RageIndex.

Переименование строк и столбцов

Предположим, что в movie у нас лежит DF с индексом в виде movie_title столбца.

Когда мы хотим переименовать индексы или наименования столбцов, мы должны сформировать словари: ключами словаря будут наименования, которые мы хотим заменить, а значениями — то, на что мы хотим их заменить. Пример:

Кроме вышеописанного метода, можно использовать получения индексов и колонок DF с последующим преобразованием в обычный Python список и изменением в нем необходимых элементов с доступом к ним по индексу. Пример:

Добавление и удаление столбцов

Pandas предоставляет несколько вариантов добавления новых столбцов в DF. Самым простым способом добавить новый столбец — присвоить ему некое значение. Например, так:

По умолчанию, новые столбцы добавляются в конец DF. Кроме констант, можно задавать значения, основанные на других столбцах DF, например, найдем сколько всего лайков в FB получили все актеры и режиссер вместе взятые:

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

Т.е. для 122 фильмов все значения полей с FB лайками были пустыми в исходном DF.

Чтобы удалить столбец, необходимо воспользоваться методом drop():

Существует еще один способ добавления столбцов — метод insert(). Метод принимает три параметра: первый — на какое место в DF должен быть вставлен новый столбец, второй — наименование нового столбца, третий — его значения. Приведем пример. В нашем DF есть столбцы gross и budget, добавим новый столбец profit сразу после gross, который будет равняться разнице между gross и budget. Для этого мы сначала найдем индекс столбца gross с помощью метода get_loc().

Важно заметить, что insert() не возвращает новый DF, а проводит изменения на месте, поэтому присваиваний никаких дополнительных мы не делаем.

Читать:
Hp laserjet mfp m139 m142 как настроить

Похожие статьи