Curvature of set of points - python

data file
import matplotlib.pylab as plt
import numpy as np
#initial data
data=np.loadtxt('profile_nonoisebigd02.txt')
x=data[:,0]
y=data[:,1]
#first derivatives
dx= np.gradient(data[:,0])
dy = np.gradient(data[:,1])
#second derivatives
d2x = np.gradient(dx)
d2y = np.gradient(dy)
#calculation of curvature from the typical formula
curvature = np.abs(dx * d2y - d2x * dy) / (dx * dx + dy * dy)**1.5
Can anyone help me out as to where am I going wrong with the curvature ?
The set of points give me a parabola, but curvature is not what I expect.

It seems that your data just isn't smooth enough; I used pandas to replace x, y, dx, dy, d2x, d2y and curvature by rolling means for different values window sizes. As the window size increases, the curvature starts to look more and more like what you'd expect to see for a smooth parabola (legend gives window size):
For the reference, here is the plot of your original data:
The code used to create the smoothed frames:
def get_smooth(smoothing=10, return_df=False):
data=np.loadtxt('profile_nonoisebigd02.txt')
if return_df:
return pd.DataFrame(data)
df = pd.DataFrame(data).sort_values(by=0).reset_index(drop=True).rolling(smoothing).mean().dropna()
# first derivatives
df['dx'] = np.gradient(df[0])
df['dy'] = np.gradient(df[1])
df['dx'] = df.dx.rolling(smoothing, center=True).mean()
df['dy'] = df.dy.rolling(smoothing, center=True).mean()
# second derivatives
df['d2x'] = np.gradient(df.dx)
df['d2y'] = np.gradient(df.dy)
df['d2x'] = df.d2x.rolling(smoothing, center=True).mean()
df['d2y'] = df.d2y.rolling(smoothing, center=True).mean()
# calculation of curvature from the typical formula
df['curvature'] = df.eval('abs(dx * d2y - d2x * dy) / (dx * dx + dy * dy) ** 1.5')
# mask = curvature < 100
df['curvature'] = df.curvature.rolling(smoothing, center=True).mean()
df.dropna(inplace=True)
return df[0], df.curvature

Related

Change in coordinate density for np.meshgrid() in matplotlib

I am plotting a vector field using the numpy function quiver() and it works. But I would like to emphasize the cowlick in the following plot:
I am not sure how to go about it, but increasing the density of arrows in the center could possibly do the trick. To do so, I would like to resort to some option within np.meshgrid() that would allow me to get more tightly packed x,y coordinate points in the center. A linear, quadratic or other specification does not seem to be built in. I am not sure if sparse can be modified to this end.
The code:
lim = 10
int = 0.22 *lim
x,y = np.meshgrid(np.arange(-lim, lim, int), np.arange(-lim, lim, int))
u = 3 * np.cos(np.arctan2(y,x)) - np.sqrt(x**2+y**2) * np.sin(np.arctan2(y,x))
v = 3 * np.sin(np.arctan2(y,x)) + np.sqrt(x**2+y**2) * np.cos(np.arctan2(y,x))
color = x**2 + y**2
plt.rcParams["image.cmap"] = "Greys_r"
mult = 1
plt.figure(figsize=(mult*lim, mult*lim))
plt.quiver(x,y,u,v,color, linewidths=.006, lw=.1)
plt.show()
Closing the loop on this, thanks to the accepted answer I was able to finally strike a balance between the density of the mesh as I learned from to do from #flwr and keeping the "cowlick" structure of the vector field conspicuous (avoiding the radial structure around the origin as much as possible):
You can construct the points whereever you want to calculate your field on and quivers will be happy about it. The code below uses polar coordinates and stretches the radial coordinate non-linearly.
import numpy as np
import matplotlib.pyplot as plt
lim = 10
N = 10
theta = np.linspace(0.1, 2*np.pi, N*2)
stretcher_factor = 2
r = np.linspace(0.3, lim**(1/stretcher_factor), N)**stretcher_factor
R, THETA = np.meshgrid(r, theta)
x = R * np.cos(THETA)
y = R * np.sin(THETA)
# x,y = np.meshgrid(x, y)
r = x**2 + y**2
u = 3 * np.cos(THETA) - np.sqrt(r) * np.sin(THETA)
v = 3 * np.sin(THETA) + np.sqrt(r) * np.cos(THETA)
plt.rcParams["image.cmap"] = "Greys_r"
mult = 1
plt.figure(figsize=(mult*lim, mult*lim))
plt.quiver(x,y,u,v,r, linewidths=.006, lw=.1)
Edit: Bug taking meshgrid twice
np.meshgrid just makes a grid of the vectors you provide.
What you could do is contract this regular grid in the center to have more points in the center (best visible with more points), e.g. like so:
# contract in the center
a = 0.5 # how far to contract
b = 0.8 # how strongly to contract
c = 1 - b*np.exp(-((x/lim)**2 + (y/lim)**2)/a**2)
x, y = c*x, c*y
plt.plot(x,y,'.k')
plt.show()
Alternatively you can x,y cooridnates that are not dependent on a grid at all:
x = np.random.randn(500)
y = np.random.randn(500)
plt.plot(x,y,'.k')
plt.show()
But I think you'd prefer a slightly more regular patterns you could look into poisson disk sampling with adaptive distances or something like that, but the key point here is that for using quiver, you can use ANY set of coordinates, they do not have to be in a regular grid.

