2013-04-28 17 views
7

In this example il colore è correlato al raggio di ciascuna barra. Come si potrebbe aggiungere una barra di colore a questa trama?Come aggiungere una barra di colore a questo esempio?

matplotlib example

miei imita codice un "rose diagramma" proiezione che è essenzialmente un grafico a barre su una proiezione polare.

qui è una parte di esso:

angle = radians(10.) 
patches = radians(360.)/angle 
theta = np.arange(0,radians(360.),angle) 
count = [0]*patches 
for i, item in enumerate(some_array_of_azimuth_directions): 
    temp = int((item - item%angle)/angle) 
    count[temp] += 1 
width = angle * np.ones(patches) 

# force square figure and square axes looks better for polar, IMO 
fig = plt.figure(figsize=(8,8)) 
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True) 

rmax = max(count) + 1 

ax.set_rlim(0,rmax) 
ax.set_theta_offset(np.pi/2) 
ax.set_thetagrids(np.arange(0,360,10)) 
ax.set_theta_direction(-1) 

# project strike distribution as histogram bars 
bars = ax.bar(theta, count, width=width) 
r_values = [] 
colors = [] 
for r,bar in zip(count, bars): 
    r_values.append(r/float(max(count))) 
    colors.append(cm.jet(r_values[-1], alpha=0.5)) 
    bar.set_facecolor(colors[-1]) 
    bar.set_edgecolor('grey') 
    bar.set_alpha(0.5) 

# Add colorbar, make sure to specify tick locations to match desired ticklabels 
colorlist = [] 
r_values.sort() 
values = [] 
for val in r_values: 
    if val not in values: 
     values.append(val*float(max(count))) 

    color = cm.jet(val, alpha=0.5) 
    if color not in colorlist: 
     colorlist.append(color) 

cpt = mpl.colors.ListedColormap(colorlist) 
bounds = range(max(count)+1) 
norm = mpl.colors.BoundaryNorm(values, cpt.N-1) 

cax = fig.add_axes([0.97, 0.3, 0.03, 0.4]) 
cb = mpl.colorbar.ColorbarBase(cax, cmap=cpt, 
            norm=norm, 
            boundaries=bounds, 
            # Make the length of each extension 
            # the same as the length of the 
            # interior colors: 
            extendfrac='auto', 
            ticks=[bounds[i] for i in range(0, len(bounds), 2)], 
            #ticks=bounds, 
            spacing='uniform') 

e qui è la trama risultante: enter image description here

come si può vedere, la colorbar non è giusto. Ho giocato così tanto con il codice e non riesco proprio a capire come normalizzare correttamente la barra di colore.

+0

Cosa c'è di sbagliato in questo? – tacaswell

+0

se si guarda da vicino, tra 16 e 17 manca un colore (arancione più scuro) e in base alla barra di colore i gialli raggiungono un valore di 15 che non è vero nel diagramma rosa (o nei dati). – Shahar

risposta

11

Il modo più semplice è utilizzare uno PatchCollection e passare la "z" (ovvero i valori che si desidera colorare con) come il kwarg array.

Come semplice esempio:

import itertools 
import matplotlib.pyplot as plt 
from matplotlib.patches import Rectangle 
from matplotlib.collections import PatchCollection 
import numpy as np 

def main(): 
    fig = plt.figure() 
    ax = fig.add_subplot(111, projection='polar') 
    x = np.radians(np.arange(0, 360, 10)) 
    y = np.random.random(x.size) 
    z = np.random.random(y.size) 
    cmap = plt.get_cmap('cool') 
    coll = colored_bar(x, y, z, ax=ax, width=np.radians(10), cmap=cmap) 
    fig.colorbar(coll) 
    ax.set_yticks([0.5, 1.0]) 
    plt.show() 

def colored_bar(left, height, z=None, width=0.8, bottom=0, ax=None, **kwargs): 
    if ax is None: 
     ax = plt.gca() 
    width = itertools.cycle(np.atleast_1d(width)) 
    bottom = itertools.cycle(np.atleast_1d(bottom)) 
    rects = [] 
    for x, y, h, w in zip(left, bottom, height, width): 
     rects.append(Rectangle((x,y), w, h)) 
    coll = PatchCollection(rects, array=z, **kwargs) 
    ax.add_collection(coll) 
    ax.autoscale() 
    return coll 

if __name__ == '__main__': 
    main() 

enter image description here

