Como definir a escala de escala como a potência de 2 em matplotlib? [duplicado]

Dec 16 2020

Quero traçar uma figura cujos valores de xtick crescem na potência de 2.

Por exemplo,

import pandas as pd
data = pd.DataFrame({
    'x': [2, 4, 8, 16, 32, 64],
    'y': [1, 2, 3, 4, 5, 6]
})

O que eu espero é uma figura como esta,

Para dados organizados como o exemplo acima, posso apenas criar uma coluna auxiliar x2de 1 ~ 6 neste dataframe e definir os xticklabels da figura como 2 ^ 1 ~ 2 ^ 6. No entanto, esta solução alternativa não se aplica a situações com outros valores como 3, 7 ou 30.

Parece que matplotlib suporta apenas uma escala logarítmica. Como posso atingir um tique com uma escala de potência de 2?

Respostas

max Dec 16 2020 at 17:20

Esta é uma pergunta particularmente capciosa (que eu não esperava que fosse ^^).

OK, vamos começar com algumas dicas para leitura: Você deseja definir a escala x / y: .matplotlib.axes.Axes.set_yscale () . Embora existam algumas escalas padrão (o padrão é obviamente 'liner', pode-se definir uma escala personalizada. Aqui estão alguns bons exemplos.

Basicamente, você define duas funções com a transformação direta e com o inverso dela. Depois, você precisa definir os ticks corretamente (porque você aplica a transformação após a plotagem, os ticks permanecem os mesmos (mas não na mesma posição devido à transformação). Um tem duas opções para isso:

  • definir os carrapatos manualmente matplotlib.axes.Axes.set_xticks(), ou
  • definindo o localizador do eixo: matplotlib.axes.Axes.xaxis.set_major_locator(). Isso é recomendado se você usar grades. Mas como meu conhecimento é limitado, agradeço uma explicação mais detalhada (porque agora também estou curioso sobre este recurso ^^)

E agora vem a parte complicada: formatar os rótulos de escala para representar um '2^x'. Não tive uma ideia melhor do que defini-los explicitamente como strings. Parece que só se pode mudar o formato geral dentro de limites restritos, veja matplotlib.pyplot.ticklabel_format(), onde se pode escolher se e quando uma notação científica deve ser usada (ou seja, exibindo um '10^x'no canto inferior direito). Deixe-me saber se existe uma solução mais genérica para isso.

import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.ticker import FixedLocator

# create dummy data
df = pd.DataFrame({
    'x': [2**x for x in range(1,10)],
    'y': list(range(1,10))
})

def forward(x):
    return np.log2(x)


def inverse(x):
    return 2**x

# open figure
fig, ax = plt.subplots(2,2)
axs = ax.flatten()
for i in range(0,4):
    # plot data
    axs[i].plot(df['x'],df['y'])
    if i > 0:
        # set scale function
        axs[i].set_xscale('function', functions=(forward,inverse))
    if i > 1:
        # set ticks
        # - OPTION 1
        axs[i].set_xticks(df['x'])
        # - OPTION 2
      axs[i].xaxis.set_major_locator(FixedLocator(2**np.arange(1,10)))
    if i > 2:
        # est tick labels
        axs[i].set_xticklabels( [f"2^{j:.0f}" for j in np.log2(df['x'])] )

plt.show()