Python matplotlib 다중 막대


92

matplotlib에서 여러 막대를 그리는 방법은 bar 함수를 여러 번 호출하려고 할 때 겹쳐지며 아래 그림에서 볼 수 있듯이 가장 높은 값인 빨간색 만 볼 수 있습니다. 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 값은 날짜입니다 ...
John Smith

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 개의 바를 시도하면 엉망이됩니다. 솔루션을 더 확장 가능하도록 업데이트 할 수 있습니까?
존 스미스

"messes up"을 정의 하시겠습니까? 겹치는 X 레이블은 레이블 autofmt_xdate()을 자동으로 회전하는를 사용하여 수정할 수 있습니다 .
John Lyon

문제는 X 레이블이 겹치는 것이 아니라 y 값도 겹치는 것입니다. 그것을 고치는 방법?
John Smith

또한 width = 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()

여기에 이미지 설명 입력


x 축에 100 일을 표시하고 싶다면 어떻게 맞출까요?
존 스미스

1
numpy 's에 필요한 날짜를 쉽게 생성 할 수 있습니다 datetime64. 예 : 1 개월 가치 : np.arange('2012-02', '2012-03', dtype='datetime64[D]'). 100 일에 걸쳐 40 개의 데이터 세트 (다른 ​​의견에 따라)가있는 경우이 데이터를 표현하는 가장 좋은 방법에 대해 더 열심히 생각해야 할 수도 있습니다.
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()

여기에 이미지 설명 입력


좋은 대답이지만 x 축 때문에 다소 불완전합니다. 더보기 좋게 만들 수 있습니까?
Spinor8

당신은 내가 가정도 판다하기 matplotlib 그의을 할 수
비키 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

좋은 기능, 매우 도움이됩니다. 감사합니다. 내가 바꾼 유일한 것은 barplot 호출에 label = data.keys [i]를 입력하고 막대 목록을 작성할 필요가 없으면 범례가 더 쉽다는 것입니다.
Adrian Tompkins

0

이 솔루션을 사용했습니다. 한 그림에 둘 이상의 플롯을 그리려면 다음 플롯을 그리기 전에 matplotlib.pyplot.hold(True) 다른 플롯을 추가 할 수 있도록 설정했는지 확인하십시오 .

X 축의 datetime 값과 관련하여 막대 정렬을 사용하는 솔루션이 적합합니다. 를 사용하여 다른 막대 그림을 만들 때을 matplotlib.pyplot.bar()사용 align='edge|center'하고 설정하십시오 width='+|-distance'.

모든 막대 (플롯)를 올바르게 설정하면 막대가 잘 표시됩니다.


문서에서 언급했듯이matplotlib.pyplot.hold v2.0부터 사용되지 않는 것 같습니다
engineervix
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.