matlab: specify which dimension to index [duplicate] - matlab

This question already has answers here:
On shape-agnostic slicing of ndarrays
(2 answers)
Dynamic slicing of Matlab array
(1 answer)
Closed 4 years ago.
Imagine a function where you want to output a snippet in a user-specified dimension of a n-dimensional matrix
function result=a(x,dim)
window=1:10;
dim=3;
result=x(:,:,window);
end
How can I put window to the desired dimension? E.g. if dim=2; then result=x(:,window,:).
The way I can think of right now is to evaluate a string command that puts window in the correct position - or use a lot of if then else blocks. What's a better way of doing this?

You can do this using a cell array to define your indexes, following the examples here.
Specifically, if you have a matrix
x = ones(7,5,9);
You can define the indexes you want like:
% get all the indexes in all dimensions
all_indexes = {':', ':', ':'};
% get all indexes in dimensions 1 and 3, and just indices 1:4 in dimension 2
indexes_2 = {':', 1:4, ':'};
And then get those indexes from your matrix x like
a = x(all_indexes{:});
b = x(indexes_2{:});
So, you could write a function like
function result=extract_cells(x, dim, window)
% Create blank cell array {':', ':', ...} with entries ':' for each dimension
% Edit (c/o Cris Luengo): need to use ndims(x) to get the number of dimensions
num_dims = ndims(x)
dims = cell(1, num_dims);
dims(:) = {':'};
% Set the specified window of cells in the specified dimension
dims{dim} = window;
% Pick out the required cells
result=x(dims{:});
end
Which could return all the cells in the dimensions other than one specified, and in that direction would return the cells in the range given by window. So in the following code, a and b would be equivalent.
a = extract_cells(x, 2, 1:5);
b = x(:, 1:5, :)

Related

Construct single column matrix from the rows of another matrix with multiple columns in Matlab [duplicate]

This question already has answers here:
rgb to ycbcr conversion in matlab
(2 answers)
Closed 4 years ago.
I'm learning about image processing, and currently translating between RGB and YCbCr.
Basically, I have a nxmx3 matrix, which represents the brightness of each colour (RGB), and the operation to change this to Y,Cb, and Cr would be something like this:
for each row, index, in RGBMatrix:
y[index] = row[0] + row[1] + row[2]
So each row of y has one value, and that value is based on the values in the same in RGBMatrix.
I've tried the following in MATLAB:
function [y,Cb, Cr] = rgbToYCbCr(r,g,b)
signalSize = size(r);
y = zeros(signalSize);
for i = 1:signalSize(1)
for j = 1:signalSize(2)
y(i,j,1) = (0.299 * r(i,j,1)) + (0.587 * g(i,j,2)) + (0.114 * b(i,j,3));
end
end
end
However, this does not give me the results that I want as y now has the same dimensions as the RGBMatrix, when instead it should be 1 column.
How can I do this efficiently in MATLAB?
I know there is an inbuilt function for RGB to YCbCr, but for the sake of learning I'd like to implement it myself.
Example:
RGB Matrix:
R G B
[[0.5,0.5,0.5],
[0.2,0.1,0.6],
[0.1,0.4,0.7]]
Y Matrix:
[[0.5+0.5+0.5],
[-0.2-0.1+0.6],
[0.1+0.4-0.7]]
or
[[1.5],
[0.3],
[-0.2]]
Ignore the fact that values aren't realistic, what I'm more concerned about is carrying out an operation per row like I've described in Matlab without using the (typically) slower iterative way (with a for loop).
In reality I have a 1080x768x3 array. Each element in the 1080x768 dimension has a value for each colour (R,G,B) and I want to convert this to (Y,Cb,Cr) eventually.
If you have an RGB image, Imgrgb that is NxMx3 in size, do:
Imgrgb= imread('peppers.png'); % A sample RGB image
% make sure you have a double image range [0-1], you may not need this.
Imgrgb=im2double(Imgrgb);
r=Imgrgb(:,:,1);
g=Imgrgb(:,:,2);
b=Imgrgb(:,:,3);
y = (0.299 * r) + (0.587 * g) + (0.114 * b);
Cb=
Cr=
% concatenate result:
ImgYCbCr=cat(3,y,Cb,Cr);
And y shoudl be NxM, the same as r,g, and b. Do the same for the other 2 values with their corresponding equation.
If your matrix is like:
A =
1 3 5
2 4 6
then you may "flatten" it to a vector like:
>>Av = A(:)
Av =
1
2
3
4
5
6
I.e. it takes the values from the matrix in a row order and converts to a vector.
To convert it back to a matrix you may use the reshape function
>> reshape(Av,[2 3])
ans =
1 3 5
2 4 6
The A(:) method is equivalent to reshape(A,[numel(A) 1])

