Sum second column elements based on first column elements - MATLAB - matlab

I have two matrix with more than 1000 rows, and two columns.
Everytime the first column is '0', the second has a value, and everytime the first column is '1', the second is zero.
Example:
M = [0 23;0 35;1 0;1 0;0 2;1 0]
M =
0 23
0 35
1 0
1 0
0 2
1 0
Let's think about the second column as a non periodic cycle.
What I want is, everytime the first column is 0 (until is one again), having the opportunity to analyse the size and sum of the second column. In the end I want to know which cycle is bigger in size and sum. (in the example matrix, as output, I know the first cycle is the bigger with a sum of 58).

Assuming A as the input two-column array, here's one approach with accumarray -
%// Create ID array for using with accumarray
id = cumsum([1;diff(A(:,1))~=0]);
%// Get summations and counts for all IDs
sums = accumarray(id,A(:,2));
counts = accumarray(id,1);
%// Get offset in case the starting element in first column is not 0
offset = (A(1,1)~=0)+1;
%// Consider only even IDs corresponding to 0 elements cycle
sums = sums(offset:2:end)
counts = counts(offset:2:end)
Sample run -
A =
1 34
1 45
0 23
0 35
1 0
1 0
0 2
0 8
0 6
1 9
sums =
58
16
counts =
2
3

Related

Comparing elements in each row of a matrix and count the similar values

I have a matrix like this:
line=[1 3 5 0 0 4 2;
1 3 8 0 8 2 2 ]
I want to compare the rows in this matrix. If the 1st column of the first row is the same as 1st column of second row then increase a counter. But if the value is zero, then the counter should not be increased.
For the example above I expect the output to be match = 3
where the matching values are 1,3,2 so the match = 3
I would go for this:
match = sum((line(1, :) == line(2, :)) & (line(1, :) != 0))
The Array comparison line(1, :) == line(2, :) will give you (logical) 1 at the points, where both rows have identical values:
ans =
1 1 0 1 0 0 1
Next, you need to exclude possible 0 values. That can be done by findind non-zero elements just in the first row (line(1, :) != 0), and then using the & operator on the results. You'll get:
ans =
1 1 0 0 0 0 1
At last, you just have to count the ones using sum.
You can check if the sum of each column divided by the first line equal 2.
So:
count = sum(sum(x)./x(1,:)==2)
Since 0/0 is indetermined, 0 will not be taken into account.

Inserting Ones at pseudo-fixed Intervals

I have the following row vector:
A = zeros(1,200);
I'd like to insert a '1' every 2-3 columns until I have exactly 80 ones in total that are approximately evenly spaced - as opposed to having fixed spacing - with the first 2 columns being zeros.
e.g.
0 0 1 0 1 0 0 1 0 0 1 0 1 ...
It would be nice if the combination could be permuted as well so that more than one row vector satisfies the criteria.
Thanks!
You could use repelem (run-length encoding) to do this. The way that repelem works is that we have two inputs: the values and the number of times each value is repeated.
For example
values = [0 1];
repeats = [1 2];
repelem(values, repeats)
% 0 1 1
We can also have duplicate values in the values array
values = [0 1 0 1];
repeats = [2 1 1 1];
repelem(values, repeats)
% 0 0 1 0 1
We can utilize this to solve your problem.
First we construct the values matrices to simply alternate between 0 and 1 meaning that we want the expanded matrix to contain some 0's followed by a 1, some 0's followed by a 1, etc.
values = ~mod(1:80, 2);
% 0 1 0 1 0 1 0 1 ....
In your case, the number of times each 0 is going to be repeated is going to be either 1 or 2. Each 1 however, is only going to be repeated once. To make this happen we use rand to pick randomly between 1 and 2 repeats. Then we assign all the repeats for 1 values to be a single repeat.
repeats = randi([1 2], size(values));
% Make sure that the 1's are always only repeated once
repeats(values) = 1;
We use 80 entries in the repeats and values arrays just to make sure that we end up with at least 80 values in the final (expanded) array.
Now apply the repelem and keep only the first 80 values
result = repelem(values, repeats);
result = result(1:80);
% 0 1 0 0 1 0 0 1 0 1 0 0 1
You can do this with a few standard functions and array indexing. Something like this ...
A = zeros(1,200);
ixs = round(cumsum(2 + rand(200,1)));
A(ixs(ixs<200))=1;
%Sample result here, first 20 entries: 0 0 1 0 1 0 0 1 0 0 1 0 1 0 0 1 0 1 0 1
What we're doing here is:
Setting up the A array (this is clear)
Defining an oversized array of index values to set to one (more on that below)
Then using that index to set values to one, trimming the oversize.
In terms of creating the index ixs, in innermost portion (2 + rand(200,1)) creates a 200x1 array of values between 2 and 3. Using cumsum generates the cumulative sum of this array, and then round rounds the values to an integer, which can be used for indexing. For example, the first 10 values is ixs look like this, for a particular run:
>> ixs(1:10)'
ans =
3 5 8 11 13 16 18 20 22 24
Since the number of 1 values will vary each time, I set this up to be oversized. That is, the last few values are [487 489 491 497 500], larger than the actual size required. This is why the values need to be trimmed with applying the index.
A = zeros(1,200);
idx = cumsum(1 + randi(2,80,1)); % This is the main trick
A(idx) = 1;
cumsum(1 + randi(2,80,1)) gets you the indexes for exactly 80 elements in A which need to be switched to 1 spaced by 2 or 3 (randomly)

