Jak ustawić skalę tick jako potęgę 2 w matplotlib? [duplikować]

Dec 16 2020

Chcę wykreślić liczbę, której wartości xtick rosną z potęgą 2.

Na przykład,

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

To, czego oczekuję, to taka postać,

Aby uzyskać uporządkowane dane, takie jak powyższy przykład, mogę po prostu utworzyć kolumnę pomocniczą x21 ~ 6 w tej ramce danych, a następnie ustawić xticklabels na rysunku na 2 ^ 1 ~ 2 ^ 6. Jednak to obejście nie ma zastosowania do sytuacji z innymi wartościami, takimi jak 3, 7 lub 30.

Wygląda na to, że matplotlib obsługuje tylko skalę logów. Jak mogę osiągnąć tik ze skalą mocy 2?

Odpowiedzi

max Dec 16 2020 at 17:20

To szczególnie podchwytliwe pytanie (którego nie spodziewałem się ^^).

OK, zacznijmy od kilku wskazówek do czytania: Chcesz ustawić skalę x / y: .matplotlib.axes.Axes.set_yscale () . Chociaż istnieje kilka standardowych skal (domyślna jest oczywiście 'liner', można ustawić niestandardową skalę. Oto kilka fajnych przykładów.

Zasadniczo definiujesz dwie funkcje za pomocą transformacji w przód i jej odwrotności . Następnie musisz poprawnie ustawić tiki (ponieważ zastosujesz transformację po wykreśleniu, tiki pozostają takie same (ale nie w tej samej pozycji z powodu transformacji). Jedna ma dwie opcje:

  • ręczne ustawianie znaczników matplotlib.axes.Axes.set_xticks()lub
  • poprzez ustawienie lokalizator osi: matplotlib.axes.Axes.xaxis.set_major_locator(). Jest to zalecane, jeśli używasz siatek. Ale ponieważ moja wiedza jest ograniczona, doceniam bardziej szczegółowe wyjaśnienie (ponieważ teraz jestem również ciekawy tej funkcji ^^)

A teraz najtrudniejsza część: formatowanie etykiet znaczników, aby reprezentowały plik '2^x'. Nie wpadłem na lepszy pomysł niż jawne ustawienie ich jako ciągów. Wydaje się, że można zmienić ogólny format tylko w ograniczonych granicach, zobacz matplotlib.pyplot.ticklabel_format(), gdzie można wybrać, czy i kiedy należy użyć notacji naukowej (tj. Wyświetlając a '10^x'w prawym dolnym rogu). Daj mi znać, jeśli istnieje bardziej ogólne rozwiązanie tego problemu.

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()