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 แท่งมันก็เลอะ คุณช่วยปรับปรุงโซลูชันของคุณให้ปรับขนาดได้มากขึ้นได้ไหม
John Smith

กำหนด "ความยุ่งเหยิง"? ป้าย X ที่ทับซ้อนกันอาจได้รับการแก้ไขโดยใช้autofmt_xdate()ซึ่งจะหมุนป้ายกำกับโดยอัตโนมัติ
John Lyon

ปัญหาคือป้าย X ทับซ้อนกันไม่ได้ปัญหาก็คือค่า y ยังทับซ้อนกัน ต้องแก้ไขอย่างไร?
John Smith

และความกว้าง = 0,2 นั้นเล็กเกินไปสำหรับช่วงเวลาที่มาก ถ้าฉันใช้ค่าที่มากกว่าฉันจะไม่ได้ผลลัพธ์เดียวกัน
John Smith

อีกประการหนึ่งคือช่องว่างที่จุดเริ่มต้นและจุดสิ้นสุด วิธีกำจัดช่องว่างและเริ่มต้นวันที่แรกโดยตรงและสิ้นสุดวันที่สุดท้ายในทำนองเดียวกันโดยไม่เว้นวรรคหรือเว้นวรรคน้อย
John Smith

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 คุณจะพอดีกับมันอย่างไร?
John Smith

1
คุณสามารถจะสร้างวันที่จำเป็นกับ numpy ของdatetime64: np.arange('2012-02', '2012-03', dtype='datetime64[D]')เช่นมูลค่าหนึ่งเดือน คุณอาจต้องคิดให้หนักขึ้นเกี่ยวกับวิธีที่ดีที่สุดในการแสดงข้อมูลนี้หากคุณมีชุดข้อมูล 40 ชุด (ตามความคิดเห็นอื่น) ซึ่งใช้เวลามากกว่า 100 วัน
John Lyon

นอกจากนี้การใช้ ax.xaxis_date () ก็เป็นข้อดีมากทำให้วันที่ของคุณพอดีกับแกน x
John Smith

3
ทำไมคุณไม่ไปก่อน? ฉันพยายามช่วยให้คุณเรียนรู้ไม่ใช่เขียนโค้ดให้คุณ ฉันแน่ใจว่าคุณทำได้xaxis_dateแต่คุณจะต้องปรับเปลี่ยนสิ่งที่ฉันเขียนไว้เพื่อชดเชยค่าวันที่ของคุณ (เช่นตามจำนวนชั่วโมงที่ใช้timedelta) สำหรับแต่ละชุดเพื่อหยุดการทับซ้อนกัน คำตอบอื่น ๆ ทำได้เพียงแค่นี้ แต่คุณอาจต้องโคลนกับป้ายกำกับในภายหลัง
John Lyon

โอเค แต่เมื่อฉันเรียกใช้ np.arange ('2012-02', '2012-03, dtype =' datetime64 [D] ') ฉันได้รับสิ่งนี้: ประเภทตัวถูกดำเนินการที่ไม่รองรับสำหรับ -:' str 'และ' str '
John Smith

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 ด้วย
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 จากนั้นคุณไม่จำเป็นต้องสร้างรายการแท่ง
Adrian Tompkins

0

ฉันทำวิธีแก้ปัญหานี้แล้ว: ถ้าคุณต้องการพล็อตมากกว่าหนึ่งพล็อตในรูปเดียวตรวจสอบให้แน่ใจก่อนที่จะวางพล็อตถัดไปคุณได้ตั้งค่าไว้ว่า matplotlib.pyplot.hold(True) จะสามารถเพิ่มพล็อตอื่นได้

เกี่ยวกับค่าวันที่และเวลาบนแกน X วิธีแก้ปัญหาโดยใช้การจัดแนวแท่งเหมาะสำหรับฉัน เมื่อคุณสร้างพล็อตบาร์อื่น ๆ ที่มีmatplotlib.pyplot.bar()การใช้งานเพียงและการตั้งค่าalign='edge|center'width='+|-distance'

เมื่อคุณตั้งค่าแถบทั้งหมด (แปลง) อย่างถูกต้องคุณจะเห็นแถบต่างๆ


ดูเหมือนว่าmatplotlib.pyplot.holdจะเลิกใช้งานแล้วตั้งแต่ v2.0 ตามที่กล่าวไว้ในเอกสาร
engineervix
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.