¿Cómo agregar barras de colores a los diagtwigs de dispersión creados de esta manera?

Creo diagtwigs de dispersión con código que, en esencia, es así.

cmap = (matplotlib.color.LinearSegmentedColormap. from_list('blueWhiteRed', ['blue', 'white', 'red'])) fig = matplotlib.figure.Figure(figsize=(4, 4), dpi=72) ax = fig.gca() for record in data: level = record.level # a float in [0.0, 1.0] marker = record.marker # one of 'o', 's', '^', '*', etc. ax.scatter(record.x, record.y, marker=marker, c=level, vmin=0, vmax=1, cmap=cmap, **otherkwargs) # various settings of ticks, labels, etc. omitted canvas = matplotlib.backends.backend_agg.FigureCanvasAgg(fig) fig.set_canvas(canvas) canvas.print_png('/path/to/output/fig.png') 

Mi pregunta es la siguiente:

¿Qué necesito agregar al código anterior para obtener una barra de color vertical (que representa el mapa de cmap en cmap ) a lo largo del borde derecho del trazado?

NOTA : Me parece que Matplotlib es completamente incomprensible , y esto se aplica tanto a su diseño como a su documentación. (No por falta de bashs: le dedico mucho tiempo, esfuerzo e incluso algo de dinero). Por lo tanto, apreciaría el código de trabajo completo (incluso si es solo un ejemplo de juguete), porque lo más probable es que no lo logre. t ser capaz de rellenar los detalles omitidos o corregir errores en el código.


EDITAR: He corregido una omisión importante en el “boceto de código” anterior, a saber, una especificación de marcador específica de registro en cada llamada a ax.scatter . Esta es la razón para crear el diagtwig de dispersión con múltiples llamadas a ax.scatter , aunque, sin duda, se podría al menos reducir el número de llamadas a dispersar a una por fabricante de forma utilizada; p.ej

 for marker in set(record.marker for record in data): X, Y, COLOR = zip(*((record.x, record.y, record.level) for record in data if record.marker == marker)) ax.scatter(X, Y, marker=marker, c=COLOR, vmin=0, vmax=1, cmap=cmap, **otherkwargs) 

Intenté extender el mismo truco para colapsar todas las llamadas a ax.scatter en una (pasando una secuencia de marcadores como el argumento del marker ), como esto:

 X, Y, COLOR, MARKER = zip(*((record.x, record.y, record.level, record.marker) for record in data)) ax.scatter(X, Y, marker=MARKER, c=COLOR, vmin=0, vmax=1, cmap=cmap, **otherkwargs) 

… pero esto falla El error es algo como esto (después de podar algunos caminos largos):

 Traceback (most recent call last): File "src/demo.py", line 222, in  main() File "src/demo.py", line 91, in main **otherkwargs) File "/matplotlib/axes.py", line 6100, in scatter marker_obj = mmarkers.MarkerStyle(marker) File "/matplotlib/markers.py", line 113, in __init__ self.set_marker(marker) File "/matplotlib/markers.py", line 179, in set_marker raise ValueError('Unrecognized marker style {}'.format(marker)) ValueError: Unrecognized marker style ('^', 'o', '^', '*', 'o', 's', 'o', 'o', '^', 's', 'o', 'o', '^', '^', '*', 'o', '*', '*', 's', 's', 'o', 's', 'o', '^', 'o', 'o', '*', '^', 's', '^', '^', 's', '*') 

AFAICTO, la receta de tcaswell requiere reducir las llamadas a ax.scatter a una sola, pero este requisito parece estar en conflicto con mi requisito absoluto de múltiples formas de marcador en el mismo diagtwig de dispersión.

Si tiene que usar un marcador diferente para cada conjunto, debe hacer un poco de trabajo adicional y forzar a que todos los clims sean iguales (de lo contrario, se ajustarán por defecto a la escala mínima / máxima de los datos c por diagtwig de dispersión) .

 from pylab import * import matplotlib.lines as mlines import itertools fig = gcf() ax = fig.gca() # make some temorary arrays X = [] Y = [] C = [] cb = None # generate fake data markers = ['','o','*','^','v'] cmin = 0 cmax = 1 for record,marker in itertools.izip(range(5),itertools.cycle(mlines.Line2D.filled_markers)):  x = rand(50)  y = rand(50)  c = rand(1)[0] * np.ones(x.shape)  if cb is None:    s = ax.scatter(x,y,c=c,marker=markers[record],linewidths=0)    s.set_clim([cmin,cmax])    cb = fig.colorbar(s)  else:    s = ax.scatter(x,y,c=c,marker=markers[record],linewidths=0)    s.set_clim([cmin,cmax]) cb.set_label('Cbar Label Here') 

