Введение
В этой части мы применим наши знания на практике. Мы попробуем поработать в Python, поговорим о том, какие основные типы данных есть в этом языке, как расширить возможности языка. Затем, мы попробуем поработать с данными в Python, используя библиотеку pandas.
Стандартные типы данных
Для анализа данных мы будем использовать Python. Чтобы работать с данными, их необходимо прочитать и сохранить в памяти как объект подходящего типа под именем, которое мы называем переменной. Мы уже говорили о переменных раньше и важно не путать использование этого термина в статистике и в программировании.
Попробуем создать переменную для числа 10.
>>> a = 10
Здесь а
— имя переменной, =
— оператор присваивания, а 10
— значение переменной. В Python тип данных, который соответствует целым числам, называется integer (целочисленный). Теперь, мы можем при необходимости обращаться к данным, которые содержит эта переменная.
>>> print(a)
10
>>> a + 10
20
Переменная может содержать и текстовые данные. Этот тип данных называется string (строка). Для создания мы используем:
>>> c = 'Name'
>>> print(c)
Name
Переменная может содержать сразу несколько элементов. Такой тип данных называется list (список). В Python список может хранить в качестве элементов любой тип данных: строки, числа или даже списки. Чтобы создать список, мы присваиваем одной переменной несколько объектов, перечисленных в квадратных скобках.
>>> b = [1, 5, 10]
>>> print(b)
[1, 5, 10]
Списки поддерживают операцию индексирования. Это значит, что мы можем обращаться к отдельным элементам списка по их порядковым номерам. Обратите внимание, что первый элемент имеет индекс 0
, второй — 1
и так далее. Это можно объяснить следующим образом: номер индекса — это число смещений указателя, которое нам нужно сделать, начиная от первого элемента. Соответственно, для первого элемента нам не нужно смещаться, для второй — нужно сместиться один раз.
>>> print(b[0])
1
Последний из типов данных, который мы упомянем в этой главе, — это вещественные числа. Обычно в языках программирования они записываются в форме десятичных дробей с некоторым ограниченным числом знаков после запятой в дробной части. В том случае, если в результате вычисления получается число, которое выходит за пределы этого числа знаков, то оно записывается в округлённом виде.
Например, результат деления 2 на 3 должен быть бесконечной периодической дробью, однако в Python он будет записан и выведен на экран следующим образом:
>>> 2 / 3
0.6666666666666666
Из курса алгебры мы можем помнить, что более точная запись ответа выглядит как 0.(6), где (6) фиксирует периодическую часть дроби. Именно необходимость манипулировать дробями с ограниченной точностью приводит к тому, что мы различаем типы данных для целых и вещественных чисел.
Стандартные типы данных в Python:
Описание | Имя в Python | Пример |
---|---|---|
Целое число | integer | 3 |
Вещественное число | float | 3.22 |
Строка | string | qwerty |
Логический тип | bool | True |
Мы упомянули в таблице логический тип, который может принимать только два значения True
и False
. Они могут быть получены как в результате выполнения логических операций, так и операций сравнения.
Мы уже упоминали, что с переменными можно выполнять операции — и даже выполняли некоторые из них: получали элемент списка по индексу и складывали числа. Знак сложения в выражении f = d + e называется оператором.
Оператор — это символ, который выполняет операцию с переменными.
Составим перечень и сразу отсортируем его по приоритету выполнения:
Уровень | Категория | Операторы |
---|---|---|
7 (в первую очередь) | Возведение в степень | ** |
6 | Умножение | *, /, //, % |
5 | Сложение | +, – |
4 | Сравнение | ==, !=, <=, >=, >, < |
3 | Логическое НЕ | not |
2 | Логическое И | and |
1 | Логическое ИЛИ | or |
Приведем несколько примеров выполнения операций с помощью операторов. Обратите внимание, что при использовании операторов сравнения и логических операторов в качестве результата мы получаем логический тип данных (bool).
>>> 1 + 23
24
>>> 2 >= 3
False
>>> True and False
False
Датафреймы
Все типы данных и действия с ними, которые мы описали в предыдущем пункте доступны любым пользователям языка Python. Это так называемая стандартная библиотека, которые содержит в себе самые базовые возможности языка. Однако она может быть расширена с помощью дополнительных библиотек, которые предоставляются другими разработчиками. Зачастую они не связаны с командой, которая развивает сам язык программирования. Обычно библиотека содержит в себе расширения, позволяющие решать какой-то конкретный тип задач: например, работу с данными или веб-разработку.
Так как стандартные возможности Python по работе с табличными данными достаточно ограничены, то для этого нам нужна дополнительная библиотека. Она называется pandas. Эта библиотека позволяет читать табличные данные, совершать с ними различные преобразования, вычислять различные статистические параметры, а также служит основой для библиотек, которые содержат реализации более сложных методов анализа.
Pandas позволяет работать с данными, которые сохранены в файлах разных форматов. Возможно самый распространенный из них — это csv, с которым вы уже работали в предыдущей главе.
В качестве примера мы будем использовать данные о пассажирах Титаника, которые хранятся в файле titanik.csv
.
Чтобы использовать библиотеку, нам нужно импортировать её — то есть добавить к стандартной библиотеке и сделать доступной для использования.
>>> import pandas as pd
Здесь мы после ключевого слова import
указываем название библиотеки, а после ключевого слова as
задаём псевдоним для библиотеки. Если псевдоним не используется, то библиотека добавляется под своим собственным названием. Для pandas принято использование псевдонима pd. C помощью ключевого слова import
мы можем загрузить любую библиотеку.
Для открытия файла csv мы используем следующую инструкцию:
>>> df = pd.read_csv('titanic.csv')
df
— это имя переменной для датафрейма. pd
указывает на то, из какой библиотеки берётся функция, read_csv()
— какую функцию из библиотеки мы хотим применить. А в скобках мы указываем параметры, которые дополнительно определяют поведение функции.
- Функция — подпрограмма, выполняющая какие-либо операции и возвращающая значение, которое может быть сохранено как переменная.
- Метод — это функция, которая относится к какому-то классу или экземпляру этого класса.
- Параметр функции — принятое функцией значение, переданное из вызывающего кода. Это значение называется «аргумент».
Например, в функции read_csv()
в качестве параметра мы указываем местоположение файла с данными, который нас интересует. Так как он в нашем примере он лежит в рабочей папке, то мы можем ограничиться только его названием. Если файл находится в отличном от рабочей директории месте, то необходимо прописать абсолютный путь к файлу. Например, так:
>>> df = pd.read_csv('C:/Users/User/Projects/Titanic/'titanic.csv')
Чтобы узнать, что именно в вашем случае является рабочей директорией, можно использовать следующий код:
>>> import os
>>> os.getcwd()
'C:/Users/User/Projects/Titanic/'
После загрузки данных мы создали объект с типом данных «датафрейм» (DataFrame). Его нет в стандартной библиотеке, но он доступен в pandas.
Ближайшей аналогией датафрейма будет таблица данных в Excel: датафрейм состоит из строк и колонок, где в строках указаны переменные, а в колонках значения, соответствующие каждой переменной.
Несколько полезных команд, которые позволят нам лучше понять, как устроены наши данные:
Получить первые десять строк датафрейма. В качестве параметра указывается число строк, которые необходимо вывести:
>>> df.head(10)
Узнать названия колонок, их количество, тип данных в них, количество наблюдений:
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 PassengerId 891 non-null int64
1 Survived 891 non-null int64
2 Pclass 891 non-null int64
3 Name 891 non-null object
4 Sex 891 non-null object
5 Age 714 non-null float64
6 SibSp 891 non-null int64
7 Parch 891 non-null int64
8 Ticket 891 non-null object
9 Fare 891 non-null float64
10 Cabin 204 non-null object
11 Embarked 889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB
Узнать базовые описательные статистики (максимальное и минимальное значение, среднее значение, стандартное отклонение и квартили), для колонок которые содержат числовые данные (float
или int
).
>>> df.describe()
Получить информацию о количестве строк и колонок. Обратите внимание, что круглые скобки после этой команды не ставятся, так как это атрибут объекта, а не метод.
>>> df.shape
(891, 12)
После того как мы составили первое впечатление о датафрейме, мы можем начать исследовать его. Разберём три задачи, которые могут перед нами стоять на этом этапе работы с данными:
- фильтровать датафрейм, то есть выбрать определенный диапазон значений по одной или нескольким переменным. Например, мы можем захотеть посмотреть на данные только по людям, старше 35 лет;
- получить базовые описательные статистики для интересующих нас переменных, а не датафрейма в частности. Например, если мы хотим узнать средний возраст мужчин, или медианную стоимость билетов в первый класс.
Фильтрация датафрейма
Начнем с фильтрации датафрейма. Попробуем увидеть единственную колонку, содержащую информацию о возрасте.
>>> df['Age']
0 22.0
1 38.0
2 26.0
3 35.0
4 35.0
...
886 27.0
887 19.0
888 NaN
889 26.0
890 32.0
Name: Age, Length: 891, dtype: float64
Можно использовать другой способ: df.Age. Здесь мы обращаемся к колонке как к атрибуту. Результат будет таким же, но он работает только в том случае, если в названии колонки нет пробелов. В дальнейшем мы будем использовать первый способ записи.
Допустим, мы можем выбрать несколько колонок, например, класс и возраст. В таком случае наш код должен выглядеть следующим образом:
>>> df[['Pclass', 'Age']]
Обратите внимание на две открывающие и две закрывающие квадратные скобки. Внешняя пара квадратных скобок — это способ указать на колонку или колонки. Внутренняя пара квадратных скобок создаёт список, который содержит нужные колонки.
В случае, если мы хотим получить доступ к определённым строкам нашего датафрейма, то можем использовать следующую инструкцию:
>>> df[0:5]
Обратите внимание, что нумерация в датафрейме начинается с 0, а не 1, так же как в списках, поэтому инструкция df[1:19] – выдала бы нам результат, в котором отсутствует первый элемент.
Когда мы работаем с большим датафреймом, то для переменных, которые описываются номинальной шкалой, нам может быть важно понять, из каких значений она состоит. Для этого мы можем использовать метод unique()
. Так, например, можно узнать, какое значение может принимать наблюдение в колонке Pclass
– или, говоря проще, какие классы были на Титанике.
>>> df['Pclass'].unique()
array([3, 1, 2])
Помимо этого, нам может быть важно узнать количество наблюдений, соответствующих каждому значению параметра класс
. То есть, сколько людей ехало на Титанике в каждом классе. Для этого используем следующую инструкцию:
>>> df['Pclass'].value_counts()
3 491
1 216
2 184
Name: Pclass, dtype: int64
Теперь мы знаем, что в первом классе ехало 216 человек. Метод value_counts
может быть уточнён с помощью параметра так, чтобы мы не просто узнали количество людей в каждом классе, но увидели пропорции. Для этого добавим к нашей записи дополнительный аргумент:
>>> df['Pclass'].value_counts(normalize=True)
3 0.551066
1 0.242424
2 0.206510
Name: Pclass, dtype: float64
Теперь мы знаем не только количество, но и пропорцию людей в разных классах. Так, чуть больше 55% людей на корабле было из третьего класса.
Мы знаем, что колонка с информацией о классе содержит всего три значения: 1, 2, 3. Они соответствуют первому, второму и третьему классу билетов. Если мы захотим узнать среднюю стоимость билета в первом классе, то сначала нам необходимо отфильтровать наблюдения, относящиеся к первому классу. О том, как узнать среднее мы поговорим чуть позже.
>>> df[df['Pclass'] == 1]
Можем сохранить в отдельный датафрейм этот результат:
>>> df_class_1 = df[df['Pclass'] == 1]
Такой способ работает для числовых переменных. Оператор ==
может быть заменен на <
, >
и другие операторы равенства и сравнения, если мы хотим получить значения, которые больше или меньше определённого значения. Мы можем использовать операторы равенства и для текстовых значений:
>>> df[df['Sex'] == 'male']
Наконец, последнее, что нам осталось научиться делать — использовать сразу несколько условий для выбора части датафрейма. Предположим, что мы хотим изучить женщин из первого класса. То есть нам необходимо задать два условия: класс должен быть первым, а пол — женским. Мы можем объединить несколько условий:
>>> df[(df['Pclass'] == 3) & (df['Sex']=='female')]
Круглые скобки вокруг каждого из отдельных условий обязательны. Еще в pandas используются другие символы для некоторых логических операторов.
Таблица различий между стандартной библиотекой и pandas:
Название | Стандартная библиотека | Pandas |
---|---|---|
Логическое НЕ | not | ~ |
Логическое И | and | & |
Логическое ИЛИ | or |
Итак, к текущему моменту мы научились:
- фильтровать нужные нам колонки и строки;
- узнавать значения переменных в той или иной колонке;
- оценивать количество уникальных значений в колонке — как пропорционально, так и количественно.
Дескриптивные статистики
Теперь посмотрим, что мы можем сказать о данных, используя самые простые приемы дескриптивной статистики: среднее, медиана и стандартное отклонение. Общий принцип здесь такой: мы указываем датафрейм, указываем интересующую нас колонку, например, стоимость билета, как в этом примере, и указываем среднее или медиану мы хотим получить:
>>> df['Fare'].mean()
32.204207968574636
>>> df['Fare'].median()
14.4542
Помимо среднего и медианы есть и другие методы. Например, мы можем узнать максимальное и минимальное значение:
>>> df['Age'].max()
80.0
>>> df['Age'].min()
0.42
Или узнать сумму всех значений в какой-то колонке, например сумму стоимости всех билетов:
>>> df['Fare'].sum()
28693.9493
Существует два схожих метода sum()
и cumsum()
. Попробуйте самостоятельно понять, чем они отличаются.
Чтобы вычислить стандартное отклонение, применим такую функцию:
>>> df['Age'].std()
14.526497332334042
Но есть важный нюанс: стандартное отклонение считаются по-разному в зависимости от того, идёт ли речь о выборке или о генеральной совокупности. С точки зрения вычислений в Python, в случае работы с выборкой мы просто изменяем значение для параметра ddof:
>>> df['Age'].std(ddof=1)
14.526497332334042
В данном случае оно остается неизменным.
Заключение
В этой главе мы определили, что такое данные и из каких элементов они состоят. Мы сказали, что для каждого наблюдения в данных мы можем фиксировать выбранные характеристики, измеряя их по выбранной нами шкале. Для анализа мы представляем данные в форме массива: вектора для отдельного наблюдения или характеристики и матрицы для нескольких наблюдений и характеристик.
Если полученные нами данные были собраны случайным образом, то их распределение может рассматриваться как приближенное к распределению всех возможных наблюдений, обладающих таким признаком. Далее мы с помощью мер центральности и других характеристик распределения можем зафиксировать основные свойства данных и построить простую модель их описания.
Наконец, мы сказали, что мы можем использовать языки программирования для обработки данных. Помимо стандартных типов данных мы можем использовать датафрейм как более удобное представление массива данных и совершать с ним манипуляции, чтобы отфильтровать данные и получить описательные статистики распределения.
В следующей главе мы поговорим более подробно о структуре данных, базовых статистических тестах и их реализации в Python.