Дополнительная ось в Matplotlib

PythonPythonBeginner
Практиковаться сейчас

This tutorial is from open-source community. Access the source code

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

Matplotlib - это популярная библиотека визуализации данных на Python. Иногда нам нужно построить два различных масштаба данных на одной графике, именно в этом случае приходится использовать концепцию вторичной оси. В этом практическом занятии мы научимся создавать вторичную ось в Matplotlib.

Советы по работе с ВМ

После запуска виртуальной машины кликните в верхнем левом углу, чтобы переключиться на вкладку Notebook и приступить к практике с использованием Jupyter Notebook.

Иногда вам может потребоваться подождать несколько секунд, пока Jupyter Notebook полностью загрузится. Проверка операций не может быть автоматизирована из-за ограничений Jupyter Notebook.

Если вы столкнетесь с проблемами во время обучения, не стесняйтесь обращаться к Labby. Оставьте отзыв после занятия, и мы оперативно решим проблему для вас.

Импортируем необходимые библиотеки

Начнём с импорта необходимых библиотек, а именно matplotlib, numpy и datetime.

import datetime
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.dates as mdates
from matplotlib.ticker import AutoMinorLocator

Построим график данных

Создадим простой синусоидальный сигнал, чтобы продемонстрировать применение вторичной оси. Построим синусоидальный сигнал, используя градусы в качестве оси x.

fig, ax = plt.subplots(layout='constrained')
x = np.arange(0, 360, 1)
y = np.sin(2 * x * np.pi / 180)
ax.plot(x, y)
ax.set_xlabel('angle [degrees]')
ax.set_ylabel('signal')
ax.set_title('Sine wave')

Создадим вторичную ось

Теперь создадим вторичную ось и преобразуем оси x из градусов в радианы. В качестве прямой функции будем использовать deg2rad, а в качестве обратной - rad2deg.

def deg2rad(x):
    return x * np.pi / 180

def rad2deg(x):
    return x * 180 / np.pi

secax = ax.secondary_xaxis('top', functions=(deg2rad, rad2deg))
secax.set_xlabel('angle [rad]')

Построим еще один пример

Теперь построим еще один пример преобразования от волнового числа к длине волны в логарифмическом масштабе. Для этого примера будем использовать случайный спектр.

fig, ax = plt.subplots(layout='constrained')
x = np.arange(0.02, 1, 0.02)
np.random.seed(19680801)
y = np.random.randn(len(x)) ** 2
ax.loglog(x, y)
ax.set_xlabel('f [Hz]')
ax.set_ylabel('PSD')
ax.set_title('Random spectrum')

Создадим вторичную ось x

Создадим вторичную ось x и преобразуем частоту в период. В качестве прямой функции будем использовать one_over, а в качестве обратной - inverse.

def one_over(x):
    """Vectorized 1/x, treating x==0 manually"""
    x = np.array(x, float)
    near_zero = np.isclose(x, 0)
    x[near_zero] = np.inf
    x[~near_zero] = 1 / x[~near_zero]
    return x

## the function "1/x" is its own inverse
inverse = one_over

secax = ax.secondary_xaxis('top', functions=(one_over, inverse))
secax.set_xlabel('period [s]')

Создадим вторичную ось y

Создадим третий пример связывания осей в трансформации, которая является эмпирически полученной из данных и специфичной для них. В этом случае мы зададим функции прямых и обратных преобразований в виде линейной интерполяции из одного набора данных в другой.

fig, ax = plt.subplots(layout='constrained')
xdata = np.arange(1, 11, 0.4)
ydata = np.random.randn(len(xdata))
ax.plot(xdata, ydata, label='Plotted data')

xold = np.arange(0, 11, 0.2)
## фальшивый набор данных, связывающий координату x с другой координатой, полученной из данных.
## xnew должен быть монотонным, поэтому мы сортируем...
xnew = np.sort(10 * np.exp(-xold / 4) + np.random.randn(len(xold)) / 3)

ax.plot(xold[3:], xnew[3:], label='Transform data')
ax.set_xlabel('X [m]')
ax.legend()

def forward(x):
    return np.interp(x, xold, xnew)

def inverse(x):
    return np.interp(x, xnew, xold)

secax = ax.secondary_xaxis('top', functions=(forward, inverse))
secax.xaxis.set_minor_locator(AutoMinorLocator())
secax.set_xlabel('$X_{other}$')

Создадим несколько осей

Теперь создадим последний пример, в котором на оси x преобразуем np.datetime64 в номер дня года, а на оси y - температуру из Цельсия в Фаренгейт. Также добавим третью ось y и расположим ее с использованием вещественного числа в качестве аргумента для позиции.

dates = [datetime.datetime(2018, 1, 1) + datetime.timedelta(hours=k * 6)
         for k in range(240)]
temperature = np.random.randn(len(dates)) * 4 + 6.7
fig, ax = plt.subplots(layout='constrained')

ax.plot(dates, temperature)
ax.set_ylabel(r'$T\ [^oC]$')
plt.xticks(rotation=70)

def date2yday(x):
    """Преобразует matplotlib datenum в дни с 2018-01-01."""
    y = x - mdates.date2num(datetime.datetime(2018, 1, 1))
    return y

def yday2date(x):
    """Возвращает matplotlib datenum для *x* дней после 2018-01-01."""
    y = x + mdates.date2num(datetime.datetime(2018, 1, 1))
    return y

secax_x = ax.secondary_xaxis('top', functions=(date2yday, yday2date))
secax_x.set_xlabel('yday [2018]')

def celsius_to_fahrenheit(x):
    return x * 1.8 + 32

def fahrenheit_to_celsius(x):
    return (x - 32) / 1.8

secax_y = ax.secondary_yaxis(
    'right', functions=(celsius_to_fahrenheit, fahrenheit_to_celsius))
secax_y.set_ylabel(r'$T\ [^oF]$')

def celsius_to_anomaly(x):
    return (x - np.mean(temperature))

def anomaly_to_celsius(x):
    return (x + np.mean(temperature))

## использование вещественного числа для позиции:
secax_y2 = ax.secondary_yaxis(
    1.2, functions=(celsius_to_anomaly, anomaly_to_celsius))
secax_y2.set_ylabel(r'$T - \overline{T}\ [^oC]$')

Резюме

В этом практическом занятии мы узнали, как создавать вторичную ось в Matplotlib. Мы использовали различные примеры, чтобы продемонстрировать концепцию вторичной оси и способы ее создания.