pythonmatplotlibplottable

Plottable Customize Bars


Given this dataframe:

import pandas as pd    
df = pd.DataFrame({'Grade':[2,3,4,5], 'start':[0,0,20,10], 'end':[100,90,80,100]})
    
       Grade  start  end
    0      2      0  100
    1      3      0   90
    2      4     20   80
    3      5     10  100

I would like to generate a table with it via plottable library with 2 columns: Grade and a column called "Progress" with bars (though a green arrow would be amazing) that starts at 'start' and ends at 'end' where the x-axis starts at 0 and ends at 100. Both 'start' and 'end should be annotated.

Here's an example of what I'm looking for in that column: enter image description here

If "Start" starts beyond 0, then the bar (or arrow) should start at that relativel position on the "x-axis". The annotations should alwasy be on the left and right sides fo the bars (arrows), as in the example above.

From the "docs", I did find this example for bar charts:

ColumnDefinition(
            "D",
            width=1.25,
            plot_fn=bar,
            plot_kw={
                "cmap": cmap,
                "plot_bg_bar": True,
                "annotate": True,
                "height": 0.5,
                "lw": 0.5,
                "formatter": decimal_to_percent,
                "xlim":[low, high],
            },
        ),

...but I cannot find any reference to control the annotations in terms of labels and positions. I found that I can add "xlim" to the plot_kws but now I need to get start and end from the passed dataframe to determine those values per row.


Solution

  • a table

    import matplotlib.pyplot as plt
    
    grades = '2 3 4 5'.split()
    starts = [0,0,20,10]
    ends = [100,90,80,100]
    
    fig, ax= plt.subplots(layout='constrained')
    ax.axis('off')
    t = plt.table(
      cellText=list(zip(grades, ['']*4)),
      colLabels=['Grade', 'Progress'],
      colWidths=[0.2, 0.3],
      cellLoc='center',
      loc='center',
      edges='',
      )
    
    # apparently this is necessary to have the cells initialized
    fig.canvas.draw()
    
    trax = ax.transAxes
    for row in range(1, 5):
        cell = t[row, 1]
        plt.plot((c._x0, c._x0+c._width), (c._y0+0.01, c._y0+0.01),
                 lw=0.3, color='black', transform=trax)
        startx = c._x0+c._width*starts[row-1]/100
        dx = c._width*(ends[row-1]-starts[row-1])/100
        plt.arrow(startx, c._y0+0.0225, dx, 0.0,
        color='forestgreen', width=0.006,
        length_includes_head=True,  transform=trax)
    plt.show()