Python 3d scatterplot colormap issue - python

I have four dimensional data (x, y, z displacements; and respective voltages) which I wish to plot in a 3d scatterplot in python. I've gotten the 3d plot to render, but I want to have the colour of the points change using a colourmap, dependent upon the magnitude of the point's voltage.
I've tried a few things, but can't seem to get it to work I'm getting the error ValueError: Cannot convert argument type <type 'numpy.ndarray'> to rgba array. I'm not sure exactly how to convert what I need to convert, so if anybody could please offer some help, I'd be most appreciative.
My code is here:
fig = plt.figure()
from mpl_toolkits.mplot3d import Axes3D
cmhot = plt.cm.get_cmap("hot")
ax = fig.add_subplot(111, projection='3d',)
ax.scatter(x, y, z, v, s=50, c = cmhot)
plt.show()

ax.scatter can take a color parameter c which is a sequence (e.g. a list or an array) of scalars, and a cmap parameter to specify a color map. So to make the colors vary according to the magnitude of the voltages, you could define:
c = np.abs(v)
This makes positive and negative voltages have the same color. If instead you wished each color (positive or negative) to have its own color, you could just use c = v.
For example,
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
x, y, z, v = (np.random.random((4,100))-0.5)*15
c = np.abs(v)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
cmhot = plt.get_cmap("hot")
cax = ax.scatter(x, y, z, v, s=50, c=c, cmap=cmhot)
plt.show()

Related

Matplotlib bar chart or similar with bars located at a specific x,y,angle

Is there a way to create a bar chart using matplotlib such that the bars are located at a specific x,y and at a specific angle? In the screenshot below, I just drew thick lines (to represent thin bars) in PowerPoint on top of the scatterplot.
It doesn't have to be a barchart necessarily, I just don't know the name of a plot that is like this. I thought about trying to mimic this with a quiver plot but wasn't sure how. Reason for wanting this is densely spaced points that have variable values (not monotonically increasing like in this example), and just coloring the scatter plot isn't visually elucidating trends of interest, even with different colormaps.
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(25)
y = -x
z = x
plt.scatter(x, y, c=z, cmap='viridis')
I don't know of a canned way to do this, but you could, in a pinch, create your own function that draws rectangles to create this plot. For example:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Rectangle
x = np.arange(25)
y = -x
z = x
plt.scatter(x, y, c=z, cmap='viridis')
def slanted_bars(x, y, z, angle, ax):
for xi, yi, zi in zip(x, y, z):
ax.add_patch(Rectangle((xi, yi), 1, zi, angle))
fig, ax = plt.subplots(1, 1)
ax.scatter(x, y, c=z, cmap='viridis')
slanted_bars(x, y, z, -45, ax)
You'd have to play with the color and shape of the rectangles to get something appealing, but it can do what you want.

Wireframing from an image using matplotlib

I'm trying to make a 3D representation of an image as a surface using wireframes with matplotlib.
ig= mpimg.imread('testIMG.png');
X = np.linspace(0,len(ig[0]),len(ig[0])); #List of discrete x values
Y = np.linspace(0,len(ig[1]),len(ig[1])); #List of discrete y values
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#Plot the wireframe
#I want to plot the image as f(x,y) and I can't understand why wireframe won't let me
ax.plot_wireframe(X, Y, ig[:,:,2], rstride=10, cstride=10)
plt.show()
The imread function gives me an MxNx3 array of M rows, N columns, and an RGB value for each point in the matrix. I don't understand how to use wireframe to plot that data properly. These z values aren't plotting what I expected (a checkerboard pattern), but instead a y=x line alternating between 0 and 1.
What do I need to do here? I want a series of cuboids in a 3D checkerboard pattern.
Image of what I have currently
You may use np.meshgrid(), so:
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
import mpl_toolkits.mplot3d
ig = mpimg.imread('testIMG.png')
x = np.linspace(0, ig.shape[1], ig.shape[1]) #List of discrete x values
y = np.linspace(0, ig.shape[0], ig.shape[0]) #List of discrete y values
X, Y = np.meshgrid(x, y)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#Plot the wireframe
#I want to plot the image as f(x,y) and I can't understand why wireframe won't let me
ax.plot_wireframe(X, Y, ig[:,:,2], rstride=10, cstride=10)
plt.show()

