How to plot timedelta data on y-axis with Reportlab?

46 views Asked by At

New to python and reportlab and got stuck on a problem when plotting timedelta variables on the y-axis of a lineplot. I'm working on a program that reads an excel file and generates graphs in a pdf using reportlab. The excel contains time values in m:ss.000 format and are read as strings into a dataframe by pandas. I would like to plot this in my pdf again in m:ss:000 format. As far as I have understood, python uses timedelta to represent time in this way, but I get the following error when I try to plot this:
TypeError: '<' not supported between instances of 'Timedelta' and 'int'

I found a suboptimal solution by converting the string to a float, so e.g. 1:57.123 becomes 117.123, and I'm able to plot it in this way, but I would still prefer to plot as 1:57.123.

The code below has an example dataframe and shows how I get to my suboptimal solution and how I convert the time from a string to timedelta and to float. When I take the same approach to plot the timedelta variables, I get the error.

import pandas as pd 

from reportlab.lib.styles import getSampleStyleSheet
from reportlab.graphics.shapes import Drawing
from reportlab.graphics.charts.lineplots import LinePlot
from reportlab.platypus import SimpleDocTemplate

def time_to_float(str):
    temp1 = list(str.split(":"))
    temp2 = list(temp1[1].split("."))
    temp1.pop()

    temp2.insert(0,temp1[0])

    return int(temp2[0])*60 + int(temp2[1]) + int(temp2[2])/1000

doc = SimpleDocTemplate('plot.pdf')
elements = []
styles = getSampleStyleSheet()

df1 = pd.DataFrame({'Lap_Nr': (1,2,3), 'Lap_Time': ('1:57.123', '1:56.988', '1:56.555')})
df2 = pd.DataFrame({'Lap_Nr': (1,2,3), 'Lap_Time': ('1:57.123', '1:56.988', '1:56.555')})

df2.Lap_Time = '00:0' + df2.Lap_Time
df2.Lap_Time = pd.to_timedelta(df2.Lap_Time)

for i in range(0,len(df1)):
    df1.iloc[i,1] = time_to_float(df1.iloc[i,1])

drawing1 = Drawing(400, 200)

chart1 = LinePlot()
chart1.x = 50
chart1.y = 50
chart1.height = 125
chart1.width = 300

chart1.data = [[]]
for i in range(0,3):
    chart1.data[0].append((df1.iloc[i,0], df1.iloc[i,1]))

drawing1.add(chart1)
elements.append(drawing1)

# drawing2 = Drawing(400, 200)

# chart2 = LinePlot()
# chart2.x = 50
# chart2.y = 0
# chart2.height = 125
# chart2.width = 300

# chart2.data = [[]]
# for i in range(0,3):
#     chart2.data[0].append((df2.iloc[i,0], df2.iloc[i,1]))

# drawing2.add(chart2)
# elements.append(drawing2)

doc.build(elements)

Can someone help me with this?

1

There are 1 answers

0
stefaan1o On

Better use datetime and format the axis to mm:ss ( or similar ). Suggest to use a more general plot library as matplotlib. To copy plot from matplotilb to reportlab you can use a BytesIO object. See example below

enter image description here

from io import BytesIO

import matplotlib.pyplot as plt
import pandas as pd
from matplotlib.dates import DateFormatter

from reportlab.lib.units import cm, inch
from reportlab.lib.utils import ImageReader
from reportlab.pdfgen import canvas


def create_chart():
    df = pd.DataFrame({'Lap_Nr': (1, 2, 3), 'Lap_Time': ('1:57.123', '1:56.988', '1:56.555')})
    df['Lap_Time'] = pd.to_datetime(df['Lap_Time'], format='%M:%S.%f')

    fig, ax = plt.subplots()
    myFmt = DateFormatter("%M:%S.%f")
    ax.yaxis.set_major_formatter(myFmt)

    ax.plot(df['Lap_Nr'], df['Lap_Time'])
    plt.gcf().autofmt_xdate()
    plt.xlabel('Lap Nr')
    plt.ylabel('Lap Time')

    plt.gcf().subplots_adjust(left=0.15)

    imgdata = BytesIO()
    fig.savefig(imgdata, format='png')
    imgdata.seek(0)

    return imgdata


if __name__ == '__main__':
    c = canvas.Canvas('test.pdf')
    Image = ImageReader(create_chart())
    c.drawImage(Image, 2 * cm, 2 * cm, 18 * cm, 12 * cm)
    c.save()