using matplotlib streamplot - python

I have problem using matplotlib streamplot. I want to use a 3d vector field in coordinates (x,y,z) stored in a numpy array, and plot slices of it with streamplot.
To test it I wanted to use a vector field with arrows pointed up in the z>0 region and pointed down in the z<0 region.
So I tried this:
import numpy as np
import matplotlib.pyplot as plt
from math import *
max = 100
min = -100
X = np.linspace(min, max, num=100)
Y = np.linspace(min, max, num=100)
Z = np.linspace(min, max, num=100)
N = X.size
#single components in the 3D matrix
Bxa = np.zeros((N, N, N))
Bya = np.zeros((N, N, N))
Bza = np.zeros((N, N, N))
for i, x in enumerate(X):
for j, y in enumerate(Y):
for k, z in enumerate(Z):
Bxa[ i, j, k] = 0.0 #x
Bya[ i, j, k] = 0.0 #y
Bza[ i, j, k] = z
#I take a slice close to Y=0
Bx_sec = Bxa[:,N/2,:]
By_sec = Bya[:,N/2,:]
Bz_sec = Bza[:,N/2,:]
fig = plt.figure()
ax = fig.add_subplot(111)
ax.streamplot(X, Z, Bx_sec, Bz_sec, color='b')
ax.set_xlim([X.min(), X.max()])
ax.set_ylim([Z.min(), Z.max()])
plt.show()
But I obtain something that looks like if I have put Bza = x! I tried to invert the order of vectors but it is unuseful!
Does anyone of you understand the problem? Thanks
Gabriele

one friend told me
The documentation for streamplot:
x, y : 1d arrays
an *evenly spaced* grid.
u, v : 2d arrays
x and y-velocities. Number of rows should match length of y, and
the number of columns should match x.
Note that the rows in u and v should match y, and the columns should match x. I think your u and v are transposed.
so I used numpy.transpose( ) and everything worked!

Related

How to plot a function with a vector and matrix in python?

But function f is a problem because I don't know how to combine the mesh with the matrix, is there a smart way to solve this problem?
It looks like your code for g is very close to the one for f. You could just define your M matrix and include it in the matrix multiplication. See code below for more details:
import numpy as np
import matplotlib.pyplot as plt
def f_function(diagonal_values):
fig = plt.figure(figsize=(15,8))
data = np.linspace(-4, 4, 20)
x_1, x_2 = np.meshgrid(data, data, indexing="ij")
fx = np.zeros_like(x_1)
#Defining M
M=np.diag(diagonal_values)
print(M)
for i in range(data.shape[0]):
for j in range(data.shape[0]):
x = np.array([x_1[i,j], x_2[i,j]])
f = x.T # M # x
fx[i,j] = f
ax = fig.add_subplot(121, projection="3d")
surf = ax.plot_surface(x_1, x_2, fx)
ax.set_xlabel("x_1")
ax.set_ylabel("x_2")
ax.set_zlabel("f")
#Randomly picking diagonal values
diag_values=np.random.uniform(0,10,2)
print('Diagonal values: '+str(diag_values))
f_function(np.array(diag_values))
The output gives:
Diagonal values: [8.62030848 2.68367524]
And the plot:

How to scatter randomly points on a sphere

using PyPlot
n = 50
u = range(0,stop=2*π,length=n);
v = range(0,stop=π,length=n);
x = cos.(u) * sin.(v)';
y = sin.(u) * sin.(v)';
z = ones(n) * cos.(v)';
scatter3D(vec(x),vec(y),vec(z);c="red",s=1)
However, if I multiply vec(x), vec(y), vec(z) with rand() ,
I still get the same plot with the only difference being that the axis change or in other words that the sphere gets "squished".
using PyPlot
n = 50
u = range(0,stop=2*π,length=n);
v = range(0,stop=π,length=n);
x = cos.(u) * sin.(v)';
y = sin.(u) * sin.(v)';
z = ones(n) * cos.(v)';
scatter3D(rand()*vec(x),rand()*vec(y),rand()*vec(z);c="red",s=1)
The simplest approach seems to be sampling a Gaussian for each dimension and then normalizing the length of the resulting vector as described in this answer. There's a very slight possibility of getting a vector with zero length, which can be handled with rejection sampling. Putting that together you would do this:
points = map(1:n) do _
while true
x = randn()
y = randn()
z = randn()
l = hypot(x, y, z)
l ≠ 0 && return (x, y, z) ./ l
end
end
This gives a vector of 3-tuples each representing x, y and z coordinates of points, which you can plot as before. Separate vectors of coordinates can be extracted using comprehensions:
xs = [p[1] for p in points]
ys = [p[2] for p in points]
zs = [p[3] for p in points]
This approach can readily be generalized to any number of dimensions.

