So fügen Sie eine zweite x-Achse in matplotlib hinzu

78

Ich habe eine sehr einfache Frage. Ich brauche eine zweite x-Achse in meinem Diagramm und möchte, dass diese Achse eine bestimmte Anzahl von Tics hat, die einer bestimmten Position der ersten Achse entsprechen.

Versuchen wir es mit einem Beispiel. Hier zeichne ich die Masse der Dunklen Materie als Funktion des Expansionsfaktors, definiert als 1 / (1 + z), der von 0 bis 1 reicht.

semilogy(1/(1+z),mass_acc_massive,'-',label='DM')
xlim(0,1)
ylim(1e8,5e12)

Ich hätte gerne eine weitere x-Achse oben in meinem Diagramm, die das entsprechende z für einige Werte des Expansionsfaktors anzeigt. Ist das möglich? Wenn ja, wie kann ich xtics ax haben?

Brian
quelle
Die Funktionen, die Sie verwenden, sind nicht in Python integriert - ich nehme an, Sie sprechen von Matplotlib ? Wenn ja, die Frage Wie zeichne ich mehrere x- oder y-Achsen in matplotlib? scheint dies abzudecken.
James

Antworten:

116

Ich nehme einen Hinweis aus den Kommentaren in @ Dharas Antwort. Es hört sich so an, als ob Sie eine Liste new_tick_locationsmit einer Funktion von der alten x-Achse zur neuen x-Achse erstellen möchten . Das tick_functionFolgende nimmt eine Reihe von Punkten auf, ordnet sie einem neuen Wert zu und formatiert sie:

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twiny()

X = np.linspace(0,1,1000)
Y = np.cos(X*20)

ax1.plot(X,Y)
ax1.set_xlabel(r"Original x-axis: $X$")

new_tick_locations = np.array([.2, .5, .9])

def tick_function(X):
    V = 1/(1+X)
    return ["%.3f" % z for z in V]

ax2.set_xlim(ax1.get_xlim())
ax2.set_xticks(new_tick_locations)
ax2.set_xticklabels(tick_function(new_tick_locations))
ax2.set_xlabel(r"Modified x-axis: $1/(1+X)$")
plt.show()

Geben Sie hier die Bildbeschreibung ein

Süchtig
quelle
2
Wenn Sie auch einen Titel hinzufügen möchten, sehen Sie diese Frage: stackoverflow.com/questions/12750355/…
szmoore
@Hooked, eine Idee, ob dies mit einer der Skalen in der Log-Skala möglich ist?
Tiago
2
@tiago Ich kann es momentan nicht überprüfen, aber funktioniert es ax.set_yscale('log')?
Hooked
5
Dieses Beispiel funktioniert "zufällig", da die xlims zwischen 0 und 1 liegen (Standardeinstellung). Damit dies für beliebige Grenzen funktioniert, muss auch sichergestellt werden, dass die Grenzen der beiden Achsen mit etwas übereinstimmen, da ax2.set_xlim(ax1.get_xlim())sonst die Xfür die Oberseite und die Xfür die Unterseite unterschiedlich sind.
Mforbes
Matplotlib hat eine native Methode zum Hinzufügen einer Sekundärachse implementiert. Das Beispiel finden Sie hier: matplotlib.org/3.1.0/gallery/subplots_axes_and_figures/…
Stefano
27

Sie können Twiny verwenden, um 2 x-Achsen-Skalen zu erstellen. Zum Beispiel:

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twiny()

a = np.cos(2*np.pi*np.linspace(0, 1, 60.))

ax1.plot(range(60), a)
ax2.plot(range(100), np.ones(100)) # Create a dummy plot
ax2.cla()
plt.show()

Ref: http://matplotlib.sourceforge.net/faq/howto_faq.html#multiple-y-axis-scales

Ausgabe: Geben Sie hier die Bildbeschreibung ein

Dhara
quelle
Es tut mir leid, aber das habe ich nicht gefragt. Ich muss nicht auf Funktionen auf demselben Plot mit unterschiedlicher Achse zeichnen. Ich brauche nur eine zweite x-Achse oben auf meinem Plot, die entsprechend einer speziellen Position auf der ersten x-Achse angekreuzt ist.
Brian
Fügen Sie dann einfach eine Dummy-2.-Achse mit dem gewünschten Bereich hinzu und löschen Sie sie. Siehe meine bearbeitete Antwort
Dhara
Entschuldigung, aber Ihr Code funktioniert nicht. Ich habe es kopiert und eingefügt und dann die zweite x-Achse mit der ersten überlappt.
Brian
Anbei die Ausgabe, ist dies nicht das, was Sie bekommen / wollen?
Dhara
Nicht wirklich. Anhand Ihres Beispiels möchte ich auf der zweiten x-Achse folgende Tics: (7,8,99) entsprechend der x-Achsenposition 10, 30, 40. Ist das irgendwie möglich?
Brian
12

Wenn Sie möchten, dass Ihre obere Achse eine Funktion der Tick-Werte der unteren Achse ist:

import matplotlib.pyplot as plt

fig, ax1 = plt.subplots()

ax1 = fig.add_subplot(111)

ax1.plot(range(5), range(5))

ax1.grid(True)

ax2 = ax1.twiny()
ax1Xs = ax1.get_xticks()

ax2Xs = []
for X in ax1Xs:
    ax2Xs.append(X * 2)

ax2.set_xticks(ax1Xs)
ax2.set_xbound(ax1.get_xbound())
ax2.set_xticklabels(ax2Xs)

title = ax1.set_title("Upper x-axis ticks are lower x-axis ticks doubled!")
title.set_y(1.1)
fig.subplots_adjust(top=0.85)

fig.savefig("1.png")

Gibt:

Geben Sie hier die Bildbeschreibung ein

Adobe
quelle
11

Beantwortung Ihrer Frage in Dharas Antwortkommentaren: " Ich möchte auf der zweiten x-Achse diese Tics: (7,8,99) entsprechend der x-Achsenposition 10, 30, 40. Ist das irgendwie möglich? " Ja , es ist.

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax1 = fig.add_subplot(111)

a = np.cos(2*np.pi*np.linspace(0, 1, 60.))
ax1.plot(range(60), a)

ax1.set_xlim(0, 60)
ax1.set_xlabel("x")
ax1.set_ylabel("y")

ax2 = ax1.twiny()
ax2.set_xlabel("x-transformed")
ax2.set_xlim(0, 60)
ax2.set_xticks([10, 30, 40])
ax2.set_xticklabels(['7','8','99'])

plt.show()

Du wirst kriegen: Geben Sie hier die Bildbeschreibung ein

Carla
quelle
4

Ab matplotlib 3.1 können Sie verwenden ax.secondary_xaxis

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(1,13, num=301)
y = (np.sin(x)+1.01)*3000

# Define function and its inverse
f = lambda x: 1/(1+x)
g = lambda x: 1/x-1

fig, ax = plt.subplots()
ax.semilogy(x, y, label='DM')

ax2 = ax.secondary_xaxis("top", functions=(f,g))

ax2.set_xlabel("1/(x+1)")
ax.set_xlabel("x")
plt.show()

Delenges
quelle
2

Ich bin gezwungen, dies als Antwort anstelle eines Kommentars zu posten, da das Ansehen gering ist. Ich hatte ein ähnliches Problem wie Matteo. Der Unterschied besteht darin, dass ich keine Karte von meiner ersten x-Achse zu meiner zweiten x-Achse hatte, nur die x-Werte selbst. Also wollte ich die Daten auf meiner zweiten x-Achse direkt einstellen, nicht die Ticks, aber es gibt keine axes.set_xdata. Ich konnte Dharas Antwort verwenden, um dies mit einer Modifikation zu tun:

ax2.lines = []

anstatt zu verwenden:

ax2.cla()

Im Gebrauch löschte auch mein Grundstück von ax1.

user2561747
quelle
3
Dies wäre eine bessere Antwort, wenn Sie das Codebeispiel angeben würden, anstatt nur die Änderungen gegenüber Dharas Antwort.
Benjamin