Физтех.Статистика
Скачать ipynb
Python для анализа данных¶
Библиотека Plotly
¶
Plotly — бесплатная графическая библиотека с открытым исходным кодом, в последние годы набирающая популярность в Data Science среде. Ее ключевое преимущество перед seaborn
и matplotlib
— удобство построения сложных интерактивных визуализаций — полноценных мини-приложений, которые делают результат работы аналитика более доступным для конечного пользователя.
Подробнее про установку можно почитать на этой странице.
import numpy as np
import scipy.stats as sps
import pandas as pd
import matplotlib.pyplot as plt
import datetime
import plotly
import plotly.graph_objects as go
import plotly.express as px
x = np.linspace(-1, 1, 101)
y1 = x**2
y2 = np.sin(10*x)
Построить график с помощью matplotlib
можно следующим образом
plt.figure(figsize=(8, 5))
plt.plot(x, y1, label='Квадрат', lw=3, color='#00CC66')
plt.plot(x, y2, label='Синус', lw=3, marker='o')
plt.legend()
plt.xlim((-1.5, 1.5))
plt.show()
С помощью plotly
графики рисуются с помощью следующей общей конструкции:
fig = go.Figure()
fig.add_trace(что рисовать)
fig.update_layout(параметры фигуры)
fig.update_xaxes(параметры оси абсцисс)
fig.update_yaxes(параметры оси ординат)
fig.show()
Теперь нарисуем наши функции с помощью plotly
.
# объявляем фигуру
fig = go.Figure()
# добавляем первый график
fig.add_trace(
go.Scatter(
x=x, y=y1, # данные
name='Квадрат', # имя в легенде
marker=dict(color='#00CC66'), # цвет в html-формате
opacity=0.8, # прозрачность
line={'width': 3} # свойства линии - толщина
)
)
# добавляем второй график
fig.add_trace(
go.Scatter(
x=x, y=y2, # данные
mode='lines+markers', # линии и точки
name='Синус', # имя в легенде
marker=dict( # свойства маркера
color='rgba(255,48,0,1)', # цвет в rgb-формате
size=7 # размер маркера
),
opacity=0.8, # прозрачность
line={'width': 3} # свойства линии - толщина
)
)
# свойства фигуры
fig.update_layout(
height=450, width=700, # размер фигуры
title_text='Простой график функций', # заголовок графика
title_font_size=16, # размер заголовка
plot_bgcolor='rgba(0,0,0,0.05)', # цвет фона
)
# параметры оси абсцисс
fig.update_xaxes(
range=[-1.5, 1.5], # ограничение графика
zeroline=True, # рисовать линию x=0
zerolinewidth=2 # толщина линии x=0
)
# параметры оси ординат
fig.update_yaxes(
zeroline=True, # рисовать линию y=0
zerolinewidth=2, # толщина линии y=0
zerolinecolor='LightGray' # цвет линии y=0
)
# показать график
fig.show()
График интерактивный:
- при наведении мышкой на линию появляется окошко, в которой указаны координаты точки и имя линии;
- с помощью выделения можно указать прямоугольную область, которую нужно приблизить;
- вернуть исходный масштаб можно двойныл щелчком или кнопкой на панели сверху;
- кликами по легенде можно убирать линии с графика.
Следующей функцией можно сохранить фигуру в html-формате.
plotly.offline.plot(fig, filename='example.html', auto_open=False)
Замечание. На сайте Plotly содержится полная документация со списом всех возможных параметров. Если параметр принимает в качестве значения словарь других параметров, то в документации приводится также список этих параметров. Ссылки на документацию для конкретных функций:
Капитализация Apple¶
Загрузим данные с информацией о капитализации компании Apple в зависимости от месяца.
df = pd.read_csv(
'https://raw.githubusercontent.com/'\
'plotly/datasets/master/finance-charts-apple.csv'
)
df.head()
Построим график по этим данным, к которому добавим также слайдер, предоставляющий возможность возможность выделять интересующий диапазон дат.
fig = go.Figure()
# две кривые
fig.add_trace(go.Scatter(x=df.Date, y=df['AAPL.High'], name="AAPL High"))
fig.add_trace(go.Scatter(x=df.Date, y=df['AAPL.Low'], name="AAPL Low"))
# свойства графика
fig.update_layout(
title_text='Финансы компании "Apple"',
title_font_size=20,
yaxis_title='Стоимость',
xaxis_rangeslider_visible=True # слайдер
)
fig.show()
2. Несколько графиков¶
Сгенерируем 5 выборок размера 200 и посчитаем по каждой из них кумулятивные суммы
x = sps.norm.rvs(size=(5, 200)).cumsum(axis=1)
Визуализируем каждую кривую на своем графике. Мы также свяжем между собой горизонтальные координатные оси. Это свойство позволяет одновременно масштабировать каждый график, что полезно в случае если величины разные по смыслу.
# объявляем сетку графиков
fig = plotly.subplots.make_subplots(
rows=5, cols=1, # 5 строк и 1 столбец
shared_xaxes=True, # горизонтальные оси связаны при изменении масштаба
vertical_spacing=0.02 # расстояние между графиками
)
# рисуем каждый график отдельно
for i in range(5):
fig.add_trace(
go.Scatter(
x=np.arange(200), y=x[i]
),
i+1, 1 # расположение графика в сетке, нумерация с 1
)
fig.show()
data = pd.read_csv('006_Et_M_CO_M', header=None)
data.columns = ['Time (s)', 'Temperature (oC)', 'Relative Humidity (%)',
'TGS2600', 'TGS2602', 'TGS2602', 'TGS2620',
'TGS2612', 'TGS2620', 'TGS2611', 'TGS2610']
data.head()
Определим какое-то интерпретируемое время
time = pd.Timestamp('2021-04-03 16:30:00') + data.iloc[:, 0] * pd.Timedelta('1 sec')
Нарисуем график в три ряда, в которых на первые два нанесем температуру и относительную влажность, а на последний — показания всех датчиков. В данном случае показания датчиков имеют одинаковый смысл и одинаковую размерность, поэтому их можно наносить на один график, а температура и влажность имеют свою размерность, поэтому их необходимо рисовать на отдельном графике. Поскольку все показания датчиков на одном графике, то для удобства анализа стоит этот график сделать шире.
# объявляем сетку графиков
fig = plotly.subplots.make_subplots(
rows=3, cols=1, # 3 строки и 1 столбец
shared_xaxes=True, # горизонтальные оси связаны при изменении масштаба
vertical_spacing=0.1, # расстояние между графиками
row_heights=[0.25, 0.25, 0.5], # высота каждой строки
subplot_titles=['Температура', 'Относительная влажность', 'Показания датчиков']
)
# визуализируем температуру и влажность
for i in [1, 2]:
fig.add_trace(
go.Scatter(
x=time, y=data.iloc[:, i],
name=data.columns[i],
),
i, 1
)
# визуализируем показания датчиков
for i in range(8):
fig.add_trace(
go.Scatter(
x=time, y=data.iloc[:, 3+i],
name=data.columns[3+i]
),
3, 1
)
fig.show()
Подробнее о make_subplots
можно почитать в документации.
cities = [
'CIBIEJNG', 'CUHAVANA', 'DLMUNICH', 'ERDUBAI',
'FRPARIS', 'JPTOKYO', 'RSMOSCOW', 'TUISTNBL'
]
df = pd.DataFrame()
# считываем отдельно каждый город
for city in cities:
# столбцы отличаются переменным числом пробелов, что парсится с помощью delim_whitespace
data = pd.read_csv(city + '.txt', delim_whitespace=True, header=None)
# соединим даты
time = pd.to_datetime(data[2].astype(str) + '.' + data[0].astype(str) + '.' + data[1].astype(str))
# образуем временной ряд
time_series = pd.Series(data[3].values, index=time)
# удалим дубли по моментам времени
time_series = time_series[~time_series.index.duplicated()]
# добавим в общую таблицу
df[city[2:]] = time_series
# значение менее -50 по Фаренгейту некорректные
df[df < -50] = np.nan
# переведем в Цельсии
df = (df - 32) * 5/9
# отсортируем моменты времени
df = df.sort_index()
df.head()
Визуализируем все кривые на одном графике
fig = go.Figure()
# добавляем каждый город
for city in df.columns:
fig.add_trace(
go.Scatter(
mode='lines+markers', # линии и точки
x=df.index,
y=df[city],
marker=dict(size=5),
name=city
)
)
# Добавление подписей для графика
fig.update_layout(
title_text="Средняя температура днем",
title_font_size=20,
xaxis_title="Дата",
yaxis_title="Температура, °C",
)
# Отображение графика
fig.show()
# Вывод ячейки выше настолько все тормозит, что принято решение его убрать.
Интерактив в этом графике очень сильно тормозит. Более того, тормозит даже сам ноутбук. Это произошло из-за того, что данных много и мы рисуем не только линии, но и сами точки.
Можно, конечно, попробовать как-то упростить, но не всегда это поможет. Вместо этого лучше заменить все Scatter
на Scattergl
, не меняя больше ничего.
fig = go.Figure()
# добавляем каждый город
for city in df.columns:
fig.add_trace(
# используем другой объект для отрисовки
go.Scattergl(
mode='lines+markers', # линии и точки
x=df.index,
y=df[city],
marker=dict(size=5),
name=city
)
)
# Добавление подписей для графика
fig.update_layout(
title_text="Средняя температура днем",
title_font_size=20,
xaxis_title="Дата",
yaxis_title="Температура, °C",
)
# Отображение графика
fig.show()
Теперь все работает очень быстро. Причина заключается в том, что Scattergl
использует инструмент WebGL — кроссплатформенный API для 3D-графики в браузере
Подробнее о Scattergl
можно почитать в документации.
4. Plotly Express¶
Вместо обычного Plotly можно использовать Plotly Express — набора шаблонов для быстрой и красивой визуализации, подобно тому как seaborn по отношению к matplotlib.
Рассмотрим встроенные в Plotly данные Gapminder Foundation — просветительской Шведской организации, которая анализирует финансовое неравенство в мире.
df = px.data.gapminder()
df.head()
Следующая функция строит интерактивный график, где по одной оси отложен ВВП, по другой — прогноз средней продолжительности жизни. Размер каждой точки на графике пропорционален размеру государства.
На графике присутствует слайдер, с помощью которого можно выбирать конкретный год.
px.scatter(
df, # таблица с данными
x="gdpPercap", y="lifeExp", # названия колонок, используемых как координата
animation_frame="year", # колонка, определяющая слайдер
animation_group="country", # колонка, отвечающая за объект (страну)
size="pop", # колонка, отвечающая за размер точек
color="continent", # колонка, отвечающая за цвет точек
hover_name="country", # колонка, отвечающая за подпись информации о точке
log_x=True, # логарифмический масштаб
size_max=55, # максимальный размер круга
range_x=[100, 100000], range_y=[25, 90] # пределы графика
)
Подробнее о plotly.express.scatter
можно почитать в документации.
5. Heatmap¶
Представим, что у нас есть 7 программистов, каждый из которых делает коммиты в гит. Сгенерируем такие данные.
programmers = [
'Дмитрий', 'Вячеслав', 'Надежда', 'Егор', 'Никита', 'Елизавета', 'София'
]
# сегодняшняя дата
base = datetime.datetime.today()
# диапазон дат
dates = base - pd.timedelta_range(start=0, freq='1D', periods=60)
# генерируем количество за день
z = sps.poisson(mu=1).rvs(size=(len(programmers), len(dates)))
Так с помощью Plotly можно проанализировать историю коммитов членов команды за два месяца работы.
fig = go.Figure()
fig.add_trace(
go.Heatmap(
z=z, # данные
x=dates, # имена столбцов
y=programmers, # имена строк
colorscale='Viridis' # цветовая схема
)
)
fig.update_layout(
title='Коммиты на GitHub по дням',
height=350,
xaxis_nticks=20
)
fig.show()
Подробнее о Heatmap
можно почитать в документации.
6. Картографические данные¶
Загрузим данные о землетрясениях, в которых указаны дата, координата и магнитуда.
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/earthquakes-23k.csv')
df.head()
Визуализируем
fig = px.density_mapbox(
df, # таблица с данными
lat='Latitude', lon='Longitude', # колонки с координатами
z='Magnitude', # колонки со значением исследуемой величины
radius=10, # радиус влияния каждой точки
center=dict(lat=0, lon=180), # центр карты
zoom=0, # масштаб карты
mapbox_style="stamen-terrain" # стиль карты
)
fig.show()
Подробнее о plotly.express.density_mapbox
можно почитать в документации.
Галерея¶
С более продвинутыми визуализациями можно ознакомиться в официальной галерее.