Python - Matplotlib - Setting X axis range - Plotting Packets Per Second - python

I'm working on a script that plots a pps count versus time from a csv file. Everything works up to this point however I can't seem to figure out how to change the interval at which the ticks/tick-labels occur at on the X-axis, I want there to be 60 timestamps/tick instead of the default. Here's where I'm at:
import matplotlib
matplotlib.use('Agg')
from matplotlib.mlab import csv2rec
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
from pylab import *
data = csv2rec('tpm_counter.log', names=['packets', 'time']) # reads in the data from the csv as column 1 = tweets column 2 = time
rcParams['figure.figsize'] = 12, 4 # this sets the ddimensions of the graph to be made
rcParams['font.size'] = 8
fig = plt.figure()
plt.plot(data['time'], data['packets']) # this sets the fields to be graphed
plt.xlabel("Time(minutes)") # this sets the x label
plt.ylabel("Packets") # this sets the y label
plt.title("Packets Capture Log: Packets Per Minute") # this sets the title
#plt.xticks(range(60)) --- nothing shows on the graph if I use this
fig.autofmt_xdate(bottom=0.2, rotation=90, ha='left')
plt.savefig('tpm.png') # this sets the output file name
I've tried plt.xticks(range(60)) but when the plot generates, it has nothing on it.

bmu's answer above works. But it might be helpful to others to see a more general way of rescaling the xticks and xlabels in a plot. I have generated some example data instead of using a csv file.
import matplotlib
import matplotlib.pyplot as plt
from pylab import *
time=range(5000) #just as an example
data=range(5000) # just as an example
fig = plt.figure()
plt.plot(time,data) # this sets the fields to be graphed
plt.xlabel("Every 60th point") # this sets the x label
plt.ylabel("Data") # this sets the y label
plt.title("Rescaling axes") # this sets the title
#Slice the data into every 60th point. We want ticks at these points
tickpos=data[::60]
#Now create a list of labels for each point...
ticklabels=[]
for point in tickpos:
ticklabels.append(str(point/60))
plt.xticks(tickpos,ticklabels) # set the xtick positions and labels
plt.savefig('tpm.png')

Have a look at the date demo.
You can use the HourLocator or the MinuteLocator together with an adapted DateFormatter.
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot_date(data['time'], data['packets'])
hours = mdates.HourLocator()
fmt = mdates.DateFormatter('%H:%M')
ax.xaxis.set_major_locator(hours)
ax.xaxis.set_major_formatter(fmt)

Related

How to set major locator of secondary axis

I want to set a major locator for a secondary axis with 24 hour intervals, but it’s not valid and does not result in any errors.
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator
dt=pd.DataFrame({'time':[100000,200000,500000,800000],'value':[1,2,4,6]})
plot= plt.subplot()
plot.plot(dt.time,dt.value)
x_major_locator=plt.MultipleLocator(100000)
plot.xaxis.set_major_locator(x_major_locator)
plot.set_xlabel("Second")
s2h=lambda s: s/3600
h2s=lambda h: h*3600
ax2=plot.secondary_xaxis("top",functions=(s2h,h2s))
x_major_locator=plt.MultipleLocator(24)
ax2.xaxis.set_major_locator(x_major_locator)
ax2.set_xlabel("Hour")
plt.show()
I am not sure why the ticks are not being modified; however, one way to get around this is to create a new subplot axis that shares y. The following will work as long as you do not change the limits because the lines are plotted over each other. If do need to change the limits, then you can do a hacky approach by plotting the line in negative y space and setting the ylims which will preserve your top x-axis.
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator
dt=pd.DataFrame({'time':[100000,200000,500000,800000],'value':[1,2,4,6]})
plot= plt.subplot()
plot.plot(dt.time,dt.value)
x_major_locator=MultipleLocator(100000)
plot.xaxis.set_major_locator(x_major_locator)
plot.set_xlabel("Second")
s2h=lambda s: s/3600
h2s=lambda h: h*3600
#ax2=plot.secondary_xaxis("top",functions=(s2h,h2s))
ax2 = plot.twiny()
ax2.plot(s2h(dt.time),dt.value)
x_major_locator = MultipleLocator(24)
ax2.xaxis.set_major_locator(x_major_locator)
ax2.set_xlabel("Hour")
#ax2.set_xlim(0,200) #If you do this, you get 2 lines
plt.show()

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?
...

How to plot time series that consists of different dates but same timestamps on one graph in matplotlib

