I have three variables for my plot and I colour by the fourth variable. I have made a scatter plot via the following code, but I want a contour plot. My code:
import numpy as np
import matplotlib.pyplot as plt
a=np.linspace(4.0,14.0,3)
b=np.linspace(0.5,2.5,3)
c=np.linspace(0.0,1.0,3)
d=np.random.rand(len(a),len(b),len(c)) #colour by this variable
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
z,y,x=np.meshgrid(c,a,b)
img = ax.scatter(x, y, z, c=d, cmap='RdGy')
fig.colorbar(img, pad=0.2).set_label('colour')
ax.set_xlabel('c')
ax.set_ylabel('a')
ax.set_zlabel('b')
I want a filled contour instead of scatter plot. I know mayavi.mlab has this feature, but I cannot import mlab for some reason. Is there an alternative, or is there a better way of presenting this data?
Here is how I would present this 3-dimensional data. Each plot is a cross-section through the cube. This makes sense intuitively.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(4.0, 14.0, 3)
y = np.linspace(0.5, 2.5, 3)
z = np.linspace(0.0, 1.0, 3)
data = np.random.rand(len(x), len(y), len(z))
fig, axes = plt.subplots(len(z), 1, figsize=(3.5, 9),
sharex=True,sharey=True)
for i, (ax, d) in enumerate(zip(axes, data.swapaxes(0, 2))):
ax.contour(x, y, d)
ax.set_ylabel('y')
ax.grid()
ax.set_title(f"z = {z[i]}")
axes[-1].set_xlabel('x')
plt.tight_layout()
plt.show()
My advice: 3D plots are rarely used for serious data visualization. While they look cool, it is virtually impossible to read any data points with any accuracy.
Same thing goes for colours. I recommend labelling the contours rather than using a colour map.
You can always use a filled contour plot to add colours as well.
Related
I have a 2D plot placed on one of the walls of a 3D plot that doesn't seem to reflect any changes from set_data(), I would like to understand what I'm doing wrong here.
Here is a sample code showing the 3D plot with the 2D 'projection' plot in question.
The output is shown here:
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['legend.fontsize'] = 10
fig = plt.figure()
ax = fig.gca(projection='3d')
# Test data for projection onto xz plane
t = linspace(0,10, num=20)
z = np.sin(t)
# Plot projection
projx, = ax.plot(np.linspace(-1,0, num=len(z)), z, 'r', zdir='y', zs=1)
# Labels and scaling
ax.set_xlabel('M_x')
ax.set_ylabel('M_y')
ax.set_zlabel('M_z')
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
ax.set_zlim(-1, 1)
# Update projection data
projx.set_data([0],[0])
# See if actually updated data
print(projx.get_xdata())
# Draw and display window
plt.draw()
ax.legend()
plt.show()
I imagine that this line:
projx.set_data([0],[0])
would make the projection plot virtually empty. Instead, the sine wave remains.
Furthermore, the printout yields [0] as expected, so the set_data() call was successful, but for some reason the plot doesn't get drawn with the new data.
Shouldn't the set_data() changes be reflected when drawn afterwards?
There is a way to update a Line3D object by directly setting its vertices. Not sure, if this might have any negative side effects, though.
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca(projection='3d')
# Test data for projection onto xz plane
t = np.linspace(0,10, num=20)
z = np.sin(t)
# Plot projections
projx, = ax.plot(np.linspace(-1,0, num=len(z)), z, 'r', zdir='y', zs=1, label="changed")
projy, = ax.plot(np.linspace(-1,0, num=len(z)), z, 'b', zdir='x', zs=-1, label="not changed")
# Labels and scaling
ax.set_xlabel('M_x')
ax.set_ylabel('M_y')
ax.set_zlabel('M_z')
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
ax.set_zlim(-1, 1)
#update vertices of one Line3D object
projx._verts3d = [0, 0.2, 0.7], [1, 1, 1], [0.5, 0.2, 0.7]
ax.legend()
plt.show()
Sample output:
However, since one cannot omit any of the x, y, and z arrays, there is no real advantage over plotting it as a 3D array with one array being a constant.
I am trying to visualize differences between images in 3D, in order to more easily differentiate between positive and negative differences.
I have succeeded with a basic plot of an image, however, between the values matplotlib is interpolating values. I need these to be step changes between pixels.
I am often testing with very low-resolution images, for example, 16 by 16, so the interpolation has a large effect.
Numpy file of 16 by 16 image:
https://wetransfer.com/downloads/c916f76e0d86a61c00c2ed4cfe4ae97520190210192200/60d87c
One way to solve this would be to repeat the values however, this seems very inefficient and requires cleaning up the ticks after.
Code to generate above image:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
SubIm = np.load("Subtract_Image.npy")
def ImPlot2D3D(img, cmap=plt.cm.jet):
Z = img[::1, ::1]
fig = plt.figure(figsize=(14, 7))
# 2D Plot
ax1 = fig.add_subplot(1, 2, 1)
im = ax1.imshow(Z, cmap=cmap)
ax1.set_title('2D')
ax1.grid(False)
# 3D Plot
ax2 = fig.add_subplot(1, 2, 2, projection='3d')
X, Y = np.mgrid[:Z.shape[0], :Z.shape[1]]
ax2.plot_surface(X, Y, Z, cmap=cmap)
ax2.set_title('3D')
plt.show()
ImPlot2D3D(SubIm)
I've looked into 3D bar charts but they all use binning schemes and I can't make it work for an image.
Eventually managed to answer my own question.
A brute force method to solve this is to repeat the values in the array, hence making the interpolation between values that 'matplotlib' does, less impactful and better approximating a step change.
This can be achieved using numpy.repeat. As this is a 3D array we must iterate over one axis than the other. Otherwise, the array will be flattened repeated and this flat array returned.
Result:
def ImPlot2D3D(img, cmap=plt.cm.jet, step=False, ratio=10):
if step:
img = (img.repeat(ratio, axis=0)).repeat(ratio, axis=1)
Z = img[::1, ::1]
fig = plt.figure(figsize=(14, 7))
# 2D Plot
ax1 = fig.add_subplot(1, 2, 1)
im = ax1.imshow(Z, cmap=cmap)
ax1.set_title('2D')
ax1.grid(False)
# 3D Plot
ax2 = fig.add_subplot(1, 2, 2, projection='3d')
X, Y = np.mgrid[:Z.shape[0], :Z.shape[1]]
ax2.plot_surface(X, Y, Z, cmap=cmap)
ax2.set_title('3D')
# Scale the ticks back down to original values
if step:
ticks_x = ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x / ratio))
ticks_y = ticker.FuncFormatter(lambda y, pos: '{0:g}'.format(y / ratio))
ax1.xaxis.set_major_formatter(ticks_x)
ax1.yaxis.set_major_formatter(ticks_y)
ax2.xaxis.set_major_formatter(ticks_x)
ax2.yaxis.set_major_formatter(ticks_y)
plt.show()
import matplotlib.ticker as ticker
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
SubIm = np.load("Subtract_Image.npy")
ImPlot2D3D(SubIm, step=True)
I have a 3d line plot of the solar spectrum, which I plotted using the command,
from mpl_toolkits.mplot3d.axes3d import Axes3D
from matplotlib.collections import PolyCollection, LineCollection
from matplotlib.colors import colorConverter, ListedColormap, BoundaryNorm
import matplotlib.cm as cm
fig = plt.figure(figsize(15, 8))
ax = fig.gca(projection='3d')
x = SpectrumDF['Wavelength']
z = SpectrumDF['DNI']
y = SpectrumDF['TESTNUM']
ax.plot(x, y, z)
ax.set_xlabel('Wavelength')
ax.set_ylabel('Test Number')
ax.set_zlabel('Intensity')
The resultant plot is solid blue and takes whichever individual color I give in the function: plot( ).
I have been trying to create a color gradient along the z-axis, intensity, without any success.
I have around 500 test numbers, each has 744 data points.
Thank you for the help!
This wouldn't let me post images because I don't have enough reputation. Anyway, here's the link to the plot I get using this code https://plus.google.com/106871046257785761571/posts/fMYsDF5wAQa
Using the example - Line colour of 3D parametric curve in python's matplotlib.pyplot - I got a scatter plot with color gradient along the z axis - here's the link to the image of that plot - https://plus.google.com/u/0/106871046257785761571/posts/SHTsntgQxTw?pid=6133159284332945618&oid=106871046257785761571
I used the following command:
fig = plt.figure(figsize(15,8))
ax = fig.gca(projection='3d')
x = FilteredDF['Wavelength']
z = FilteredDF['DNI']
y = FilteredDF['TESTNUM']
ax.scatter(x, y, z, c=plt.cm.jet(z/max(z)))
ax.set_xlabel('Wavelength')
ax.set_ylabel('Test Number')
ax.set_zlabel('Intensity')
plt.show()
I am still working on getting a colored line plot because I have a lot of points, which makes scatter plot very slow to work with.
Thank you
I'm trying to replicate the matrix adjacency visualization as demonstrated in this D3.js example. Note that each cell is padded, resulting in a white border around each cell.
This is what I've got so far:
img = matplotlib.pyplot.imshow(m, interpolation='none')
img.axes.xaxis.tick_top()
img.axes.xaxis.set_ticks_position('none')
img.axes.yaxis.set_ticks_position('none')
img.axes.spines['top'].set_color('none')
img.axes.spines['bottom'].set_color('none')
img.axes.spines['left'].set_color('none')
img.axes.spines['right'].set_color('none')
matplotlib.pyplot.set_cmap('gray_r')
matplotlib.pyplot.xticks(range(len(m)), G.nodes(), rotation='vertical')
matplotlib.pyplot.yticks(range(len(m)), G.nodes(), rotation='horizontal')
I've looked into ways of iterating through each cell, and into other interpolation techniques, but I'd really like to keep no interpolation at all, as I'd like to keep the cells square. Has anyone tried to do this before?
One possible solution is to use the pcolor method of pyplot, as it accepts a kwarg edgecolor.
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(6)
y = np.arange(6)
X, Y = np.meshgrid(x, y)
Z = np.random.rand(5, 5)
ax = plt.subplot(111, aspect='equal') # To make the cells square
ax.pcolor(X, Y, Z,
edgecolor='white', # Color of "padding" between cells
linewidth=2) # Width of "padding" between cells
plt.show()
x,y are positions of the circles and r is the radius - all vectors.I want to plot them all at once. Something like:
import matplotlib.pyplot as plt
from matplotlib.patches Circle
#define x,y,r vectors
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
plt.Circle((x,y),r,color='r')
plt.show()
Thanks.
plt.scatter allows you to define a radius of the points plotted.
From the doc
matplotlib.pyplot.scatter(x, y, s=20, c='b', marker='o')
[...]
s:
size in points^2. It is a scalar or an array of the same length as x and y.
Playing with facecolor and edgecolor you should be able to get what you want
You can find an example in How to set_gid() for each bubble in matplot scatter chart?
I am not informed about the Circles patch, but here is how you can do it with the standard plot command:
import numpy as np
import matplotlib.pyplot as plt
x = np.array([0.2,0.4])
y = np.array([0.2,1.2])
r = np.array([0.5,0.3])
phi = np.linspace(0.0,2*np.pi,100)
na=np.newaxis
# the first axis of these arrays varies the angle,
# the second varies the circles
x_line = x[na,:]+r[na,:]*np.sin(phi[:,na])
y_line = y[na,:]+r[na,:]*np.cos(phi[:,na])
plt.plot(x_line,y_line,'-')
plt.show()
The basic idea is to give the plt.plot(...) command two 2D arrays. In that case they are interpreted as a list of plots. Espacially for many plots (=many circles) this is much faster, than plotting circle by circle.