Анализ данных с Pandas.Python

У вас есть данные которые надо превратить в аккуратную табличку, Pandas вам поможет ) Pandas — программная библиотека на языке Python для обработки и анализа данных. Работа с данными строится поверх библиотеки NumPy, являющейся инструментом более низкого уровня. Вообщем если вы ходите ваши хаотичные данные загнать в красивую табличку, где уже намного легче обрабатывать и анализировать данные, поехали.

У нас есть набор данных, который нужно превратить в таблицу. Это делается вызовом конструктора DataFrame() Конструктор принимает два аргумента – список данных и названия столбцов, которые должны быть в таблице. Попробуем :

import pandas as pd

data = [['yakov_segidenko', 'yakov_segidenko@mail.ru', '1010vbh'],
        ['novokuzmenu', 'novokuzmenu@mail.ru', '9068989m'],
        ['zhele.anastasiya', 'zhele.anastasiya@mail.ru', 'drakosha']
]
colums = ['login', 'mail', 'pass']
tables = pd.DataFrame(data = data , columns = colums)
print(tables)

Вот мы получили аккуратную таблицу с 3-мя колонками и 3-мя рядами (с 0 по 2 индекс). Конечно мы руками не будем забивать все данные . Давайте загрузим из из CSV файла, я подготовил не большую базу для примера на которой мы будем тренироваться. Качаем .

df = pd.read_csv('mail.csv' ,error_bad_lines=False)
# error_bad_lines=False - база немного битая , пропускаем строки с ошибками.

CSV (от англ. Comma-Separated Values — значения, разделённые запятыми) — текстовый формат, предназначенный для представления табличных данных. Строка таблицы соответствует строке текста, которая содержит одно или несколько полей, разделенных запятыми.

Информация о базе.

Давайте ознакомимся с данными с помощью двух методов : head() и tail() . head(по умолчанию 5) — вернет строки набора данные с начала таблички, tail() — соответственно с хвоста.

print(df.head())
print(df.tail())

У DataFrame есть неотъемлемые свойства, значения которых можно запросить. Атрибут columns содержит информацию о названиях столбцов в наборе данных. Для просмотра типа данных каждого столбца лучше всего использовать атрибут dtypes. object соответствует типу srt. О размерах таблицы с данными сообщает её атрибут shape.

print(df.columns)
print()
print(df.dtypes)
print()
print(df.shape)

Всю информацию, которую предоставляют разные атрибуты DataFrame, можно получить вызовом одного-единственного метода info().

print(df.info())

Работа с данными или индексация.

К каждой ячейке с данными в DataFrame можно обратиться по её индексу и названию столбца. Мы можем получать различные срезы данных в зависимости от того, какой запрос к DataFrame мы сформулируем.

Атрибут loc[строка, столбец] даёт доступ к элементу по строке и столбцу.

  • Одна ячейка — .loc[7, 'название столбца']
  • Один столбец — .loc[:, 'название столбца']
  • Несколько столбцов — .loc[:, ['название столбца', 'название столбца']]
  • Несколько столбцов подряд (срез) — .loc[:, 'login': 'pass']
  • Одна строка — .loc[1]
  • Все строки, начиная с заданной — .loc[1:]
  • Все строки до заданной — .loc[:3]
  • Несколько строк подряд (срез) — .loc[2:5]

Но это не самое интересное, мы можем использовать логическую индексацию. Давайте сделаем выборку все данных где пароль задан 12345 :

tmp = df.loc[df.loc[:,'pass'] == '12345'][:]
print(tmp)

[: , 'pass'] == '12345' ] — логическое условие которое проверяется для каждого элемента столбца 'pass' . [ : ] — какой столбец выбираем.

Так же мы можем посчитать количество данных. В Pandas для этого есть метод count() :

tmp = df.loc[df.loc[:,'pass'] == '12345']
print(tmp.count())
login    4
mail     4
pass     4
dtype: int64

Для примера посчитаем долю паролей «12345» в базе данных :

total_pass = df.loc[:, 'Password']
sim_pass = df.loc[df.loc[:, 'Password'] == '12345']['Password'].count()
x = sim_pass / (total_pass.loc[:].count())
print('Пароль "12345" используется только в {:.3%} случаях'.format(x))

>>> Пароль "12345" используется только в 0.027% случаях.

Очистка данных.

Конечно данные не идеальны. Где то пропущены значения, возможно есть дубликаты и т.д. Pandos конечно нам позволяет очистить данные от мусора.

Для начало поменяем название столбцов, воспользуемся методом set_axis() . Он принимает три аргумента:

  • список с новыми названиями столбцов;
  • axis  — ось, которой новые названия присваиваются: ‘index’, если они даются строкам, и ‘columns’, если это список новых названий столбцов;
  • inplace — принимает значения True либо False. В первом случае метод set_axis()перестраивает структуру данных так, что она замещает прежнюю в переменной с тем же именем.
new = ['Login', 'Email', 'Password']
df.set_axis(new, axis = 'columns', inplace = True)

