REF: Шпаргалка по Pandas
Создание таблиц
В pandas есть два основных объекта: DataFrame (таблица) и Series (последовательность значений, например, один столбец DataFrame). У таблицы есть ключи (индексы) строчек - index и колонок - columns. По умолчанию index целочисленный. Индексы строк не обязаны быть уникальными. При их совпадении, обращение к индексу df.loc[row] вернёт таблицу с несколькими строками.
Принципиальная разница с массивами numpy в том, что колонки pandas DataFrame могут быть различного типа (в том числе экземплярами класса). Таблицу можно транспонировать df.T и в этом смысле особой разницы между строками и колонками нет. Но если колонки различного типа транспонирование превратит все типы в object.
import pandas as pd df = pd.DataFrame() # создаём пустую таблицу print(df.index, df.columns) # Index( [] ) Index([]) df = pd.DataFrame([1,3,2], # одна колонка из 3-х чисел с именем A columns=['A']) # индексы строчек по умолчанию [0,1,2] print(df.index) # RangeIndex(start=0,stop=3,step=1) print(df.columns) # Index(['A']) df = pd.DataFrame() # тоже самое df['A'] = [1,2,3] # создаём колонку в пустой таблице df = pd.DataFrame([[1,2], # таблица из 2D массива [3,4]]) # первая строка [1,2], вторая [3,4]
Таблицу можно задать при помощи словаря (его ключи - имена колонок). Если у ключа скалярное значение - оно будет продублировано. При задании ключей строчек (index) можно смешивать str, int, Timestamp и т.п.:
cat = pd.Categorical(["a","b","a"])} # категориальные (не строки!) df = pd.DataFrame( {"Num": 1, # | Num Name Age Cat "Name": ["Jon", "Mia", "Sam"], # -------------------- "Age" : [43, np.nan, 56], # m | 1 Jon 43 a "Cat": cat, # f | 1 Mia NaN b index=['m', 'f', 0]) # 0 | 1 Sam 56 aСписок словарей - это список строк. Каждый новый ключ порождает колонку:
# | b c a pd.DataFrame([{ 'b': 1, 'c': 2}, # ---------------- {'a': 3, 'b': 4, 'c': 5}]) # 0 | 1 2 NaN # 1 | 3 4 5
df1 = df.copy() # deepcopy таблицы
Работа с файлами
При загрузке из csv-файла можно (index_col) задать некоторую колонку (по имени или по номеру) как индексную (имена строчек):df = pd.read_csv("name.csv", index_col="id")Чтение больших файлов по кускам:
chunksize = 10 ** 6 with pd.read_csv(filename, chunksize=chunksize) as reader: for chunk in reader: process(chunk)Сохранение в csv-файл:
df.to_csv("submission.csv", index=False) # без сохранения индекса df.to_csv('probs.zip', index=False, # запаковать в zip-файл compression=dict(method='zip', archive_name='probs.csv') )Если index=False не указывать, в csv добавиться первая безымянная колонка с индексами.
Сводная информация
print('shape:', df.shape) # вывод формы (rows, cols) display(df) # распечатать (если не последняя команда) df.head(7) # 7 первых строк (5 по умолчанию) df.tail(2) # 2 последние строки (5 по умолчанию) df.sample(2) # 2 случайные строки df.describe() # статистики по всем колонкам df.col.describe() # статистики колонке c именем 'col' df.describe(include=[bool])) # только логические колонки (bool,float,object,category) df.col.mean() # среднее значение по колонке 'col' df.col.unique() # список уникальных значений df.col.value_counts() # таблица имя значения - число раз в 'col' df.info() # список колонок с типами и непустых данных df.duplicated().sum() # число дубликатов строк df.isna().sum() # вывод числа пропусков в каждой колонкеФормат для вывода describe и т.п.:
pd.set_option('display.float_format', lambda x: '%.2f' % x)
Доступ к элементам
Метод at даёт доступ к элементам таблицы, а метод loc - к срезам по индексам (строк и колонок). Тоже самое для iat, iloc, но индексация при помощи номеров строк и колонок (целые числа, срезы подобно numpy). Для loc срезы включают границы, а для iloc срезы понимаются в обычном для Python смысле (верхняя граница не включена).
Свойство at[rowID, colID] быстрее, чем loc[rowID, colID] для одного элемена:df.at[0,'a'] = 5Свойство loc позволяет извлечь одну строку, список строк или их срез, а также срез таблицы (index, columns). При этом в срезе последний индекс включается (!):
df.loc[ 0 ] # Series (0-я строка) df.loc[ 0 ] = 5 # присвоить всем элементам строки 0 значение 5 df.loc[ [1,3] ] # DataFrame с 2-я строками df.loc[ :2] # DataFrame c 3-я строками df.loc[0, 'a'] # аналогично at, но медленнее df.loc[1:3, 'a'] # результат - Series (3 элемента) df.a.loc[1:3] # тоже самое (можно и со строковыми индексами) df.loc[1:3, ['a','b']] # DataFrame (срез 3 строки, 2 колонки) df.loc[[1,3], ['a','b']] # DataFrame (вырез 2 строки, 2 колонки)
Получение колонок: df['col_name'] - это даёт Series. Тот-же эффект: df.col_name (если колонка уже существует). Можно список имён колонок: df[['a','c']] - тогда это DataFrame, а не Series.
df['a'] = 5 # изменить все значения в колонке 5 df.a = 5 # тоже самое df['a'] = [1,2,3] # элементы колонки (должна совпадать с другими колонками)
Логическая индексация
df.loc[ df.a > 1 ] # DataFrame в которой значения в кллонке a > 1 df.loc[ (df.a > 1) & (df.a < 5) ] df.loc[df.c.isin(['aa','bb'])] # присутствуют значения в колонке 'c' df.loc[df.isin(['aa','bb'])] # присутствуют значения в любой колонке df.loc[lambda df: df.a == 8] # отобрать строки, удовлетворящие функции df[df > 0] # все элементы большие нуля (остальные NaN) df[df > 0] = -df
Изменение таблиц
Сортировка:
df = df.sort_values(by="A") # сортировки по колонкам df = df.sort_values(by=["A", "B"]) df = df.sort_index(axis=1, ascending=False) # сортировка колонок df = df.sort_index(axis=0) # сортировка индексов строк (один типа!)
Переименование колонок и индексов строк:
df.rename(columns = {'points': 'score'}) df.rename(index = {0: 'firstEntry', 1: 'secondEntry'})
Добавление строк и колонок
Добавляем строку при помощи loc или конкатенацией concat таблиц:df = pd.DataFrame({'a':[1,2], 'b':[-1,-2]}) # | a b # ---------- df.loc[len(df.index)] = [3, -3] # 0 | 1 -1 # 1 | 2 -2 da = pd.DataFrame([{'a':4, 'b':'-4'}]) # 2 | 3 -3 df = pd.concat([df, da]) # 3 | 4 -4 df = df.reset_index() # сделать снова индексы строк уникальными (!)Добавление колонки со значением длины строки в колонке "name"
df["len"] = df["name"].map(lambda x: len(x))
Неопределённые ячейки
Некоторые ячейки могут быть неопределёнными (None или np.nan), что при выводе отражается как NaN. Метод isna возвращает DataFrame с логическими значениями (его антипод notna):df = pd.DataFrame({'a':[1,2], 'b':[3,None]}) # | a b # --------------- # 0 | False False df.isna() # 1 | False True
Можно создавать таблицу с неопределёнными ячейками (для порождения больших таблиц эффективнее создать сначала неопределённую, и затем её заполнять, чем добавлять строки). Значениями в ячейках могут быть объекты (списки и т.п.).
df = pd.DataFrame(index=range(2), # | a columns=['a']) # -------- df.at[0,'a'] = [1,2] # 0 | [1,2] df # 1 | NaNНо после сохранения в файл они будут json-строками и после чтения файла их надо будет снова парсить.
df.dropna(how="any") # выкинуть все строки с пропусками df.fillna(value=5) # заполнить одним значением
Вычисления в таблицах
df['c']= df.a + df.b # создаём новую колонку или меняем существующую df.a = df.a.map(lambda x: x - 50) # меняем колонку при помощи функции
count | количество не-NA объектов |
---|---|
sum | cумма |
mean | cреднее значение |
mad | cреднее абсолютное отклонение |
median | медиана |
min | минимум |
max | максимум |
mode | мода |
abs | абсолютное значение |
prod | произведение |
std | стандартное отклонение |
var | несмещенная дисперсия |
sem | стандартная ошибка среднего |
skew | скошенность (момент 3-го порядка) |
kurt | эксцесс (момент 4-го порядка) |
quantile | квантиль (%) |
cumsum | кумулятивная сумма |
cumprod | кумулятивное произведение |
cummax | кумулятивный максимум |
cummin | кумулятивный минимум |
Агрегированная информация
Вместо стандартной describe, можно самостоятельно формировать итоговые таблицы по колонкам (текстовые имена для функций панды df.sum и т.п.), иначе просто имя функции:def fun(x): return sum(x)**0.5 fun.__name__ = 'sqrt' # можно для вывода любое имя df.agg(['sum', 'mean', 'std', max, strange])
Группы
df = pd.DataFrame({ 'A': ['a','b','a','b','b'], 'B': ['0','1','0','1','0'], 'X': [ 1, 2, 3, 4, 5 ], 'Y': [ 0, 1, 1, 0, 0 ] } )Сгруппируем одинаковые значения в колонке A и пробежимся по всем группам (каждая группа - это DataFrame):
groups = df.groupby("A") # val=a # | A B X Y for val_A, df_A in groups: # -------------- print("val=",val_A) # 0 | a 0 1 0 display(df_A) # 2 | a 0 3 1 #...Получим сумму по всем числовым колонкам в каждой группе (индекс получил имя index.name='A'):
# | X Y df.groupby("A").sum() # A ------- # a | 4 1 # b | 11 1 df.groupby("A").X.sum() # получить Series сумм в группах для колонки X
Сначала сгруппировать по A, затем по B:
# | X Y df.groupby(["A","B"]).sum() # A B |----- # a 0 | 4 1 # b 0 | 5 0 # 1 6 | 1Теперь индекс - это не одно число а пара чисел:
MultiIndex([('a', '0'), ('b', '0'), ('b', '1')], names=['A', 'B'])
Агрегация групп
df = pd.DataFrame({'s': [0,0,0,1,1,2], 'i': [1,2,1,1,3,2], 't': [0,0,1,0,1,0]})Создание таблицы в которой колонка sess уникальна, а все aid собраны в множество (второй пример - с переименованием колонки "set" → "s1" "min" → "mi"):
df.groupby('s').i.agg([set, min]) # s set min # 0 {1,2} 1 df.groupby('s').agg(s1=('i',set), mi=('i',min)) # 1 {1,3} 1 # 2 {2} 2После группировки индекс таблицы преобретает имя index.name == s.
gr0 = df[df.t==0].groupby('s').agg(s1=('i',set)) # s s1 s2 gr1 = df[df.t==1].groupby('s').agg(s2=('i',set)) # 0 {1,2} {1} # 1 {1} {3} pd.concat([gr0, gr1], axis=1) # 2 {2} NaN
Использование справочников
Связывание таблиц подобно SQL (df_ref - таблица справочник даёт свои значения в таблицу df по ключу i):df_ref = pd.DataFrame({'i':[0, 1, 3, 4], # | i v s 'v':[10,20,30,40], # ---------------- 's':['a','b','c','d']}) # 0 | 0 10 a # 1 | 0 10 a df = pd.DataFrame({"i":[0,0,1,3,2] }) # 2 | 1 20 b # 3 | 3 30 c df.merge(df_ref, on='i', how="left") # 4 | 2 NaN NaNТо-же если имена колонок для коннекта различные
df.merge(df_ref, left_on='i1', right_on='i', how="left")
Работа со строковыми данными
Через атрибут str получаем доступ ко всем методам типа str применительно к Series:s.str.lower() s.str.lower().str.strip()
Работа с временем
Объекты времени и периода времениts = pd.Timestamp('2022-11-13 00:00:00', tz=None) ts.to_pydatetime() # datetime.datetime(2022, 11, 13, 0, 0) rng = pd.date_range('2022-11-13', periods=3, freq='D') rng.to_pydatetime() # list 3-х datetimeЕсли дата-время в csv сохранена Unix timestamp (seconds passed since 1970-1-1) то их можно конвертировать:
df = pd.DataFrame({"date": [1659304800, # 2022-07-31 22:00:00 1659304904]}) # 2022-07-31 22:01:44 df.date = pd.to_datetime(df.date, unit='s') # D,s,ms,us,ns
t1 = pd.to_datetime(df.ts.min(), unit='s') t2 = pd.to_datetime(df.ts.max(), unit='s') print(t1, t2, pd.Timedelta(t2 - t1) )
df.time = pd.to_datetime(df.date, unit='s') # D,s,ms,us,ns df['hour'] = df.time.dt.hour # номер часа df['weekday'] = df.time.dt.weekday # номер дня недели