Reputation: 938
I am trying to recreate the look of figure below using matplotlib (source).
However, I am having issues with the placement of the ylabel
. I want it at the top of the y
-axis, as it is on the figure. I have tried setting its position with ax.yaxis.set_label_position()
, but this only accepts left
or right
for the y-axis. Is there an option to control the position of the ylabel
, or should I just use ax.text
and set the text's position manually?
EDIT: As it turns out, the ax.set_ylabel(position=(x,y))
sets the position of the label relative to the graph coordinates. However, because of its horizontal rotation, the label is a little too much to the right, and position(x,y)
does not seem to accept negative inputs. Is there a way to move the label a little to the left?
I include the code used to generate the skeleton of the figure here, even though it's rather messy.
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
mpl.rcParams['text.usetex'] = True
mpl.rcParams['text.latex.preamble'] = [r"\usepackage[charter]{mathdesign}"]
mpl.rcParams['font.family'] = ['serif']
mpl.rcParams['font.size'] = 10
nb_procs = np.array([1, 2, 4, 12, 24, 48, 96, 192, 384])
def adjust_spines(ax, spines):
for loc, spine in ax.spines.items():
if loc in spines:
spine.set_position(('outward', 10)) # outward by 10 points
spine.set_smart_bounds(True)
else:
spine.set_color('none') # don't draw spine
# turn off ticks where there is no spine
if 'left' in spines:
ax.yaxis.set_ticks_position('left')
else:
# no yaxis ticks
ax.yaxis.set_ticks([])
if 'bottom' in spines:
ax.xaxis.set_ticks_position('bottom')
else:
# no xaxis ticks
ax.xaxis.set_ticks([])
# -- We create the figure.
figPres = plt.figure(figsize=(3,1.75))
axPres = figPres.add_subplot(111)
# -- We remove any superfluous decoration.
# Remove the axis decorations on the right and on the top.
axPres.spines['top'].set_visible(False)
axPres.spines['right'].set_visible(False)
# Make the remaining spines a light gray.
axPres.spines['bottom'].set_color('gray')
axPres.spines['left'].set_color('gray')
adjust_spines(axPres, ['left', 'bottom'])
# -- Set the x ticks.
axPres.set_xscale('log')
axPres.set_xlim((0.75,500))
axPres.set_xticks((nb_procs))
axPres.set_xticklabels( (r'1', r'2', r'4', r'12', r'24', r'48', r'96', r'192', r'384'), color='gray' )
axPres.xaxis.set_ticks_position('bottom')
for tic in axPres.xaxis.get_major_ticks():
tic.tick1On = tic.tick2On = False
# -- Set the y ticks.
axPres.set_ylim((0,1))
axPres.set_yticks((0.0,0.5,1.0))
axPres.set_yticklabels((r'0', '', r'1'))
axPres.yaxis.set_ticks_position('left')
axPres.tick_params(axis='y', colors='gray')
#for tac in axPres.yaxis.get_major_ticks():
# tac.tick1On = tac.tick2On = False
for toc in axPres.xaxis.get_minor_ticks():
toc.tick1On = toc.tick2On = False
# -- Set the titles of the axes.
axPres.set_ylabel(r"Efficacit\'e", color='gray', rotation='horizontal')
axPres.yaxis.set_label_position('right')
axPres.set_xlabel(r"Nombre de processeurs", color='gray')
plt.show()
Upvotes: 46
Views: 118532
Reputation: 69213
You can move the ylabel using ax.yaxis.set_label_coords
, which does accept negative numbers. For your example, I removed the line with set_label_position
, and added:
axPres.yaxis.set_label_coords(-0.1,1.02)
From the documentation:
Axis.set_label_coords(x, y, transform=None)
Set the coordinates of the label.
By default, the x coordinate of the y label and the y coordinate of the x label are determined by the tick label bounding boxes, but this can lead to poor alignment of multiple labels if there are multiple axes.
You can also specify the coordinate system of the label with the transform. If None, the default coordinate system will be the axes coordinate system: (0, 0) is bottom left, (0.5, 0.5) is center, etc.
For further examples of using this method, see the second example on the matplotlib Align y-labels example page.
Upvotes: 69
Reputation: 5010
Some methods have meanwhile been deprecated. Here is a more recent approach.
I moved most of the style-options to the global style parameters.
You can find a list of available parameters with descriptions here.
I hope the rest is self-explanatory.
import matplotlib.pyplot as plt
import numpy as np
# Alternative: plt.rc_context()
plt.rcParams.update({
'figure.constrained_layout.use': True,
'font.size': 12,
'axes.edgecolor': 'gray',
'xtick.color': 'gray',
'ytick.color': 'gray',
'axes.labelcolor':'gray',
'axes.spines.right':False,
'axes.spines.top': False,
'xtick.direction': 'in',
'ytick.direction': 'in',
'xtick.major.size': 6,
'xtick.minor.size': 4,
'ytick.major.size': 6,
'ytick.minor.size': 4,
'xtick.major.pad': 15,
'xtick.minor.pad': 15,
'ytick.major.pad': 15,
'ytick.minor.pad': 15,
})
X = np.linspace(-2,2,500)
Y = np.exp(-X**2)
# Generate Sample Points
Sx = np.random.choice(X, 31)
Sy = np.exp(-Sx**2) + np.random.normal(scale=.02, size=31)
fig, ax = plt.subplots( figsize=(6,4) )
# Disjoin bottom / left spines by moving them outwards
ax.spines[['bottom', 'left']].set_position(('outward', 20))
# Set axis / spine lengths
ax.spines['bottom'].set_bounds(Sx.min(), Sx.max())
ax.spines['left'].set_bounds(0, Sy.max())
ax.set_yticks( ticks=[0, Sy.max()], labels=['0', '650 mW'])
ax.set_yticks( ticks=[(Sy.max()+Sy.min())/2], labels=[''], minor=True )
ax.set_xticks( ticks=[Sx.min(), Sx.max()], labels=['16', '19'])
ax.set_xticks( ticks=[0], labels=['17.2 GHz'], minor=True)
ax.set_ylabel('Output power', ha='left', y=1, rotation=0, labelpad=0)
ax.plot(X,Y, color='orange')
ax.plot(Sx, Sy, marker='o', markersize=3, linestyle='', color='black')
fig.savefig('so.png', dpi=300, bbox_inches='tight')
Upvotes: 5
Reputation: 352
It seems like the 3.5 version of matplotlib doesn't support the yaxis
any more. I have found a workaround that gives similar result
axPres.set_ylabel(r"Efficacit\'e", loc="top", rotation="horizontal")
Upvotes: 3