how to deal with plot x axis datetime not continue - python

Here is my example code:
import sys
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib.dates as md
import pymysql
import numpy as np
import datetime as dt
import matplotlib.ticker as ticker
import pylab as pl
t=[4100,4105,4100,4105,4100,4105,4100,4105,4100,4105,4100,4105]
s=[dt.datetime.now(),dt.datetime.now()+dt.timedelta(seconds=1),dt.datetime.now()+dt.timedelta(seconds=2),dt.datetime.now()+dt.timedelta(seconds=3),
dt.datetime.now()+dt.timedelta(seconds=4),
dt.datetime.now()+dt.timedelta(seconds=5),
dt.datetime.now()+dt.timedelta(seconds=6),
dt.datetime.now()+dt.timedelta(seconds=7),
dt.datetime.now()+dt.timedelta(seconds=8),
dt.datetime.now()+dt.timedelta(seconds=9),
dt.datetime.now()+dt.timedelta(seconds=10),
dt.datetime.now()+dt.timedelta(minutes=1)]
N = len(s)
ind = np.arange(N) # the evenly spaced plot indices
def format_date(x, pos=None):
thisind = np.clip(int(x+0.5), 0, N-1)
return pl.num2date(x).strftime('%Y-%m-%d %H:%M:%S')
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(s, t)
ax.xaxis.set_major_formatter(ticker.FuncFormatter(format_date))
fig.autofmt_xdate()
plt.show()
Please notice the last datetime element is
dt.datetime.now()+dt.timedelta(minutes=1)
This means the x axis does not continue between the last element and it's previous element, this time range has no data.
When I see the plot above code draw, the result image is just like w character, except the last element, because it's time interval is not same as others, so it's far away from it's previous element.
But what I really need is: skip the empty time range of x-axis, and let the last element have same interval in x-axis as others, and remain signal it's time correctly .
I learnt this code mainly from:
https://matplotlib.org/2.1.2/gallery/api/date_index_formatter.html
but this example seems only deal with skip weekend whole day, cannot skip exactly time range, for example skip 1 hour 20 min in x-axis, can anybody tell me how to finish it ? Thanks.

Related

Matplotlib x-axis ticks, fixed location for dates

In the timeline plot I’m making, I want date tickers to show only specified dates. (In my example I show tickers for events ‘A’, but it can be any list on tickers). I found how to do it when x-axis data is numeric (upper subplot in my example), but this won’t work with timestamp date type (bottom plot).
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.ticker as ticker
myData = pd.DataFrame({'date':['2019-01-15','2019-02-10','2019-03-20','2019-04-17','2019-05-23','2019-06-11'],'cnt':range(6),'event':['a','b','a','b','a','b']})
myData['date'] = [pd.Timestamp(j) for j in myData['date']]
start = pd.Timestamp('2019-01-01')
stop = pd.Timestamp('2019-07-01')
inxa = myData.loc[myData['event'] == 'a'].index
inxb = myData.loc[myData['event'] == 'b'].index
# create two plots, one with 'cnt' as x-axis, the other 'dates' on x-axis.
fig, ax = plt.subplots(2,1,figsize=(16,9))
ax[0].plot((0,6),(0,0), 'k')
ax[1].plot((start, stop),(0,0))
for g in inxa:
ax[0].plot((myData.loc[g,'cnt'],myData.loc[g,'cnt']),(0,1),c='r')
ax[1].plot((myData.loc[g,'date'],myData.loc[g,'date']),(0,1),c='r')
for g in inxb:
ax[0].plot((myData.loc[g,'cnt'],myData.loc[g,'cnt']),(0,2),c='b')
ax[1].plot((myData.loc[g,'date'],myData.loc[g,'date']),(0,2),c='b')
xlist0 = myData.loc[myData['event']=='a','cnt']
xlist1 = myData.loc[myData['event']=='a','date']
ax[0].xaxis.set_major_locator(ticker.FixedLocator(xlist0))
# ax[1].xaxis.set_major_locator(**???**)
Couldn't find a sufficient duplicate, maybe I didn't look hard enough. There are a number of ways to do this:
Converting to numbers first or using the underlying values of a Pandas DateTime Series
xticks = [mdates.date2num(z) for z in xlist1]
# or
xticks = xlist1.values
and at least a couple ways to use it/them
ax[1].xaxis.set_major_locator(ticker.FixedLocator(xticks))
ax[1].xaxis.set_ticks(xticks)
Date tick labels
How to set the xticklabels for date in matplotlib
how to get ticks every hour?
...

