Move spines in matplotlib 3d plot?
Asked Answered
F

2

12

I'm trying to move the spines in a 3D matplotlib axes object.

This seems like a really simple issue, but I have not found any questions/answers that address this directly. I've included a list of my research on this topic at the bottom of this question.

I can set the position of the spines in matplotlib 2D plots. The following code:

import matplotlib.pyplot as plt, numpy as np

fig, axes = plt.subplots(1, 2)
r, theta = 1, np.linspace(0, 2*np.pi, 100)
x, y = r*np.cos(theta), r*np.sin(theta)
for ax in axes:  # plot the same data on both axes
    ax.plot(x, y)
    ax.set_aspect("equal")
for spine in ax.spines.values():  # adjust spines on last active axis 
    spine.set_position(("data", 0))

produces: super sick 2d plots

However, when I try the same thing with a 3D axis...

z = np.zeros(x.shape)  # exciting stuff
fig = plt.figure()
for i in range(2):  # create two 3D subplots
    ax = plt.subplot(1,2,i+1, projection="3d", aspect="equal")
    plt.plot(x, y, z)
for spine in ax.spines.values():  # adjust spines on last active axis
    spine.set_position(("data", 0))

the above code gives me:

really lame 3d plots

I.e. no effect, even though the code still runs. Also, for the 3D axes, ax.spines looks like:

OrderedDict([('left', <matplotlib.spines.Spine at 0x120857b8>),
             ('right', <matplotlib.spines.Spine at 0xfd648d0>),
             ('bottom', <matplotlib.spines.Spine at 0xe89e4e0>),
             ('top', <matplotlib.spines.Spine at 0xe89eef0>)])

I'm not sure what "left", "right", "bottom", "top" refer to in the context of a 3D axis. I've tried changing other properties like colour of the spines; no luck there either. How can I get hold of the actual x, y, z spines on the axes?

Research:

  • searching "matplotlib spines 3d" in stackoverflow gives 5 results (including this question) at the time of writing.
  • The mplot3d documentation doesn't mention spines at all.
  • This question shows how to set the pane colour with ax.w_xaxis.set_pane_color(), but there is no similar ax.w_zaxis.set_spine... method.
  • This question shows how to set the spine colour using ax.w_zaxis.line.set_color(). I thought about making a horrible workaround to set ax.w_zaxis.line.set_data manually, but it only has x and y data; no z! Even the x and y axes don't have z data.
Ford answered 25/1, 2018 at 12:18 Comment(2)
This is for sure not the first time this is being asked. Make sure to have a look at previous question on this. My guess would be that it is not actually possible to move the axes in the intended way, but I can't say for sure.Hatpin
@Hatpin yes, I appreciate that. This is something I have searched for many times before asking this question. Honestly, it's difficult to find anything which even mentions the 3d spines (including the matplotlib documentation). In response to your comment, I've added a brief summary of other questions to show that I haven't been able to find an existing answer.Ford
A
20

There seems to be no obvious way to do this at the moment. Setting the spines when the axis projection is 3D is not implemented. However, there is a small hack here.

The ax.spines setting is for 2D rendering. When you set projection=3d in the initialization of the figure, certain 2D properties (like ax.spines, etc.) are ignored. It's why you don't get any response when you set the 2D spines.

The 3D figure axis line (the thick black line for each axis) locations are determined by the parameter ax.xaxis._axinfo['juggled'] (and similarly for y and z axes). This specifies which of the six outer boundaries of a 3D plot bounding box are plotted as thick black lines.

You can shift the position of the axis line for each of x,y,z axis by overwriting the juggled value, which specifies which axis lines are the main ones, as the following example for the x axis, enter image description here the default setting, ax.xaxis._axinfo['juggled'] = (1,0,2) enter image description here new setting, ax.xaxis._axinfo['juggled'] = (2,0,1) The parameters for all the six outer boundaries are, enter image description here

Actinomycosis answered 1/4, 2018 at 19:16 Comment(4)
Is there a way to set the spines linewidth?Hagio
for 2d you can set the spine width separately, use ax.spines['left'].set_linewidth(x), where x is the linewidth you want, and left can be changed to others ('right', 'top', 'bottom'). for 3d, I don't think you can set separately (the source code for axis3d is at matplotlib.org/3.1.3/_modules/mpl_toolkits/mplot3d/axis3d.html), but you can use rcParams['axes.linewidth'] = x to set the linewidth globally to x.Actinomycosis
The default orientation of the ticks is kept when changing the "spine" that is drawn. Is there a way to set the ticks orientation afterwards?Subaxillary
What are the parameters for the inner boundaries?Simitar
L
0

This works in matplotlib 3.8+.

fig, axes = plt.subplots(1, 2, subplot_kw={'projection': '3d'})
axes[0].zaxis.set_ticks_position('lower')
axes[0].zaxis.set_label_position('lower')

enter image description here

Lancelle answered 6/5, 2024 at 11:56 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.