πΌ 25 Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠ΅ΠΉ Pandas, ΠΎ ΠΊΠΎΡΠΎΡΡΡ Π²Ρ Π½Π΅ Π·Π½Π°Π»ΠΈ (ΠΈΠ½ΡΠ° 80%)
Разработчик ПО (системы PDM/PLM) с 1993 года, компания "ИНТЕРМЕХ" (www.intermech.ru). В 2020-м успешно закончил курсы "Основы Data Science" (минская IT Academy) Референт-переводчик технической литературы с английского языка. Новые и малоизвестные возможности Pandas, о которых полезно знать каждому специалисту по Data Science. Сколько раз вы говорили: «было бы здорово, если бы я мог это сделать в Pandas»? Возможно, вы это и можете! Pandas настолько огромна и глубока, что она позволяет выполнять практически любые операции с таблицами, которые вы можете себе представить. Однако подобная глубина имеет свою цену. Многие элегантные возможности, решающие редко возникающие задачи и уникальные сценарии, теряются в глубинах документации, затертые более часто используемыми функциями. Эта статья попытается заново открыть для вас многие возможности Pandas и показать, что она может намного больше, чем вам казалось. Автор Erik Mclean. Источник Pexels ExcelWriter – это обобщенный класс для создания файлов Excel (с листами!) и записи в них DataFrame. Допустим, у нас есть два набора данных: У класса есть дополнительные атрибуты для определения используемого формата DateTime: хотите ли вы создать новый файл Excel или изменить существующий, что делать, если лист существует и т. п. Детали см. в документации. Автор Leah Kelley. Источник Pexels pipe – одна из лучших функций для проведения очистки данных в Pandas кратким и четким образом. Она позволяет объединять несколько пользовательских функций в одну операцию. Для примера допустим, что у вас есть функции удаления дубликатов (remove_duplicates), удаления выбросов (remove_outliers) и кодирования категориальных признаков (encode_categoricals), каждая со своими аргументами. Вот как вы можете применить все эти функции одной операцией: Мне нравится, что эта функция похожа на конвейеры Sklearn. Вы можете сделать с ее помощью намного больше – читайте документацию или эту полезную статью. Больше полезной информации вы найдете на нашем телеграм-канале «Библиотека data scientist’а» Интересно, перейти к каналу Эта функция – альтернатива LabelEncoder из Sklearn в Pandas: В отличие от LabelEncoder‘а, factorize возвращает кортеж из двух значений: закодированный столбец и список уникальных категорий. Автор Joshua Sukoff. Источник Unsplash explode – это функция с интересным именем (взрыв). Давайте сначала посмотрим пример, а потом объясним: В столбце dirty есть две строки, в которых хранятся не единственные значения, а списки. Такие данные часто встречаются в опросах, поскольку некоторые вопросы допускают несколько ответов. explode распространяет данные из ячейки с массивоподобным значением на несколько строк. Установите ignore_index в True, чтобы сохранить числовой индекс по порядку. Автор cottonbro. Источник Pexels Еще одна функция с забавным именем – это squeeze (сжатие), используемая в очень редких, но надоедливых случаях. Один из таких случаев – когда запрос подмножества из DataFrame возвращает единственное значение. Рассмотрим следующий пример: Хотя результат – всего одна ячейка, он возвращается как DataFrame. Это неудобно, поскольку вам придется еще раз использовать .loc и передать как имя столбца, так и индекс, чтобы получить цену. Но если вы знаете про squeeze, нам не придется этого делать. Эта функция позволяет убрать оси из DataFrame или Series, содержащих единственную ячейку. Например: Теперь мы получили скалярное значение. Можно также указать, какую ось нужно убрать: Обратите внимание, что squeeze работает только с DataFrame и Series, имеющими единственное значение. Автор Justin Dream. Источник Pexels Это довольно изящная функция для бинарного индексирования числовых признаков по принадлежности диапазону: У всех DataFrame есть простой атрибут T, что значит «транспонирование». Возможно, вы не будете часто его использовать, но я считаю его довольно полезным при выводе DataFrame’ов после метода describe(): Набор данных о недвижимости Бостона содержит 30 числовых столбцов. Если просто вызвать describe, результирующий DataFrame будет сжат по горизонтали, и сравнить статистику будет трудно. Транспонирование поменяет оси местами, и по горизонтали будут уже статистические параметры. А вы знали, что Pandas позволяет вам задавать стили DataFrame’ов? У них есть атрибут style, открывающий дверь к настройкам и стилям, ограниченным только вашими знаниями HTML и CSS. Я не буду рассказывать обо всем, что вы можете сделать с помощью style, покажу лишь свои любимые функции: Мы выделили ячейки, содержащие максимальные значения каждого столбца. Еще один полезный стилизатор – background_gradient, присваивающий ячейкам градиентный фоновый цвет на основе их значений: Это особенно полезно, если вы вызываете describe для таблицы со множеством столбцов и хотите сравнить суммарную статистику. Подробности см. в документации по стилизатору. Как и в Matplotlib, в Pandas есть глобальные настройки, которые можно изменять для настройки поведения по умолчанию: Эти настройки разделены на 5 модулей. Давайте посмотрим, какие настройки есть в модуле display: В группе display много настроек, но я в основном использую max_columns и precision: Больше подробностей об этих замечательных настройках можно найти в документации. Все мы знаем, что Pandas имеет раздражающую привычку присваивать столбцам тип данных object. Вместо того чтобы назначать типы данных вручную, вы можете использовать метод convert_dtypes, который пытается подобрать наиболее подходящий тип данных: К сожалению, метод не может разбирать даты из-за нюансов различных форматов их представления. Функция, которую я использую постоянно – это select_dtypes. Ее функционал очевиден из ее названия: выбор по типам данных. У нее есть параметры include и exclude, позволяющие выбрать столбцы, включая или исключая определенные типы данных. Например, можно выбрать только числовые столбцы, указав np.number: или, наоборот, исключить их: Автор Pixabay. Источник Pexels. Функция mask позволяет быстро заменять значения в тех ячейках, для которых выполняется определенное условие. Предположим, что у нас есть данные опроса людей от 50 до 60: Мы будем считать возраст, выходящий за границы от 50 до 60 лет, ошибками ввода (таких ошибок две – 49 и 66 лет), которые заменим на NaN: Таким образом, mask заменяет значения ячеек, не удовлетворяющие cond, значениями other. Хотя функции min и max широко известны, они имеют еще одно применение, полезное для особых случаев. Давайте рассмотрим следующий набор данных: Этот DataFrame содержит воображаемую точность моделей, полученных в результате четырех видов градиентного бустинга на пяти различных наборах данных. Мы хотим найти лучшую точность на каждом наборе. Вот как элегантно это можно сделать с помощью max: Если вы хотите найти максимум или минимум по каждому столбцу, достаточно изменить 1 на 0. Иногда вам недостаточно получить максимальное или минимальное значение столбца. Вы хотите получить N максимальных (или минимальных) значений. Для этого пригодятся функции nlargest и nsmallest. Давайте выведем 5 самых дорогих и самых дешевых бриллиантов: Когда вы вызываете max или min для столбца, pandas возвращает максимальное или минимальное значение. Однако иногда вам нужна позиция этого значения, а не оно само. Для этого используйте функции idxmax и idxmin: Можно также задать axis=’columns’, при этом функции будут возвращать номер индекса нужного столбца. Для нахождения процента пропущенных значений чаще всего используется комбинация isnull и sum и деление на длину массива. Но вы можете сделать то же самое с помощью value_counts, если задать соответствующие аргументы: В этом столбце 47% пустых значений (NaN). Автор Ann H. Источник Pexels Обнаружение и удаление выбросов часто используется в анализе данных. Функция clip позволяет очень легко найти выбросы, выходящие за пределы диапазона и заменить их предельными значениями. Давайте вернемся к примеру с возрастами людей от 50 до 60: На этот раз мы заменим значения, выходящие за диапазон от 50 до 60, крайними значениями диапазона. Быстро и эффективно! Эти функции могут пригодиться при работе с данными, разделенными по времени с высокой степенью детализации. Давайте создадим таблицу, содержащую 100 часовых интервалов: Функция at_time позволяет выбирать значения по заданным дате или времени. Давайте выделим все строки, соответствующие 15.00: Здорово, правда? А теперь давайте используем between_time, чтобы выделить строки в заданном интервале времени: Заметьте, что обе функции требуют DateTimeIndex, и они работают только с временем. Если вы хотите выделить строки в определенном интервале DateTime, используйте between. bdate_range – это функция для быстрого создания индексов TimeSeries с частотой в один бизнес-день. Частота в бизнес-день часто встречается в финансовом мире. Значит, эта функция может оказаться полезной для переиндексирования существующих временных интервалов функцией reindex. Один из важнейших компонентов анализа временных последовательностей – это изучение автокорреляции переменной. Автокорреляция – это старый добрый коэффициент корреляции, но взятый по сравнению с лагом той же последовательности. Точнее, автокорреляция при lag=k вычисляется следующим образом: 1. Последовательность сдвигается k периодов времени: 2. Рассчитывается корреляция между исходным значением и каждым лагом. Вместо того чтобы делать все это вручную, вы можете использовать функцию Pandas autocorr: О важности автокорреляции для анализа временных рядов можно прочитать в этой статье. Pandas предлагает метод для простой проверки, содержит ли Series какие-либо пустые значения – атрибут hasnans: Согласно его документации, атрибут существенно ускоряет производительность. Заметьте, что он работает только для Series. Эти два метода доступа к значениям – гораздо более быстрые альтернативы loc и iloc, но с существенным недостатком: они позволяют получить или изменить только одно значение: Эту функцию стоит использовать каждый раз, когда вы хотите выделить индексы, образующие отсортированный массив: Общеизвестно, что Pandas позволяет использовать встроенные функции Python на датах и строках с помощью методов доступа вроде dt или str. Кроме того, в Pandas есть специальный тип данных category для категориальных переменных, как показано ниже: Если столбец имеет тип category, над ним можно выполнять несколько специальных функций с помощью метода доступа cat. Например, давайте рассмотрим различные виды огранки бриллиантов: Есть и такие функции, как remove_categories, rename_categories и т. д.: Полный список функций, доступных через метод доступа cat, можно посмотреть здесь. Эта функция работает только для объектов GroupBy. После группировки она возвращает n-ю строку каждой группы: Несмотря на то, что новые библиотеки вроде Dask и datatable потихоньку вытесняют Pandas благодаря их сверкающим новым возможностям для обработки огромных массивов данных, Pandas по-прежнему остается самым популярным средством манипуляции данными в экосистеме Data Science на Python. Эта библиотека – образец для подражания, который другие пытаются имитировать и улучшить, поскольку она прекрасно интегрируется в современный стек SciPy. Спасибо за внимание! *** Ilya Ginsburg
1. ExcelWriter
# Загрузим два набора данных diamonds = sns.load_dataset("diamonds") tips = sns.load_dataset("tips") # Пишем оба в один и тот же файл Excel with pd.ExcelWriter("data/data.xlsx") as writer: diamonds.to_excel(writer, sheet_name="diamonds") tips.to_excel(writer, sheet_name="tips")
2. pipe
df_preped = (diamonds.pipe(drop_duplicates). pipe(remove_outliers, ['price', 'carat', 'depth']). pipe(encode_categoricals, ['cut', 'color', 'clarity']) )
3. factorize
# Обратите внимание на [0] в конце diamonds["cut_enc"] = pd.factorize(diamonds["cut"])[0] >>> diamonds["cut_enc"].sample(5) 52103 2 39813 0 31843 0 10675 0 6634 0 Name: cut_enc, dtype: int64
codes, unique = pd.factorize(diamonds["cut"], sort=True) >>> codes[:10] array([0, 1, 3, 1, 3, 2, 2, 2, 4, 2], dtype=int64) >>> unique ['Ideal', 'Premium', 'Very Good', 'Good', 'Fair']
4. explode – Бабах!
data = pd.Series([1, 6, 7, [46, 56, 49], 45, [15, 10, 12]]).to_frame("dirty") >>> data
>>> data.explode("dirty", ignore_index=True)
5. squeeze
subset = diamonds.loc[diamonds.index < 1, ["price"]] >>> subset
>>> subset.squeeze() 326
>>> subset.squeeze("columns") # или "rows" 0 326 Name: price, dtype: int64
6. between
# Выбрать бриллианты с ценой от 3500 до 3700 долларов diamonds[diamonds["price"] .between(3500, 3700, inclusive="neither")].sample(5)
7. T
>>> boston.describe().T.head(10)
8. Стилизатор Pandas
>>> diabetes.describe().T.drop("count", axis=1) .style.highlight_max(color="darkred")
diabetes.describe().T.drop("count", axis=1).style.background_gradient( subset=["mean", "50%"], cmap="Reds" )
9. Опции Pandas
>>> dir(pd.options) ['compute', 'display', 'io', 'mode', 'plotting']
>>> dir(pd.options.display) ['chop_threshold', 'max_columns', 'max_colwidth', 'max_info_columns', 'max_info_rows', 'max_rows', ... 'precision', 'show_dimensions', 'unicode', 'width']
# Убрать лимит на изображаемое количество столбцов pd.options.display.max_columns = None # Показывать только 5 цйфр после запятой pd.options.display.precision = 5 # избавиться от научной нотации
10. convert_dtypes
sample = pd.read_csv( "data/station_day.csv", usecols=["StationId", "CO", "O3", "AQI_Bucket"], ) >>> sample.dtypes StationId object CO float64 O3 float64 AQI_Bucket object dtype: object >>> sample.convert_dtypes().dtypes StationId string CO float64 O3 float64 AQI_Bucket string dtype: object
11. select_dtypes
# Выбрать только числовые столбцы diamonds.select_dtypes(include=np.number).head()
# Исключить числовые столбцы diamonds.select_dtypes(exclude=np.number).head()
12. mask
# Create sample data ages = pd.Series([55, 52, 50, 66, 57, 59, 49, 60]).to_frame("ages") ages
ages.mask(cond=~ages["ages"].between(50, 60), other=np.nan)
13. min и max по строкам и столбцам
index = ["Diamonds", "Titanic", "Iris", "Heart Disease", "Loan Default"] libraries = ["XGBoost", "CatBoost", "LightGBM", "Sklearn GB"] df = pd.DataFrame( {lib: np.random.uniform(90, 100, 5) for lib in libraries}, index=index ) >>> df
>>> df.max(axis=1) Diamonds 99.52684 Titanic 99.63650 Iris 99.10989 Heart Disease 99.31627 Loan Default 97.96728 dtype: float64
14. nlargest и nsmallest
diamonds.nlargest(5, "price")
diamonds.nsmallest(5, "price")
15. idxmax и idxmin
>>> diamonds.price.idxmax() 27749 >>> diamonds.carat.idxmin() 14
16. value_counts с параметром dropna=False
ames_housing = pd.read_csv("data/train.csv") >>> ames_housing["FireplaceQu"].value_counts(dropna=False, normalize=True) NaN 0.47260 Gd 0.26027 TA 0.21438 Fa 0.02260 Ex 0.01644 Po 0.01370 Name: FireplaceQu, dtype: float64
17. clip
ages.clip(50, 60)
18. at_time и between_time
index = pd.date_range("2021-08-01", periods=100, freq="H") data = pd.DataFrame({"col": list(range(100))}, index=index) >>> data.head()
data.at_time("15:00")
from datetime import datetime >>> data.between_time("09:45", "12:00")
19. bdate_range
series = pd.bdate_range("2021-01-01", "2021-01-31") # A period of one month >>> len(series) 21
20. autocorr
time_series = tips[["tip"]] time_series["lag_1"] = time_series["tip"].shift(1) time_series["lag_2"] = time_series["tip"].shift(2) time_series["lag_3"] = time_series["tip"].shift(3) time_series["lag_4"] = time_series["tip"].shift(4) # time_series['lag_k'] = time_series['tip'].shift(k) >>> time_series.head()
# Автокорреляция tip при lag_8 >>> time_series["tip"].autocorr(lag=8) 0.07475238789967077
21. hasnans
series = pd.Series([2, 4, 6, "sadf", np.nan]) >>> series.hasnans True
22. at и iat
# [index, label] >>> diamonds.at[234, "cut"] 'Ideal' # [index, index] >>> diamonds.iat[1564, 4] 61.2 # Заменить 16541-ю строку столбца price >>> diamonds.at[16541, "price"] = 10000
23. argsort
tips.reset_index(inplace=True, drop=True) sort_idx = tips["total_bill"].argsort(kind="mergesort") # Теперь выведем значения `tips` в порядке сортировки по total_bill tips.iloc[sort_idx].head()
24. Метод доступа cat
>>> diamonds.dtypes carat float64 cut category color category clarity category depth float64 table float64 price int64 x float64 y float64 z float64 cut_enc int64 dtype: object
>>> diamonds["cut"].cat.categories ['Ideal', 'Premium', 'Very Good', 'Good', 'Fair']
diamonds["new_cuts"] = diamonds["cut"].cat.rename_categories(list("ABCDE")) >>> diamonds["new_cuts"].cat.categories Index(['A', 'B', 'C', 'D', 'E'], dtype='object')
25. GroupBy.nth
diamonds.groupby("cut").nth(5)
Заключение
Материалы по теме
- 0 views
- 0 Comment