Verwenden von Colormaps zum Festlegen der Linienfarbe in matplotlib

74

Wie setzt man die Farbe einer Linie in matplotlib mit Skalarwerten, die zur Laufzeit mithilfe einer Farbkarte (z. B. jet) bereitgestellt werden ? Ich habe hier ein paar verschiedene Ansätze ausprobiert und denke, ich bin ratlos. values[]ist eine sortierte Anordnung von Skalaren. Kurven sind eine Reihe von 1-D-Arrays, und Beschriftungen sind ein Array von Textzeichenfolgen. Jedes der Arrays hat die gleiche Länge.

fig = plt.figure()
ax = fig.add_subplot(111)
jet = colors.Colormap('jet')
cNorm  = colors.Normalize(vmin=0, vmax=values[-1])
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)
lines = []
for idx in range(len(curves)):
    line = curves[idx]
    colorVal = scalarMap.to_rgba(values[idx])
    retLine, = ax.plot(line, color=colorVal)
    #retLine.set_color()
    lines.append(retLine)
ax.legend(lines, labels, loc='upper right')
ax.grid()
plt.show()
Fodon
quelle

Antworten:

86

Der Fehler, den Sie erhalten, hängt davon ab, wie Sie ihn definieren jet. Sie erstellen die Basisklasse Colormapmit dem Namen 'jet', dies unterscheidet sich jedoch stark von der Standarddefinition der 'Jet'-Farbkarte. Diese Basisklasse sollte niemals direkt erstellt werden, und nur die Unterklassen sollten instanziiert werden.

Was Sie in Ihrem Beispiel gefunden haben, ist ein fehlerhaftes Verhalten in Matplotlib. Beim Ausführen dieses Codes sollte eine klarere Fehlermeldung generiert werden.

Dies ist eine aktualisierte Version Ihres Beispiels:

import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.cm as cmx
import numpy as np

# define some random data that emulates your indeded code:
NCURVES = 10
np.random.seed(101)
curves = [np.random.random(20) for i in range(NCURVES)]
values = range(NCURVES)

fig = plt.figure()
ax = fig.add_subplot(111)
# replace the next line 
#jet = colors.Colormap('jet')
# with
jet = cm = plt.get_cmap('jet') 
cNorm  = colors.Normalize(vmin=0, vmax=values[-1])
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)
print scalarMap.get_clim()

lines = []
for idx in range(len(curves)):
    line = curves[idx]
    colorVal = scalarMap.to_rgba(values[idx])
    colorText = (
        'color: (%4.2f,%4.2f,%4.2f)'%(colorVal[0],colorVal[1],colorVal[2])
        )
    retLine, = ax.plot(line,
                       color=colorVal,
                       label=colorText)
    lines.append(retLine)
#added this to get the legend to work
handles,labels = ax.get_legend_handles_labels()
ax.legend(handles, labels, loc='upper right')
ax.grid()
plt.show()

Ergebend:

Geben Sie hier die Bildbeschreibung ein

Die Verwendung von a ScalarMappableist eine Verbesserung gegenüber dem in meiner Antwort vorgestellten Ansatz: Erstellen von über 20 eindeutigen Legendenfarben mit matplotlib

Yann
quelle
2
Diese Antwort ist nicht minimal und direkt. Das Codebeispiel enthält viele unnötige Unordnung und zeigt kein gutes Verständnis von matplotlib. Die Antwort von blahreport sollte stattdessen akzeptiert werden.
olq_plo
@Yann Können wir auch für 'plt.scatter' verschiedene Farben wie diese erzeugen?
Klinge
65

Ich dachte, es wäre vorteilhaft, eine meiner Meinung nach einfachere Methode einzubeziehen, bei der der Linspace von numpy mit dem cm-Objekt von matplotlib gekoppelt wird. Es ist möglich, dass die obige Lösung für eine ältere Version gilt. Ich verwende Python 3.4.3, Matplotlib 1.4.3 und Numpy 1.9.3. Meine Lösung lautet wie folgt.

import matplotlib.pyplot as plt

from matplotlib import cm
from numpy import linspace

start = 0.0
stop = 1.0
number_of_lines= 1000
cm_subsection = linspace(start, stop, number_of_lines) 

colors = [ cm.jet(x) for x in cm_subsection ]

for i, color in enumerate(colors):
    plt.axhline(i, color=color)

plt.ylabel('Line Number')
plt.show()

Dies führt zu 1000 einfarbigen Linien, die sich über die gesamte cm.jet-Farbkarte erstrecken (siehe Abbildung unten). Wenn Sie dieses Skript ausführen, können Sie die einzelnen Zeilen vergrößern.

cm.jet zwischen 0,0 und 1,0 mit 1000 Teilungen

