Plotting time in Python with Matplotlib

499,669

Solution 1

Update:

This answer is outdated since matplotlib version 3.5. The plot function now handles datetime data directly. See https://matplotlib.org/3.5.1/api/_as_gen/matplotlib.pyplot.plot_date.html

The use of plot_date is discouraged. This method exists for historic reasons and may be deprecated in the future.

datetime-like data should directly be plotted using plot.

If you need to plot plain numeric data as Matplotlib date format or need to set a timezone, call ax.xaxis.axis_date / ax.yaxis.axis_date before plot. See Axis.axis_date.


Old, outdated answer:

You must first convert your timestamps to Python datetime objects (use datetime.strptime). Then use date2num to convert the dates to matplotlib format.

Plot the dates and values using plot_date:

import matplotlib.pyplot
import matplotlib.dates

from datetime import datetime

x_values = [datetime(2021, 11, 18, 12), datetime(2021, 11, 18, 14), datetime(2021, 11, 18, 16)]
y_values = [1.0, 3.0, 2.0]

dates = matplotlib.dates.date2num(x_values)
matplotlib.pyplot.plot_date(dates, y_values)

enter image description here

Solution 2

You can also plot the timestamp, value pairs using pyplot.plot (after parsing them from their string representation). (Tested with matplotlib versions 1.2.0 and 1.3.1.)

Example:

import datetime
import random
import matplotlib.pyplot as plt

# make up some data
x = [datetime.datetime.now() + datetime.timedelta(hours=i) for i in range(12)]
y = [i+random.gauss(0,1) for i,_ in enumerate(x)]

# plot
plt.plot(x,y)
# beautify the x-labels
plt.gcf().autofmt_xdate()

plt.show()

Resulting image:

Line Plot


Here's the same as a scatter plot:

import datetime
import random
import matplotlib.pyplot as plt

# make up some data
x = [datetime.datetime.now() + datetime.timedelta(hours=i) for i in range(12)]
y = [i+random.gauss(0,1) for i,_ in enumerate(x)]

# plot
plt.scatter(x,y)
# beautify the x-labels
plt.gcf().autofmt_xdate()

plt.show()

Produces an image similar to this:

Scatter Plot

Solution 3

7 years later and this code has helped me. However, my times still were not showing up correctly.

enter image description here

Using Matplotlib 2.0.0 and I had to add the following bit of code from Editing the date formatting of x-axis tick labels in matplotlib by Paul H.

import matplotlib.dates as mdates
myFmt = mdates.DateFormatter('%d')
ax.xaxis.set_major_formatter(myFmt)

I changed the format to (%H:%M) and the time displayed correctly. enter image description here

All thanks to the community.

Solution 4

I had trouble with this using matplotlib version: 2.0.2. Running the example from above I got a centered stacked set of bubbles.

graph with centered stack of bubbles

I "fixed" the problem by adding another line:

plt.plot([],[])

The entire code snippet becomes:

import datetime
import random
import matplotlib.pyplot as plt
import matplotlib.dates as mdates


# make up some data
x = [datetime.datetime.now() + datetime.timedelta(minutes=i) for i in range(12)]
y = [i+random.gauss(0,1) for i,_ in enumerate(x)]

# plot
plt.plot([],[])
plt.scatter(x,y)

# beautify the x-labels
plt.gcf().autofmt_xdate()
myFmt = mdates.DateFormatter('%H:%M')
plt.gca().xaxis.set_major_formatter(myFmt)

plt.show()
plt.close()

This produces an image with the bubbles distributed as desired.

graph with bubbles distributed over time

Solution 5

Pandas dataframes haven't been mentioned yet. I wanted to show how these solved my datetime problem. I have datetime to the milisecond 2021-04-01 16:05:37. I am pulling linux/haproxy throughput from /proc so I can really format it however I like. This is nice for feeding data into a live graph animation.

Here's a look at the csv. (Ignore the packets per second column I'm using that in another graph)

head -2 ~/data
date,mbps,pps
2021-04-01 16:05:37,113,9342.00
...

By using print(dataframe.dtype) I can see how the data was read in:

(base) ➜  graphs ./throughput.py
date      object
mbps      int64
pps       float64
dtype:    object

Pandas pulls the date string in as "object", which is just type char. Using this as-is in a script:

import matplotlib.pyplot as plt
import pandas as pd

dataframe = pd.read_csv("~/data")

dates = dataframe["date"]
mbps = dataframe["mbps"]

plt.plot(dates, mbps, label="mbps")
plt.title("throughput")
plt.xlabel("time")
plt.ylabel("mbps")
plt.legend()
plt.xticks(rotation=45)

plt.show()

enter image description here

Matplotlib renders all the milisecond time data. I've added plt.xticks(rotation=45) to tilt the dates but it's not what I want. I can convert the date "object" to a datetime64[ns]. Which matplotlib does know how to render.

dataframe["date"] = pd.to_datetime(dataframe["date"]) 

This time my date is type datetime64[ns]

