I want to plot red, blue and green colors on the three axis and an array which stores the value corresoding to each combination of color in python2.7....when i run my program either becomes unresponsive for 24 hours or it gives me memory error. Here is my code:
import pylab
import math
from itertools import product
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
N=[]
p=np.zeros((256,256,256))
S=[]
fig=plt.figure()
ax=fig.gca(projection='3d')
X=np.arange(0,256,1) #for one of the features either red, blue or green
Y=np.arange(0,256,1)
X,Y = np.meshgrid(X,Y)
R=np.sqrt(X**2 + Y**2)
Z=R/np.sqrt(2)
N=p.flatten();
N=(p[i,j,k] for k in Z)
surf=ax.plot_surface(X,Y,Z, rstride=1, cstride=1,
facecolors=cm.jet(N),
linewidth=0, antialiased=False, shade=False)
plt.show()
Please help. I have read the previous posts, and have used them, still I am getting memory error. Here p is a containing values of combinations of red, green and blue. For simplicity I have initialized it to zero...it is giving the following error..colset.append(fcolors[rs][cs])
IndexError: index out of bounds
First, your program is slow because you're doing a lot of unnecessary work building N. You're building a 70 MB list a few bytes at a time (256*256*256=16,777,216 appends!). A better (faster, memory efficient) way to build p is to use numpy's array broadcasting, and then reuse p to make N:
import numpy as np
a = np.arange(256)
p = a[:,np.newaxis,np.newaxis] * a[np.newaxis,:,np.newaxis] * a[np.newaxis,np.newaxis,:]
N = p.flatten()
Second and more importantly, you're not using plot_surface() correctly. According to the docs, X, Y and Z should be 2D arrays. X and Y lay down a 2D grid and Z provides the "height" for each point on that 2D grid. If you want to manually set the facecolor, it should also be a 2D array. You should look at the example in the docs for a working example.
EDIT:
I'm not sure what your plot is intended to look like, so lets walk through the MPL demo.
Make the necessary imports and create an axis object (yours does this correctly):
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
Next, make an X/Y grid and corresponding Z. In your program, X, Y and Z are 1D. They describe a line in 3D space, not a surface.
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y) # <-- returns a 2D grid from initial 1D arrays
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
Lets first plot the simplest thing possible. No colors, default anti-aliasing, lines, etc.
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1)
plt.show()
Now add a colors. Note that the color comes from the Z component.
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.jet)
plt.show()
Now manually control the colors (MPL inspiration).
colortuple = ('y', 'k') # only use two colors: yellow and black
xlen, ylen = X.shape # get length of
colors = np.empty(X.shape, dtype=str) # make a 2D array of strings
for i in range(xlen):
for j in range(ylen):
index = (i + j) % 2 # alternating 0's and 1's
colors[i,j] = colortuple[index]
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
facecolors=colors)
If you want to color based on some other metric, you can create your own colormap. There are many answered questions on how to do that.
Edit 2:
Colors can also be specified as RGB sequences. For something like your red on X, green on Y description you could do this:
xlen, ylen = X.shape
colors = np.zeros((xlen,ylen,3))
jspan = np.linspace(0., 1., ylen)
ispan = np.linspace(0., 1., xlen)
for i in range(xlen):
colors[i,:,0] = jspan
for j in range(ylen):
colors[:,j,1] = ispan
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=colors,)
Related
I have a large set of measurements that I want to visualize in 4D using matplotlib in Python.
Currently, my variables are arranged in this way:
x = np.array(range(0, v1))
y = np.array(range(0, v2))
z = np.array(range(0, v3))
I have C which is a 3D array containing measurement values for each combination of the previous variables. So it has a dimension of v1*v2*v3.
Currently, I visualize my measurements using contourf function and I plot that for each z value. This results in 3D contour plot i.e. 2D + color map for the values. Now, I want to combine all the variables and look at the measurements in 4D dimensions (x, y, z, and color corresponding to the measurement value). What is the most efficient way to do this in python?
Regarding to #Sameeresque answer, I think the question was about a 4D graph like this (three coordinates x, y, z and a color as the fourth coordinate):
import numpy as np
import matplotlib.pyplot as plt
# only for example, use your grid
z = np.linspace(0, 1, 15)
x = np.linspace(0, 1, 15)
y = np.linspace(0, 1, 15)
X, Y, Z = np.meshgrid(x, y, z)
# Your 4dimension, only for example (use yours)
U = np.exp(-(X/2) ** 2 - (Y/3) ** 2 - Z ** 2)
# Creating figure
fig = plt.figure()
ax = plt.axes(projection="3d")
# Creating plot
ax.scatter3D(X, Y, Z, c=U, alpha=0.7, marker='.')
plt.show()
A 4D plot with (x,y,z) on the axis and the fourth being color can be obtained like so:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = np.array(range(0, 50))
y = np.array(range(0, 50))
z = np.array(range(0, 50))
colors = np.random.standard_normal(len(x))
img = ax.scatter(x, y, z, c=colors, cmap=plt.hot())
fig.colorbar(img)
plt.show()
A simple way to visualize your 4D function, call it W(x, y, z), could be producing a gif of the cross-section contour plots along the z-axis.
Package plot4d could help you do it. An example plotting an isotropic 4D function:
from plot4d import plotter
import numpy as np
plotter.plot4d(lambda x,y,z:x**2+y**2+z**2, np.linspace(0,1,20), wbounds=(0,3), fps=5)
The code above generates this gif:
Is there a way to map the color-scheme from one surface plot onto another?
For example, let's say I have:
surf_1 = ax.plot_surface(X, Y, Z, cmap='summer')
and
surf_2 = ax.plot_surface(X, Y, Z-Q, cmap='summer')
Is there a way to map the colorscheme for the surface defined by Z-Q onto the surface defined by Z? In other words, I want to visualize surf_1, but I want its surface to take on the colors defined by surf_2.
For context, I am trying to visualize the colors of the fluctuations of a parameter (Z) around a variable height (Q), where Q is not necessarily equal to 0.
EDIT: Is there a way I could extract the colors in surf_2 as an array, and use those colors as input colors for surf_1? Any suggestions would be much appreciated!
You can use ScalarMappable() function to create all colors to use as facecolors in the two surface plots. Here is the runnable code that demonstrates the steps to achieve what you want.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
fig, ax = plt.subplots(subplot_kw={'projection': '3d'})
fig.set_size_inches([10, 8])
# Make up data for 2 surfaces
X = np.logspace(0, np.log10(16), 50)
Y = np.linspace(3, 6, 50)
Z = np.linspace(-1, 1, 50)
# Convert to 2d arrays
Z = np.outer(Z.T, Z) # 50x50
X, Y = np.meshgrid(X, Y) # 50x50
# Make use of `ScalarMappable()` for custom color
# This use Z to get a colormap for plotting the surface
C = np.linspace(-1, 1, Z.size).reshape(Z.shape)
colormap = "summer" # 'inferno' 'plasma' 'viridis'
scmap = plt.cm.ScalarMappable(cmap=colormap)
# for clarity, 2 surfaces are separated by some z shift
zshift = 80
# Upper-surface
# Note: ax.plot_surface(X, Y, Z*X+zshift, cmap=colormap)
# is almost equivalent with this
ax.plot_surface(X, Y, Z*X+zshift, facecolors=scmap.to_rgba(Z*X+zshift), shade=False)
# `shade=False` is used to suppress 3D shading
# Lower-surface
# Also use `facecolors=scmap.to_rgba(Z*X+zshift)`
# thus, equivalent with taking color from previous surface
ax.plot_surface(X, Y, Z, facecolors=scmap.to_rgba(Z*X+zshift), shade=False)
plt.show()
The output plot:
I am trying to visualize a 3D parameter space where I am confident about my parameters with voxels, and where my estimated parameters are with scatters. The goal is to clearly see whether or not the parameters are within this space.
Even though the voxels are not see-through, the scatters are plotted 'on top' of the voxel:
I would like the voxel to at least hide the scatters behind it, but preferably I would like to make the voxels see-through (e.g. alpha 0.5), with the scatters changing color depending on how much voxel the light has travelled through. Is something like this even possible with matplotlib?
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
x, y, z = np.random.random((3, 100)) * 5
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.scatter(x, y, z)
cube = np.mgrid[0:1:0.2,0:1:0.2] > 0.5
ax.voxels(cube, edgecolor='k', linewidth=0.)
As #tmdavison pointed out, matplotlib is quite limited for these kind of things. Here is my example using mayavi instead.
import mayavi.mlab
import np as np
x, y, z = np.random.random((3, 100)) * 5
xx, yy, zz = np.where(np.mgrid[0:1:0.2,0:1:0.2] > 0.5)
nodes = mayavi.mlab.points3d(x, y, z,
color=(1, 0, 0),
scale_factor=0.1)
mayavi.mlab.points3d(xx, yy, zz,
mode="cube",
color=(0, 1, 0),
scale_factor=1)
mayavi.mlab.show()
Okay, apologies for this question but I'm pulling my hair out here.
I have a data structure loaded in python in the form:
[(1,0,#),(1,1,#),(1,2,#),(1,3,#),(2,0,#),(2,1,#) ... (26,3,#)]
with # being a different number each time that I wish to represent on the z-axis. You can see that x and y are always integers.
Plotting a scatter graph is simple:
x,y,z = zip(*data)
fig = plt.figure()
ax = fig.gca(projection = '3d')
surface = ax.scatter(x, y, z)
plt.show()
But when it comes to surfaces, I can see two methods:
1) Call ax.plot_trisurf(), which should work with 1D arrays similar to ax.scatter() and apparently works here, but for me gives me an error:
"AttributeError: Axes3D subplot object has not attribute 'plot_trisurf'"
This error also appears if I use the example source code at:
http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#tri-surface-plots, suggesting it's something wrong with my installation - my Matplotlib version is 1.1.1rc,. This error does not appear if, for example, ax.plot_surface() is called, nor ax.scatter().
2) Use meshgrid() or griddata() in combination with ax.plot_surface() - in either case, after two days' of pouring over the documentation and examples, I still don't understand how to correctly use these in my case, particularly when it comes to generating the values for Z.
Any help would be much appreciated.
To address your first question (1) I believe you need to import Axes3D from the mplot3d library, even if you're not directly calling it. Maybe try adding
from mpl_toolkits.mplot3d import Axes3D
before your main code (this line triggered a memory while reading the tutorial).
As for (2), X, Y and Z need to be matrix (2d array) type objects. This can get confusing, but you may consider an example:
# two arrays - one for each axis
x = np.arange(-5, 5, 0.25)
y = np.arange(-5, 5, 0.25)
# create a mesh / matrix like object from the arrays
X, Y = np.meshgrid(x, y)
# create Z values - also in a mesh like shape
Z = np.sin(np.sqrt(X**2 + Y**2))
# plot!
surface = ax.plot_surface(X, Y, Z)
Here is an example of how could you extract your z-values from data
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
data = [(j,i,i**2 + j) for j in range(1,27) for i in range(4)]
print data
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(0, 4, 1)
Y = np.arange(1, 27, 1)
X, Y = np.meshgrid(X, Y)
print X.shape
print Y.shape
Z = np.array([z for _,_,z in data]).reshape(26,4)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=True)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.xlabel('X')
plt.ylabel('Y')
plt.show()
I want to create some plots of the farfield of electromagnetic scattering processes.
To do this, I calculated values θ, φ and r. The coordinates θ and φ create a regular grid on the unitsphere so I can use plot_Surface (found here) with conversion to cartesian coordinates.
My problem is now, that I need a way to color the surface with respect to the radius r and not height z, which seems to be the default.
Is there a way, to change this dependency?
I don't know how you're getting on, so maybe you've solved it. But, based on the link from Paul's comment, you could do something like this. We pass the color values we want using the facecolor argument of plot_surface.
(I've modified the surface3d demo from the matplotlib docs)
EDIT: As Stefan noted in his comment, my answer can be simplified to:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(-5, 5, 0.25)
xlen = len(X)
Y = np.arange(-5, 5, 0.25)
ylen = len(Y)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
maxR = np.amax(R)
Z = np.sin(R)
# Note that the R values must still be normalized.
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=cm.jet(R/maxR),
linewidth=0)
plt.show()
And (the end of) my needlessly complicated original version, using the same code as above though omitting the matplotlib.cm import,
# We will store (R, G, B, alpha)
colorshape = R.shape + (4,)
colors = np.empty( colorshape )
for y in range(ylen):
for x in range(xlen):
# Normalize the radial value.
# 'jet' could be any of the built-in colormaps (or your own).
colors[x, y] = plt.cm.jet(R[x, y] / maxR )
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=colors,
linewidth=0)
plt.show()