Simple DFT Coefficients => Amplitude/Frequencies => Plot

Im trying on DFT and FFT in Python with numpy and pyplot.
My Sample Vector is
x = np.array([1,2,4,3]
The DFT coefficients for that vector are
K = [10+0j, -3+1j, 0+0j, -3-1j]
so basically we have 10, -3+i, 0 and -3-1i as DFT coefficients.
My problem now is to get a combination of sin and cos to fit all 4 points.
Let's assume we have a sample Rate of 1hz.
This is my code :
from matplotlib import pyplot as plt
import numpy as np
x = np.array([1,2,4,3])
fft = np.fft.fft(x)
space = np.linspace(0,4,50)
values = np.array([1,2,3,4])
cos0 = fft[0].real * np.cos(0 * space)
cos1 = fft[1].real * np.cos(1/4 * np.pi * space)
sin1 = fft[1].imag * np.sin(1/4 * np.pi * space)
res = cos0 + cos1 + sin1
plt.scatter(values, x, label="original")
plt.plot(space, cos0, label="cos0")
plt.plot(space, cos1, label="cos1")
plt.plot(space, sin1, label="sin1")
plt.plot(space, res, label="combined")
plt.legend()
As result i get the plot:
(source: heeser-it.de)
Why isnt the final curve hitting any point?
I would appreciate your help. Thanks!
EDIT:
N = 1000
dataPoints = np.linspace(0, np.pi, N)
function = np.sin(dataPoints)
fft = np.fft.fft(function)
F = np.zeros((N,))
for i in range(0, N):
F[i] = (2 * np.pi * i) / N
F_sin = np.zeros((N,N))
F_cos = np.zeros((N,N))
res = 0
for i in range(0, N):
F_sin[i] = fft[i].imag / 500 * np.sin(dataPoints * F[i])
F_cos[i] = fft[i].real / 500* np.cos(dataPoints * F[i])
res = res + F_sin[i] + F_cos[i]
plt.plot(dataPoints, function)
plt.plot(dataPoints, res)
my plot looks like:
(source: heeser-it.de)
where do i fail?
Your testing vector x looks bit like a sawtooth because it rises linearly and then starts to decrease but with that few datapoints it's hard to tell what signal it is. This has an infinite FFT series, which means it has lot of higher harmonic frequency components in it. So to describe it with DTF coefficients and get close to original points, you would have to use
higher sample rate, to get information about higher frequencies (you should learn about nyquist theorem)
more data points (samples), so you can extract more precise information about frequencies in your signal) This means you have to have more items in your array 'x'.
Also you could try to fit some simpler signal. What about you try to fit a sine signal for start? Generate 1000 data points of low frequency sine (1Hz or one cycle per 1000 samples) and then run DTF on it to check if your code works.
There are a few mistakes:
The xs you assigned to the original values are off by one
The frequency you assigned to fft[1] is incorrect
The coefficients are incorrectly scaled
This one works:
from matplotlib import pyplot as plt
import numpy as np
x = np.array([1,2,4,3])
fft = np.fft.fft(x)
space = np.linspace(0,4,50)
values = np.array([0,1,2,3])
cos0 = fft[0].real * np.cos(0 * space)/4
cos1 = fft[1].real * np.cos(1/2 * np.pi * space)/2
sin1 = -fft[1].imag * np.sin(1/2 * np.pi * space)/2
res = cos0 + cos1 + sin1
plt.scatter(values, x, label="original")
plt.plot(space, cos0, label="cos0")
plt.plot(space, cos1, label="cos1")
plt.plot(space, sin1, label="sin1")
plt.plot(space, res, label="combined")
plt.legend()
plt.show()

