Hi I have a correlation Matrix:
A = [1 2 1 3 1 2 4 3 5 1;
2 3 4 5 6 6 6 7 7 8];
I need to find out how many times each individual element of row 1 is linked to elements in row 2.
For example, here element 1 in row 1 is related to the following elements of row 2, {2, 4, 6, 8}, so total 4 elements.
Similarly 2 is linked to {3, 6}, for a total of 2 elements.
Resulting Matrix C should be:
[element name in 1st row; Number of connection].
In previous example, C = [1 2 ....; 4 2 ...];
Since actual matrix size is of the order of 1000s', it is impossible to do manually. Any help will be appreciated.
There is probably a way to do this without resorting to a for loop but this is one solution I can think of right now. Identify the unique elements in the first row of matrix A and loop over all the elements to find the ones they are linked to in the second row.
I have assumed that you only need to identify the unique elements that the first row is linked to, hence the unique() function inside the for loop; if that is not the case, please remove that from the code.
a = [1 2 1 3 1 2 4 3 5 1; 2 3 4 5 6 6 6 7 7 8];
row1el = unique(a(1, :));
c = zeros(2, length(row1el));
for i = 1:length(row1el)
idx = a(1, :) == row1el(i);
linkedEl = a(2, idx);
c(1, i) = row1el(i);
c(2, i) = length(unique(linkedEl));
end
disp(c)
If I understood the question properly, you are not really concerned about the values in the second row, but the number of occurrences of the elements in row 1. This can be obtained with the unique and histc functions:
C(1,:)=unique(A(1,:));
C(2,:)=histc(A(1,:),C(1,:));
C =
1 2 3 4 5
4 2 2 1 1
The answer depends on whether repeated columns should be counted repeatedly or not. Consider the following data:
A = [1 2 1 3 1 2 4 3 5 1;
2 3 4 5 6 6 6 5 7 8]; %// col [3;5] appears twice
If repeated columns should be counted according to their multiplicity: you can use accumarray:
[ii, ~, kk] = unique((A(1,:)));
jj = accumarray(kk.', A(2,:).', [], #(x) numel(x)).';
C = [ii; jj];
Result with my example A:
C =
1 2 3 4 5
4 2 2 1 1
Or you can use sparse:
[~, ii, jj] = find(sum(sparse(A(2,:), A(1,:), 1)));
C = [ii; jj];
The result is the same as above.
If repeated columns should be counted just once: either of the two approaches is easily adapted to this case:
[ii, ~, kk] = unique((A(1,:)));
jj = accumarray(kk.', A(2,:).', [], #(x) numel(unique(x))).'; %'// note: "unique"
C = [ii; jj];
or
[~, ii, jj] = find(sum(sparse(A(2,:), A(1,:), 1) > 0)); %// note: ">0"
C = [ii; jj];
Result (note third column is different than before):
C =
1 2 3 4 5
4 2 1 1 1
Related
I have a matrix that I'd like to create a new ordering of, for example,
vals = [1 2; 3 4]
I also have two matrices, new_x and new_y such that new_x(a,b) = j and new_x(a,b) = k means that I want the value at vals (a,b) to be mapped to new_vals(j,k).
For example, given
new_x = [1 2; 2 1]
new_y = [2 2; 1 1]
I'd want
new_vals = [4 3; 1 2]
I understand that I could just write two for loops to build the new array, but matlab is notoriously good at providing operations on entire matricies. My question is, how would I build new_vals without the for loops?
Basically you are trying to get a matrix that when indexed with new_x and new_y would give us vals, i.e. -
output(new_x(1,1),new_y(1,1)) must be equal to vals(1,1),
output(new_x(1,2),new_y(1,2)) must be equal to vals(1,2) and so on.
We will try to verify this later on. For now, here's one solution using linear indexing -
nrows = size(vals,1); %// Store number of rows
%// Calculate linear indices
idx = (new_x + (new_y-1)*nrows);
%// Trace/map back to sorted version of "1:numel(vals)"
[~,traced_back_idx] = sort(idx(:));
%// Index into vals with traced back linear indices & then reshape & transpose
out = reshape(vals(traced_back_idx),[],nrows).'
Here's another and possibly faster way -
out = nan(size(vals));
out((new_x + (new_y-1)*nrows)) = vals;
out = out.'
As discussed earlier for verification, let's index into out with new_x and new_y and that should match up with vals. Here's a code to do so -
for ii = 1:size(out,1)
for jj = 1:size(out,2)
check_back(ii,jj) = out(new_y(ii,jj),new_x(ii,jj));
end
end
Sample runs -
Case #1 (sample from question):
vals =
1 2
3 4
new_x =
1 2
2 1
new_y =
2 2
1 1
new_vals =
4 3
1 2
out =
4 3
1 2
check_back = (must be same as vals)
1 2
3 4
Case #2:
vals =
1 2 5
3 4 5
6 8 3
new_x =
1 2 3
3 1 2
3 2 1
new_y =
2 2 3
2 1 1
1 3 3
out =
4 5 6
1 2 3
3 8 5
check_back = (must be same as vals)
1 2 5
3 4 5
6 8 3
I think i see what you are trying to do here. new_x and new_y are just coordinates for the new_val matrix rigth? The problem is tha what you are trying to do only works for vectors, not for matrix, so the only way is to transform the matrix into a vector, reorder the values and then go back to matrix like:
vals = [1 ,2; 3, 4];
A=reshape(vals,1,4); % A is a vector [ 1 3 2 4]
new_coord=[2,3,4,1];
B(new_c)=A; %B is [4 1 3 2]
new_val=reshape(B,2,2) %back to matrix
Obtainig new_val=[4 3; 1 2]. Also B=A(new_c) is also allowed but with different coordinates, eventhoug is much easy to think the rigth coordinates in that way.
I am sure there must be a way to include the new_x matrix and transform everything into new_coord
Given a vector such as a = [2 5 9] and a matrix such as
8 11 5
b = 2 6 1
4 9 3
What's the best way to find which column of b contains each element of a? In this example I'd want an output like [1 3 2] because 2 is in the first column, 5 is in the third column, and 9 is in the second column. For my purposes it's safe to assume that a number can only appear in one column.
One approach -
[colID,~] = find(squeeze(any(bsxfun(#eq,b,permute(a,[1 3 2])),1)))
Or if you would like to avoid squeeze and any -
[~,colID,~] = ind2sub([size(b) numel(a)],find(bsxfun(#eq,b(:),a)))
Another way would be to use ismember:
A = [2 5 9];
B = [8 11 5; 2 6 1; 4 9 3];
[~, ind] = ismember(A,B);
[~, col] = ind2sub(size(B), ind)
col =
1 3 2
Another approach:
[~, index] = ismember(a, b);
[row, col] = ind2sub(size(b, 1), index);
I’ve a matrix A = (4*4) and a cell array B {4,1}. I’d like to find all the values of B in A, searching row by row and after I’d like to delete the correspondent column associated to this particular value. I’ve a problem using bsxfun o cellfun and find function with a matrix and a cell array. I‘ve tried to convert the cell array into a matrix but I don’t have more the exact correspondence.
For example:
A =
1 5 10 23
2 4 2 18
3 3 5 14
1 9 10 4
B =
1
2 4
3 3 14
1
To obtain:
C =
10
2
5
10
Thanks in advance,
L.
Here's how:
C = cellfun(#(x, y){sparse(1,find(ismember(x,y),numel(y)),true,1,size(A,2))}, ...
mat2cell(A, ones(size(A, 1), 1), size(A, 2)), B(:));
C = A(:, all(~vertcat(C{:})));
The cellfun is fed with two cell arrays: the first one contains the rows of A and second one is B. The anonymous function is the tricky part; it operates on a pair of two corresponding rows as follows:
It employs ismember to check which columns in A contain any of the elements in B.
It uses find to pick only the first N ones, with respect to the number of elements in the B.
It uses sparse as a fancy way of zeroing out the rest of the elements.
For your example it would look like this:
A = [1 5 10 23; 2 4 2 18; 3 3 5 14; 1 9 10 4];
B = {1; [2 4]; [3 3 14]; 1};
C = cellfun(#(x, y){sparse(1,find(ismember(x,y),numel(y)),true,1,size(A,2))}, ...
mat2cell(A, ones(size(A, 1), 1), size(A, 2)), B(:));
which yields:
C =
{
[1 0 0 0]
[1 1 0 0]
[1 1 0 1]
[1 0 0 0]
}
After that, it's a simple matter of logical indexing to pick the resulting columns:
C = A(:, all(~vertcat(C{:})));
which in this case should be:
C =
10
2
5
10
just lets make it simple, assume that I have a 10x3 matrix in matlab. The numbers in the first two columns in each row represent the x and y (position) and the number in 3rd columns show the corresponding value. For instance, [1 4 12] shows that the value of function in x=1 and y=4 is equal to 12. I also have same x, and y in different rows, and I want to average the values with same x,y. and replace all of them with averaged one.
For example :
A = [1 4 12
1 4 14
1 4 10
1 5 5
1 5 7];
I want to have
B = [1 4 12
1 5 6]
I really appreciate your help
Thanks
Ali
Like this?
A = [1 4 12;1 4 14;1 4 10; 1 5 5;1 5 7];
[x,y] = consolidator(A(:,1:2),A(:,3),#mean);
B = [x,y]
B =
1 4 12
1 5 6
Consolidator is on the File Exchange.
Using built-in functions:
sparsemean = accumarray(A(:,1:2), A(:,3).', [], #mean, 0, true);
[i,j,v] = find(sparsemean);
B = [i.' j.' v.'];
A = [1 4 12;1 4 14;1 4 10; 1 5 5;1 5 7]; %your example data
B = unique(A(:, 1:2), 'rows'); %find the unique xy pairs
C = nan(length(B), 1);
% calculate means
for ii = 1:length(B)
C(ii) = mean(A(A(:, 1) == B(ii, 1) & A(:, 2) == B(ii, 2), 3));
end
C =
12
6
The step inside the for loop uses logical indexing to find the mean of rows that match the current xy pair in the loop.
Use unique to get the unique rows and use the returned indexing array to find the ones that should be averaged and ask accumarray to do the averaging part:
[C,~,J]=unique(A(:,1:2), 'rows');
B=[C, accumarray(J,A(:,3),[],#mean)];
For your example
>> [C,~,J]=unique(A(:,1:2), 'rows')
C =
1 4
1 5
J =
1
1
1
2
2
C contains the unique rows and J shows which rows in the original matrix correspond to the rows in C then
>> accumarray(J,A(:,3),[],#mean)
ans =
12
6
returns the desired averages and
>> B=[C, accumarray(J,A(:,3),[],#mean)]
B =
1 4 12
1 5 6
is the answer.
I tried to solve this problem, but I could not implement.
Could you help me anything for this?
Problem
Mat1 | Mat2 | Mat3
1 2 | 1 3 | 2 6
1 3 | 2 6 | 2 5
2 4 | 3 1 | 3 1
3 1 | 3 5 | 5 2
4 5 |
When there are 3 matrices(for example above), I want to get this result for the intersection rows in [column1 column2 matrixnumber] form.
The result for above example would be
1 3 1
1 3 2
2 6 2
2 6 3
3 1 1
3 1 2
3 1 3
It would be OK if the result is in the form [column1 column2 firstmatrix secondmatrix, ...]
1 3 1 2
2 6 2 3
3 1 1 2 3
For this problem, I want to use at most one for-loop.
Do you have any idea for this?
Here an alternative solution (which seems to run faster than Gunther's) using MATLAB's intersect:
Mat = {[1 2; 1 3; 2 4; 3 1; 4 5],
[1 3; 2 6; 3 1; 3 5],
[2 6; 2 5; 3 1; 5 2]};
result = zeros(sum(cellfun(#(x)size(x, 1), Mat)), 3); % # Preallocate memory
k = 1;
for cc = transpose(nchoosek(1:numel(Mat), 2))
x = intersect(Mat{cc}, 'rows'); % # Find intersection
y = ones(size(x, 1), 2) * diag(cc); % # Generate matrix indices
result(k:k + numel(y) - 1, :) = [[x; x], y(:)];
k = k + numel(y);
end
result(all(~result, 2), :) = []; % # Discard zero rows
result = unique(result, 'rows'); % # Discard repeated rows
The matrix result should now contain the unique intersection rows and their corresponding matrix indices, just like you want:
result =
1 3 1
1 3 2
2 6 2
2 6 3
3 1 1
3 1 2
3 1 3
If I understand correctly, you have a number of sets of pairs: Mat1,Mat2, Mat3, ... MatN. Now you want to find the unique pairs and then find out in which set every unique pair appears.
If you have a large number of sets, I suggest you start using a cell array to hold them all, makes things a lot easier:
N = 3; % total number of data sets
Mat = cell(N,1);
Mat{1} = [1 2;
1 3;
2 4;
3 1;
4 5];
Mat{2} = [1 3;
2 6;
3 1;
3 5];
Mat{3} = [2 6;
2 5;
3 1;
5 2];
% etc.
First let's find the unique pairs:
uniq_pairs = unique(cat(1,Mat{:}),'rows');
M = size(uniq_pairs ,1);
Then use ismember to check which sets contain which pairs:
matcontpair = false(M,N); %preallocate
for ii=1:N % unavoidable loop
matcontpair(:,ii) = ismember(uniq_pairs,Mat{ii},'rows');
end
To translate this intersection matrix to a set of matrix numbers for each pair, loop through it again and store the final result in a cell array (you can't use an array, because they might not be of same size (some pairs only found once, other twice, other three times ...)
pair_occurence= cell(M,1);
d=1:N;
for jj=1:M
pair_occurence{jj} = d(matcontpair(jj,:));
end
Now you have a matrix uniq_pairs of size Mx2 containing the unique pairs, and a occurence cell array pair_occurence of size Mx1: each cell corresponds to a pair and contains a list of matrices where the pair is present.
If you want to remove pairs from the list which are only present in one matrix, use the following:
% find them
lonely_pairs = cellfun(#numel,pair_occurence)<2;
% and destroy them
uniq_pairs(lonely_pairs,:) = [];
pair_occurence(lonely_pairs) = [];