Matplotlib labeling x-axis with time stamps, deleting extra 0's in microseconds

I am plotting over a period of seconds and have time as the labels on the x-axis. Here is the only way I could get the correct time stamps. However, there are a bunch of zeros on the end. Any idea how to get rid of them??
plt.style.use('seaborn-whitegrid')
df['timestamp'] = pd.to_datetime(df['timestamp'])
fig, ax = plt.subplots(figsize=(8,4))
seconds=MicrosecondLocator(interval=500000)
myFmt = DateFormatter("%S:%f")
ax.plot(df['timestamp'], df['vibration(g)_0'], c='blue')
ax.xaxis.set_major_locator(seconds)
ax.xaxis.set_major_formatter(myFmt)
plt.gcf().autofmt_xdate()
plt.show()
This produces this image. Everything looks perfect except for all of the extra zeros. How can I get rid of them while still keeping the 5?
I guess you would want to simply cut the last 5 digits out of the string. That's also what answers to python datetime: Round/trim number of digits in microseconds suggest.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.dates import MicrosecondLocator, DateFormatter
from matplotlib.ticker import FuncFormatter
x = np.datetime64("2018-11-30T00:00") + np.arange(1,4, dtype="timedelta64[s]")
fig, ax = plt.subplots(figsize=(8,4))
seconds=MicrosecondLocator(interval=500000)
myFmt = DateFormatter("%S:%f")
ax.plot(x,[2,1,3])
def trunc_ms_fmt(x, pos=None):
return myFmt(x,pos)[:-5]
ax.xaxis.set_major_locator(seconds)
ax.xaxis.set_major_formatter(FuncFormatter(trunc_ms_fmt))
plt.gcf().autofmt_xdate()
plt.show()
Note that this format is quite unusual; so make sure the reader of the plot understands it.

matplotlib HourLocator steals my x labels

