Indexing must appear last in an index expression - matlab

I have a vector CD1 (120-by-1) and I separate CD1 into 6 parts. For example, the first part is extracted from row 1 to row 20 in CD1, and second part is extracted from row 21 to row 40 in CD1, etc. For each part, I need to compute the means of the absolute values of second differences of the data.
for PartNo = 1:6
% extract data
Y(PartNo) = CD1(1 + 20*(PartNo-1):20*(PartNo),:);
% find the second difference
Z(PartNo) = Y(PartNo)(3:end) - Y(PartNo)(1:end-2);
% mean of absolute value
MEAN_ABS_2ND_DIFF_RESULT(PartNo) = mean(abs(Z));
end
However, the commands above produce the error:
()-indexing must appear last in an index expression for Line:2
Any ideas to change the code to have it do what I want?

This error is often encountered when Y is a cell-array. For cell arrays,
Y{1}(1:3)
is legal. Curly braces ({}) mean data extraction, so this means you are extracting the array stored in location 1 in the cell array, and then referencing the elements 1 through 3 of that array.
The notation
Y(1)(1:3)
is different in that it does not extract data, but it references the cell's location 1. This means the first part (Y(1)) returns a cell-array which, in your case, contains a single array. So you won't have direct access to the regular array as before.
It is an infamous limitation in Matlab that you cannot do indirect or double-referencing, which is in effect what you are doing here.
Hence the error.
Now, to resolve: I suspect replacing a few normal braces with curly ones will do the trick:
Y{PartNo} = CD1(1+20*(PartNo-1):20*PartNo,:); % extract data
Z{PartNo} = Y{PartNo}(3:end)-Y{PartNo}(1:end-2); % find the second difference
MEAN_ABS_2ND_DIFF_RESULT{PartNo} = mean(abs(Z{PartNo})); % mean of absolute value

I might suggest a different approach
Y = reshape(CD1, 20, 6);
Z = diff(y(1:2:end,:));
MEAN_ABS_2ND_DIFF_RESULT = mean(abs(Z));

This is not a valid statement in matlab:
Y(PartNo)(3:end)
You should either make Y two-dimensional and use this indexing
Y(PartNo, 3:end)
or extract vector parts and use them directly, if you use a loop like you have shown
for PartNo = 1:6
% extract data
Y = CD1(1 + 20*(PartNo-1):20*(PartNo),:);
% find the second difference
Z = Y(3:end) - Y(1:end-2);
% mean of absolute value
MEAN_ABS_2ND_DIFF_RESULT(PartNo) = mean(abs(Z));
end
Also, since CD1 is a vector, you do not need to index the second dimension. Drop the :
Y = CD1(1 + 20*(PartNo-1):20*(PartNo));
Finally, you do not need a loop. You can reshape the CD1 vector to a two-dimensional array Y of size 20x6, in which the columns are your parts, and work directly on the resulting matrix:
Y = reshape(CD1, 20, 6);
Z = Y(3:end,:)-Y(1:end-1,:);
MEAN_ABS_2ND_DIFF_RESULT = mean(abs(Z));

Related

Count number of unique symbols for a Buckingham Pi calculation in an expression with MATLAB

