Python: 3D scatter losing colormap - python

I'm creating a 3D scatter plot with multiple sets of data and using a colormap for the whole figure. The code looks like this:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for R in [range(0,10), range(5,15), range(10,20)]:
data = [np.array(R), np.array(range(10)), np.array(range(10))]
AX = ax.scatter(*data, c=data[0], vmin=0, vmax=20, cmap=plt.cm.jet)
def forceUpdate(event): AX.changed()
fig.canvas.mpl_connect('draw_event', forceUpdate)
plt.colorbar(AX)
This works fine but as soon as I save it or rotate the plot, the colors on the first and second scatters turn blue.
The force update is working by keeping the colors but only on the last scatter plot drawn. I tried making a loop that updates all the scatter plots but I get the same result as above:
AX = []
for R in [range(0,10), range(5,15), range(10,20)]:
data = [np.array(R), np.array(range(10)), np.array(range(10))]
AX.append(ax.scatter(*data, c=data[0], vmin=0, vmax=20, cmap=plt.cm.jet))
for i in AX:
def forceUpdate(event): i.changed()
fig.canvas.mpl_connect('draw_event', forceUpdate)
Any idea how I can make sure all scatters are being updated so the colors don't disappear?
Thanks!

Having modified your code so that it does anything:
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> from mpl_toolkits.mplot3d import Axes3D
>>> AX = \[\]
>>> fig = plt.figure()
>>> ax = fig.add_subplot(111, projection='3d')
>>> for R in \[range(0,10), range(5,15), range(10,20)\]:
... data = \[np.array(R), np.array(range(10)), np.array(range(10))\]
... AX = ax.scatter(*data, c=data\[0\], vmin=0, vmax=20, cmap=plt.cm.jet)
... def forceUpdate(event): AX.changed()
... fig.canvas.mpl_connect('draw_event', forceUpdate)
...
9
10
11
>>> plt.colorbar(AX)
<matplotlib.colorbar.Colorbar instance at 0x36265a8>
>>> plt.show()
then I get:
So the above code is working. If your existing code isn't then I suggest that you try the exact code above and if that doesn't work look into the versions of code that you are using if it does work then you will have to investigate the differences between it and your actual code, (rather than your example code).

Related

How to plot a Polycollection created with .fill_between() in matplotlib?

I have this code to plot
import matplotlib.pyplot as plt
import numpy as np
# Data
time = np.arange(0,20,0.05)
data = np.sin(time)
# Plot
main_plot = plt.plot(time, data)
error_plot = plt.fill_between(time, data+0.5, data-0.5, alpha=0.2)
plt.show()
and I would like to plot, after in the code, the same plot but using the main_plot and error_plot variable. So I have this
plt.plot(*main_plot[0].get_data())
# Here I want to plot the error_plot (PolyCollection type)
# but I don't know how. Something like
# plt.plot(error_plot)
I have tried this related post with no result. I've also tried
fig = plt.figure()
ax = fig.add_subplot(111)
ax.add_collection(error_plot)
but resulting in this error
1 fig = plt.figure()
2 ax = fig.add_subplot(111)
----> 3 ax.add_collection(error_plot)
RuntimeError: Can not put single artist in more than one figure

Show legend that matplotlib dynamically created

My df has 4 columns: x, y, z, and grouping. I have created a 3D plot, with the assigned color of each point being decided by what grouping it belongs to in that row. For reference, a "grouping" can be any number from 1 to 6. The code is shown below:
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter3D(df.x, df.y, df.z, c=df.grouping)
plt.show()
I would like to show a legend on the plot that shows which color belongs to which grouping. Previously, I was using Seaborn for a 2D plot and the legend was automatically plotted. How can I add this feature with matplotlib?
If the values to be colormapped are numeric, the solution can be as simple as:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
a = np.random.rand(3,40)
c = np.random.randint(1,7, size=a.shape[1])
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
sc = ax.scatter3D(*a, c=c)
plt.legend(*sc.legend_elements())
plt.show()

Matplotlib copy/duplicate a 3D figure?