I have data that shows some values collected on three different dates: 2015-01-08, 2015-01-09 and 2015-01-12. For each date there are several data points that have timestamps.
Date/times are in a list and it looks as follows:
['2015-01-08-09:00:00', '2015-01-08-10:00:00', '2015-01-08-11:00:00', '2015-01-08-12:00:00', '2015-01-08-13:00:00', '2015-01-09-14:00:00', '2015-01-09-15:00:00', '2015-01-09-16:00:00', '2015-01-12-09:00:00', '2015-01-12-10:00:00', '2015-01-12-11:00:00']
On the other hand I have corresponding values (floats) in another list:
[12210.0, 12210.0, 12180.0, 12240.0, 12250.0, 12420.0, 12390.0, 12400.0, 12380.0, 12450.0, 12460.0]
To put all this together and plot a graph I use following code:
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.dates as md
import dateutil
from matplotlib.font_manager import FontProperties
timestamps = ['2015-01-08-09:00:00', '2015-01-08-10:00:00', '2015-01-08-11:00:00', '2015-01-08-12:00:00', '2015-01-08-13:00:00', '2015-01-09-14:00:00', '2015-01-09-15:00:00', '2015-01-09-16:00:00', '2015-01-12-09:00:00', '2015-01-12-10:00:00', '2015-01-12-11:00:00']
ticks = [12210.0, 12210.0, 12180.0, 12240.0, 12250.0, 12420.0, 12390.0, 12400.0, 12380.0, 12450.0, 12460.0]
plt.subplots_adjust(bottom=0.2)
plt.xticks( rotation=90 )
dates = [dateutil.parser.parse(s) for s in timestamps]
ax=plt.gca()
ax.set_xticks(dates)
ax.tick_params(axis='x', labelsize=8)
xfmt = md.DateFormatter('%H:%M:%S')
ax.xaxis.set_major_formatter(xfmt)
plt.plot(dates, ticks, label="Price")
plt.xlabel("Date and time", fontsize=12)
plt.ylabel("Price", fontsize=12)
plt.suptitle("Price during last three days", fontsize=12)
plt.legend(loc=0,prop={'size':8})
plt.savefig("figure.pdf")
When I try to plot these datetimes and values I get a messy graph with the line going back and forth.
It looks like the dates are being ignored and only timestamps are taken in account which is the reason for the messy chart. I tried to edit the datetimes to have the same date and consecutive timestamps and it fixed the chart. However, I must have dates as well..
What am I doing wrong?
When I try to plot these datetimes and values I get a messy graph with the line going back and forth.
Your plots are going all over the place because plt.plot connects the dots in the order you give it. If this order is not monotonically increasing in x, then it looks "messy". You can sort the points by x first to fix this. Here is a minimal example:
import numpy as np
import pylab as plt
X = np.random.random(20)
Y = 2*X+np.random.random(20)
idx = np.argsort(X)
X2 = X[idx]
Y2 = Y[idx]
fig,ax = plt.subplots(2,1)
ax[0].plot(X,Y)
ax[1].plot(X2,Y2)
plt.show()

Plotting chart with epoch time x axis using matplotlib

