axes.set_xticklabels breaks datetime format - python

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)

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.

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

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"

Matplotlib : Default Resolution of Plot Mouse-over Values

I'm plotting a time-series with MatplotLib. The time series values, x-axis, have the resolution '%d/%m/%y %H:%M', but only month and year are indicated in the mouse over.
My question is how can one override the default and set what datetime items that should be shown during mouse over?
My preference is to show at least day, month, and year
.....................................................................
For example, this is a screenshot where I did a mouse-over for one of the points:
As you can see the (bottom LHS corner) x value gives a date which indicated only month and year.
When zoomed in, day, month and year are shown:
The values that are shown on mouse-over are controlled by the ax.format_coord method, which is meant to be monkey-patched by a user-supplied method when customization is needed.
For example:
import matplotlib.pyplot as plt
def formatter(x, y):
return '{:0.0f} rainbows, {:0.0f} unicorns'.format(10*x, 10*y)
fig, ax = plt.subplots()
ax.format_coord = formatter
plt.show()
There are also the ax.format_xdata and ax.format_ydata which the default ax.format_coord calls, to allow easier customization of only the x or y components.
For example:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.format_xdata = '{:0.1f}meters'.format
plt.show()
Note that I passed in the string's format method, but it could have just as easily been a lambda or any arbitrary method that expects a single numeric argument.
By default, format_xdata and format_ydata use the axis's major tick formatter, which is why you're getting day-level resolution for your date axis.
However, you'll also need to convert matplotlib's internal numeric date format back to a "proper" datetime object. Therefore, you can control your formatting similar to the following:
import datetime as dt
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
fig, ax = plt.subplots()
ax.xaxis_date()
ax.set_xlim(dt.datetime(2015, 1, 1), dt.datetime(2015, 6, 1))
ax.format_xdata = lambda d: mdates.num2date(d).strftime('%d/%m/%y %H:%M')
plt.show()

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()

Custom ticks autoscaled when using imshow?

I am trying to change the value of the ticks on the x-axis an imshow plot using the following code:
import matplotlib.pyplot as plt
import numpy as np
def scale_xaxis(number):
return(number+1001)
data = np.array([range(10),range(10,20)])
fig = plt.figure(figsize=(3,5))
ax = fig.add_subplot(111)
ax.imshow(data,aspect='auto')
ax.autoscale(False)
xticks = ax.get_xticks()
ax.xaxis.set_ticklabels(scale_xaxis(xticks))
plt.savefig("test.png")
Resulting image http://ubuntuone.com/2Y5ujtlEkEnrlTcVUxvWLU
However the x-ticks overlap and have "non-round" values. Is there some way for matplotlib to automatically do this? Either by using set_ticklabels or some other way?
Also look into using extent (doc) to let matplotlib do all the thinking about how to put in the tick labels and add in an arbitrary shift:
data = np.array([range(10),range(10,20)])
fig = plt.figure(figsize=(3,5))
ax = fig.add_subplot(111)
ax.imshow(data,aspect='auto',extent=[10000,10010,0,1])
If you definitely want do to it my hand, you might be better off setting the formatter and locator of the axis to get what you want (doc).
import matplotlib.pyplot as plt
import numpy as np
def scale_xaxis(number):
return(number+1001)
def my_form(x,pos):
return '%d'%scale_xaxis(x)
data = np.array([range(10),range(10,20)])
fig = plt.figure(figsize=(3,5))
ax = fig.add_subplot(111)
ax.imshow(data,aspect='auto')
ax.autoscale(False)
ax.xaxis.set_major_locator(matplotlib.ticker.MultipleLocator(int(2)))
ax.xaxis.set_major_formatter(matplotlib.ticker.FuncFormatter(my_form))
The locator needs to be set to make sure that ticks don't get put at non-integer locations which are then forcible cast to integers by the formatter (which would leave them in the wrong place)
related questions:
matplotlib: format axis offset-values to whole numbers or specific number
removing leading 0 from matplotlib tick label formatting
There are several ways to do this.
You can:
Pass in an array of ints instead of an array of floats
Pass in an array of formatted strings
Use a custom tick formatter
The last option is overkill for something this simple.
As an example of the first option, you'd change your scale_xaxis function to be something like this:
def scale_xaxis(numbers):
return numbers.astype(int) + 1001
Note that what you're getting out of ax.get_xticks is a numpy array instead of a single value. Thus, we need to do number.astype(int) instead of int(number).
Alternately, we could return a series of formatted strings. set_xticklabels actually expects a sequence of strings:
def scale_xaxis(numbers):
return ['{:0.0f}'.format(item + 1001) for item in numbers]
Using a custom tick formatter is overkill here, so I'll leave it out for the moment. It's quite handy in the right situation, though.

Categories

Resources