Subtract vector from 3d array - matlab

Say if I have a 3d array:
lat = 45:49;
lon = -116:-110;
b = rand(5,7,12);
where the first dimension represents the latitude, the second dimension represents the longitude and the third row represents the data. I aim to plot this 3d data on a map using the mapping toolbox. But, before doing this, I would like to find the difference between the data in 'b' and the following vector:
vals = [2.3,5,6.8,5.4,3.3,12,1.5,4.6,9.8,82,3.3,1];
Specifically, for each spatial data point that I have in my mapped data I owuld like to calculate the mean absolute error between that vector and the data at each point in b. If these were two normal vectors I would use:
mae = mean(abs(bv - vals))
but I'm not sure how this can be done with the 3d array. Eventually, I aim to map this mean absolute error to see how it varies spatially. Can anyone suggest how this can be done in matlab?

Use bsxfun for this (it's more efficient than repmat):
V = permute(vals, [1,3,2]) %// Make sure that the dimesions 'align' correctly. i.e. 12 elements must go in the thrid dimension to match b
mae = mean(abs(bsxfun(#minus, b, V)),3)

As MATLAB does not support broadcasting, you need to create a matrix the same size as b with the repeated values of vals. To be able to do that, first you need to change vals to a shape of 1x1x12 and then repeat it 5x7 times. You can do that with
values=repmat(permute(vals,[1 3 2]),[5 7 1]);
now you can
mae = mean(abs(bv - values))

Related

How to loop through each value in a 3D matrix?

I have a matrix, 10x10x40, that is storing information of an image through time, where the the rows and columns indicate the spectral value at a specific point, and the third dimension is time. So in other words, a 10x10 image at 40 points in time. I would like to loop through each row, column and view that pixel history (1,1,:), (1,2,:)....(10,10,:).
Here's what I'm doing now:
val = [];
for i = 1:10;
for j = 1:10;
for k = 1:length(timevector)
val(k) = my_matrix(i,j,k);
end
end
end
Since I want to iterate through each pixel in time and then save that data, what would be the best way to store the new value/time vectors? I want to end up with 100 pixel history vectors, right now I end with one, and it's because val is written over within the loop. I know it's not advised to create variables within a loop, so what is the best alternative? Should I look into storing the output as a structure? I've been staring at this and I have over-complicated everything.
Depending on the structure you prefer, you can also use matlab's functions reshape and num2cell to get the output in the following form:
Alternative 1:
A = reshape(A,[],10);
This will return a matrix (100x40) where each row is a pixel's history.
Alternative 2:
A = num2cell( reshape(A,[],40), 2)
This will return a cell array (100x1) where each cell contains a vector (40x1) with each pixel's history.
Alternative 3:
A = squeeze( num2cell( permute(A, [3,1,2]), 1) );
This will return a cell array (10x10) where each cell contains a vector (40x1) with each pixel's history.
Depending on what you want to do with it, you don't need to store them in separate vectors. You can just get one of these pixel history vectors, like so,
pixel_history = squeeze(my_matrix(1,1,:));
squeeze will remove the singleton dimension from the slice, and make it into a 40-by-1 vector, instead of a 1-by-1-by-40 matrix.
To make the time dimension the first matrix dimension, you could also permute the matrix,
permute(my_matrix, [3 2 1]);
This will swap the 3rd and 1st dimensions, making the 1st dimension time.

How do I visualize n-dimensional features?

I have two matrices A and B. The size of A is 200*1000 double (here: 1000 represents 1000 different features). Matrix A belongs to group 1, where I use ones(200,1) as the label vector. The size of B is also 200*1000 double (here: 1000 also represents 1000 different features). Matrix B belongs to group 2, where I use -1*ones(200,1) as the label vector.
My question is how do I visualize matrices A and B so that I can clearly distinguish them based on the given groups?
I'm assuming each sample in your matrices A and B is determined by a row in either matrix. If I understand you correctly, you want to draw a series of 1000-dimensional vectors, which is impossible. We can't physically visualize anything beyond three dimensions.
As such, what I suggest you do is perform a dimensionality reduction to reduce your data so that each input is reduced to either 2 or 3 dimensions. Once you reduce your data, you can plot them normally and assign a different marker to each point, depending on what group they belonged to.
If you want to achieve this in MATLAB, use Principal Components Analysis, specifically the pca function in MATLAB, that calculates the residuals and the reprojected samples if you were to reproject them onto a lower dimensionality. I'm assuming you have the Statistics Toolbox... if you don't, then sorry this won't work.
Specifically, given your matrices A and B, you would do this:
[coeffA, scoreA] = pca(A);
[coeffB, scoreB] = pca(B);
numDimensions = 2;
scoreAred = scoreA(:,1:numDimensions);
scoreBred = scoreB(:,1:numDimensions);
The second output of pca gives you reprojected values and so you simply have to determine how many dimensions you want by extracting the first N columns, where N is the desired number of dimensions you want.
I chose 2 for now, and we can see what it looks like in 3 dimensions after. Once we have what we need for 2 dimensions, it's just a matter of plotting:
plot(scoreAred(:,1), scoreAred(:,2), 'rx', scoreBred(:,1), scoreBred(:,2), 'bo');
This will produce a plot where the samples from matrix A are with red crosses while the samples from matrix B are with blue circles.
Here's a sample run given completely random data:
rng(123); %// Set seed for reproducibility
A = rand(200,1000); B = rand(200,1000); %// Generate random data
%// Code as before
[coeffA, scoreA] = pca(A);
[coeffB, scoreB] = pca(B);
numDimensions = 2;
scoreAred = scoreA(:,1:numDimensions);
scoreBred = scoreB(:,1:numDimensions);
%// Plot the data
plot(scoreAred(:,1), scoreAred(:,2), 'rx', scoreBred(:,1), scoreBred(:,2), 'bo');
We get this:
If you want three dimensions, simply change numDimensions = 3, then change the plot code to use plot3:
plot3(scoreAred(:,1), scoreAred(:,2), scoreAred(:,3), 'rx', scoreBred(:,1), scoreBred(:,2), scoreBred(:,3), 'bo');
grid;
With those changes, this is what we get:

Dimensionality reduction in Matlab

I want to reduce the dimension of data to ndim dimensions in MATLAB. I am using pcares to reduce dimension but the result (i.e. residuals,reconstructed) has the same dimensions as the data and not ndim. How can I project the residuals to ndim dimensions only.
[residuals,reconstructed] = pcares(X,ndim)
Sample code
MU = [0 0];
SIGMA = [4/3 2/3; 2/3 4/3];
X = mvnrnd(MU,SIGMA,1000);
[residuals,reconstructed] = pcares(X,1)
Now I expect the residuals to have 1 dimensions i.e. the data X projected to prime component as I specified it as pcares(X,1). But here both residuals and reconstructed have the same of 2.
pcares is doing its job. If you read the documentation, you call the function this way:
[RESIDUALS,RECONSTRUCTED] = pcares(X,NDIM);
RESIDUALS returns the residuals for each data point by retaining the first NDIM dimensions of your data and RECONSTRUCTED is the reconstructed data using the first NDIM principal components.
If you want the actual projection vectors, you need to use pca instead. You'd call it this way:
[coeff,score] = pca(x);
In fact, this is what pcares does under the hood but it also reconstructs the data for you using the above outputs. coeff returns the principal coefficients for your data while score returns the actual projection vectors themselves. score is such that each column is a single projection vector. It should be noted that these are ordered with respect to dominance as you'd expect with PCA... and so the first column is the most dominant direction, second column second dominant direction, etc.
Once you call the above, you simply index into coeff and score to retain whatever components you want. In your case, you just want the first component, and so do this:
c = coeff(1);
s = score(:,1);
If you want to reconstruct the data given your projection vectors, referring to the second last line of code, it's simply:
[coeff,score] = pca(x);
n = size(X,1);
ndim = 1; %// For your case
reconstructed = repmat(mean(X,1),n,1) + score(:,1:ndim)*coeff(:,1:ndim)';
The above is basically what pcares does under the hood.

Euclidean distance between two columns of two vector Matlab

I have two vectors A & B of size 250x4. The first column in each vector has the X values and the second column has the Y values. I want to calculate the euclidean distance between each the X & Y of each row in the two vectors and save the result in a new vector C of size 250x1 which holds the result of the euclidean distance. For example, if the first row in A is A1x, A1y, A1n, A1m and the first row in B is B1x, B1y, B1n, B1m so I want to get the eucledian distance which will be [(A1x-B1x)^2 + (A1y-B1y)^2]^0.5 and the result will be saved in C1 and same will be done for the rest of the 250 rows. So if anyone could please advise how to do this in Matlab.
Like this:
%// First extract on x-y data from A and B
Axy = A(:,1:2);
Bxy = B(:,1:2);
%// Find all euclidean distances (row-wise)
C1 = sqrt(sum((Axy-Bxy).^2,2));
plus it handles higher dimension too
use pdist2:
C1=diag(pdist2(A(:,1:2),B(:,1:2)));
Actually, pdist2 will give you a 250x250 matrix, because it calculate all the distances. You need only the main diagonal, so calling diag on the result (as in the code above) will produce the wanted result.

Put elements of a 1D vector into a 3D matrix using another matrix of positions

I have a vector of data points which were saved from a 3D array by some other program in one big list. The vector is nk elements long. nk = nx*ny*nz where nx, ny and nz are the dimensions of the original 3D array.
The position of a data point in the original array is stored in a (nk x 3) array, arranged with each row (position(k,:)) giving the (i,j,k) position of the corresponding data point.
I can't use reshape on my data array as the position vector is not simple (it depends on some stuff to do with how the data was generated - it isn't completely random - but I won't necessarily know what it looks like beforehand).
If the nk vector is called 'data', the nk x 3 position array is called 'position' and the output array is called 'data_reshaped' then currently I am doing the following:
for k = 1:nk
data_reshaped(position(k,1),position(k,2),position(k,3)) = data(k);
end
This is really slow - is there some faster method without knowing much about what 'position' looks like?
You could use sub2ind function:
data_reshaped = zeros(nx, ny, nz);
data_reshaped( sub2ind([nx ny nz], position(:,1), ...
position(:,2), ...
position(:,3)) ) = data;