5

I've tried to duplicate plotted graphs originally created with flotr2 for pdf output with matplotlib. I must say that flotr is way easyer to use... but that aside - im currently stuck at trying to format the dates /times on x-axis to desired format, which is hours:minutes with interval of every 2 hours, if period on x-axis is less than one day and year-month-day format if period is longer than 1 day with interval of one day.

I've read through numerous examples and tried to copy them, but outcome remains the same which is hours:minutes:seconds with 1 to 3 hour interval based on how long is the period. enter image description here

My code:

colorMap = {
        'speed': '#3388ff',
        'fuel': '#ffaa33',
        'din1': '#3bb200',
        'din2': '#ff3333',
        'satellites': '#bfbfff'
}

otherColors = ['#00A8F0','#C0D800','#CB4B4B','#4DA74D','#9440ED','#800080','#737CA1','#E4317F','#7D0541','#4EE2EC','#6698FF','#437C17','#7FE817','#FBB117']

plotMap = {}

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.dates as dates
fig = plt.figure(figsize=(22, 5), dpi = 300, edgecolor='k')
ax1 = fig.add_subplot(111)

realdata = data['data']
keys = realdata.keys()
if 'speed' in keys:
    speed_index = keys.index('speed')
    keys.pop(speed_index)
    keys.insert(0, 'speed')

i = 0
for key in keys:
    if key not in colorMap.keys():
        color = otherColors[i]
        otherColors.pop(i)
        colorMap[key] = color
        i += 1

label = u'%s' % realdata[keys[0]]['name']
ax1.set_ylabel(label)

plotMap[keys[0]] = {}
plotMap[keys[0]]['label'] = label

first_dates = [ r[0] for r in realdata[keys[0]]['data']]

date_range = first_dates[-1] - first_dates[0]


ax1.xaxis.reset_ticks()

if date_range > datetime.timedelta(days = 1):
    ax1.xaxis.set_major_locator(dates.WeekdayLocator(byweekday = 1, interval=1))
    ax1.xaxis.set_major_formatter(dates.DateFormatter('%Y-%m-%d'))
else:
    ax1.xaxis.set_major_locator(dates.HourLocator(byhour=range(24), interval=2))
    ax1.xaxis.set_major_formatter(dates.DateFormatter('%H:%M'))

ax1.xaxis.grid(True)

plotMap[keys[0]]['plot'] = ax1.plot_date(
                                dates.date2num(first_dates), 
                                [r[1] for r in realdata[keys[0]]['data']], colorMap[keys[0]], xdate=True)

if len(keys) > 1:
    first = True
    for key in keys[1:]:
        if first:
            ax2 = ax1.twinx()
            ax2.set_ylabel(u'%s' % realdata[key]['name'])
            first = False
        plotMap[key] = {}
        plotMap[key]['label'] = u'%s' % realdata[key]['name']
        plotMap[key]['plot'] = ax2.plot_date(
                                    dates.date2num([ r[0] for r in realdata[key]['data']]), 
                                    [r[1] for r in realdata[key]['data']], colorMap[key], xdate=True)


plt.legend([value['plot'] for key, value in plotMap.iteritems()], [value['label'] for key, value in plotMap.iteritems()], loc = 2)

plt.savefig(path +"node.png", dpi = 300, bbox_inches='tight')

could someone point out why im not getting desired results, please?

Edit1:

I moved the formatting block after the plotting and seem to be getting better results now. They are still now desired results though. If period is less than day then i get ticks after every 2 hours (interval=2), but i wish i could get those ticks at even hours not uneven hours. Is that possible?

if date_range > datetime.timedelta(days = 1):
    xax.set_major_locator(dates.DayLocator(bymonthday=range(1,32), interval=1))
    xax.set_major_formatter(dates.DateFormatter('%Y-%m-%d'))
else:
    xax.set_major_locator(dates.HourLocator(byhour=range(24), interval=2))
    xax.set_major_formatter(dates.DateFormatter('%H:%M'))