I have the following code to plot a chart with matplotlib
#!/usr/bin/env python
import matplotlib.pyplot as plt
import urllib2
import json
req = urllib2.urlopen("http://localhost:17668/retrieval/data/getData.json? pv=LNLS:ANEL:corrente&donotchunk")
data = json.load(req)
secs = [x['secs'] for x in data[0]['data']]
vals = [x['val'] for x in data[0]['data']]
plt.plot(secs, vals)
plt.show()
The secs arrays is epoch time.
What I want is to plot the data in the x axis (secs) as a date (DD-MM-YYYY HH:MM:SS).
How can I do that?
To plot date-based data in matplotlib you must convert the data to the correct format.
One way is to first convert your data to datetime objects, for an epoch timestamp you should use datetime.datetime.fromtimestamp().
You must then convert the datetime objects to the right format for matplotlib, this can be handled using matplotlib.date.date2num.
Alternatively you can use matplotlib.dates.epoch2num and skip converting your date to datetime objects in the first place (while this will suit your use-case better initially, I would recommend trying to keep date based date in datetime objects as much as you can when working, it will save you a headache in the long run).
Once you have your data in the correct format you can plot it using plot_date.
Finally to format your x-axis as you wish you can use a matplotlib.dates.DateFormatter object to choose how your ticks will look.
import matplotlib.pyplot as plt
import matplotlib.dates as mdate
import numpy as np
# Generate some random data.
N = 40
now = 1398432160
raw = np.array([now + i*1000 for i in range(N)])
vals = np.sin(np.linspace(0,10,N))
# Convert to the correct format for matplotlib.
# mdate.epoch2num converts epoch timestamps to the right format for matplotlib
secs = mdate.epoch2num(raw)
fig, ax = plt.subplots()
# Plot the date using plot_date rather than plot
ax.plot_date(secs, vals)
# Choose your xtick format string
date_fmt = '%d-%m-%y %H:%M:%S'
# Use a DateFormatter to set the data to the correct format.
date_formatter = mdate.DateFormatter(date_fmt)
ax.xaxis.set_major_formatter(date_formatter)
# Sets the tick labels diagonal so they fit easier.
fig.autofmt_xdate()
plt.show()
You can change the ticks locations and formats on your plot:
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
import time
secs = [10928389,102928123,383827312,1238248395]
vals = [12,8,4,12]
plt.plot(secs,vals)
plt.gcf().autofmt_xdate()
plt.gca().xaxis.set_major_locator(mtick.FixedLocator(secs))
plt.gca().xaxis.set_major_formatter(
mtick.FuncFormatter(lambda pos,_: time.strftime("%d-%m-%Y %H:%M:%S",time.localtime(pos)))
)
plt.tight_layout()
plt.show()

Six subplots with the same number of xticklabels in matplotlib

