3

I want to achieve the following three things:

  • add arrows on x and y axes
  • show only the values that are used
  • add labels for coordinates

My code on this moment:

x = [9, 8, 11, 11, 14, 13, 16, 14, 14]
y = [9, 16, 15, 11, 10, 11, 10, 8, 8]
fig = plt.figure(figsize=(7,7), dpi=300)
axes = fig.add_axes([0,1,1,1])

axes.set_xlim(0, 17)
axes.set_ylim(0, 17)

axes.invert_yaxis()

axes.scatter(x, y, color='green')
axes.vlines(x, 0, y, linestyle="dashed", color='green')
axes.hlines(y, 0, x, linestyle="dashed", color='green')
axes.spines.right.set_visible(False)
axes.spines.bottom.set_visible(False)

plt.show()

Visually:

enter image description here

And plot that I want to realize enter image description here

1

3 Answers 3

3

You can draw arrows by overlaying triangle shaped points over the ends of your spines.

You'll need to leverage some transforms, but you can also create your labels by manually adding text to your Axes objects as well.

Labelling each coordinate can be done via axes.annotate, but you'll need to manually specify the location of each annotation to ensure they don't overlap with lines or other annotations.

import matplotlib.pyplot as plt
from matplotlib.ticker import FixedLocator

x = [9, 8, 11, 11, 14, 13, 16, 14, 14]
y = [9, 16, 15, 11, 10, 11, 10, 8, 8]

fig = plt.figure(figsize=(7,7), dpi=300)
axes = fig.add_axes([.05,.05,.9,.9])

# Plots the data
axes.scatter(x, y, color='green')
axes.vlines(x, 0, y, linestyle="dashed", color='green')
axes.hlines(y, 0, x, linestyle="dashed", color='green')

axes.set_xlim(0, 17)
axes.set_ylim(0, 17)
axes.set_xticks(x)
axes.set_yticks(y)
axes.invert_yaxis()

# Move ticks to top side of plot
axes.xaxis.set_tick_params(
    length=0, bottom=False, labelbottom=False, top=True, labeltop=True
)
axes.xaxis.set_tick_params(length=0)

# Add arrows to the spines by drawing triangle shaped points over them
axes.plot(1, 1, '>k', transform=axes.transAxes, clip_on=False)
axes.plot(0, 0, 'vk', transform=axes.transAxes, clip_on=False)
axes.spines[['bottom', 'right']].set_visible(False)

# Add labels for 0, F_1 and F_2
from matplotlib.transforms import offset_copy
axes.text(
    0, 1, s='0', fontstyle='italic', ha='right', va='bottom',
    transform=offset_copy(axes.transAxes, x=-5, y=5, fig=fig, units='points'),
)
axes.text(
    1, 1, s='$F_1$', fontstyle='italic', ha='right', va='bottom',
    transform=offset_copy(axes.transAxes, x=0, y=5, fig=fig, units='points'),
)
axes.text(
    0, 0, s='$F_2$', fontstyle='italic', ha='right',
    transform=offset_copy(axes.transAxes, x=-5, y=0, fig=fig, units='points'),
)

# Add labels at each point. Leveraging the alignment of the text
# AND padded offset.
lc = ('top', 'center', 0, -5)
ll = ('top', 'right', -5, -5)
lr = ('top', 'left', 5, -5)
ur = ('bottom', 'left', 5, 5)
alignments = [lc, lc, lc, ll, lc, ll, lc, ur, lr]
for i, (xc, yc, (va, ha, padx, pady)) in enumerate(zip(x, y, alignments)):
    axes.annotate(
        xy=(xc, yc), xytext=(padx, pady),
        text=f'$F(x_{i})$', ha=ha, va=va, textcoords='offset points')

plt.show()

enter image description here

Sign up to request clarification or add additional context in comments.

Comments

1

Adding

axes.plot(1, 0, ">k", transform=axes.get_yaxis_transform(), clip_on=False)  
axes.plot(0, 0, "vk", transform=axes.get_xaxis_transform(), clip_on=False)

Will do it for you. This is basically just a cheat plot of markers.


There are also

from mpl_toolkits.axisartist.axislines.AxesZero which allow

for direction in ["xzero", "yzero"]:
    # adds arrows at the ends of each axis
    ax.axis[direction].set_axisline_style("-|>")

but they can't handle the reversed y axis in your case with default settings.

Comments

0

Came across this when looking for a way to make arrows on axes, but I wanted to control the aspect ratio of the arrows, which you can do using the above answers, slightly modified.

You define (a) an Affine2D scale transform (mpl.transforms.Affine2D().scale(sx,sy)) and (b) use a MarkerStyle using that transform (mpl.markers.MarkerStyle()) . This transform is independent of the coordinate transform that uses axes coordinates (e.g. ax.get_xaxis_transform()). Do this once for each axis. See this page for example.

Example with 2:1 length:width arrowheads:

# Arrows on axes
# We'll stretch out the triangular markers
stretchxax = mpl.transforms.Affine2D().scale(sx=2.,sy=1.)
stretchyax = mpl.transforms.Affine2D().scale(sx=1.,sy=2.)
ax.plot(1,0,'k',marker=mpl.markers.MarkerStyle('>',transform=stretchxax),transform=ax.get_yaxis_transform(),clip_on=False)
ax.plot(0,1,'k',marker=mpl.markers.MarkerStyle('^',transform=stretchyax),transform=ax.get_xaxis_transform(),clip_on=False)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.