Joey Dumont
Joey Dumont

Reputation: 938

Setting the position of the `ylabel`

I am trying to recreate the look of figure below using matplotlib (source).

Template figure

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

Answers (3)

tmdavison
tmdavison

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)

enter image description here

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

Suuuehgi
Suuuehgi

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

plot

Upvotes: 5

Paweł Wójcik
Paweł Wójcik

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

Related Questions