Using meshgrid to convert X,Y,Z triplet to three 2D arrays for surface plot in matplotlib

I'm new to Python so please be patient. I appreciate any help!
What I have: three 1D lists (xr, yr, zr), one containing x-values, the other two y- and z-values
What I want to do: create a 3D contour plot in matplotlib
I realized that I need to convert the three 1D lists into three 2D lists, by using the meshgrid function.
Here's what I have so far:
xr = np.asarray(xr)
yr = np.asarray(yr)
zr = np.asarray(zr)
X, Y = np.meshgrid(xr,yr)
znew = np.array([zr for x,y in zip(np.ravel(X), np.ravel(Y))])
Z = znew.reshape(X.shape)
Running this gives me the following error (for the last line I entered above):
total size of new array must be unchanged
I went digging around stackoverflow, and tried using suggestions from people having similar problems. Here are the errors I get from each of those suggestions:
Changing the last line to:
Z = znew.reshape(X.shape[0])
Gives the same error.
Changing the last line to:
Z = znew.reshape(X.shape[0], len(znew))
Gives the error:
Shape of x does not match that of z: found (294, 294) instead of (294, 86436).
Changing it to:
Z = znew.reshape(X.shape, len(znew))
Gives the error:
an integer is required
Any ideas?
Well,sample code below works for me
import numpy as np
import matplotlib.pyplot as plt
xr = np.linspace(-20, 20, 100)
yr = np.linspace(-25, 25, 110)
X, Y = np.meshgrid(xr, yr)
#Z = 4*X**2 + Y**2
zr = []
for i in range(0, 110):
y = -25.0 + (50./110.)*float(i)
for k in range(0, 100):
x = -20.0 + (40./100.)*float(k)
v = 4.0*x*x + y*y
zr.append(v)
Z = np.reshape(zr, X.shape)
print(X.shape)
print(Y.shape)
print(Z.shape)
plt.contour(X, Y, Z)
plt.show()
TL;DR
import matplotlib.pyplot as plt
import numpy as np
def get_data_for_mpl(X, Y, Z):
result_x = np.unique(X)
result_y = np.unique(Y)
result_z = np.zeros((len(result_x), len(result_y)))
# result_z[:] = np.nan
for x, y, z in zip(X, Y, Z):
i = np.searchsorted(result_x, x)
j = np.searchsorted(result_y, y)
result_z[i, j] = z
return result_x, result_y, result_z
xr, yr, zr = np.genfromtxt('data.txt', unpack=True)
plt.contourf(*get_data_for_mpl(xr, yr, zr), 100)
plt.show()
Detailed answer
At the beginning, you need to find out for which values of x and y the graph is being plotted. This can be done using the numpy.unique function:
result_x = numpy.unique(X)
result_y = numpy.unique(Y)
Next, you need to create a numpy.ndarray with function values for each point (x, y) from zip(X, Y):
result_z = numpy.zeros((len(result_x), len(result_y)))
for x, y, z in zip(X, Y, Z):
i = search(result_x, x)
j = search(result_y, y)
result_z[i, j] = z
If the array is sorted, then the search in it can be performed not in linear time, but in logarithmic time, so it is enough to use the numpy.searchsorted function to search. but to use it, the arrays result_x and result_y must be sorted. Fortunately, sorting is part of the numpy.unique method and there are no additional actions to do. It is enough to replace the search (this method is not implemented anywhere and is given simply as an intermediate step) method with np.searchsorted.
Finally, to get the desired image, it is enough to call the matplotlib.pyplot.contour or matplotlib.pyplot.contourf method.
If the function value does not exist for (x, y) for all x from result_x and all y from result_y, and you just want to not draw anything, then it is enough to replace the missing values with NaN. Or, more simply, create result_z as numpy.ndarray` from NaN and then fill it in:
result_z = numpy.zeros((len(result_x), len(result_y)))
result_z[:] = numpy.nan

Matplotlib like matlab's trisurf

To make a long story short, I'd like to plot a generic 3D triangle mesh in python. Matplotlib seems to be the ideal candidate, but I'd go with any 3D rendering that can do what I'm about to describe.
Suppose I have a triangle mesh defined by X, Y, and Z, the 3D coordinates of a point cloud, each a vector of length n, and UVW, a 2D m-x-3 matrix in which each row is a triplet of indices into the point cloud. This triplet represents an individual triangle. In other words, I have m triangles over n points. In Matlab, to generated a 3D plot, I just do:
trisurf(UVW, X, Y, Z)
Does anyone have any experience with this? In particular, can mplots trisurf be shoehorned to work?
Depending on your performance needs, mayavi is likely to be best suited for this - as per Davis comment.
However, matplotlib comes with plot_trisurf to which you can perfectly pass generic UVW, X, Y , Z as you describe.
Exemple with a torus mesh:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.tri as mtri
R = 1.
r = 0.8
n = 50
m = 50
def torus_triangles(n, m):
""" Returns triangles to mesh a (n, m) torus """
tri = []
for i in range(n):
for j in range(m):
a = i + j*(n)
b = ((i+1) % n) + j*n
d = i + ((j+1) % m) * n
c = ((i+1) % n) + ((j+1) % m) * n
tri += [[a, b, d], [b, c, d]]
return np.array(tri, dtype=np.int32)
theta0 = np.linspace(0, (2*np.pi), n, endpoint=False)
phi0 = np.linspace(0, (2*np.pi), m, endpoint=False)
theta, phi = np.meshgrid(theta0, phi0)
x = (R + r * np.sin(phi)) * np.cos(theta)
y = (R + r * np.sin(phi)) * np.sin(theta)
z = r * np.cos(phi)
triangles = torus_triangles(n , m)
triang = mtri.Triangulation(x.ravel(), y.ravel(), triangles)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_trisurf(triang, z.ravel(), lw=0.2, edgecolor="black", color="grey",
alpha=0.5)
plt.show()
I was also looking for a solution to this problem and this discussion helped me to succeed. Here is how it works:
A solution to very similar problem was already given as a link here in the comment from GBy (see above: Colouring the surface of a sphere with a set of scalar values in matplotlib)
Transfering the knowledge to the problem here it results in creating an additional array containing the amplitudes and assigning it to the "underlying ScalarMappable through set_array method". The corresponding python code looks like this:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib import pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
colors = np.mean(CorticalImage[Face], axis=1)
collec = ax.plot_trisurf(Xcoordinates, Ycoordinates, Zcoordinates, triangles=Face, cmap=cm.jet, linewidth=0.2)
collec.set_array(colors)
collec.autoscale()
ax.view_init(30, 0)
cbar = fig.colorbar(collec)
The arrays Xcoordinates, Ycoordinates, Zcoordinates contain the X, Y and Z coordinates of the mesh nodes. When checking their shape with e.g. Xcoordinates.shape it should look like this (750,), where 750 is the number of mesh nodes. The matrix Face is the same as the matrix UVW in the original question asked by Larry. It is "a 2D m-x-3 matrix in which each row is a triplet of indices into the point cloud". If you check the shape of the matrix Face it should be something like (1496, 3), where 1496 is the number of triangles in the mesh and 3 is the number of nodes in one triangle. Finally, the array CorticalImage contains the amplitudes for every node in the mesh and these are the values, which we want to use for the colors of the mesh (and not the Z values). The shape of that array should be like the shapes of the coordinates arrays, i.e. (750,).
IMPORTANT!!! You can see that the number of nodes and the number of triangles are not equal. This is almost always the case. Additionally, the amplitudes are usually given for the nodes and not for the triangles. Consequently, the amplitudes should be calculated for the triangles in order to get the right colors in the plot. This is done in the line colors = np.mean(CorticalImage[Face], axis=1).
Plotly has an open-source trisurf Python implementation that is closer to MATLAB's trisurf().
Python code and examples here:
https://plot.ly/python/tri-surf/
Thought for completeness sake I would add a mayavi example here, using the mesh from GBy's answer.
import numpy as np
from mayavi import mlab
from tvtk.api import tvtk
R = 1.
r = 0.8
n = 50
m = 50
def torus_triangles(n, m):
""" Returns triangles to mesh a (n, m) torus """
tri = []
for i in range(n):
for j in range(m):
a = i + j*(n)
b = ((i+1) % n) + j*n
d = i + ((j+1) % m) * n
c = ((i+1) % n) + ((j+1) % m) * n
tri += [[a, b, d], [b, c, d]]
return np.array(tri, dtype=np.int32)
theta0 = np.linspace(0, (2*np.pi), n, endpoint=False)
phi0 = np.linspace(0, (2*np.pi), m, endpoint=False)
theta, phi = np.meshgrid(theta0, phi0)
x = (R + r * np.sin(phi)) * np.cos(theta)
y = (R + r * np.sin(phi)) * np.sin(theta)
z = r * np.cos(phi)
triangles = torus_triangles(n , m)
mesh = mlab.triangular_mesh(x,y,z, triangles, representation='wireframe',color=(0,0,1) )
mlab.show()
Yielding:

Using numpy/scipy to calculate iso-surface from 3D array

I have a 3D numpy array that contains the values of a given function. I want to calculate a 2D iso-surface, or a set of iso-surfaces that represent certain values of this function.
In this particular case, each 1D column (column = myarray[i, j, :]) of the 3D array can be treated independently. So what I would like to know are the last index positions (2D array) where the function is equal to a certain value, say myvalue.
Some (slow) code to exemplify:
# myarray = 3D ndarray
import numpy as np
from scipy import interpolate
result = np.zeros(nx, ny)
z_values = np.arange(nz)
for i in range(nx):
for j in range(ny):
f = interpolate.interp1d(my_array[i, j], z_values)
result[i, j] = f(myvalue)
I know this can be sped up a bit with np.ndenumerate and other tricks, but was wondering if there is already a simpler way of doing this kind of iso-surface. I couldn't find anything in ndimage or other libraries. I know that mayavi2 and vtk have a lot of tools to deal with iso-surfaces, but my aim here is not visualisation -- I want to perform calculations on those iso-surface values, not display them. Plus, a lot of the iso-surface methods of vtk seem to involve polygons and the like, and what I need is just a 2D array of positions for each iso-surface value.
Using only numpy you can get a good solution using argsort, sort, take and the proper array manipulation. The function below uses a weighted average to compute the iso-surface:
def calc_iso_surface(my_array, my_value, zs, interp_order=6, power_parameter=0.5):
if interp_order < 1: interp_order = 1
from numpy import argsort, take, clip, zeros
dist = (my_array - my_value)**2
arg = argsort(dist,axis=2)
dist.sort(axis=2)
w_total = 0.
z = zeros(my_array.shape[:2], dtype=float)
for i in xrange(int(interp_order)):
zi = take(zs, arg[:,:,i])
valuei = dist[:,:,i]
wi = 1/valuei
clip(wi, 0, 1.e6, out=wi) # avoiding overflows
w_total += wi**power_parameter
z += zi*wi**power_parameter
z /= w_total
return z
This solution does not handle situations where there is more than one z corresponding to my_value. An application example to build the iso-surfaces below is given in the following code:
from numpy import meshgrid, sin, cos, pi, linspace
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
dx = 100; dy = 50; dz = 25
nx = 200; ny = 100; nz = 100
xs = linspace(0,dx,nx)
ys = linspace(0,dy,ny)
zs = linspace(0,dz,nz)
X,Y,Z = meshgrid( xs, ys, zs, dtype=float)
my_array = sin(0.3*pi+0.4*pi*X/dx)*sin(0.3*pi+0.4*pi*Y/dy)*(Z/dz)
fig = plt.figure()
ax = fig.gca(projection='3d')
z = calc_iso_surface( my_array, my_value=0.1, zs=zs, interp_order=6 )
ax.plot_surface(X[:,:,0], Y[:,:,0], z, cstride=4, rstride=4, color='g')
z = calc_iso_surface( my_array, my_value=0.2, zs=zs, interp_order=6 )
ax.plot_surface(X[:,:,0], Y[:,:,0], z, cstride=4, rstride=4, color='y')
z = calc_iso_surface( my_array, my_value=0.3, zs=zs, interp_order=6 )
ax.plot_surface(X[:,:,0], Y[:,:,0], z, cstride=4, rstride=4, color='b')
plt.ion()
plt.show()
You can also play with different interpolation functions. See below one example that takes the average of the two closest zs:
def calc_iso_surface_2(my_array, my_value, zs):
'''Takes the average of the two closest zs
'''
from numpy import argsort, take
dist = (my_array - my_value)**2
arg = argsort(dist,axis=2)
z0 = take(zs, arg[:,:,0])
z1 = take(zs, arg[:,:,1])
z = (z0+z1)/2
return z

Categories

Resources