matplotlib: Formatieren Sie die Achsenversatzwerte auf ganze Zahlen oder eine bestimmte Zahl

92

Ich habe eine Matplotlib-Figur, in der ich Daten zeichne, die immer als Nanosekunden bezeichnet werden (1e-9). Wenn ich auf der y-Achse Daten habe, die zehn Nanosekunden betragen, d. H. In 44e-9 wird der Wert auf der Achse als 4,4 mit einem + 1e-8 als Versatz angezeigt. Gibt es überhaupt eine Möglichkeit, die Achse zu zwingen, 44 mit einem Versatz von + 1e-9 anzuzeigen?

Gleiches gilt für meine x-Achse, auf der die Achse + 5.54478e4 anzeigt, wo ich lieber einen Versatz von +55447 anzeigen möchte (ganze Zahl, keine Dezimalstelle - der Wert hier ist in Tagen).

Ich habe ein paar Dinge wie diese ausprobiert:

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

für die x-Achse, aber das funktioniert nicht, obwohl ich es wahrscheinlich falsch benutze oder etwas aus den Dokumenten falsch interpretiere, kann mich jemand in die richtige Richtung weisen?

Danke, Jonathan

Problemillustration


Ich habe versucht, etwas mit Formatierern zu tun, habe aber noch keine Lösung gefunden ...:

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

und

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

Nebenbei bemerkt, ich bin tatsächlich verwirrt darüber, wo sich das Objekt "Versatznummer" tatsächlich befindet ... ist es Teil der Haupt- / Neben-Ticks?

Jonathan
quelle
1
Hast du es versucht set_units? matplotlib.sourceforge.net/api/… (Ich kann es nicht versuchen, weil ich hier kein Matplotlib habe.)
Katriel
1
Ich habe die Funktion set_units ausgecheckt und sie scheint viel komplizierter als nötig zu sein (muss ein zusätzliches Modul geschrieben / hinzugefügt werden ?? - basic_units?). Es muss eine Möglichkeit geben, nur das Format des Häkchens zu bearbeiten. Die Funktion parts / set_unit scheint eher der Einheitenumrechnung zu entsprechen. Vielen Dank für den Tipp, der mich zu einigen anderen Lösungen geführt hat, nach denen ich jetzt suche!
Jonathan
1
Bitte überlegen Sie rcParams, ob standardmäßig deaktiviert werden soll : rcParams["axes.formatter.useoffset"] = Falsewie hier: stackoverflow.com/questions/24171064/…
Ruggero Turra

Antworten:

100

Ich hatte genau das gleiche Problem und diese Zeilen haben das Problem behoben:

from matplotlib.ticker import ScalarFormatter

y_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False)
ax.yaxis.set_major_formatter(y_formatter)
Gonzalo
quelle
1
Dies ist die schnelle und einfache Antwort. Danke dir.
Maxm
6
Ein ax.get_yaxis().get_major_formatter().set_useOffset(False)
Einzeiler
3
Für Noobs wie mich vergessen Sie nicht, dass der from matplotlib.ticker import ScalarFormatterCode von @Gonzalo funktioniert, oder verwenden Sie einfach die oben genannte Lösung von
@Dataman
36

Eine viel einfachere Lösung besteht darin, die Häkchenbezeichnungen einfach anzupassen. Nehmen Sie dieses Beispiel:

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

Alt-Text

Beachten Sie, wie ich im Fall der y-Achse die Werte multipliziert habe, indem ich 1e9diese Konstante im y-Label erwähnt habe


BEARBEITEN

Eine andere Möglichkeit besteht darin, den Exponentenmultiplikator zu fälschen, indem Sie seinen Text manuell oben im Plot hinzufügen:

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

Sie können den Versatzwert der x-Achse auch auf folgende Weise formatieren:

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)

Alt-Text

