Normally, to change an axis's (e.g. ax.xaxis
) label position, you'd do axis.label.set_position(xy)
. Or you can just set one coordinate, e.g. 'ax.xaxis.set_x(1)`.
In your case, it would be:
ax['xzero'].label.set_x(1)
ax['yzero'].label.set_y(1)
However, axislines
(and anything else in axisartist
or axes_grid
) is a somewhat outdated module (which is why axes_grid1
exists). It doesn't subclass things properly in some cases. So, when we try to set the x and y positions of the labels, nothing changes!
A quick workaround would be to use ax.annotate
to place labels at the ends of your arrows. However, let's try making the plot a different way first (after which we'll wind up coming back to annotate
anyway).
These days, you're better off using the new spines functionality to do what you're trying to accomplish.
Setting the x and y axes to be "zeroed" is as simple as:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
for spine in ['left', 'bottom']:
ax.spines[spine].set_position('zero')
# Hide the other spines...
for spine in ['right', 'top']:
ax.spines[spine].set_color('none')
ax.axis([-4, 10, -4, 10])
ax.grid()
plt.show()

However, we still need the nice arrow decoration. This is a bit more complex, but it's just two calls to annotate with the approriate arguments.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
#-- Set axis spines at 0
for spine in ['left', 'bottom']:
ax.spines[spine].set_position('zero')
# Hide the other spines...
for spine in ['right', 'top']:
ax.spines[spine].set_color('none')
#-- Decorate the spins
arrow_length = 20 # In points
# X-axis arrow
ax.annotate('', xy=(1, 0), xycoords=('axes fraction', 'data'),
xytext=(arrow_length, 0), textcoords='offset points',
arrowprops=dict(arrowstyle='<|-', fc='black'))
# Y-axis arrow
ax.annotate('', xy=(0, 1), xycoords=('data', 'axes fraction'),
xytext=(0, arrow_length), textcoords='offset points',
arrowprops=dict(arrowstyle='<|-', fc='black'))
#-- Plot
ax.axis([-4, 10, -4, 10])
ax.grid()
plt.show()

(The width of the arrow is controlled by the text size (or an optional parameter to the arrowprops
), so specifying something like size=16
to annotate
will make the arrow a bit wider, if you'd like.)
At this point, it's easiest to just add the "X" and "Y" labels as a part of the annotation, though setting their positions would work as well.
If we just pass in a label as the first argument to annotate instead of an empty string (and change the alignment a bit) we'll get nice labels at the ends of the arrows:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
#-- Set axis spines at 0
for spine in ['left', 'bottom']:
ax.spines[spine].set_position('zero')
# Hide the other spines...
for spine in ['right', 'top']:
ax.spines[spine].set_color('none')
#-- Decorate the spins
arrow_length = 20 # In points
# X-axis arrow
ax.annotate('X', xy=(1, 0), xycoords=('axes fraction', 'data'),
xytext=(arrow_length, 0), textcoords='offset points',
ha='left', va='center',
arrowprops=dict(arrowstyle='<|-', fc='black'))
# Y-axis arrow
ax.annotate('Y', xy=(0, 1), xycoords=('data', 'axes fraction'),
xytext=(0, arrow_length), textcoords='offset points',
ha='center', va='bottom',
arrowprops=dict(arrowstyle='<|-', fc='black'))
#-- Plot
ax.axis([-4, 10, -4, 10])
ax.grid()
plt.show()

With a only a tiny bit more work (directly accessing the spine's transform), you can generalize the use of annotate to work with any type of spine alignment (e.g. "dropped" spines, etc).
At any rate, hope that helps a bit. You can also get fancier with it, if you'd like.