I am trying to write a script that goes through the Buckingham Pi theorem given a list of variables, each having a dimension. The set of dimensions do not need to be unique (it can contain repeats) only the same size as the set of variables.
clc, clear, close all
syms M L T Theta
dimen = [M,L,T,Theta]
mass - M
length - L
time - T
temperature - Theta
(I can add in other dimensions like electric current later, but for now, I want to get it working with these four).
Here is what I have so far in MatLab.
A = L^2; % maybe an area
V = L/T; % maybe a velocity
D = M/L^3; % maybe a density
% This the array of the combinations
param = {A,V,D};
I want to count how many of the syms M L T and Theta show up in my cell param.
For example starting at the first entry in the cell array.
param{1} = A
L^2
At this step, it should count that L has shown up once, and the others 0 times each.
param{2} = V
L/T
At this step, it counts that L has shown up once, but since it was already counted so I don't want to count it again. It should also count that T has shown up once. So far 1 L, and 1 T.
param{3} = D
M/L^3
Finally, this count that M has shown up once. So far 1 L, 1 T, and 1 M.
Since there are four possible symbols, I want to end the algorithm with this.
j = num; % how many times each of the syms was counted at least once.
If a dimension is not counted, that is fine. I am only interested in counting how many times each of the dimensions in the cell array are counted at least once. I will then use these to solve a system of equations to identify dimensionless groups.
I've received some suggestions based on other answers that I am providing below.
How to extract powers of a symbolic polynomial?
present = cellfun(#(expr), ismember(dimen, symvar(expr)), param, 'UniformOutput', false)
counts = sum(vertcat(present{:}), 1)
This last suggestions gives this error.
Error using cellfun
Input #2 expected to be a cell array, was sym instead.
Addendum
Removing the comma as suggested in the comments/answer still gives the same error. I am using release 2021b and a mlx file.
cellfun expects a cell array as its second argument: by introducing a comma between #(expr) and ismember(dimen, symvar(expr)) it's as if you were asking cellfun to iterate over the content of ismember(dimen, symvar(expr)), which is not what you really want, as that's the body of the anonymous function you are passing to cellfun as first argument.
The correct way of using cellfun is shown in the following script:
clc, clear, close all
syms M L T Theta
dimen = [M,L,T,Theta]
A = L^2; % maybe an area
V = L/T; % maybe a velocity
D = M/L^3; % maybe a density
% This the array of the combinations
param = {A,V,D};
% Counts the appearences of each dimension in each param
% and stores them in a cell array of vectors
present = cellfun(#(expr) ismember(dimen, symvar(expr)), param, 'UniformOutput', false)
% Unpack the cell array of vectors and
% compute the total number of appearences of each dimension
counts = sum(vertcat(present{:}), 1)

Vectorize code to create a 3-dimensional array consisting of coordinate windows from a coordinate vector

I have a coordinate vector comp_points holding an image-coordinate-pair in each row. Now I want to create an array comp_windows holding nxm-windows of an image around the coordinates of comp_points. These windows shall be aligned along the 3rd dimension of comp_windows.
I solved the task like this:
I2=randi([0 255],[500 500]);
comp_points=randi([10 490],[20 2]);
delta_u_window=5;
delta_v_window=5;
for ii=1:size(comp_points,1)
comp_windows(:,:,ii)=I2(...
comp_points(ii,1)-delta_u_window:...
comp_points(ii,1)+delta_u_window,...
comp_points(ii,2)-delta_v_window:...
comp_points(ii,2)+delta_v_window);
end
Now I feel like I could do this without the for-loop using a concatenation or indexing expression or something, but I can't figure it out.
You already have the operations as slicing without any compute. So, I am not sure if it's worth to vectorize it, but let's put it out anyway with big help from bsxfun -
% Get range arrays
r1 = [-delta_u_window : delta_u_window];
r2 = [-delta_v_window : delta_v_window];
% Get row and column indices for all points in comp_points
r = bsxfun(#plus,r1(:),comp_points(:,1).');
c = bsxfun(#plus,r2(:),comp_points(:,2).');
% Next up, the work is to combine those row and column indices in a meshed-way
% Get 3D version of r and c - Keeping their last dim aligned and "spreading
% out" their first dims against each others. Then, perform elementwise
% summations to give us a summed up array of indices, indexing into which
% would give us the desired output.
r3D = reshape(r,size(r,1),1,[]);
c3D = reshape((c-1)*size(I2,1),1,size(c,1),[]);
out = I2(bsxfun(#plus, r3D, c3D));
For permute lovers, we can replace the last three steps with a single one, like so -
I2(bsxfun(#plus, permute(r,[1,3,2]), permute((c-1)* size(I2,1),[3,1,2])))

Matrix as input and output of Matlab function

For example I want to have a function which let me delete rows of my matrix where the highest value is 1. Thus I wrote:
% A is an input matrix
% pict2 suppose to be output cleared matrix
function pict2 = clear_(A)
B=A
n = size(A,1)
for i=1:n
if(max(B(i,:))==1)
B(i,:)=[]
end
end
But after I call:
pict2=clear_(pict) the Matlab resposes:
"warning: clear_: some elements in list of return values are undefined
warning: called from
clear_ at line 5 column 1 pict2 = "
I'm not sure which elements were left undefined?
The variable name of your output argument must match the variable that you want to be returned. So you'll want to change the first line to the following so that your modifications to B are saved and returned.
function B = clear_(A)
As far as your algorithm is concerned, it's not going to work because you are modifying B while trying to loop through it. Instead, you can replace your entire function with the following expression which computes the maximum value of each row, then determines if this value is equal to 1 and removes the rows where this is the case.
B(max(B, [], 2) == 1, :) == [];
I believe that, alternatively to the suggestions you already recieved, you might want to try the following. Using logicals is probably one of the best options for such a problem, since you don't need to use for-loops:
function out = clear_matr(A)
% ind is true for all the rows of A, where the highest value is not equal to 1
ind = ~(max(A, [], 2) == 1);
% filter A accordingly
out = A(ind, :);
end

Importing text file into matrix form with indexes as strings?

I'm new to Matlab so bear with me. I have a text file in this form :
b0002 b0003 999
b0002 b0004 999
b0002 b0261 800
I need to read this file and convert it into a matrix. The first and second column in the text file are analogous to row and column of a matrix(the indices). I have another text file with a list of all values of 'indices'. So it should be possible to create an empty matrix beforehand.
b0002
b0003
b0004
b0005
b0006
b0007
b0008
Is there anyway to access matrix elements using custom string indices(I doubt it but just wondering)? If not, I'm guessing the only way to do this is to assign the first row and first column the index string values and then assign the third column values based on the first text file. Can anyone help me with that?
You can easily convert those strings to numbers and then use those as indices. For a given string, b0002:
s = 'b0002'
str2num(s(2:end); % output = 2
Furthermore, you can also do this with a char matrix:
t = ['b0002';'b0003';'b0004']
t =
b0002
b0003
b0004
str2num(t(:,2:end))
ans =
2
3
4
First, we use textscan to read the data in as two strings and a float (could use other numerical formats. We have to open the file for reading first.
fid = fopen('myfile.txt');
A = textscan(fid,'%s%s%f');
textscan returns a cell array, so we have to extract your three variables. x and y are converted to single char arrays using cell2mat (works only if all the strings inside are the same length), n is a list of numbers.
x = cell2mat(A{1});
y = cell2mat(A{2});
n = A{3};
We can now convert x and y to numbers by telling it to take every row : but only the second to final part of the row 2:end, e.g 002, 003 , not b002, b003.
x = str2num(x(:,2:end));
y = str2num(y(:,2:end));
Slight problem with indexing - if I have a matrix A and I do this:
A = magic(8);
A([1,5],[3,8])
Then it returns four elements - [1,3],[5,3],[1,8],[5,8] - not two. But what you want is the location in your matrix equivalent to x(1),y(1) to be set to n(1) and so on. To do this, we need to 1) work out the final size of matrix. 2) use sub2ind to calculate the right locations.
% find the size
% if you have a specified size you want the output to be use that instead
xsize = max(x);
ysize = max(y);
% initialise the output matrix - not always necessary but good practice
out = zeros(xsize,ysize);
% turn our x,y into linear indices
ind = sub2ind([xsize,ysize],x,y);
% put our numbers in our output matrix
out(ind) = n;

How to change row number in a FOR loop... (MATLAB newbie)

I have a set of data that is <106x25 double> but this is inside a struct and I want to extract the data into a matrix. I figured a simple FOR loop would accomplish this but I have hit a road block quite quickly in my MATLAB knowledge.
This is the only piece of code I have, but I just don't know enough about MATLAB to get this simple bit of code working:
>> x = zeros(106,25); for i = 1:106, x(i,:) = [s(i).surveydata]; end
??? Subscripted assignment dimension mismatch.
's' is a very very large file (in excess of 800MB), it is a <1 x 106 struct>. Suffice it to say, I just need to access a small portion of this which is s.surveydata where most rows are a <1 x 25 double> (a row vector IIRC) and some of them are empty and solely return a [].
s.surveydata obviously returns the results for all of the surveydata contained where s(106).surveydata would return the result for the last row. I therefore need to grab s(1:106).surveydata and put it into a matrix x. Is creating the matrix first by using x = zeros(106,25) incorrect in this situation?
Cheers and thanks for your time!
Ryan
The easiest, cleanest, and fastest way to write all the survey data into an array is to directly catenate it, using CAT:
x = cat(1,s.surveydata);
EDIT: note that if any surveydata is empty, x will have fewer rows than s has elements. If you need x to have the same amount of rows as s has elements, you can do the following:
%# find which entries in s have data
%# note that for the x above, hasData(k) contains the
%# element number in s that the k-th row of x came from
hasData = find(arrayfun(#(x)~isempty(x.surveydata),s));
%# initialize x to NaN, so as to not confuse the
%# real data with missing data entries. The call
%# to hasData when indexing makes this robust to an
%# empty first entry in s
x = NaN(length(s),length(s(hasData(1)).surveydata);
%# fill in only the rows of x that contain data
x(hasData,:) = cat(1,s(hasData).surveydata);
No, creating an array of zeroes is not incorrect. In fact it's a good idea. You don't have to declare variables in Matlab before using them, but for loops, pre-allocating has speed benefits.
x = zeros(size(s), size(s(1)));
for i = 1:106
if ~isempty(s(i).surveydata)
x(i, :) = s(i).surveydata;
end
end
Should accomplish what you want.
EDIT: Since OP indicated that some rows are empty, I accounted for that like he said.
what about this?
what s is?
if s(i).surveydata is scalar:
x = zeros(106,25);
for i = 1:106
x(i,1) = [s(i).surveydata];
end
I am guessing that is what you want tough it is not clear at all :
if s(i).surveydata is row vector:
x = zeros(106,25);
for i = 1:106
x(i,:) = [s(i).surveydata];
end
if s(i).surveydata is column vector:
x = zeros(106,25);
for i = 1:106
x(i,:) = [s(i).surveydata]';
end