I am really struggling with matplotlib, escpecially with the axis settings. My goal is to set up 6 subplots in one figure, which all display different datasets but have the same amount of ticklabels.
The relevant part of my sourcecode looks like:
graph4.py:
# Import Matolotlib Modules #
import matplotlib as mpl
from matplotlib.figure import Figure
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
from matplotlib import ticker
import matplotlib.pyplot as plt
mpl.rcParams['font.sans-serif']='Arial' #set font to arial
# Import GTK Modules #
import gtk
#Import System Modules #
import sys
# Import Numpy Modules #
from numpy import genfromtxt
import numpy
# Import Own Modules #
import mysubplot as mysp
class graph4():
weekdays = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag']
def __init__(self, graphview):
#create new Figure
self.figure = Figure(figsize=(100,100), dpi=75)
#create six subplots within self.figure
self.subplot = []
for j in range(6):
self.subplot.append(self.figure.add_subplot(321 + j))
self.__conf_subplots__() #configure title, xlabel, ylabel and grid of all subplots
#to make it look better
self.figure.subplots_adjust(left=0.125, bottom=0.1, right=0.9, top=0.96, wspace=0.2, hspace=0.6)
#Matplotlib <-> GTK
self.canvas = FigureCanvas(self.figure) # a gtk.DrawingArea
self.canvas.set_flags(gtk.HAS_FOCUS|gtk.CAN_FOCUS)
self.canvas.grab_focus()
self.canvas.show()
graphview.pack_start(self.canvas, True, True)
#add labels and grid to all subplots
def __conf_subplots__(self):
index = 0
for i in self.subplot:
mysp.conf_subplot(i, 'Zeit', 'Menge', graph4.weekdays[index], True)
i.plot([], [], 'bo') #empty plot
index +=1
def plot(self, filename_list):
index = 0
for filename in filename_list:
data = genfromtxt(filename, delimiter=',') #load data from filename
if data.size != 0: #only if file isn't empty
if index <= len(self.subplot): #plot every file on a different subplot
mysp.plot(self.subplot[index],data[0:, 1], data[0:, 0])
index +=1
self.canvas.draw()
def clear_plot(self):
#clear axis of all subplots
for i in self.subplot:
i.cla()
self.__conf_subplots__()
mysubplot.py: (helper module)
# Import Matplotlib Modules
from matplotlib.axes import Subplot
import matplotlib.dates as md
import matplotlib.pyplot as plt
# Import Own Modules #
import mytime as myt
# Import Numpy Modules #
import numpy as np
def conf_subplot(subplot, xlabel, ylabel, title, grid):
if(xlabel != None):
subplot.set_xlabel(xlabel)
if(ylabel != None):
subplot.set_ylabel(ylabel)
if(title != None):
subplot.set_title(title)
subplot.grid(grid)
#rotate xaxis labels
plt.setp(subplot.get_xticklabels(), rotation=30, fontsize=12)
#display date on xaxis
subplot.xaxis.set_major_formatter(md.DateFormatter('%H:%M:%S'))
subplot.xaxis_date()
def plot(subplot, x, y):
subplot.plot(x, y, 'bo')
I think the best way to explain what goes wrong is with the use of screenshots. After I start my application, everything looks good:
If I double click a 'Week'-entry on the left, the method clear_plot() in graph4.py is called to reset all subplots. Then a list of filenames is passed to the method plot() in graph4.py. The method plot() opens each file and plots each dataset on a different subplot. So after I double click a entry, it looks like:
As you can see, each subplot has a different number of xtick labels, which looks pretty ugly to me. Therefore, I am looking for a solution to improve this. My first approach was to set the ticklabels manually with xaxis.set_ticklabels(), so that each subplot has the same number of ticklabels. However, as strange as it sounds, this only works on some datasets and I really don't know why. On some datasets, everything works fine and on other datasets, matplotlib is basically doing what it wants and displays xaxis labels that I didn't specify. I also tried FixedLocator(), but I got the same result. On some datasets it is working and on others, matplotlib is using a different number of xtick labels.
What am I doing wrong?
Edit:
As #sgpc suggested, I tried to use pyplot. My sourcecode now looks like this:
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
import matplotlib.dates as md
mpl.rcParams['font.sans-serif']='Arial' #set font to arial
import gtk
import sys
# Import Numpy Modules #
from numpy import genfromtxt
import numpy
# Import Own Modules #
import mysubplot as mysp
class graph2():
weekdays = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag']
def __init__(self, graphview):
self.figure, temp = plt.subplots(ncols=2, nrows=3, sharex = True)
#2d array -> list
self.axes = [ y for x in temp for y in x]
#axis: date
for i in self.axes:
i.xaxis.set_major_formatter(md.DateFormatter('%H:%M:%S'))
i.xaxis_date()
#make space and rotate xtick labels
self.figure.autofmt_xdate()
#Matplotlib <-> GTK
self.canvas = FigureCanvas(self.figure) # a gtk.DrawingArea
self.canvas.set_flags(gtk.HAS_FOCUS|gtk.CAN_FOCUS)
self.canvas.grab_focus()
self.canvas.show()
graphview.pack_start(self.canvas, True, True)
def plot(self, filename_list):
index = 0
for filename in filename_list:
data = genfromtxt(filename, delimiter=',') #get dataset
if data.size != 0: #only if file isn't empty
if index < len(self.axes): #print each dataset on a different subplot
self.axes[index].plot(data[0:, 1], data[0:, 0], 'bo')
index +=1
self.canvas.draw()
#not yet implemented
def clear_plot(self):
pass
If I plot some datasets, I get the following output:
http://i.imgur.com/3ngYTNr.png (sorry, I still don't have enough reputation to embedd pictures)
Moreover, I am not really sure if sharing the x-axis is a really good idea, because it is possible that the x-values differ in every subplot (for example: in the first subplot, the x-values ranges from 8:00am - 11:00am and in the second subplot the x-values ranges from 7:00pm - 9:00pm).
If I get rid of sharex = True, I get the following output:
http://i.imgur.com/rxHeSyJ.png (sorry, I still don't have enough reputation to embedd pictures)
As you can see, the output now looks better. However now, the labels on the x-axes are not updated. I assume that is because the last suplots are empty.
My next attempt was to use an axis for each subplot. Therefore, I made this changes:
for i in self.axes:
plt.setp(i.get_xticklabels(), visible=True, rotation = 30) #<-- I added this line...
i.xaxis.set_major_formatter(md.DateFormatter('%H:%M:%S'))
i.xaxis_date()
#self.figure.autofmt_xdate() #<--changed this line
self.figure.subplots_adjust(left=0.125, bottom=0.1, right=0.9, top=0.96, wspace=0.2, hspace=0.6) #<-- and added this line
Now I get the following output:
i.imgur.com/TmA1goE.png (sorry, I still don't have enough reputation to embedd pictures)
So with this attempt, I am basically struggling with the same problem as with Figure() and add_subplot().
I really don't know, what else I could try to make it work...
I would strongly recommend you to use pyplot.subplots() with sharex=True:
fig, axes = subplots(ncols=2, nrows=3, sharex= True)
Then you access each axes using:
ax = axes[i,j]
And you can plot doing:
ax.plot(...)
To control the number of ticks for each AxesSubplot you can use:
ax.locator_params(axis='x', nbins=6)
OBS: axis can be 'x', 'y' or 'both'

Categories