简体   繁体   English

不连续单杠 plot

[英]Discontinuous horizontal bar plot

Consider the following data frame考虑以下数据框

                 A      B
2022-09-28     1.3    0.0
2022-09-29     1.3    0.0
2022-09-30     1.3    0.9
2022-10-01     1.3    0.9
2022-10-02     0.0    0.9
2022-10-03     0.0    0.9
2022-10-04     0.0    0.0
2022-10-05     0.1    0.0
2022-10-06     0.1    0.0
2022-10-07     0.1    0.0

I would need a horizontal bar plot with two vertical levels (A and B) and the date on the x-axis.我需要一个水平条 plot,它具有两个垂直水平(A 和 B)和 x 轴上的日期。 The length of the bars (barwidth) equals the time intervals of nonzero values and the linewidth (bar height) the average of the values.条形的长度(条宽)等于非零值的时间间隔,线宽(条形高度)等于值的平均值。

For the example we would have two bars at the A level.例如,我们将在 A 级别设置两个栏。 The first one from 2022-09-28 to 2022-10-01 with linewidth 1.3 and the second one from 2022-10-05 to 2022-10-07 with linewidth 0.1.第一个从 2022-09-28 到 2022-10-01,线宽 1.3,第二个从 2022-10-05 到 2022-10-07,线宽 0.1。 On the B level there would be one bar only from 2022-09-30 to 2022-10-03 with linewidth 0.9.在 B 级别上,只有从 2022-09-30 到 2022-10-03 的一根柱线,线宽为 0.9。

This is pretty close, but it is a solution for one bar per level only. 非常接近,但它是每个级别仅一个条的解决方案。

You can use Matplotlib's broken_barh function to plot various discontinuous ('broken') horizontal bar plots.您可以使用 Matplotlib 的broken_barh function 到 plot 各种不连续(“破碎”)水平条形图。 The trick is to feed the data in the dataframe to broken_barh correctly: you need to create (x start, x duration) for each part of the discontinuous bar plot.诀窍是将 dataframe 中的数据正确地提供给broken_barh :您需要为不连续柱 plot 的每个部分创建(x start, x duration)

For example, A has two parts as you mentioned.例如,A 有您提到的两个部分。 One part would be (2022-09-28, 3 days) with linewidth 1.3, and the second part would be (2022-10-05, 2 days) with linewidth 0.1.一部分是(2022-09-28, 3 days) ,线宽为 1.3,第二部分是(2022-10-05, 2 days) ,线宽为 0.1。 We would feed broken_barh with x ranges [(2022-09-28, 3 days), (2022-10-05, 2 days)] and linewidth [1.3, 0.1] .我们将为broken_barh提供 x 范围[(2022-09-28, 3 days), (2022-10-05, 2 days)]和线宽[1.3, 0.1]

import matplotlib.pyplot as plt
import pandas as pd


if __name__ == '__main__':

    # Create dataframe    
    df_dates = pd.date_range('2022-09-28', periods=10)
    df = pd.DataFrame({'A': [1.3, 1.3, 1.3, 1.3, 0.0, 0.0, 0.0, 0.1, 0.1, 0.1],
                       'B': [0.0, 0.0, 0.9, 0.9, 0.9, 0.9, 0.0, 0.0, 0.0, 0.0]},
                      index=df_dates)

    # xranges and linewidth we will feed matplotlib later
    a_xranges = []
    a_lw = []

    # Find unique values in A - aka the parts of the A broken bar plot
    for lw_value in df['A'].unique().tolist():
        if lw_value != 0.0:  # skip 0.0, we will not plot it
            
            # Find rows where linewidth for the A values is located
            idx = df.index[df['A'] == lw_value].tolist()
            sub_df = df.loc[idx]

            # Find where the bar plot starts for x axis and x duration
            x_range = (sub_df.index[0], sub_df.index[-1] - sub_df.index[0])  # (x start, x duration)

            # Add x range and linewidth values for that part
            a_xranges.append(x_range)
            a_lw.append(lw_value)

a_xranges and a_lw are in the correct format for broken_barh . a_xrangesa_lwbroken_barh的正确格式。 Matplotlib will manage the pandas dates so you don't have to worry about date formatting. Matplotlib 将管理 pandas 日期,因此您不必担心日期格式。

You can repeat the same for B - you could also make a function and call it instead of adding the same loop to clean up your code.您可以对 B 重复相同的操作 - 您也可以制作一个 function 并调用它,而不是添加相同的循环来清理您的代码。

    b_xranges = []
    b_lw = []

    for lw_value in df['B'].unique().tolist():
        if lw_value != 0.0:
            idx = df.index[df['B'] == lw_value].tolist()
            sub_df = df.loc[idx]
            x_range = (sub_df.index[0], sub_df.index[-1] - sub_df.index[0])   
            b_xranges.append(x_range)
            b_lw.append(lw_value)

    # Start figure
    fig, ax = plt.subplots(figsize=(12, 5))
    # Plot A bar plot
    # The (10,9) is for the y axis (ymin, y duration)
    ax.broken_barh(a_xranges, (10, 9), edgecolor='k', facecolor='white', linewidth=a_lw)
    # Plot B bar plot
    ax.broken_barh(b_xranges, (20, 9), edgecolor='k', facecolor='white', linewidth=b_lw)

    ax.set_ylabel("Level")
    ax.set_yticks([15, 25], labels=['A', 'B'])
    ax.set_xlabel("Date")
    plt.show()

示例图片

If you wanted the bars closer, narrower, etc... you could play around with the y-values (10,9) and (20,9) values I gave them.如果你想让条形更近、更窄等……你可以使用我给它们的 y 值(10,9)(20,9)值。 Hope this helps - cheers!希望这会有所帮助 - 干杯!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM