11

I am working on a task, where I need to calculate time spent on each day and then represent that time using a bar plot, so for this task I used python and able to get time spent on each day, and stored it in a list "time_list", now I don't understand how to plot this using matplotlib function.

The problem is that, this list contains datetime.timedelta class values. Example:

time_list
[datetime.timedelta(0, 23820), datetime.timedelta(0, 27480), datetime.timedelta(0, 28500), datetime.timedelta(0, 24180), datetime.timedelta(0, 27540), datetime.timedelta(0, 28920), datetime.timedelta(0, 28800), datetime.timedelta(0, 29100), datetime.timedelta(0, 29100), datetime.timedelta(0, 24480), datetime.timedelta(0, 27000)]

And these values meaning is as follow:

Total Time Spent on  2  is  6:37:00
Total Time Spent on  3  is  7:38:00
Total Time Spent on  4  is  7:55:00
Total Time Spent on  5  is  6:43:00
Total Time Spent on  8  is  7:39:00
Total Time Spent on  9  is  8:02:00
Total Time Spent on  10  is  8:00:00
Total Time Spent on  11  is  8:05:00
Total Time Spent on  12  is  8:05:00
Total Time Spent on  15  is  6:48:00
Total Time Spent on  16  is  7:30:00
Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
xpress_embedo
  • 377
  • 1
  • 4
  • 18
  • It depends on what do you want to see on the ticks labels of the X axis on your bar plot. You already have the number of seconds spent on work ( e.g. 23820 = 60*60*6 + 60*37), so it's the matter of binning and labeling, according to your requirements. – Maciek Jan 17 '18 at 05:59
  • @Maciek I want date on X-Axis and Time Spent on Y-Axis. 2, 3, 4, 5, 8, 9, 10, 11, 12, 15, 16 are dates and they should be on X-Axis and the time, on Y-Axis. So that I can plot graph, indicating that, on 2nd Jan -> 6:37 time spent on 3rd Jan -> 7:38 time spend, and so on – xpress_embedo Jan 17 '18 at 06:06

3 Answers3

14

I don't think you can directly plot timedelta in Matplotlib, but since you already have the number of seconds, you can define a custom tick format that converts seconds to hours and minutes.

from matplotlib.ticker import FuncFormatter

def format_func(x, pos):
    hours = int(x//3600)
    minutes = int((x%3600)//60)
    seconds = int(x%60)

    return "{:d}:{:02d}".format(hours, minutes)
    # return "{:d}:{:02d}:{:02d}".format(hours, minutes, seconds)

formatter = FuncFormatter(format_func)

Then, you can set the tick formatter for the y-axis.

Here's an example using a bar.

labels = [2, 3, 4, 5, 8, 9, 10, 11, 12, 15, 16]
seconds = [i.seconds for i in time_list]
f = plt.figure()
ax = f.add_subplot(1,1,1)
ax.bar(labels, seconds)

ax.yaxis.set_major_formatter(formatter)
# this locates y-ticks at the hours
ax.yaxis.set_major_locator(matplotlib.ticker.MultipleLocator(base=3600))
# this ensures each bar has a 'date' label
ax.xaxis.set_major_locator(matplotlib.ticker.MultipleLocator(base=1))

Example Bar Graph

Phil
  • 2,080
  • 2
  • 12
  • 13
  • Thanks for your answer, unfortunately i am not much familiar with these functions, thanks for introducing this to me. I will read documentation and understand this more properly. – xpress_embedo Jan 17 '18 at 16:23
6

While matplotlib can in principle handle datetime objects, the bar plot cannot interprete them directly. So one may add an arbitrary date to the timedeltas and convert to numbers using matplotlib.dates.date2num(). Then using a DateFormatter enables nice ticklabels.

import numpy as np
import datetime
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

days = [2, 3, 4, 5, 8, 9, 10, 11, 12, 15, 16]

time_list = [datetime.timedelta(0, 23820), datetime.timedelta(0, 27480), 
             datetime.timedelta(0, 28500), datetime.timedelta(0, 24180), 
             datetime.timedelta(0, 27540), datetime.timedelta(0, 28920), 
             datetime.timedelta(0, 28800), datetime.timedelta(0, 29100), 
             datetime.timedelta(0, 29100), datetime.timedelta(0, 24480), 
             datetime.timedelta(0, 27000)]

# specify a date to use for the times
zero = datetime.datetime(2018,1,1)
time = [zero + t for t in time_list]
# convert datetimes to numbers
zero = mdates.date2num(zero)
time = [t-zero for t in mdates.date2num(time)]

f = plt.figure()
ax = f.add_subplot(1,1,1)

ax.bar(days, time, bottom=zero)
ax.yaxis_date()
ax.yaxis.set_major_formatter(mdates.DateFormatter("%H:%M"))

# add 10% margin on top (since ax.margins seems to not work here)
ylim = ax.get_ylim()
ax.set_ylim(None, ylim[1]+0.1*np.diff(ylim))

plt.show()

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Thanks for this solution. – xpress_embedo Jan 17 '18 at 16:27
  • 1
    @ImportanceOfBeingErnest, I apologize for stepping out of line, but is it possible to do something similar using `plot` instead of `bar`? I tried to implement something similar but I end up getting a `ValueError: view limit minimum 0.01 is less than 1 and is an invalid Matplotlib date value`. I do apologize for hijacking your question, @xpress_embedo. – naughty_waves May 31 '19 at 19:36
1

I had similar problem when I wanted to plot the data with a y axis equal to Timedelta64[ns] type. I found the easiest solution to handle this issue from this blog : solution. For short, just change the dtype of your column to .astype('timedelta64[m]'). You can change to hour, minutes or seconds for your case just by changing the value in a square brackets. It changes the dtype of your y column to float64 and then you can easily plot the bar graph or plot with normal units and not like nanoseconds

ArKey
  • 31
  • 4