I want to plot a bar graph with a variable amount of values along the x-axis. For the data, I have a set of labels which I want to show on the x-axis under the bars. I also want the x-axis limits to start at -1, since otherwise, only half of the first bar at index 0 would be visible. I've tried multiple alternatives for achieving that, none of them worked, because the xticklabels are always one or more off. And IF they work for a given set of data, with another set of data (with more or less bars) it does not work again. See minimum code example below
from matplotlib import pyplot as plt
from matplotlib import ticker
import numpy as np
randData = np.random.rand(100)
xValues = np.linspace(0, len(randData)-1, num=len(randData))
labels = []
for i in range(len(randData)):
labels.append('label' + str(i))
fig, ax = plt.subplots()
ax.bar(np.linspace(0, len(randData)-1, num=len(randData)), randData)
ax.xaxis.set_major_locator(ticker.MultipleLocator(1))
# Alternative 1
# Use an empty string for index -1, set labels, then set new xlim
labels.insert(0, '')
ax.set_xticklabels(labels, size='x-small', rotation=90)
plt.xlim(-1, len(randData))
# Alternative 2
# Use an empty string for index -1, set new xlim, then set labels
labels.insert(0, '')
plt.xlim(-1, len(randData))
ax.set_xticklabels(labels, size='x-small', rotation=90)
# Alternative 3
# Setting limits with ax.set_xlim
ax.set_xticklabels(labels, size='x-small', rotation=90)
ax.set_xlim([-1, len(randData)])
# Alternative 4
# Setting limits with plt.xlim
ax.set_xticklabels(labels, size='x-small', rotation=90)
plt.xlim(-1, len(randData))
plt.show()
None of the variants worked so far. One part of the problem is that the pyplot automatically sets its xlimits depending on the amount of bar graphs (sometimes it starts at -1, with more values it might sometimes start at -4).
One of the faulty results is shown below:
Any help would be appreciated.
P.S.: If I may, I'd like to add a little side question: How can I remove the Warning "UserWarning: FixedFormatter should only be used together with FixedLocator" when setting the xticklabels? Nothing from this answer worked for me.
I am having an issue trying to get my date ticks rotated in matplotlib. A small sample program is below. If I try to rotate the ticks at the end, the ticks do not get rotated. If I try to rotate the ticks as shown under the comment 'crashes', then matplot lib crashes.
This only happens if the x-values are dates. If I replaces the variable dates with the variable t in the call to avail_plot, the xticks(rotation=70) call works just fine inside avail_plot.
Any ideas?
import numpy as np
import matplotlib.pyplot as plt
import datetime as dt
def avail_plot(ax, x, y, label, lcolor):
ax.plot(x,y,'b')
ax.set_ylabel(label, rotation='horizontal', color=lcolor)
ax.get_yaxis().set_ticks([])
#crashes
#plt.xticks(rotation=70)
ax2 = ax.twinx()
ax2.plot(x, [1 for a in y], 'b')
ax2.get_yaxis().set_ticks([])
ax2.set_ylabel('testing')
f, axs = plt.subplots(2, sharex=True, sharey=True)
t = np.arange(0.01, 5, 1)
s1 = np.exp(t)
start = dt.datetime.now()
dates=[]
for val in t:
next_val = start + dt.timedelta(0,val)
dates.append(next_val)
start = next_val
avail_plot(axs[0], dates, s1, 'testing', 'green')
avail_plot(axs[1], dates, s1, 'testing2', 'red')
plt.subplots_adjust(hspace=0, bottom=0.3)
plt.yticks([0.5,],("",""))
#doesn't crash, but does not rotate the xticks
#plt.xticks(rotation=70)
plt.show()
If you prefer a non-object-oriented approach, move plt.xticks(rotation=70) to right before the two avail_plot calls, eg
plt.xticks(rotation=70)
avail_plot(axs[0], dates, s1, 'testing', 'green')
avail_plot(axs[1], dates, s1, 'testing2', 'red')
This sets the rotation property before setting up the labels. Since you have two axes here, plt.xticks gets confused after you've made the two plots. At the point when plt.xticks doesn't do anything, plt.gca() does not give you the axes you want to modify, and so plt.xticks, which acts on the current axes, is not going to work.
For an object-oriented approach not using plt.xticks, you can use
plt.setp( axs[1].xaxis.get_majorticklabels(), rotation=70 )
after the two avail_plot calls. This sets the rotation on the correct axes specifically.
Solution works for matplotlib 2.1+
There exists an axes method tick_params that can change tick properties. It also exists as an axis method as set_tick_params
ax.tick_params(axis='x', rotation=45)
Or
ax.xaxis.set_tick_params(rotation=45)
As a side note, the current solution mixes the stateful interface (using pyplot) with the object-oriented interface by using the command plt.xticks(rotation=70). Since the code in the question uses the object-oriented approach, it's best to stick to that approach throughout. The solution does give a good explicit solution with plt.setp( axs[1].xaxis.get_majorticklabels(), rotation=70 )
An easy solution which avoids looping over the ticklabes is to just use
fig.autofmt_xdate()
This command automatically rotates the xaxis labels and adjusts their position. The default values are a rotation angle 30° and horizontal alignment "right". But they can be changed in the function call
fig.autofmt_xdate(bottom=0.2, rotation=30, ha='right')
The additional bottom argument is equivalent to setting plt.subplots_adjust(bottom=bottom), which allows to set the bottom axes padding to a larger value to host the rotated ticklabels.
So basically here you have all the settings you need to have a nice date axis in a single command.
A good example can be found on the matplotlib page.
Another way to applyhorizontalalignment and rotation to each tick label is doing a for loop over the tick labels you want to change:
import numpy as np
import matplotlib.pyplot as plt
import datetime as dt
now = dt.datetime.now()
hours = [now + dt.timedelta(minutes=x) for x in range(0,24*60,10)]
days = [now + dt.timedelta(days=x) for x in np.arange(0,30,1/4.)]
hours_value = np.random.random(len(hours))
days_value = np.random.random(len(days))
fig, axs = plt.subplots(2)
fig.subplots_adjust(hspace=0.75)
axs[0].plot(hours,hours_value)
axs[1].plot(days,days_value)
for label in axs[0].get_xmajorticklabels() + axs[1].get_xmajorticklabels():
label.set_rotation(30)
label.set_horizontalalignment("right")
And here is an example if you want to control the location of major and minor ticks:
import numpy as np
import matplotlib.pyplot as plt
import datetime as dt
fig, axs = plt.subplots(2)
fig.subplots_adjust(hspace=0.75)
now = dt.datetime.now()
hours = [now + dt.timedelta(minutes=x) for x in range(0,24*60,10)]
days = [now + dt.timedelta(days=x) for x in np.arange(0,30,1/4.)]
axs[0].plot(hours,np.random.random(len(hours)))
x_major_lct = mpl.dates.AutoDateLocator(minticks=2,maxticks=10, interval_multiples=True)
x_minor_lct = matplotlib.dates.HourLocator(byhour = range(0,25,1))
x_fmt = matplotlib.dates.AutoDateFormatter(x_major_lct)
axs[0].xaxis.set_major_locator(x_major_lct)
axs[0].xaxis.set_minor_locator(x_minor_lct)
axs[0].xaxis.set_major_formatter(x_fmt)
axs[0].set_xlabel("minor ticks set to every hour, major ticks start with 00:00")
axs[1].plot(days,np.random.random(len(days)))
x_major_lct = mpl.dates.AutoDateLocator(minticks=2,maxticks=10, interval_multiples=True)
x_minor_lct = matplotlib.dates.DayLocator(bymonthday = range(0,32,1))
x_fmt = matplotlib.dates.AutoDateFormatter(x_major_lct)
axs[1].xaxis.set_major_locator(x_major_lct)
axs[1].xaxis.set_minor_locator(x_minor_lct)
axs[1].xaxis.set_major_formatter(x_fmt)
axs[1].set_xlabel("minor ticks set to every day, major ticks show first day of month")
for label in axs[0].get_xmajorticklabels() + axs[1].get_xmajorticklabels():
label.set_rotation(30)
label.set_horizontalalignment("right")
Simply use
ax.set_xticklabels(label_list, rotation=45)
I am clearly late but there is an official example which uses
plt.setp(ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor")
to rotate the labels while keeping them correctly aligned with the ticks, which is both clean and easy.
Ref: https://matplotlib.org/stable/gallery/images_contours_and_fields/image_annotated_heatmap.html
Below is my code for a line graph. I would like another x label under the current one (so I can show the days of the week).
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns;sns.set()
sns.set()
data = pd.read_csv("123.csv")
data['DAY']=["01","02","03","04","05","06","07","08","09","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31"]
plt.figure(figsize=(15,8))
plt.plot('DAY','SWST',data=data,linewidth=2,color="k")
plt.plot('DAY','WMID',data=data,linewidth=2,color="m")
plt.xlabel('DAY', fontsize=20)
plt.ylabel('VOLUME', fontsize=20)
plt.legend()
EDIT: After following the documentation, I have 2 issues. The scale has changed from 31 to 16, and the days of the week do not line up with the day number.
data['DAY']=["01","02","03","04","05","06","07","08","09","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31"]
tick_labels=['1','\n\nThu','2','\n\nFri','3','\n\nSat','4','\n\nSun','5','\n\nMon','6','\n\nTue','7','\n\nWed','8','\n\nThu','9','\n\nFri','10','\n\nSat','11','\n\nSun','12','\n\nMon','13','\n\nTue','14','\n\nWed','15','\n\nThu','16','\n\nFri','17','\n\nSat','18','\n\nSun','19','\n\nMon','20','\n\nTue','21','\n\nWed','22','\n\nThu','23','\n\nFri','24','\n\nSat','25','\n\nSun','26','\n\nMon','27','\n\nTue','28','\n\nWed','29','\n\nThu','30','\n\nFri','31','\n\nSat']
tick_locations = np.arange(31)
plt.figure(figsize=(15,8))
plt.xticks(tick_locations, tick_labels)
plt.plot('DAY','SWST',data=data,linewidth=2,color="k")
plt.plot('DAY','WMID',data=data,linewidth=2,color="m")
plt.xlabel('DAY', fontsize=20)
plt.ylabel('VOLUME', fontsize=20)
plt.legend()
plt.show()
The pyplot function you are looking for is plt.xticks(). This is essentially a combination of ax.set_xticks() and ax.set_xticklabels()
From the documentation:
Parameters:
ticks : array_like
A list of positions at which ticks should be placed. You can pass an
empty list to disable xticks.
labels:
array_like, optional A list of explicit labels to place at the given
locs.
You would want something like the below code. Note you should probably explicitly set the tick locations as well as the labels to avoid setting labels in the wrong positions:
tick_labels = ['1','\n\nThu','2',..., '31','\n\nSat')
plt.xticks(tick_locations, tick_labels)
Note that the object-orientated API (i.e. using ax.) allows for more customisable plots.
Update
After the edit, I see that the labels you want to go below are part of the same list. Therefore your label list actually has a length of 62. So you need to join every 2 elements of your list together:
tick_labels=['1','\n\nThu','2','\n\nFri','3','\n\nSat','4','\n\nSun','5','\n\nMon','6','\n\nTue','7','\n\nWed','8',
'\n\nThu','9','\n\nFri','10','\n\nSat','11','\n\nSun','12','\n\nMon','13','\n\nTue','14','\n\nWed','15',
'\n\nThu','16','\n\nFri','17','\n\nSat','18','\n\nSun','19','\n\nMon','20','\n\nTue','21','\n\nWed','22',
'\n\nThu','23','\n\nFri','24','\n\nSat','25','\n\nSun','26','\n\nMon','27','\n\nTue','28','\n\nWed','29',
'\n\nThu','30','\n\nFri','31','\n\nSat']
tick_locations = np.arange(31)
new_labels = [ ''.join(x) for x in zip(tick_labels[0::2], tick_labels[1::2]) ]
plt.figure(figsize=(15, 8))
plt.xticks(tick_locations, new_labels)
plt.show()
Never use ax.set_xticklabels without setting the locations of the ticks as well. This can be done via ax.set_xticks.
ax.set_xticks(...)
ax.set_xticklabels(...)
Of course you may do the same with pyplot
ax = plt.gca()
ax.set_xticks(...)
ax.set_xticklabels(...)
I want to plot a series of values against a date range in matplotlib. I changed the tick base parameter to 7, to get one tick at the beginning of every week (plticker.IndexLocator, base = 7). The problem is that the set_xticklabels function does not accept a base parameter. As a result, the second tick (representing day 8 on the beginning of week 2) is labelled with day 2 from my date range list, and not with day 8 as it should be (see picture).
How to give set_xticklabelsa base parameter?
Here is the code:
my_data = pd.read_csv("%r_filename_%s_%s_%d_%d.csv" % (num1, num2, num3, num4, num5), dayfirst=True)
my_data.plot(ax=ax1, color='r', lw=2.)
loc = plticker.IndexLocator(base=7, offset = 0) # this locator puts ticks at regular intervals
ax1.set_xticklabels(my_data.Date, rotation=45, rotation_mode='anchor', ha='right') # this defines the tick labels
ax1.xaxis.set_major_locator(loc)
Here is the plot:
Plot
Many thanks - your solution perfectly works. For the case that other people run into the same issue in the future: i have implemented the above-mentioned solution but also added some code so that the tick labels keep the desired rotation and also align (with their left end) to the respective tick. May not be pythonic, may not be best-practice, but it works
x_fmt = mpl.ticker.IndexFormatter(x)
ax.set_xticklabels(my_data.Date, rotation=-45)
ax.tick_params(axis='x', pad=10)
ax.xaxis.set_major_formatter(x_fmt)
labels = my_data.Date
for tick in ax.xaxis.get_majorticklabels():
tick.set_horizontalalignment("left")
The reason your ticklabels went bad is that setting manual ticklabels decouples the labels from your data. The proper approach is to use a Formatter according to your needs. Since you have a list of ticklabels for each data point, you can use an IndexFormatter. It seems to be undocumented online, but it has a help:
class IndexFormatter(Formatter)
| format the position x to the nearest i-th label where i=int(x+0.5)
| ...
| __init__(self, labels)
| ...
So you just have to pass your list of dates to IndexFormatter. With a minimal, pandas-independent example (with numpy only for generating dummy data):
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
# create dummy data
x = ['str{}'.format(k) for k in range(20)]
y = np.random.rand(len(x))
# create an IndexFormatter with labels x
x_fmt = mpl.ticker.IndexFormatter(x)
fig,ax = plt.subplots()
ax.plot(y)
# set our IndexFormatter to be responsible for major ticks
ax.xaxis.set_major_formatter(x_fmt)
This should keep your data and labels paired even when tick positions change:
I noticed you also set the rotation of the ticklabels in the call to set_xticklabels, you would lose this now. I suggest using fig.autofmt_xdate to do this instead, it seems to be designed exactly for this purpose, without messing with your ticklabel data.