Append rows in matrix - matlab

for i = 1:6
if data(i,1) == 1
disp(i)
m(i,:) = data(i,:)
end
end
The code above returns a matrix m, with rows of data from the data file.
However, data(i,1) == 1 is true 4 times for the particular data, however m has 6 rows. 2 of the rows of m are just full of 0's, but the if statement is only true 4 times.
Why is that happening?

In answer to "why is that happening", it is because your matrices are the same size, but you only assign values to the rows which satisfy a condition. Therefore leaving other rows as 0s.
You either need a way to build m row by row (see end of this post) or create it in another way (my answer).
You can do this with logical indexing
% For creating m
m = data(data(:, 1) == 1, :);
% For displaying which indices satisfy your condition, one call to disp
disp( find(data(:, 1) == 1) )
Breaking this down, m is assigned to the values of data, where the column 1 of data is equal to 1, and all of the columns.
find returns the index of any non-zero element. The logical indexing returns an array of 0s and 1s, so all elements which satisfy the condition (and are 1) will be indexed by find.
You could also create the logical index and use it twice, better for maintenance at a later date if your condition changes:
% create logical index
idx = ( data(:,1) == 1 );
% same as above but using idx
m = data(idx, :);
disp( find(idx) )
Documentation
Logical indexing - https://uk.mathworks.com/help/matlab/matlab_prog/find-array-elements-that-meet-a-condition.html
find - https://uk.mathworks.com/help/matlab/ref/find.html
#Ander's suggestion to append only certain rows will work, and demonstrates well how to build a matrix. However, in this case you do not need your loop and if condition at all.

This is standard MATLAB.
Lets assume data(1,1) and data(3,1) are 1.
Then m(1,:)=data(1,:) and later m(3,:)=data(3,:). But what about m(2,:) It has to exist, because you filled m(3,:). There is no 3 without 2!
If you want m to have only the ones where data(i,1) == 1 then do:
m=[]; %create empty matrix
for i = 1:6
if data(i,1) == 1
disp(i)
m= [m; data(i,:)]; % "append" to m
end
end

Related

Simplify how to find unique rows of a Matlab array