How to sort all elements of a 2d matrix in MATLAB?

The sort() function sorts the elements row/column wise but how to sort the elements absolutely? The result should be another matrix with smallest element in (1,1) , second smallest in (1,2) and so on.
Take some random input
input = rand(5,10);
If you want the output to be a row vector, simply use
sortedRow = sort(input(:)).';
If you want the result to be the same shape as the input, then use
sortedOriginalShape = reshape(sort(input(:)), size(input,2), size(input,1)).';
Note that when maintaining the shape, we must use the reversed size dimensions and then transpose. This is because otherwise the result is column-wise i.e. smallest element in (1,1), next in (2,1) etc, which is the opposite of what you requested.
You can use the column operator (:) to vectorize all elements of 'nxm' matrix as a vector of 'nxm' elements and sort this vector. Then you can use direct assignement or 'reshape' function to store elements as matricial form.
All you need to know is that matlab use column-major-ordering to vectorize/iterate elements:
A = rand(3, 5);
A(:) = sort(A(:);
Will preserve colum-major-ordering, or as you said you prefer row-major ordering:
A = rand(3, 5);
A = reshape(sort(A(:)), fliplr(size(A)).';
Note the fliplr to store columnwise with reversed dimension and then the .' operator to transpose again the result.
EDIT
Even if matlab uses column-major-ordering for storing elements in memory, here below are two generic routines to work with row-major-order whatever the number of dimension of your array (i.e. no limited to 2D):
function [vector] = VectorizeWithRowMajorOrdering(array)
%[
axisCount = length(size(array)); % Squeezed size of original array
permutation = fliplr(1:(axisCount + 2)); % +2 ==> Trick to vectorize data in correct order
vector = permute(array, permutation);
vector = vector(:);
%]
end
function [array] = ReshapeFromRowMajorOrdering(vector, siz)
%[
siz = [siz( : ).' 1]; % Fix size if only one dim
array = NaN(siz); % Init
axisCount = length(size(array)); % Squeezed size!
permutation = fliplr(1:(axisCount + 2)); % +2 ==> Trick to vectorize data in correct order
array = reshape(vector, [1 1 fliplr(size(array))]);
array = ipermute(array, permutation);
%]
end
This can be useful when working with data coming from C/C++ (these languages use row-major-ordering). In your case this can be used this way:
A = rand(3, 5);
A = ReshapeFromRowMajorOrdering(sort(A(:)), size(A));

How to make calculations on certain cells (within a table) that meet specific criteria?

I have the following code:
L_sum = zeros(height(ABC),1);
for i = 1:height(ABC)
L_sum(i) = sum(ABC{i, ABC.L(i,4:281)});
end
Here my table:
Problem: My sum function sums the entire row values (col. 4-281) per date whereas I only want those cells to be added whose headers are in the cell array of ABC.L, for any given date.
X = ABC.L{1, 1}; gives (excerpt):
Red arrow: what sum function is referencing (L of same date).
Green arrow: what I am trying to reference now (L of previous date).
Thanks for your help
In general, in matlab you dont need to use for loops to do simple operations like selective sums.
Example:
Data=...
[1 2 3;
4 5 6;
7 8 9;
7 7 7];
NofRows=size(Data,1);
RowsToSum=3:NofRows;
ColToSum=[1,3];
% sum second dimension 2d array
Result=sum(Data(RowsToSum,ColToSum), 2)
% table mode
DataTable=array2table(Data);
Result2=sum(DataTable{RowsToSum,ColToSum}, 2)
To do that you need to first extract the columns you want to sum, and then sum them:
% some arbitrary data:
ABC = table;
ABC.L{1,1} = {'aa','cc'};
ABC.L{2,1} = {'aa','b'};
ABC.L{3,1} = {'aa','d'};
ABC.L{4,1} = {'b','d'};
ABC{1:4,2:5} = magic(4);
ABC.Properties.VariableNames(2:5) = {'aa','b','cc','d'}
% summing the correct columns:
L_sum = zeros(height(ABC),1);
col_names = ABC.Properties.VariableNames; % just to make things shorter
for k = 1:height(ABC)
% the following 'cellfun' compares each column to the values in ABC.L{k},
% and returns a cell array of the result for each of them, then
% 'cell2mat' converts it to logical array, and 'any' combines the
% results for all elements in ABC.L{k} to one logical vector:
col_to_sum = any(cell2mat(...
cellfun(#(x) strcmp(col_names,x),ABC.L{k},...
'UniformOutput', false).'),1);
% then a logical indexing is used to define the columns for summation:
L_sum(k) = sum(ABC{k,col_to_sum});
end

move left or right from point in matlab [duplicate]

This question already has an answer here:
MatLab function that shifts points to the left or right from a max or minimum point
(1 answer)
Closed 6 years ago.
I have written a script that does the function:
eval(['VCathodicOh3(2,i) =' 'min(' 'data_' varnames{1} '(49000:51000,9));'])
In this, 'VCathodicOh3' is made by taking data from column 9 in different variables that are organized in arrays (they are 1:100000 rows by 10 columns).
I want to, instead of just finding the min, also add something to this function so that it takes the min, finds the point at which that min is at, and then move 4 points to the left of that point.
Ex:
min(data_var(49000:51000,9)) = -3.190
then lets say (50250, -3.190) is the point at which you find the min. I would like the script to move that point -4 (50246, y) and give me the new corresponding value.
You can use the second output of min to tell you the index of the minimum value. You could then decrement this value by 4 to shift it to the left.
a = [5 3 2 1 0]
[~, ind] = min(a);
newval = a(ind-4)
If you have a 2D matrix, you can convert the linear index ind to rows / columns and decrement that:
a = magic(10);
[~, ind] = min(a);
[row,col] = ind2sub(size(a), ind);
newval = a(row, col-4)
As a side note, I would really discourage using dynamic variable names like you are and use a more appropriate data structure such as a cell array or struct. The usage of eval can get out of hand very quickly as I'm sure you've noticed.

Mysterious MATLAB error using ./ in a for loop [duplicate]

This question already has answers here:
Multiple loop variables Matlab
(3 answers)
Closed 7 years ago.
I need to make a for loop in MATLAB to divide each column in a matrix by a separate column vector. I only want to do this for a selection of the columns in the matrix, not all the columns.
This is what I'd like to do, where Indexes is a 19x1 vector of integers (not all consecutive numbers), big_matrix is 82x24, and other_column is 82x1:
matrix_to_fill = zeros(82,length(Indexes));
for x = Indexes
new_column = big_matrix(:,x)./other_column;
new_index = find(Indexes==x);
matrix_to_fill(:,new_index) = new_column;
end
When I run this I get the following error:
Error using ./
Matrix dimensions must agree.
I can run each iteration separately without getting errors, so I know that the matrix dimensions agree. What's more, if I type out the Indexes as a vector it works fine:
matrix_to_fill = zeros(82,length(Indexes));
for x = [1,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,23]
new_column = big_matrix(:,x)./other_column;
new_index = find(Indexes==x);
matrix_to_fill(:,new_index) = new_column;
end
And I think the "x=Indexes" syntax is fine because I've tested that using just:
for x = Indexes
disp(x)
end
So I'm completely stumped. Any help would be much appreciated!
The problem is in your definition of the for loop. When you say that you think the "x=Indexes" syntax is correct you haven't been observant enough to see that it is not correct.
What you need is
for x = Indexes'
% Do your looping
end
Note the transpose in the above.
If you do
for x = Indexes
disp(x)
end
Then the loop is executed once, with x taking on the value of the whole vector.
If you do
for x = Indexes'
disp(x)
end
then x will take on the individual elements of the matrix and you'll have 19 scalars displayed, once each time through the loop.