Физтех.Статистика
Скачать ipynb
Python для анализа данных¶
Перед изучением убедитесь, что вы достаточно хорошо ознакомились с первой частью нашего материала про pandas.
Операции в pandas¶
import numpy as np
import pandas as pd
import scipy.stats as sps
1. Простые операции¶
Сгенерируем случайные числа и представим их в виде DataFrame
.
df = pd.DataFrame(sps.norm.rvs(size=(10, 4)),
columns=['A', 'B', 'C', 'D'])
df
Выведем описательные статистики по столбцам — количество значений, среднее, стандартное отклонение (корень из дисперсии), минимум, квантили, максимум.
df.describe()
Среднее по столбцам
df.mean()
Оценка матрицы корреляций значений в столбцах
df.corr()
Применение функции к данным. Для примера посчитаем разброс значений — разница максимума и минимума.
df.apply(lambda x: x.max() - x.min())
2. Объединение таблиц¶
2.1 Функция df.append
¶
Добавление строк в виде таблицы other
в таблицу df
. При наличии у новых строк колонок, которых нет в таблице, они добавляются в таблицу.
df.append(other, ignore_index=False, verify_integrity=False, sort=None)
df
— таблица;other
— добавляемые строки в виде таблицы;ignore_index
— сохранить индексы или определить и как $0, ..., n-1$;verify_integrity
— еслиTrue
, то создает исключение в случае повторения индексов;sort
— сортировать ли колонки, если они (или их порядок) различаются.
Создадим новую таблицу из первых четырех строк таблицы df
. В новую таблицу добавим колонку flag
, в которую запишем условие, что число в столбце D положительно. Затем добавим строки из новой таблицы к старой. Полученная таблица содержит пропуски, которые отмечены как NaN
.
other = df[:4].copy() # Полное копирование
other['flag'] = other['D'] > 0
other['D'] = other['D'] ** 2
df.append(other, ignore_index=True, sort=False)
2.2 Функция pd.concat
¶
Соединение таблиц вдоль выбранной оси
pd.concat(objs, axis=0, join='outer', ignore_index=False, copy=True, ...)
objs
— объединяемые таблицы;axis
: {0
или'index'
,1
или'columns'
} — ось индексов или ось колонок, иными словами соединение по вертикали или по горизонтали;join
: {'inner'
,'outer'
} — тип объединения — пересечение или объединение индексов/колонок;ignore_index
— сохранить индексы или определить и как $0, ..., n-1$;copy
— копировать данные или нет.
Простой пример соединения таблиц:
pd.concat([df[:5], df[5:]])
2.3 Функции pd.merge
и df.join
¶
Слияние таблиц по вертикали путем выполнения операций слияния баз данных в стиле SQL.
pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None, left_index=False, right_index=False, suffixes=('_x', '_y'), ...)
left
иright
— объединяемые таблицы.how
— тип объединения:left
— только по ключам из левой таблицы == SQL left outer join;right
— только по ключам из правой таблицы == SQL right outer join;outer
— по объединению ключей == SQL full outer join;inner
— по пересечению ключей == SQL inner join.
on
— имя (или имена) колонок, по которым будет производиться объединение (т.е. ключи). Если их несколько, то нужно передать список имен. Имена колонок в таблице должны совпадать.left_on
иright_on
— аналогичноon
для случая, когда в таблицах различаются имена колонок, соответствующие ключам.left_index
иright_index
— использовать ли индексы в качестве ключей.suffixes
— суффиксы, которые будут добавлены к тем колонкам, имена которых повторяются.
Пример. Опция how=left, left_on='A', right_on='B'
соответствует взятию всех строк из таблицы left
, а из таблицы right
берутся те строки, в которых значения в колонке A
таблицы left
совпадает со значением колонки B
таблицы right
. Если в одной из таблиц таких значений несколько, то строки другой таблицы дублируются. Если в таблице right
каких-то значений нет, то в результирующей таблице будут пропуски.
df.join(other, on=None, how='left', lsuffix='', rsuffix='', sort=False)
df
— основная таблица. В качестве ключей используется индекс.other
— другая таблица.on
— колонка(-и) вother
, соответствующая ключам, по ним происходит объедиенение. ЕслиNone
, то используется индекс.how
— тип объединения (см.pd.merge
).lsuffix
иrsuffix
— суффиксы, которые будут добавлены к тем колонкам, имена которых повторяются.
left = pd.DataFrame({'key': ['A', 'A'],
'lval': [1, 2]})
right = pd.DataFrame({'key': ['A', 'A'],
'rval': [4, 5]})
left
right
В результате объединения получаем 4 строки — для каждой строки из левой таблице есть две строки из правой таблицы с таким же ключом.
pd.merge(left, right, on='key')
Пример 2.¶
В таблицах ключи не повторяются
left = pd.DataFrame({'key': ['A', 'B'],
'lval': [1, 2]})
right = pd.DataFrame({'key': ['A', 'B'],
'rval': [4, 5]})
left
right
В результате объединения получаем 2 строки — для каждой строки из левой таблице есть только одна строка из правой таблицы с таким же ключом.
pd.merge(left, right, on='key')
Пример 3.¶
Посмотрим на различные типы объединения. Сооздадим и напечатаем две таблицы.
left = pd.DataFrame({'lkey': ['A', 'B', 'C', 'A'],
'value': range(4)})
right = pd.DataFrame({'rkey': ['A', 'B', 'D', 'B'],
'value': range(4, 8)})
left
right
Внешнее слияние — используются ключи из объединения списков ключей. Иначе говоря, используются ключи, которые есть хотя бы в одной из таблиц. Если в другой таблице таких ключей нет, то ставятся пропуски.
pd.merge(left, right,
left_on='lkey', right_on='rkey', how='outer')
Внутреннее слияние — используются ключи из пересечения списков ключей. Иначе говоря, используются ключи, которые присутствуют в обеих таблицах.
pd.merge(left, right,
left_on='lkey', right_on='rkey', how='inner')
Объединение по ключам левой таблицы. Не используются ключи, которые есть в правой таблицы, но которых нет в левой. Если в правой таблице каких-то ключей нет, то ставятся пропуски.
pd.merge(left, right,
left_on='lkey', right_on='rkey', how='left')
Объединение по ключам правой таблицы. Не используются ключи, которые есть в левой таблицы, но которых нет в правой. Если в левой таблице каких-то ключей нет, то ставятся пропуски.
pd.merge(left, right,
left_on='lkey', right_on='rkey', how='right')
Выполним внтуреннее объединение и установим ключ качестве индекса
pd.merge(left, right,
left_on='lkey', right_on='rkey', how='inner') \
.set_index('lkey')[['value_x', 'value_y']]
Ту же операцию можно выполнить с помощью join
left.set_index('lkey') \
.join(right.set_index('rkey'), rsuffix='_r', how='inner')
3. Группировка¶
Часто на практике необходимо вычислять среднее по каким-либо категориям или группам в данных. Группа может определяться, например, столбцом в таблице, у которого не так много значений. Мы хотели бы для каждого такого значения посчитать среднее значение другой колонки данных в этой группе.
Этапы группировки данных:
- разбиение данных на группы по некоторым критериям;
- применение функции отдельно к каждой группе;
- комбинирование результата в структуру данных.
Группировка выполняется функцией
df.groupby(by=None, axis=0, level=None, sort=True, ...)
df
— таблица, данные которой должны быть сгруппированы;by
— задает принцип группировки. Чаще всего это имя столбца, по которому нужно сгруппировать. Может так же быть функцией;axis
— ось (0 = группировать строки, 1 = группировать столбцы);level
— если ось представлена мультииндексом, то указывает на уровень мультииндекса;sort
— сортировка результата по индексу.
Результатом группировки является объект, состоящий из пар (имя группы, подтаблица). Имя группы соответствует значению, по которому произведена группировка. К объекту-результату группировки применимы, например, следующие операции:
for name, group in groupped: ...
— цикл по группам;get_group(name)
— получить таблицу, соответствующую группе с именемname
;groups
— получить все группы в виде словаря имя-подтаблица;count()
— количество значений в группах, исключая пропуски;size()
— размер групп;sum()
,max()
,min()
;mean()
,median()
,var()
,std()
,corr()
,quantile(q)
;describe()
— вывод описательных статистик;aggregate(func)
— применение функции (или списка функций)func
к группам.
Создадим таблицу для примера
df = pd.DataFrame({
'Животное' : ['Котик', 'Песик', 'Котик', 'Песик',
'Котик', 'Песик', 'Котик', 'Песик'],
'Цвет шерсти' : ['белый', 'белый', 'коричневый', 'черный',
'коричневый', 'коричневый', 'белый', 'черный'],
'Рост' : sps.gamma(a=12, scale=3).rvs(size=8),
'Длина хвостика' : sps.gamma(a=10).rvs(size=8)
})
df
Пример 1.¶
Если все котики встанут друг на друга, то какой их суммарный рост? А у песиков? А какова суммарная длинна хвостиков у котиков и у песиков?
Группировка по одной колонке и последующее применение операции суммирования:
df.groupby('Животное').sum()
Посчитаем описательные статистики для каждого животного
df.groupby('Животное').describe()
Пример 2.¶
Теперь предположим, что котики и песики встают только на представителей своего вида и своего цвета шерсти. Что тогда будет?
Группировка по двум колонкам и последующее применение операции суммирования
df.groupby(['Животное', 'Цвет шерсти']).sum()
Полученная таблица имеет мультииндекс
df.groupby(['Животное', 'Цвет шерсти']).sum().index
4. Таблицы сопряженности (Crosstab) и сводные таблицы (Pivot table)¶
Задача. В медицинской клинике информацию о приемах записывают в таблицу со следующими полями:
- время приема,
- врач,
- пациент,
- поставленный диагноз,
- назначение,
- другие поля.
Требуется посчитать, сколько раз за предыдущий месяц каждый врач ставил какой-либо диагноз. Результаты представить в виде таблицы, в которой посчитать также суммы по строкам и столбцам, т.е. сколько врач сделал приемов за месяц и сколько раз конкретный диагноз поставлен всеми врачами.
Как решать?
Способ 1
- Группировка по врачам.
- Для каждого врача группировка по диагнозам.
- В каждой группе вычисление суммы.
- Соединение в одну таблицу.
- Вычисление суммы по столбцам и по строкам.
Можете прикинуть количество строк кода и время работы 😁
Способ 2
- Создать пустую таблицу.
- Циклом 🤣 по всем записям исходной таблицы считать суммы.
- Вычисление суммы по столбцам и по строкам.
И снова можете прикинуть количество строк кода и время работы 😁
Способ 3
Применить умную функцию из pandas, которая сделает все сама!
4.1 Функция pd.crosstab
¶
Эксель-подобные таблицы сопряженности
pd.crosstab(index, columns, values=None, rownames=None, colnames=None, aggfunc=None, margins=False, margins_name='All', dropna=True, normalize=False)
index
— значения для группировки по строкам;columns
— значения для группировки по столбцам;values
— аггригируемый столбец (или столбцы), его значения непосредственно определяют значения таблицы сопряженности;aggfunc
— функция, которая будет применена к каждой группе значенийvalues
, сгруппированным по значениямindex
иcolumns
. Значения этой функции и есть значения сводной таблицы;rownames
иcolnames
— имена строк и столбцов таблицы сопряженности;margins
— добавляет результирующий столбец/строку;margins_name
— имя результирующего столбец/строку;dropna
— не включать столбцы, которые состоят только изNaN
;normalize
:boolean
, {'all'
,'index'
,'columns'
} — нормировка всей таблицы (или только по строкам/столбцам).
В примере выше:
pd.crosstab(df['Врач'], df['Диагноз'], margins=True)
4.2 Функция pd.pivot_table
¶
Эксель-подобные сводные таблицы
pd.pivot_table(data, values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All')
data
— исходная таблица;values
— аггригируемый столбец, его значения непосредственно определяют значения сводной таблицы;index
— ключи для группировки, относятся к индексам сводной таблицы;columns
— ключи для группировки, относятся к столбцам сводной таблицы;aggfunc
— функция, которая будет применена к каждой группе значенийvalues
, сгруппированным по значениямindex
иcolumns
. Значения этой функции и есть значения сводной таблицы. Если передается список функций, то сводная таблица имеет иерархические имена колонок, верхние значения которых — имена функций;fill_value
— значения для замены пропусков;dropna
— не включать столбцы, которые состоят только изNaN
;margins
— добавляет результирующий столбец/строку;margins_name
— имя результирующего столбец/строку.
В примере выше:
pd.pivot_table(df, index='врач', columns='диагноз', margins=True)
df = pd.DataFrame({
'Специальность' : ['Ветеринар', 'Ветеринар',
'Психолог', 'Психолог'] * 6,
'Врач' : ['Андрей', 'Сергей', 'Ирина'] * 8,
'Диагноз' : ['Простуда', 'Простуда', 'Простуда',
'Волнения', 'Волнения', 'Простуда'] * 4,
'Доза' : sps.randint(low=1, high=6).rvs(size=24),
'Продолжительность' : sps.randint(low=1, high=6).rvs(size=24)
})
df
Посчитаем, сколько раз какой врач ставил каждый из диагнозов, а также суммы по строкам и столбцам
pd.crosstab(df['Врач'], df['Диагноз'], margins=True)
Посчитаем, какую среднюю дозу какой врач назначал по каждому из диагнозов
pd.crosstab(df['Врач'], df['Диагноз'],
values=df['Доза'], aggfunc=np.mean)
Простейший вариант сводной таблицы — среднее в группах, определяемых столбцом. Посчитаем средние по каждому врачу
pd.pivot_table(df, index=['Врач'])
Посчитаем, сколько раз врач и в какой специальности ставил тот или иной диагноз
pd.pivot_table(df,
values='Доза',
index=['Специальность', 'Врач'],
columns=['Диагноз'],
aggfunc=np.sum)
Добавим строчку, являющейся суммой столбцов, и столбец, являющийся суммой строк
pd.pivot_table(df,
values='Доза',
index=['Специальность', 'Врач'],
columns=['Диагноз'],
aggfunc=np.sum,
margins=True)
Применим несколько функций и несколько столбцов со значениями
pd.pivot_table(df,
values=['Доза', 'Продолжительность'],
index=['Специальность', 'Врач'],
columns=['Диагноз'],
aggfunc=[np.min, np.mean, np.max],
margins=True)