2012-03-08 24 views
11

ho inciampato attraversato il seguente frammento, per la creazione di grafico a barre orizzontali usando matplotlib:matplotlib - la creazione di grafico a barre orizzontali

import matplotlib 
from pylab import * 

val = 3+10*rand(5) # the bar lengths 
pos = arange(5)+.5 # the bar centers on the y axis 
print pos 
figure(1) 
barh(pos,val, align='center') 
yticks(pos, ('Tom', 'Dick', 'Harry', 'Slim', 'Jim')) 
xlabel('Performance') 
title('horizontal bar chart using matplotlib') 
grid(True) 
show() 

voglio modificare lo script di cui sopra come segue:

  1. Marchio le barre tracciate "meno pesanti" (ovvero riducono l'altezza delle barre di horiz tracciate)
  2. Riportate i numeri negativi e positivi come barre orizzontali sullo stesso grafico

qualsiasi aiuto (snippet di codice o collegamenti) per aiutarmi a rendere le modifiche di cui sopra sarebbe molto utile.

a parte, se volessi creare barre orizzontali impilate (ad esempio ogni etichetta aveva 3 barre orizzontali impilate), come modificherei il codice di cui sopra per tracciare un grafico a barre orizzontali a 3 colonne?

[[Edit]]

Qualcuno potrebbe inviare due brevi frammento di codice che mostra come:

  1. Stampa etichette sul lato opposto delle barre orizzontali (in modo che, ad esempio, l'etichetta per le barre "negative" viene visualizzata nel primo quarto e le etichette per le barre "positive" vengono visualizzate nel secondo quadrante

  2. Plot multiple (ad esempio 2 o 3) barre orizzontali (invece di una sola). amples sono il first two images shown here

risposta

21
import matplotlib 
from pylab import * 

val = 3-6*rand(5) # the bar lengths  # changed your data slightly 
pos = arange(5)+.5 # the bar centers on the y axis 
print pos 
figure(1) 
barh(pos,val, align='center',height=0.1) # notice the 'height' argument 
yticks(pos, ('Tom', 'Dick', 'Harry', 'Slim', 'Jim')) 

gca().axvline(0,color='k',lw=3) # poor man's zero level 

xlabel('Performance') 
title('horizontal bar chart using matplotlib') 
grid(True) 
show() 

In generale, io suggerirei di non usare from pyplot import *. A meno che non siete in modalità interattiva, utilizzare l'oggetto approccio orientato:

import matplotlib.pyplot as plt 
from numpy.random import rand 
from numpy import arange 

val = 3-6*rand(5) # the bar lengths 
pos = arange(5)+.5 # the bar centers on the y axis 
print pos 

fig = plt.figure() 
ax = fig.add_subplot(111) 
ax.barh(pos,val, align='center',height=0.1) 
ax.set_yticks(pos, ('Tom', 'Dick', 'Harry', 'Slim', 'Jim')) 

ax.axvline(0,color='k',lw=3) # poor man's zero level 

ax.set_xlabel('Performance') 
ax.set_title('horizontal bar chart using matplotlib') 
ax.grid(True) 
plt.show() 

Un buon punto di partenza per i vari tipi di grafici è la matplotlib gallery

+0

Grazie, questo è quello che sto cercando di fare. Una cosa che ho notato è che le etichette (asse Y) mancano sulla trama ?. Per le barre orizzontali negative, voglio le etichette nel 1 ° quadrante, e per le barre orizzontali positive, voglio le etichette nel 2 ° quadrante. –

+0

Per quanto ne so, non può essere fatto automaticamente. Tuttavia, non è difficile farlo automaticamente, usando 'text' o' annotate'. –

8

Come detto da Zhenya, si dovrà tweek tua tracciare.

Come esempio, di seguito è una funzione che produce una personalizzazione barra orizzontale terreno:

  • l'ingresso è i dati, racchiusi in un dizionario
  • poi calcola la posizione Y zecche secondo di il numero di misure (bar) che hai in ogni categoria (persone) e lo spazio che vuoi mettere tra ogni categoria.
  • infine trame ciascuna della misura di dati (con un colore diverso se è stato specificato che)

Per impostazione predefinita, verrà tracciare il nome delle categorie (persone) a destra, ma si può di Certo che cambia.

import numpy as np 
import matplotlib.pyplot as plt 

# creation of the data 
name_list = ['day1', 'day2', 'day3', 'day4'] 
data = {name: 3+10*np.random.rand(5) for name in name_list} 

colors_list = ['0.5', 'r', 'b', 'g'] #optional 

def customize_barh(data, width_bar=1, width_space=0.5, colors=None): 
    n_measure = len(data)     #number of measure per people 
    n_people = data[data.keys()[0]].size # number of people 

    #some calculation to determine the position of Y ticks labels 
    total_space = n_people*(n_measure*width_bar)+(n_people-1)*width_space 
    ind_space = n_measure*width_bar 
    step = ind_space/2. 
    pos = np.arange(step, total_space+width_space, ind_space+width_space) 

    # create the figure and the axes to plot the data 
    fig = plt.figure(figsize=(8,6)) 
    ax = fig.add_axes([0.15, 0.15, 0.65, 0.7]) 

    # remove top and right spines and turn ticks off if no spine 
    ax.spines['right'].set_color('none') 
    ax.spines['top'].set_color('none') 
    ax.xaxis.set_ticks_position('bottom') 
    ax.yaxis.set_ticks_position('right') # ticks position on the right 
    # postition of tick out 
    ax.tick_params(axis='both', direction='out', width=3, length=6, 
        labelsize=24, pad=8) 
    ax.spines['left'].set_linewidth(3) 
    ax.spines['bottom'].set_linewidth(3) 

    # plot the data 
    for i,day in enumerate(data.keys()): 
     if colors == None: 
      ax.barh(pos-step+i*width_bar, data[day], width_bar, #facecolor='0.4', 
        edgecolor='k', linewidth=3) 
     else: 
      ax.barh(pos-step+i*width_bar, data[day], width_bar, facecolor=colors[i], 
        edgecolor='k', linewidth=3) 


    ax.set_yticks(pos) 
    # you may want to use the list of name as argument of the function to be more 
    # flexible (if you have to add a people) 
    ax.set_yticklabels(('Tom', 'Dick', 'Harry', 'Slim', 'Jim'))   
    ax.set_ylim((-width_space, total_space+width_space)) 
    ax.set_xlabel('Performance', size=26, labelpad=10) 

customize_barh(data, colors=colors_list) 
plt.savefig('perf.png') 
plt.show() 

che produce: this

+0

Grazie per lo snippet di codice. Un po 'più complicato di quanto avrei pensato. Ancora .., questo è quasi il 2 ° parte fatto completamente. Leggerò la funzione 'customize_barh' per assicurarmi di capire che cosa sta succedendo ... Inoltre, ho ragione nel presumere che se voglio visualizzare una legenda per la trama sopra, aggiungo semplicemente l'istruzione plt.legend() . Ultimo ma non meno importante, potresti pubblicare uno snippet che mostra come tracciare un grafico come descritto nella prima parte? (ad esempio, le barre negative hanno etichette nel 1 ° quadrante ecc.). Non mi è ancora chiaro come ottenerlo con matplotlib. –

+0

Inoltre, come faccio a tracciare le etichette per il grafico a barre orizzontale personalizzato sulla sinistra, invece del diritto predefinito ?. Grazie –

+0

@HomunculusReticulli, la funzione era solo un esempio di cosa puoi fare. Per una "trama unica", prendi semplicemente i pezzi di codice che ti interessano all'interno della funzione. L'ax.yaxis.set_ticks_position ('right') 'è la linea che vuoi cambiare per avere i ticks a sinistra (cambialo a' left'). Per quanto riguarda ciò che si vuole ottenere, giocare con 'ax.text' dovrebbe essere più semplice invece di cercare di capire come alternare la posizione delle zecche rispetto ai valori (positivi o negativi). Per la legenda, qualcosa come 'plt.legend (['d1', 'd2', 'd3', 'd4'])' funzionerà. – gcalmettes

1

I seguenti codici snippet è un esempio di utilizzare la funzione testo di annotare etichetta di testo sul lato sinistro per valori negativi e destra per i valori positivi come menzionato da entrambe le gcalmette e Zhenya.

from pylab import setp 
import numpy as np 
import matplotlib.pyplot as plt 
import math 

# creation of the data 
name_list = ['day1', 'day2', 'day3', 'day4'] 
data = {name: 3+10*np.random.rand(5) for name in name_list} 

for name in name_list: 
    data[name][0] = data[name][0]*-1 
    data[name][2] = data[name][2]*-1 

colors_list = ['0.5', 'r', 'b', 'g'] #optional 

def customize_barh(data, width_bar=1, width_space=0.5, colors=None): 
    n_measure = len(data)     #number of measure per people 
    n_people = data[data.keys()[0]].size # number of people 

    #some calculation to determine the position of Y ticks labels 
    total_space = n_people*(n_measure*width_bar)+(n_people-1)*width_space 
    ind_space = n_measure*width_bar 
    step = ind_space/2. 
    pos = np.arange(step, total_space+width_space, ind_space+width_space) 
    # create the figure and the axes to plot the data 
    fig = plt.figure(figsize=(8,6)) 
    ax = fig.add_axes([0.15, 0.15, 0.65, 0.7]) 

    # remove top and right spines and turn ticks off if no spine 
    ax.spines['right'].set_color('none') 
    ax.spines['top'].set_color('none') 
    ax.xaxis.set_ticks_position('bottom') 
    ax.yaxis.set_ticks_position('default') # ticks position on the right 
    # postition of tick out 
    ax.tick_params(axis='both', direction='out', width=3, length=6, 
        labelsize=24, pad=8) 
    ax.spines['left'].set_linewidth(3) 
    ax.spines['bottom'].set_linewidth(3) 

    # plot the data 
    for i,day in enumerate(data.keys()): 
     if colors == None: 
      ax.barh(pos-step+i*width_bar, data[day], width_bar, #facecolor='0.4', 
        edgecolor='k', linewidth=3) 
     else: 
      ax.barh(pos-step+i*width_bar, data[day], width_bar, facecolor=colors[i], 
        edgecolor='k', linewidth=3) 


    ax.set_yticks(pos) 
    # you may want to use the list of name as argument of the function to be more 
    # flexible (if you have to add a people) 
    setp(ax.get_yticklabels(), visible=False)   
    ax.set_ylim((-width_space, total_space+width_space)) 
    ax.set_xlabel('Performance', size=26, labelpad=10) 
    labels_list = ['Tom', 'Dick', 'Harry', 'Slim','Jim'] 

    # creation of an array of positive/negative values (based on the values 
    # of the data) that will be used as x values for adding text as side labels 
    side_list = [] 
    for index in range(len(labels_list)): 
     sum = 0 
     for name in name_list: 
      sum+= data[name][index] 
     if math.copysign(1,sum) > 0: 
      side_list.append(16) 
     else: 
      side_list.append(-21) 
    for label in labels_list: 
     plt.text(side_list[labels_list.index(label)], pos[labels_list.index(label)]-0.5, label,fontsize=26) 
customize_barh(data, colors=colors_list) 
plt.savefig('perf.png') 
plt.show() 

Funziona sulla base del fatto che i tutti i bar per una data persona devono essere positivo o negativo per il testo da essere annotato sul lato corretto. Per cambiare questo comportamento basta cambiare la generazione di side_list.

E.g Se si desidera che una determinata soglia della barra determini la posizione dell'etichetta, contare i valori dei dati oltre tale soglia anziché sommare i valori per un determinato nome.

Es Per soglia di 3 bar su tuttavia molti, il ciclo diventa

for index in range(len(labels_list)): 
     count = 0 
      for name in name_list: 
       if data[name][index] > 0: 
        count+= 1 
      if count > 3: 
       side_list.append(16) 
      else: 
       side_list.append(-21) 

La generazione di side_list dovranno anche essere modificato per regolare l'intervallo di dati, come gli esempi riportati uso dati casuali in un intervallo specificato.

E.g È necessario regolare gli offset di etichetta di side_list.append(16) e side_list.append(-21) in base ai propri dati.

Problemi correlati