How to make a fittable Ring Gaussian model in python?

The purpose of my experiment is to fit a ring gaussian model to image data and find out the parameters of elliptical or ring Gaussian object in an image.
I've tried Astropy to make a Ring Gaussian model for simplicity and trial. Unfortunately, it's completely different from my artificial data.
from astropy.modeling import Fittable2DModel, Parameter, fitting
import matplotlib.pyplot as plt
import numpy as np
class ringGaussian(Fittable2DModel):
background = Parameter(default=5.)
amplitude = Parameter(default=1500.)
x0 = Parameter(default=15.)
y0 = Parameter(default=15.)
radius = Parameter(default=2.)
width = Parameter(default=1.)
#staticmethod
def evaluate(x, y, background, amplitude, x0, y0, radius, width):
z = background + amplitude * np.exp( -((np.sqrt((x-x0)**2. + (y-y0)**2.) - radius) / width)**2 )
return z
Then I made some artificial data (initial parameters) to test the fitting function of ring gaussian class.
back = 10 # background
amp = 2000 # highest value of the Gaussian
x0 = 10 # x coordinate of center
y0 = 10 # y coordinate of center
radius = 3
width = 1
xx, yy = np.mgrid[:30, :30]
z = back + amp * np.exp( -((np.sqrt((xx-x0)**2. + (yy-y0)**2.) - radius) / width)**2 )
I plotted xx, yy and z using contourf:
fig = plt.subplots(figsize=(7,7))
plt.contourf(xx,yy,z)
plt.show()
It is what I got:
enter image description here
Then I tried to fit the z using my fittable class:
p_init = ringGaussian() #bounds={"x0":[0., 20.], "y0":[0., 20.]}
fit_p = fitting.LevMarLSQFitter()
p = fit_p(p_init, xx, yy, z)
# It is the parameter I got:
<ringGaussian(background=133.0085329497139, amplitude=-155.53652181827655, x0=25.573499373946227, y0=25.25813520725603, radius=8.184302497405568, width=-7.273935403490675)>
I plotted the model:
fig = plt.subplots(figsize=(7,7))
plt.contourf(xx,yy,p(xx,yy))
plt.show()
It is what I got:
enter image description here
Originally, I also tried to include the derivative in my class:
#staticmethod
def fit_deriv(x, y, background, amplitude, x0, y0, radius, width):
g = (np.sqrt((x-x0)**2. + (y-y0)**2.) - radius) / width
z = amplitude * np.exp( -g**2 )
dg_dx0 = - (x-x0)/np.sqrt((x-x0)**2. + (y-y0)**2.)
dg_dy0 = - (y-y0)/np.sqrt((x-x0)**2. + (y-y0)**2.)
dg_dr0 = - 1/width
dg_dw0 = g * -1/width
dz_dB = 1.
dz_dA = z / amplitude
dz_dx0 = -2 * z * g**3 * dg_dx0
dz_dy0 = -2 * z * g**3 * dg_dy0
dz_dr0 = -2 * z * g**3 * dg_dr0
dz_dw0 = -2 * z * g**3 * dg_dw0
return [dz_dB, dz_dA, dz_dx0, dz_dy0, dz_dr0, dz_dw0]
But it returned "ValueError: setting an array element with a sequence."
I'm quite desperate now. Can anyone suggest some possible solutions? or alternative ways to implement the ring gaussian fit in python?
Many many thanks~~~
Your implementation is OK (I did not try or check your fit_deriv implementation).
The issue is just that your fit doesn't converge because the initial model parameters are too far from the true values, so the optimiser fails. When I run your code, I get this warning:
WARNING: The fit may be unsuccessful; check fit_info['message'] for more information. [astropy.modeling.fitting]
If you change to model parameters so that your model roughly matches the data, the fit succeeds:
p_init = ringGaussian(x0=11, y0=11)
To check how your model compares with the data, you can use imshow to display data and model images (or also e.g. residual images):
plt.imshow(z)
plt.imshow(p_init(xx,yy))
plt.imshow(p(xx,yy))