Se si desidera una mappa dei colori discreta, è più facile per specificare solo il numero di intervalli vuoi quando si chiama plt.get_cmap. Ad esempio, nel codice di cui sopra, se si sostituisce la linea cmap = plt.get_cmap('cool') con:

cmap = plt.get_cmap('cool', 5) 

Poi si otterrà un colormap discreta con intervalli di 5. (. In alternativa, si potrebbe passare nel ListedColormap che si è creato nel tuo esempio)

enter image description here

Se si desidera una funzione di schema "full-optional" rosa, si potrebbe fare qualcosa di simile:

import itertools 
import matplotlib.pyplot as plt 
from matplotlib.patches import Rectangle 
from matplotlib.collections import PatchCollection 
import numpy as np 

def main(): 
    azi = np.random.normal(20, 30, 100) 
    z = np.cos(np.radians(azi + 45)) 

    plt.figure(figsize=(5,6)) 
    plt.subplot(111, projection='polar') 
    coll = rose(azi, z=z, bidirectional=True) 
    plt.xticks(np.radians(range(0, 360, 45)), 
       ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']) 
    plt.colorbar(coll, orientation='horizontal') 
    plt.xlabel('A rose diagram colored by a second variable') 
    plt.rgrids(range(5, 20, 5), angle=290) 

    plt.show() 

def rose(azimuths, z=None, ax=None, bins=30, bidirectional=False, 
     color_by=np.mean, **kwargs): 
    """Create a "rose" diagram (a.k.a. circular histogram). 

    Parameters: 
    ----------- 
     azimuths: sequence of numbers 
      The observed azimuths in degrees. 
     z: sequence of numbers (optional) 
      A second, co-located variable to color the plotted rectangles by. 
     ax: a matplotlib Axes (optional) 
      The axes to plot on. Defaults to the current axes. 
     bins: int or sequence of numbers (optional) 
      The number of bins or a sequence of bin edges to use. 
     bidirectional: boolean (optional) 
      Whether or not to treat the observed azimuths as bi-directional 
      measurements (i.e. if True, 0 and 180 are identical). 
     color_by: function or string (optional) 
      A function to reduce the binned z values with. Alternately, if the 
      string "count" is passed in, the displayed bars will be colored by 
      their y-value (the number of azimuths measurements in that bin). 
     Additional keyword arguments are passed on to PatchCollection. 

    Returns: 
    -------- 
     A matplotlib PatchCollection 
    """ 
    azimuths = np.asanyarray(azimuths) 
    if color_by == 'count': 
     z = np.ones_like(azimuths) 
     color_by = np.sum 
    if ax is None: 
     ax = plt.gca() 
    ax.set_theta_direction(-1) 
    ax.set_theta_offset(np.radians(90)) 
    if bidirectional: 
     other = azimuths + 180 
     azimuths = np.concatenate([azimuths, other]) 
     if z is not None: 
      z = np.concatenate([z, z]) 
    # Convert to 0-360, in case negative or >360 azimuths are passed in. 
    azimuths[azimuths > 360] -= 360 
    azimuths[azimuths < 0] += 360 
    counts, edges = np.histogram(azimuths, range=[0, 360], bins=bins) 
    if z is not None: 
     idx = np.digitize(azimuths, edges) 
     z = np.array([color_by(z[idx == i]) for i in range(1, idx.max() + 1)]) 
     z = np.ma.masked_invalid(z) 
    edges = np.radians(edges) 
    coll = colored_bar(edges[:-1], counts, z=z, width=np.diff(edges), 
         ax=ax, **kwargs) 
    return coll 

def colored_bar(left, height, z=None, width=0.8, bottom=0, ax=None, **kwargs): 
    """A bar plot colored by a scalar sequence.""" 
    if ax is None: 
     ax = plt.gca() 
    width = itertools.cycle(np.atleast_1d(width)) 
    bottom = itertools.cycle(np.atleast_1d(bottom)) 
    rects = [] 
    for x, y, h, w in zip(left, bottom, height, width): 
     rects.append(Rectangle((x,y), w, h)) 
    coll = PatchCollection(rects, array=z, **kwargs) 
    ax.add_collection(coll) 
    ax.autoscale() 
    return coll 

if __name__ == '__main__': 
    main() 

enter image description here

+0

grazie Joe. Tuttavia, vorrei mantenere lo stile dei colori discreti piuttosto il colore continuo prodotto dall'esempio. – Shahar

+0

È possibile passare in una mappa colori discreta. Ad esempio: 'cmap = plt.get_cmap ('jet', 10)' darebbe una mappa dei colori con 10 segmenti discreti. –

Problemi correlati