Библиотека 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ой части