Занятие 7
Библиотека matplotlib¶
Matplotlib — библиотека на языке программирования Python для визуализации данных двумерной и трёхмерной графикой.
Основой для Matplotlib послужил MATLAB, но в отличие от него Matplotlib является более гибким, легко конфигурируемым пакетом.
Библиотека поддерживает многие виды графиков и диаграмм:
- Графики (англ. line plot)
- Диаграммы рассеяния (англ. scatter plot)
- Столбчатые диаграммы (англ. bar chart) и гистограммы (англ. histogram)
- Круговые диаграммы (англ. pie chart)
- Диаграммы стебель-листья (англ. stem plot)
- Контурные графики (англ. contour plot)
- Поля градиентов (англ. quiver)
- Спектральные диаграммы (англ. spectrogram)
Установить бибилиотеку можно через команду:
!pip install matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
%matplotlib inline
Линейные графики¶
y = [1, 2, 3, 5, 8, 13, 21]
plt.plot(y)
plt.title('График зависимой переменной y')
plt.xlabel('x')
plt.ylabel('y')
plt.show()
Для построения линейных графиков используется функция plot():
```plot([x], y, [fmt], *, data=None, **kwargs)
plot([x], y, [fmt], [x2], y2, [fmt2], …, **kwargs)```
Рассмотрим аргументы функции plot():
- x, x2, …: array - набор данных для оси абсцисс первого, второго и т.д. графика.
- y, y2, …: array - набор данных для оси ординат первого, второго и т.д. графика.
- fmt: str - формат графика, задается в виде строки: ‘[marker][line][color]’.
- **kwargs – свойства класса Line2D, которые предоставляют доступ к большому количеству настроек внешнего вида графика/
Подробнее можно посмотреть в документации: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html
x = [1, 5, 10, 15, 20]
y1 = [1, 7, 3, 5, 11]
y2 = [4, 3, 1, 8, 12]
y3 = [12, 5, 10, 6, 11]
plt.figure(figsize=(12, 7))
plt.plot(x, y1, 'X-y', alpha=0.4, label="first", lw=6, mec='b', mew=2, ms=10)
plt.plot(x, y2, 'v-.g', label="second", mec='r', lw=2, mew=2, ms=12)
plt.plot(x, y3, 'o--g', label="third", mec='g', lw=2, mew=2, ms=10)
plt.legend()
plt.grid(True)
x = [1, 6, 3, 9, 4, 16, 2]
# y's copied from above for easier reading
y = [1, 2, 3, 5, 8, 13, 21]
plt.plot(x, y);
plt.plot(x, y, 'go--');
plt.plot(x, y, 'rx--');
Другой способ задать точно такой же график:
plt.plot(x, y, color='red', marker='x', linestyle='dashed');
import numpy as np
# y = x^2 in the range of -10 to 10
x = np.linspace(-10, 10, 100)
y = x**2
plt.plot(x, y, color='#c26603', marker='*', markersize=16, markeredgecolor='green',
markerfacecolor='#3333fc', linewidth=1, linestyle=':', alpha=0.8, markevery=10);
Графики рассеяния¶
x = np.array([5,7,8,7,2,17,2,9,4,11,12,9,6])
y = np.array([99,86,87,88,111,86,103,87,94,78,77,85,86])
plt.scatter(x, y)
plt.show()
Теперь отобразим на графики две группы точек различным цветом.
x = np.array([5,7,8,7,2,17,2,9,4,11,12,9,6])
y = np.array([99,86,87,88,111,86,103,87,94,78,77,85,86])
plt.scatter(x, y)
x = np.array([2,2,8,1,15,8,12,9,7,3,11,4,7,14,12])
y = np.array([100,105,84,105,90,99,90,95,94,100,79,112,91,80,85])
plt.scatter(x, y)
plt.show()
Можно менять размер маркеров с помощью которых отображаются значения в зависимости от значения.
price = np.asarray([2.50, 1.23, 4.02, 3.25, 5.00, 4.40])
sales_per_day = np.asarray([34, 62, 49, 22, 13, 19])
profit_margin = np.asarray([20, 35, 40, 20, 27.5, 15])
plt.scatter(x=price, y=sales_per_day, s=profit_margin * 10)
plt.show()
Можно разделить точки на несколько групп каждую показать своим цветом.
low = (0, 1, 0)
medium = (1, 1, 0)
high = (1, 0, 0)
sugar_content = [low, high, medium, medium, high, low]
plt.scatter(
x=price,
y=sales_per_day,
s=profit_margin * 10,
c=sugar_content,
)
plt.show()
Круговые диаграммы¶
Подходят для отображения части целого, т. е. какую долю от общего числа составляет какая-то/какие-то из категорий.
plt.figure(figsize=(6, 6))
plt.pie([4, 1, 6, 9], explode=[0, 0.2, 0, 0], labels=["Cherry Pie", "Apple Pie", "Blueberry Pie", "Rhubarb Pie"],
colors=["red", "green", "blue", "orange"], startangle=45, textprops={'fontsize': 20});
Круговые диаграммы¶
Подходят для отображения части целого, т. е. какую долю от общего числа составляет какая-то/какие-то из категорий.
data = [23, 45, 56, 78, 213]
plt.bar([1,2,3,4,5], data)
plt.show()
Добавим горизонтальные линии для удобства считывания значений.
data = [23, 45, 56, 78, 213]
plt.bar(range(len(data)), data, color='royalblue', alpha=0.7)
plt.grid(color='#95a5a6', linestyle='--', linewidth=2, axis='y', alpha=0.7)
plt.show()
Несколько столбчатых диаграмм можно построить следующим образом:
data1 = [23,85, 72, 43, 52]
data2 = [42, 35, 21, 16, 9]
width =0.3
plt.bar(np.arange(len(data1)), data1, width=width)
plt.bar(np.arange(len(data2))+ width, data2, width=width)
plt.show()
data1 = [23,85, 72, 43, 52]
data2 = [42, 35, 21, 16, 9]
plt.bar(range(len(data1)), data1)
plt.bar(range(len(data2)), data2, bottom=data1)
plt.show()
Примеры различных типов графиков можно посмотреть в gallery.
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.lines import Line2D
from IPython.core.display import display
# Create a new figure
fig = Figure(figsize=(5, 8))
# Attach a canvas to the figure
FigureCanvas(fig)
# Add a subplot (i.e., an Axes object)
ax = fig.add_subplot(1, 1, 1)
# Create some lines and add them to the plot.
# We could do this more easily with pyplot's .plot(),
# but this is an exercise in masochism.
# intercepts and slopes for the three data series
lines = [(2, -0.8), (1, 0.3), (-1, 1.2)]
# Other visual parameters that vary by line
colors = ['navy', 'g', 'k']
markers = ['*', 'o', 'v']
labels = ['blue', 'green', 'black']
# Loop over data series, construct Line2D for each one,
# and add it to the subplot
for i in range(3):
intercept, slope = lines[i]
# Create some data
x = np.random.normal(size=30)
y = x * slope + np.random.normal(intercept, 0.6, size=30)
# Make the line
line = Line2D(x, y, marker=markers[i], linestyle='', color=colors[i],
markersize=12, fillstyle='none', markeredgewidth=1.5,
label=labels[i])
# Add line to plot
ax.add_line(line)
# Set a gray background
ax.set_facecolor('#eeeef2')
# Set up grid lines
ax.grid(which='both')
major_ticks = np.arange(-10, 10, 1)
minor_ticks = np.arange(-10, 10, 0.2)
ax.set_xticks(major_ticks)
ax.set_xticks(minor_ticks, minor=True)
ax.set_yticks(major_ticks)
ax.set_yticks(minor_ticks, minor=True)
ax.grid(which='minor', alpha=0.7, color='#ddddff')
ax.grid(which='major', alpha=0.9)
# Pick sane x and y-axis limits
ax.set(xlim=(-3, 3), ylim=(-4, 4))
# title and axis labels
ax.set_title('Distribution of colored shapes', fontsize=22,
fontname='Arial')
ax.set_xlabel('X', fontsize=18, labelpad=10)
ax.set_ylabel('Y', fontsize=18)
# Add legend in lower right with semi-transparent frame
ax.legend(loc='lower right', fontsize=14, framealpha=0.5, edgecolor='k')
# Show the plot in the notebook
display(fig)
Аннотация графиков¶
x = np.linspace(-10, 10, 100)
y = x**2
# Basic plot...
plt.plot(x, y, color='#c26603', marker='*', markersize=16, markeredgecolor='black',
markerfacecolor='#3333fc', linewidth=3, linestyle=':', alpha=0.8, markevery=10)
### Customize the plot ###
# Change the aspect
plt.gcf().set_size_inches((8, 4))
# # Set title and axis labels
plt.title("Matplotlib impressions", fontsize=20, fontname="Comic Sans MS")
plt.xlabel("Time spent working in matplotlib", fontsize=16, labelpad=15)
plt.ylabel("Desire to work in matplotlib", fontsize=16, labelpad=15)
# # # Replace tick labels with something more... descriptive
plt.xticks([-9.5, 10], ['newbie', 'master'], fontsize=12)
plt.yticks([18, 110], ['PLZ NO MOAR', 'Mmmm'], rotation=90, ha='right', fontsize=12)
# # # Hide ticks
plt.gca().tick_params(axis=u'both', which=u'both',length=0)
# # # Annotate the second star
plt.annotate("a lucky blue star!", (x[10] + 0.5, y[10]), xytext=(x[20] + 3, y[20] + 40),
arrowprops=dict(facecolor='black', width=2), fontsize=14);
iris = sns.load_dataset('iris')
iris.head(5)
# Set up the figure and axes--a 4 x 3 grid.
# We share both the x and y axes so it's easy to compare values.
fig, axes = plt.subplots(4, 3, figsize=(6, 6))
# We'll plot each species of iris in a different column
species = ['setosa', 'versicolor', 'virginica']
# ..and a histogram of each attribute in a separate row
attrs = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
for i in range(3):
sp = species[i]
sp_data = iris.query('species == @sp')
# Show column names
axes[0, i].set_title(sp, fontsize="16")
for j in range(4):
attr = attrs[j]
values = sp_data[attr]
# plot separately on each Axes
axes[j, i].hist(values)
# Only plot y-axis label for first column
if i == 0:
axes[j, i].set_ylabel(attr, fontsize=12)
# A fairly magical layout manager that tends to clean up
# figures well and prevent overlap between elements.
plt.tight_layout()
Pandas¶
# KDE plot of all iris attributes, collapsing over species
iris.plot();
iris.plot(kind='kde');
# Separate boxplot of iris attributes for each species
iris.boxplot(by='species', figsize=(16, 4), layout=(1, 4));
fig, axes = plt.subplots(1, 4, figsize=(16, 3.5), sharey=False)
subplots = iris.boxplot(by='species', ax=axes, return_type='both', notch=True,
bootstrap=10000, patch_artist=True, fontsize=12);
varnames = ['Petal length', 'Petal width', 'Sepal length', 'Sepal width']
ylabels = ["length (cm)", "width (cm)", "length (cm)", "width (cm)"]
colors = ['#ffddff', '#ddffdd', '#ddffff', '#ffdddd']
# Stuff we need to do separately for each subplot
for i, sp in enumerate(subplots):
# We asked for both the Axes and the boxplot's dict
ax, box = sp
# Embiggen title
ax.set_title(varnames[i], fontsize=16)
# Only show gridlines along y
ax.grid(axis='x')
# y-axis label
ax.set_ylabel(ylabels[i], fontsize=16)
# background
ax.set_facecolor('#eeeef2')
# Hide xlabel
ax.set_xlabel('')
# Set all boxes in the current subplot to the same color
for patch in box['boxes']:
patch.set(facecolor=colors[i], edgecolor='k', linewidth=1)
for whisk in box['whiskers']:
whisk.set(color='k')
# Change figure-level options
fig = plt.gcf()
fig.suptitle('Iris attributes by species', size=22, y=1.1, fontname='Arial')
# Fix spacing issues
plt.subplots_adjust(wspace=0.3)
Seaborn¶
sns.set_style('darkgrid')
sns.boxplot(data=iris)
fig, axes = plt.subplots(1, 4, figsize=(16, 4), sharey=False)
# Explicitly list the variables to map onto subplots
variables = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
# For each of the variables, use a different subplot
for i, var in enumerate(variables):
species_data = iris[[var, 'species']]
sns.boxplot(x='species', y=var, data=species_data, ax=axes[i], palette='Set2',
notch=True, bootstrap=10000)
# seaborn expects data in tidy, long format, so we need
# to first "melt" our dataset so that each row is a single
# observation/variable combination.
iris_melted = iris.melt('species')
# Create the FacetGrid, indicating that we want a separate
# column for each variable.
g = sns.FacetGrid(iris_melted, col='variable', sharey=False, )
# Apply the boxplot plotting function to each cell of the FacetGrid.
# Here, the first argument gives the plotting function, and subsequent
# arguments are passed through to the plotting function. I.e.,
g.map_dataframe(sns.boxplot, x='species', y='value', hue='species', notch=True, bootstrap=10000);
# Note: if working in JupyterLab, replace with %matplotlib widget
# You may have to wrestle with dependencies to get this to work.
%matplotlib notebook
Ipython widgets¶
x = np.linspace(0, 10, 1000)
y = np.sin(x)
plt.plot(x, y);
def f(freq=1):
x = np.linspace(0, 10, 1000)
y = np.sin(x * freq)
plt.plot(x, y);
Now all we need to do is bind the freq argument in our function f() to a slider in the Jupyter notebook. The interact function makes this trivial:
# Revert to ordinary inline plotting;
# notebook mode will interfere.
%matplotlib inline
from ipywidgets import interact
interact(f, freq=(-10, 10));
Кейс по анализу данных¶
Загрузим данные по планетам и посмотрим какие инсайты можно выявить используя построение графиков.
df = sns.load_dataset('planets')
df.head()
df.sample(10)
df.info()
lst_col = list(df.columns)
for i in lst_col[1:]:
plt.hist(df[i], color='royalblue', alpha=0.7)
plt.title(f'Гистограмма распределения {i}')
plt.grid(color='#95a5a6', linestyle='--', linewidth=2, axis='y', alpha=0.7)
plt.show()
df_year = df.groupby('year')['number'].count()
df_year
df_year.isnull().sum()
plt.plot(df_year, 'o--g');
Проанализируем корреляции признаков. Для этого построим корреляционную матрицу и визуализируем еёё с помощью библиотеки seaborn.
sns.heatmap(df.corr(), annot = True);
Ограничить число выводимых знаков после запятой можно через параметр fmt:
sns.heatmap(df.corr(), annot = True, fmt='.1g')
sns.heatmap(df.corr(), annot = True, vmin=-1, vmax=1, center= 0);
Изменим цвет, используя аргумент cmap
sns.heatmap(df.corr(), annot = True, vmin=-1, vmax=1, center= 0, cmap= 'coolwarm');
matrix = np.triu(df.corr())
sns.heatmap(df.corr(), annot=True, mask=matrix);
mask = np.tril(df.corr())
sns.heatmap(df.corr(), annot=True, mask=mask);