Python matplotlib кілька барів


92

Як побудувати декілька стовпчиків у matplotlib, коли я намагався викликати функцію бару кілька разів, вони перекриваються, і як видно на малюнку нижче, видно лише найвище значення червоного. Як я можу побудувати кілька координат з датами на осях осей?

Поки що я спробував це:

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()

Я отримав це:

введіть тут опис зображення

Результати повинні бути приблизно такими, але з датами на осях осей, а стовпчики розташовані поруч:

введіть тут опис зображення


вам потрібно змінити значення x
jterrace

2
Що ви маєте на увазі ? Значення X - дати ...
Джон Сміт,

4
чому це просто не підтримується matplotlib ?!
ihadanny

Відповіді:


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()

введіть тут опис зображення

Я не знаю, що означає "значення y також перекриваються", чи вирішує вашу проблему наступний код?

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()

введіть тут опис зображення


Дякую, але якщо у мене є 3 бари, це виглядає добре. Коли я намагаюся, як 40 барів, це псується. Чи можете ви оновити своє рішення, щоб воно стало більш масштабованим?
John Smith

Визначте "переплутати"? Накладання міток X може бути виправлено за допомогою autofmt_xdate(), який автоматично обертає мітки.
John Lyon,

Проблема полягає не в перекритті міток X, справа в тому, що значення y також перекриваються. Як це виправити?
John Smith

а також ширина = 0,2 занадто мала для великого проміжку часу. Якщо я використовую більші значення, я не отримую однаковий результат.
John Smith

Інша справа, що пробіли на початку та в кінці. Як усунути пробіли та розпочати безпосередньо першу дату і так само закінчити останню дату без пробілу чи менше місця.
Джон Сміт,

61

Проблема використання дат як значень x полягає в тому, що якщо ви хочете мати гістограму, як на вашому другому малюнку, вони будуть помилятися. Вам слід використовувати або стовпчасту діаграму (кольори один на одного), або згрупувати за датою ("фальшива" дата на осі х, в основному просто групуючи точки даних).

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()

введіть тут опис зображення


якщо я хочу показати, як 100 днів на осях х, як ти їх підбираєш?
John Smith

1
Ви можете легко створювати необхідні дати з NumPy - х datetime64: наприклад , один місяць на суму: np.arange('2012-02', '2012-03', dtype='datetime64[D]'). Можливо, вам доведеться більше подумати про найкращий спосіб представити ці дані, якщо у вас є 40 наборів даних (згідно з іншим коментарем), що охоплюють більше 100 днів.
John Lyon,

А також використання ax.xaxis_date () - це дуже переваги, оскільки воно відповідає вашим датам у осях x.
Джон Сміт,

3
Чому б вам не спершу спробувати? Я намагаюся допомогти вам навчитися, а не писати свій код замість вас. Я впевнений, що ви можете це зробити, xaxis_dateале вам доведеться адаптувати те, що я написав, щоб компенсувати ваші значення дати (наприклад, на кількість годин використання timedelta) для кожної серії, щоб зупинити їх накладання. Інша відповідь робить саме це, але вам може знадобитися згодом позначити ярлики.
John Lyon,

гаразд, але коли я запускаю np.arange ('2012-02', '2012-03, dtype =' datetime64 [D] '), я отримую це: непідтримувані типи операндів для -:' str 'і' str '
Джон Сміт

25

Я знаю, що це приблизно matplotlib, але використання pandasта seabornможе заощадити вам багато часу:

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()

введіть тут опис зображення


Чудова відповідь, але вона дещо неповна через вісь х. Чи можете ви зробити його більш презентабельним?
Spinor8,

Ви можете, я припускаю, також зробити його з пандами та матплотлібом
Вікі Б

18

шукаючи подібне рішення і не знайшовши нічого досить гнучкого, я вирішив написати для нього власну функцію. Це дозволяє вам мати стільки стовпчиків на групу, скільки забажаєте, і вказати як ширину групи, так і індивідуальні ширини брусків у групах.

Насолоджуйтесь:

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()

Вихід:

введіть тут опис зображення


Як ми можемо змінити це, щоб додати мітки до осі х? Як у кожній групі барів?
x89

змінити xticksсюжет, наприкладplt.xticks(range(5), ["one", "two", "three", "four", "five"])
pascscha

приємна функція, дуже корисна, дякую. Єдине, що я змінив, так це те, що, на мою думку, легенду простіше, якщо ви просто вкладете label = data.keys [i] у виклик barplot, і тоді вам не потрібно будувати список барів.
Адріан Томпкінс,

0

Я зробив це рішення: якщо ви хочете побудувати більше одного сюжету на одному малюнку, переконайтеся, що перед побудовою наступних графіків ви встановили право matplotlib.pyplot.hold(True) на можливість додавання інших сюжетів.

Що стосується значень дати та часу на осі X, для мене працює рішення із вирівнюванням стовпчиків. Коли ви створюєте інший штриховий графік за допомогою matplotlib.pyplot.bar(), просто використовуйте align='edge|center'та встановлюйте width='+|-distance'.

Коли ви правильно встановите всі штрихи (графіки), ви побачите штанги чудово.


схоже matplotlib.pyplot.hold, що застаріла з версії 2.0, як згадувалося в документах
engineervix
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.