How can I show UTC time on x axis of matplotlib [duplicate] - python

I am trying to plot some data against a list of datetime objects in the x axis with pyplot. However the dates appear as the standard format, which is %Y-%m-%d %H:%M:%S (way too long). I can circumvent this by creating a list of date strings with strftime and use that instead. I also know that there is some kind of date object intrinsic for pyplot which I could use instead of datetime.
Is there a way to tell pyplot in which format to plot the datetimeobjects however? Without having to transform everything to string or another kind of object?
Thank you.

You can use DateFormatter:
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(your_dates, your_data)
# format your data to desired format. Here I chose YYYY-MM-DD but you can set it to whatever you want.
import matplotlib.dates as mdates
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
# rotate and align the tick labels so they look better
fig.autofmt_xdate()

Apart from manually specifying the datetime format for the axes as shown in the other answer, you may use rcParams to set the format.
The standard is
# date.autoformatter.year : %Y
# date.autoformatter.month : %Y-%m
# date.autoformatter.day : %Y-%m-%d
# date.autoformatter.hour : %m-%d %H
# date.autoformatter.minute : %d %H:%M
# date.autoformatter.second : %H:%M:%S
# date.autoformatter.microsecond : %M:%S.%f
You may change that in the matplotlib rc file,
or inside the code via
plt.rcParams["date.autoformatter.minute"] = "%Y-%m-%d %H:%M:%S"

Related

matplotlib: what's the difference with set_major_formatter() and fmt_xdata?

I am new to matplotlib. And I copied code for simple pyqt-matplotlib example from here, and changed plot() of class PlotCanvas to make a Value-Time chart.
def plot(self):
dates = mdates.drange(dt.datetime(2010, 1, 1), dt.datetime(2010,1,2),
dt.timedelta(minutes=10))
y_val = [random.random() for i in range(len(dates))]
ax = self.figure.add_subplot(111)
ax.plot(dates, y_val)
use_major_formatter = True
if use_major_formatter:
ax.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d %H:%M'))
else:
ax.fmt_xdata = DateFormatter('%%m-%d %H:%M')
self.figure.autofmt_xdate()
ax.set_title('PyQt Matplotlib Example')
self.draw()
Above code requires theseimports:
import matplotlib.dates as mdates
import datetime as dt
from matplotlib.dates import DateFormatter
I tried set_major_formatter() and fmt_xdata. And I've found fmt_xdata doesn't work.
But why?
What's the difference with set_major_formatter() and fmt_xdata?
Notice: In the above code, I added if statement to test two methods.
ax.xaxis.set_major_formatter() and ax.fmt_xdata are entirely different things.
The first, ax.xaxis.set_major_formatter(), sets the formatter for the x axis in your plot. This is useful for showing the ticklabels in a required format.
The second, ax.fmt_xdata is a function that takes in a data coordinate and formats it. It will by default just return the same as the major formatter. It is used internally to format the numbers in the GUI that are shown when moving the mouse around.
You can replace the ax.fmt_xdata method by your own custom callable in case you want the GUI to show different numbers/strings than your axis.

axes.set_xticklabels breaks datetime format