Displaying Contours in 3D matplotlib Surface Graphs based on adjacent axis values

I have already posted an example similar to the following one regarding another issue here:
Displaying Contours in front of Surface in matplotlib
I am posting it again regarding a different question:
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.array([[200,800,1500,2000,3000],[200,700,1500,2000,3000],[200,800,1500,2000,3000],[200,800,1500,2000,3000]])
Y = np.array([[50,50,50,50,50],[350,350,350,350,350],[500,500,500,500,500],[1000,1000,1000,1000,1000]])
Z = np.array([[0,0,33,64,71],[44,62,69,74,76],[59,67,72,75,77],[63,68,73,76,77]])
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, alpha=0.5)
cset = ax.contour(X, Y, Z, zdir='x', offset=200, cmap=cm.coolwarm)
levels = [500,700,1000,2000,3000]
ax.set_xticks(levels)
ax.set_xlabel('X')
ax.set_xlim(200, 3000)
ax.set_ylabel('Y')
ax.set_ylim(0, 1000)
ax.set_zlabel('Z')
ax.set_zlim(0, 100)
plt.show()
Is it possible to have my contours plot a number of contours equal to the adjacent axis values:
e.g. 3D_Surface_from_code_above For the contours in my Y-Z plane to the left of the picture. Instead of having 7 contours displaying (which I'm not sure what X value they correspond too) would it be possible to have one for each X tick value ? i.e. 500, 700, 1000, 2000, 3000.
I hope that make sense, it would allow the viewer to follow the contour corresponding to X = 700 and see how Z varies with respect to Y for this fixed value of X. This would allow me to set the contours for values of X which are of particular interest to me.
Thank you for your help.
You can set the levels directly as an argument of the contours function as
levels = [500,700,1000,2000,3000]
cset = ax.contour(X, Y, Z, levels, zdir='x', offset=200, cmap=cm.coolwarm)

Scatter plot and Color mapping in Python

I have a range of points x and y stored in numpy arrays.
Those represent x(t) and y(t) where t=0...T-1
I am plotting a scatter plot using
import matplotlib.pyplot as plt
plt.scatter(x,y)
plt.show()
I would like to have a colormap representing the time (therefore coloring the points depending on the index in the numpy arrays)
What is the easiest way to do so?
Here is an example
import numpy as np
import matplotlib.pyplot as plt
x = np.random.rand(100)
y = np.random.rand(100)
t = np.arange(100)
plt.scatter(x, y, c=t)
plt.show()
Here you are setting the color based on the index, t, which is just an array of [1, 2, ..., 100].
Perhaps an easier-to-understand example is the slightly simpler
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(100)
y = x
t = x
plt.scatter(x, y, c=t)
plt.show()
Note that the array you pass as c doesn't need to have any particular order or type, i.e. it doesn't need to be sorted or integers as in these examples. The plotting routine will scale the colormap such that the minimum/maximum values in c correspond to the bottom/top of the colormap.
Colormaps
You can change the colormap by adding
import matplotlib.cm as cm
plt.scatter(x, y, c=t, cmap=cm.cmap_name)
Importing matplotlib.cm is optional as you can call colormaps as cmap="cmap_name" just as well. There is a reference page of colormaps showing what each looks like. Also know that you can reverse a colormap by simply calling it as cmap_name_r. So either
plt.scatter(x, y, c=t, cmap=cm.cmap_name_r)
# or
plt.scatter(x, y, c=t, cmap="cmap_name_r")
will work. Examples are "jet_r" or cm.plasma_r. Here's an example with the new 1.5 colormap viridis:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(100)
y = x
t = x
fig, (ax1, ax2) = plt.subplots(1, 2)
ax1.scatter(x, y, c=t, cmap='viridis')
ax2.scatter(x, y, c=t, cmap='viridis_r')
plt.show()
Colorbars
You can add a colorbar by using
plt.scatter(x, y, c=t, cmap='viridis')
plt.colorbar()
plt.show()
Note that if you are using figures and subplots explicitly (e.g. fig, ax = plt.subplots() or ax = fig.add_subplot(111)), adding a colorbar can be a bit more involved. Good examples can be found here for a single subplot colorbar and here for 2 subplots 1 colorbar.
To add to wflynny's answer above, you can find the available colormaps here
Example:
import matplotlib.cm as cm
plt.scatter(x, y, c=t, cmap=cm.jet)
or alternatively,
plt.scatter(x, y, c=t, cmap='jet')
Subplot Colorbar
For subplots with scatter, you can trick a colorbar onto your axes by building the "mappable" with the help of a secondary figure and then adding it to your original plot.
As a continuation of the above example:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(10)
y = x
t = x
fig, (ax1, ax2) = plt.subplots(1, 2)
ax1.scatter(x, y, c=t, cmap='viridis')
ax2.scatter(x, y, c=t, cmap='viridis_r')
# Build your secondary mirror axes:
fig2, (ax3, ax4) = plt.subplots(1, 2)
# Build maps that parallel the color-coded data
# NOTE 1: imshow requires a 2-D array as input
# NOTE 2: You must use the same cmap tag as above for it match
map1 = ax3.imshow(np.stack([t, t]),cmap='viridis')
map2 = ax4.imshow(np.stack([t, t]),cmap='viridis_r')
# Add your maps onto your original figure/axes
fig.colorbar(map1, ax=ax1)
fig.colorbar(map2, ax=ax2)
plt.show()
Note that you will also output a secondary figure that you can ignore.
Single colorbar for multiple subplots
sometimes it is preferable to have a single colorbar to indicate data values visualised on multiple subplots.
In this case, a Normalize() object needs to be created using the minimum and maximum data values across both plots.
Then a colorbar object can be created from a ScalarMappable() object, which maps between scalar values and colors.
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(10)
y = x
t1 = x # Colour data for first plot
t2 = 2*x # Color data for second plot
all_data = np.concatenate([t1, t2])
# Create custom Normalise object using the man and max data values across both subplots to ensure colors are consistent on both plots
norm = plt.Normalize(np.min(all_data), np.max(all_data))
fig, axs = plt.subplots(1, 2)
axs[0].scatter(x, y, c=t1, cmap='viridis', norm=norm)
axs[1].scatter(x**2, y, c=t2, cmap='viridis', norm=norm)
# Create the colorbar
smap = plt.cm.ScalarMappable(cmap='viridis', norm=norm)
cbar = fig.colorbar(smap, ax=axs, fraction=0.1, shrink = 0.8)
cbar.ax.tick_params(labelsize=11)
cbar.ax.set_ylabel('T', rotation=0, labelpad = 15, fontdict = {"size":14})
plt.show()
subplots_colorbar

Python matplotlib : plot3D with a color for 4D

I am trying to make a 3D plot from x, y, z points list, and I want to plot color depending on the values of a fourth variable rho.
Currently I have ;
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot3D(cell_x, cell_y, cell_z, linestyle='None', marker='o', markersize = 5, antialiased=True)
ax.set_xlim3d(0.45, 0.55)
ax.set_ylim3d(0.45, 0.55)
ax.set_zlim3d(0.45, 0.55)
How to add cell_rho (my fourth array) as the color of my x, y, z points ? (for example for a jet colormap).
Thank you very much.
EDIT : I can't use scatter plots because for my 18000 points scatter plots are very slow compared to plot3d with markers only.
If you want to display a simple 3D scatterplot, can't you just use scatter?
E.g.,
x, y, z = randn(100), randn(100), randn(100)
fig = plt.figure()
from mpl_toolkits.mplot3d import Axes3D
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x, y, z, c=randn(100))
plt.show()
(I'm running the above code under python -pylab.)
It seems, on the contrary, that with plot3D you must convert your fourth dimension to RGB tuples.

Categories

Resources