Python matplotlib несколько полос

92

Как построить несколько полосок в matplotlib, когда я пытался вызвать функцию полосы несколько раз, они перекрываются, и, как видно на рисунке ниже, можно увидеть только самое высокое значение красного цвета. Как я могу нанести несколько столбцов с датами на оси x?

Пока что я пробовал это:

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 и столбцами рядом друг с другом:

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

Джон Смит
источник
вам нужно изменить значения 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()

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

HYRY
источник
Спасибо, но если у меня 3 полоски, все будет хорошо. Когда я пробую 40 тактов, получается сбой. Не могли бы вы обновить свое решение, чтобы оно было более масштабируемым?
Джон Смит
Дайте определение "лажа"? Перекрытие меток X может быть исправлено с помощью autofmt_xdate()автоматического поворота меток.
Джон Лайон
Проблема не в перекрытии меток X, дело в том, что значения y также перекрываются. Как это исправить?
Джон Смит
а также ширина = 0,2 слишком мала для большого промежутка времени. Если я использую большие значения, я не получу того же результата.
Джон Смит
Другое дело, что пробелы в начале и в конце. Как убрать пробелы и начать сразу первую дату и точно так же закончить последнюю дату без пробелов или с меньшим пространством.
Джон Смит
61

Проблема с использованием дат в качестве значений x заключается в том, что если вам нужна гистограмма, как на втором рисунке, они будут неправильными. Вы должны либо использовать линейчатую диаграмму с накоплением (цвета друг над другом), либо группировать по дате («поддельная» дата на оси 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 дней по оси x, как вы их поместите?
Джон Смит
1
Вы можете легко создавать необходимые даты с NumPy - х datetime64: например , один месяц на сумму: np.arange('2012-02', '2012-03', dtype='datetime64[D]'). Возможно, вам придется больше подумать о том, как лучше всего представить эти данные, если у вас есть 40 наборов данных (согласно другому комментарию), охватывающих более 100 дней.
Джон Лайон
Кроме того, использование ax.xaxis_date () очень выгодно, потому что оно соответствует вашим датам по осям x.
Джон Смит
3
Почему бы тебе не попробовать сначала? Я пытаюсь помочь вам учиться, а не писать за вас код. Я уверен, что вы справитесь с этим, xaxis_dateно вам нужно будет адаптировать то, что я написал, чтобы компенсировать ваши значения даты (например, на количество часов использования timedelta) для каждой серии, чтобы они не перекрывались. Другой ответ делает именно это, но вам, возможно, впоследствии придется испортить ярлыки.
Джон Лайон
хорошо, но когда я запускаю 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()

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

liwt31
источник
Отличный ответ, но он несколько неполный из-за оси x. А можно сделать презентабельнее?
Spinor8
Вы могли бы, я полагаю, сделать то же самое с пандами и матплотлибом
Vicki B
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()

Выход:

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

пасща
источник
Как мы можем изменить это, чтобы добавить метки к оси x? Что касается каждой группы полосок?
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, как упоминалось в документации,
engineeringvix