im trying to force my will onto this matplotlib graph. When I set ax1.xaxis.set_major_formatter(myFmt) it works fine like in the upper graph.
However when I add ax1.set_xticklabels((date),rotation=45) the timeformat reverts to matplotlib time like in the lower graph.
Both use the same input time variable. I also tried ax1.plot_date() but that only changes the look of the graph not the timeformat.,
date_1 = np.vectorize(dt.datetime.fromtimestamp)(time_data) # makes a datetimeobject from unix timestamp
date = np.vectorize(mdates.date2num)(date_1) # from datetime makes matplotib time
myFmt = mdates.DateFormatter('%d-%m-%Y/%H:%M')
ax1 = plt.subplot2grid((10,3), (0,0), rowspan=4, colspan=4)
ax1.xaxis_date()
ax1.plot(date, x)
ax1.xaxis.set_major_formatter(myFmt)
ax1.set_xticklabels((date),rotation=45)#ignores time format
Any ideas how I can force the custom timeformat onto the xticklabels? I get that xticklabels directly reads and displays the date variable but shouldnt it be possible to make it stick to the format? Especially if you later want to add xticks in custom date locations.
All ideas appreciated. Cheers
A locator specifies the locations of the ticks. A formatter formats the ticklabels at those positions. Using a formatter, like
ax1.xaxis.set_major_formatter(dates.DateFormatter('%d-%m-%Y/%H:%M'))
hence works well. However, using set_xticklabels after speciying the formatter, removes the DateFormatter and replaces it with a FixedFormatter. You will hence get ticklabels at automatically chosen positions but with labels that do not correspond to those positions. The graph will hence be labelled incorrectly.
Therefore, you should never use set_xticklabels without specifying a custom locator, e.g. via set_xticks, as well.
Here there is no need to use set_xticklabels at all. The formatter alone is enough.
import datetime as dt
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import numpy as np
time_data = np.array([1.5376248e+09,1.5376932e+09,1.5377112e+09])
x = np.array([1,3,2])
date_1 = np.vectorize(dt.datetime.fromtimestamp)(time_data)
date = np.vectorize(mdates.date2num)(date_1)
myFmt = mdates.DateFormatter('%d-%m-%Y/%H:%M')
ax1 = plt.subplot2grid((4,4), (0,0), rowspan=4, colspan=4)
ax1.xaxis_date()
ax1.plot(date, x)
ax1.xaxis.set_major_formatter(myFmt)
plt.setp(ax1.get_xticklabels(), rotation=45, ha="right")
plt.show()
Alright I think I got it now.
str_dates = []
for i in time_data:
j = dt.datetime.fromtimestamp(i)
k = j.strftime('%d-%m-%Y/%H:%M')
str_dates.append(k)
print(str_dates)
ax1.set_xticklabels((str_dates),rotation=45)
Im not sure why this doesnt work with vectorize, but taking each date one by one removes the error source that the arrays are giving me.
#iDrwish: thanks again you pushed me in the right direction.
You can coerce your time format by converting the datetime object to string.
You will have to do special handling of the dates if that are in utc-format:
from datetime import datetime
str_dates = [datetime.utcfromtimestamp(timestamp).strftime('%d-%m-%Y/%H:%M') for timestamp in date]
ax1.set_xticklabels((str_dates),rotation=45)

How to change the datetime tick label frequency for matplotlib plots

