Intersection with two columns in each matrix - arrays

I would like to find the intersection of two columns in two matrix (see example below). So to find the position where A and B intersect -- in this case in position 3 and 5.
My solution so far, was to combine the two columns to one column and use intersect function on one column afterwards with a string. Is there a more elegant solution?
A = [1,1;1,3;1,4;2,1;2,5;3,1]
A =
1 1
1 3
1 4
2 1
2 5
3 1
B = [2,5;1,4]
B =
2 5
1 4

You can avoid combining the columns. When using intersect you can use the rows option.
A = [1,1;1,3;1,4;2,1;2,5;3,1]
B = [2,5;1,4]
[C,ia,ib] = intersect(B,A,'rows');
>>ib
3
5
Additionally, if you do not want the intersection result to be ordered you can use the stable option.
[C,ia,ib] = intersect(B,A,'rows','stable');
>>ib
5
3

Related

Replace the values in a vector with the keys from a map in MATLAB?

I have a vector of nondecreasing data. Here is a sample:
1
1
1
2
2
2
2
2
2
2
2
3
3
4
4
6
Clearly there are duplicates and missing numbers. I can remove the duplicates using unique, so my unique values are:
uniqueVals = unique(sortedData);
So far, so good. Now, I want to change the data so that the values in sortedData are replaced with their index number in uniqueVals. For instance, uniqueVals first 5 elements would be 1,2,3,4,6, with indices 1,2,3,4,5. I want to change sortedData so that 1 maps to 1, 2 maps to 2, 3 to 3, 4 to 4, 6 to 5 and so on.
I know I can create a "map" object, but that seems to just be used to map uniqueVals to its index. How do I apply that mapping so that the entries in sortedData are changed?
I have no need for this to be a particularly fast operation. sortedData contains only a few hundred thousand rows and it only needs to be done once.
You can use the third output from unique
[uniqueVals,~,yourOutput] = unique(sortedData);
yourOutput =
1
1
1
2
2
2
2
2
2
2
2
3
3
4
4
5
You can also use g = findgroups(sortedData);, which will give you the group index, where there is one group per unique value. The 2nd output of this tells you the value itself
[g, gValue] = findgroups( sortedData );

How to compare and add elements of an array

