2012-07-22 105 views
10

Estoy tratando de hacer un gráfico de barras agrupadas en matplotlib, siguiendo el ejemplo en la galería. Yo uso los siguientes:estableciendo el espaciado entre gráficos de barras agrupadas en 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

Mis preguntas son:

  1. ¿Cómo puedo controlar el espacio entre los grupos de barras? En este momento, el espacio es enorme y parece tonto. Tenga en cuenta que no quiero que las barras sean más anchas, quiero que tengan el mismo ancho, pero estén más juntas.

  2. ¿Cómo puedo hacer que las etiquetas se centren debajo de los grupos de barras? Traté de hacer algunos cálculos aritméticos para colocar las xlabels en el lugar correcto (ver el código anterior) pero todavía está un poco apagado ... se siente como escribir una biblioteca de trazado en lugar de usar una. ¿Cómo se puede arreglar esto? (¿Hay un envoltorio o construido en la utilidad para matplotlib donde esto es el comportamiento por defecto?)

EDIT: Responder a @mlgill: gracias por su respuesta. Su código es ciertamente mucho más elegante, pero aún tiene el mismo problema, es decir, que el ancho de las barras y el espaciado entre los grupos no se controlan por separado. Su gráfica se ve correcta, pero las barras son demasiado anchas, parece un gráfico de Excel, y quería que la barra se adelgazara.

Ancho y el margen ahora están vinculados, por lo que si trato:

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

Esto hace que la barra más delgada, pero trae el grupo muy separados, por lo que la trama de nuevo no se ve bien.

Cómo puedo hacer una función de diagrama de barras agrupadas que toma dos parámetros: el ancho de cada barra y el espacio entre los grupos de barras, y traza correctamente como lo hizo tu código, es decir, con las etiquetas del eje x centradas debajo ¿los grupos?

creo que ya que el usuario tiene que calcular las cantidades específicas de diseño de bajo nivel como el margen y el ancho, todavía estamos escribiendo básicamente una biblioteca de trazado :)

Respuesta

15

El truco para ambos de sus preguntas es entender que los gráficos de barra en Matplotlib, se espera que cada serie (G1, G2) tenga un ancho total de "1.0", contando los márgenes de cada lado. Por lo tanto, probablemente sea más fácil establecer los márgenes y luego calcular el ancho de cada barra, dependiendo de cuántos de ellos haya por serie. En tu caso, hay dos barras por serie.

Asumiendo que dejó alinear cada barra, en lugar de alinearlas en el centro como lo hizo, esta configuración dará como resultado series que abarcan desde 0.0 a 1.0, 1.0 a 2.0, y así sucesivamente en el eje x. Por lo tanto, el centro exacto de cada serie, que es donde desea que aparezcan sus etiquetas, estará en 0.5, 1.5, etc.

He limpiado su código ya que había muchas variables extrañas. Ver comentarios dentro.

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

Además, tenga en cuenta el número muy reducido de comandos, una vez que se hayan eliminado mis comentarios. Si bien creo que la función de trazado de barras de Matplotlib podría usar mejoras menores en algunos aspectos, esto ya no es como escribir una biblioteca de trazado. :) –

+0

gracias por tu comentario, te respondí en una edición de mi publicación principal. – user248237dfsf

+0

Según lo que has escrito arriba, parece que quieres que el ancho del gráfico entero sea más pequeño (esto se puede establecer en la línea que crea la figura) o quieres que los márgenes sean más grandes, lo que mantendrá el relación de aspecto igual. También puede ajustar el ancho y los cálculos xdata para que haya un margen entre cada una de las barras. Lograr esto requiere solo álgebra básica. Más allá de estas tres ideas, no tengo idea de lo que estás preguntando. –

2

leí una respuesta que Paul Ivanov publicada el Nabble que podrían resolver este problema con menos complejidad. Simplemente configure el índice como se muestra a continuación.Esto aumentará el espacio entre las columnas agrupadas.

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

En realidad creo que este problema es resuelto mediante el ajuste mejor figsize y width; aquí está mi salida con figsize=(2,7) y width=0.3:

enter image description here

Por cierto, este tipo de cosas se convierte en una gran cantidad más simple si se utiliza pandas envolturas (yo también he importado seaborn, no necesario para la solución , pero hace que la trama mucho más bonito y más moderno en mi opinión):

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

Cuestiones relacionadas