Below shows a plot of simulated data, which contains the xticks that I want to modify. By default, the pd.df.plot chooses dates that are approximately 3 months apart as ticks. But what I want is each month being a tick. What is the best way to do this? What about seasonal ticks? Thank you in advance.
First of all you have to convert pandas date objects to python date objects. This conversion is needed because of matplotlib internal date conversion functions. Then use functions from matplotlib.dates to set desired formatter and tick positions like here:
import pandas as pd
import numpy as np
import matplotlib.pylab as plt
import matplotlib.dates as mdates
# convert date objects from pandas format to python datetime
index = pd.date_range(start = "2015-07-01", end = "2017-01-01", freq = "D")
index = [pd.to_datetime(date, format='%Y-%m-%d').date() for date in index]
data = np.random.randint(1,100, size=len(index))
df = pd.DataFrame(data=data,index=index, columns=['data'])
print (df.head())
ax = df.plot()
# set monthly locator
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=1))
# set formatter
ax.xaxis.set_major_formatter(mdates.DateFormatter('%d-%m-%Y'))
# set font and rotation for date tick labels
plt.gcf().autofmt_xdate()
plt.show()
For season labels you have to construct it by yourself and then set it with plt.setp function (for month 02 set label winter, 04 - spring etc.):
plt.setp(new_labels, rotation=90, fontsize=9).
head of df:
data
2015-07-01 26
2015-07-02 33
2015-07-03 46
2015-07-04 69
2015-07-05 17
This answer is based on the one by Serenity as well as on this one by ImportanceOfBeingErnest.
The best way to customize time series tick labels is to use the tick locators and formatters from the matplotlib.dates module (mdates). Though it is worth noting that if you want a tick frequency based on the same unit as the time series you are plotting, it may be more convenient to create and format the tick labels using the dates as strings like in the answers to this question concerning pandas bar plots.
As described in the documentation, pandas uses matplotlib to create plots with its own custom tick formatters for time series:
pandas provides custom formatters for timeseries plots. These change the formatting of the axis labels for dates and times. By default, the custom formatters are applied only to plots created by pandas with DataFrame.plot() or Series.plot().
The ticks and labels of pandas time series plots are currently formatted like this by default:
import numpy as np # v 1.19.2
import pandas as pd # v 1.1.3
import matplotlib.dates as mdates # v 3.3.2
# Create random dataset stored as a pandas DataFrame with a DatetimeIndex
rng = np.random.default_rng(seed=1) # random number generator
date_day = pd.date_range(start='2015-07-01', end='2016-12-31', freq='D')
traffic = rng.lognormal(sigma=2, size=date_day.size)
df_day = pd.DataFrame(dict(traffic=traffic), index=date_day)
# Create pandas plot with default settings except for figure size
df_day.plot(figsize=(10,5));
To be able to use the mdates tick locators and formatters and override the default tick formatting, the pandas dates must be correctly recognized by matplotlib. The problem is that pandas and matplotlib have different approaches to computing the date numbers that are used to locate the ticks on the time axis (the x-axis by default).
In pandas, time is measured in nanoseconds starting at zero on 1970-01-01 00:00:00 (the origin of the Unix epoch) and individual time points are stored as pandas timestamp objects. But when it comes to creating time scales for plots, pandas uses another numbering system which starts at the same origin but then increases by 1 for each period of the chosen frequency (in this example the frequency is in days).
Matplotlib uses the same default origin as pandas since version 3.3.0 released in July 2020 but the dates are always numbered in terms of days:
Matplotlib represents dates using floating point numbers specifying the number of days since a default epoch of 1970-01-01 UTC; for example, 1970-01-01, 06:00 is the floating point number 0.25.
You can check what numbers are being used for the scale by running ax.get_xticks(), with ax = df.plot() when using pandas.
As you might have guessed, this means no date conversion is needed when the time series has a frequency in days, as illustrated here with a simple custom tick locator and formatter:
ax = df_day.plot(figsize=(10,5))
# Create custom ticks using matplotlib date tick locator and formatter
loc = mdates.MonthLocator(interval=2)
ax.xaxis.set_major_locator(loc)
fmt = mdates.DateFormatter('%b\n%Y')
ax.xaxis.set_major_formatter(fmt)
This particular case makes it convenient for keeping other pandas default settings for the x-axis limits and minor x ticks. But this is an exception to the general rule.
To be able to use mdates tick locators and formatters with a pandas plot of a time series of any type of frequency, you need to use the (long-existing yet absent-from-the-docstring and barely-documented) x_compat=True argument. The following example illustrates its use with the same dataset resampled to a monthly frequency. It may often be the case that you just want to slightly tweak the default pandas format, so in the following example, the default format is recreated from scratch to show what methods can be used to adjust it:
# Resample time series to monthly frequency and plot it using date
# numbers that are compatible with mdates
df_month = df_day.resample('MS').sum()
ax = df_month.plot(figsize=(10,5), x_compat=True)
# Set major and minor date tick locators
maj_loc = mdates.MonthLocator(bymonth=np.arange(1,12,2))
ax.xaxis.set_major_locator(maj_loc)
min_loc = mdates.MonthLocator()
ax.xaxis.set_minor_locator(min_loc)
# Set major date tick formatter
zfmts = ['', '%b\n%Y', '%b', '%b-%d', '%H:%M', '%H:%M']
maj_fmt = mdates.ConciseDateFormatter(maj_loc, zero_formats=zfmts, show_offset=False)
ax.xaxis.set_major_formatter(maj_fmt)
ax.figure.autofmt_xdate(rotation=0, ha='center')
ax.set_xlim(df_month.index.min(), df_month.index.max());
Documentation: pd.date_range, date format codes, mdates.ConciseDateFormatter, fig.autofmt_xdate
I had a hard time trying to get #Serenity answer to work because I'm working directly with Matplotlib instead of plotting the Pandas dataset. So if you are one of these, my answer might help.
Plotting with Matplotlib.plot()
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
# Process dataset
bitcoin['Date'] = pd.to_datetime(bitcoin['Date'])
bitcoin['Open'] = pd.to_numeric(bitcoin['Open'])
# Plot
plt.figure()
plt.plot(bitcoin['Date'], bitcoin['Open'])
ax = plt.gca()
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=4))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%d-%m-%Y'))
plt.gcf().autofmt_xdate() # Rotation
plt.show()
bitcoin[['Date', 'Open']].head()
Date Open
0 2017-09-05 4228.29
1 2017-09-04 4591.63
2 2017-09-03 4585.27
3 2017-09-02 4901.42
4 2017-09-01 4701.76

