I'm attempting to draw streamlines for a two-dimensional vector field. I have the data in a two-dimensional array with one column each containing the X coordinate, y-coordinate, horizontal velocity, and vertical velocity. I'm attempting to use the streamline function but I'm having trouble figuring out how to format the input data correctly.
I know that each input matrix should be the same size. So I have attempted to use the following to get workable inputs:
[X Y]= meshgrid(sf(1:250:end,1), sf(1:250:end, 2));
[U V]= meshgrid(sf(1:250:end,3), sf(1:250:end,4));
But my velocity matrices obviously no longer make sense compared to my locations.
I'm at a bit of a loss so any help would be awesome.
You may use griddata to re-arrange your data into a regular grid
f = min( sf(:,1:2), [], 1 ); %// XY grid starting points
t = max( sf(:,1:2), [], 1 ); %// XY endpoints
[X Y] = meshgrid( linspace( f(1), t(1), 50 ), linspace( f(2), t(2), 50 ) ); %//grid
U = griddata( sf(:,1), sf(:,2), sf(:,3), X, Y );
V = griddata( sf(:,1), sf(:,2), sf(:,4), X, Y );
startx = ; %// define streamline starting points
starty = ; %//
streamline( X, Y, U, V, startx, starty );
Related
I am currently trying to write my own 2D Gaussian function as a coding exercise, and have been able to create the following script:
import numpy as np
import matplotlib.pyplot as plt
def Gaussian2D_v1(coords=None, # x and y coordinates for each image.
amplitude=1, # Highest intensity in image.
xo=0, # x-coordinate of peak centre.
yo=0, # y-coordinate of peak centre.
sigma_x=1, # Standard deviation in x.
sigma_y=1, # Standard deviation in y.
rho=0, # Correlation coefficient.
offset=0): # Offset from zero (background radiation).
x, y = coords
xo = float(xo)
yo = float(yo)
# Create covariance matrix
mat_cov = [[sigma_x**2, rho * sigma_x * sigma_y],
[rho * sigma_x * sigma_y, sigma_y**2]]
mat_cov = np.asarray(mat_cov)
# Find its inverse
mat_cov_inv = np.linalg.inv(mat_cov)
G_array = []
# Calculate pixel by pixel
# Iterate through row last
for i in range(0, np.shape(y)[0]):
# Iterate through column first
for j in range(0, np.shape(x)[1]):
mat_coords = np.asarray([[x[i, j]-xo],
[y[i, j]-xo]])
G = (amplitude * np.exp(-0.5*np.matmul(np.matmul(mat_coords.T,
mat_cov_inv),
mat_coords)) + offset)
G_array.append(G)
G_array = np.asarray(G_array)
G_array = G_array.reshape(64, 64)
return G_array.ravel()
coords = np.meshgrid(np.arange(0, 64), np.arange(0, 64))
model_1 = Gaussian2D_v1(coords,
amplitude=20,
xo=32,
yo=32,
sigma_x=6,
sigma_y=3,
rho=0.8,
offset=20).reshape(64, 64)
plt.figure(figsize=(5, 5)).add_axes([0,
0,
1,
1])
plt.contourf(model_1)
The code as it is works, but as you can see, I am currently iterating through the mesh grid one point at a time, and appending each point to a list, which is then converted to an array and re-shaped to give the 2D Gaussian distribution.
How can I modify the script to forgo using a nested "for" loop and have the program consider the whole meshgrid for matrix calculations? Is such a method possible?
Thanks!
Of course there is a solution, numpy is all about array operations and vectorization of the code! np.matmul can take args with more than 2 dimensions and apply the matrix multiplication on the last two axes only (and this calculation in parallel over the others axes). However, making sure of the right axes order can get tricky.
Here is your edited code:
import numpy as np
import matplotlib.pyplot as plt
def Gaussian2D_v1(coords, # x and y coordinates for each image.
amplitude=1, # Highest intensity in image.
xo=0, # x-coordinate of peak centre.
yo=0, # y-coordinate of peak centre.
sigma_x=1, # Standard deviation in x.
sigma_y=1, # Standard deviation in y.
rho=0, # Correlation coefficient.
offset=0): # Offset from zero (background radiation).
x, y = coords
xo = float(xo)
yo = float(yo)
# Create covariance matrix
mat_cov = [[sigma_x**2, rho * sigma_x * sigma_y],
[rho * sigma_x * sigma_y, sigma_y**2]]
mat_cov = np.asarray(mat_cov)
# Find its inverse
mat_cov_inv = np.linalg.inv(mat_cov)
# PB We stack the coordinates along the last axis
mat_coords = np.stack((x - xo, y - yo), axis=-1)
G = amplitude * np.exp(-0.5*np.matmul(np.matmul(mat_coords[:, :, np.newaxis, :],
mat_cov_inv),
mat_coords[..., np.newaxis])) + offset
return G.squeeze()
coords = np.meshgrid(np.arange(0, 64), np.arange(0, 64))
model_1 = Gaussian2D_v1(coords,
amplitude=20,
xo=32,
yo=32,
sigma_x=6,
sigma_y=3,
rho=0.8,
offset=20)
plt.figure(figsize=(5, 5)).add_axes([0, 0, 1, 1])
plt.contourf(model_1)
So, the equation is exp(-0.5 * (X - µ)' Cinv (X - µ) ), where X is our coordinate matrix, µ the mean (x0, y0) and Cinv the inverse covariance matrix (and ' is a transpose). In the code, I stack both meshgrids to a new matrix so that: mat_coords has a shape of (Ny, Nx, 2). In the first np.matmul call, I add a new axis so that the shapes go like :(Ny, Nx, 1, 2) * (2, 2) = (Ny, Nx, 1, 2). As you see, the matrix multiplication is done on the two last axes, in parallel on the other. Then, I add a new axis so that: (Ny, Nx, 1, 2) * (Ny, Nx, 2, 1) = (Ny, Nx, 1, 1).
The np.squeeze() call returns a version without the two last singleton axes.
So I tried to plot a contour in Julia by interpolating a 2D function, using the following code:
using Interpolations
using Plots
gr()
xs = 1:0.5:5
ys = 1:0.5:8
# The function to be plotted
f(x, y) = (3x + y ^ 2)
g = Float64[f(x,y) for x in xs, y in ys]
# Interpolate the function
g_int = interpolate(g, BSpline(Quadratic(Line(OnCell()))))
# Scale the interpolated function to the correct grid
gs_int = scale(g_int, xs, ys)
xc = 1:0.1:5
yc = 1:0.1:5
# Compare the real value and the interpolated value of the function at an arbitrary point
println("gs_int(3.2, 3.2) = ", gs_int(3.2, 3.2))
println("f(3.2, 3.2) = ", f(3.2, 3.2))
# Contour of the interpolated plot
p1 = contour(xs, ys, gs_int(xs, ys), fill=true)
# Real contour of the function
p2 = contour(xc, yc, f, fill=true)
plot(p1, p2)
And this obviously didn't give the correct contour, although the interpolation was seemingly correct:
The problem was fixed by transposing gs_int(xs, ys):
p1 = contour(xs, ys, gs_int(xs, ys)', fill=true)
Then I randomly generated some points in 2D space, and repeated the same procedures:
using DelimitedFiles
using Interpolations
using Plots
gr()
data = readdlm("./random_points.txt", Float64)
# Create a dictionary to test different orders of interpolations.
inter = Dict("constant" => BSpline(Constant()),
"linear" => BSpline(Linear()),
"quadratic" => BSpline(Quadratic(Line(OnCell()))),
"cubic" => BSpline(Cubic(Line(OnCell())))
)
x = range(-10, length=64, stop=10)
y = range(-10, length=64, stop=10)
v_unscaled = interpolate(data, inter["cubic"])
v = scale(v_unscaled, x, y)
# The contour of the data points
p0 = contour(x, y, data, fill=true)
display(p0)
# The contour of the interpolated function
p_int = contour(x, y, v(x,y)', fill=true)
display(p_int)
However the two contour plots don't look the same.
As I removed the apostrophe after v(x,y), this worked:
p_int = contour(x, y, v(x,y), fill=true)
Now I don't get it. When should I apply transposition, and when shouldn't I do so?
That's because in your first example you plot a function, in the second example you plot two arrays. The two arrays don't need to be transposed as they are oriented the same way. But in the first example, the way you generate the array is transposed relative to the way Plots generates an array from the 2-d function you're passing.
When you plot a function, Plots will calculate the outcome as g = Float64[f(x,y) for y in ys, x in xs] not the other way around, like you did in your code. For a good discussion of transposes in plotting, again refer to https://github.com/JuliaPlots/Makie.jl/issues/205
I currently have a 4D matrix of images in the form height x width x RGB x imageNumber in which I would like to index with a 2D array without using a for loop. The 2D array is in the format of height x width with the values being the image number to index.
I've got it working with A for loop but due to speed is there a way to do it without looping? I've tried resizing the matrix and index array but no luck so far.
Here is the for loop I've got working (albeit slowly on large images):
for height = 1:h
for width = 1:w
imageIndex = index(height, width);
imageOutput(height, width, :) = matrix4D(height, width, :, imageIndex);
end
end
where h and w are the height and width dimensions of the images.
Thank you!
This uses implicit expansion to build a linear index that produces the desired result:
matrix4D = rand(4,2,3,5); % example matrix
[h, w, c, n] = size(matrix4D); % sizes
index = randi(n,h,w); % example index
ind = reshape(1:h*w,h,w) + reshape((0:c-1)*h*w,1,1,[]) + (index-1)*h*w*c; % linear index
imageOutput = matrix4D(ind); % desired result
For Matlab versions before R2016b you need to use bsxfun instead of implicit expansion:
ind = bsxfun(#plus, bsxfun(#plus, ...
reshape(1:h*w,h,w), reshape((0:c-1)*h*w,1,1,[])), (index-1)*h*w*c); % linear index
I want to produce a plot with two y-axis and apply multiple datasets to one of the axis. For example
[hAx,hLine1,hLine2] = plotyy([x1',x2',x3'],[y1',y2',y3'],x4,y4);
where x1 and y1 are 1000x1-arrays, x2 and y2 are 2000x1-arrays and x3 and y3 are 3000x1-arrays. The range of the arrays is more or less the same. When i try producing this plot, MATLAB gets me an error saying
Error using horzcat Dimensions of matrices being concatenated are not
consistent.
Is there any workaround for this error?
EDIT:
Here's my real code, which is not working:
[hAx,hLine1,hLine2] = plotyy([erg_cm.Time.data,erg_cm.Time.data,t',t'],...
[erg_cm.Car_FxFL.data,erg_cm.Car_FxFR.data,Fx(1,:),Fx(2,:)],...
erg_cm.Time.data,diff);
And my original data:
erg_cm.Time.data is 1x4001
t is 80300x1
erg_cm.Car_FxFL.data is 1x4001
erg_cm.Car_FxFR.data is 1x4001
Fx is 4x80300
diff is 1x4001
Your x and y vectors are column vectors and you're trying to concatenate them horizontally, which you cannot do because they are not the same size. You want to vertically concatenate them:
[hAx,hLine1,hLine2] = plotyy([x1;x2;x3],[y1;y2;y3],x4,y4);
EDIT: This is what I'm testing with
erg_cm.Time.data = rand(1, 4001);
t = rand(80300, 1);
erg_cm.Car_FxFL.data = rand(1, 4001);
erg_cm.Car_FxFR.data = rand(1, 4001);
Fx = rand(4, 80300);
diff = rand(1, 4001);
[hAx,hLine1,hLine2] = plotyy([erg_cm.Time.data,erg_cm.Time.data,t',t'],...
[erg_cm.Car_FxFL.data,erg_cm.Car_FxFR.data,Fx(1,:),Fx(2,:)],...
erg_cm.Time.data,diff);
I have a 1D matrix x and I want for specific iterations to slide the analyse window, so that every time it moves by 20 samples with 50% overlap. I found bsxfun but i don't know how to adjust it to my problem.
I wrote the below code but I'm not getting the results I expect. I need to get for every iteration the max value of autocorr, for every overlapping window. I get an error for the number of lags.
x = rand(1,100);
N = length(x); % length of signal
n1 = 20; % length of analysing window
win_num = floor((N/n1)*2-1); % number of windows
for i=1:win_num
xmax(i) = max(bsxfun(#autocorr,x(1:n1/2:N),win_num-1));
end
You can modify your loop as follows, to make it work:
x = rand(1,100); %// example data
N = length(x) %// length of signal
n = 20 %// length of analysing window
for ii = n/2:n/2:N-1
xmax(ii*2/n) = max( x( ii-n/2+1 : ii+n/2) );
end
A vectorized version could be:
xmax = max( x( bsxfun(#plus, (1:n).',0:n/2:N-n) ) )
Explanation:
%// create index matrix with moving window
idx = bsxfun(#plus, (1:n).',0:n/2:N-n); %'
%// get values of original vector
xM = x( idx );
%// find maximimum in dimension 1
xmax = max( xM ).'