pavlov
pavlov

Reputation: 121

Getting default x-tick label text to use as indices to update tick labels with other text

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

Answers (1)

ImportanceOfBeingErnest
ImportanceOfBeingErnest

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()

enter image description here

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

Related Questions