I've been using Python/NumPy for a lot of things for a while now, but I am still confused about creating 3D plots.
In a "traditional" data analysis program (Origin, SigmaPlot, Excel...), if you want to make a 3D plot or a contour plot, you usually have your data in (X,Y,Z) format, that is, for each pair of X and Y you have one value of Z.
As opposed to this, all Python plotting guides I find use numpy.meshgrid for plotting -and I don't fully understand the connection to the traditional plotting software.
Let's say I have the following code:
axes_range = np.linspace(-5, 5, num=25)
alphas = []
for xcoord in axes_range:
for ycoord in axes_range:
alphas.append(f(xcoord,ycoord))
What's the best way of making a plot of (xcoord, ycoord, alphas)?
With matplotlib you simply need
import matplotlib.pylab as plt
import numpy as np
from matplotlib import cm
X, Y = np.meshgrid(xcoord, ycoord)
plt.contourf(X, Y, alphas.T, levels=20, cmap=cm.jet)
plt.show()
I think you need to transpose alphas as I do here.
Related
I'm trying to generate a 3d plot from a few datapoints. My goal is it, to compare two different datasets and show how good they match at different points. Right now I'm working on the first surface and my supervisor is unhappy with the visualization.
I use the following code at the moment:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import axes3d
# Create the figure and axes objects
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# Define the data for the first surface
x1 = [25,35,40,45,50,55,60]
y1 = [1300,4000,5000,5400]
z1 = [8.06,5.81,5.10,4.55,4.1,3.01,2.51,6.46,4.93,4.4,4.03,3.15,2.83,2.4,5.95,4.6,3.87,3.19,2.91,2.7,2.36,5.69,4.29,3.63,3.1,2.85,2.65,2.33]
# Convert the z1 data to 2D arrays
x, y = np.meshgrid(x1, y1)
z1 = np.array(z1).reshape(x.shape)
# Plot the first surface
ax.plot_surface(x, y, z1)
# Show the plot
plt.show()
And as a result the following plot is displayed:
enter image description here
My supervisor wants it to look something like this:
enter image description here
Note that this is a completly different diagram with a different dataset and also different axes.
I wonder if it is even possible to generate such a high resolution of a grid with so few datapoints.
Has is something to do with the way the points are connected in the diagram? In my diagram it looks like a linear interpolation. Is it possible to influence the interpolation?
I would be glad if anyone has an idea and is able to help me.
Thanks, and all the best!
Is there a simple way to animate a scatterplot in matplotlib, in a similar way to which the plot is created?
I know currently I can do this to create the plot:
scatter = ax.scatter([x values], [y values], [z values])
However, every example I find online uses numpy functions to generate its data rather than something external like three lists, leaving me with much difficulty understanding how the data is modified in the method which updates the animation.
Is it possible to give matplotlib an entirely new set of data to plot for each frame? (every point of data will change anyway)
Note: in case there are special considerations for this situation, this is a 3D plot.
The easiest way to animate is to plot in interactive mode, as a minimal(ish) examples with lists,
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
plt.show()
for i in range(1000):
x =[1,2,3,4,5,6,7,8,9,10]
y =[5,6+i%10,2,3,13,4,1,2,4-i%10,8]
z =[2+(i%10)*2.,3,3,3,5,7,9+i%10,11,9+i%10,10]
ax.scatter(x, y, z, marker='o')
ax.set_xlim([0.,10.])
ax.set_ylim([0.,20.])
ax.set_zlim([0.,20.])
plt.pause(0.01)
plt.cla()
A reason to plot using numpy arrays instead of lists is the data is stored as a contiguous block and this speeds up plots (it's easy to convert to an array with xn = np.array(x)). The main reason most examples will use various numpy functions is that it is just easier to provide a self contained demonstration with animation in matplotlib requiring a function which adjusts the collection object. For a great example of a minimum scatter plot animation, see the second example of this.
I'm currently trying to plot with matplotlib a 2d map recorded with an instrument. The instrument is moving 2 motors (it makes a raster) and records the associated intensity value.
I'm currently able to plot the data and to associate the values I want to the axes, but I would like to digitize (make discrete) these values in order to obtain at each pixel of the image the corresponding values for the motors.
I'm currently using the following code (in the example I'll use x and y to define the motor positions):
import pylab as pl
pl.imshow(intensity, extent=(x_min, x_max, y_min, y_max),
interpolation='none')
The code works quite well but if I select one of the pixel on my plot with the cursor, it returns continuous values with many digits (like in figure).
Would it be possible to obtain directly the values of the motors (which I have stored for each point/pixel) by positioning the cursor on them?
Thanks for the help,
Fabio
You can do it by modifying the coordinate formatter like in this example on the matplotlib documentation. A simple adaptation to your request is:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
X = 10*np.random.rand(5, 3)
fig, ax = plt.subplots()
ax.imshow(X, cmap=cm.jet, interpolation='nearest')
def format_coord(x, y):
return 'x=%i, y=%i' % (x+1, y+1)
ax.format_coord = format_coord
plt.show()
, which will result in this:
Also you might want to check out mpldatacursor for something more pretty. For this option take a look at this question here in SO.
I have 3 different parameters X,Y and Z over a range of values, and for each combination of these a certain value of V. To make it clearer, the data would look something like this.
X Y Z V
1 1 2 10
1 2 3 15
etc...
I'd like to visualize the data with a surface/contour plot, using V as a colour to see its value at that point, but I do not see how to add my custom colouring scheme into the mix using Python. Any idea on how to do this (or is this visualization outright silly)?
Thanks a lot!
Matplotlib allows one to pass the facecolors as an argument to e.g.
ax.plot_surface.
That would imply then that you would have to perform 2D interpolation on your
current array of colors, because you currently only have the colors in the
corners of the rectangular faces (you did mention that you have a rectilinear
grid).
You could use
scipy.interpolate.interp2d
for that, but as you see from the documentation, it is suggested to use
scipy.interpolate.RectBivariateSpline.
To give you a simple example:
import numpy as np
y,x = np.mgrid[1:10:10j, 1:10:10j] # returns 2D arrays
# You have 1D arrays that would make a rectangular grid if properly reshaped.
y,x = y.ravel(), x.ravel() # so let's convert to 1D arrays
z = x*(x-y)
colors = np.cos(x**2) - np.sin(y)**2
Now I have a similar dataset as you (one-dimensional arrays for x, y, z and
colors). Remark that the colors are defined for
each point (x,y). But when you want to plot with plot_surface, you'll
generate rectangular patches, of which the corners are given by those points.
So, on to interpolation then:
from scipy.interpolate import RectBivariateSpline
# from scipy.interpolate import interp2d # could 've used this too, but docs suggest the faster RectBivariateSpline
# Define the points at the centers of the faces:
y_coords, x_coords = np.unique(y), np.unique(x)
y_centers, x_centers = [ arr[:-1] + np.diff(arr)/2 for arr in (y_coords, x_coords)]
# Convert back to a 2D grid, required for plot_surface:
Y = y.reshape(y_coords.size, -1)
X = x.reshape(-1, x_coords.size)
Z = z.reshape(X.shape)
C = colors.reshape(X.shape)
#Normalize the colors to fit in the range 0-1, ready for using in the colormap:
C -= C.min()
C /= C.max()
interp_func = RectBivariateSpline(x_coords, y_coords, C.T, kx=1, ky=1) # the kx, ky define the order of interpolation. Keep it simple, use linear interpolation.
In this last step, you could also have used interp2d (with kind='linear'
replacing the kx=1, ky=1). But since the docs suggest to use the faster
RectBivariateSpline...
Now you're ready to plot it:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.cm as cm
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
r = ax.plot_surface(X,Y,Z,
facecolors=cm.hot(interp_func(x_centers, y_centers).T),
rstride=1, cstride=1) # only added because of this very limited dataset
As you can see, the colors on the faces have nothing to do anymore with the height of the dataset.
Note that you could have thought simply passing the 2D array C to facecolors would work, and matplotlib would not have complained. However, the result isn't accurate then, because matplotlib will use only a subset of C for the facecolors (it seems to ignore the last column and last row of C). It is equivalent to using only the color defined by one coordinate (e.g. the top-left) over the entire patch.
An easier method would have been to let matplotlib do the interpolation and obtain the facecolors and then pass those in to the real plot:
r = ax.plot_surface(X,Y,C, cmap='hot') # first plot the 2nd dataset, i.e. the colors
fc = r.get_facecolors()
ax.clear()
ax.plot_surface(X, Y, Z, facecolors=fc)
However, that won't work in releases <= 1.4.1 due to this recently submitted bug.
It really depends on how you plan on plotting this data. I like to plot graphs with gnuplot: it's easy, free and intuitive. To plot your example with gnuplot you'd have to print those line into a file (with only those four columns) and plot using a code like the following
reset
set terminal png
set output "out.png"
splot "file.txt" using 1:2:3:4 with lines palette
Assuming that you save your data into the file file.txt. splot stands for surface plot. Of course, this is a minimum example.
Alternatively you can use matplotlib, but that is not, in my opinion, as intuitive. Although it has the advantage of centering all the processing in python.
Here is the code:
plots=imshow(Z,extent=extent,origin,cmap=cmap,aspect='auto',vmin=vmin,vmax=vmax)
plots.plot(Response,component,vrange)
It plots an image based on data list Z, how can I let it print data points instead of an image?
Looks like needs to change to scatter(x, y,...) to plot data points, how difficult it is to change array Z to x, y?
As #jdj081 said, you want to produce a scatter plot.
import os.path
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
# get an image from the sample data directory
fname = os.path.join(matplotlib.get_data_path(), 'sample_data', 'lena.png')
im = plt.imread(fname)
# Reduce the data by a factor of 4 (so that we can see the points)
im = im[::4, ::4]
# generate coordinates for the image. Note that the image is "top down", so the y coordinate goes from high to low.
ys, xs = np.mgrid[im.shape[0]:0:-1, 0:im.shape[1]]
# Scatter plots take 1d arrays of xs and ys, and the colour takes a 2d array,
# with the second dimension being RGB
plt.scatter(xs.flatten(), ys.flatten(), s=4,
c=im.flatten().reshape(-1, 3), edgecolor='face')
plt.show()
You didn't provide much information to go on, but it sounds like you really want to create a scatter plot.
There are many options here depending on what you are plotting and what you want to see, but I have found the following helpful:
Fixing color in scatter plots in matplotlib
import pylab
pylab.figure(1)
pylab.plot([1,2,3,4],[1,7,3,5]) # draw on figure one
pylab.show() # show figure on screen