Streamplot that masks a circular region

I'm using streamplot in order to plot stress trajectories around an open circle. I do not want the stress trajectories to be analyzed inside the radius of the circle for two reasons: (1) The stresses will not propagate through the air as they would through the medium surrounding the hole, and (2) The math doesn't allow for it. I have been messing around with the idea of a mask but I haven't been able to get it to work. There might be a better way. Does anyone know how I can plot these trajectories without them plotting inside the radius of the hole? I effectively need some sort of command to tell the streamplot to stop whenever it gets to the outer radius of the hole, but then also know where to pick back up again. The first bit of code below is just the math used to derive the directions of the stress trajectories. I included this for reference. Following this I plot the trajectories.
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
def stress_trajectory_cartesian(X,Y,chi,F,a):
# r is the radius out from the center of the hole at which we want to know the stress
# Theta is the angle from reference at which we want to know the stress
# a is the radius of the hole
r = np.sqrt(np.power(X,2)+np.power(Y,2))*1.0
c = (1.0*a)/(1.0*r)
theta = np.arctan2(Y,X)
A = 0.5*(1 - c**2. + (1 - 4*c**2. + 3*c**4.)*np.cos(2*theta))
B = 0.5*(1 - c**2. - (1 - 4*c**2. + 3*c**4.)*np.cos(2*theta))
C = 0.5*(1 + c**2. - (1 + 3*c**4.)*np.cos(2*theta))
D = 0.5*(1 + c**2. + (1+ 3*c**4.)*np.cos(2*theta))
E = 0.5*((1 + 2*c**2. - 3*c**4.)*np.sin(2*theta))
tau_r = 1.0*F*c**2. + (A-1.0*chi*B) # Radial stress
tau_theta = -1.*F*c**2. + (C - 1.0*chi*D) # Tangential stress
tau_r_theta = (-1 - 1.0*chi)*E # Shear stress
tau_xx = .5*tau_r*(np.cos(2*theta)+1) -1.0*tau_r_theta*np.sin(2*theta) + .5*(1-np.cos(2*theta))*tau_theta
tau_xy = .5*np.sin(2*theta)*(tau_r - tau_theta) + 1.0*tau_r_theta*np.cos(2*theta)
tau_yy = .5*(1-np.cos(2*theta))*tau_r + 1.0*tau_r_theta*np.sin(2*theta) + .5*(np.cos(2*theta)+1)*tau_theta
tan_2B = (2.*tau_xy)/(1.0*tau_xx - 1.0*tau_yy)
beta1 = .5*np.arctan(tan_2B)
beta2 = .5*np.arctan(tan_2B) + np.pi/2.
return beta1, beta2
# Functions to plot beta as a vector field in the Cartesian plane
def stress_beta1_cartesian(X,Y,chi,F,a):
return stress_trajectory_cartesian(X,Y,chi,F,a)[0]
def stress_beta2_cartesian(X,Y,chi,F,a):
return stress_trajectory_cartesian(X,Y,chi,F,a)[1]
#Used to return the directions of the betas
def to_unit_vector_x(angle):
return np.cos(angle)
def to_unit_vector_y(angle):
return np.sin(angle)
The code below plots the stress trajectories:
# Note that R_min is taken as the radius of the hole here
# Using R_min for a in these functions under the assumption that we don't want to analyze stresses across the hole
def plot_stresses_cartesian(F,chi,R_min):
Y_grid, X_grid = np.mgrid[-5:5:100j, -5:5:100j]
R_grid = np.sqrt(X_grid**2. + Y_grid**2.)
cart_betas1 = stress_beta1_cartesian(X_grid,Y_grid,chi,F,R_min)
beta_X1s = to_unit_vector_x(cart_betas1)
beta_Y1s = to_unit_vector_y(cart_betas1)
beta_X1s[R_grid<1] = np.nan
beta_Y1s[R_grid<1] = np.nan
cart_betas2 = stress_beta2_cartesian(X_grid,Y_grid,chi,F,R_min)
beta_X2s = to_unit_vector_x(cart_betas2)
beta_Y2s = to_unit_vector_y(cart_betas2)
beta_X2s[R_grid<1] = np.nan
beta_Y2s[R_grid<1] = np.nan
fig = plt.figure(figsize=(5,5))
#streamplot
ax=fig.add_subplot(111)
ax.set_title('Stress Trajectories')
plt.streamplot(X_grid, Y_grid, beta_X1s, beta_Y1s, minlength=0.9, arrowstyle='-', density=2.5, color='b')
plt.streamplot(X_grid, Y_grid, beta_X2s, beta_Y2s, minlength=0.9, arrowstyle='-', density=2.5, color='r')
plt.axis("image")
plt.xlabel(r'$\chi = $'+str(round(chi,1)) + ', ' + r'$F = $'+ str(round(F,1)))
plt.ylim(-5,5)
plt.xlim(-5,5)
plt.show()
plot_stresses_cartesian(0,1,1)
I think that you just need to have NaN values for the region that you do not want to consider. I generated a simple example below.
import numpy as np
import matplotlib.pyplot as plt
Y, X = np.mgrid[-5:5:100j, -5:5:100j]
R = np.sqrt(X**2 + Y**2)
U = -1 - X**2 + Y
V = 1 + X - Y**2
U[R<1] = np.nan
V[R<1] = np.nan
plt.streamplot(X, Y, U, V, density=2.5, arrowstyle='-')
plt.axis("image")
plt.savefig("stream.png", dpi=300)
plt.show()
With plot

