Eric Hansen
Eric Hansen

Reputation: 1809

Adding UTC time axis to plot with local time in time format

I have a DataFrame plot which looks something like this

enter image description here

The index on the plotted DataFrame is as follows

In[102]: res.index
Out[102]: 
Index([00:00:00, 00:05:00, 00:10:00, 00:15:00, 00:20:00, 00:25:00, 00:30:00,
       00:35:00, 00:40:00, 00:45:00,
       ...
       23:10:00, 23:15:00, 23:20:00, 23:25:00, 23:30:00, 23:35:00, 23:40:00,
       23:45:00, 23:50:00, 23:55:00],
      dtype='object', name='time', length=288)

The times depicted on the x-axis are in Central Standard Time, and I would like to add a secondary x-axis to the top of the plot with the corresponding UTC times. I'm aware that this will make the numbers go 6:00 ... 22:00 ... 4:00 but that is fine. How can I do so?


My attempts

I know that a second x-axis can be created with the following, and I managed to procure what seems like the xticks from the first x-axis with the following - my next step was going to be to try to determine how to shift these times to UTC - which I am still unsure on ideas for.

ax2 = ax.twiny()
ax2.set_xlabel('UTC time')
local_ticks = ax.get_xticks().tolist()

However upon plotting this rudimentary step, I found that get_xticks() has yielded one extra tick than the original x-axis, and so they are not aligned.

enter image description here

Correspondingly, treating these as seconds and attempting to perform some further manipulations to go seconds->CST->UTC is still going to leave me with the ugly scaling "double white-line" at each tick. Ideally, I want the exact same ticks along the secondary x-axis, except with appropriately labelled UTC times.


Sample generation

If you want to generate a plot like the first one I provided, I threw this together.

import datetime

import numpy as np
import random
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use('ggplot')
start_date = datetime.datetime(2016, 9, 5)
end_date = datetime.datetime.now()

dts = []
cur_date = start_date 
while cur_date < end_date:
    dts.append((cur_date, np.random.uniform(low=0.0, high=12.0)))
    cur_date += datetime.timedelta(minutes=np.random.uniform(10, 20))
dts = pd.DataFrame(dts, columns=['timestamp', 'lag'])
dts.index = dts.timestamp
dts.drop('timestamp', axis=1, inplace=True)

cur_time = datetime.datetime(1, 1, 1, 0, 0)
aggs = []
while cur_time < datetime.datetime(1, 1, 1, 23, 59, 0):
    aggs.append((cur_time.time(), 
                dts.between_time(cur_time.time(),
                (cur_time + datetime.timedelta(minutes=5)).time(), 
                include_end=False)['lag'].mean()))
    cur_time += datetime.timedelta(minutes=5)

res = pd.DataFrame(aggs, columns = ['time', 'lag'])
res.index = res.time
res.drop('time', axis=1, inplace=True)

ax = res.plot(figsize=(15, 10))

Upvotes: 2

Views: 2095

Answers (1)

PrestonH
PrestonH

Reputation: 566

I got it to work by explicitly setting the x-axis tick labels rather than letting matplotlib decide. This can be done with:

ax.set_xticks([2*60*60*i for i in range(0,13)])

This has the added benefit that the listed times aren't random; you can set the increments to be one hour, 2 hours, ... In the above snippet, the 2* part indicates 2 hour increments.

Then, in the rest of the code you need to copy the axis over to ax2, and manually set the labels. Here's how I did that:

# Begin solution

ax.set_xticks([2*60*60*i for i in range(0,13)])

local_ticks = ax.get_xticks().tolist()
labels = [str(datetime.timedelta(seconds=(second+6*60*60))) for second in local_ticks]

ax2 = ax.twiny()
plt.sca(ax2)
ax2.set_xlabel('UTC time')
plt.xticks(local_ticks, labels)

Here's the final image:

enter image description here

Changing the '1 day' part in the above axis can be done manually with string parsing.

Actually, the easiest fix is to simply add ax2.set_xticks(ax.get_xticks()). I think that the problem is that you don't explicity state the xticks for axis 1, but you do for axis 2; by the time the plot is created, the xticks have been recalculated while the xticks for axis 2 remain the same.

Upvotes: 2

Related Questions