Applying repmat to each row of a matrix in Matlab with different inputs - matlab

I want to stack each row of a matrix A in Matlab a different number of times according to what is reported in the vector count. To do that I use repmat in the following way:
counts=[524282; 524286; 524283; 524290];
A=randn(4,19);
f=#() cell2mat(arrayfun(#(x) repmat(A(x,:),counts(x),1), 1:size(counts,1), 'UniformOutput', 0)');
timeit(f)
The code takes approx 0.45 sec.
Would you be able to suggest anything faster?

You could use repelem to repeat each element in the initial matrix a particular number of times
result = reshape(repelem(A(:), repmat(counts(:), size(A, 2), 1)), [], size(A, 2));
Explanation
A = [1, 2, 3; 4, 5, 6; 7, 8, 9];
counts = [1, 3, 2];
repelem (for vector inputs), repeats the ith element of the first input the number of times specified in the corresponding element of the second input.
repelem([1, 2, 3], [3, 4, 1])
% 1 1 1 2 2 2 2 3
Since repelem only supports vector inputs, we flatten A into a column vector using A(:). We also then need to craft the second input (the number of times to repeat each element of A) such that we repeat counts for each column of A.
nTimes = repmat(counts(:), size(A, 2), 1)
We then use repelem to perform the repetition
repeated = repelem(A(:), nTimes);
We then reshape the result to have the correct number of columns
reshape(repeated, [], size(A, 2))

A solution base on diff and cumsum:
co = size(A,2);
A_diff=diff([zeros(1,co);A]);
idx=cumsum([1;counts(1:end-1)]);
result=zeros(sum(counts),co);
result(idx,:)=A_diff;
result = cumsum(result)
The idea is using cumsum we can reconstruct a vector form the first difference of it.
a=[5 3 2 4];
d= diff([0 a]);
out = cumsum(d)
So out is equal to a.
We can make a matrix all of zeros of the desired size:
result=zeros(sum(count),co);
we should find indices of rows of each section
idx=cumsum([1;count(1:end-1)]);
set the first row of each section to first difference of the matrix A
A_diff=diff([zeros(1,co);A]);
result(idx,:)=A_diff;
so
A = randi(4,4,7);
count=[3 ;4 ;2 ;5];
A =
2 1 4 2 1 3 1
2 3 2 4 4 3 2
1 3 1 4 1 2 1
2 3 2 2 3 1 2
A_diff =
2 1 4 2 1 3 1
0 2 -2 2 3 0 1
-1 0 -1 0 -3 -1 -1
1 0 1 -2 2 -1 1
result =
2 1 4 2 1 3 1
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 2 -2 2 3 0 1
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
-1 0 -1 0 -3 -1 -1
0 0 0 0 0 0 0
1 0 1 -2 2 -1 1
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
apply cumsum to it to get the desired result
result = cumsum(result)
result =
2 1 4 2 1 3 1
2 1 4 2 1 3 1
2 1 4 2 1 3 1
2 3 2 4 4 3 2
2 3 2 4 4 3 2
2 3 2 4 4 3 2
2 3 2 4 4 3 2
1 3 1 4 1 2 1
1 3 1 4 1 2 1
2 3 2 2 3 1 2
2 3 2 2 3 1 2
2 3 2 2 3 1 2
2 3 2 2 3 1 2
2 3 2 2 3 1 2

Related

MATLAB - Inserting zero rows and columns into matrix

I have written some code that compresses a matrix to remove zero columns and rows, but I can't work out how to reconstruct the original matrix.
Say I have a matrix:
A = [ 0 3 0 2 1 0 6
3 0 0 4 8 0 5
0 0 0 0 0 0 0
2 4 0 0 2 0 1
1 8 0 2 0 0 7
0 0 0 0 0 0 0
6 5 0 1 7 0 0 ]
Here rows/columns 3 and 6 are empty, so my compression function will give the output:
A_dash = [ 0 3 2 1 6
3 0 4 8 5
2 4 0 2 1
1 8 2 0 7
6 5 1 7 0 ]
A_map = [ 1 2 4 5 7]
Where A_map is a vector mapping the indicies of the rows/columns of A_dash to A. This means that if A_map(3) = 4, then row/column 4 of A is the same as row/column 3 of A_dash - ie. a row/column of zeroes must be inserted between columns/rows 2 and 3 in A_dash
What is the easiest way people can suggest for me to recreate matrix A from A_dash, using the information in A_map?
Here is what I have got so far:
% orig_size is original number of columns/rows
c_count = size(A_dash,1);
A = zeros(c_count, orig_size); % c_count rows to avoid dimension mismatch
for ii = 1:c_count
A(:,A_map(ii)) == A_dash(:,ii);
end
This gives me the right result column-wise:
A = [ 0 3 0 2 1 0 6
3 0 0 4 8 0 5
2 4 0 0 2 0 1
1 8 0 2 0 0 7
6 5 0 1 7 0 0 ]
However, I'm not sure how i should go about inserting the rows, i suppose i could copy the first 1:i rows into one matrix, i:end rows to a second matrix and concatenate those with a zero row in between, but that feels like a bit of a
clunky solution, and probably not very efficient for large sized matrices..
Otherwise, is there a better way that people can suggest I store the map information? I was thinking instead of storing the mapping between column/row indices, that I just store the indices of the zero columns/rows and then insert columns/rows of zeros where appropriate. Would this be a better way?
You've got the indices of the valid rows/columns. Now all you've got to do is put them in a new matrix of zeros the same size as A:
B=zeros(size(A));
B(A_map,A_map)=A_dash
B =
0 3 0 2 1 0 6
3 0 0 4 8 0 5
0 0 0 0 0 0 0
2 4 0 0 2 0 1
1 8 0 2 0 0 7
0 0 0 0 0 0 0
6 5 0 1 7 0 0
Just to check...
>> A==B
ans =
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1
A and B are equal everywhere, so we've reconstructed A.

