Документация pandas: pandas documentation — pandas 1.1.2 documentation
Руководство по использованию pandas для анализа больших наборов данных
При использовании библиотеки pandas для анализа маленьких наборов данных, размер которых не превышает 100 мегабайт, производительность редко становится проблемой. Но когда речь идёт об исследовании наборов данных, размеры которых могут достигать нескольких гигабайт, проблемы с производительностью могут приводить к значительному увеличению длительности анализа данных и даже могут становиться причиной невозможности проведения анализа из-за нехватки памяти.
В то время как инструменты наподобие Spark могут эффективно обрабатывать большие наборы данных (от сотен гигабайт до нескольких терабайт), для того чтобы полноценно пользоваться их возможностями обычно нужно достаточно мощное и дорогое аппаратное обеспечение. И, в сравнении с pandas, они не отличаются богатыми наборами средств для качественного проведения очистки, исследования и анализа данных. Для наборов данных средних размеров лучше всего попытаться более эффективно использовать pandas, а не переходить на другие инструменты.
В материале, перевод которого мы публикуем сегодня, мы поговорим об особенностях работы с памятью при использовании pandas, и о том, как, просто подбирая подходящие типы данных, хранящихся в столбцах табличных структур данных DataFrame
, снизить потребление памяти почти на 90%.
Работа с данными о бейсбольных матчах
Мы будем работать с данными по бейсбольным играм Главной лиги, собранными за 130 лет и взятыми с Retrosheet.
Изначально эти данные были представлены в виде 127 CSV-файлов, но мы объединили их в один набор данных с помощью csvkit и добавили, в качестве первой строки получившейся таблицы, строку с названиями столбцов. Если хотите, можете загрузить нашу версию этих данных и экспериментировать с ними, читая статью.
Начнём с импорта набора данных и взглянем на его первые пять строк. Их вы можете найти в этой таблице, на листе Фрагмент исходного набора данных
.
import pandas as pd
gl = pd.read_csv('game_logs.csv')
gl.head()
Ниже приведены сведения о наиболее важных столбцах таблицы с этими данными. Если вы хотите почитать пояснения по всем столбцам — здесь вы можете найти словарь данных для всего набора данных.
date
— Дата проведения игры.v_name
— Название команды гостей.v_league
— Лига команды гостей.h_name
— Название команды хозяев.h_league
— Лига команды хозяев.v_score
— Очки команды гостей.h_score
— Очки команды хозяев.v_line_score
— Сводка по очкам команды гостей, например —010000(10)00
.h_line_score
— Сводка по очкам команды хозяев, например —010000(10)0X
.park_id
— Идентификатор поля, на котором проводилась игра.attendance
— Количество зрителей.
Для того чтобы узнать общие сведения об объекте DataFrame
, можно воспользоваться методом DataFrame.info(). Благодаря этому методу можно узнать о размере объекта, о типах данных и об использовании памяти.
По умолчанию pandas, ради экономии времени, указывает приблизительные сведения об использовании памяти объектом DataFrame
. Нас интересуют точные сведения, поэтому мы установим параметр memory_usage
в значение 'deep'
.
gl.info(memory_usage='deep')
Вот какие сведения нам удалось получить:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 171907 entries, 0 to 171906
Columns: 161 entries, date to acquisition_info
dtypes: float64(77), int64(6), object(78)
memory usage: 861.6 MB
Как оказалось, у нас имеется 171,907 строк и 161 столбец. Библиотека pandas автоматически выяснила типы данных. Здесь присутствует 83 столбца с числовыми данными и 78 столбцов с объектами. Объектные столбцы используются для хранения строковых данных, и в тех случаях, когда столбец содержит данные разных типов.
Теперь, для того, чтобы лучше понять то, как можно оптимизировать использование памяти этим объектом DataFrame
, давайте поговорим о том, как pandas хранит данные в памяти.
Внутреннее представление объекта DataFrame
Внутри pandas столбцы данных группируются в блоки со значениями одинакового типа. Вот пример того, как в pandas хранятся первые 12 столбцов объекта DataFrame
.
Внутреннее представление данных разных типов в pandas
Можно заметить, что блоки не хранят сведения об именах столбцов. Происходит это из-за того, что блоки оптимизированы для хранения значений, имеющихся в ячейках таблицы объекта DataFrame
. За хранение сведений о соответствии между индексами строк и столбцов набора данных и того, что хранится в блоках однотипных данных, отвечает класс BlockManager
. Он играет роль API, который предоставляет доступ к базовым данным. Когда мы читаем, редактируем или удаляем значения, класс DataFrame
взаимодействует с классом BlockManager
для преобразования наших запросов в вызовы функций и методов.
Каждый тип данных имеет специализированный класс в модуле pandas.core.internals
. Например, pandas использует класс ObjectBlock
для представления блоков, содержащих строковые столбцы, и класс FloatBlock
для представления блоков, содержащих столбцы, хранящие числа с плавающей точкой. Для блоков, представляющих числовые значения, выглядящие как целые числа или числа с плавающей точкой, pandas комбинирует столбцы и хранит их в виде структуры данных ndarray
библиотеки NumPy. Эта структура данных построена на основе массива C, значения хранятся в непрерывном блоке памяти. Благодаря такой схеме хранения данных доступ к фрагментам данных осуществляется очень быстро.
Так как данные разных типов хранятся раздельно, мы исследуем использование памяти разными типами данных. Начнём со среднего показателя использования памяти по разным типам данных.
for dtype in ['float','int','object']:
selected_dtype = gl.select_dtypes(include=[dtype])
mean_usage_b = selected_dtype.memory_usage(deep=True).mean()
mean_usage_mb = mean_usage_b / 1024 ** 2
print("Average memory usage for {} columns: {:03.2f} MB".format(dtype,mean_usage_mb))
В результате оказывается, что средние показатели по использованию памяти для данных разных типов выглядят так:
Average memory usage for float columns: 1.29 MB
Average memory usage for int columns: 1.12 MB
Average memory usage for object columns: 9.53 MB
Эти сведения дают нам понять то, что большая часть памяти уходит на 78 столбцов, хранящих объектные значения. Мы ещё поговорим об этом позже, а сейчас давайте подумаем о том, можем ли мы улучшить использование памяти столбцами, хранящими числовые данные.
Подтипы
Как мы уже говорили, pandas представляет числовые значения в виде структур данных ndarray
NumPy и хранит их в непрерывных блоках памяти. Эта модель хранения данных позволяет экономно расходовать память и быстро получать доступ к значениям. Так как pandas представляет каждое значение одного и того же типа, используя одинаковое число байт, и структуры ndarray
хранят сведения о числе значений, pandas может быстро и точно выдать сведения об объёме памяти, потребляемых столбцами, хранящими числовые значения.
У многих типов данных в pandas есть множество подтипов, которые могут использовать меньшее число байт для представления каждого значения. Например тип float
имеет подтипы float16
, float32
и float64
. Число в имени типа указывает на количество бит, которые подтип использует для представления значений. Например, в только что перечисленных подтипах для хранения данных используется, соответственно, 2, 4, 8 и 16 байт. В следующей таблице представлены подтипы наиболее часто используемых в pandas типов данных.
Использование памяти, байт | Число с плавающей точкой | Целое число | Беззнаковое целое число | Дата и время | Логическое значение | Объект |
1 | int8 | uint8 | bool | |||
2 | float16 | int16 | uint16 | |||
4 | float32 | int32 | uint32 | |||
8 | float64 | int64 | uint64 | datetime64 | ||
Переменный объём памяти | object |
Значение типа int8
использует 1 байт (8 бит) для хранения числа и может представлять 256 двоичных значений (2 в 8 степени). Это означает, что этот подтип можно использовать для хранения значений в диапазоне от -128 до 127 (включая 0).
Для проверки минимального и максимального значения, подходящего для хранения с использованием каждого целочисленного подтипа, можно воспользоваться методом numpy.iinfo()
. Рассмотрим пример:
import numpy as np
int_types = ["uint8", "int8", "int16"]
for it in int_types:
print(np.iinfo(it))
Выполнив этот код, мы получаем следующие данные:
Machine parameters for uint8
---------------------------------------------------------------
min = 0
max = 255
---------------------------------------------------------------
Machine parameters for int8
---------------------------------------------------------------
min = -128
max = 127
---------------------------------------------------------------
Machine parameters for int16
---------------------------------------------------------------
min = -32768
max = 32767
---------------------------------------------------------------
Тут можно обратить внимание на различие между типами uint
(беззнаковое целое) и int
(целое число со знаком). Оба типа имеют одинаковую ёмкость, но, при хранении в столбцах только положительных значений, беззнаковые типы позволяют эффективнее расходовать память.
Оптимизация хранения числовых данных с использованием подтипов
Функцию pd.to_numeric()
можно использовать для нисходящего преобразования числовых типов. Для выбора целочисленных столбцов воспользуемся методом DataFrame.select_dtypes()
, затем оптимизируем их и сравним использование памяти до и после оптимизации.
# Мы будем часто выяснять то, сколько памяти используется,
# поэтому создадим функцию, которая поможет нам сэкономить немного времени.
def mem_usage(pandas_obj):
if isinstance(pandas_obj,pd.DataFrame):
usage_b = pandas_obj.memory_usage(deep=True).sum()
else: # исходим из предположения о том, что если это не DataFrame, то это Series
usage_b = pandas_obj.memory_usage(deep=True)
usage_mb = usage_b / 1024 ** 2 # преобразуем байты в мегабайты
return "{:03.2f} MB".format(usage_mb)
gl_int = gl.select_dtypes(include=['int'])
converted_int = gl_int.apply(pd.to_numeric,downcast='unsigned')
print(mem_usage(gl_int))
print(mem_usage(converted_int))
compare_ints = pd.concat([gl_int.dtypes,converted_int.dtypes],axis=1)
compare_ints.columns = ['before','after']
compare_ints.apply(pd.Series.value_counts)
Вот что получается в результате исследования потребления памяти:
7.87 MB
1.48 MB
До | После | |
uint8 | NaN | 5.0 |
uint32 | NaN | 1.0 |
int64 | 6.0 | NaN |
В результате можно видеть падение использования памяти с 7.9 до 1.5 мегабайт, то есть — мы снизили потребление памяти больше, чем на 80%. Общее воздействие этой оптимизации на исходный объект DataFrame
, однако, не является особенно сильным, так как в нём очень мало целочисленных столбцов.
Сделаем то же самое со столбцами, содержащими числа с плавающей точкой.
gl_float = gl.select_dtypes(include=['float'])
converted_float = gl_float.apply(pd.to_numeric,downcast='float')
print(mem_usage(gl_float))
print(mem_usage(converted_float))
compare_floats = pd.concat([gl_float.dtypes,converted_float.dtypes],axis=1)
compare_floats.columns = ['before','after']
compare_floats.apply(pd.Series.value_counts)
В результате получается следующее:
100.99 MB
50.49 MB
До | После | |
float32 | NaN | 77.0 |
float64 | 77.0 | NaN |
В результате все столбцы, хранившие числа с плавающей точкой с типом данных float64
, теперь хранят числа типа float32
, что дало нам 50% уменьшение использования памяти.
Создадим копию исходного объекта DataFrame
, используем эти оптимизированные числовые столбцы вместо тех, что присутствовали в нём изначально, и посмотрим на общий показатель использования памяти после оптимизации.
optimized_gl = gl.copy()
optimized_gl[converted_int.columns] = converted_int
optimized_gl[converted_float.columns] = converted_float
print(mem_usage(gl))
print(mem_usage(optimized_gl))
Вот что у нас получилось:
861.57 MB
804.69 MB
Хотя мы значительно уменьшили потребление памяти столбцами, хранящими числовые данные, в целом, по всему объекту DataFrame
, потребление памяти снизилось лишь на 7%. Источником куда более серьёзного улучшения ситуации может стать оптимизация хранения объектных типов.
Прежде чем мы займёмся такой оптимизацией, поближе познакомимся с тем, как в pandas хранятся строки, и сравним это с тем, как здесь хранятся числа.
Сравнение механизмов хранения чисел и строк
Тип object
представляет значения с использованием строковых объектов Python. Отчасти это так от того, что NumPy не поддерживает представление отсутствующих строковых значений. Так как Python — это высокоуровневый интерпретируемый язык, он не даёт программисту инструментов для тонкого управления тем, как данные хранятся в памяти.
Это ограничение ведёт к тому, что строки хранятся не в непрерывных фрагментах памяти, их представление в памяти фрагментировано. Это ведёт к увеличению потребления памяти и к замедлению скорости работы со строковыми значениями. Каждый элемент в столбце, хранящем объектный тип данных, на самом деле, представляет собой указатель, который содержит «адрес», по которому настоящее значение расположено в памяти.
Ниже показана схема, созданная на основе этого материала, на которой сравнивается хранение числовых данных с использованием типов данных NumPy и хранение строк с применением встроенных типов данных Python.
Хранение числовых и строковых данных
Тут вы можете вспомнить о том, что выше, в одной из таблиц, было показано, что для хранения данных объектных типов используется переменный объём памяти. Хотя каждый указатель занимает 1 байт памяти, каждое конкретное строковое значение занимает тот же объём памяти, который использовался бы для хранения отдельно взятой строки в Python. Для того чтобы это подтвердить, воспользуемся методом sys.getsizeof()
. Сначала взглянем на отдельные строки, а затем на объект Series
pandas, хранящий строковые данные.
Итак, сначала исследуем обычные строки:
from sys import getsizeof
s1 = 'working out'
s2 = 'memory usage for'
s3 = 'strings in python is fun!'
s4 = 'strings in python is fun!'
for s in [s1, s2, s3, s4]:
print(getsizeof(s))
Здесь данные по использованию памяти выглядят так:
60
65
74
74
Теперь посмотрим на то, как выглядит использование строк в объекте Series
:
obj_series = pd.Series(['working out',
'memory usage for',
'strings in python is fun!',
'strings in python is fun!'])
obj_series.apply(getsizeof)
Здесь мы получаем следующее:
0 60
1 65
2 74
3 74
dtype: int64
Тут можно видеть, что размеры строк, хранящихся в объектах Series
pandas, аналогичны их размерам при работе с ними в Python и при представлении их в виде самостоятельных сущностей.
Оптимизация хранения данных объектных типов с использованием категориальных переменных
Категориальные переменные появились в pandas версии 0.15. Соответствующий тип, category
, использует в своих внутренних механизмах, вместо исходных значений, хранящихся в столбцах таблицы, целочисленные значения. Pandas использует отдельный словарь, устанавливающий соответствия целочисленных и исходных значений. Такой подход полезен в тех случаях, когда столбцы содержат значения из ограниченного набора. Когда данные, хранящиеся в столбце, конвертируют в тип category
, pandas использует подтип int
, который позволяет эффективнее всего распорядиться памятью и способен представить все уникальные значения, встречающиеся в столбце.
Исходные данные и категориальные данные, использующие подтип int8
Для того чтобы понять, где именно мы сможем воспользоваться категориальными данными для снижения потребления памяти, выясним количество уникальных значений в столбцах, хранящих значения объектных типов:
gl_obj = gl.select_dtypes(include=['object']).copy()
gl_obj.describe()
То, что у нас получилось, вы может найти в этой таблице, на листе Количество уникальных значений в столбцах
.
Например, в столбце day_of_week
, представляющем собой день недели, в который проводилась игра, имеется 171907 значений. Среди них всего 7 уникальных. В целом же, одного взгляда на этот отчёт достаточно для того, чтобы понять, что во многих столбцах для представления данных примерно 172000 игр используется довольно-таки мало уникальных значений.
Прежде чем мы займёмся полномасштабной оптимизацией, давайте выберем какой-нибудь один столбец, хранящий объектные данные, да хотя бы day_of_week
, и посмотрим, что происходит внутри программы при преобразовании его в категориальный тип.
Как уже было сказано, в этом столбце содержится всего 7 уникальных значений. Для преобразования его в категориальный тип воспользуемся методом .astype()
.
dow = gl_obj.day_of_week
print(dow.head())
dow_cat = dow.astype('category')
print(dow_cat.head())
Вот что у нас получилось:
0 Thu
1 Fri
2 Sat
3 Mon
4 Tue
Name: day_of_week, dtype: object
0 Thu
1 Fri
2 Sat
3 Mon
4 Tue
Name: day_of_week, dtype: category
Categories (7, object): [Fri, Mon, Sat, Sun, Thu, Tue, Wed]
Как видите, хотя тип столбца изменился, данные, хранящиеся в нём, выглядят так же, как и раньше. Посмотрим теперь на то, что происходит внутри программы.
В следующем коде мы используем атрибут Series.cat.codes
для того, чтобы выяснить то, какие целочисленные значения тип category
использует для представления каждого из дней недели:
dow_cat.head().cat.codes
Нам удаётся выяснить следующее:
0 4
1 0
2 2
3 1
4 5
dtype: int8
Тут можно заметить то, что каждому уникальному значению назначено целочисленное значение, и то, что столбец теперь имеет тип int8
. Здесь нет отсутствующих значений, но если бы это было так, для указания таких значений использовалось бы число -1.
Теперь давайте сравним потребление памяти до и после преобразования столбца day_of_week
к типу category
.
print(mem_usage(dow))
print(mem_usage(dow_cat))
Вот что тут получается:
9.84 MB
0.16 MB
Как видно, сначала потреблялось 9.84 мегабайт памяти, а после оптимизации — лишь 0.16 мегабайт, что означает 98% улучшение этого показателя. Обратите внимание на то, что работа с этим столбцом, вероятно, демонстрирует один из наиболее выгодных сценариев оптимизации, когда в столбце, содержащем примерно 172000 элементов, используется лишь 7 уникальных значений.
Хотя идея преобразования всех столбцов к этому типу данных выглядит привлекательно, прежде чем это делать, стоит учитывать негативные побочные эффекты такого преобразования. Так, наиболее серьёзный минус этого преобразования заключается в невозможности выполнения арифметических операций над категориальными данными. Это касается и обычных арифметических операций, и использования методов наподобие Series.min()
и Series.max()
без предварительного преобразования данных к настоящему числовому типу.
Нам стоит ограничить использование типа category
, в основном, столбцами, хранящими данные типа object
, в которых уникальными являются менее 50% значений. Если все значения в столбце уникальны, то использование типа category
приведёт к повышению уровня использования памяти. Это происходит из-за того, что в памяти приходится хранить, в дополнение к числовым кодам категорий, ещё и исходные строковые значения. Подробности об ограничениях типа category
можно почитать в документации к pandas.
Создадим цикл, который перебирает все столбцы, хранящие данные типа object
, выясняет, не превышает ли число уникальных значений в столбцах 50%, и если это так, преобразует их в тип category
.
converted_obj = pd.DataFrame()
for col in gl_obj.columns:
num_unique_values = len(gl_obj[col].unique())
num_total_values = len(gl_obj[col])
if num_unique_values / num_total_values < 0.5:
converted_obj.loc[:,col] = gl_obj[col].astype('category')
else:
converted_obj.loc[:,col] = gl_obj[col]
Теперь сравним то, что получилось после оптимизации, с тем, что было раньше:
print(mem_usage(gl_obj))
print(mem_usage(converted_obj))
compare_obj = pd.concat([gl_obj.dtypes,converted_obj.dtypes],axis=1)
compare_obj.columns = ['before','after']
compare_obj.apply(pd.Series.value_counts)
Получим следующее:
752.72 MB
51.67 MB
До | После | |
object | 78.0 | NaN |
category | NaN | 78.0 |
В нашем случае все обрабатываемые столбцы были преобразованы к типу category
, однако нельзя говорить о том, что то же самое произойдёт при обработке любого набора данных, поэтому, обрабатывая по этой методике свои данные, не забывайте о сравнениях того, что было до оптимизации, с тем, что получилось после её выполнения.
Как видно, объём памяти, необходимый для работы со столбцами, хранящими данные типа object
, снизился с 752 мегабайт до 52 мегабайт, то есть на 93%. Теперь давайте посмотрим на то, как нам удалось оптимизировать потребление памяти по всему набору данных. Проанализируем то, на какой уровень использования памяти мы вышли, если сравнить то, что получилось, с исходным показателем в 891 мегабайт.
optimized_gl[converted_obj.columns] = converted_obj
mem_usage(optimized_gl)
Вот что у нас получилось:
'103.64 MB'
Результат впечатляет. Но мы ещё можем кое-что улучшить. Как было показано выше, в нашей таблице имеются данные типа datetime
, столбец, хранящий которые, можно использовать в качестве первого столбца набора данных.
date = optimized_gl.date
print(mem_usage(date))
date.head()
В плане использования памяти здесь получается следующее:
0.66 MB
Вот сводка по данным:
0 18710504
1 18710505
2 18710506
3 18710508
4 18710509
Name: date, dtype: uint32
Можно вспомнить, что исходные данные были представлены в целочисленном виде и уже оптимизированы с использованием типа uint32
. Из-за этого преобразование этих данных в тип datetime
приведёт к удвоению потребления памяти, так как этот тип использует для хранения данных 64 бита. Однако в преобразовании данных к типу datetime
, всё равно, есть смысл, так как это позволит нам легче выполнять анализ временных рядов.
Преобразование выполняется с использованием функции to_datetime()
, параметр format
которой указывает на то, что данные хранятся в формате YYYY-MM-DD
.
optimized_gl['date'] = pd.to_datetime(date,format='%Y%m%d')
print(mem_usage(optimized_gl))
optimized_gl.date.head()
В результате получается следующее:
104.29 MB
Данные теперь выглядят так:
0 1871-05-04
1 1871-05-05
2 1871-05-06
3 1871-05-08
4 1871-05-09
Name: date, dtype: datetime64[ns]
Выбор типов при загрузке данных
До сих пор мы исследовали способы уменьшения потребления памяти существующим объектом DataFrame
. Мы сначала считывали данные в их исходном виде, затем, пошагово, занимались их оптимизацией, сравнивая то, что получилось, с тем, что было. Это позволило как следует разобраться с тем, чего можно ожидать от тех или иных оптимизаций. Как уже было сказано, часто для представления всех значений, входящих в некий набор данных, может попросту не хватить памяти. В связи с этим возникает вопрос о том, как применить методики экономии памяти в том случае, если нельзя даже создать объект DataFrame
, который предполагается оптимизировать.
К счастью, оптимальные типы данных для отдельных столбцов можно указать ещё до фактической загрузки данных. Функция pandas.read_csv() имеет несколько параметров, позволяющих это сделать. Так, параметр dtype
принимает словарь, в котором присутствуют, в виде ключей, строковые имена столбцов, и в виде значений — типы NumPy.
Для того чтобы воспользоваться этой методикой, мы сохраним итоговые типы всех столбцов в словаре с ключами, представленными именами столбцов. Но для начала уберём столбец с датой проведения игры, так как его нужно обрабатывать отдельно.
dtypes = optimized_gl.drop('date',axis=1).dtypes
dtypes_col = dtypes.index
dtypes_type = [i.name for i in dtypes.values]
column_types = dict(zip(dtypes_col, dtypes_type))
# вместо вывода всех 161 элементов, мы
# возьмём 10 пар ключ/значение из словаря
# и аккуратно их выведем
preview = first2pairs = {key:value for key,value in list(column_types.items())[:10]}
import pprint
pp = pp = pprint.PrettyPrinter(indent=4)
pp.pprint(preview)
Вот что у нас получится:
{ 'acquisition_info': 'category',
'h_caught_stealing': 'float32',
'h_player_1_name': 'category',
'h_player_9_name': 'category',
'v_assists': 'float32',
'v_first_catcher_interference': 'float32',
'v_grounded_into_double': 'float32',
'v_player_1_id': 'category',
'v_player_3_id': 'category',
'v_player_5_id': 'category'}
Теперь мы сможем воспользоваться этим словарём вместе с несколькими параметрами, касающимися данных о датах проведения игр, в ходе загрузки данных.
Соответствующий код получается довольно-таки компактным:
read_and_optimized = pd.read_csv('game_logs.csv',dtype=column_types,parse_dates=['date'],infer_datetime_format=True)
print(mem_usage(read_and_optimized))
read_and_optimized.head()
В результате объём использования памяти выглядит так:
104.28 MB
Данные теперь выглядят так, как показано на листе Фрагмент оптимизированного набора данных
в этой таблице.
Внешне таблицы, приведённые на листах Фрагмент оптимизированного набора данных
и Фрагмент исходного набора данных
, за исключением столбца с датами, выглядят одинаково, но это касается лишь их внешнего вида. Благодаря оптимизации использования памяти в pandas нам удалось снизить потребление памяти с 861.6 Мбайт до 104.28 Мбайт, получив впечатляющий результат экономии 88% памяти.
Анализ бейсбольных матчей
Теперь, после того, как мы оптимизировали данные, мы можем заняться их анализом. Взглянем на распределение игровых дней.
optimized_gl['year'] = optimized_gl.date.dt.year
games_per_day = optimized_gl.pivot_table(index='year',columns='day_of_week',values='date',aggfunc=len)
games_per_day = games_per_day.divide(games_per_day.sum(axis=1),axis=0)
ax = games_per_day.plot(kind='area',stacked='true')
ax.legend(loc='upper right')
ax.set_ylim(0,1)
plt.show()
Дни, в которые проводились игры
Как видно, до 1920-х годов игры редко проводились по воскресеньям, после чего, примерно в течение 50 лет, игры в этот день постепенно проводились всё чаще.
Кроме того, можно заметить, что распределение дней недели, в которые проводились игры последние 50 лет, является практически неизменным.
Теперь взглянем на то, как со временем менялась длительность игр.
game_lengths = optimized_gl.pivot_table(index='year', values='length_minutes')
game_lengths.reset_index().plot.scatter('year','length_minutes')
plt.show()
Длительность игр
Возникает такое ощущение, что с 1940-х годов по настоящее время матчи становятся всё более длительными.
Итоги
В этом материале мы обсудили особенности хранения данных разных типов в pandas, после чего воспользовались полученными знаниями для уменьшения объёма памяти, необходимого для хранения объекта DataFrame
, почти на 90%. Для этого мы применили две простые методики:
- Мы произвели нисходящее преобразование типов числовых данных, хранящихся в столбцах, выбрав более эффективные, в плане использования памяти, типы.
- Мы преобразовали строковые данные к категориальному типу данных.
Нельзя сказать, что оптимизация любого набора данных способна привести к столь же впечатляющим результатам, но, особенно учитывая возможность выполнения оптимизации на этапе загрузки данных, можно говорить о том, что любому, кто занимается анализом данных с помощью pandas, полезно владеть методиками работы, которые мы здесь обсудили.
Уважаемые читатели! Перевести эту статью нам порекомендовал наш читатель eugene_bb. Если вам известны какие-нибудь интересные материалы, которые стоит перевести — расскажите нам о них.
Аналитикам: большая шпаргалка по Pandas
Привет. Я задумывал эту заметку для студентов курса Digital Rockstar, на котором мы учим маркетологов автоматизировать свою работу с помощью программирования, но решил поделиться шпаргалкой по Pandas со всеми. Я ожидаю, что читатель умеет писать код на Python хотя бы на минимальном уровне, знает, что такое списки, словари, циклы и функции.
- Что такое Pandas и зачем он нужен
- Структуры данных: серии и датафреймы
- Создаем датафреймы и загружаем в них данные
- Исследуем загруженные данные
- Получаем данные из датафреймов
- Считаем производные метрики
- Объединяем несколько датафреймов
- Решаем задачу
Что такое Pandas и зачем он нужен
Pandas — это библиотека для работы с данными на Python. Она упрощает жизнь аналитикам: где раньше использовалось 10 строк кода теперь хватит одной.
Например, чтобы прочитать данные из csv, в стандартном Python надо сначала решить, как хранить данные, затем открыть файл, прочитать его построчно, отделить значения друг от друга и очистить данные от специальных символов.
> with open('file.csv') as f: |
В Pandas всё проще. Во-первых, не нужно думать, как будут храниться данные — они лежат в датафрейме. Во-вторых, достаточно написать одну команду:
> data = pd.read_csv('file.csv') |
Pandas добавляет в Python новые структуры данных — серии и датафреймы. Расскажу, что это такое.
Структуры данных: серии и датафреймы
Серии — одномерные массивы данных. Они очень похожи на списки, но отличаются по поведению — например, операции применяются к списку целиком, а в сериях — поэлементно.
То есть, если список умножить на 2, получите тот же список, повторенный 2 раза.
> vector = [1, 2, 3] |
А если умножить серию, ее длина не изменится, а вот элементы удвоятся.
> import pandas as pd |
Обратите внимание на первый столбик вывода. Это индекс, в котором хранятся адреса каждого элемента серии. Каждый элемент потом можно получать, обратившись по нужному адресу.
> series = pd.Series(['foo', 'bar']) |
Еще одно отличие серий от списков — в качестве индексов можно использовать произвольные значения, это делает данные нагляднее. Представим, что мы анализируем помесячные продажи. Используем в качестве индексов названия месяцев, значениями будет выручка:
> months = ['jan', 'feb', 'mar', 'apr'] |
Теперь можем получать значения каждого месяца:
Так как серии — одномерный массив данных, в них удобно хранить измерения по одному. На практике удобнее группировать данные вместе. Например, если мы анализируем помесячные продажи, полезно видеть не только выручку, но и количество проданных товаров, количество новых клиентов и средний чек. Для этого отлично подходят датафреймы.
Датафреймы — это таблицы. У их есть строки, колонки и ячейки.
Технически, колонки датафреймов — это серии. Поскольку в колонках обычно описывают одни и те же объекты, то все колонки делят один и тот же индекс:
> months = ['jan', 'feb', 'mar', 'apr'] |
Объясню, как создавать датафреймы и загружать в них данные.
Создаем датафреймы и загружаем данные
Бывает, что мы не знаем, что собой представляют данные, и не можем задать структуру заранее. Тогда удобно создать пустой датафрейм и позже наполнить его данными.
А иногда данные уже есть, но хранятся в переменной из стандартного Python, например, в словаре. Чтобы получить датафрейм, эту переменную передаем в ту же команду:
> df = pd.DataFrame(data=sales, index=months)) |
Случается, что в некоторых записях не хватает данных. Например, посмотрите на список goods_sold
— в нём продажи, разбитые по товарным категориям. За первый месяц мы продали машины, компьютеры и программное обеспечение. Во втором машин нет, зато появились велосипеды, а в третьем снова появились машины, но велосипеды исчезли:
> goods_sold = [ |
Если загрузить данные в датафрейм, Pan
Введение в анализ данных с помощью Pandas / Хабр
Сегодня речь пойдет о пакете Pandas. Данный пакет делает Python мощным инструментом для анализа данных. Пакет дает возможность строить сводные таблицы, выполнять группировки, предоставляет удобный доступ к табличным данным, а при наличии пакета matplotlib дает возможность рисовать графики на полученных наборах данных. Далее будут показаны основы работы с пакетом, такие как загрузка данных, обращение к полям, фильтрация и построение сводных.
Основные структуры данных и их загрузка
Для начала, скажем, пару слов о структурах хранения данных в Pandas. Основными являются Series и DataFrame.
Series – это проиндексированный одномерный массив значений. Он похож на простой словарь типа dict, где имя элемента будет соответствовать индексу, а значение – значению записи.
DataFrame — это проиндексированный многомерный массив значений, соответственно каждый столбец DataFrame, является структурой Series.
Итак, со структурами чуток разобрались. Перейдем непосредственно к работе с пакетом. Для начала анализа каких-либо данных их надо загрузить. Pandas предоставляет широкий выбор источников данных, например:
- SQL
- Текстовые файлы
- Excel файлы
- HTML
Подробней о них можно прочитать в документации.
Для пример загрузим 2 текстовых файла. Это можно сделать функцией read_csv():
from pandas import read_csv
df1 = read_csv("df1.txt")
df2 = read_csv("df2.txt",";") #второй аргумент задает разделитель
Теперь у нас есть 2 набора данных df1, содержащий магазины и количество отгрузок:
shop | qty |
---|---|
427 | 3 |
707 | 4 |
957 | 2 |
437 | 1 |
И df2, содержащий магазин и его город:
shop | name |
---|---|
347 | Киев |
427 | Самара |
707 | Минск |
957 | Иркутск |
437 | Москва |
Базовые операции с наборами данных
Над наборами данных можно выполнять различные действия, например объединение, добавление столбцов, добавление записей, фильтрация, построение сводных и другие. Давайте теперь, чтобы продемонстрировать все описанные выше возможности, следующие задачи:
- в набор с городами магазинов добавим поле `country` и заполним соответствующими странами
- выберем украинский магазин и поменяем его номер
- добавим магазин, полученный на предыдущем шаге, к общему списку
- добавим количество из df1 к набору df2
- построим сводную таблицу по странам и количеству отгрузок
Итак, для добавления нового столбца в набор данных существует команда insert():
country = [u'Украина',u'РФ',u'Беларусь',u'РФ',u'РФ']
df2.insert(1,'country',country)
В нашем случае функции передается 3 аргумент:
- номер позиции, куда будет вставлен новый столбец
- имя нового столбца
- массив значений столбца (в нашем случае, это обычный список list)
Вид df2 после выполнения выше описанных операций:
shop | country | name |
---|---|---|
347 | Украина | Киев |
427 | РФ | Самара |
707 | Беларусь | Минск |
957 | РФ | Иркутск |
437 | РФ | Москва |
Теперь на надо выбрать магазин, у которого страна будет равна `Украина`. Для обращения к столбцам в DataFrame существует 2 способа:
- через точку — НаборДанных.ИмяПоля
- в квадратных скобках – НаборДанных[‘ИмяПоля’]
Для того, чтобы отфильтровать набор данных можно использовать квадратные скобки внутри которых будет условие НаборДанных[условие]. Условие должно содержать имена полей, в формате описанном выше, и условие, налагаемое на них. Таким образом выбрать брать интересующий магазин и заменить его номер можно так:
t = df2[df2.country == u'Украина']
t.shop = 345
Результатом выполнения данного кода, будет новый промежуточный набор данных t, содержащий одну запись:
shop | country | name |
---|---|---|
345 | Украина | Киев |
Для того чтобы добавить полученную на предыдущем шаге запись, нужно выполнить функцию append(), в качестве аргумента которой передается набор данных, который нужно добавить к исходному:
df2 = df2.append(t)
Агрегация данных
Теперь к нашему основному списку магазинов df2, можно подтянуть количество из набора данных df1. Сделать это можно с помощью функции merge(), которая соединяет два набора данных (аналог join в SQL):
res = df2.merge(df1, 'left', on='shop')
В качестве параметров функция принимает:
- набор данных (который будет присоединен к исходному)
- тип соединения
- поле, по которому происходит соединение
Подробнее о параметрах можно прочитать в документации. Набор данных перед финальной операцией выглядит так:
shop | country | name | qty |
---|---|---|---|
347 | Украина | Киев | NaN |
427 | РФ | Самара | 3 |
707 | Беларусь | Минск | 4 |
957 | РФ | Иркутск | 2 |
437 | РФ | Москва | 1 |
345 | Украина | Киев | NaN |
Осталось построить сводную таблицу, чтобы понять, какое количество по каждой стране отгружено. Для этого существует функция pivot_table(). В нашем примере функция в качестве параметров принимает:
- список столбцов, по которым будет считаться агрегированные значение
- список столбцов, которые будут строками итоговой таблицы
- функция, которая используется для агрегации
- параметр для замены пустых значений на 0
Код для построения сводной выглядит так:
res.pivot_table(['qty'],['country'], aggfunc='sum', fill_value = 0)
Итоговая таблица будет выглядеть так:
country | qty |
---|---|
Беларусь | 4 |
РФ | 6 |
Украина | 0 |
Заключение
В качестве заключения хотелось бы сказать, Pandas является неплохой альтернативой Excel при работе с большими объемами данных. Показанные функции это только верхушка айсберга под название Pandas. В дальнейшем, я планирую написать серию статей в которых будет показана вся мощь данного пакета.
4 способа установки библиотеки Pandas и первый запуск ~ PythonRu
Библиотека pandas в Python — это идеальный инструмент для тех, кто занимается анализом данных, используя для этого язык программирования Python.
В этом материале речь сначала пойдет об основных аспектах библиотеки и о том, как установить ее в систему. Потом вы познакомитесь с двумя структурам данных: series
и dataframes
. Сможете поработать с базовым набором функций, предоставленных библиотекой pandas, для выполнения основных операций по обработке. Знакомство с ними — ключевой навык для специалиста в этой сфере. Поэтому так важно перечитать материал до тех, пока он не станет понятен на 100%.
А на примерах сможете разобраться с новыми концепциями, появившимися в библиотеке — индексацией структур данных. Научитесь правильно ее использовать для управления данными. В конце концов, разберетесь с тем, как расширить возможности индексации для работы с несколькими уровнями одновременно, используя для этого иерархическую индексацию.
Библиотека Python для анализа данных
Pandas — это библиотека Python с открытым исходным кодом для специализированного анализа данных. Сегодня все, кто использует Python для изучения статистических целей анализа и принятия решений, должны быть с ней знакомы.
Библиотека была спроектирована и разработана преимущественно Уэсом Маккини в 2008 году. В 2012 к нему присоединился коллега Чан Шэ. Вместе они создали одну из самых используемых библиотек в сообществе Python.
Pandas появилась из необходимости в простом инструменте для обработки, извлечения и управления данными.
Этот пакет Python спроектирован на основе библиотеки NumPy. Такой выбор обуславливает успех и быстрое распространение pandas. Он также пользуется всеми преимуществами NumPy и делает pandas совместимой с большинством другим модулей.
Еще одно важное решение — разработка специальных структур для анализа данных. Вместо того, чтобы использовать встроенные в Python или предоставляемые другими библиотеками структуры, были разработаны две новых.
Они спроектированы для работы с реляционными и классифицированными данными, что позволяет управлять данными способом, похожим на тот, что используется в реляционных базах SQL и таблицах Excel.
Дальше вы встретите примеры базовых операций для анализа данных, которые обычно используются на реляционных или таблицах Excel. Pandas предоставляет даже более расширенный набор функций и методов, позволяющих выполнять эти операции эффективнее.
Основная задача pandas — предоставить все строительные блоки для всех, кто погружается в мир анализа данных.
Установка pandas
Простейший способ установки библиотеки pandas — использование собранного решения, то есть установка через Anaconda или Enthought.
Установка в Anaconda
В Anaconda установка занимает пару минут. В первую очередь нужно проверить, не установлен ли уже pandas, и если да, то какая это версия. Для этого введите следующую команду в терминале:
conda list pandas
Если модуль уже установлен (например в Windows), вы получите приблизительно следующий результат:
# packages in environment at C:\Users\Fabio\Anaconda:
#
pandas 0.20.3 py36hce827b7_2
Если pandas не установлена, ее необходимо установить. Введите следующую команду:
conda install pandas
Anaconda тут же проверит все зависимости и установит дополнительные модули.
Solving environment: done
Environment location: C:\Users\Fabio\Anaconda3
added / updated specs:
- pandas
The following new packages will be installed:
Pandas: 0.22.0-py36h6538335_0
Proceed ([y]/n)?
Press the y key on your keyboard to continue the installation.
Preparing transaction: done
Verifying transaction: done
Executing transaction: done
Если требуется обновить пакет до более новой версии, используется эта интуитивная команда:
conda update pandas
Система проверит версию pandas и версию всех модулей, а затем предложит соответствующие обновления. Затем предложит перейти к обновлению.
Установка из PyPI
Pandas можно установить и с помощью PyPI, используя эту команду:
pip install pandas
Установка в Linux
Если вы работаете в дистрибутиве Linux и решили не использовать эти решения, то pandas можно установить как и любой другой пакет.
В Debian и Ubuntu используется команда:
sudo apt-get install python-pandas
А для OpenSuse и Fedora — эта:
zypper in python-pandas
Установка из источника
Если есть желание скомпилировать модуль pandas из исходного кода, тогда его можно найти на GitHub по ссылке https://github.com/pandas-dev/pandas:
git clone git://github.com/pydata/pandas.git
cd pandas
python setup.py install
Убедитесь, что Cython установлен. Больше об этом способе можно прочесть в документации: (http://pandas.pydata.org/pandas-docs/stable/install.html).
Репозиторий для Windows
Если вы работаете в Windows и предпочитаете управлять пакетами так, чтобы всегда была установлена последняя версия, то существует ресурс, где всегда можно загрузить модули для Windows: Christoph Gohlke’s Python Extension Packages for Windows (www.lfd.uci.edu/~gohlke/pythonlibs/). Каждый модуль поставляется в формате WHL для 32 и 64-битных систем. Для установки нужно использовать приложение pip:
pip install SomePackage-1.0.whl
Например, для установки pandas потребуется найти и загрузить следующий пакет:
pip install pandas-0.22.0-cp36-cp36m-win_amd64.whl
При выборе модуля важно выбрать нужную версию Python и архитектуру. Более того, если для NumPy пакеты не требуются, то у pandas есть зависимости. Их также необходимо установить. Порядок установки не имеет значения.
Недостаток такого подхода в том, что нужно устанавливать пакеты отдельно без менеджера, который бы помог подобрать нужные версии и зависимости между разными пакетами. Плюс же в том, что появляется возможность освоиться с модулями и получить последние версии вне зависимости от того, что выберет дистрибутив.
Проверка установки pandas
Библиотека pandas может запустить проверку после установки для верификации управляющих элементов (документация утверждает, что тест покрывает 97% всего кода).
Во-первых, нужно убедиться, что установлен модуль nose
. Если он имеется, то тестирование проводится с помощью следующей команды:
nosetests pandas
Оно займет несколько минут и в конце покажет список проблем.
Модуль Nose
Этот модуль спроектирован для проверки кода Python во время этапов разработки проекта или модуля Python. Он расширяет возможности модуль
unittest
. Nose используется для проверки кода и упрощает процесс.Здесь о нем можно почитать подробнее: _http://pythontesting.net/framework/nose/nose-introduction/.
Первые шаги с pandas
Лучший способ начать знакомство с pandas — открыть консоль Python и вводить команды одна за одной. Таким образом вы познакомитесь со всеми функциями и структурами данных.
Более того, данные и функции, определенные здесь, будут работать и в примерах будущих материалов. Однако в конце каждого примера вы вольны экспериментировать с ними.
Для начала откройте терминал Python и импортируйте библиотеку pandas. Стандартная практика для импорта модуля pandas следующая:
>>> import pandas as pd
>>> import numpy as np
Теперь, каждый раз встречая pd
и np
вы будете ссылаться на объект или метод, связанный с этими двумя библиотеками, хотя часто будет возникать желание импортировать модуль таким образом:
>>> from pandas import *
В таком случае ссылаться на функцию, объект или метод с помощью pd
уже не нужно, а это считается не очень хорошей практикой в среде разработчиков Python.
10 трюков библиотеки Python Pandas, которые вам нужны
Любите панд? Мы тоже. А еще мы любим эффективный код, поэтому собрали классные трюки, которые облегчат работу с библиотекой Python Pandas.
Некоторые команды уже знакомы? А вы не пробовали использовать их таким образом? 😉
Все знают эту команду. Но когда данные, которые вы пытаетесь обработать, большие, попробуйте добавить аргумент nrows = 5, чтобы прочитать только крошечную часть таблицы перед фактической загрузкой всей таблицы. Зачем? Так удастся избежать ошибки с выбором неправильного разделителя, ведь это не всегда запятая.
Или используйте head в Linux, чтобы вывести первые, скажем, 5 строк из любого текстового файла: head –n 5 data.txt.
Извлеките список столбцов с df.columns.tolist()
. Добавьте аргумент usecols = [‘c1’, ‘c2’,…], чтобы загрузить только необходимые столбцы. Кроме того, если знаете типы данных нескольких конкретных столбцов, добавьте аргумент dtype = {‘c1’: str, ‘c2’: int,…}. Так загрузка будет быстрее. Этот аргумент даёт ещё одно преимущество. Если один столбец содержит строки и числа, рекомендуется объявить его тип строковым. Так вы избежите ошибок при объединении таблиц, когда используете этот столбец как ключ.
Если нужна предварительная обработка данных на языке Python, то эта команда сэкономит вам время. После чтения из таблицы типами данных по умолчанию для каждого столбца будут bool, int64, float64, object, category, timedelta64 или datetime64. Можете сначала проверить распределение с помощью
df.dtypes.value_counts()
чтобы узнать все типы данных объекта DataFrame. Далее выполните
df.select_dtypes(include = ['float64', 'int64'])
чтобы выбрать подмножество объекта DataFrame только с числовыми характеристиками.
Это важная команда, если ещё не слышали о ней. Когда выполните следующие операции
import pandas as pd df1 = pd.DataFrame({ 'a':[0,0,0], 'b': [1,1,1]}) df2 = df1 df2['a'] = df2['a'] + 1 df1.head()
обнаружите, что df1 изменён. Это потому, что df2 = df1 не делает копию df1 и не присваивает это значение df2, а устанавливает указатель, который ссылается на df1. Таким образом, любые изменения в df2 приведут к изменениям в df1. Чтобы это исправить, сделайте
df2 = df1.copy()
или же
from copy import deepcopy df2 = deepcopy(df1)
Это классная команда для простого преобразования данных. Определяете словарь, в котором «ключами» являются старые значения, а «значениями» – новые значения:
level_map = {1: 'high', 2: 'medium', 3: 'low'} df['c_level'] = df['c'].map(level_map)
Некоторые примеры:
- трансформация true, false в 1, 0 для моделирования;
- определение уровней;
- установленные пользователем кодировки.
Нужен новый столбец с несколькими другими столбцами в качестве входных данных? Функция apply спешит на помощь!
def rule(x, y): if x == 'high' and y > 10: return 1 else: return 0 df = pd.DataFrame({ 'c1':[ 'high' ,'high', 'low', 'low'], 'c2': [0, 23, 17, 4]}) df['new'] = df.apply(lambda x: rule(x['c1'], x['c2']), axis = 1) df.head()
Здесь определена функцию с двумя входными переменными, и с помощью функции apply применяем к столбцам ‘c1’ и ‘c2’.
Но проблема применения «apply» в том, что иногда это слишком медленно. Например, вам надо рассчитать максимум для ‘c1’ и ‘c2’. Конечно, можно сделать
df['maximum'] = df.apply(lambda x: max(x['c1'], x['c2']), axis = 1)
но это будет намного медленнее, чем
df['maximum'] = df[['c1','c2']].max(axis =1)
Вывод: не используйте apply, если можете выполнить ту же работу с помощью других встроенных функций, которые часто работают быстрее. Например, если нужно округлить столбец ‘c’ до целых чисел, выполните round(df[‘c’], 0) или df[‘c’].round(0) вместо apply: df.apply(lambda x: round(x['c'], 0), axis = 1)
.
Команда для проверки распределения значений. Чтобы проверить возможные значения и частоту каждого отдельного значения в столбце ‘c’, выполните:
df['c'].value_counts()
Некоторые полезные трюки и аргументы этой функции:
- normalize = True – проверить частоту вместо подсчёта.
- dropna = False – включить пропущенные значения в статистику.
df['c'].value_counts().reset_index()
– преобразовать таблицу статистики в объект Pandas DataFrame.df['c'].value_counts().reset_index().sort_values(by='index')
– показывать статистику, отсортированную по уникальным значениям в столбце ‘c’ вместо количества.
При построении моделей часто надо исключить строку с большим количеством пропущенных значений или строки со всеми пропущенными значениями. Используйте .isnull() и .sum() для подсчёта количества пропущенных значений в указанных столбцах:
import pandas as pd import numpy as np df = pd.DataFrame({ 'id': [1,2,3], 'c1':[0,0,np.nan], 'c2': [np.nan,1,1]}) df = df[['id', 'c1', 'c2']] df['num_nulls'] = df[['c1', 'c2']].isnull().sum(axis=1) df.head()
В SQL используем SELECT * FROM… WHERE ID в («A001», «C022»,…) и получаем записи с конкретными идентификаторами. Если хотите сделать то же с помощью Python библиотеки Pandas, используйте
df_filter = df['ID'].isin(['A001','C022',...]) df[df_filter]
У нас есть числовой столбец. Хотим классифицировать значения в этом столбце по группам:
- верхние 5% в группе 1
- 5–20% в группе 2
- 20–50% в группе 3
- нижние 50% в группе 4
Конечно, можно сделать это с помощью pandas.cut, но рассмотрим другой вариант
import numpy as np cut_points = [np.percentile(df['c'], i) for i in [50, 80, 95]] df['group'] = 1 for i in range(3): df['group'] = df['group'] + (df['c'] < cut_points[i]) # или <= cut_points[i]
который быстро запускается, потому что не применяется функция apply.
Опять же, это команда, которую все будут использовать. Здесь расскажем о двух трюках. Первый:
print(df[:5].to_csv())
Используйте эту команду для вывода первых пяти строк того, что будет в точности записано в файл.
Ещё один трюк касается смешения целых чисел и пропущенных значений. Если столбец содержит как пропущенные значения, так и целые числа, типом данных по-прежнему будет float, а не int. Когда экспортируете таблицу, добавьте float_format = ‘%. 0f’, чтобы округлить числа с плавающей точкой до целых чисел. Используйте этот трюк, когда нужны только целочисленные выходные данные столбцов – так избавитесь от надоедливых ‘.0’.
Это перевод статьи о трюках библиотеки Python Pandas на Towards Data Science.
Интересуетесь Data Science и Python? Читайте статьи по теме:
Какой трюк пакета Python Pandas попробуете в ближайшее время?
Изучаем Pandas. Урок 1. Введение в Pandas и его установка
Это первый урок из цикла, посвященного библиотеке pandas. Данный цикл будет входить в большую группу обучающих материалов, тематику которых можно определить как “Машинное обучение и анализ данных”. pandas – это удобный и быстрый инструмент для работы с данными, обладающий большим функционалом.
- Что такое pandas?
- Установка pandas
Если очень кратко, то pandas – это библиотека, которая предоставляет очень удобные с точки зрения использования инструменты для хранения данных и работе с ними. Если вы занимаетесь анализом данных или машинным обучением и при этом используете язык Python, то вы просто обязаны знать и уметь работать с pandas.
pandasвходи в группу проектов, спонсируемых numfocus. Numfocus – это организация, которая поддерживает различные проекты, связанные с научными вычислениями.
Официальный сайт pandas находится здесь. Стоит отметить, что документация по этому продукту очень хорошая. Если вы знаете английский язык, то для вас не будет большой проблемой разобраться с pandas.
Особенность pandas состоит в том, что эта библиотека очень быстрая, гибкая и выразительная. Это важно, т.к. она используется с языком Python, который не отличается высокой производительностью. pandas прекрасно подходит для работы с одномерными и двумерными таблицами данных, хорошо интегрирован с внешним миром – есть возможность работать с файлами CSV, таблицами Excel, может стыковаться с языком R.
Для проведения научных расчетов, анализа данных или построения моделей в рамках машинно обучения для языка Python существуют прекрасное решение – Anaconda. Anaconda – это пакет, который содержит в себе большой набор различных библиотек, интерпретатор языка Python и несколько сред для разработки. Подробно об установке пакета Anaconda написано в этой статье.
pandas присутствует в стандартной поставке Anaconda. Если же его там нет, то его можно установить отдельно. Для этого стоит воспользоваться пакетным менеджером, который входит в состав Anaconda, который называется conda. Для его запуска необходимо перейти в каталог [Anaconda install path]\Scripts\ в Windows. В операционной системе Linux, после установки Anaconda менеджер conda должен быть доступен везде.
Введите командной строке:
>conda install pandas
В случае, если требуется конкретная версия pandas, то ее можно указать при установке.
>conda install pandas=0.13.1
При необходимости, можно воспользоваться пакетным менеджером pip, входящим в состав дистрибутива Python.
>pip install pandas
Если вы используете Linux, то ещё одни способ установить pandas – это воспользоваться пакетным менеджером самой операционной системы. Для Ubuntu это выглядит так:
>sudo apt-get install python-pandas
После установки необходимо проверить, что pandas установлен и корректно работает. Для этого запустите интерпретатор Python и введите в нем следующие команды.
>>> import pandas as pd >>> pd.test()
В результате в окне терминала должен появиться следующий текст:
Running unit tests for pandas pandas version 0.18.1 numpy version 1.11.1 pandas is installed in c:\Anaconda3\lib\site-packages\pandas Python version 3.5.2 |Anaconda 4.1.1 (64-bit)| (default, Jul 5 2016, 11:41:13) [MSC v.1900 64 bit (AMD64)] nose version 1.3.7 .......... ---------------------------------------------------------------------- Ran 11 tests in 0.422s OK
Это будет означать, что pandas установлен и его можно использовать.
P.S.
Все уроки по библиотеке Pandas собраны в книге “Pandas. Работа с данными”.
<<<Урок 2. Структуры данных Series и DataFrame
Pandas для самых маленьких: основы анализа данных
Pandas – Python-библиотека обработки и анализа данных. Ее используют для чтения и манипулирования данными, упакованные в датасет. Датасет может быть представлен в формате csv, json, html, xlsx и т.д. Чаще всего используется csv – формат, в котором значения разделены запятой. Чтобы скачать библиотеку Pandas введите в командной строке:
pip install pandas
В качестве примера будем использовать датасет с олимпийскими играми, содержащий данные о спортсменах с 1896 по 2016 года. Скачать датасет можно на сайте Kaggle – площадки для соревнований по машинному обучению.
Анализ данных с Pandas
В основе Pandas лежит DataFrame. DataFrame – двумерное (табличное) представление данных. Один из способов создания DataFrame – чтение датасета, например в формате csv. Для начала импортируем библиотеку pandas и будем обращаться к ней как pd
:
import pandas as pd data = pd.read_csv('athlete_events.csv')
Метод read_csv
читает датасет и возвращает DataFrame. Чтобы посмотреть первые пять строк вызываем метод head
:
data.head()
Данные выглядят следующим образом:
Отображение 5 первых строк в Pandas
Считаем количество строк и столбцов
Как видно, здесь присутствуют пустые записи – NaN (c ними поработаем чуть позже). Чтобы посмотреть сколько всего строк и столбцов используем метод shape:
>>> data.shape (271116, 15)
Следовательно, в датасете 271.116 строк и 15 столбцов.
Для просмотра названий столбцов используем метод index
:
>>> data.index Index(['ID', 'Name', 'Sex', 'Age', 'Height', 'Weight', 'Team', 'NOC', 'Games', 'Year', 'Season', 'City', 'Sport', 'Event', 'Medal'], dtype='object')
Извлекаем отдельные столбцы в Pandas
Из DataFrame можно извлекать отдельные столбцы. DataFrame имеет сходный синтаксис со словарем (dict), т.е. обращаться к значениям можно через квадратные скобки. Например, посмотрим на столбец Age:
>>> data['Age'] 0 24.0 1 23.0 2 24.0 3 34.0 4 21.0 ... 271111 29.0 271112 27.0 271113 27.0 271114 30.0 271115 34.0 Name: Age, Length: 271116, dtype: float64
Стоит заметить, что такое обращение к столбцу возвращает объект Series. Series – одномерное (столбчатое) представление данных. Можно сказать, что DataFrame представлен коллекцией Series.
Обращаться можно и к нескольким столбцам. Используя список (list) необходимых столбцов, можно получить новый датафрейм:
data[['Name', 'City', 'Sport']]
В результате получаем:
Отображение столбцов в Pandas
Name, City, Sport являются элементами списка. Для обращения используются квадратные скобки, так же как и с одним значением.
Извлекаем отдельные строки в Pandas
Извлечение строк также возможно с помощью метод iloc
. Вот так выглядит обращение к одной строчке под номером 100:
>>> data.iloc[100] ID 36 Name Stefan Remco Aartsen Sex M Age 21 Height 194 Weight 78 Team Netherlands NOC NED Games 1996 Summer Year 1996 Season Summer City Atlanta Sport Swimming Event Swimming Men's 100 metres Butterfly Medal NaN Name: 100, dtype: object
Несколько строк извлекаем, устанавливая диапазон start и stop в методе iloc
через двоеточие:
data.iloc[100:105]
Отображение строк с 100 по 104
Извлекаем oтдельные строки и столбцы одновременно
Извлекаем строки и столбцы одновременно, соединяя оба метода:
data.iloc[100:105][['Name', 'City', 'Sport']]
Заметим также, порядок не извлечения не важен. Аналогично можно написать следующее:
Извлечение строк и столбцов одновременно
data[['Name', 'City', 'Sport']].iloc[100:105]
что приведет к тому же результату.
Описательная статистика и пустые значения
Для числовых значений можно получить описательную статистику методом describe
:
data.describe()
Описательная статистика
Пробежимся по атрибутам описательной статистики:
- сount обозначает количество записей,
- mean – среднее арифметическое,
- std – стандартное отклонение,
- min – минимальное значение,
- n-ый (25, 50, 75) % – n-ый квартиль,
- max – максимальное значение.
Строки с пустыми (NaN)значениями отбрасываются методом drop
. У drop есть параметр subset, уточняющий, в каком столбце удалять; если его не указать, то отбросятся строки с NaN’ами каждого из столбцов. Удалим пустые значения Medal:
>>> data.dropna(subset=['Medal'])['Medal'] 3 Gold 37 Bronze 38 Bronze 40 Bronze 41 Bronze ... 271078 Silver 271080 Bronze 271082 Bronze 271102 Bronze 271103 Silver Name: Medal, Length: 39783, dtype: object
Как можно заметить, этот столбец имеет только 39.783 не NaN значений тогда, когда всего 271.116.
После всех манипуляций с данными не забудьте переприсвоить изменения. Вышеперечисленные методы создают новый DataFrame, а не изменяют старый. Например, чтобы в дальнейшем иметь дело с тремя столбцами, пишем:
data = data[['Name', 'City', 'Sport']]
Подведем итоги
read_csv
создает DataFrame из датасета.- Метод
head
вызывает 5 первых строк. - Атрибут
shape
показывает количество строк и столбцов. - Атрибут
index
возвращает список названий столбцов. - Для извлечения столбца или столбцов указывается в квадратных скобках название столбца или список названий, соответственно.
- Метод
iloc
возвращает строки. - Метод
describe
показывает описательную статистику. - Метод
drop
отбрасывает пустые строки.
В нашей следующей статье мы познакомимся с 5 методами визуализации данных в matplotlib. На курсах по Python в нашем лицензированном учебном центре обучения и повышения квалификации ИТ-специалистов в Москве вы узнаете о еще большем применении Pandas на практике.
Учебники
— документация pandas 0.15.2
Это руководство ко многим руководствам по пандам, предназначенное в основном для новых пользователей.
панды Поваренная книга
Цель этой поваренной книги (написанной Джулией Эванс) —
дать вам несколько конкретных примеров для начала работы с пандами. Эти
примеры с реальными данными, а также все ошибки и странности, которые
что влечет за собой.
Вот ссылки на выпуск v0.1. Актуальное оглавление см. В pandas-cookbook на GitHub.
репозиторий.Чтобы запустить примеры из этого руководства, вам необходимо
клонируйте репозиторий GitHub и запустите IPython Notebook.
См. Как пользоваться этой поваренной книгой.
- Краткий обзор ноутбука IPython:
Демонстрирует потрясающее завершение вкладок и волшебные функции IPython. - Глава 1:
Считывание ваших данных в pandas — самая простая вещь. Даже
когда кодировка неправильная! - Глава 2:
Не совсем очевидно, как выбирать данные из фрейма данных pandas.
Здесь мы объясняем основы (как делать срезы и получать столбцы) - Глава 3:
Здесь мы серьезно подходим к нарезке и нарезке кубиками и узнаем, как фильтровать
dataframes сложными способами, очень быстро. - Глава 4:
Groupby / aggregate — моя любимая вещь в пандах
и я использую его постоянно. Тебе, наверное, стоит это прочитать. - Глава 5:
Здесь вы узнаете, холодно ли в Монреале зимой
(спойлер: да). Очистка веб-страниц с помощью панд — это весело! Здесь мы объединяем фреймы данных. - Глава 6:
Струны с пандами — это здорово. В нем есть все эти векторизованные строки
операции, и они лучшие. Будем крутить связку ниток
содержащий «Снег» в векторах чисел трижды. - Глава 7:
Очистка беспорядочных данных никогда не бывает радостью, но с пандами это проще. - Глава 8:
Анализ временных меток Unix сначала сбивает с толку, но оказывается
быть действительно легким.
Уроки для новых пользователей pandas
Для получения дополнительных ресурсов посетите главный репозиторий.
- 01 — Урок:
— Импорт библиотек
— Создание наборов данных
— Создание фреймов данных
— Чтение из CSV
— Экспорт в CSV
— Поиск максимумов
— Построение данных - 02 — Урок:
— Чтение из TXT
— Экспорт в TXT
— Выбор верхних / нижних записей
— Описательная статистика
— Группировка / сортировка данных - 03 — Урок:
— Создание функций
— Чтение из EXCEL
— Экспорт в EXCEL
— выбросы
— Лямбда-функции
— Данные нарезки и кости - 04 — Урок:
— Добавление / удаление столбцов
— Индексные операции - 05 — Урок:
— Функции Stack / Unstack / Transpose - 06 — Урок:
— Функция GroupBy - 07 — Урок:
— Способы вычисления выбросов - 08 — Урок:
— Чтение из баз данных Microsoft SQL - 09 — Урок:
— Экспорт в CSV / EXCEL / TXT - 10 — Урок:
— Преобразование между различными форматами - 11 — Урок:
— Объединение данных из разных источников
Практический анализ данных с Python
Это руководство представляет собой всестороннее введение в процесс анализа данных с использованием экосистемы данных Python и интересного открытого набора данных.Четыре раздела посвящены выбранным темам:
диаграммы Excel с пандами, винсентом и xlsxwriter
.Обзор пакета
— документация pandas 1.1.2
pandas — это пакет Python, обеспечивающий быструю,
гибкие и выразительные структуры данных, предназначенные для работы с
«Реляционные» или «помеченные» данные простые и интуитивно понятные. Он стремится стать
фундаментальный строительный блок высокого уровня для практических, реальных данных
анализ на Python. Кроме того, у него есть более широкая цель — стать
самый мощный и гибкий инструмент анализа / обработки данных с открытым исходным кодом
доступно на любом языке .Он уже идет к этой цели.
pandas хорошо подходят для разных типов данных:
Табличные данные с разнотипными столбцами, как в таблице SQL или
Таблица ExcelУпорядоченные и неупорядоченные (не обязательно с фиксированной частотой) данные временных рядов.
Произвольные матричные данные (однородно типизированные или неоднородные) со строкой и
подписи столбцовЛюбая другая форма наборов наблюдательных / статистических данных.Данные на самом деле
не нужно помечать вообще, чтобы поместить в структуру данных pandas
Две первичные структуры данных панд, Серия
(1-мерный)
и DataFrame
(2-мерный), обрабатывают подавляющее большинство типичных
случаев в финансах, статистике, социальных науках и многих областях
инженерия. Для пользователей R DataFrame
предоставляет все, что R’s
data.frame
предоставляет и многое другое. pandas построен на основе NumPy и предназначен для хорошей интеграции в научный
вычислительная среда со многими другими сторонними библиотеками.
Вот лишь некоторые из вещей, которые хорошо умеют делать панды:
Простая обработка недостающих данных (представленных как NaN) с плавающей запятой как
а также данные с плавающей запятойИзменчивость размера: столбцов можно вставить и удалить из DataFrame и
объекты более высоких измеренийАвтоматическое и явное выравнивание данных : объекты могут быть явно
выровнен по набору меток, или пользователь может просто игнорировать метки и
пусть Series , DataFrame и т. д.автоматически выравнивать данные для вас в
вычисленияМощная, гибкая группировка функциональность для выполнения
операции разделения-применения-объединения с наборами данных, как для агрегирования, так и для
преобразование данныхСделайте простым преобразованием разорванных данных с разными индексами в другие
Структуры данных Python и NumPy в объекты DataFrameИнтеллектуальная нарезка на основе этикеток , произвольная индексация и поднабор
больших наборов данныхИнтуитивно понятный объединение и объединение наборов данных
Гибкий изменение формы и поворот наборов данных
Иерархическая маркировка осей (возможно иметь несколько меток на
галочка)Надежные инструменты ввода-вывода для загрузки данных из плоских файлов (CSV и с разделителями),
Файлы Excel, базы данных и сохранение / загрузка данных из сверхбыстрого HDF5
форматВременные ряды — специфическая функциональность: создание диапазона дат и частота
преобразование, статистика движущегося окна, сдвиг даты и запаздывание.
Многие из этих принципов используются для частого устранения недостатков.
опыт использования других языков / среды научных исследований. Для данных
ученых, работа с данными обычно делится на несколько этапов:
сбор и очистка данных, их анализ / моделирование, а затем организация результатов
анализа в форму, подходящую для графического или табличного отображения. панды
является идеальным инструментом для всех этих задач.
Некоторые другие примечания
pandas — это быстро .Многие из низкоуровневых алгоритмических битов были
сильно настроен в коде Cython. Однако, как и в случае с
все остальное обобщение обычно приносит в жертву производительность. Итак, если вы сосредоточитесь
на одной функции для вашего приложения вы сможете создать более быстрый
специализированный инструмент.pandas — это зависимость от statsmodels, что делает его важной частью
экосистема статистических вычислений в Python.pandas широко используются в производстве финансовых приложений.
Структуры данных¶
Размеры | Имя | Описание |
---|---|---|
1 | Серия | Одномерный массив с однородной типизацией |
2 | DataFrame | Общая двухмерная маркированная табличная структура с изменяемым размером и потенциально гетерогенно типизированным столбцом |
Почему более одной структуры данных? ¶
Лучший способ думать о структурах данных pandas — это гибкость
контейнеры для данных меньшего размера.Например, DataFrame — это контейнер
for Series, а Series — это контейнер для скаляров. Мы хотели бы быть
возможность вставлять и удалять объекты из этих контейнеров в словарях
мода.
Также мы хотели бы разумного поведения по умолчанию для общих функций API.
которые учитывают типичную ориентацию временных рядов и
наборы данных поперечного сечения. При использовании ndarrays для хранения 2- и 3-мерных
данных, на пользователя возлагается бремя учитывать ориентацию данных
устанавливается при написании функций; оси считаются более или менее эквивалентными (кроме
когда непрерывность C или Fortran важна для производительности).В пандах топоры
предназначены для придания данным более семантического значения; т.е. для конкретного
набор данных, вероятно, будет «правильный» способ ориентировать данные. Цель,
то, чтобы уменьшить количество умственных усилий, необходимых для кодирования данных
преобразования в подчиненных функциях.
Например, с табличными данными (DataFrame) семантически более полезно
подумайте об индексе (строки) и столбцах , а не о оси 0 и
ось 1. Таким образом, перебор столбцов DataFrame дает больше
читаемый код:
для столбца в df.столбцы: series = df [col] # сделать что-нибудь с сериалом
Изменчивость и копирование данных¶
Все структуры данных pandas изменяют значения (значения, которые они содержат, могут быть
изменен), но не всегда изменяемый по размеру. Длина серии не может быть
изменено, но, например, столбцы можно вставить в DataFrame. Однако,
подавляющее большинство методов создают новые объекты и оставляют входные данные
нетронутый. В общем, нам нравится , предпочитаем неизменность , где это разумно.
Получение поддержки¶
Первая остановка для проблем и идей pandas — это трекер проблем Github. Если у вас есть общий вопрос,
Эксперты сообщества pandas могут ответить через Stack Overflow.
Сообщество¶
панд сегодня активно поддерживается сообществом единомышленников вокруг
мир, который вкладывает свое драгоценное время и энергию в создание открытого исходного кода
панды возможны. Спасибо всем нашим участникам.
Если вы заинтересованы в участии, посетите руководство для авторов.
панды — это проект, спонсируемый NumFOCUS.
Это поможет обеспечить успех разработки pandas как open-source мирового класса.
проект, и дает возможность сделать пожертвование проекту.
Управление проектом¶
Процесс управления, который проект pandas неофициально использовал с момента его создания в 2008 году, формализован в документах по управлению проектом.
В документах разъясняется, как принимаются решения и как взаимодействуют различные элементы нашего сообщества, в том числе отношения между совместной разработкой с открытым исходным кодом и работой, которая может финансироваться коммерческими или некоммерческими организациями.
Уэс МакКинни — пожизненный доброжелательный диктатор (БДФЛ).
Команда разработчиков¶
Список членов основной группы и более подробную информацию можно найти на странице для людей в репозитории корпоративного управления.
Институциональные партнеры¶
Информацию о текущих институциональных партнерах можно найти на странице веб-сайта pandas.
.
Участие в pandas — pandas 0.25.0.dev0 + 752.g49f33f0d документация
Все материалы, отчеты об ошибках, исправления ошибок, улучшения документации,
улучшения и идеи приветствуются.
Если вы новичок в pandas или разработке с открытым исходным кодом, мы рекомендуем
через вкладку GitHub «Проблемы»
найти интересующие вас вопросы. В разделе «Документы» указан ряд проблем.
и хороший первый выпуск
с чего бы вы могли начать. Найдя интересную проблему, вы можете
вернитесь сюда, чтобы настроить среду разработки.
Не стесняйтесь задавать вопросы в списке рассылки или в Gitter.
Отчеты об ошибках
— важная часть повышения стабильности pandas . Наличие полного отчета об ошибке
позволит другим воспроизвести ошибку и даст представление о том, как ее исправить. Видеть
эта статья stackoverflow и
этот блог
за советами по написанию хорошего отчета об ошибке.
Попытка кода, вызывающего ошибку, на ветке master часто бывает стоящим упражнением
чтобы подтвердить, что ошибка все еще существует.Также стоит поискать существующие отчеты об ошибках и запросы на вытягивание.
чтобы узнать, сообщалось ли о проблеме и / или исправлена ли она.
Сообщения об ошибках должны:
Включите короткий автономный фрагмент кода Python, воспроизводящий проблему.
Вы можете красиво отформатировать код, используя GitHub Flavored Markdown:`` питон >>> из панд импортировать DataFrame >>> df = DataFrame (...) ... `` ''
Включите полную строку версии pandas и ее зависимостей.Вы можете использовать встроенную функцию:
>>> импортировать панд как pd >>> pd.show_versions ()
Объясните, почему текущее поведение является неправильным / нежелательным и чего вы ожидаете вместо этого.
Затем проблема будет показана сообществу pandas и будет открыта для комментариев / идей от других.
Теперь, когда у вас есть проблема, которую вы хотите исправить, усовершенствовать или улучшить документацию,
вам нужно научиться работать с GitHub и базой кода pandas .
Контроль версий, Git и GitHub
Для нового пользователя работа с Git — один из самых сложных аспектов участия в pandas .
Это может очень быстро стать ошеломляющим, но соблюдение приведенных ниже рекомендаций поможет сохранить процесс.
простой и в основном беспроблемный. Как всегда, если у вас возникнут трудности, пожалуйста
смело просите о помощи.
Код размещен на GitHub. Чтобы
внести свой вклад, вам нужно будет зарегистрировать бесплатную учетную запись GitHub.Мы используем Git для
контроль версий, позволяющий многим людям работать вместе над проектом.
Отличные ресурсы для изучения Git:
Вилка
Для работы с кодом вам понадобится собственный форк. Перейти к проекту pandas
страницу и нажмите кнопку Fork
. Вы будете
хотите клонировать вилку на свою машину:
git clone https://github.com/your-user-name/pandas.git pandas-ваше имя cd pandas-ваше имя git remote добавить вверх по течению https://github.com/pandas-dev/pandas.мерзавец
Это создает каталог pandas-yourname и подключает ваш репозиторий к
апстрим (основной проект) репозиторий pandas .
.
| Развернуть DataFrame из широкого формата в длинный, при необходимости оставив идентификаторы установленными. |
| Вернуть измененный DataFrame, организованный по заданным значениям индекса / столбца. |
| Создайте сводную таблицу в виде электронной таблицы как DataFrame. |
| Вычислите простую перекрестную таблицу двух (или более) факторов. |
| Значения бункера в дискретные интервалы. |
| Функция дискретизации на основе квантилей. |
| Объединить объекты DataFrame или именованные серии с помощью соединения в стиле базы данных. |
| Выполнить объединение с дополнительным заполнением / интерполяцией. |
| Выполнить слияние asof. |
| Объедините объекты pandas по определенной оси с дополнительной логикой установки по другим осям. |
| Преобразование категориальной переменной в фиктивные / индикаторные переменные. |
| Кодировать объект как перечислимый тип или категориальную переменную. |
| Уникальный на основе хеш-таблицы. |
| Широкая панель для длинного формата. |
.