Python matplotlib múltiples barras

92

Cómo trazar varias barras en matplotlib, cuando intenté llamar a la función de la barra varias veces, se superponen y, como se ve en la siguiente figura, solo se puede ver el valor más alto en rojo. ¿Cómo puedo trazar las múltiples barras con fechas en los ejes x?

Hasta ahora, probé esto:

import matplotlib.pyplot as plt
import datetime

x = [
    datetime.datetime(2011, 1, 4, 0, 0),
    datetime.datetime(2011, 1, 5, 0, 0),
    datetime.datetime(2011, 1, 6, 0, 0)
]
y = [4, 9, 2]
z = [1, 2, 3]
k = [11, 12, 13]

ax = plt.subplot(111)
ax.bar(x, y, width=0.5, color='b', align='center')
ax.bar(x, z, width=0.5, color='g', align='center')
ax.bar(x, k, width=0.5, color='r', align='center')
ax.xaxis_date()

plt.show()

Tengo esto:

ingrese la descripción de la imagen aquí

Los resultados deberían ser algo así, pero con las fechas en los ejes xy las barras están una al lado de la otra:

ingrese la descripción de la imagen aquí

John Smith
fuente
necesita cambiar los valores x
jterrace
2
Qué quieres decir ? Los valores X son fechas ...
John Smith
4
¿Por qué esto no es simplemente compatible con matplotlib?
ihadanny

Respuestas:

115
import matplotlib.pyplot as plt
from matplotlib.dates import date2num
import datetime

x = [
    datetime.datetime(2011, 1, 4, 0, 0),
    datetime.datetime(2011, 1, 5, 0, 0),
    datetime.datetime(2011, 1, 6, 0, 0)
]
x = date2num(x)

y = [4, 9, 2]
z = [1, 2, 3]
k = [11, 12, 13]

ax = plt.subplot(111)
ax.bar(x-0.2, y, width=0.2, color='b', align='center')
ax.bar(x, z, width=0.2, color='g', align='center')
ax.bar(x+0.2, k, width=0.2, color='r', align='center')
ax.xaxis_date()

plt.show()

ingrese la descripción de la imagen aquí

No sé qué significa "los valores y también se superponen", ¿el siguiente código resuelve su problema?

ax = plt.subplot(111)
w = 0.3
ax.bar(x-w, y, width=w, color='b', align='center')
ax.bar(x, z, width=w, color='g', align='center')
ax.bar(x+w, k, width=w, color='r', align='center')
ax.xaxis_date()
ax.autoscale(tight=True)

plt.show()

ingrese la descripción de la imagen aquí

HYRY
fuente
Gracias, pero si tengo 3 barras, se ve bien. Cuando pruebo 40 compases, se estropea. ¿Puede actualizar su solución para que sea más escalable?
John Smith
¿Definir "lío"? La superposición de etiquetas X se puede arreglar usando autofmt_xdate(), que gira automáticamente las etiquetas.
John Lyon
El problema no es que se superpongan las etiquetas X, sino que los valores de y también se superponen. Como arreglarlo ?
John Smith
y también el ancho = 0,2 es demasiado pequeño para un período de tiempo grande. Si utilizo valores más grandes, no obtengo el mismo resultado.
John Smith
Otra cosa es eso, los espacios al principio y al final. Cómo eliminar los espacios y comenzar directamente la primera cita y de igual manera terminar la última fecha sin ningún espacio o menos espacio.
John Smith
61

El problema de usar fechas como valores x es que si desea un gráfico de barras como en su segunda imagen, se equivocarán. Debes usar un gráfico de barras apiladas (colores uno encima del otro) o agrupar por fecha (una fecha "falsa" en el eje x, básicamente agrupando los puntos de datos).

import numpy as np
import matplotlib.pyplot as plt

N = 3
ind = np.arange(N)  # the x locations for the groups
width = 0.27       # the width of the bars

fig = plt.figure()
ax = fig.add_subplot(111)

yvals = [4, 9, 2]
rects1 = ax.bar(ind, yvals, width, color='r')
zvals = [1,2,3]
rects2 = ax.bar(ind+width, zvals, width, color='g')
kvals = [11,12,13]
rects3 = ax.bar(ind+width*2, kvals, width, color='b')

ax.set_ylabel('Scores')
ax.set_xticks(ind+width)
ax.set_xticklabels( ('2011-Jan-4', '2011-Jan-5', '2011-Jan-6') )
ax.legend( (rects1[0], rects2[0], rects3[0]), ('y', 'z', 'k') )

def autolabel(rects):
    for rect in rects:
        h = rect.get_height()
        ax.text(rect.get_x()+rect.get_width()/2., 1.05*h, '%d'%int(h),
                ha='center', va='bottom')

autolabel(rects1)
autolabel(rects2)
autolabel(rects3)

plt.show()

ingrese la descripción de la imagen aquí

