matplotlib: formatta i valori di offset dell'asse su numeri interi o numeri specifici


92

Ho una figura matplotlib di cui sto tracciando i dati a cui ci si riferisce sempre come nanosecondi (1e-9). Sull'asse y, se ho dati che sono decine di nanosecondi, cioè. 44e-9, il valore sull'asse viene visualizzato come 4.4 con un + 1e-8 come offset. C'è comunque per forzare l'asse a mostrare 44 con un offset + 1e-9?

Lo stesso vale per il mio asse x in cui l'asse mostra + 5.54478e4, dove preferirei mostrare un offset di +55447 (numero intero, nessun decimale - il valore qui è in giorni).

Ho provato un paio di cose come questa:

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

per l'asse x, ma questo non funziona, anche se probabilmente lo sto usando in modo errato o sto interpretando male qualcosa dai documenti, qualcuno può indicarmi la direzione corretta?

Grazie, Jonathan

Illustrazione del problema


Ho provato a fare qualcosa con i formattatori ma non ho ancora trovato alcuna soluzione ...:

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

e

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

In una nota a margine, in realtà sono confuso su dove risiede effettivamente l'oggetto 'numero di offset' ... fa parte delle tacche maggiori / minori?


1
Hai provato set_units? matplotlib.sourceforge.net/api/… (Non posso provarlo perché non ho matplotlib qui.)
Katriel

1
Ho controllato la funzione set_units e sembra molto più complicata del necessario (devi scrivere / aggiungere un modulo aggiuntivo ?? - basic_units?). Deve esserci un modo per modificare semplicemente il formato del segno di spunta. La funzione units / set_unit sembra più sulla falsariga della conversione di unità. Grazie per il suggerimento, mi ha portato ad alcune altre soluzioni che sto cercando ora!
Jonathan

1
si prega di considerare rcParamsdi disattivare se disattivato per impostazione predefinita: rcParams["axes.formatter.useoffset"] = Falsecome qui: stackoverflow.com/questions/24171064/…
Ruggero Turra

Risposte:


100

Ho avuto esattamente lo stesso problema e queste righe hanno risolto il problema:

from matplotlib.ticker import ScalarFormatter

y_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False)
ax.yaxis.set_major_formatter(y_formatter)

1
Questa è la risposta facile e veloce. Grazie.
maxm

6
Una battuta potrebbe essere:ax.get_yaxis().get_major_formatter().set_useOffset(False)
Dataman

3
per i principianti come me non dimenticare il from matplotlib.ticker import ScalarFormattercodice di @Gonzalo per funzionare o semplicemente usa la soluzione di
@Dataman

36

Una soluzione molto più semplice è semplicemente personalizzare le etichette di spunta. Prendi questo esempio:

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

testo alternativo

Si noti come nel caso dell'asse y, ho moltiplicato i valori per 1e9poi menzionare quella costante nell'etichetta y


MODIFICARE

Un'altra opzione è falsificare il moltiplicatore di esponente aggiungendo manualmente il suo testo all'inizio del grafico:

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

EDIT2

Inoltre è possibile formattare il valore di offset dell'asse x nello stesso modo:

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)

testo alternativo


Questo è esattamente quello che ho fatto all'inizio. Sfortunatamente, non sono riuscito a trovare un modo semplice per impostare / mostrare il moltiplicatore dell'asse (oltre a inserirlo esplicitamente nell'etichetta dell'asse y, come hai fatto tu). Se non ti dispiace non avere l'etichetta del moltiplicatore dell'asse, questo è il modo più semplice. Ad ogni modo, +1 da me.
Joe Kington,

1
@ Joe Kington: puoi aggiungerlo manualmente come testo ... vedi la modifica sopra :)
Amro

Grande! Proverò il tuo approccio con le etichette per l'asse x. Prenderò la base del primo valore x, quindi lo rimuoverò da ogni valore x e aggiungerò un "+ minxval" come etichetta. Non riesco a capire in quale altro modo formattare l'offset x-tick. Sto bene con l'ampiezza dell'offset, ho solo bisogno che venga visualizzato come un valore non esponenziale.
Jonathan

Wow. Ottimo lavoro nel mostrare come puoi davvero prendere il controllo di matplotlib e adattarlo alle tue esigenze e davvero un po 'di pizazz alle tue trame.
physicsmichael

Come si modifica la dimensione del carattere di 1e-9 nella figura?
un'offerta non può rifiutare il

30

Devi sottoclasse ScalarFormatterper fare quello che ti serve ... _set_offsetaggiunge solo una costante, che vuoi impostare ScalarFormatter.orderOfMagnitude. Sfortunatamente, l'impostazione manuale orderOfMagnitudenon farà nulla, poiché viene ripristinata quando l' ScalarFormatteristanza viene chiamata per formattare le etichette di tick dell'asse. Non dovrebbe essere così complicato, ma non riesco a trovare un modo più semplice per fare esattamente quello che vuoi ... Ecco un esempio:

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

Che produce qualcosa come: testo alternativo

Considerando che, la formattazione predefinita sarebbe simile a: testo alternativo

Spero che questo aiuti un po '!

Modifica: per quello che vale, non so nemmeno dove risiede l'etichetta offset ... Sarebbe leggermente più semplice impostarla manualmente, ma non riuscivo a capire come farlo ... Ho la sensazione che ci deve essere un modo più semplice di tutto questo. Funziona, però!


Grazie! La creazione di sottoclassi di ScalarFormatter funziona alla grande! Ma immagino di non aver dichiarato chiaramente cosa volevo per l'asse x. Vorrei mantenere l'offset per l'asse x, ma formattare il valore dell'offset in modo che non venga visualizzato come esponente.
Jonathan

Questo è l'unico metodo che ha funzionato per me! Grazie :)
Ocean Scientist

11

Simile alla risposta di Amro, puoi usare 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()

5

La soluzione di Gonzalo ha iniziato a funzionare per me dopo aver aggiunto set_scientific(False):

ax=gca()
fmt=matplotlib.ticker.ScalarFormatter(useOffset=False)
fmt.set_scientific(False)
ax.xaxis.set_major_formatter(fmt)

5

Come è stato sottolineato nei commenti e in questa risposta , l'offset può essere disattivato globalmente, procedendo come segue:

matplotlib.rcParams['axes.formatter.useoffset'] = False

4

Penso che un modo più elegante sia usare il formattatore ticker. Ecco un esempio sia per xaxis che per 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)

1
Questo non risponde alla domanda, ovvero come specificare l'offset e / o il fattore utilizzato nella notazione scientifica .
sodd

@nordev Anche se la mia risposta non risponde in modo specifico alla domanda, fornisce comunque un suggerimento. Il messaggio è che puoi scegliere un altro formattatore e ottenere quello che vuoi invece di una data dal mio esempio. Nel mondo scientifico il giorno giuliano è la norma, oppure puoi usare la data come nel mio esempio. Quello che stavo cercando di suggerire è che si può adottare un approccio diverso. A volte può essere posta una domanda perché la persona non ha un'idea migliore al momento. Soluzioni alternative non devono essere eliminate o trattate con mancanza di rispetto. Tutto sommato non meritavo il voto -1.
Bogdan

2

Per la seconda parte, senza ripristinare manualmente tutte le zecche, questa era la mia soluzione:

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)

ovviamente puoi impostare la stringa di formato come preferisci.


Sfortunatamente non ho studiato come impostare il moltiplicatore come afferma la prima parte della tua domanda.
astrojuanlu
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.