matplotlib: formatuje wartości przesunięcia osi na liczby całkowite lub określoną liczbę

92

Mam figurę matplotlib, na której wykreślam dane, które są zawsze określane jako nanosekundy (1e-9). Na osi Y, jeśli mam dane to dziesiątki nanosekund, tj. 44e-9, wartość na osi pokazuje 4,4 z + 1e-8 jako przesunięcie. Czy jest w ogóle wymuszenie na osi pokazania 44 z przesunięciem + 1e-9?

To samo dotyczy mojej osi X, gdzie oś pokazuje + 5.54478e4, gdzie wolałbym, aby pokazywała przesunięcie +55447 (liczba całkowita, bez dziesiętnego - tutaj wartość jest w dniach).

Wypróbowałem kilka rzeczy takich jak ta:

p = axes.plot(x,y)
p.ticklabel_format(style='plain')

dla osi X, ale to nie działa, chociaż prawdopodobnie używam jej nieprawidłowo lub błędnie interpretuję coś z dokumentacji, czy ktoś może wskazać mi właściwy kierunek?

Dzięki, Jonathan

Ilustracja problemu


Próbowałem zrobić coś z elementami formatującymi, ale nie znalazłem jeszcze żadnego rozwiązania ...:

myyfmt = ScalarFormatter(useOffset=True)
myyfmt._set_offset(1e9)
axes.get_yaxis().set_major_formatter(myyfmt)

i

myxfmt = ScalarFormatter(useOffset=True)
myxfmt.set_portlimits((-9,5))
axes.get_xaxis().set_major_formatter(myxfmt)

Na marginesie, jestem właściwie zdezorientowany co do tego, gdzie faktycznie znajduje się obiekt „numer przesunięcia”… czy jest to część dużych / pomocniczych taktów?

Jonathan
źródło
1
Czy próbowałeś set_units? matplotlib.sourceforge.net/api/… (nie mogę tego wypróbować, ponieważ nie mam tutaj matplotlib.)
Katriel
1
Sprawdziłem funkcję set_units i wydaje się to o wiele bardziej skomplikowane niż to konieczne (trzeba napisać / dodać dodatkowy moduł ?? - basic_units?). Musi istnieć sposób, aby po prostu edytować format zaznaczenia. Funkcja units / set_unit wydaje się być bardziej podobna do konwersji jednostek. Jednak dzięki za wskazówkę, doprowadziło mnie to do innych rozwiązań, na które teraz patrzę!
Jonathan,
1
proszę rozważyć rcParamswyłączenie, jeśli domyślnie wyłączone: rcParams["axes.formatter.useoffset"] = Falsejak tutaj: stackoverflow.com/questions/24171064/…
Ruggero Turra

Odpowiedzi:

100

Miałem dokładnie ten sam problem, a te linie rozwiązały problem:

from matplotlib.ticker import ScalarFormatter

y_formatter = ScalarFormatter(useOffset=False)
ax.yaxis.set_major_formatter(y_formatter)
Gonzalo
źródło
1
To jest szybka i łatwa odpowiedź. Dziękuję Ci.
maxm
6
ax.get_yaxis().get_major_formatter().set_useOffset(False)
Jednowierszowy tekst
3
dla noobów takich jak ja, nie zapomnij from matplotlib.ticker import ScalarFormattero kodzie for @Gonzalo do działania lub po prostu użyj rozwiązania
@Dataman
36

Dużo łatwiejszym rozwiązaniem jest po prostu dostosowanie etykiet kleszczy. Weź ten przykład:

from pylab import *

# Generate some random data...
x = linspace(55478, 55486, 100)
y = random(100) - 0.5
y = cumsum(y)
y -= y.min()
y *= 1e-8

# plot
plot(x,y)

# xticks
locs,labels = xticks()
xticks(locs, map(lambda x: "%g" % x, locs))

# ytikcs
locs,labels = yticks()
yticks(locs, map(lambda x: "%.1f" % x, locs*1e9))
ylabel('microseconds (1E-9)')

show()

tekst alternatywny

Zwróć uwagę, jak w przypadku osi y pomnożyłem wartości, 1e9a następnie wspomniałem o tej stałej w etykiecie y


EDYTOWAĆ