(base) ➜  graphs ./throughput.py
date    datetime64[ns]
mbps             int64
pps            float64
dtype:          object

Same script with 1 line difference.

#!/usr/bin/env python
import matplotlib.pyplot as plt
import pandas as pd

dataframe = pd.read_csv("~/data")

# convert object to datetime64[ns]
dataframe["date"] = pd.to_datetime(dataframe["date"]) 

dates = dataframe["date"]
mbps = dataframe["mbps"]

plt.plot(dates, mbps, label="mbps")
plt.title("throughput")
plt.xlabel("time")
plt.ylabel("mbps")
plt.legend()
plt.xticks(rotation=45)

plt.show()

This might not have been ideal for your usecase but it might help someone else.

enter image description here

Share:
499,669

Related videos on Youtube

PPP
Author by

PPP

Updated on March 31, 2022

Comments

  • PPP
    PPP about 2 years

    I have an array of timestamps in the format (HH:MM:SS.mmmmmm) and another array of floating point numbers, each corresponding to a value in the timestamp array.

    Can I plot time on the x axis and the numbers on the y-axis using Matplotlib?

    I was trying to, but somehow it was only accepting arrays of floats. How can I get it to plot the time? Do I have to modify the format in any way?

  • PPP
    PPP over 14 years
    will it plot the Date or just the time? I only want the time since on conversion to datetime, the year is set to 1990.
  • codeape
    codeape over 14 years
    "... on conversion to datetime, the year is set to 1990": Could you please post the code you use to convert from string to datetime? Something might be wrong with the conversion. Re. the formatting of chart labels, see the date_demo1 link provided by J. K. Seppänen. The matplot lib documentation is excellent, BTW. matplotlib.sourceforge.net/index.html
  • Pete
    Pete over 13 years
    plot_dates --> matplotlib.pyplot.plot_date(dates,values)
  • moooeeeep
    moooeeeep about 10 years
    @andi it should work just as in my example (change plot to scatter).
  • dwanderson
    dwanderson over 7 years
    I don't understand why this is the 5th question I've encountered where no one has actually TRIED using a scatter plot - it doesn't work. Type Error - invalid type promotion.
  • dwanderson
    dwanderson over 7 years
    What about a scatter plot? I want to provide a size array to determine the size of the scatter-bubbles, but there is no equivalent scatter_date(), and no obvious/direct way to get this graph.
  • moooeeeep
    moooeeeep over 7 years
    @dwanderson Then, maybe you're doing something wrong. I've added an example for the scatter plot with datetime.datetime objects on the x axis to my post.
  • Christophe Roussy
    Christophe Roussy over 7 years
    For the grid add plt.grid(True)
  • Arindam Roychowdhury
    Arindam Roychowdhury almost 7 years
    The above returns a list with one object of type: matplotlib.lines.Line2D object. Now how do I use this to show the graph?
  • Akash Kandpal
    Akash Kandpal over 6 years
    could you provide the data used?
  • AimForClarity
    AimForClarity over 5 years
    Doesnt work on py2.6 for me. I get ValueError: view limit minimum -0.05500000000000001 is less than 1 and is an invalid Matplotlib date value. This often happens if you pass a non-datetime value to an axis that has datetime units <Figure size 432x288 with 1 Axes>
  • moooeeeep
    moooeeeep over 5 years
    @AimForClarity Works for me with matplotlib.__version__ 2.2.2. The scatter plot has some overly wide x-limits set by default, though. This answer seems to provide a workaround other than manually adjusting plt.xlim(...): stackoverflow.com/a/46467896/1025391
  • segFaultCoder
    segFaultCoder almost 5 years
    plt.gcf().autofmt_xdate() is the ticket
  • Qaswed
    Qaswed about 4 years
    @AkashKandpal, the data (x and y) are generated within the code. Without a seed being set, you cannot reproduce the graph to 100%, but you should be able to get the idea and to generate a similar graph.
  • Qaswed
    Qaswed about 4 years
    A solution to the strange x-lims is to set them on your own: stackoverflow.com/questions/21423158/…
  • alper
    alper over 2 years
    Would it be work if the intervals are different in between timestamps?
  • Ruben
    Ruben over 2 years
    This saved me a bunch of time and frustration, thanks for sharing! Any idea how to get the x-lable dates to show in format day-month-year-hour-min?
  • b0bu
    b0bu over 2 years
    Cool! Yea create a subplot to access the xaxis attribute. This is a comment so hard to show it but from my example above add fig,ax = plt.subplots() and add date_form = DateFormatter("%d-%m-%Y-%H-%M") and ax.xaxis.set_major_formatter(date_form) before .show()
  • Shayan
    Shayan over 2 years
    What's the values parameter in the last line??
  • codeape
    codeape over 2 years
    @Shayan I updated the code example.
  • Olli Niemitalo
    Olli Niemitalo about 2 years
    matplotlib.pyplot.plot_date is discouraged and will be deprecated as plot can now handle datetime directly.
  • codeape
    codeape about 2 years
    Great, thanks for the info. I edited the answer.