Pandas — широко распространённая Python-библиотека для работы со структурированными данными. По её использованию уже составлено большое количество уроков, однако, я хотел бы рассказать о нескольких небольших хитростях, которые могут оказаться полезными.
read_csv
Эта команда вам хорошо знакома. Если данные, которые вы пытаетесь считать, слишком большие, то попробуйте добавить этот аргумент: nrows = 5, чтобы считать только часть таблицы, прежде чем загрузить её полностью. Это поможет избежать ошибок при неправильном выборе разделителя (данные не всегда разделяются при помощи запятых).
(В Linux можно использовать команду head, чтобы просмотреть, например, первые 5 строчек любого текстового файла: head -c 5 data.txt).
Также, можно извлечь список всех колонок при помощи команды df.columns.tolist()
. Если добавить аргумент usecols = [‘c1’, ‘c2’, …], то вы получите список только тех колонок, которые вы выберете сами. Чтобы команда выполнилась быстрее и если вы знаете тип данных каждой колонки, можно добавить в качестве аргумента dtype = {‘c1’: str, ‘c2’: int, …}. Одно из преимуществ этого аргумента — если у вас есть колонка, в которой чередуются строки и числа, можно определить тип всех данных как строковый, чтобы в дальнейшем не возникало ошибок.
select_dtypes
Эта команда позволит сохранить время при проведении предварительной обработки данных в Python. После считывания таблицы для каждой колонки будет определён тип данных: bool, int64, float64, object, category, timedelta64, datetime64. Их распределение можно узнать, используя df.dtypes.value_counts()
. Затем, если вам нужно выбрать только численную часть данных, используйте df.select_dtypes(include=[‘float64’, ‘int64’])
.
copy
Это очень важная команда, в особенности, если вы не знали о ней раньше. Допустим, что вы запустили вот этот кусок кода:
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, а делает df2 указателем на ту же ячейку памяти, что и df1. То есть, любое изменение df2 отразится и на df1. Чтобы создать копию датафрейма, используйте эту команду:
df2 = df1.copy()
или эту:
from copy import deepcopy
df2 = deepcopy(df1)
map
Это полезная команда для преобразования данных. Для начала нужно объявить словарь, в котором ключами являются старые значения в колонках, а значениями — то, на что планируется их заменить.
level_map = {1: ‘high’, 2: ‘medium’, 3: ‘low’}
df[‘c_level’] = df[‘c’].map(level_map)
Пример: замена True и False на 1 и 0, обозначение уровней, определённая пользователем лексическая кодировка.
apply: использовать или нет?
Если мы хотим создать новую колонку на основе уже существующих, функция 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()
В коде выше мы объявляем функцию с двумя входными значениями и применяем её для колонок 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, если задачу можно выполнить при помощи других существующих встроенных функций (они работают быстрее). Например, если нужно округлить значения колонки c1, используйте round(df[‘c’], 0)
вместо apply.
value_counts
Эта команда проверяет распределение значений. Чтобы узнать, допустим, список всех значений колонки “С” и их частоту, можно использовать команду df[‘c’].value_counts()
.
У этой команды есть несколько полезных аргументов:
- normalize = True: если нужно получить частоту появления значения, а не количество
- dropna = False: при подсчёте будут учитываться и пропущенные значения
- sort = False: выводит статистику, отсортированную по количеству появления значения
Также, используйте df[‘c].value_counts().reset_index()
, если вы хотите преобразовать таблицу со статистикой в датафрейм для выполнения каких-либо операций над ним в будущем.
Количество пропущенных значений
При построении модели, скорее всего, вы хотели бы просто пропустить строки с большим количеством пропущенных значений. Для подсчёта количества пропущенных значений можно использовать .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 … (например, SELECT * FROM … WHERE ID in (‘A001’, ‘C022’, …) выберет часть датафрейма, в котором ID равен одному из указанных значений. В pandas то же самое можно сделать вот так:
df_filter = df[‘ID’].isin([‘A001’,‘C022’,...])
df[df_filter]
Группировка по процентному соотношению
У вас есть колонка с численными значениями и вы хотели бы разделить эти значения по группам: скажем, 1 группа — наибольшие 5%, 5%–20% во второй группе, 20%-50% — 3 группа, остальное — 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])
# or <= cut_points[i]
Он хорош тем, что он довольно быстрый, так как не требует функции apply.
to_csv
Ещё одна команда, с которой вы хорошо знакомы, но, вероятно, не знакомы с двумя хитростями.
- Команда
print(df[:5].to_csv())
выводит первые 5 строк файла точно в таком же формате, в котором они будут записаны в файл. - Наверняка, вы сталкивались со случаем, когда колонка содержала и пропущенные значения, и целые числа. Тогда типом данных этой колонки будет float, а не int. При экспорте таблицы можно добавить
float_format = ‘%.0f’
для того, чтобы округлить все дробные числа до целых. Это пригодится, если вы хотите установить во всех колонках только целые значения.
Перевод статьи Shiu-Tang Li: 10 Python Pandas tricks that make your work more efficient