Inną opcją jest sfałszowanie mnożnika wykładnika przez ręczne dodanie jego tekstu na górze wykresu:

locs,labels = yticks()
yticks(locs, map(lambda x: "%.1f" % x, locs*1e9))
text(0.0, 1.01, '1e-9', fontsize=10, transform = gca().transAxes)

EDYCJA2

Możesz również sformatować wartość przesunięcia osi X w ten sam sposób:

locs,labels = xticks()
xticks(locs, map(lambda x: "%g" % x, locs-min(locs)))
text(0.92, -0.07, "+%g" % min(locs), fontsize=10, transform = gca().transAxes)

tekst alternatywny

Amro
źródło
To było dokładnie to, co zrobiłem na początku. Niestety, nie mogłem znaleźć prostego sposobu na ustawienie / pokazanie mnożnika osi (poza jawnym umieszczeniem go na etykiecie osi y, tak jak to zrobiłeś). Jeśli nie masz nic przeciwko temu, aby nie mieć etykiety mnożnika osi, jest to prostszy sposób. Tak czy inaczej, +1 ode mnie.
Joe Kington
1
@Joe Kington: możesz dodać go ręcznie jako tekst ... zobacz edycję powyżej :)
Amro
Świetny! Spróbuję twojego podejścia z etykietami osi X. Wezmę podstawową wartość pierwszej wartości x, a następnie usunę ją z każdej wartości x i dodam „+ minxval” jako etykietę. Nie mogę dowiedzieć się, jak inaczej sformatować przesunięcie x-tick. Nie mam problemu z wielkością przesunięcia, po prostu potrzebuję, aby było wyświetlane jako wartość niewykładnicza.
Jonathan,
Łał. Świetna robota, pokazując, jak naprawdę możesz przejąć kontrolę nad matplotlib i dostosować go do swoich potrzeb oraz naprawdę i trochę pizazz do swoich działek.
physicsmichael
Jak zmienić rozmiar czcionki 1e-9 na rysunku?
oferta nie może odmówić
30

Musisz podklasę, ScalarFormatteraby zrobić to, czego potrzebujesz ... _set_offsetpo prostu dodaje stałą, którą chcesz ustawić ScalarFormatter.orderOfMagnitude. Niestety ręczne ustawienie orderOfMagnitudenic nie da, ponieważ jest resetowane, gdy ScalarFormatterinstancja jest wywoływana w celu sformatowania etykiet znaczników osi. To nie powinno być takie skomplikowane, ale nie mogę znaleźć prostszego sposobu na zrobienie dokładnie tego, co chcesz ... Oto przykład:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter, FormatStrFormatter

class FixedOrderFormatter(ScalarFormatter):
    """Formats axis ticks using scientific notation with a constant order of 
    magnitude"""
    def __init__(self, order_of_mag=0, useOffset=True, useMathText=False):
        self._order_of_mag = order_of_mag
        ScalarFormatter.__init__(self, useOffset=useOffset, 
                                 useMathText=useMathText)
    def _set_orderOfMagnitude(self, range):
        """Over-riding this to avoid having orderOfMagnitude reset elsewhere"""
        self.orderOfMagnitude = self._order_of_mag

# Generate some random data...
x = np.linspace(55478, 55486, 100) 
y = np.random.random(100) - 0.5
y = np.cumsum(y)
y -= y.min()
y *= 1e-8

# Plot the data...
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, 'b-')

# Force the y-axis ticks to use 1e-9 as a base exponent 
ax.yaxis.set_major_formatter(FixedOrderFormatter(-9))

# Make the x-axis ticks formatted to 0 decimal places
ax.xaxis.set_major_formatter(FormatStrFormatter('%0.0f'))
plt.show()

Co daje coś takiego: tekst alternatywny

Natomiast domyślne formatowanie wyglądałoby następująco: tekst alternatywny

Mam nadzieję, że to trochę pomoże!

Edycja: Nie wiem, gdzie znajduje się etykieta przesunięcia ... Byłoby nieco łatwiej po prostu ustawić ją ręcznie, ale nie mogłem wymyślić, jak to zrobić ... Mam wrażenie że musi być łatwiejszy sposób niż to wszystko. Ale działa!