When I plot my half hourly time series, my axis labels are odd (like 16:33:12h or so...)
When I use HourLocator to fix this (16:33h -> 16:00h), then my x label disappear completely.
My code is:
from datetime import date, timedelta, datetime, time
from matplotlib.dates import DayLocator, HourLocator
import matplotlib.pyplot as plt
start = time(0, 0, 0)
delta = timedelta(minutes=30)
times = []
for i in range(len(day_load)):
dt = datetime.combine(date.today(), time(0, 0)) + delta * i
times.append(dt.time())
load = [i/48 for i in range(48)]
fig, ax = plt.subplots()
ax.plot_date(times, load)
ax.xaxis.set_major_locator(HourLocator())
plt.show()
How can I achieve "even" labels (in a best practice way - I don't want to rewrite code for every other plot again).
When I comment second last line, I get normal "odd" labels :(
Thanks for answers!
There are two main issues:
You need to work with complete datetime objects, not only with time. So instead of dt.time() you should append dt directly.
You not only need a locator, but also a formatter to produce nice ticklabels. Here you may use a DateFormatter("%H:%M") to show hours and minutes.
Complete code:
from datetime import date, timedelta, datetime, time
from matplotlib.dates import DayLocator, HourLocator,DateFormatter
import matplotlib.pyplot as plt
start = time(0, 0, 0)
delta = timedelta(minutes=30)
times = []
n=48
for i in range(n):
# use complete datetime object, not only time
dt = datetime.combine(date.today(), time(0, 0)) + delta * i
times.append(dt)
load = [i/float(n) for i in range(n)]
fig, ax = plt.subplots()
ax.plot_date(times, load)
# set a locator, as well as a formatter
ax.xaxis.set_major_locator(HourLocator())
ax.xaxis.set_major_formatter(DateFormatter("%H:%M"))
#optionally rotate the labels and make more space for them
fig.autofmt_xdate()
plt.show()
This is an old question, but for anyone else facing this problem: you can leave the data types what they should be and use matplotlib.ticker.IndexLocator to get the axis ticks located nicely.
For example,
locator = mpl.ticker.IndexLocator(base=2 * 60 * 60, offset=0)
ax.xaxis.set_major_locator(locator)
places ticks at every two full hours, i.e. it uses the total number of seconds since midnight, regardless of the length of the intervals in the data.
Your code doesn't run, because day_load is undefined and I get other issues as well.
Not an answer, but I think you're better of using pandas. It makes it easy to create a date_range, and plotting is handled pretty well without adjustments.
from scipy import stats
import pandas as pd
n = 20
index = pd.date_range(start = '2016-01-01', periods = n, freq='1H')
df = pd.DataFrame(index = index)
df["value"] = stats.norm().rvs(n)
df.plot()

matplotlib.pyplot.annotate() : place a text on subplot with 'axes fraction' coordinates

I have some trouble with matplotlib.pyplot.annotate() when I use big numbers in the horizontal axis, for example doing time series with time data in "seconds since epoch" where the data will reach 10^9.
Here is an example:
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import os.path
import random
import calendar
save_path='my_path'
fig,ax=plt.subplots(2,sharex=True)
fig.set_size_inches(6,5)
a=int(calendar.timegm((2009,1,1,0,0,0)))
b=int(calendar.timegm((2009,2,1,0,0,0)))
x=xrange(a,b,(b-a)/100)
#x=xrange(0,b-a,(b-a)/100)
y=[random.random() for i in x]
z=[random.random() for i in x]
ax[0].scatter(x,y)
ax[1].scatter(x,z)
for sub in ax:
sub.set_xlim(x[0],x[-1])
ax[0].annotate('test',(0.1,0.1),textcoords='axes fraction')
ax[1].annotate('test',(0.9,0.9),textcoords='axes fraction')
fig.savefig(os.path.join(save_path,'test.png'),bbox_inches='tight')
plt.close()
With x=xrange(a,b,(b-a)/100)
I get:
While with x=xrange(0,b-a,(b-a)/100)
I get:
I don't understand why the first case doesn't work but the second case works as expected, I just reduced the numbers basically.
I have no problems if I use 'data' coordinates though.

Not write out all dates on an axis, Matplotlib

Take a look at this example:
import datetime as dt
from matplotlib import pyplot as plt
import matplotlib.dates as mdates
x = []
d = dt.datetime(2013, 7, 4)
for i in range(30):
d = d+dt.timedelta(days=1)
x.append(d)
y = range(len(x))
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%d-%m-%Y'))
plt.gca().xaxis.set_major_locator(mdates.DayLocator())
plt.gcf().autofmt_xdate()
plt.bar(x,y)
plt.show()
The code writes out dates on the x-axis in the plot, see the picture below. The problem is that the dates get clogged up, as seen in the picture. How to make matplotlib to only write out every fifth or every tenth coordinate?
You can specify an interval argument to the DateLocator as in the following. With e.g. interval=5 the locator places ticks at every 5th date. Also, place the autofmt_xdate() after the bar method to get the desired output.
import datetime as dt
from matplotlib import pyplot as plt
import matplotlib.dates as mdates
x = []
d = dt.datetime(2013, 7, 4)
for i in range(30):
d = d+dt.timedelta(days=1)
x.append(d)
y = range(len(x))
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%d-%m-%Y'))
plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=5))
plt.bar(x, y, align='center') # center the bars on their x-values
plt.title('DateLocator with interval=5')
plt.gcf().autofmt_xdate()
plt.show()
With interval=3 you will get a tick for every 3rd date:

Categories