establecer el espaciado entre plots de barras agrupadas en matplotlib

Estoy tratando de hacer un gráfico de barras agrupadas en matplotlib, siguiendo el ejemplo en la galería. Yo uso lo siguiente:

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) 

introduzca la descripción de la imagen aquí

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 hacer las barras más anchas, quiero que tengan el mismo ancho, pero que estén más juntas.

  2. ¿Cómo puedo conseguir que las tags se centren debajo de los grupos de barras? Intenté realizar algunos cálculos aritméticos para colocar los xlabels en el lugar correcto (ver código anterior), pero todavía está un poco apagado … se siente un poco como escribir una biblioteca de ploteo en lugar de usar uno. ¿Cómo se puede arreglar esto? (¿Existe un envoltorio o una utilidad integrada para matplotlib donde este es el comportamiento predeterminado?)

EDITAR: Responder a @mlgill: gracias por su respuesta. Su código es ciertamente mucho más elegante, pero sigue teniendo el mismo problema, a saber, que el ancho de las barras y el espacio entre los grupos no se controlan por separado. Su gráfico se ve correcto, pero las barras son demasiado anchas (parece un gráfico de Excel) y quería que la barra fuera más delgada.

El ancho y el margen ahora están vinculados, así que si lo bash:

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

Hace que la barra sea más delgada, pero aleja al grupo, por lo que la ttwig de nuevo no se ve bien.

¿Cómo puedo realizar una función de gráfico de barras agrupadas que tome dos parámetros: el ancho de cada barra y el espaciado entre los grupos de barras, y los grafica correctamente como lo hizo su código, es decir, con las tags del eje x centradas debajo de los grupos?

Creo que dado que el usuario tiene que calcular cantidades de diseño de bajo nivel específicas como el margen y el ancho, básicamente estamos escribiendo una biblioteca de trazado 🙂

El truco para ambas preguntas es comprender que los gráficos de barras en Matplotlib esperan que cada serie (G1, G2) tenga un ancho total de “1.0”, contando los márgenes en 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 su caso, hay dos barras por serie.

Suponiendo que alinea cada barra a la izquierda, en lugar de alinearlas en el centro como lo había hecho, esta configuración resultará en una serie que abarca 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 tags, estará en 0.5, 1.5, etc.

He limpiado tu código ya que había muchas variables extrañas. Ver comentarios en el interior.

 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) 

Salida de mi código.

En realidad, creo que este problema se resuelve mejor ajustando el width y la figsize ; Aquí está mi salida con figsize=(2,7) y width=0.3 :

introduzca la descripción de la imagen aquí

Por cierto, este tipo de cosas se vuelve mucho más simple si utiliza envoltorios de pandas (también he importado seaborn , no es necesario para la solución, pero hace que la ttwig sea mucho más bonita y moderna 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=0.8, figsize=(2,5)) plt.show() 

introduzca la descripción de la imagen aquí

Leí una respuesta que Paul Ivanov publicó en Nabble que podría resolver este problema con menos complejidad. Solo establece el índice como se muestra abajo. Esto boostá el espacio entre las columnas agrupadas.

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