Joe Kington
źródło
Dzięki! Podklasa ScalarFormatter działa świetnie! Ale chyba nie określiłem jasno, czego chcę dla osi X. Chciałbym zachować przesunięcie osi X, ale sformatuj wartość przesunięcia, aby nie była wyświetlana jako wykładnik.
Jonathan,
To jedyna metoda, która u mnie zadziałała! Dzięki :)
Ocean Scientist
11

Podobnie jak w przypadku odpowiedzi Amro, możesz użyć FuncFormatter

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter

# Generate some random data...
x = np.linspace(55478, 55486, 100) 
y = np.random.random(100) - 0.5
y = np.cumsum(y)
y -= y.min()
y *= 1e-8

# Plot the data...
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, y, 'b-')

# Force the y-axis ticks to use 1e-9 as a base exponent 
ax.yaxis.set_major_formatter(FuncFormatter(lambda x, pos: ('%.1f')%(x*1e9)))
ax.set_ylabel('microseconds (1E-9)')

# Make the x-axis ticks formatted to 0 decimal places
ax.xaxis.set_major_formatter(FuncFormatter(lambda x, pos: '%.0f'%x))
plt.show()
idrinkpabst
źródło
5

Rozwiązanie Gonzalo zaczęło działać dla mnie po dodaniu set_scientific(False):

ax=gca()
fmt=matplotlib.ticker.ScalarFormatter(useOffset=False)
fmt.set_scientific(False)
ax.xaxis.set_major_formatter(fmt)
ha7ilm
źródło
5

Jak wskazano w komentarzach oraz w tej odpowiedzi , offset można wyłączyć globalnie, wykonując następujące czynności:

matplotlib.rcParams['axes.formatter.useoffset'] = False
Evgeni Sergeev
źródło
4

Myślę, że bardziej eleganckim sposobem jest użycie programu formatującego. Oto przykład dla osi x i yaxis:

from pylab import *
from matplotlib.ticker import MultipleLocator, FormatStrFormatter

majorLocator   = MultipleLocator(20)
xFormatter = FormatStrFormatter('%d')
yFormatter = FormatStrFormatter('%.2f')
minorLocator   = MultipleLocator(5)


t = arange(0.0, 100.0, 0.1)
s = sin(0.1*pi*t)*exp(-t*0.01)

ax = subplot(111)
plot(t,s)

ax.xaxis.set_major_locator(majorLocator)
ax.xaxis.set_major_formatter(xFormatter)
ax.yaxis.set_major_formatter(yFormatter)

#for the minor ticks, use no labels; default NullFormatter
ax.xaxis.set_minor_locator(minorLocator)
Bogdana
źródło
1
Nie odpowiada to na pytanie, jak określić offset i / lub współczynnik używany w notacji naukowej .
sodd
@nordev Nawet jeśli moja odpowiedź nie odpowiada konkretnie na pytanie, nadal daje wskazówkę. Wiadomość jest taka, że ​​możesz wybrać inny program formatujący i uzyskać to, czego chcesz, zamiast daty z mojego przykładu. W świecie naukowym dzień juliański jest normą lub możesz użyć daty jak na moim przykładzie. Próbowałem zasugerować, że można przyjąć inne podejście. Czasami można zadać pytanie, ponieważ dana osoba nie ma w tej chwili lepszego pomysłu. Nie należy odrzucać alternatywnych rozwiązań ani traktować ich z brakiem szacunku. W sumie nie zasłużyłem na -1 głos.
Bogdan
2

W drugiej części, bez ponownego ręcznego resetowania wszystkich ticków, było to moje rozwiązanie:

class CustomScalarFormatter(ScalarFormatter):
    def format_data(self, value):
        if self._useLocale:
            s = locale.format_string('%1.2g', (value,))
        else:
            s = '%1.2g' % value
        s = self._formatSciNotation(s)
        return self.fix_minus(s)
xmajorformatter = CustomScalarFormatter()  # default useOffset=True
axes.get_xaxis().set_major_formatter(xmajorformatter)

oczywiście możesz ustawić ciąg formatu na cokolwiek chcesz.

astrojuanlu
źródło
Niestety nie zbadałem, jak ustawić mnożnik w pierwszej części twojego pytania.
astrojuanlu