John Lyon
fuente
si quiero mostrar como 100 días en los ejes x, ¿cómo los encajas?
John Smith
1
Desde aquí se puede generar las fechas requeridas con numpy de datetime64: ejemplo, un mes vale la pena: np.arange('2012-02', '2012-03', dtype='datetime64[D]'). Es posible que deba pensar más sobre la mejor manera de representar estos datos si tiene 40 conjuntos de datos (según otro comentario) que abarcan más de 100 días.
John Lyon
Y también, usar ax.xaxis_date () es muy ventajoso, porque encaja sus fechas en los ejes x.
John Smith
3
¿Por qué no lo intentas primero? Estoy tratando de ayudarte a aprender, no de escribir tu código por ti. Estoy seguro de que puede hacerlo, xaxis_datepero tendrá que adaptar lo que he escrito para compensar sus valores de fecha (por ejemplo, por un número de horas usando timedelta) para cada serie para evitar que se superpongan. La otra respuesta hace exactamente esto, pero es posible que deba limpiar las etiquetas después.
John Lyon
bien, pero cuando ejecuto np.arange ('2012-02', '2012-03, dtype =' datetime64 [D] '), obtengo esto: tipos de operandos no admitidos para -:' str 'y' str '
John Smith
25

Sé que se trata matplotlib, pero usar pandasy seabornpuede ahorrarle mucho tiempo:

df = pd.DataFrame(zip(x*3, ["y"]*3+["z"]*3+["k"]*3, y+z+k), columns=["time", "kind", "data"])
plt.figure(figsize=(10, 6))
sns.barplot(x="time", hue="kind", y="data", data=df)
plt.show()

ingrese la descripción de la imagen aquí

liwt31
fuente
Gran respuesta, pero es algo incompleta debido al eje x. ¿Puedes hacerlo más presentable?
Spinor8
Supongo que también podrías hacer el suyo con pandas y matplotlib
Vicki B
18

después de buscar una solución similar y no encontrar nada lo suficientemente flexible, decidí escribir mi propia función para ella. Le permite tener tantas barras por grupo como desee y especificar tanto el ancho de un grupo como los anchos individuales de las barras dentro de los grupos.

Disfrutar:

from matplotlib import pyplot as plt


def bar_plot(ax, data, colors=None, total_width=0.8, single_width=1, legend=True):
    """Draws a bar plot with multiple bars per data point.

    Parameters
    ----------
    ax : matplotlib.pyplot.axis
        The axis we want to draw our plot on.

    data: dictionary
        A dictionary containing the data we want to plot. Keys are the names of the
        data, the items is a list of the values.

        Example:
        data = {
            "x":[1,2,3],
            "y":[1,2,3],
            "z":[1,2,3],
        }

    colors : array-like, optional
        A list of colors which are used for the bars. If None, the colors
        will be the standard matplotlib color cyle. (default: None)

    total_width : float, optional, default: 0.8
        The width of a bar group. 0.8 means that 80% of the x-axis is covered
        by bars and 20% will be spaces between the bars.

    single_width: float, optional, default: 1
        The relative width of a single bar within a group. 1 means the bars
        will touch eachother within a group, values less than 1 will make
        these bars thinner.

    legend: bool, optional, default: True
        If this is set to true, a legend will be added to the axis.
    """

    # Check if colors where provided, otherwhise use the default color cycle
    if colors is None:
        colors = plt.rcParams['axes.prop_cycle'].by_key()['color']

    # Number of bars per group
    n_bars = len(data)

    # The width of a single bar
    bar_width = total_width / n_bars

    # List containing handles for the drawn bars, used for the legend
    bars = []

    # Iterate over all data
    for i, (name, values) in enumerate(data.items()):
        # The offset in x direction of that bar
        x_offset = (i - n_bars / 2) * bar_width + bar_width / 2

        # Draw a bar for every value of that type
        for x, y in enumerate(values):
            bar = ax.bar(x + x_offset, y, width=bar_width * single_width, color=colors[i % len(colors)])

        # Add a handle to the last drawn bar, which we'll need for the legend
        bars.append(bar[0])

    # Draw legend if we need
    if legend:
        ax.legend(bars, data.keys())


if __name__ == "__main__":
    # Usage example:
    data = {
        "a": [1, 2, 3, 2, 1],
        "b": [2, 3, 4, 3, 1],
        "c": [3, 2, 1, 4, 2],
        "d": [5, 9, 2, 1, 8],
        "e": [1, 3, 2, 2, 3],
        "f": [4, 3, 1, 1, 4],
    }

    fig, ax = plt.subplots()
    bar_plot(ax, data, total_width=.8, single_width=.9)
    plt.show()

Salida:

ingrese la descripción de la imagen aquí

pascscha
fuente
¿Cómo podemos modificar esto para agregar etiquetas al eje x? ¿Como en cada grupo de barras?
x89
cambiar el xticksde la trama, por ejemploplt.xticks(range(5), ["one", "two", "three", "four", "five"])
pascscha
buena función, muy útil, gracias. Lo único que cambié es que creo que la leyenda es más fácil si solo pones label = data.keys [i] en la llamada de gráfico de barras y luego no necesitas construir la lista de barras.
Adrian Tompkins
0

Hice esta solución: si desea trazar más de una parcela en una figura, asegúrese de que antes de trazar las siguientes parcelas haya configurado correctamente la matplotlib.pyplot.hold(True) posibilidad de agregar otras parcelas.

Con respecto a los valores de fecha y hora en el eje X, una solución que utiliza la alineación de barras funciona para mí. Cuando cree otro diagrama de barras con matplotlib.pyplot.bar(), solo use align='edge|center'y configure width='+|-distance'.

Cuando establezca todas las barras (gráficos) correctamente, verá las barras bien.

Dave
fuente
parece que matplotlib.pyplot.holdha quedado obsoleto desde la versión 2.0, como se menciona en los documentos
Engineervix