Plotting a parameter along the depth in 3D in Python - python

I have to visualize the length of cylinder in 3d along with some other parameters. I am able to plot a 3d cylinder in python using matplotlib and also define color for range of points. How I can draw a line through the middle of the cylinder and also plot other parameters like angles at that interval, varying radius etc.
The main idea is to have a well like structure and display some measurements along the depth of the cylinder.
Thanks in advance!
Code used to generate the cylinder:
fig = plt.figure()
ax = fig.add_subplot(111, projection = '3d')
u = np.linspace(0,2*np.pi) # divide circle into parts
#h = np.linspace(0,100,6) # divide height into parts
h = np.arange(3560.064,4213.86,step)
x = np.outer(np.sin(u), np.ones(len(h)))
y = np.outer(np.cos(u), np.ones(len(h)))
z = np.outer(np.ones(len(u)), h)
bounds = [3500,3650,4200,4214]
colors = ["blue", "red","green"]
cmap = matplotlib.colors.ListedColormap(colors)
norm = matplotlib.colors.BoundaryNorm(bounds, len(colors))
ax.plot_surface(x,y,z, cmap = cmap, norm = norm)
plt.show()
Generated Cylinder

Related

how to find average of maximum intensity of an image after plot a 3D surface plot?

I am plotting a 3D surface plot by using an image.
Below is my code:
x = np.arange(768)
y = np.arange(576)
X, Y = np.meshgrid(x, y)
Z = cv2.imread('F:\Multispectral Imaging\Readings\Different images while studying openCV\Threshold
images\Plastics\PET\PET 460 nm\with 460 nm filter.bmp', -1)
fig = plt.figure(figsize=(15,15))
ax1 = fig.add_subplot(111, projection='3d')
mycmap = plt.get_cmap('gist_earth')
ax1.set_title('gist_earth color map')
surf1 = ax1.plot_surface(X, Y, Z, cmap=mycmap)
fig.colorbar(surf1, ax=ax1, shrink=0.5, aspect=5)
plt.show()
After plotting, I see this:
I want to find out the average of the maximum intensity.
I was thinking about finding the coordinate( x and y) of the point where the intensity is maximum ( 255) and then select an area around that point ( eg: 10 * 10 pixels) and find the average of it which can give me the average intensity? Does that make sense? If it does, How can I do that?
Also, my pixels size is 768 * 576. After plotting this 3D surface,
I use a method array = surf1.get_array()
and got something of size (2304,). Why is this size? Why not 768*576?`

How to create a circle with uniformly distributed dots in the perimeter of it with scatterplot in python

Suppose I have a circle x**2 + y**2 = 20.
Now I want to plot the circle with n_dots number of dots in the circles perimeter in a scatter plot. So I created the code like below:
n_dots = 200
x1 = np.random.uniform(-20, 20, n_dots//2)
y1_1 = (400 - x1**2)**.5
y1_2 = -(400 - x1**2)**.5
plt.figure(figsize=(8, 8))
plt.scatter(x1, y1_1, c = 'blue')
plt.scatter(x1, y1_2, c = 'blue')
plt.show()
But this shows the dots not uniformly distributed all the places in the circle. The output is :
So how to create a circle with dots in scatter plot where all the dots are uniformly distributed in the perimeter of the circle?
A simple way to plot evenly-spaced points along the perimeter of a circle begins with dividing the whole circle into equally small angles where the angles from circle's center to all points are obtained. Then, the coordinates (x,y) of each point can be computed. Here is the code that does the task:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=(8, 8))
n_dots = 120 # set number of dots
angs = np.linspace(0, 2*np.pi, n_dots) # angles to the dots
cx, cy = (50, 20) # center of circle
xs, ys = [], [] # for coordinates of points to plot
ra = 20.0 # radius of circle
for ang in angs:
# compute (x,y) for each point
x = cx + ra*np.cos(ang)
y = cy + ra*np.sin(ang)
xs.append(x) # collect x
ys.append(y) # collect y
plt.scatter(xs, ys, c = 'red', s=5) # plot points
plt.show()
The resulting plot:
Alternately, numpy's broadcasting nature can be used and shortened the code:
import matplotlib.pyplot as plt
import numpy as np
fig=plt.figure(figsize=(8, 8))
n_dots = 120 # set number of dots
angs = np.linspace(0, 2*np.pi, n_dots) # angles to the dots
cx, cy = (50, 20) # center of circle
ra = 20.0 # radius of circle
# with numpy's broadcasting feature...
# no need to do loop computation as in above version
xs = cx + ra*np.cos(angs)
ys = cy + ra*np.sin(angs)
plt.scatter(xs, ys, c = 'red', s=5) # plot points
plt.show()
for a very generalized answer that also works in 2D:
import numpy as np
import matplotlib.pyplot as plt
def u_sphere_pts(dim, N):
"""
uniform distribution points on hypersphere
from uniform distribution in n-D (<-1, +1>) hypercube,
clipped by unit 2 norm to get the points inside the insphere,
normalize selected points to lie on surface of unit radius hypersphere
"""
# uniform points in hypercube
u_pts = np.random.uniform(low=-1.0, high=1.0, size=(dim, N))
# n dimensional 2 norm squared
norm2sq = (u_pts**2).sum(axis=0)
# mask of points where 2 norm squared < 1.0
in_mask = np.less(norm2sq, np.ones(N))
# use mask to select points, norms inside unit hypersphere
in_pts = np.compress(in_mask, u_pts, axis=1)
in_norm2 = np.sqrt(np.compress(in_mask, norm2sq)) # only sqrt selected
# return normalized points, equivalently, projected to hypersphere surface
return in_pts/in_norm2
# show some 2D "sphere" points
N = 1000
dim = 2
fig2, ax2 = plt.subplots()
ax2.scatter(*u_sphere_pts(dim, N))
ax2.set_aspect('equal')
plt.show()
# plot histogram of angles
pts = u_sphere_pts(dim, 1000000)
theta = np.arctan2(pts[0,:], pts[1,:])
num_bins = 360
fig1, ax1 = plt.subplots()
n, bins, patches = plt.hist(theta, num_bins, facecolor='blue', alpha=0.5)
plt.show()
similar/related:
https://stackoverflow.com/questions/45580865/python-generate-an-n-dimensional-hypercube-using-rejection-sampling#comment78122144_45580865
Python Uniform distribution of points on 4 dimensional sphere
http://mathworld.wolfram.com/HyperspherePointPicking.html
Sampling uniformly distributed random points inside a spherical volume

Python/Matplotlib - How to plot a vector sum on top of a circular histogram?

I am trying to plot both a circular histogram and a vector (overlapping) on the same polar plot, but cannot get the vector to show up.
Basically, my data set consists of the times at which unitary events occur during a repeating cycle. This data is in the array "all_phases", which is just a list of degree values for each of these events. I want to show (1) the overall distribution of events w/ a circular histogram (bins corresponding to degree ranges) and (2) a vector sum as a measure of the coherence of all of these values (treating each event as a unit vector).
I can plot either one of these things individually on the subplot titled "histo", but when I try to plot both, only the histogram shows up. I have tried playing with the z-indexes of both objects to no use. The code is:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
import math
array = np.array
all_phases = [array([-38.24240218]), array([-120.51570738]), array([-23.70224663]),
array([114.9540152]), array([ 2.94523445]), array([-2.16112692]), array([-18.72274284]),
array([13.2292216]), array([-95.5659992]), array([15.69046269]), array([ 51.12022047]),
array([-89.10567276]), array([ 41.77283949]), array([-9.92584921]), array([-7.59680678]),
array([166.71824996]), array([-178.94642752]), array([-23.75819463]), array([38.69481261]),
array([-52.26651244]), array([-57.40976514]), array([33.68226762]), array([-122.1818295]),
array([ 10.17007425]), array([-38.03726335]),array([44.9504975]), array([ 134.63972923]),
array([ 63.02516932]),array([-106.54049292]), array([-25.6527599])]
number_bins = 60
bin_size = 360/number_bins
cluster_num = 1
counts, theta = np.histogram(all_phases, np.arange(-180, 180 + bin_size, bin_size), density=True)
theta = theta[:-1]+ bin_size/2.
theta = theta * np.pi / 180
a_deg = map(lambda x: np.ndarray.item(x), all_phases)
a_rad = map(lambda x: math.radians(x), a_deg)
a_cos = map(lambda x: math.cos(x), a_rad)
a_sin = map(lambda x: math.sin(x), a_rad)
uv_x = sum(a_cos)/len(a_cos)
uv_y = sum(a_sin)/len(a_sin)
uv_radius = np.sqrt((uv_x*uv_x) + (uv_y*uv_y))
uv_phase = np.angle(complex(uv_x, uv_y))
"""
plot histogram and vector sum
"""
fig = plt.figure()
ax1 = fig.add_axes([0.1, 0.16, 0.05, 0.56])
histo = fig.add_subplot(111, polar=True)
histo.yaxis.set_ticks(())
histo.arrow(0,0,0.11, 1, head_width=.01, zorder=2)
plt.suptitle("Phase distribution for Neuron #" + str(cluster_num), fontsize=15, y=.94)
plt.subplots_adjust(bottom=0.12, right=0.95, top=0.78, wspace=0.4)
width = (2*np.pi) / number_bins
bars = histo.bar(theta, counts, width = width, bottom=0.002)
for r, bar in zip(counts, bars):
bar.set_facecolor(plt.cm.jet(r / max(counts)))
bar.set_alpha(0.7)
bar.set_zorder(1)
norm = matplotlib.colors.Normalize(vmin (counts.min())*len(all_phases)*bin_size, vmax=(counts.max())*len(all_phases)*bin_size)
cb1 = matplotlib.colorbar.ColorbarBase(ax1, cmap=plt.cm.jet,
orientation='vertical', norm=norm, alpha=0.4,
ticks=np.arange(0, (counts.max())*len(all_phases)*bin_size)+1, )
cb1.ax.tick_params(labelsize=9)
cb1.solids.set_rasterized(True)
cb1.set_label("# spikes")
cb1.ax.yaxis.set_label_position('left')
plt.show()
cluster_num = cluster_num + 1
vs_radius and vs_phase are the parameters for the vector sum arrow I want to put on the polar plot, which I end up calling w/ histo.arrow().
My suspicion is that it might have something to do with trying to put two things on a subplot object?
Any help or thoughts would be very much appreciated!!
The problem is that the FancyArrow that is used by Axes.arrow() does not play well with polar plots.
Instead, you could use the annotate() function to draw a simple arrow that works better in the case of polar plots.
for example:
# Compute pie slices
N = 20
theta = np.linspace(0.0, 2 * np.pi, N, endpoint=False)
radii = 10 * np.random.rand(N)
width = np.pi / 4 * np.random.rand(N)
ax = plt.subplot(111, projection='polar')
bars = ax.bar(theta, radii, width=width, bottom=0.0)
# Use custom colors and opacity
for r, bar in zip(radii, bars):
bar.set_facecolor(plt.cm.viridis(r / 10.))
bar.set_alpha(0.5)
v_angle = 0.275*np.pi
v_length = 4
ax.annotate('',xy=(v_angle, v_length), xytext=(v_angle,0), xycoords='data', arrowprops=dict(width=5, color='red'))
plt.show()
As a general rule, when you deal with polar plot, you have to work just as if you were working with a linear plot. That is to say, you shouldn't try to draw your arrow from (0,0) but rather from (uv_phase, 0)
fig, ax = plt.subplots()
bars = ax.bar(theta, radii, width=width, bottom=0.0)
# Use custom colors and opacity
for r, bar in zip(radii, bars):
bar.set_facecolor(plt.cm.viridis(r / 10.))
bar.set_alpha(0.5)
ax.annotate('',xy=(v_angle, v_length), xytext=(v_angle,0), xycoords='data', arrowprops=dict(width=5, color='red'))

contour deformation in python

I have a contour map and I want to make a deformation of all the contour lines, where the contour of level 0.5 will be deformed around the blue point situated in his line and then pass on the blue point on the contour of level 1, and so on.
Original map :
Deformed map :
I think there are two steps, the first one is the delete some parts of the map and the second is to redraw the contour map.
I think i have to iterate through the contour map like this:
CS = plt.contour(X, Y, Z)
for level in CS.collections:
for kp, path in list(enumerate(level.get_paths())):
But I have no idea how to use kp and path
Any tips for doing this would be appreciated!
Here is an example on how you could change the contour plot to achieve the intended deformation.
It generates some data x,y,z which should later be modified. Then it specifies a deformation function, which when multiplied to z deforms the data in the desired way. This deformation function takes the x and y data as input as well as the angle of the line along which to perform the deformation and a width (spread) of the deformation. Finally a parameter i is used for stearing the degree of deformation (i.e. i=0 means no deformation). Of course you can use any other function to deform your contour.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation
#### generate some x,y,z data ####
r = np.linspace(0,6, num=100)
phi = np.linspace(0, 2*np.pi, num=200)
R, Phi = np.meshgrid(r,phi)
x = R*np.cos(Phi)
y = R*np.sin(Phi)
z = R
##################################
fig, ax=plt.subplots()
ax.set_aspect("equal")
def update(i):
ax.clear()
f = lambda x,y, offs, width, i: 1-i*np.exp(-(np.arctan2(x,y)-offs)**2/width)
z_deformed = z*f(x,y, np.pi/4, 1./8., i=i)
ax.contour(x,y,z_deformed, 10, linewidths=4)
ax.contourf(x,y,z_deformed, 10, alpha=0.3)
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
update(0) #plot the original data
anipath = 0.5*np.sin(np.linspace(0, np.pi, num=20))**2
ani = matplotlib.animation.FuncAnimation(fig, update, frames=anipath, interval = 100)
plt.show()
Of course you can use other shapes of deformation. E.g. to get a triangular shape use
f = lambda x, A, a, b: A*(1.-np.abs((x-b)/a))*(np.abs((x-b)) < a )
z_deformed = z - f(np.arctan2(x,y), i, 1./8., np.pi/4 )

Matplotlib - contour and quiver plot in projected polar coordinates

I need to plot contour and quiver plots of scalar and vector fields defined on an uneven grid in (r,theta) coordinates.
As a minimal example of the problem I have, consider the contour plot of a Stream function for a magnetic dipole, contours of such a function are streamlines of the corresponeding vector field (in this case, the magnetic field).
The code below takes an uneven grid in (r,theta) coordinates, maps it to the cartesian plane and plots a contour plot of the stream function.
import numpy as np
import matplotlib.pyplot as plt
r = np.logspace(0,1,200)
theta = np.linspace(0,np.pi/2,100)
N_r = len(r)
N_theta = len(theta)
# Polar to cartesian coordinates
theta_matrix, r_matrix = np.meshgrid(theta, r)
x = r_matrix * np.cos(theta_matrix)
y = r_matrix * np.sin(theta_matrix)
m = 5
psi = np.zeros((N_r, N_theta))
# Stream function for a magnetic dipole
psi = m * np.sin(theta_matrix)**2 / r_matrix
contour_levels = m * np.sin(np.linspace(0, np.pi/2,40))**2.
fig, ax = plt.subplots()
# ax.plot(x,y,'b.') # plot grid points
ax.set_aspect('equal')
ax.contour(x, y, psi, 100, colors='black',levels=contour_levels)
plt.show()
For some reason though, the plot I get doesn't look right:
If I interchange x and y in the contour function call, I get the desired result:
Same thing happens when I try to make a quiver plot of a vector field defined on the same grid and mapped to the x-y plane, except that interchanging x and y in the function call no longer works.
It seems like I made a stupid mistake somewhere but I can't figure out what it is.
If psi = m * np.sin(theta_matrix)**2 / r_matrix
then psi increases as theta goes from 0 to pi/2 and psi decreases as r increases.
So a contour line for psi should increase in r as theta increases. That results
in a curve that goes counterclockwise as it radiates out from the center. This is
consistent with the first plot you posted, and the result returned by the first version of your code with
ax.contour(x, y, psi, 100, colors='black',levels=contour_levels)
An alternative way to confirm the plausibility of the result is to look at a surface plot of psi:
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as axes3d
r = np.logspace(0,1,200)
theta = np.linspace(0,np.pi/2,100)
N_r = len(r)
N_theta = len(theta)
# Polar to cartesian coordinates
theta_matrix, r_matrix = np.meshgrid(theta, r)
x = r_matrix * np.cos(theta_matrix)
y = r_matrix * np.sin(theta_matrix)
m = 5
# Stream function for a magnetic dipole
psi = m * np.sin(theta_matrix)**2 / r_matrix
contour_levels = m * np.sin(np.linspace(0, np.pi/2,40))**2.
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')
ax.set_aspect('equal')
ax.plot_surface(x, y, psi, rstride=8, cstride=8, alpha=0.3)
ax.contour(x, y, psi, colors='black',levels=contour_levels)
plt.show()

Categories

Resources