Reputation: 2729
I am using matplotlib to generate an image in a tkinter application. The data provides a value stored against a time (real app data is open sessions by second to show load). I am trying to display this with time across the x axis, and sessions up the y axis.
I am having some difficulty however with formatting the labels on the x axis. If you see image Test 1, if i assign integers to the x axis, then although data is 0-19, matplotlib automatically displays 0,5,10,15, to keep the axis tidy.
If I just assign labels to this, then the first 4 labels are shown rather than every 5th label. If I reset the tciks and labels, I get every tick and every label (which just about fits here but in my real app theres too much data).
My solution has been to manually calculate every Nth tick and Nth label and assign those values which has worked but seems like there should be some built in functionality that handles this sort of data, the same way as it does for integers.
My code is:
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg)
import datetime
import tkinter
def main():
x_labels = []
x = []
y = []
dt_now = datetime.datetime.now()
entries = 20
for i in range(0, entries):
newtime=(dt_now + datetime.timedelta(seconds=i)).strftime("%H:%M:%S")
x.append(i)
y.append(i/2)
x_labels.append(newtime)
root = tkinter.Tk()
fig = plt.figure(figsize=(8,8))
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.draw()
ax1 = fig.add_subplot(221)
ax1.plot(x,y)
ax1.set_title('Test 1 - Original Values')
ax1.set_xlabel('Entry ID')
ax1.set_ylabel('Sessions')
ax2 = fig.add_subplot(222)
ax2.plot(x,y)
ax2.set_title('Test 2 - Labels 0,1,2,3\nExpecting labels 0,5,10,15')
ax2.set_xlabel('Entry Time')
ax2.set_ylabel('Sessions')
ax2.set_xticklabels(x_labels, rotation=90, ha='center')
ax3 = fig.add_subplot(223)
ax3.plot(x_labels,y)
ax3.set_title('Test 3 - Every label')
ax3.set_xlabel('Entry Time')
ax3.set_ylabel('Sessions')
ax3.set_xticklabels(x_labels, rotation = 90)
major_ticks = []
major_tick_labels = []
for i in range(0,entries,int(entries/5)):
major_ticks.append(x[i])
major_tick_labels.append(x_labels[i])
ax4 = fig.add_subplot(224)
ax4.plot(x,y)
ax4.set_title('Test 4 - What I''m expecting\nbut hard coded')
ax4.set_xlabel('Entry Time')
ax4.set_ylabel('Sessions')
ax4.set_xticks(major_ticks)
ax4.set_xticklabels(major_tick_labels, rotation=90, ha='center')
plt.subplots_adjust(hspace = 0.75, bottom = 0.2)
canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1)
tkinter.mainloop()
if __name__ == '__main__':
main()
Which generates the following:
Is this the only way to achieve the requirement or is there something available that I am missing. I have read the documentation, but couldn't see anything relevant in there (past making sure I set ticks and labels together). I'd like the process to be as automated as possible, as the time frame will be user driven, so may be best with 4 ticks, 5 ticks 10 ticks, etc. Matplotlib seems to handle this requirement well the a defined range of integers, I just want to associate the same labels.
Upvotes: 2
Views: 95
Reputation: 10541
You could use matplotlib.dates. Note that you need to keep the x_labels formatted as datetimes for this to work (I removed your call to .strftime)
import datetime
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
x_labels = []
x = []
y = []
dt_now = datetime.datetime.now()
entries = 20
for i in range(0, entries):
newtime=(dt_now + datetime.timedelta(seconds=i))
x.append(i)
y.append(i/2)
x_labels.append(newtime)
fig = plt.figure(figsize=(8,8))
ax4 = fig.add_subplot(224)
ax4.plot(x_labels,y)
ax4.set_title('One possible solution')
ax4.set_xlabel('Entry Time')
ax4.set_ylabel('Sessions')
ax4.xaxis.set_major_locator(mdates.SecondLocator(interval=4))
ax4.tick_params(axis="x", rotation=90)
This will give you
Upvotes: 1