Buggy behavior in matplotlib pcolormesh with FFT arrays?

I am trying to reproduce the example of the Gabor transform that it is in his wikipedia entry, and I do not know if it is a bug or I am missing something. The example is the calculate the Gabor transform of a sinusoidal signal:
To plot the frequencies sorted, I create an unsorted axis. Then I use mesh grid to create 2D axes and plot with pcolormesh. Here is the piece of the code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridsp
dt = 0.05
x = np.arange(-50.0,50.0,dt)
y = np.sin(2.0 * np.pi * x)
Nx = len(x)
w = np.fft.fftfreq(Nx,dt)
sigma = 1.0 / 3.0
neg = np.where (x <= 0.0)
pos = np.where (x > 0.0)
T,W = np.meshgrid(x,w)
func = np.zeros(Nx)
tmp = np.zeros(Nx,dtype='complex64')
gabor = np.zeros((Nx,Nx))
func[neg] = np.sin(2.0 * np.pi * x[neg])
func[pos] = np.sin(4.0 * np.pi * x[pos])
for it in range(Nx):
tmp[:] = np.fft.fft(func[:] * np.exp( - ( x[it] - x[:] ) * ( x[it] - x[:] ) / 2.0 / sigma / sigma ) )
gabor[:,it] = np.real(np.conj(tmp) * tmp)
fig = plt.figure(figsize=(20,10),facecolor='white')
gs = gridsp.GridSpec(2, 1)
ax1 = plt.subplot(gs[0,0])
ax1.plot(x,func,'r',linewidth=2)
ax1.axis('tight')
ax1.set_xticks(np.arange(min(x),max(x),1.) )
ax1.set_xlabel('time',fontsize=20)
ax1.set_ylabel(r'$\sin{time}$',fontsize=20)
ax1.set_xlim([-6.0,6.0])
ax2 = plt.subplot(gs[1,0])
surf1 = ax2.pcolormesh(T,W,gabor,shading='gouraud')
ax2.axis('tight')
ax2.set_xticks(np.arange(min(x),max(x),2.) )
ax2.set_yticks(np.arange(min(w),max(w),2.) )
ax2.set_xlabel('time',fontsize=20)
ax2.set_ylabel('frequency',fontsize=20)
ax2.set_xlim([-6.0,6.0])
ax2.set_ylim([-4.0,4.0])
gs.tight_layout(fig)
plt.show()
Here is the figure I get,
It seems that the upper part of the plot is reduced to zero. If I try it using fftshift when I create the transform and the axis,
for it in range(Nx):
tmp[:] = np.fft.fftshift(np.fft.fft(func[:] * np.exp( - ( x[it] - x[:] ) * ( x[it] - x[:] ) / 2.0 / sigma / sigma ) ) )
gabor[:,it] = np.real(np.conj(tmp) * tmp)
T,W = np.meshgrid(x,np.fft.fftshift(w))
Then I get this figure:
!
It seems that pcolormesh routine can not flip upside down the array as it is usually done in 1D plots. does anybody know exactly why it is doing this?
Thanks,
Alex
The problem lies in W. Or actually in w. When w is plotted:
Thus pcolormesh receives non-monotonic Y coordinates and gets confused. If you look at the description of pcolor or pcolormesh it is clear they cannot do anything reasonable with non-monotonic data.
So, your gabor is fine:
ax.imshow(gabor)
as you can see:
There are several possibilities how to fix this. One of them is to feed both W and gabor to fftshift that way the frequencies will roll back to monotonic. Or - if you want to have the figure as above (negative frequencies on the top), just add the maximum frequency to all negative values of W.
It might also be cleaner to supply pcolormesh with x and w instead of T and W.
If you want performance, you might be better of with imshow (it can be used when the data is equispaced in both dimensions. The only slight problem is the calculation of extents (which actually may be slightly off even in the question). The extents tell the outer limets of the highest, lowest, leftmost and rightmost pixels. However, the pixel vectors only tell the centers of the pixels.
We need to know the following:
number of points in X direction (num_x)
number of points in Y direction (num_y)
value of the first and last x sample (x0, x1)
value of the first and last y sample (y0, y1)
After that we can use imshow to show the data with correct scaling:
dx = 1. * (x1 - x0) / (num_x-1)
dy = 1. * (y1 - y0) / (num_y-1)
ax.imshow(img, extent=[x0 - dx/2, x1 + dx/2, y0 - dy/2, y1 + dy/2], origin='lower', interpolation='nearest')
So, applied to the question's data:
gabor_shifted = np.fft.fftshift(gabor, axes=0)
w_shifted = np.fft.fftshift(w)
x0 = x[0]
x1 = x[-1]
w0 = w_shifted[0]
w1 = w_shifted[-1]
dx = 1.*(x1-x0) / (len(x) - 1)
dw = 1.*(w1-w0) / (len(w) - 1)
ax2.imshow(gabor_shifted, extent=[x0-dx/2, x1+dx/2, w0-dw/2, w1+dw/2], interpolation='nearest', origin='lower', aspect='auto')
ax2.grid('on', color='w')
ax2.ylim(-4,4)
which gives:

Categories