Так же в данных бывает что и нет данных ) В нашей таблице есть ячейки без данных .Замены пропущенных значений в DataFrame бывают трёх видов:

  • ОжидаемыеNone или NaN. None (англ. none, «ничто») — это эквивалент null в других языках программирования: особое значение, указывающее, что в этой ячейке таблицы никакого значения нет. None относится к NoneType .
  • Странные: плейсхолдеры (тексты-заполнители) какого-нибудь общепринятого стандарта, иногда неизвестного вам, но которого придерживаются составители. Чаще всего это n/anaNA (от англ. no answer, «нет ответа»), и N.N.либо NN (от лат. Nomen nescio, «не знаю имени»).
  • Неожиданные: например, разработчики решили, что пустые значения в таблице будут заполняться знаками вопроса или нулями. В лучшем случае этот факт укажут в документации, в худшем – придётся просматривать данные самостоятельно. Если какой-нибудь спецсимвол или число встречаются часто, и этому нет внятного объяснения, то высока вероятность, что так передаются пропущенные значения.

Для поиска первых двух типов в Pandas есть два метода .isnull() и .isna() . Если значение элемента не существует, .isnull() и .inna() возвращает True, а иначе — False. Суммируют эти True вызовом метода sum()  который в этом случае возвращает общее число элементов без определённых значений.

print('.isna()')
print(df.isna().sum())
print('.isnull()')
print(df.isnull().sum())

Как мы видим у нас есть пропущенные значения в столбце Login и Password. Давайте пропущенные значения в столбце Login заполним нулями . Для этого будем использовать метод .fillna(чем заполнить)

print(df.loc[8640])
df['Login'] = df['Login'].fillna(0)
print()
print(df.loc[8640])

А строки с пропущенными данными в Password удалим методом .dropna()

print(df.loc[8150:8160])
df.dropna(subset = ['Password' ], inplace= True)
print()
print(df.loc[8150:8160])

________________________________________________________________________________________________

Заметьте , индексирование таблицы у нас нарушилась : после 8152 идет сразу 8154 . Исправим это методом .reset_index( drop=True ) .

Теперь перейдем к дубликатам. Для поиска дубликатов есть метод .duplicated(). Он возвращает Series со значением True при наличии дубликатов, и False, когда их нет.

print('Всего дубликатов',df.duplicated().sum())

Всего дубликатов 3641
                  Login                     Email  Password
1740   zhele.anastasiya  zhele.anastasiya@mail.ru  drakosha
14962  zhele.anastasiya  zhele.anastasiya@mail.ru  drakosha

Для удаления дубликатов из данных используем метод .drop_duplicates()

df = df.drop_duplicates().reset_index(drop=True)

Для просмотра всех уникальных значений в столбце используется метод .unique()

Для замены определенных значений можно воспользоваться методом replace(), где первый аргумент — текущее значение, а второй — новое, нужное.

df['Солб'] = df['Солб'].replace('12345', '123457')

Анализ данных.

Для анализа данных мы возьмем базу найденных экзопланет в формате CSV , качаем.

  • name: название экзопланеты;
  • mass: масса в массах планеты Юпитер;
  • radius: радиус, пересчитанный в радиусах Земли;
  • discovered: год открытия экзопланеты.
  • star_mass : масса в массах Солнца.

Анализ данных начинают с разделения их на группы по какому-нибудь признаку. Эта операция называется группировка данных. Группировка оправданна, если данные чётко делятся по значимому признаку, а полученные группы близки к теме задачи.

В Рandas для группировки данных есть метод .groupby() . Он принимает как аргумент название столбца, по которому нужно группировать. В случае с делением экзопланет по годам открытия:

import pandas as pd
df = pd.read_csv('exoplanet.eu_catalog.csv' ,error_bad_lines=False)
print(df.groupby('discovered').count())

Применение метода .groupby() к объекту типа DataFrame приводит к созданию объекта особого типа — DataFrameGroupBy. Это сгруппированные данные. Если применить к ним какой-нибудь метод Pandas, они станут новой структурой данных типа DataFrame или Series.

В 2014 и 2016 было открыто очень много планет. Уммм

Если нужно сравнить по одному показателю, метод применяют к DataFrameGroupBy с указанием на один столбец. Например радиус :

radius = df.groupby('discovered')['radius'].count()
print(radius)

Давайте сделаем выборку по планетам радиус которых почти равен земному и отсортируем по убыванию. Для сортировки значений в Pandas есть метод .sort_values( )

  • ‘имя столбца’ — имя столбца, по которому нужно сортировать;
  •  ascending: по умолчанию True. Для сортировки по убыванию установите значение False.
exo = df[(df['radius'] <= 1.1) & (df['radius'] >= 0.9)].sort_values('radius')
print(exo)

Oписательная статистика — четыре основных метода максимум, минимум, медиана и среднее

grup = df.groupby('discovered')
# Максимум
print(grup['radius'].max())
# Минимум
print(grup['radius'].min())

В статистике медиана делит выборку пополам: в одной половине значения меньше медианного, в другой больше. Логично, что для определения медианы список обязательно должен быть отсортирован — либо по возрастанию, либо по убыванию.

df.sort_values('radius')
#Медиан
print(grup['radius'].median())
#Сред
print(grup['radius'].mean())