2012-07-22 12 views
10

Sto provando a creare un grafico a barre raggruppato in matplotlib, seguendo l'esempio nella galleria. Io uso il seguente:impostazione della spaziatura tra i grafici a barre raggruppati in matplotlib

import matplotlib.pyplot as plt 
plt.figure(figsize=(7,7), dpi=300) 
xticks = [0.1, 1.1] 
groups = [[1.04, 0.96], 
      [1.69, 4.02]] 
group_labels = ["G1", "G2"] 
num_items = len(group_labels) 
ind = arange(num_items) 
width = 0.1 
s = plt.subplot(1,1,1) 
for num, vals in enumerate(groups): 
    print "plotting: ", vals 
    group_len = len(vals) 
    gene_rects = plt.bar(ind, vals, width, 
         align="center") 
    ind = ind + width 
num_groups = len(group_labels) 
# Make label centered with respect to group of bars 
# Is there a less complicated way? 
offset = (num_groups/2.) * width 
xticks = arange(num_groups) + offset 
s.set_xticks(xticks) 
print "xticks: ", xticks 
plt.xlim([0 - width, max(xticks) + (num_groups * width)]) 
s.set_xticklabels(group_labels) 

enter image description here

Le mie domande sono:

  1. Come posso controllare lo spazio tra i gruppi di barre? In questo momento la spaziatura è enorme e sembra sciocca. Nota che non voglio allargare le barre: voglio che abbiano la stessa larghezza, ma che siano più vicine.

  2. Come posso ottenere che le etichette siano centrate sotto i gruppi di barre? Ho cercato di elaborare alcuni calcoli aritmetici per posizionare gli xlabels nel posto giusto (vedi il codice sopra) ma è ancora leggermente fuori ... sembra un po 'come scrivere una libreria di stampa piuttosto che usarne uno. Come si puo aggiustare? (C'è un involucro o costruito nel programma di utilità per matplotlib in cui questo è il comportamento predefinito?)

EDIT: Rispondi a @mlgill: la ringrazio per la risposta. Il tuo codice è sicuramente molto più elegante ma ha ancora lo stesso problema, ovvero che la larghezza delle barre e la spaziatura tra i gruppi non sono controllate separatamente. Il tuo grafico sembra corretto ma le barre sono troppo larghe - sembra un grafico Excel - e volevo rendere la barra più sottile.

larghezza e il margine sono ora collegati, quindi se provo:

margin = 0.60 
width = (1.-2.*margin)/num_items 

rende la barra di magro, ma porta il gruppo distanti tra loro, in modo ancora una volta la trama non sembra giusto.

Come posso creare una funzione di grafico a barre raggruppata che impiega due parametri: la larghezza di ogni barra e la spaziatura tra i gruppi di barre e la stampa correttamente come il tuo codice, cioè con le etichette dell'asse x centrate sotto i gruppi?

Penso che da quando l'utente deve calcolare quantità specifiche di layout a basso livello come il margine e la larghezza, stiamo ancora fondamentalmente scrivendo una biblioteca tramando :)

risposta

15

Il trucco per entrambe le domande è la comprensione che i grafici a barre in Matplotlib si aspetta che ciascuna serie (G1, G2) abbia una larghezza totale di "1.0", contando i margini su entrambi i lati. Pertanto, è probabilmente più semplice impostare i margini e calcolare la larghezza di ciascuna barra in base a quanti di essi esistono per serie. Nel tuo caso, ci sono due barre per serie.

Supponendo di aver allineato a sinistra ogni barra, invece di allineare il centro come avevate fatto, questa configurazione produrrà serie che vanno da 0,0 a 1,0, da 1,0 a 2,0 e così via sull'asse x. Così, il centro esatto di ogni serie, che è dove si desidera visualizzare le etichette, sarà a 0.5, 1.5, ecc

ho ripulito il codice come c'erano un sacco di variabili estranee. Vedi i commenti all'interno.

import matplotlib.pyplot as plt 
import numpy as np 

plt.figure(figsize=(7,7), dpi=300) 

groups = [[1.04, 0.96], 
      [1.69, 4.02]] 
group_labels = ["G1", "G2"] 
num_items = len(group_labels) 
# This needs to be a numpy range for xdata calculations 
# to work. 
ind = np.arange(num_items) 

# Bar graphs expect a total width of "1.0" per group 
# Thus, you should make the sum of the two margins 
# plus the sum of the width for each entry equal 1.0. 
# One way of doing that is shown below. You can make 
# The margins smaller if they're still too big. 
margin = 0.05 
width = (1.-2.*margin)/num_items 

s = plt.subplot(1,1,1) 
for num, vals in enumerate(groups): 
    print "plotting: ", vals 
    # The position of the xdata must be calculated for each of the two data series 
    xdata = ind+margin+(num*width) 
    # Removing the "align=center" feature will left align graphs, which is what 
    # this method of calculating positions assumes 
    gene_rects = plt.bar(xdata, vals, width) 


# You should no longer need to manually set the plot limit since everything 
# is scaled to one. 
# Also the ticks should be much simpler now that each group of bars extends from 
# 0.0 to 1.0, 1.0 to 2.0, and so forth and, thus, are centered at 0.5, 1.5, etc. 
s.set_xticks(ind+0.5) 
s.set_xticklabels(group_labels) 

Output from my code.

+0

Inoltre, si noti il ​​numero notevolmente ridotto di comandi, una volta che i miei commenti sono stati rimossi. Mentre penso che la funzione di tracciamento delle barre di Matplotlib potrebbe usare miglioramenti minori in qualche modo, non è certamente più come scrivere una libreria di stampa. :) –

+0

grazie per il tuo commento, ti ho risposto in una modifica al mio post principale. – user248237dfsf

+0

Da quanto hai scritto sopra, sembra che tu voglia o meno la larghezza dell'intero grafico (questo può essere impostato nella linea che crea la figura) o vuoi che i margini stessi siano più grandi, il che manterrà la proporzioni uguali. È anche possibile regolare la larghezza e i calcoli xdata in modo che vi sia un margine tra ciascuna barra. Per fare ciò è necessaria solo l'algebra di base. Al di là di queste tre idee, non ho idea di cosa stai chiedendo. –

2

Ho letto una risposta che Paul Ivanov postato su Nabble che potrebbe risolvere questo problema con minore complessità. Basta impostare l'indice come di seguito.Ciò aumenterà la spaziatura tra le colonne raggruppate.

ind = np.arange(0,12,2) 
9

realtà io che questo problema è risolto migliore regolando figsize e width; ecco la mia uscita con figsize=(2,7) e width=0.3:

enter image description here

Tra l'altro, questo tipo di cose diventa molto più semplice se si utilizza pandas wrapper (ho anche importato seaborn, non è necessaria per la soluzione , ma rende la trama molto più bella e più moderno alla ricerca a mio parere):

import pandas as pd   
import seaborn 
seaborn.set() 

df = pd.DataFrame(groups, index = group_labels) 
df.plot(kind='bar', legend = False, width = .8, figsize = (2,5)) 
plt.show() 

enter image description here

Problemi correlati