I would like your help to vectorise (or, more generally, make more efficient) a Matlab code where:
Step 1: I construct a matrix C of size sgxR, where each row contains a sequence of ones and zeros, according to whether certain logical conditions are satisfied.
Step 2: I identify the indices of the unique rows of C.
I now describe the code in more details.
Step 1: Creation of the matrix C. I divide this step in 3 sub-steps.
Step 1.a: Create the 1x3 cell U_grid. For j=1,2,3, U_grid{j} is a sg x K matrix of numbers.
clear
rng default
n_U_sets=3; %This parameter will not be changed
sg=4; %sg is usually quite large, for instance 10^6
K=5; %This parameter can range between 3 and 8
Ugrid=cell(n_U_sets,1);
for j=1:n_U_sets
Ugrid{j}=randn(sg,K);
end
Step 1.b: For each g=1,...,sg
Take the 3 rows Ugrid{1}(g,:), Ugrid{2}(g,:), Ugrid{3}(g,:).
Take all possible 1x3 rows that can be formed such that the first element is from Ugrid{1}(g,:), the second element is from
Ugrid{2}(g,:), and the third element is from Ugrid{3}(g,:). There are K^3 such rows.
Create the matrix D{g} storing row-wise all possible pairs of such 1x3 rows. D{g} will have size (K^3*(K^3-1)/2)x6
This is coded as:
%Row indices of all possible pairs of rows
[y, x] = find(tril(logical(ones(K^(n_U_sets))), -1));
indices_pairs = [x, y]; %K^3*(K^3-1)/2
%Create D{g}
for g=1:sg
vectors = cellfun(#(x) {x(g,:)}, Ugrid); %1x3
T_temp = cell(1,n_U_sets);
[T_temp{:}] = ndgrid(vectors{:});
T_temp = cat(n_U_sets+1, T_temp{:});
T = reshape(T_temp,[],n_U_sets);
D{g}=[T(indices_pairs(:,1),:) T(indices_pairs(:,2),:)]; %(K^3*(K^3-1)/2) x (6)
end
Step 1.c: From D create C. Let R=(K^3*(K^3-1)/2). R is the size of any D{g}. C is a sg x R matrix constructed as follows: for g=1,...,sg and for r=1,...,R
if D{g}(r,1)>=D{g}(r,5)+D{g}(r,6) or D{g}(r,4)<=D{g}(r,2)+D{g}(r,3)
then C(g,r)=1
otherwise C(g,r)=0
This is coded as:
R=(K^(n_U_sets)*(K^(n_U_sets)-1)/2);
C=zeros(sg,R);
for g=1:sg
for r=1:R
if D{g}(r,1)>=D{g}(r,5)+D{g}(r,6) || D{g}(r,4)<=D{g}(r,2)+D{g}(r,3)
C(g,r)=1;
end
end
end
Step 2: Assign the same index to any two rows of C that are equal.
[~,~,idx] = unique(C,"rows");
Question: Steps 1.b and 1.c are the critical ones. With sg large and K between 3 and 8, they take a lot of time, due to the loop and reshape. Do you see any way to simplify them, for instance by vectorising?

Sorting with tiebreaks in matlab recursively

I want to define a recursive function that sorts an input vector and uses a sequence of secondary vectors to break any ties (or randomises them if it runs out of tiebreak vectors)
Given some input vector I and some tiebreaker matrix T, the pseudocode for the algorithm is as follows:
check if T is empty, if so, we reached stopping condition, therefore randomise input
get order of indices for sorted I, using matlab's standard sort function
find indices of duplicate values
for each duplicate value,
call function recursively on T(:,1) with rows corresponding to the indices of that duplicate value, with T(:,2:end)(with appropriate rows) as the new tiebreaker matrix - if empty then this call will just return random indices
fix the order of the sorted indices in the original sorted I
return the sorted I and corresponding indices
Here is what I have so far:
function [vals,idxs] = tiebreak_sort(input, ties)
% if the tiebreak matrix is empty, then return random
if isempty(ties)
idxs = randperm(size(input,1));
vals = input(idxs);
return
end
% sort the input
[vals,idxs] = sort(input);
% check for duplicates
[~,unique_idx] = unique(vals);
dup_idx = setdiff(1:size(vals,1),unique_idx);
% iterate over each duplicate index
for i = 1:numel(dup_idx)
% resolve tiebreak for duplicates
[~,d_order] = tiebreak_sort(ties(input==input(i),1),...
ties(input==input(i),2:end));
% fix the order of sorted indices (THIS IS WHERE I AM STUCK)
idxs(vals==input(i)) = ...?
end
return
I want to find a way to map the output of the recursive call, to the indices in idxs, to fix their order based on the (possibly recursive) tie breaks, but my brain is getting twisted in knots thinking about it..
Can I just use the fact that Matlabs sort function is stable and preserves the original order, and do it like this?
% find indices of duplicate values
dups = find(input==input(i));
% fix the order of sorted indices
idxs(vals==input(i)) = dups(d_order);
Or will that not work? is there another way of doing what I am trying to do, in general?
Just to give a concrete example, this would be a sample input:
I = [1 2 2 1 2 2]'
T = [4 1 ;
3 7 ;
3 4 ;
2 2 ;
1 8 ;
5 3 ]
and the output would be:
vals = [1 1 2 2 2 2]'
idxs = [4 1 5 3 2 6]'
Here, there are clearly duplicates in the input, so the function is called recursively on the first column of the tiebreaker matrix, which was able to fix the 1s but it needed a second recursive call on the 3s of the first column to break those ties.
No need to define a function, sortrows does that:
[S idxs] = sortrows([I T]);
vals = S(:,1);

Matlab: construct a binary matrix satisfying horizontal and vertical constraints

I want to initiate a mxn binary matrix. The summation of each column of the matrix equals to a given value s(j), for j=1..n. In addition, the summation of each row of the matrix should be within the range of given bounds: lhs is dl(i) and rhs is du(i), for i=1..m.
Now I can only randomly generate binary columns, in each of which the sum of ones equals to s(j) of that column, such as the following codes.
xij = zeros(m,n);
for j=1:n
randRows=randperm(m); %a row vector containing a random permutation of the integers from 1 to m inclusive.
rowsWithOne=randRows(1:sj(j)); %row indices having 1, and sum up to sj
xij(rowsWithOne,j)=1;
end
However, xij usually doesn't satisfy the horizontal constraints. I was thinking I should create a matrix first meets the row constraints lhs (lower bound) dl(i), then, column constraint s(j), and finally fill the offsets to meet rhs du(i), but I don't know how to implement it in Matlab. Is there any ideas to create xij?
Thanks in advance.
There are a couple things to first take into account, mainly if the problem can even be solved with the given constraints. First, you must check that the sum of s(j), sum(s) is greater than the sum of the lower bound constraints, sum(dl), and also less than the sum of the upper bound constraints, sum(du).
A simple if statement should be able to check this. After this has been done, the following code should be one solution to the problem, but given the nature of the problem, there will probably not be a unique solution.
%initialize counters
x = 1;
y = 1;
%make sure the lower bounds are first satisfied
for k = 1:nrows
while (dl(k) > 0)
if (s(y) > 0)
xij(k,y) = 1;
dl(k) = dl(k)-1;
du(k) = du(k)-1;
s(y) = s(y)-1;
end
y = y+1;
end
y = 1;
end
%make sure the columns all add to their specified values
for k = 1:ncols
while (s(k) > 0)
if (xij(x,k) == 0 && du(x) > 0)
xij(x,k) = 1;
du(x) = du(x)-1;
s(k) = s(k)-1;
end
x = x+1;
end
x = 1;
end
The first for loop adds 1s along the rows such that each row satisfies the minimum constraint. The next for loop adds 1s along the columns such that each column adds up to the desired value. The if statement in the first for loop ensures that a 1 isn't added if that column has already reached its desired value, and the if statement in the second for loop ensures that a 1 isn't added to a row that would put it over the maximum value of the row.
In order to keep track of the amount of ones, the du is lowered by 1 every time a 1 is added to the corresponding row, and likewise the desired value s(j) is subtracted by 1 every time a 1 is added to that column of the xij matrix.
%Create an initial xij matrix, each column of which consists of a random binary xij sequence, but sum of ones in each column equals to sj
Init_xij = zeros(nShift,nCombo);
for j=1:nCombo
randRows=randperm(nShift); %a row vector containing a random permutation of the integers from 1 to nShift inclusive.
rowsWithOne=randRows(1:sj(j)); %row indices having 1, and sum up to sj
Init_xij(rowsWithOne,j)=1;
end
%Adjust the initial xij matrix to make it feasible, satisfying horizontal
%LHS (dli) and RHS (dui) constraints
Init_xij_Feasible=Init_xij;
k=1;
while k
RowSum=sum(Init_xij_Feasible,2); %create a column vector containing the sum of each row
CheckLB=lt(RowSum,dli); %if RowSum <dli, true
CheckUB=gt(RowSum,dui); %if RowSum >dui, true
if ~any(CheckLB)&&~any(CheckUB) %if any element in CheckLB and CheckUB is zero
break,
else
[~,RowIdxMin]=min(RowSum);
[~,RowIdxMax]=max(RowSum);
ColIdx=find(Init_xij_Feasible(RowIdxMax,:) & ~Init_xij_Feasible(RowIdxMin,:),1); % returns the first 1 column index corresponding to the nonzero elements in row RowIdxMax and zero elements in row RowIdxMin.
%swap the min and max elements
[Init_xij_Feasible(RowIdxMin,ColIdx),Init_xij_Feasible(RowIdxMax,ColIdx)]=deal(Init_xij_Feasible(RowIdxMax,ColIdx),Init_xij_Feasible(RowIdxMin,ColIdx));
end
k=k+1;
end

MATLAB. Modify matrix rows starting by a certain (variable) colums index

I have a big matrix of numerical data, let assume here for practicity a small matrix
a=[1 1 1;
1 1 1]
Then I have a vector of indices
b=[4;
2]
My goal is to "apply" vector b to a, row by row, in such a way to nullify all the items of the i-th row of a which are falling in the columns whose indices are greater than the i-th element of b, when possible.
Therefore my desired output would be:
c=some_smart_indexing_operation(a,b) %this is pseudo-code of course
c=[1 1 1;
1 0 0]
Let me comment the results row by row:
on the first row, b's first element is 4: having a only 3 colums no element is nullify
on the second tow, b's second element is 2: I should nullify the second and the third element of this row.
I could perform such an operation with a for loop by I was wondering if I could get the same result applying some smart indexing operation or applying some vectorial native functions.
You can use bsxfun to create a mask of zero-one values, and then multiply a element-wise by that mask:
c = a .* bsxfun(#lt, 1:size(a,2), b);
In Matlab R2016b onwards the following simpler syntax can be used:
c = a .* ((1:size(a,2))<b);
Another approach is to use the complementary mask to the one above as a logical index to write the zeros.
c = a;
c(bsxfun(#ge, 1:size(a,2), b)) = 0; % or c(((1:size(a,2))>=b)) = 0

delete elements from a matrix and calculate mean

I have an N-by-M-Matrix as input called GR wich consists of the following numbers: -3,0,2,4,7,10,12
And I have to return a vector. If M=1, then it should just return the input.
If M>1 It should remove the lowest number from the matrix and then calculate the mean of the remaining numbers.
However, if one of the numbers in the row is -3, it should return the value -3 in the output.
My thoughts of the problem:
Is it possible to make a for loop?
for i=1:length(GR(:,1))
If length(GR(1,:))==1
GR=GR
end
If length(GR(1,:))>1
x=min(GR(i,:))=[] % for removing the lowest number in the row
GR=sum(x)/length(x(i,:))
I just don't have any Idea of how to detect if any of the numbers in the row is -3 and then return that value instead of calculating the mean and when I tried to delete the lowest number in the matrix using x=min(GR(i,:)) matlab gave me this error massage 'Deletion requires an existing variable.'
I put in a break function. As soon as it detects a -3 value it breaks from the loop. Same goes for the other function.
Note that it is an i,j (M*N) matrix. So you might need to change your loop.
for i=1:length(GR(:,1))
if GR(i,1)==-3
GR=-3
break
end
If length(GR(1,:))==1
GR=GR
break
end
If length(GR(1,:))>1
x=min(GR(i,:))=[] % for removing the lowest number in the row
GR=sum(x)/length(x(i,:))
end
end
you can use Nan's, nanmean, any, and dim argument in these functions:
% generate random matrix
M = randi(3);
N = randi(3);
nums = [-3,0,2,4,7,10,12];
GR = reshape(randsample(nums,N*M,true),[N M]);
% computation:
% find if GR has only one column
if size(GR,2) == 1
res = GR;
else
% find indexes of rows with -3 in them
idxs3 = any(GR == -3,2);
% the (column) index of the min. value in each row
[~,minCol] = min(GR,[],2);
% convert [row,col] index pair into linear index
minInd = sub2ind(size(GR),1:size(GR,1),minCol');
% set minimum value in each row to nan - to ignore it on averaging
GR(minInd) = nan;
% averaging each rows (except for the Nans)
res = nanmean(GR,2);
% set each row with (-3) in it to (-3)
res(idxs3) = -3;
end
disp(res)