Angenommen, ich möchte, dass meine 1000 Linienfarben nur den grünlichen Teil zwischen den Linien 400 bis 600 überspannen. Ich ändere einfach meine Start- und Stoppwerte auf 0,4 und 0,6, und dies führt dazu, dass nur 20% der cm.jet-Farbkarte zwischen 0,4 und 0,4 verwendet werden 0,6.

Geben Sie hier die Bildbeschreibung ein

In einer einzeiligen Zusammenfassung können Sie eine Liste der RGBA-Farben aus einer matplotlib.cm-Farbkarte entsprechend erstellen:

colors = [ cm.jet(x) for x in linspace(start, stop, number_of_lines) ]

In diesem Fall verwende ich die häufig aufgerufene Karte mit dem Namen jet, aber Sie können die vollständige Liste der in Ihrer matplotlib-Version verfügbaren Farbkarten finden, indem Sie Folgendes aufrufen:

>>> from matplotlib import cm
>>> dir(cm)
blahreport
quelle
3
Natürlich ist 1 der beste Wert. Wenn Sie eine größere Farbpalette wünschen, müssen Sie nur erhöhen number_of_lines. Und für den Fall, dass Sie nur einen Teil der Farben im Band haben möchten, verringern stopund erhöhen Sie diese startnach Bedarf.
Chidimo
1
Eine kurze Frage: Wie können Sie Ihrem Plot eine Farbleiste anstelle einer Legende hinzufügen?
Thanhtang
14

Eine Kombination aus Linienstilen, Markierungen und qualitativen Farben aus matplotlib:

import itertools
import matplotlib as mpl
import matplotlib.pyplot as plt
N = 8*4+10
l_styles = ['-','--','-.',':']
m_styles = ['','.','o','^','*']
colormap = mpl.cm.Dark2.colors   # Qualitative colormap
for i,(marker,linestyle,color) in zip(range(N),itertools.product(m_styles,l_styles, colormap)):
    plt.plot([0,1,2],[0,2*i,2*i], color=color, linestyle=linestyle,marker=marker,label=i)
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.,ncol=4);

Geben Sie hier die Bildbeschreibung ein

UPDATE: Unterstützt nicht nur ListedColormap, sondern auchLinearSegmentedColormap

import itertools
import matplotlib.pyplot as plt
Ncolors = 8
#colormap = plt.cm.Dark2# ListedColormap
colormap = plt.cm.viridis# LinearSegmentedColormap
Ncolors = min(colormap.N,Ncolors)
mapcolors = [colormap(int(x*colormap.N/Ncolors)) for x in range(Ncolors)]
N = Ncolors*4+10
l_styles = ['-','--','-.',':']
m_styles = ['','.','o','^','*']
fig,ax = plt.subplots(gridspec_kw=dict(right=0.6))
for i,(marker,linestyle,color) in zip(range(N),itertools.product(m_styles,l_styles, mapcolors)):
    ax.plot([0,1,2],[0,2*i,2*i], color=color, linestyle=linestyle,marker=marker,label=i)
ax.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.,ncol=3,prop={'size': 8})

Geben Sie hier die Bildbeschreibung ein

Pablo Reyes
quelle
1
Wie kann ich das für die cmap "coolwarm" machen? In diesem Fall gibt es kein .colors-Attribut
seralouk
coolwarmist vom Typ LinearSegmentedColormapim Gegensatz zum Typ ListedColormap(z Dark2. B. ). Ich habe meine Antwort aktualisiert, um auch LinearSegmentedColormapFarbkarten alsviridis
Pablo Reyes
4

Sie können tun, was ich von meinem gelöschten Konto geschrieben habe (Verbot für neue Beiträge :( gab es). Es ist ziemlich einfach und sieht gut aus.

Ich benutze normalerweise eine dieser drei, normalerweise habe ich auch die Versionen 1 und 2 überprüft.

from matplotlib.pyplot import cm
import numpy as np

#variable n should be number of curves to plot (I skipped this earlier thinking that it is obvious when looking at picture - sorry my bad mistake xD): n=len(array_of_curves_to_plot)
#version 1:

color=cm.rainbow(np.linspace(0,1,n))
for i,c in zip(range(n),color):
   ax1.plot(x, y,c=c)

#or version 2: - faster and better:

color=iter(cm.rainbow(np.linspace(0,1,n)))
c=next(color)
plt.plot(x,y,c=c)

#or version 3:

color=iter(cm.rainbow(np.linspace(0,1,n)))
for i in range(n):
   c=next(color)
   ax1.plot(x, y,c=c)

Beispiel von 3:

Schiff RAO of Roll gegen Ikeda Dämpfung in Abhängigkeit von der Rollamplitude A44

Robert GRZELKA
quelle