Reputation: 121
My intention: Plot some stuff, get the numbers corresponding to the default x-tick labels, and use these to index a list of dates so that I can put those dates in place of the default labels.
Imagine I have two lists:
my_data = range(30)
dates_ls = ['date_str1', 'date_str2', ... , 'date_str30']
I want to plot this data, then replace the x-ticks with the appropriate date from dates_ls
, which just means indexing dates_ls
with the numbers from the default x-tick labels.
EDIT: So, the problem appears to be somehow due to the fact that I'm in a for loop:
dates_ls = ['dates_' + str(x+1) for x in range(30)]
metrics_ls = ['sessions', 'pageviews']
URL_names = ['URL_1', 'URL_2']
DataDict_final = dict()
for name in URL_names:
DataDict_final[name] = {metric: [] for metric in metrics_ls}
DataDict_final['URL_1']['sessions'] = range(30) # manually define for this example
DataDict_final['URL_1']['pageviews'] = range(30)
DataDict_final['URL_2']['sessions'] = range(30)
DataDict_final['URL_2']['pageviews'] = range(30)
for cur_page in URL_names:
# plot
fig, ax = plt.subplots()
for i, cur_metric in enumerate(metrics_ls):
data2plot = DataDict_final[cur_page] [cur_metric]
ax.plot(data2plot, label=cur_metric)
# x and y axis limits and labels
ax.set_xlim(0, len(DataDict_final[cur_page] [cur_metric])-1)
x_labs1 = [int(label.get_position()[0]) for label in ax.get_xticklabels()]
x_labs2 = [dates_ls[x] for x in x_labs1]
ax.set_xticklabels(x_labs2, fontsize=8, rotation=45, ha='right')
In the above, I'm looping through URL_names and metrics_ls, which are lists of dictionary keys that allow me to get data2plot, which is just a list of numbers. If I highlight and run the code with the for loop it works fine, but if I run the entire loop, producing multiple figures, x_labs1 ends up being a list of 0s, and therefore all my x-tick labels are the same, each one the first item in dates_ls.
Upvotes: 1
Views: 1609
Reputation: 339112
The approach from the question is not well suited to deterministically set the ticklabels. Ticklabels should be set according to the value they mark, not the position on screen.
In order to customize ticklabels, a FuncFormatter
is often helpful. This could take a function as input that returns the desired ticklabel, given the x value to tick.
def fmt(x,pos):
if x in range(len(dates_ls)):
return dates_ls[int(x)]
else:
return ""
# then later
ax.xaxis.set_major_formatter(FuncFormatter(fmt))
plt.setp(ax.get_xticklabels(), fontsize=8, rotation=45, ha='right')
Complete example:
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter
dates_ls = ['dates_' + str(x+1) for x in range(30)]
metrics_ls = ['sessions', 'pageviews']
URL_names = ['URL_1', 'URL_2']
DataDict_final = dict()
for name in URL_names:
DataDict_final[name] = {metric: [] for metric in metrics_ls}
DataDict_final['URL_1']['sessions'] = range(30) # manually define for this example
DataDict_final['URL_1']['pageviews'] = range(30)
DataDict_final['URL_2']['sessions'] = range(30)
DataDict_final['URL_2']['pageviews'] = range(30)
def fmt(x,pos):
if x in range(len(dates_ls)):
return dates_ls[int(x)]
else:
return ""
for cur_page in URL_names:
# plot
fig, ax = plt.subplots()
for i, cur_metric in enumerate(metrics_ls):
data2plot = DataDict_final[cur_page] [cur_metric]
ax.plot(data2plot, label=cur_metric)
# x and y axis limits and labels
ax.set_xlim(0, len(DataDict_final[cur_page] [cur_metric])-1)
ax.xaxis.set_major_formatter(FuncFormatter(fmt))
plt.setp(ax.get_xticklabels(), fontsize=8, rotation=45, ha='right')
plt.show()
Note that if the purpose of this is to show datetimes on the axis, it is much too complicated. Instead use datetime objectes to plot your data directly, as shown in the Datetime example
Upvotes: 2