Amro
quelle
Genau das habe ich zuerst getan. Leider konnte ich keine einfache Möglichkeit finden, den Achsenmultiplikator festzulegen / anzuzeigen (außer wie Sie es explizit in die Beschriftung der y-Achse eingefügt haben). Wenn es Ihnen nichts ausmacht, keine Achsenmultiplikatorbezeichnung zu haben, ist dies der einfachere Weg. So oder so, +1 von mir.
Joe Kington
1
@ Joe Kington: Sie können es manuell als Text hinzufügen ... siehe die Bearbeitung oben :)
Amro
Toll! Ich werde Ihren Ansatz mit den Beschriftungen für die x-Achse versuchen. Ich nehme den Boden des ersten x-Werts, entferne ihn dann von jedem x-Wert und füge ein "+ minxval" als Beschriftung hinzu. Ich kann nicht herausfinden, wie ich den X-Tick-Offset sonst formatieren soll. Ich bin mit der Größe des Versatzes einverstanden. Ich brauche ihn nur, um ihn als nicht exponentiellen Wert anzuzeigen.
Jonathan
Beeindruckend. Großartige Arbeit, um zu zeigen, wie Sie wirklich die Kontrolle über Matplotlib übernehmen und es an Ihre Bedürfnisse und wirklich und etwas Pizazz an Ihren Plots anpassen können.
Physikmichael
Wie ändern Sie die Schriftgröße von 1e-9 in der Abbildung?
Ein Angebot kann den
30

Sie müssen eine Unterklasse erstellen ScalarFormatter, um das zu tun, was Sie benötigen. Fügen Sie _set_offseteinfach eine Konstante hinzu, die Sie festlegen möchten ScalarFormatter.orderOfMagnitude. Leider führt die manuelle Einstellung zu orderOfMagnitudenichts, da sie zurückgesetzt wird, wenn die ScalarFormatterInstanz aufgerufen wird, um die Achsen-Tick-Beschriftungen zu formatieren. Es sollte nicht so kompliziert sein, aber ich kann keinen einfacheren Weg finden, genau das zu tun, was Sie wollen ... Hier ein Beispiel:

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

Was so etwas ergibt wie: Alt-Text

Die Standardformatierung würde folgendermaßen aussehen: Alt-Text

Hoffe das hilft ein bisschen!

Bearbeiten: Für das, was es wert ist, weiß ich auch nicht, wo sich das Offset-Label befindet ... Es wäre etwas einfacher, es einfach manuell einzustellen, aber ich konnte nicht herausfinden, wie das geht ... Ich habe das Gefühl dass es einen einfacheren Weg geben muss als all dies. Es funktioniert aber!

Joe Kington
quelle
Vielen Dank! Die Unterklasse von ScalarFormatter funktioniert hervorragend! Aber ich glaube, ich habe nicht klar gesagt, was ich für die x-Achse wollte. Ich möchte den Versatz für die x-Achse beibehalten, aber den Wert des Versatzes so formatieren, dass er nicht als Exponent angezeigt wird.
Jonathan
Dies ist die einzige Methode, die bei mir funktioniert hat! Danke :)
Ocean Scientist
11

Ähnlich wie bei Amro können Sie FuncFormatter verwenden

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
quelle
5

Gonzalos Lösung begann für mich zu funktionieren, nachdem sie hinzugefügt hatte set_scientific(False):

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

Wie in den Kommentaren und in dieser Antwort ausgeführt wurde , kann der Offset global wie folgt ausgeschaltet werden:

matplotlib.rcParams['axes.formatter.useoffset'] = False
Evgeni Sergeev
quelle
4

Ich denke, dass eine elegantere Art die Verwendung des Ticker-Formatierers ist. Hier ist ein Beispiel für xaxis und 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)
Bogdan
quelle
1
Dies beantwortet nicht die Frage, wie der Versatz und / oder der in der wissenschaftlichen Notation verwendete Faktor angegeben werden sollen .
Sodd
@nordev Auch wenn meine Antwort die Frage nicht speziell beantwortet, gibt sie dennoch einen Hinweis. Die Nachricht ist, dass Sie einen anderen Formatierer auswählen und anstelle eines Datums aus meinem Beispiel das erhalten können, was Sie möchten. In der wissenschaftlichen Welt ist der julianische Tag die Norm, oder Sie können das Datum wie in meinem Beispiel verwenden. Ich wollte vorschlagen, dass ein anderer Ansatz gewählt werden kann. Manchmal kann eine Frage gestellt werden, weil die Person im Moment keine bessere Idee hat. Alternative Lösungen sollten nicht verworfen oder mit Respektlosigkeit behandelt werden. Alles in allem habe ich die -1-Stimme nicht verdient.
Bogdan
2

Für den zweiten Teil war dies meine Lösung, ohne alle Ticks manuell zurückzusetzen:

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)

Natürlich können Sie die Formatzeichenfolge beliebig einstellen.

Astrojuanlu
quelle
Leider habe ich nicht untersucht, wie der Multiplikator als erster Teil Ihrer Frage festgelegt wird.
Astrojuanlu