Библиотека Pandas для работы с данными
- На основе материалов за авторством: Юрий Кашницкий (DS in KPN, NLP-Researcher) и Екатерина Демидова (DS в Segmento).
Pandas — программная библиотека на языке Python для обработки и анализа данных. Работа pandas с данными строится поверх библиотеки NumPy, являющейся инструментом более низкого уровня. Предоставляет специальные структуры данных и операции для манипулирования числовыми таблицами и временны́ми рядами.
Главный элемент пандаса - DataFrame (датафрейм, df), с которым можно производить необходимые преобразования. df - “таблица”, состоящая из строк и столбцов. По умолчанию, строчки таблицы - это объекты, а столбцы - признаки (фичи) объектов.
import pandas as pd import numpy as np
Создание DataFrame
d = {'feature1': [4,3,2,1,0], 'feature2': ['x', 'z', 'y', 'x', 'z'], 'feature3': [2,3,4,1,0]} df = pd.DataFrame(d) df
feature1 | feature2 | feature3 | |
---|---|---|---|
0 | 4 | x | 2 |
1 | 3 | z | 3 |
2 | 2 | y | 4 |
3 | 1 | x | 1 |
4 | 0 | z | 0 |
data = [['tom', 10], ['nick', 15], ['juli', 14]] df = pd.DataFrame(data, columns = ['Name', 'Age']) df
Name | Age | |
---|---|---|
0 | tom | 10 |
1 | nick | 15 |
2 | juli | 14 |
data = {'Name':['Tom', 'Jack', 'nick', 'juli'], 'marks':[99, 98, 95, 90]} df = pd.DataFrame(data, index =['rank1', 'rank2', 'rank3', 'rank4']) df
Name | marks | |
---|---|---|
rank1 | Tom | 99 |
rank2 | Jack | 98 |
rank3 | nick | 95 |
rank4 | juli | 90 |
data = [{'a': 1, 'b': 2, 'c':3}, {'a':10, 'b': 20}] df = pd.DataFrame(data) df
a | b | c | |
---|---|---|---|
0 | 1 | 2 | 3.0 |
1 | 10 | 20 | NaN |
d = {'one' : pd.Series([10, 20, 30, 40], index =['a', 'b', 'c', 'd']), 'two' : pd.Series([10, 20, 30, 40], index =['a', 'b', 'c', 'd'])} df = pd.DataFrame(d) df
one | two | |
---|---|---|
a | 10 | 10 |
b | 20 | 20 |
c | 30 | 30 |
d | 40 | 40 |
Первичный анализ данных с Pandas
Pandas — это библиотека Python, предоставляющая широкие возможности для анализа данных. С ее помощью очень удобно загружать, обрабатывать и анализировать табличные данные с помощью SQL-подобных запросов. В связке с библиотеками Matplotlib и Seaborn появляется возможность удобного визуального анализа табличных данных.
Данные, с которыми работают датсаентисты и аналитики, обычно хранятся в виде табличек — например, в форматах .csv, .tsv или .xlsx. Для того, чтобы считать нужные данные из такого файла, отлично подходит библиотека Pandas.
Основными структурами данных в Pandas являются классы Series и DataFrame. Первый из них представляет собой одномерный индексированный массив данных некоторого фиксированного типа. Второй - это двухмерная структура данных, представляющая собой таблицу, каждый столбец которой содержит данные одного типа. Можно представлять её как словарь объектов типа Series. Структура DataFrame отлично подходит для представления реальных данных: строки соответствуют признаковым описаниям отдельных объектов, а столбцы соответствуют признакам.
pd.read_csv('beauty.csv', nrows=2)
wage;exper;union;goodhlth;black;female;married;service;educ;looks | |
---|---|
0 | 5.73;30;0;1;0;1;1;1;14;4 |
1 | 4.28;28;0;1;0;1;1;0;12;3 |
#help(pd.read_csv) path_to_file = 'beauty.csv' data = pd.read_csv(path_to_file, sep=';') print(data.shape) #df.tail() data.head()
(1260, 10)
wage | exper | union | goodhlth | black | female | married | service | educ | looks | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 5.73 | 30 | 0 | 1 | 0 | 1 | 1 | 1 | 14 | 4 |
1 | 4.28 | 28 | 0 | 1 | 0 | 1 | 1 | 0 | 12 | 3 |
2 | 7.96 | 35 | 0 | 1 | 0 | 1 | 0 | 0 | 10 | 4 |
3 | 11.57 | 38 | 0 | 1 | 0 | 0 | 1 | 1 | 16 | 3 |
4 | 11.42 | 27 | 0 | 1 | 0 | 0 | 1 | 0 | 16 | 3 |
Мы считали данные по модельному бизнесу 80-90е года в США
type(data)
pandas.core.frame.DataFrame
#data.shape len(data)
1260
Чтобы посмотреть общую информацию по датафрейму и всем признакам, воспользуемся методом info:
data.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 1260 entries, 0 to 1259 Data columns (total 10 columns): wage 1260 non-null float64 exper 1260 non-null int64 union 1260 non-null int64 goodhlth 1260 non-null int64 black 1260 non-null int64 female 1260 non-null int64 married 1260 non-null int64 service 1260 non-null int64 educ 1260 non-null int64 looks 1260 non-null int64 dtypes: float64(1), int64(9) memory usage: 98.6 KB
int64 и float64 — это типы признаков. Видим, что 1 признак — float64 и 9 признаков имеют тип int64.
Метод describe показывает основные статистические характеристики данных по каждому числовому признаку (типы int64 и float64): число непропущенных значений, среднее, стандартное отклонение, диапазон, медиану, 0.25 и 0.75 квартили.
data.describe()
wage | exper | union | goodhlth | black | female | married | service | educ | looks | |
---|---|---|---|---|---|---|---|---|---|---|
count | 1260.000000 | 1260.000000 | 1260.000000 | 1260.000000 | 1260.000000 | 1260.000000 | 1260.000000 | 1260.000000 | 1260.000000 | 1260.000000 |
mean | 6.306690 | 18.206349 | 0.272222 | 0.933333 | 0.073810 | 0.346032 | 0.691270 | 0.273810 | 12.563492 | 3.185714 |
std | 4.660639 | 11.963485 | 0.445280 | 0.249543 | 0.261564 | 0.475892 | 0.462153 | 0.446089 | 2.624489 | 0.684877 |
min | 1.020000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 5.000000 | 1.000000 |
25% | 3.707500 | 8.000000 | 0.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 12.000000 | 3.000000 |
50% | 5.300000 | 15.000000 | 0.000000 | 1.000000 | 0.000000 | 0.000000 | 1.000000 | 0.000000 | 12.000000 | 3.000000 |
75% | 7.695000 | 27.000000 | 1.000000 | 1.000000 | 0.000000 | 1.000000 | 1.000000 | 1.000000 | 13.000000 | 4.000000 |
max | 77.720000 | 48.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 17.000000 | 5.000000 |
Посмотрим на признак “exper” - рабочий стаж
data['exper'].head() #data.exper.head() # 2-ой вариант
0 30 1 28 2 35 3 38 4 27 Name: exper, dtype: int64
Как описывалось ранее - тип данных в колонке является Series, что по сути является проиндексированным массивом
type(data['exper'])
pandas.core.series.Series
loc и iloc
С помощью loc и iloc - можно из начального датафрейма зафиксировать определённые интервал строк и интересующих столбцов и работать/смотреть только их
#data.loc[1:5, ['wage']] data.wage.loc[1:5]
1 4.28 2 7.96 3 11.57 4 11.42 5 3.91 Name: wage, dtype: float64
#data.iloc[0,1] # первое число - номер столбца (начинается с 0). Второе - индекс строчки data['wage'].iloc[1:5]
1 4.28 2 7.96 3 11.57 4 11.42 Name: wage, dtype: float64
Условия
Посмотрим на наш датафрейм, на соответствие какому-то условию
(data['exper'] >= 15)
0 True 1 True 2 True 3 True 4 True ... 1255 True 1256 False 1257 True 1258 True 1259 True Name: exper, Length: 1260, dtype: bool
Посмотрим только те строки, в датафрейме, которые удовлетворяют определённому условию, и выведем первые 5 из них
data[(data['female'] == 1) & (data['black'] == 1)].head(10)
wage | exper | union | goodhlth | black | female | married | service | educ | looks | |
---|---|---|---|---|---|---|---|---|---|---|
44 | 4.95 | 20 | 0 | 1 | 1 | 1 | 0 | 1 | 14 | 3 |
85 | 10.12 | 40 | 0 | 1 | 1 | 1 | 0 | 1 | 10 | 3 |
110 | 3.37 | 36 | 0 | 1 | 1 | 1 | 0 | 1 | 13 | 3 |
148 | 7.21 | 20 | 1 | 0 | 1 | 1 | 1 | 1 | 17 | 3 |
167 | 2.81 | 14 | 0 | 1 | 1 | 1 | 1 | 0 | 13 | 3 |
211 | 2.88 | 7 | 0 | 1 | 1 | 1 | 0 | 1 | 13 | 4 |
497 | 7.07 | 8 | 1 | 1 | 1 | 1 | 0 | 0 | 13 | 3 |
499 | 3.89 | 4 | 0 | 1 | 1 | 1 | 0 | 0 | 16 | 4 |
504 | 6.54 | 8 | 0 | 1 | 1 | 1 | 0 | 0 | 13 | 3 |
507 | 7.69 | 16 | 0 | 1 | 1 | 1 | 1 | 0 | 13 | 3 |
Посмотрим только те строки, которые удовлетворяют условию и выведем значение определённого столбца
data[data['female'] == 1]['wage'].head(10)
0 5.73 1 4.28 2 7.96 5 3.91 8 5.00 9 3.89 10 3.45 18 10.44 19 7.69 44 4.95 Name: wage, dtype: float64
data[(data['female'] == 0) & (data['married'] == 1)].head(10)
wage | exper | union | goodhlth | black | female | married | service | educ | looks | |
---|---|---|---|---|---|---|---|---|---|---|
3 | 11.57 | 38 | 0 | 1 | 0 | 0 | 1 | 1 | 16 | 3 |
4 | 11.42 | 27 | 0 | 1 | 0 | 0 | 1 | 0 | 16 | 3 |
6 | 8.76 | 12 | 0 | 1 | 0 | 0 | 1 | 0 | 16 | 3 |
11 | 4.03 | 6 | 0 | 1 | 0 | 0 | 1 | 0 | 16 | 4 |
12 | 5.14 | 19 | 0 | 1 | 0 | 0 | 1 | 1 | 17 | 2 |
14 | 7.99 | 12 | 0 | 1 | 0 | 0 | 1 | 0 | 16 | 4 |
15 | 6.01 | 17 | 0 | 1 | 0 | 0 | 1 | 0 | 16 | 4 |
16 | 5.16 | 7 | 0 | 1 | 0 | 0 | 1 | 0 | 17 | 3 |
17 | 11.54 | 12 | 0 | 1 | 0 | 0 | 1 | 1 | 17 | 4 |
21 | 6.79 | 19 | 0 | 1 | 0 | 0 | 1 | 1 | 14 | 3 |
# Метод describe для сложного условия data[(data['female'] == 0) & (data['married'] == 1)].describe()
wage | exper | union | goodhlth | black | female | married | service | educ | looks | |
---|---|---|---|---|---|---|---|---|---|---|
count | 658.000000 | 658.000000 | 658.000000 | 658.000000 | 658.000000 | 658.0 | 658.0 | 658.000000 | 658.000000 | 658.000000 |
mean | 7.716778 | 22.136778 | 0.308511 | 0.937690 | 0.037994 | 0.0 | 1.0 | 0.194529 | 12.495441 | 3.164134 |
std | 4.798763 | 11.714753 | 0.462230 | 0.241902 | 0.191327 | 0.0 | 0.0 | 0.396139 | 2.716007 | 0.655469 |
min | 1.050000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | 0.0 | 1.0 | 0.000000 | 5.000000 | 1.000000 |
25% | 4.810000 | 12.000000 | 0.000000 | 1.000000 | 0.000000 | 0.0 | 1.0 | 0.000000 | 12.000000 | 3.000000 |
50% | 6.710000 | 20.500000 | 0.000000 | 1.000000 | 0.000000 | 0.0 | 1.0 | 0.000000 | 12.000000 | 3.000000 |
75% | 8.890000 | 32.000000 | 1.000000 | 1.000000 | 0.000000 | 0.0 | 1.0 | 0.000000 | 13.000000 | 4.000000 |
max | 41.670000 | 48.000000 | 1.000000 | 1.000000 | 1.000000 | 0.0 | 1.0 | 1.000000 | 17.000000 | 5.000000 |
Посчитаем средние значения из тех данных, что удовлетворяют условию
data[data['female'] == 1]['wage'].mean(), data[data['female'] == 0]['wage'].mean() # .std, .min, .max, .count
(4.299357798165136, 7.3688228155339734)
Вывод медианного значения, для данных, удовлетворяющих сложному условию
data[(data['female'] == 0) & (data['married'] == 1)]['wage'].median(), \ data[(data['female'] == 0) & (data['married'] == 0)]['wage'].median()
(6.710000000000001, 5.0649999999999995)
data['wage'].nunique()
520
Ниже приводятся примеры использования метода groupby для отображения информации по сгруппированному признаку
data.groupby('looks').wage.count()
looks 1 13 2 142 3 722 4 364 5 19 Name: wage, dtype: int64
for look, sub_df in data.drop(['goodhlth'],axis=1).groupby('looks'): print(look) print(sub_df.head()) print()
1 wage exper union black female married service educ looks 28 8.35 41 0 0 0 1 1 16 1 200 3.75 36 0 0 0 0 0 12 1 248 10.99 40 0 0 0 1 0 12 1 327 1.65 24 0 0 1 0 1 13 1 751 7.93 39 1 0 0 1 0 12 1 2 wage exper union black female married service educ looks 12 5.14 19 0 0 0 1 1 17 2 33 8.17 18 0 0 0 1 0 16 2 35 9.62 37 0 0 0 1 0 13 2 37 7.69 10 1 0 0 1 0 13 2 57 6.56 17 0 0 0 1 0 13 2 3 wage exper union black female married service educ looks 1 4.28 28 0 0 1 1 0 12 3 3 11.57 38 0 0 0 1 1 16 3 4 11.42 27 0 0 0 1 0 16 3 5 3.91 20 0 0 1 1 0 12 3 6 8.76 12 0 0 0 1 0 16 3 4 wage exper union black female married service educ looks 0 5.73 30 0 0 1 1 1 14 4 2 7.96 35 0 0 1 0 0 10 4 7 7.69 5 1 0 0 0 0 16 4 10 3.45 3 0 0 1 0 0 12 4 11 4.03 6 0 0 0 1 0 16 4 5 wage exper union black female married service educ looks 26 14.84 29 0 0 0 0 1 13 5 27 19.08 17 0 0 0 0 0 17 5 76 23.32 15 0 0 0 1 1 17 5 112 6.11 7 0 0 1 1 0 12 5 316 3.92 12 0 0 0 1 1 12 5
for look, sub_df in data.groupby('looks'): print(look) print(sub_df['wage'].median()) print()
1 3.46 2 4.595000000000001 3 5.635 4 5.24 5 4.81
for look, sub_df in data.groupby('looks'): print(look) print(round(sub_df['female'].mean(), 3)) print()
1 0.385 2 0.38 3 0.323 4 0.374 5 0.421
for look, sub_df in data.groupby(['looks', 'female']): print(look) print(sub_df['goodhlth'].mean()) print()
(1, 0) 0.75 (1, 1) 1.0 (2, 0) 0.9431818181818182 (2, 1) 0.9259259259259259 (3, 0) 0.9304703476482618 (3, 1) 0.9012875536480687 (4, 0) 0.9649122807017544 (4, 1) 0.9411764705882353 (5, 0) 1.0 (5, 1) 1.0
С помощью .agg метод groupby может применять различные функции к данным, что он получает
data.groupby('looks')[['wage', 'exper']].max()
wage | exper | |
---|---|---|
looks | ||
1 | 10.99 | 41 |
2 | 26.24 | 45 |
3 | 38.86 | 48 |
4 | 77.72 | 47 |
5 | 23.32 | 32 |
Декартово произведение признаков из столбцов и их отображение
pd.crosstab(data['female'], data['married'])
married | 0 | 1 |
---|---|---|
female | ||
0 | 166 | 658 |
1 | 223 | 213 |
pd.crosstab(data['female'], data['looks'])
looks | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
female | |||||
0 | 8 | 88 | 489 | 228 | 11 |
1 | 5 | 54 | 233 | 136 | 8 |
Создание нового признака из наложения дополнительных условий на основе старых данных
data['exp'] = (data['exper'] >=15).astype(int) data.head(10)
wage | exper | union | goodhlth | black | female | married | service | educ | looks | exp | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 5.73 | 30 | 0 | 1 | 0 | 1 | 1 | 1 | 14 | 4 | 1 |
1 | 4.28 | 28 | 0 | 1 | 0 | 1 | 1 | 0 | 12 | 3 | 1 |
2 | 7.96 | 35 | 0 | 1 | 0 | 1 | 0 | 0 | 10 | 4 | 1 |
3 | 11.57 | 38 | 0 | 1 | 0 | 0 | 1 | 1 | 16 | 3 | 1 |
4 | 11.42 | 27 | 0 | 1 | 0 | 0 | 1 | 0 | 16 | 3 | 1 |
5 | 3.91 | 20 | 0 | 0 | 0 | 1 | 1 | 0 | 12 | 3 | 1 |
6 | 8.76 | 12 | 0 | 1 | 0 | 0 | 1 | 0 | 16 | 3 | 0 |
7 | 7.69 | 5 | 1 | 1 | 0 | 0 | 0 | 0 | 16 | 4 | 0 |
8 | 5.00 | 5 | 0 | 1 | 0 | 1 | 0 | 0 | 16 | 3 | 0 |
9 | 3.89 | 12 | 0 | 1 | 0 | 1 | 0 | 0 | 12 | 3 | 0 |
new = data[data['female'] == 1] new.to_csv('new.csv', index=False) new.head()
wage | exper | union | goodhlth | black | female | married | service | educ | looks | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 5.73 | 30 | 0 | 1 | 0 | 1 | 1 | 1 | 14 | 4 |
1 | 4.28 | 28 | 0 | 1 | 0 | 1 | 1 | 0 | 12 | 3 |
2 | 7.96 | 35 | 0 | 1 | 0 | 1 | 0 | 0 | 10 | 4 |
5 | 3.91 | 20 | 0 | 0 | 0 | 1 | 1 | 0 | 12 | 3 |
8 | 5.00 | 5 | 0 | 1 | 0 | 1 | 0 | 0 | 16 | 3 |
data['wage'].sort_values(ascending=False).head(3)
602 77.72 269 41.67 415 38.86 Name: wage, dtype: float64
data['is_rich'] = (data['wage'] > data['wage'].quantile(.75)).astype('int64')
data['wage'].quantile(.75)
7.695
data.head()
wage | exper | union | goodhlth | black | female | married | service | educ | looks | exp | is_rich | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 5.73 | 30 | 0 | 1 | 0 | 1 | 1 | 1 | 14 | 4 | 1 | 0 |
1 | 4.28 | 28 | 0 | 1 | 0 | 1 | 1 | 0 | 12 | 3 | 1 | 0 |
2 | 7.96 | 35 | 0 | 1 | 0 | 1 | 0 | 0 | 10 | 4 | 1 | 1 |
3 | 11.57 | 38 | 0 | 1 | 0 | 0 | 1 | 1 | 16 | 3 | 1 | 1 |
4 | 11.42 | 27 | 0 | 1 | 0 | 0 | 1 | 0 | 16 | 3 | 1 | 1 |
data['rubbish'] = .56 * data['wage'] + 0.32 * data['exper'] data.head()
wage | exper | union | goodhlth | black | female | married | service | educ | looks | exp | is_rich | rubbish | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 5.73 | 30 | 0 | 1 | 0 | 1 | 1 | 1 | 14 | 4 | 1 | 0 | 12.8088 |
1 | 4.28 | 28 | 0 | 1 | 0 | 1 | 1 | 0 | 12 | 3 | 1 | 0 | 11.3568 |
2 | 7.96 | 35 | 0 | 1 | 0 | 1 | 0 | 0 | 10 | 4 | 1 | 1 | 15.6576 |
3 | 11.57 | 38 | 0 | 1 | 0 | 0 | 1 | 1 | 16 | 3 | 1 | 1 | 18.6392 |
4 | 11.42 | 27 | 0 | 1 | 0 | 0 | 1 | 0 | 16 | 3 | 1 | 1 | 15.0352 |
Контест для проверки понимания ссылка.
Домашнее задание будет во 2ой части