Edit2: This seemed to give me what i wanted:

if date_range > datetime.timedelta(days = 1):
    xax.set_major_locator(dates.DayLocator(bymonthday=range(1,32), interval=1))
    xax.set_major_formatter(dates.DateFormatter('%Y-%m-%d'))
else:
    xax.set_major_locator(dates.HourLocator(byhour=range(0,24,2)))
    xax.set_major_formatter(dates.DateFormatter('%H:%M'))

Alan

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Odif Yltsaeb
  • 5,575
  • 12
  • 49
  • 80
  • did you ever get this sorted out? – tacaswell Jun 05 '13 at 21:34
  • Yes actually i did. If you look at the edit2, then you can see what gave me what i wanted. – Odif Yltsaeb Jun 06 '13 at 11:07
  • you should put edit2 as an answer and the accept it. That will mark the problem as solved which will help future users more than having the solution in the question (and you can get rep from your answer). – tacaswell Jun 06 '13 at 13:58

2 Answers2

10

You are making this way harder on your self than you need to. matplotlib can directly plot against datetime objects. I suspect your problem is you are setting up the locators, then plotting, and the plotting is replacing your locators/formatters with the default auto versions. Try moving that block of logic about the locators to below the plotting loop.

I think that this could replace a fair chunk of your code:

d = datetime.timedelta(minutes=2)
now =  datetime.datetime.now()
times = [now + d * j for j in range(500)]
ax = plt.gca() # get the current axes
ax.plot(times, range(500))

xax = ax.get_xaxis() # get the x-axis
adf = xax.get_major_formatter() # the the auto-formatter


adf.scaled[1./24] = '%H:%M'  # set the < 1d scale to H:M
adf.scaled[1.0] = '%Y-%m-%d' # set the > 1d < 1m scale to Y-m-d
adf.scaled[30.] = '%Y-%m' # set the > 1m < 1Y scale to Y-m
adf.scaled[365.] = '%Y' # set the > 1y scale to Y

plt.draw()

doc for AutoDateFormatter

tacaswell
  • 84,579
  • 22
  • 210
  • 199
  • thanks. this works (no errors!)... and yet it does not. It does not seem to do the formatting: When i open period longer than a day, then i dont get the %Y-%m-%d format but get %H:%M:%S format instead which is not specified anywhere. I moved my own formatting block after plotting and seem to get better results now - even though they are still not desired results. – Odif Yltsaeb Jan 22 '13 at 08:52
  • did you copy the keys as floats (not ints)? – tacaswell Jan 22 '13 at 14:12
  • i do not understand what keys (floats?ints?) are you talking about. In any case i got what i needed :) – Odif Yltsaeb Jan 23 '13 at 07:00
  • @Zayatzz what version of mpl are you using? – tacaswell Jan 24 '13 at 14:41
  • whichever it came with from ubuntu repo - matplotlib.__version__ prints out 1.0.1 – Odif Yltsaeb Jan 25 '13 at 07:40
  • Nice. But this no longer works in MatplotLib 2 (2.0.2). `AttributeError: 'ScalarFormatter' object has no attribute 'scaled'` Do you have an updated way of using a formatter that adjusts its precision? – Brent Faust Oct 13 '17 at 23:50
  • 1
    @BrentFaust The issue is that your formatter is as `ScalarFormatter` not a `AutoDateFormatter` is the above code assumes. – tacaswell Oct 14 '17 at 19:48
9

I achieved what i wanted by doing this:

if date_range > datetime.timedelta(days = 1):
    xax.set_major_locator(dates.DayLocator(bymonthday=range(1,32), interval=1))
    xax.set_major_formatter(dates.DateFormatter('%Y-%m-%d'))
else:
    xax.set_major_locator(dates.HourLocator(byhour=range(0,24,2)))
    xax.set_major_formatter(dates.DateFormatter('%H:%M'))
Odif Yltsaeb
  • 5,575
  • 12
  • 49
  • 80