Inserting columns to a matrix in Matlab

I'd like to insert columns to a matrix, but the insertion column positions within the matrix differ by row. How can I do this without using for-loop?
Following is a simplified example in MATLAB;
From A,X,P, I want to get APX without using for-loop.
>> A = zeros(4,5) % inclusive matrix
A =
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
>> X = [9,8;5,7;8,3;6,7] % data to insert
X =
9 8
5 7
8 3
6 7
>> P = [3;2;4;1] % insertion position within the matrix
P =
3
2
4
1
>> APX = [0,0,9,8,0;0,5,7,0,0;0,0,0,8,3;6,7,0,0,0] % what I want
APX =
0 0 9 8 0
0 5 7 0 0
0 0 0 8 3
6 7 0 0 0
It's simply determining the right column-major indices to access the matrix so you can populate it with your desired values. This first requires generating the right row and column values to access the right positions in APX so you can use X to populate those positions.
Using P, each element tells you which column you should start populating for each row of X. You will need to generate column indices in increasing order up to as many columns as there are in X. To generate the row indices, simply create a matrix that is the same size as X where each column spans from 0 up to as many rows as there are in X minus 1 (i.e. 0:size(X,2)-1). This matrix gives you the correct offsets so that you can take P and add it with this matrix. Once you do that you will have a column index matrix that tells you specifically where each element should go with regards to the columns of the output matrix per row of P. Finally, use sub2ind to generate the column-major indices using the rows and columns generated above to place X in APX.
In other words:
P = [3;2;4;1];
X = [9,8;5,7;8,3;6,7];
rowInd = repmat((1:size(X,1)).', 1, size(X,2)); %'
colInd = bsxfun(#plus, P, 0:size(X,2)-1);
APX = zeros(size(X,1), max(colInd(:)));
APX(sub2ind(size(APX), rowInd, colInd)) = X;
To generate the row locations, we use repmat to create a matrix that is the same size as X where each column spans from 1 up to as many rows as X. To generate the column locations, we use bsxfun to create a matrix where each column is the vector P but increasing by 1 per column. We then create APX to be of compatible size then use sub2ind to finally populate the matrix.
With your above test inputs, we get:
APX =
0 0 9 8 0
0 5 7 0 0
0 0 0 8 3
6 7 0 0 0
Minor Note
You really should actually try using loops before trying it vectorized. Though using loops was slow in previous versions of MATLAB, MATLAB R2015b has an improved JIT engine where loops are now competitive. You should time your code using loops and ensuring that it is justifiable before switching to vectorized implementations.

How to select specific rows based upon column attribute values in matlab?

I have [sentence cross words] logical matrix where value = 1 shows presence of a word in that sentence and 0 shows absence like as follows:
0 0 1 1
1 0 1 0
0 0 0 1
1 1 0 0
I have done some processing and selected specific words i.e. 2 & 3
result = 2 3
Now, I want to select only those rows in which value of words 2 & 3 are equal to 1 and return there row number as follows:
row = 1 2 4
This should be done for every word that is in result variable - thanks.
Think you are looking for something like this, assuming A as the input binary array -
result = [2 3]; %// select words by IDs
row = find(any(A(:,result),2))
Sample run -
A =
0 0 1 1
1 0 1 0
0 0 0 1
1 1 0 0
row =
1
2
4
For fun-sake, you can also use matrix-multiplication as an alternative approach -
row = find(A(:,result)*ones(numel(result),1))
First choose the columns that you want to extract and create a matrix that concatenates all of these columns together. Next, use any and operate along the columns in combination with find to obtain the desired locations.
Therefore, given your logical matrix stored in X, do:
ind = [2 3];
matr = X(:,ind);
vals = find(any(matr, 2));
With your above example, we get:
vals =
1
2
4

Resorting rows in matrix in non-decreasing order based on the entries of an arbitrary column

I am working with nx2 matrices in Matlab, and what I'm trying to do is fairly simple in principle. I randomly generate a square matrix, I run it through a series of functions and I get an mx2 matrix. I use the unique function on the rows to get rid of repeated rows and I end up with an nx2 matrix. What I'm having trouble doing is further reducing this matrix so that for all entries in the first column that have the exact same entry, only keep the row with the highest number on the second column.
I was using a loop to check the ith and (i+1)th entries of the first column and store the rows with the highest value in the second column, but I am trying to avoid for-loops as much as possible.
If anyone has an idea or suggestion please let me know!
Example:
0 0 0 0
0 1 0 1 0 3
A= 0 3 ---> unique(A, 'rows') = 0 3 --WANT--> 1 1
1 0 1 0 2 4
1 0 1 1
0 0 2 1
2 1 2 4
1 1
2 4
What you are looking for is:
[u,~,n] = unique(A(:,1));
B = [u, accumarray(n, A(:,2), [], #max)];
I don't exactly understand your problem description, but it sounds like sortrows() may be of some help to you.