bsxfun rows subtraction in loop - matlab

I have a text file D with 98 rows and 2 columns. looks like this:
10 0.261344
11 0.456167
12 0.668595
2 0.481754
... etc
I have another excel file M with 17 rows and 2 col.
I want to subtract all rows values from the 1st row, then from 2, etc.so i get something like this:
10-11 -0.194823
10-12 -0.407251
... etc
in other words difference between 1st row and all rest, 2nd row and all rest ..excluding those rows of which 1st column has same values as in 2nd M excel file 1st column. which makes in total difference compare to 81 rows.
i have been trying to use this:
M = xlsread('...');
FSumID=fopen(D);
MatrixSub = loadD);
m = textscan(FSumID,'%d %f');
horzcat(m{:})
NewData = M(:,1)
D_size=size(D);
for i=1:D_size
if (ptvBodyDist(:,1)~=NewData(i))
%for calculating
DVec = bsxfun(#minus, ((m{i,2})), (m{i,2}));
%when i check this it gives only one loop all zeros(98,1)while i need (((98-17)^2,1). I need to change,last part of expression to get what i want but I'm stuck with bsxfun. i dont know how to make correct expression to subtract all rows.
I'm newbie in Matlab any help would be much appreciated. also if you could suggest some manuals or tutorials,apart from mathworks help. Many thanks.

Let D be n by 2 matrix (data from text file).
Let M by m by 2 matrix (data from excel file).
The first stage is to find the relevant n-m rows of D: rows that their first value does not appear in M, using setdiff.
[~, idx] = setdiff( D( :, 1 ), M( :, 1 ), 'stable' );
Now we can compute the all-vs-all diff using bsxfun
d = bsxfun( #minus, permute( D( idx, : ), [1 3 2] ), ...
permute( D( idx, : ), [3 1 2] ) );
Now d is |idx|x|idx|x2 array where
d( ii, jj, : ) = D( idx(ii), :) - D( idx(jj), : )

Related

Grouping by nested unique values

I have a matrix A in Matlab:
A = [176 5406 1 4 7903;
155 5406 1 5 7903;
122 5407 0 4 7903;
140 5407 0 5 7904;
130 5407 0 3 7904];
Just for information - the second column is a user ID, while the fourth column is a time. So 5406 is one user and 5407 is another user. Both of these users have some information stored in the first column and the 4th column which I am interested in accessing.
So basically what I want to do is:
For each user take the median of their values in the first column. I have written code (below) that works for this.
If there are two equal "time" values in column 5 for each user then I want to average the values in column 4. So like for user 5406 the time values are both 7903, I want to the average of values in column 4 - i.e. the average of 4 and 5 to end up with one value (4.5).
But for example for the next user 5407 I will have two final values - one will be the average of 5 and 3 (because 7904 is repeated) and one will be 4 (because 7903 is not repeated).
I am a bit confused about how to do this, I know there needs to be an if statement of some sort, but I've been stuck on it for ages. Can anyone help?
Thanks
Code for the first part:
u=unique(A(:,2));
for i=1:size(u,1)
M=find(A(i,2)==u(i));
med(i)=median(A(M,1));
end
You could run unique for the time values of each user (within the loop) and do a similar sub loop to collect the mean of unique timestamp for that user.
But here I think it's neater to use accumarray. In first example below, I've modified your code just a bit.
% Get unique
[user, ~, userIdx] = unique(A(:,2));
nUser = numel(user);
% Allocate container for result
med = zeros(nUser,1);
men = cell(nUser,1); % <-- Need a cell since length of result could vary
for i = 1:nUser
% Median of col #1
med(i) = median(A(userIdx == i, 1));
% Mean of col #4 for unique times
[~, ~, timeIdx] = unique(A(userIdx == i, 5));
men{i} = accumarray(timeIdx, A(userIdx == i, 4), [], #mean);
end
Result:
>> med =
165.5
130
>> celldisp(men)
men{1} =
4.5
men{2} =
4
4
To squeeze it a bit more, you could take unique time for entire A and use accumarray for both
[~, ~, userIdx] = unique(A(:,2));
[~, ~, timeIdx] = unique(A(:,5));
med = accumarray(userIdx, A(:,1), [], #median);
men = accumarray([userIdx timeIdx], A(:,4), [], #mean, NaN);
This gives men not as a cell but a matrix. Therefore the blank spaces has to be filled (here I choose NaN since 0 could be a result of #mean).
men =
4.5 NaN
4 4
If you want it as a cell without NaN you could just loop over the rows and pick non-NaN values, or place only the men calculation in the loop, or any other way...
If you are sure that column 4 of A doesn't contain any negative or zero numbers (mean value should never risk being 0), you could collect the result of men as a sparse matrix instead
men = accumarray([userIdx timeIdx], A(:,4), [], #mean, 0, true);
men =
(1,1) 4.5
(2,1) 4
(2,2) 4
I got another solution for your task without using any loops:
Median values.
u=unique(A(:,2));
umedians = arrayfun( #(x) median (A( A(:,2)==x, 1)), u);
Explanation:
find all unique users first. Then using arrayfun to find all data for current user and calculate median for every one of them.
Average values of column 4.
This task is a bit harder. We can go this way:
temp = arrayfun( #(x) unique(A ( A(:,2)==x,5 )), u, 'UniformOutput',false);
result = cellfun( #(y,z) arrayfun( #(x) mean( A( A(:,2) == u(z) & A(:,5) == x ,4) ), ...
y, 'UniformOutput',false), temp , num2cell( [1:size(u,1)]'), 'UniformOutput',false)
Explanation: first of all lets find all unique times for each users. Save it to cell array temp. Now we need for each cell find the same times and calculate mean. So lets use cellfun to made it for each cell of temp and use arrayfun into it to calculate mean.
Hope it helps!

Shifting repeating rows to a new column in a matrix

I am working with a n x 1 matrix, A, that has repeating values inside it:
A = [0;1;2;3;4; 0;1;2;3;4; 0;1;2;3;4; 0;1;2;3;4]
which correspond to an n x 1 matrix of B values:
B = [2;4;6;8;10; 3;5;7;9;11; 4;6;8;10;12; 5;7;9;11;13]
I am attempting to produce a generalised code to place each repetition into a separate column and store it into Aa and Bb, e.g.:
Aa = [0 0 0 0 Bb = [2 3 4 5
1 1 1 1 4 5 6 7
2 2 2 2 6 7 8 9
3 3 3 3 8 9 10 11
4 4 4 4] 10 11 12 13]
Essentially, each repetition from A and B needs to be copied into the next column and then deleted from the first column
So far I have managed to identify how many repetitions there are and copy the entire column over to the next column and then the next for the amount of repetitions there are but my method doesn't shift the matrix rows to columns as such.
clc;clf;close all
A = [0;1;2;3;4;0;1;2;3;4;0;1;2;3;4;0;1;2;3;4];
B = [2;4;6;8;10;3;5;7;9;11;4;6;8;10;12;5;7;9;11;13];
desiredCol = 1; %next column to go to
destinationCol = 0; %column to start on
n = length(A);
for i = 2:1:n-1
if A == 0;
A = [ A(:, 1:destinationCol)...
A(:, desiredCol+1:destinationCol)...
A(:, desiredCol)...
A(:, destinationCol+1:end) ];
end
end
A = [...] retrieved from Move a set of N-rows to another column in MATLAB
Any hints would be much appreciated. If you need further explanation, let me know!
Thanks!
Given our discussion in the comments, all you need is to use reshape which converts a matrix of known dimensions into an output matrix with specified dimensions provided that the number of elements match. You wish to transform a vector which has a set amount of repeating patterns into a matrix where each column has one of these repeating instances. reshape creates a matrix in column-major order where values are sampled column-wise and the matrix is populated this way. This is perfect for your situation.
Assuming that you already know how many "repeats" you're expecting, we call this An, you simply need to reshape your vector so that it has T = n / An rows where n is the length of the vector. Something like this will work.
n = numel(A); T = n / An;
Aa = reshape(A, T, []);
Bb = reshape(B, T, []);
The third parameter has empty braces and this tells MATLAB to infer how many columns there will be given that there are T rows. Technically, this would simply be An columns but it's nice to show you how flexible MATLAB can be.
If you say you already know the repeated subvector, and the number of times it repeats then it is relatively straight forward:
First make your new A matrix with the repmat function.
Then remap your B vector to the same size as you new A matrix
% Given that you already have the repeated subvector Asub, and the number
% of times it repeats; An:
Asub = [0;1;2;3;4];
An = 4;
lengthAsub = length(Asub);
Anew = repmat(Asub, [1,An]);
% If you can assume that the number of elements in B is equal to the number
% of elements in A:
numberColumns = size(Anew, 2);
newB = zeros(size(Anew));
for i = 1:numberColumns
indexStart = (i-1) * lengthAsub + 1;
indexEnd = indexStart + An;
newB(:,i) = B(indexStart:indexEnd);
end
If you don't know what is in your original A vector, but you do know it is repetitive, if you assume that the pattern has no repeats you can use the find function to find when the first element is repeated:
lengthAsub = find(A(2:end) == A(1), 1);
Asub = A(1:lengthAsub);
An = length(A) / lengthAsub
Hopefully this fits in with your data: the only reason it would not is if your subvector within A is a pattern which does not have unique numbers, such as:
A = [0;1;2;3;2;1;0; 0;1;2;3;2;1;0; 0;1;2;3;2;1;0; 0;1;2;3;2;1;0;]
It is worth noting that from the above intuitively you would have lengthAsub = find(A(2:end) == A(1), 1) - 1;, But this is not necessary because you are already effectively taking the one off by only looking in the matrix A(2:end).

find row indices of different values in matrix

Having matrix A (n*2) as the source and B as a vector containing a subset of elements A, I'd like to find the row index of items.
A=[1 2;1 3; 4 5];
B=[1 5];
F=arrayfun(#(x)(find(B(x)==A)),1:numel(B),'UniformOutput',false)
gives the following outputs in a cell according to this help page
[2x1 double] [6]
indicating the indices of all occurrence in column-wise. But I'd like to have the indices of rows. i.e. I'd like to know that element 1 happens in row 1 and row 2 and element 5 happens just in row 3. If the indices were row-wise I could use ceil(F{x}/2) to have the desired output. Now with the variable number of rows, what's your suggested solution? As it may happens that there's no complete inclusion tag 'rows' in ismember function does not work. Besides, I'd like to know all indices of specified elements.
Thanks in advance for any help.
Approach 1
To convert F from its current linear-index form into row indices, use mod:
rows = cellfun(#(x) mod(x-1,size(A,1))+1, F, 'UniformOutput', false);
You can combine this with your code into a single line. Note also that you can directly use B as an input to arrayfun, and you avoid one stage of indexing:
rows = arrayfun(#(x) mod(find(x==A)-1,size(A,1))+1, B(:), 'UniformOutput', false);
How this works:
F as given by your code is a linear index in column-major form. This means the index runs down the first column of B, the begins at the top of the second column and runs down again, etc. So the row number can be obtained with just a modulo (mod) operation.
Approach 2
Using bsxfun and accumarray:
t = any(bsxfun(#eq, B(:), reshape(A, 1, size(A,1), size(A,2))), 3); %// occurrence pattern
[ii, jj] = find(t); %// ii indicates an element of B, and jj is row of A where it occurs
rows = accumarray(ii, jj, [], #(x) {x}); %// group results according to ii
How this works:
Assuming A and B as in your example, t is the 2x3 matrix
t =
1 1 0
0 0 1
The m-th row of t contains 1 at column n if the m-th element of B occurs at the n-th row of B. These values are converted into row and column form with find:
ii =
1
1
2
jj =
1
2
3
This means the first element of B ocurrs at rows 1 and 2 of A; and the second occurs at row 3 of B.
Lastly, the values of jj are grouped (with accumarray) according to their corresponding value of ii to generate the desired result.
One approach with bsxfun & accumarray -
%// Create a match of B's in A's with each column of matches representing the
%// rows in A where there is at least one match for each element in B
matches = squeeze(any(bsxfun(#eq,A,permute(B(:),[3 2 1])),2))
%// Get the indices values and the corresponding IDs of B
[indices,B_id] = find(matches)
%// Or directly for performance:
%// [indices,B_id] = find(any(bsxfun(#eq,A,permute(B(:),[3 2 1])),2))
%// Accumulate the indices values using B_id as subscripts
out = accumarray(B_id(:),indices(:),[],#(x) {x})
Sample run -
>> A
A =
1 2
1 3
4 5
>> B
B =
1 5
>> celldisp(out) %// To display the output, out
out{1} =
1
2
out{2} =
3
With arrayfun,ismember and find
[r,c] = arrayfun(#(x) find(ismember(A,x)) , B, 'uni',0);
Where r gives your desired results, you could also use the c variable to get the column of each number in B
Results for the sample input:
>> celldisp(r)
r{1} =
1
2
r{2} =
3
>> celldisp(c)
c{1} =
1
1
c{2} =
2

Group index vector from vector with nummer of elements in group

I want to create a vector containing the group identifier for each element from a vector containing the number of elements in each group.
Example:
E = [2 3 4]'
I am looking for a vector as follows:
I = [1 1 2 2 2 3 3 3 3]
I found one solution involving a loop:
I = [];
for e=1:size(E,1),
I = [I ; e*ones(E(e),1)];
end
But this doesn't seem very elegant. Any advice for improvements are welcome.
You are looking for run length decoding.
Try this
n = sum( E ); %// tot number of elelments
I = zeros( 1, n ); % //preallocate!
I(cumsum( [ 1 E(1:end-1) ] ) ) = 1;
I = cumsum( I )
See a running example at ideone.
Try this:
X = arrayfun(#(x) [1 zeros(1,x-1)], E, 'uni',0)
Y = cumsum( [X{:}] )
I had a similar problem. I think, without using a for-loop, it's not as simple as one could think.
Here is my solution:
I = cell2mat(arrayfun(#(x) repmat(x,E(x),1),1:numel(E),'UniformOutput',false)')
Some explanation:
x is the Index of E.
The array-function "iterates" from 1 to numel(E). repmat replicates the index x E(x) times.
The Output of the array-function is a 1x3-Cell array with vectors [11], [222] and [3333]. Because of the different vector sizes, I have to set the Uniformoutput to false. But I want to concatenate these nested vectors, so I use cell2mat(...)

construct a binary matrix such that each column contains only single "1" and sum of each row is of desired value

I want to construct a binary(0s and 1s) matrix satisfying the following constraints:
Each column must contains only single binary 1 and rest elements of that column are 0s.
The sum of each ROW of matrix should be a desired value.
For example, given a rowSum vector of [5 7 6 8 .......] then the sum of first row should be 5, sum of second row should be 7 and so on.
nCol==Sum(rowSum)
Moreover, I would like to consider several (e.g., 7) matrices satisfying the same conditions.
EDIT:
I have tried to write the code and completed the one part of it. The code is:
x=rand(21,50,7);
for k=1:7
cons=max(x(:,:,7));
for i=1:50
for j=1:21
if x(j,i,k)==cons(i)
x(j,i,k)=1;
else
x(j,i,k)=0;
end
end
end
end
x
It would not always be possible to construct a binary matrix that satisfies your requirements.
Suppose you want a binary matrix of size nRowsxnCols with rowSum (a vector of length nRows) rowSum(k) the number of 1s in kth row.
So, if nCol ~= sum( rowSum ) it would be impossible to construct such matrix: you would either have columns with no 1s, or columns with too many 1s...
Therefore, your binary matrix is completely defined through rowSum - up to random permutation of its columns.
How about this function to construct the basic matrix b:
function b = makeBizarreBinaryMatrix( rowSum )
nRows = numel( rowSum );
nCols = sum( rowSum );
rows = 1:nRows;
rowInd = zeros( 1, nCols );
rowInd( cumsum( [1 rowSum(1:end-1)] ) ) = 1;
rowInd = rows( cumsum( rowInd ) );
b = sparse( rowInd, 1:nCols, 1, nRows, nCols );
Now you can use randperm to randomly permute the order of the columns:
nb = b(:, randperm(size(b,2)) );
Good luck with your thesis.