el linewidths=0 establece el ancho del borde de las formas, encuentro que para las formas pequeñas el borde negro puede abrumar el color del relleno.

diagrama de dispersión de color

Si solo necesita una forma, puede hacer todo esto con un solo diagtwig de dispersión, no hay necesidad de hacer una por separado para cada paso a través de su bucle.

 from pylab import * fig = gcf() ax = fig.gca() # make some temorary arrays X = [] Y = [] C = [] # generate fake data for record in range(5): x = rand(50) y = rand(50) c = rand(1)[0] * np.ones(x.shape) print c X.append(x) Y.append(y) C.append(c) X = np.hstack(X) Y = np.hstack(Y) C = np.hstack(C) 

una vez que tenga todos los datos eliminados en una matriz 1D, realice el diagtwig de dispersión y mantenga el valor devuelto:

 s = ax.scatter(X,Y,c=C) 

Luego, crea la barra de color y pasa el objeto devuelto por la scatter como primer argumento.

 cb = plt.colorbar(s) cb.set_label('Cbar Label Here') 

Debe hacer esto para que la barra de color sepa qué mapa de color (tanto el mapa como el rango) deben usar.

introduzca la descripción de la imagen aquí

Creo que lo mejor que puedes hacer es meter tus datos en un dataframe de pandas, y recorrer todos tus marcadores de la siguiente manera:

 import numpy as np import pandas as pd import matplotlib.pyplot as plt markers = ['s', 'o', '^'] records = [] for n in range(37): records.append([np.random.normal(), np.random.normal(), np.random.normal(), markers[np.random.randint(0, high=3)]]) records = pd.DataFrame(records, columns=['x', 'y', 'z', 'marker']) fig, ax = plt.subplots() for m in np.unique(records.marker): selector = records.marker == m s = ax.scatter(records[selector].x, records[selector].y, c=records[selector].z, marker=m, cmap=plt.cm.coolwarm, vmin=records.z.min(), vmax=records.z.max()) cbar = plt.colorbar(mappable=s, ax=ax) cbar.set_label('My Label') 

gráfico de resolución

Creo que esto debería hacer el truco. Estoy bastante seguro de que tomé esto de uno de los ejemplos de libros de cocina de matplotlib hace un tiempo, pero parece que no puedo encontrarlo ahora …

 from mpl_toolkits.axes_grid1 import make_axes_locatable cmap = (matplotlib.color.LinearSegmentedColormap. from_list('blueWhiteRed', ['blue', 'white', 'red'])) fig = matplotlib.figure.Figure(figsize=(4, 4), dpi=72) ax = fig.gca() for record in data: level = record.level # a float in [0.0, 1.0] ax.scatter(record.x, record.y, c=level, vmin=0, vmax=1, cmap=cmap, **otherkwargs) # various settings of ticks, labels, etc. omitted divider= make_axes_locatable(ax) cax = divider.append_axes("right", size="1%", pad=0.05) cb = plt.colorbar(cax=cax) cb.set_label('Cbar Label Here') canvas = matplotlib.backends.backend_agg.FigureCanvasAgg(fig) fig.set_canvas(canvas) canvas.print_png('/path/to/output/fig.png') 

La respuesta a esto puede ser trazar solo una dispersión, lo que permitiría crear un colobar directamente. Esto implica colocar los marcadores en la PathCollection creada por la dispersión a posteriori, pero se puede colocar fácilmente en una función. Esta función proviene de mi respuesta en otra pregunta, pero es directamente aplicable aquí.

Tomando los datos de la publicación de @ PaulH, esto parecería

 import numpy as np import pandas as pd import matplotlib.pyplot as plt def mscatter(x,y,ax=None, m=None, **kw): import matplotlib.markers as mmarkers ax = ax or plt.gca() sc = ax.scatter(x,y,**kw) if (m is not None) and (len(m)==len(x)): paths = [] for marker in m: if isinstance(marker, mmarkers.MarkerStyle): marker_obj = marker else: marker_obj = mmarkers.MarkerStyle(marker) path = marker_obj.get_path().transformed( marker_obj.get_transform()) paths.append(path) sc.set_paths(paths) return sc markers = ['s', 'o', '^'] records = [] for n in range(37): records.append([np.random.normal(), np.random.normal(), np.random.normal(), markers[np.random.randint(0, high=3)]]) records = pd.DataFrame(records, columns=['x', 'y', 'z', 'marker']) fig, ax = plt.subplots() sc = mscatter(records.x, records.y, c=records.z, m=records.marker, ax=ax) fig.colorbar(sc, ax=ax) plt.show() 

introduzca la descripción de la imagen aquí