I'd like to obtain all unique products for a given vector.
For example, given a:
a = [4,10,12,3,6]
I want to obtain a matrix that contains the results of:
4*10
4*12
4*3
4*6
10*12
10*3
10*6
12*3
12*6
3*6
Is there a short and/or quick way of doing this in MATLAB?
EDIT: a may contain duplicate numbers, giving duplicate products - and these must be kept.
Given:
a =
4 10 12 3 6
Construct the matrix of all pairwise products:
>> all_products = a .* a.'
all_products =
16 40 48 12 24
40 100 120 30 60
48 120 144 36 72
12 30 36 9 18
24 60 72 18 36
Now, construct a mask to keep only those values below the main diagonal:
>> mask = tril(true(size(all_products)), -1)
mask =
0 0 0 0 0
1 0 0 0 0
1 1 0 0 0
1 1 1 0 0
1 1 1 1 0
and apply the mask to the product matrix:
>> unique_products = all_products(mask)
unique_products =
40
48
12
24
120
30
60
36
72
18
If you have the Statistics Toolbox, you can abuse pdist, which considers only one of the two possible orders for each pair:
result = pdist(a(:), #times);
One option involves nchoosek, which returns all combinations of k elements out of a vector, each row is one combination. prod computes the product of rows or columns:
a = [4,10,12,3,6];
b = nchoosek(a,2);
b = prod(b,2); % 2 indicates rows
Try starting with this. Have the unique function filter out the result of multiplying a by itself.
b = unique(a*a')
Related
I am trying to shift non circularly in MATLAB so even if I shift outside of the index it will add 0s to correct it. I tried following the answer in How do I shift columns (left or right) in a matrix? but had no success.
data = [1 2 3 4 5; 11 12 13 14 15; 21 22 23 24 25; 31 32 33 34 35]
d = 3; % shift; positive/negative for right/left
result = zeros(size(data), 'like', data); % preallocate with zeros
result(:,max(1,1+d):min(end,end+d)) = data(:,max(1,1-d):min(end,end-d)); % write values
In my output results is nothing but the same size but all zeroes
Desired output:
0 0 0 1 2 3 4 5
0 0 0 11 12 13 14 15
0 0 0 21 22 23 24 25
0 0 0 31 32 33 34 35
You can do it by creating a matrix result, the final size, filled with zeros, then copying the original data into the final result, making sure you place the data at the right indices.
What you have in your example code is not right for what you ask. If I run it,the final result is padded fine but truncated at the size of the original data matrix. This is how some matrix are shifted (with the shifted columns dropped altogether), but that's not what you asked.
A simple way to do it, is to create a padding matrix of the proper size, then simply concatenate it with your original data matrix. This can be done as below:
%% Initial data
data = [1 2 3 4 5; 11 12 13 14 15; 21 22 23 24 25; 31 32 33 34 35] ;
d = 3 ;
%% shift and pad with zeros
nrows = size(data,1) ; % Number of rows in [data]
pad = zeros( nrows , abs(d) ) ; % create padding matrix
if d>0
result = [pad data] ; % Concatenate the filler matrix on the left
else
result = [data pad] ; % Concatenate the filler matrix on the right
end
And just to be sure:
>> result
result =
0 0 0 1 2 3 4 5
0 0 0 11 12 13 14 15
0 0 0 21 22 23 24 25
0 0 0 31 32 33 34 35
If you want to reuse the same way than in your example code, you have to adjust it a bit to allow for the new columns:
%% create result and copy data
result = zeros( size(data,1) , size(data,2)+abs(d) ) ;
colStart = max(1,1+d) ;
result(:,colStart:colStart+size(data,2)-1) = data ;
This will create the same result matrix as above.
I have a matrix 2000x5, in the first column the point number, and in columns 2-5 the 4 neighbours (0s if there isnt a neighbour). Is there an efficient way to create an adjacency matrix out of this ?
1 129 0 65 0
2 130 0 66 85
3 131 169 67 0
4 132 170 68 87
5 133 0 69 81
6 134 0 70 82
7 135 173 71 83
8 136 174 72 84
9 137 161 73 0
10 138 162 74 93
11 139 163 75 0
12 140 164 76 95
13 141 165 77 89
14 142 166 78 90
15 143 167 79 91
16 144 168 80 92
17 145 0 81 65
18 146 0 82 66
....
I found the following thread, where it is explained for just one neighbour, but I am not sure how to use it for multiple neighbours.
matlab adjacency list to adjacency matrix
I would very much appreciate any help.
A quick and simple technique:
adjMat = zeros(size(A,1));
for ind = 1:size(A,1)
% Flag 1 on each row 'ind' at the indices mentioned in col 2-5
adjMat(ind, nonzeros(A(ind,2:end))) = 1;
end
Since you have mentioned using the nearest neighbour search, it is likely that the adjacency list should be completely filled to result in a undirected graph, in the sense that if row 1 has 20 as a neighbour, row 20 very likely has 1 as a neighbour.
However technically speaking, this will produce an adjacency matrix exactly equivalent to the adjacency list, assuming nothing by itself.
Example:
For an adjacency list
A = [1 2 3; 2 0 1; 3 1 4; 4 5 3; 5 4 0]
A =
1 2 3
2 0 1
3 1 4
4 5 3
5 4 0
The result is:
adjMat =
0 1 1 0 0
1 0 0 0 0
1 0 0 1 0
0 0 1 0 1
0 0 0 1 0
P.S. To force undirected-ness, you can simply add another statement in the for loop body:
adjMat(nonzeros(A(ind,2:end)),ind) = 1;
This will ensure that the adjacency matrix will be symmetric, which is a characteristic of undirected graphs.
Firstly, I'm going to assume that the adjacency list is undirected. In any case, it's not that far of a stretch to go to multiple neighbours. What you need to do first is detect the total number of non-zero elements per row from columns 2 to 5. Once you do this, for the rows of the adjacency matrix, you would copy the point number for as many times as there are non-zero elements per that row. The function repelem is perfectly suitable to do that for you. The column indices would simply be the second to fifth columns removing all of the zero elements. How you can do this is first transpose the matrix resulting in indexing the second to fifth columns, then using a logical indexing matrix to remove out the zero entries. Doing this will unroll your vector in a column-major fashion, which is why transposing is required before doing this operation. Once you do this, you can create row and column access indices so that these can be input into sparse much like that post you linked.
Supposing that your matrix was stored in A, you would do something like this. This also assumes that each of the weights connecting the nodes are 1:
% Find total number of non-zero elements per row, skipping first column
non_zero = sum(A(:,2:end) ~= 0, 2);
% Create row indices
rows = repelem(A(:,1), non_zero);
% Create column indices
cols = A(:,2:end).';
cols = cols(cols ~= 0);
% Create adjacency matrix
adj = sparse([rows; cols],[cols; rows], 1);
The above representation is in sparse. If you want the full numeric version, cast the output using full:
adj = full(adj);
If your graph is directed
If you have a directed graph instead of an undirected graph, the above call to sparse duplicates edges so that you are creating links to and from each of the neighbours. If your graph is actually directed, then you simply have to only use the row and column indices once instead of twice as seen in the above code:
% Create adjacency matrix
adj = sparse(rows, cols , 1);
Test Case
Here's a small test case to show you that this works. Supposing my adjacency list looked like the following:
>> A = [1 0 2 3; 2 4 0 0; 3 0 0 4]
A =
1 0 2 3
2 4 0 0
3 0 0 4
The adjacency matrix is now:
>> full(adj)
ans =
0 1 1 0
1 0 0 1
1 0 0 1
0 1 1 0
Taking a look at the list above and how the matrix is populated, we can verify that this is correct.
Note about repelem
repelem assumes you have MATLAB R2015a or later. If you don't have this, you can consult this answer by user Divakar on a custom implementation of repelem here: Repeat copies of array elements: Run-length decoding in MATLAB
I'm fairly new to Matlab (and programming in general) and I can't figure out how to do this. Perhaps it is quite simple, but I could really use some help. I've got this matrix:
25 53 52 25 37
26 54 0 26 38
27 55 0 27 0
28 56 0 28 0
0 59 0 0 0
0 60 0 0 0
I would like to compute all different combinations, in terms of rows with one value from each column, like, 25,53,52,25,37 and 25,54,52,26,38 and 25,54,52,27,0 etc. Besides, I want to discard the combinations containing 0 (like 25,53,0,25,37).
Take a look at this function, allcombo([25:28],[53:56 59:60],52,[25:28],[37:38]) should be what you are looking for.
Adapted from this answer:
M = [ 25 53 52 25 37
26 54 0 26 38
27 55 0 27 0
28 56 0 28 0
0 59 0 0 0
0 60 0 0 0 ]; %// data matrix
[m n] = size(M);
C = mat2cell(M, m, ones(1,n)); %// convert to cell array of column vectors
combs = cell(1,n); %// pre-define to generate comma-separated list
[combs{end:-1:1}] = ndgrid(C{end:-1:1}); %// the reverse order in these two
%// comma-separated lists is needed to produce the rows of the result matrix in
%// lexicographical order
combs = cat(n+1, combs{:}); %// concat the n n-dim arrays along dimension n+1
combs = reshape(combs,[],n); %// result: each row gives a combination
This gives, in your example:
>> combs(1:5,:)
ans =
25 53 52 25 37
25 53 52 25 38
25 53 52 25 0
25 53 52 25 0
. . .
If any column has repeated entries, the result will have repeated rows. To keep only unique rows (note this changes the order of the rows):
combs = unique(combs,'rows');
I have some question. I have 2 matrix, it's have same size.
For example, first matrix :
1
1
0
0
1
0
Second matrix
34
56
12
12
33
14
Then, I want to compare this two matrix and groups it by the criteria on first matrix
so I will have this two groups matrix :
Matrix when the first matrix is have value 1
34
56
33
and
Matrix when the first matrix is have value 0
12
12
14
You could try this:
a = [1 1 0 0 1 0]';
b = [34 56 12 12 33 14]';
b(a==1)
b(a==0)
I need to compare the content of two tables, more exactly two columns (one column per table), in MATLAB to see for each element of the first column, if there is an equal element in the second column.
Should I use a for loop or is there an existing MATLAB function that does this?
If the order is important, you do element-wise comparison, after which you use all
%# create two arrays
A = [1,2;1,3;2,5;3,3];
B = [2,2;1,3;1,5;3,3];
%# compare the second column of A and B, and check if the comparison is `true` for all elements
all(A(:,2)==B(:,2))
ans =
1
If the order is unimportant and all elements are unique, use ismember
all(ismember(A(:,1),B(:,1))
ans =
1
If the order is unimportant, and there are repetitions, use sort
all(sort(A(:,1))==sort(B(:,2)))
ans =
0
did you know you could do this:
>> a = [1:5];
>> b = [5:-1:1];
>> a == b
ans =
0 0 1 0 0
so you could compare 2 columns in matlab by using the == operator on the whole column. And you could use the result from that as a index specifier to get the equal values. Like this:
>> a(a == b)
ans =
3
This mean, select all the elements out of a for which a == b is true.
For example you could also select all the elements out of a which are larger than 3:
>> a(a > 3)
ans =
4 5
Using this knowledge I would say you could solve your problem.
For arithmetic values, both solutions mentioned will work. For strings or cell arrays of strings, use strcmp/strcmpi.
From the help file:
TF = strcmp(C1, C2) compares each element of C1 to the same element in C2, where C1 and C2 are equal-size cell arrays of strings. Input C1 or C2 can also be a character array with the right number of rows. The function returns TF, a logical array that is the same size as C1 and C2, and contains logical 1 (true) for those elements of C1 and C2 that are a match, and logical 0 (false) for those elements that are not.
An example (also from the help file):
Example 2
Create 3 cell arrays of strings:
A = {'MATLAB','SIMULINK';'Toolboxes','The MathWorks'};
B = {'Handle Graphics','Real Time Workshop';'Toolboxes','The MathWorks'};
C = {'handle graphics','Signal Processing';' Toolboxes', 'The MATHWORKS'};
Compare cell arrays A and B with sensitivity to case:
strcmp(A, B)
ans =
0 0
1 1
Compare cell arrays B and C without sensitivity to case. Note that 'Toolboxes' doesn't match because of the leading space characters in C{2,1} that do not appear in B{2,1}:
strcmpi(B, C)
ans =
1 0
0 1
To get a single return value rather than an array of logical values, use the all function as explained by Jonas.
You can use for loop (code below) to compare the content of the column 1 and column % 2 in the same table:
clc
d=[ 19 24 16 12 35 0
16 16 18 0 23 18
16 10 7 10 13 24
19 8 30 0 12 26
16 12 4 1 13 12
24 0 31 0 40 0
12 11 10 6 20 0
16 11 6 2 25 9
17 9 21 0 17 8
20 0 7 10 22 0
13 16 12 18 17 13
17 23 17 0 23 20
25 0 10 3 17 15
14 4 4 17 12 10
19 24 21 5 35 0
15 20 5 0 10 31
13 8 0 16 40 0
18 27 26 1 19 14
12 0 2 0 12 4
20 0 6 2 15 21
20 0 26 0 18 26
12 11 1 13 19 15
14 0 20 0 9 16
14 15 6 12 40 0
20 0 8 10 18 12
10 11 14 0 13 11
5 0 22 0 8 12 ];
x1=d(:,1);
y1=d(:,2);
for i=1:27
if x1(i)>y1(i);
z(i)=x1(i);
else
z(i)=y1(i);
end
end
z'