I've tried to find a way to copy a 3D figure in matplotlib but I didn't find a solution which is appropriate in my case.
From these posts
How do I reuse plots in matplotlib?
and
How to combine several matplotlib figures into one figure?
Using fig2._axstack.add(fig2._make_key(ax),ax) as in the code below gives quite the good result but figure 2 is not interactive I can't rotate the figure etc :
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(1)
ax = fig.gca(projection = '3d')
ax.plot([0,1],[0,1],[0,1])
fig2 = plt.figure(2)
fig2._axstack.add(fig2._make_key(ax),ax)
plt.show()
An alternative would be to copy objects from ax to ax2 using a copy method proposed in this post How do I reuse plots in matplotlib? but executing the code below returns RuntimeError: Can not put single artist in more than one figure :
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np, copy
fig = plt.figure(1)
ax = fig.gca(projection = '3d')
ax.plot([0,1],[0,1],[0,1])
fig2 = plt.figure(2)
ax2 = fig2.gca(projection = '3d')
for n in range(len(ax.lines)) :
ax2.add_line(copy.copy(ax.lines[n]))
plt.show()
Those codes are pretty simple but I don't want to copy/paste part of my code for drawing similar figures
Thanks in advance for your reply !

Invert y axis on matplotlib trisurf 3d graph python

I'm using matplotlib to produce a 3d trisurf graph. I have everything working except that I would like to invert the y-axis, so that the origin is 0,0 not 0,100. I've looked through the matplotlib axes3d API and cannot figure out how to do this. Here is my code:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
# my data, xs=xaxis, ys=yaxis, zs=zaxis
mortar_xs = []
cycles_ys = []
score_zs = []
#... populate my data for the 3 arrays: mortar_xs, cycles_ys, score_zs
# plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(mortar_xs,cycles_ys,score_zs,cmap=cm.coolwarm)
ax.set_zlim(bottom=0.0,top=1.0)
ax.legend()
ax.set_xlabel("# Mortar")
ax.set_ylabel("# Goals")
ax.set_zlabel("# Score")
plt.show()
My graph produced is the following, but I need the '# Goals' or the y-axis inverted, so that the origin is 0,0 not 0,100. If possible, I would like to do this without changing my data.
tmdavison's comment is what I was looking for:
ax.set_ylim(0,100)
Or
ax.set_ylim(100,0)
The simplest method would be to use ax.invert_yaxis()

How can an existing 2D AxesSubplot object be converted/upgraded to an Axes3DSubplot object?

Assume I have some matplotlib code that looks like the following:
### import statements, etc. ###
ax1 = fig.add_subplot(221)
### plot some 2D data to ax1 ###
ax2 = fig.add_subplot(221, projection='3d')
### plot some 3D data to ax2 ###
The effect of the ax2 = ... line is to remove whatever was plotted to ax1, and to create a new Axes3DSubplot object.
My question is: how can I get an ax2 object (having the same subplot position as ax1 had) which has a 3D projection and which "imports" all of the 2D data that was previously plotted to ax1?
You can plot the data from the 2D-Axes in the corresponding 3D-Axes. Example:
get from this:
plot here:
This is the code used for the above example:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
plt.ion()
ax1 = plt.subplot(221)
ax1.plot([1,2,3,4])
ax2 = plt.subplot(222)
ax2.plot([4,3,2,1])
ax3 = plt.subplot(223)
ax3.plot([4,3,2,1])
ax4 = plt.subplot(224)
ax4.plot([1,2,3,4])
plt.gcf().tight_layout()
plt.show()
plt.figure()
ax1_3d = plt.subplot(221, projection='3d')
ax2_3d = plt.subplot(222, projection='3d')
ax3_3d = plt.subplot(223, projection='3d')
ax4_3d = plt.subplot(224, projection='3d')
[ax1_3d.plot(*ax1.lines[i].get_data()) for i,v in enumerate(ax1.lines)]
[ax2_3d.plot(*ax2.lines[i].get_data()) for i,v in enumerate(ax2.lines)]
[ax3_3d.plot(*ax3.lines[i].get_data()) for i,v in enumerate(ax3.lines)]
[ax4_3d.plot(*ax4.lines[i].get_data()) for i,v in enumerate(ax4.lines)]
plt.gcf().tight_layout()
plt.show()
This is a bit of a resurrection, but I came across this same issue today and found a solution, based mostly off of #Saullo Castro's answer. This example uses one subplot, but the idea is the same no matter how many you have:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Initial 2d axes
ax1 = plt.subplot(111)
ax1.plot([1,2,3,4])
# Create Axes3D and plot the 2d data on it
ax1_3d = plt.subplot(111, projection='3d')
[ax1_3d.plot(*ax1.lines[i].get_data()) for i,v in enumerate(ax1.lines)]
# Important step: turn off the old 2d axes!
ax1.set_axis_off()
plt.show()

Categories