Eixo Secundário #

Às vezes, queremos um eixo secundário em um gráfico, por exemplo, para converter radianos em graus no mesmo gráfico. Podemos fazer isso criando eixos filhos com apenas um eixo visível por meio axes.Axes.secondary_xaxisde e axes.Axes.secondary_yaxis. Esse eixo secundário pode ter uma escala diferente do eixo principal, fornecendo uma função de conversão direta e inversa em uma tupla para o argumento de palavra-chave de funções :

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

fig, ax = plt.subplots(constrained_layout=True)
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')


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]')
plt.show()
onda senoidal

Aqui está o caso da conversão de número de onda para comprimento de onda em uma escala log-log.

Observação

Nesse caso, a escala x do pai é logarítmica, portanto, o filho também é logarítmico.

fig, ax = plt.subplots(constrained_layout=True)
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')


def one_over(x):
    """Vectorized 1/x, treating x==0 manually"""
    x = np.array(x).astype(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]')
plt.show()
espectro aleatório

Às vezes, queremos relacionar os eixos em uma transformação que é ad hoc dos dados e é derivada empiricamente. Nesse caso, podemos definir as funções de transformação direta e inversa como interpolações lineares de um conjunto de dados para o outro.

Observação

Para lidar adequadamente com as margens de dados, as funções de mapeamento ( forwarde inverseneste exemplo) precisam ser definidas além dos limites nominais do gráfico.

No caso específico da interpolação linear numpy, numpy.interp, essa condição pode ser aplicada arbitrariamente fornecendo argumentos de palavra-chave opcionais left , right de modo que os valores fora do intervalo de dados sejam mapeados bem fora dos limites do gráfico.

fig, ax = plt.subplots(constrained_layout=True)
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)
# fake data set relating x coordinate to another data-derived coordinate.
# xnew must be monotonic, so we sort...
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}$')

plt.show()
eixo secundário

Um exemplo final converte np.datetime64 em dia do ano no eixo x e de Celsius para Fahrenheit no eixo y. Observe a adição de um terceiro eixo y e que ele pode ser colocado usando um ponto flutuante para o argumento de localização

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(constrained_layout=True)

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


def date2yday(x):
    """Convert matplotlib datenum to days since 2018-01-01."""
    y = x - mdates.date2num(datetime.datetime(2018, 1, 1))
    return y


def yday2date(x):
    """Return a matplotlib datenum for *x* days after 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))


# use of a float for the position:
secax_y2 = ax.secondary_yaxis(
    1.2, functions=(celsius_to_anomaly, anomaly_to_celsius))
secax_y2.set_ylabel(r'$T - \overline{T}\ [^oC]$')


plt.show()
eixo secundário

Referências

O uso das seguintes funções, métodos, classes e módulos é mostrado neste exemplo:

Tempo total de execução do script: ( 0 minutos 4,894 segundos)

Galeria gerada por Sphinx-Gallery