I'd like to take a single array lets say 3x5 as follows
1 3 5
1 3 5
2 8 6
4 5 7
4 5 8
and write a code that will create a new array that adds the third column together with the previous number if the numbers in the first and second columns equal the numbers in the row below it.
since the first two values in row 1 and 2, then add the third elements in row 1 and 2 together
so the output from the array above should look like this
1 3 10
2 8 6
4 5 15
The function accumarray(subs,val) accumulate elements of vector val using the subscripts subs. So we can use this function to sum the elements in the third column having the same value in the first and second column. We can use unique(...,'rows') to determine which pairs of value are unique.
%Example data
A = [1 3 5,
1 3 5,
2 3 6,
4 5 7,
4 5 7]
%Get the unique pair of value based on the first two column (uni) and the associated index.
[uni,~,sub] = unique(A(:,1:2),'rows');
%Create the result using accumarray.
R = [uni, accumarray(sub,A(:,3))]
If the orders matters the script would be a little bit more complex:
%Get the unique pair of value based on the first two column (uni) and the associated index.
[uni,~,sub] = unique(A(:,1:2),'rows');
%Locate the consecutive similar row with this small tricks
dsub = diff([0;sub])~=0;
%Create the adjusted index
subo = cumsum(dsub);
%Create the new array
R = [uni(sub(dsub),:), accumarray(subo,A(:,3))]
Or you can get an identical result with a for loop:
R = A(1,:)
for ii = 2:length(A)
if all(A(ii-1,1:2)==A(ii,1:2))
R(end,3) = R(end,3)+A(ii,3)
else
R(end+1,:) = A(ii,:)
end
end
Benchmark:
With an array A of size 100000x3 on the mathworks live editor:
The for loop take about 5.5s (no pre-allocation, so it's pretty slow)
The vectorized method take about 0.012s

remove the rows in a matrix whose elements are the same in matlab

Given a 2-column matrix, for instance. The input is:
[ 1,2;
3,4;
5,5]
The expected output is:
[1,2;
3,4;]
Does anyone know how do accomplish this? Many thanks for your time and attention.
You could use logical indexing:
A = [1 2;3 4;5 5];
match = A(:,1) == A(:,2); // 1 where row has the same elements in both columns
A(match,:) = []; // make the match columns empty
You would need to make this more generic for another case, but for two columns and your example this will work.
Your question suggests your matrix may have an arbitrary number of columns. In that case you may want to delete a row if it has (a) any two elements equal, or (b) all elements equal.
One possible approach is:
Apply sort along each row;
Use diff to compute differences between consecutive elements;
Generate a logical index with all to (a) keep rows for which all such differences are non-zero, or with any to (b) keep rows for which any such difference is non-zero:
So:
X = [1 2 3;
3 4 3;
5 5 5];
Y = X(all(diff(sort(X,2),[],2),2),:);
Z = X(any(diff(sort(X,2),[],2),2),:);
gives
Y =
1 2 3
Z =
1 2 3
3 4 3

merge two matrix and its attributes in matlab

I've two matrix a and b and I'd like to combine the rows in a way that in the first row I got no duplicate value and in the second value, columns in a & b which have the same row value get added together in new matrix. i.e.
a =
1 2 3
8 2 5
b =
1 2 5 7
2 4 6 1
Desired outputc =
1 2 3 5 7
10 6 5 6 1
Any help is welcomed,please.
For two-row matrices
You want to add second-row values corresponding to the same first-row value. This is a typical use of unique and accumarray:
[ii, ~, kk] = unique([a(1,:) b(1,:)]);
result = [ ii; accumarray(kk(:), [a(2,:) b(2,:)].').'];
General case
If you need to accumulate columns with an arbitrary number of columns (based on the first-row value), you can use sparse as follows:
[ii, ~, kk] = unique([a(1,:) b(1,:)]);
r = repmat((1:size(a,1)-1).', 1, numel(kk));
c = repmat(kk.', size(a,1)-1, 1);
result = [ii; full(sparse(r,c,[a(2:end,:) b(2:end,:)]))];

Count based on column and row

I can't seem to find something quite like this problem...
I have an array table where each row contains a random assortment of numbers 1-N
On another sheet, I have a table with column and row headers numbered 1-N
I want to count how many rows in the array contain both the column and row headers for a given cell in the table. Since countifs only reference the current cell in the specified array, they don't seem to be working in this scenario.
Example array:
A B C D
1 3 5 7
1 2 3 4
2 3 4 5
2 4 6 8
...
Table results (symmetrical about the diagonal):
A B C D E F
. 1 2 3 4 5 ...
1 - 1 2 1 1
2 1 - 2 2 1
3 2 2 - 2 2
4 1 2 2 - 1
5 1 1 2 1 -
Would using nested countifs work?
I don't agree with your results corresponding to 4/2, which surely should be 3, not 2, but this formula, based on the array table being in Sheet1 A1:D4 and the results table being in Sheet2 A1:F6, placed in cell B2 of the latter, should work:
=IF($A2=B$1,"-",SUMPRODUCT(N(MMULT(N(COUNTIF(OFFSET(Sheet1!$A$1:$D$1,ROW(Sheet1!$A$1:$D$4)-MIN(ROW(Sheet1!$A$1:$D$4)),),CHOOSE({1,2},B$1,$A2))>0),{1;1})=2)))
Copy across and down as required.
Note: If your actual table is in fact much larger than that given, it will probably be worth adding a simple clause into the above to the effect that the results for approximately half of the cells are obtained from their symmetrical counterparts, rather than via calculation of this construction, thus saving resource.
Regards

Resources