In Pandas, can't show x-axis dates nicely and y-axis in unwanted logs

Here's my chart:
I have two issues; I can't get the datetime objects on the x-axis to come out nicely (i.e. January 1st, 2013) and I would like the y-axis labels to be absolute values, not log values.
Here's my annotated code: (date_sorted is my Pandas dataframe)
fig = plt.figure()
date_sorted.plot( x = ["ReleaseDate"], y = ["DomesticTotalGross"])
plt.title("Domestic Total Gross over Time")
plt.xticks(rotation=45)
plt.yscale('linear') # ---- this doesn't seem to do anything
plt.ticklabel_format(useOffset=False) #--- this gives this error: AttributeError: This method only works with the ScalarFormatter.
fig.autofmt_xdate() #thought this was supposed to convert my x-axis datetime objects into nice dates?
Regarding the date format, one way to achieve your objective would be to reset your index to a date format instead of datetime:
date_sorted.set_index([ts.date for ts in date_sorted.index]).plot(x="ReleaseDate", y="DomesticTotalGross")

Dates in the xaxis for a matplotlib plot with imshow

So I am new to programming with matplotlib. I have created a color plot using imshow() and an array. At first the axis were just the row and column number of my array. I used extent = (xmin,xmax,ymin,ymax) to get the x-axis in unix time and altitude, respectively.
I want to change the x-axis from unix time (982376726,982377321) to UT(02:25:26, 02:35:21). I have created a list of the time range in HH:MM:SS. I am not sure how to replace my current x-axis with these new numbers, without changing the color plot (or making it disappear).
I was looking at datetime.time but I got confused with it.
Any help would be greatly appreciated!
I have put together some example code which should help you with your problem.
The code first generates some randomised data using numpy.random. It then calculates your x-limits and y-limits where the x-limits will be based off of two unix timestamps given in your question and the y-limits are just generic numbers.
The code then plots the randomised data and uses pyplot methods to convert the x-axis formatting to nicely represented strings (rather than unix timestamps or array numbers).
The code is well commented and should explain everything you need, if not please comment and ask for clarification.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime as dt
# Generate some random data for imshow
N = 10
arr = np.random.random((N, N))
# Create your x-limits. Using two of your unix timestamps you first
# create a list of datetime.datetime objects using map.
x_lims = list(map(dt.datetime.fromtimestamp, [982376726, 982377321]))
# You can then convert these datetime.datetime objects to the correct
# format for matplotlib to work with.
x_lims = mdates.date2num(x_lims)
# Set some generic y-limits.
y_lims = [0, 100]
fig, ax = plt.subplots()
# Using ax.imshow we set two keyword arguments. The first is extent.
# We give extent the values from x_lims and y_lims above.
# We also set the aspect to "auto" which should set the plot up nicely.
ax.imshow(arr, extent = [x_lims[0], x_lims[1], y_lims[0], y_lims[1]],
aspect='auto')
# We tell Matplotlib that the x-axis is filled with datetime data,
# this converts it from a float (which is the output of date2num)
# into a nice datetime string.
ax.xaxis_date()
# We can use a DateFormatter to choose how this datetime string will look.
# I have chosen HH:MM:SS though you could add DD/MM/YY if you had data
# over different days.
date_format = mdates.DateFormatter('%H:%M:%S')
ax.xaxis.set_major_formatter(date_format)
# This simply sets the x-axis data to diagonal so it fits better.
fig.autofmt_xdate()
plt.show()

Categories

Resources