Finding the non-intersecting rows in a matrix

I want to find the non-intersecting rows in a large matrix. As an example:
A=[1 5 3; 3 4 5; 7 9 10;4 5 6;11 2 8; 3 5 10]
In this matrix, the non-intersecting rows are: [1 5 3], [11 2 8] and [7 9 10]. How can I program this in Matlab in a fast way?
If I may bsxfun -
M = squeeze(any(bsxfun(#eq,A,permute(unique(A),[3 2 1])),2))
[~,row_idx] = max(M,[],1)
out = A(sum(M,2).' == histc(row_idx,1:size(A,1)),:)
Sample step-by-step run -
A =
1 5 3
3 4 5
7 9 10
4 5 6
11 2 8
3 5 10
M =
1 0 1 0 1 0 0 0 0 0 0
0 0 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 0 1 1 0
0 0 0 1 1 1 0 0 0 0 0
0 1 0 0 0 0 0 1 0 0 1
0 0 1 0 1 0 0 0 0 1 0
row_idx =
1 5 1 2 1 4 3 5 3 3 5
out =
1 5 3
7 9 10
11 2 8
You can look for rows that adding them to union of previous rows increases the number of elements in the union by the number of columns (i.e. all elements in that row are new):
B = [];
C = zeros(1,size(A,1));
for k=1:size(A,1),
B1 = union(B, A(k,:));
C(k) = numel(B1)-numel(B);
B=B1;
end
result = A(C==size(A,2),:);

Generating a matrix containing 3 numbers

I am trying to create an 8x8 matrix containing 0s, 1s and 2s. Each row and each column should contain two 0s, three 1s and three 2s.
Previously I have used the below to generate an example containing only 1s and 0s.
output = zeros(8, 8);
for i=1:8
tmp = (1:8) + (i);
tmp = rem(tmp, 4);
output(i,:) = tmp;
output(i,:) = tmp > 0;
end
output =
1 1 0 1 1 1 0 1
1 0 1 1 1 0 1 1
0 1 1 1 0 1 1 1
1 1 1 0 1 1 1 0
1 1 0 1 1 1 0 1
1 0 1 1 1 0 1 1
0 1 1 1 0 1 1 1
1 1 1 0 1 1 1 0
However I would now like something similar to the following:
output =
1 1 0 1 2 2 0 2
1 0 1 2 2 0 2 1
0 1 2 2 0 2 1 1
1 2 2 0 2 1 1 0
2 2 0 2 1 1 0 1
2 0 2 1 1 0 1 2
0 2 1 1 0 1 2 2
2 1 1 0 1 2 2 0
Thanks for your help.
What you have in your example is a Hankel matrix so you could use the hankel function
c = [1 1 0 1 2 2 0 2];
k = [2 1 1 0 1 2 2 0];
A = hankel(c,k)
where c is the first column of the output matrix and k is the last row.
Making your output matrix a Hankel matrix is a good idea (based on your requirements) as it will enforce the row and column frequency counts for each value. You would not necessarily get this just by creating rows that are random permutations of a base row (using randperm for example) as duplicate rows would be possible which would break your column requirements.
As an example, if you want random c with fixed numbers of specific elements, you can randomly permute a base vector containing the required values and frequencies - as per your requirement this would be
c = [0 0 1 1 1 2 2 2];
index = randperm(numel(c));
c = c(index);
c =
0 2 0 2 2 1 1 1
To get the square Hankel structure then choose k to be the next cyclic permutation of c
k = circshift(c',1)'
k =
1 0 2 0 2 2 1 1
and just call hankel with these as mentioned above
A = hankel(c,k)
A =
0 2 0 2 2 1 1 1
2 0 2 2 1 1 1 0
0 2 2 1 1 1 0 2
2 2 1 1 1 0 2 0
2 1 1 1 0 2 0 2
1 1 1 0 2 0 2 2
1 1 0 2 0 2 2 1
1 0 2 0 2 2 1 1
The above output is based on what I got on my machine based on the output from randperm.
Any output matrix generated using the above will meet your requirements specified in the question.

Get all vector elements between different indices

I have one vector of starting points (start) and another vector for end points (end):
start=[1 0 0 0 1 0 0 0 1 0 0 0]
end= [0 0 1 0 0 0 1 0 0 0 1 0]
I want a third vector A all the numbers between each start and endpoint.
A = [1 2 3 4 5 6 7 8 9 1 2 3]
so the result for this example could look like this:
A_result= [1 2 3 5 6 7 9 1 2]
any ideas?
Without loop:
s=[1 0 0 0 1 0 0 0 1 0 0 0];
e=[0 0 1 0 0 0 1 0 0 0 1 0];
mask = cumsum(s)-cumsum([shift(e,1)])
# Will be [1 1 1 0 1 1 1 0 1 1 1 0]
A = [1 2 3 4 5 6 7 8 9 1 2 3];
A(find(mask))
# will be [1 2 3 5 6 7 9 1 2]
Or as The Minion pointed out, simply:
A(mask==1)
# will be [1 2 3 5 6 7 9 1 2]
Check the solution from seb it is way faster for large arrays.
This is a solution using a for-loop
d_start=[1 0 0 0 1 0 0 0 1 0 0 0];
d_end= [0 0 1 0 0 0 1 0 0 0 1 0];
A = [1 2 3 4 5 6 7 8 9 1 2 3];
starting_idx = find(d_start);
ending_idx = find(d_end);
A_result = [];
for l=1:numel(starting_idx)
A_result=horzcat(A_result, A(starting_idx(l):ending_idx(l)));
end
What I am doing is: 1. I get the index of the starting and end points (find). Then i predefine my result. Now i just have to loop over the number of entries in the starting points and add those values from that position to next ending to my result.
Edit: Timing test:
I used my solution as well as the solution from seb (modified to my variable names):
mask = cumsum(d_start)-cumsum([0 d_end(1:end-1)]);
B_res =A(find(mask));
as well as my comment to his solution:
mask = cumsum(d_start)-cumsum([0 d_end(1:end-1)]);
B_res =A(mask==1);
These were the timing results for A:[12888x1]double
t_forloop : 33 sec
t_seb_find: 0.31 sec
t_seb_logical: 0.1964 sec

Matrix creation Octave / Matlab, loopless solution request

I want to create a matrix like
A = [0 0 0 0 1;
0 0 0 1 1;
0 0 0 1 1;
0 0 0 1 1;
0 0 1 1 1;
0 1 1 1 1]
based on a vector indicating how many '0's should precede '1's on each row:
B = [4 3 3 3 2 1]
Is there a loopless way to do this ?
You don't mention in your question how the horizontal size of the array should be defined (the number of ones).
For predefined width you can use this code:
width = 5;
A = cell2mat(arrayfun(#(x) [ zeros(1,x), ones(1,width-x) ], B, 'UniformOutput', false)');
If you want that A has minimal width, but still at least one 1 in every row:
A = cell2mat(arrayfun(#(x) [ zeros(1,x), ones(1,max(B)+1-x) ], B, 'UniformOutput', false)');
A shorter “old-school” way to achieve this without a loop would be as follows:
A = repmat(B',1,max(B)+1)<repmat([1:max(B)+1],size(B,2),1)
If you want to have a minimum number of ones
min_ones=1; %or whatever
A = repmat(B',1,max(B)+min_ones)<repmat([1:max(B)+min_ones],size(B,2),1)
I don’t know how this compares speedwise to #nrz’s approach (I’ve only got Octave to hand right now), but to me it's more intuitive as it’s simply comparing a max(B) + min_ones * column tiling of B:
4 4 4 4 4
3 3 3 3 3
3 3 3 3 3
3 3 3 3 3
2 2 2 2 2
1 1 1 1 1
with a row tiling of [1 : max(B) + min_ones]
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
To generate:
A =
0 0 0 0 1
0 0 0 1 1
0 0 0 1 1
0 0 0 1 1
0 0 1 1 1
0 1 1 1 1
This requires only one line, and seems to be faster than previous solutions based on repmat or arrayfun:
%// Example data
ncols = 5;
B = [4 3 3 3 2 1];
%// Generate A
A = bsxfun(#gt, 1:ncols, B(:));