I have a line chart based on a simple list of numbers. By default the x-axis is just the an increment of 1 for each value plotted. I would like to be a percentage instead but can't figure out how. So instead of having an x-axis from 0 to 5, it would go from 0% to 100% (but keeping reasonably spaced tick marks. Code below. Thanks!
from matplotlib import pyplot as plt
from mpl_toolkits.axes_grid.axislines import Subplot
data=[8,12,15,17,18,18.5]
fig=plt.figure(1,(7,4))
ax=Subplot(fig,111)
fig.add_subplot(ax)
plt.plot(data)
The code below will give you a simplified x-axis which is percentage based, it assumes that each of your values are spaces equally between 0% and 100%.
It creates a perc array which holds evenly-spaced percentages that can be used to plot with. It then adjusts the formatting for the x-axis so it includes a percentage sign using matplotlib.ticker.FormatStrFormatter. Unfortunately this uses the old-style string formatting, as opposed to the new style, the old style docs can be found here.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.ticker as mtick
data = [8,12,15,17,18,18.5]
perc = np.linspace(0,100,len(data))
fig = plt.figure(1, (7,4))
ax = fig.add_subplot(1,1,1)
ax.plot(perc, data)
fmt = '%.0f%%' # Format you want the ticks, e.g. '40%'
xticks = mtick.FormatStrFormatter(fmt)
ax.xaxis.set_major_formatter(xticks)
plt.show()
This is a few months late, but I have created PR#6251 with matplotlib to add a new PercentFormatter class. With this class you can do as follows to set the axis:
import matplotlib.ticker as mtick
# Actual plotting code omitted
ax.xaxis.set_major_formatter(mtick.PercentFormatter(5.0))
This will display values from 0 to 5 on a scale of 0% to 100%. The formatter is similar in concept to what #Ffisegydd suggests doing except that it can take any arbitrary existing ticks into account.
PercentFormatter() accepts three arguments, max, decimals, and symbol. max allows you to set the value that corresponds to 100% on the axis (in your example, 5).
The other two parameters allow you to set the number of digits after the decimal point and the symbol. They default to None and '%', respectively. decimals=None will automatically set the number of decimal points based on how much of the axes you are showing.
Note that this formatter will use whatever ticks would normally be generated if you just plotted your data. It does not modify anything besides the strings that are output to the tick marks.
Update
PercentFormatter was accepted into Matplotlib in version 2.1.0.
Totally late in the day, but I wrote this and thought it could be of use:
def transformColToPercents(x, rnd, navalue):
# Returns a pandas series that can be put in a new dataframe column, where all values are scaled from 0-100%
# rnd = round(x)
# navalue = Nan== this
hv = x.max(axis=0)
lv = x.min(axis=0)
pp = pd.Series(((x-lv)*100)/(hv-lv)).round(rnd)
return pp.fillna(navalue)
df['new column'] = transformColToPercents(df['a'], 2, 0)
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(...)
If I create a plot with matplotlib using the following code:
import numpy as np
from matplotlib import pyplot as plt
xx = np.arange(0,5, .5)
yy = np.random.random( len(xx) )
plt.plot(xx,yy)
plt.imshow()
I get a result that looks like the attached image. The problem is the
bottom-most y-tick label overlaps the left-most x-tick label. This
looks unprofessional. I was wondering if there was an automatic
way to delete the bottom-most y-tick label, so I don't have
the overlap problem. The fewer lines of code, the better.
In the ticker module there is a class called MaxNLocator that can take a prune kwarg.
Using that you can remove the first tick:
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
import numpy as np
xx = np.arange(0,5, .5)
yy = np.random.random( len(xx) )
plt.plot(xx,yy)
plt.gca().xaxis.set_major_locator(MaxNLocator(prune='lower'))
plt.show()
Result:
You can pad the ticks on the x-axis:
ax.tick_params(axis='x', pad=15)
Replace ax with plt.gca() if you haven't stored the variable ax for the current figure.
You can also pad both the axes removing the axis parameter.
A very elegant way to fix the overlapping problem is increasing the padding of the x- and y-tick labels (i.e. the distance to the axis). Leaving out the corner most label might not always be wanted. In my opinion, in general it looks nice if the labels are a little bit farther from the axis than given by the default configuration.
The padding can be changed via the matplotlibrc file or in your plot script by using the commands
import matplotlib as mpl
mpl.rcParams['xtick.major.pad'] = 8
mpl.rcParams['ytick.major.pad'] = 8
Most times, a padding of 6 is also sufficient.
This is answered in detail here. Basically, you use something like this:
plt.xticks([list of tick locations], [list of tick lables])