Construct a matrix from a set of coordinates - matlab

I have a set of coordinates:
x y
65 17
66 17
67 18
68 18
24 26
25 26
26 27
27 27
34 35
35 35
I want to construct a 70-by-70 matrix A such that A(67,18) = A(68,18) = A(24,26) = ... =A(35,35) = 1, otherwise A(:,:) = 0.
Is there any quick way to do this?

A=sparse(x,y,1,70,70)
If you don't want a sparse matrix, convert it:
A=full(A)

I recommend using sparse as shown in Daniel's answer, but here is a simple sub2ind based alternative:
n = 70;
A = zeros(n);
A(sub2ind([n,n],x,y))=1;

Or, you can use linear indexing:
A = zeros(70);
A(x+70*(y-1))=1;

you can try this:
coords = ...
[65 17;
66 17;
67 18;
68 18;
24 26;
25 26;
26 27;
27 27;
34 35;
35 35];
%// creating the result array
result = zeros(70,70);
%// loop through the entrys
for i = 1 : size(coords,1)
result(coords(i,1), coords(i,2)) = 1;
end
UPDATE
you can solve it without an loop too:
coords = ...
[65 17;
66 17;
67 18;
68 18;
24 26;
25 26;
26 27;
27 27;
34 35;
35 35];
%// creating the result array
result = zeros(70,70);
%// splitting the coords into a `x` and a `y` vector and save them into a cell
indexes = mat2cell(coords, size(coords, 1), ones(1, size(coords, 2)));
%// setting the ones by the indexes cell
result(sub2ind(size(result), indexes{:})) = 1;

Here's another way using accumarray:
result = accumarray([x(:) y(:)], 1, [70 70]); %// full matrix
The third input argument, [70 70], specifies matrix size.
If you prefer sparse result, use a sixth input argument (called issparse in the documentation) as follows:
result = accumarray([x(:) y(:)], 1, [70 70], [], 0, true); %// sparse matrix
Either of the above statements accumulates coincident values. That is, if there is a repeated coordinate you will get a value 2 at that entry. If you want to keep value 1 in those cases, change the fourth input argument:
result = accumarray([x(:) y(:)], 1, [70 70], #(x) 1, 0); %// force 1, full matrix
This could of course be combined with the issparse flag set to true:
result = accumarray([x(:) y(:)], 1, [70 70], #(x) 1, 0, true); %// force 1, sparse matrix

Related

How to get a vector from the elements along a fixed dimension in a 3D array?

I have a 3D array in MATLAB like this:
val(:,:,1) =
1.1461 2.3993
2.3993 15.4036
val(:,:,2) =
1.0041 0.8106
0.8106 10.6503
val(:,:,3) =
1.0001 0.9895
0.9895 3.0384
val(:,:,4) =
1.0024 0.9936
0.9936 2.0169
It's a 2 x 2 x 600 array. I want the second element of each of the 600 "matrices". Is there a simple way to extract these in MATLAB?
The output I would desire is like this:
output = [ 2.3993; 0.8106; 0.9895; 0.9936 ];
My attempt so far has been the following:
val(1, 2, :)
But, this returns:
ans(:,:,1) =
2.3993
ans(:,:,2) =
0.8106
ans(:,:,3) =
0.9895
ans(:,:,4) =
0.9936
I need these values as a vector.
Your approach accessing val(1, 2, :) is correct. Nevertheless, the output produced has a size of 1 x 1 x 4. What you want to do is to remove the (unnecessary) dimensions of length 1. That is what the squeeze function is for.
Having a 3-dimensional array val like yours and fixed indices x, y for the first and second dimensions, we just surround your command with squeeze:
% 3-dimensional array
val = reshape(1:36, 3, 3, 4)
% Squeezed output for fixed x, y in dimensions 1 and 2
x = 1;
y = 2;
out3d = squeeze(val(x, y, :))
The output is the following:
val =
ans(:,:,1) =
1 4 7
2 5 8
3 6 9
ans(:,:,2) =
10 13 16
11 14 17
12 15 18
ans(:,:,3) =
19 22 25
20 23 26
21 24 27
ans(:,:,4) =
28 31 34
29 32 35
30 33 36
out3d =
4
13
22
31
This works for arbitrary dimensions and number of indices.
A 4-dimensional array with two fixed indices will produce a 2-dimensional output:
% 4-dimensional array
val = reshape(1:108, 3, 3, 4, 3)
% Squeezed output for fixed x, y in dimensions 1, 2
x = 1;
y = 2;
out4d = squeeze(val(x, y, :, :))
Output:
val = (omitted here)
out4d =
4 40 76
13 49 85
22 58 94
31 67 103
A 4-dimensional array with three fixed indices will again produce a 1-dimensional output:
% 4-dimensional array
val = reshape(1:108, 3, 3, 4, 3)
% Squeezed output for fixed x, y, z in dimensions 1, 2, 3
x = 1;
y = 2;
z = 1;
out4d = squeeze(val(x, y, z, :))
Output:
val = (omitted here)
out4d =
4
40
76
Hope that helps!

Find Array around Maximum Values of an Array

i have a quite complicated question here which i am working on. It's extremely difficult to describe in words, so i will try to explain it with an example.
Assume i have a matrix of values:
A =
[31 85 36 71 51]
[12 33 74 39 12]
[67 11 13 14 18]
[35 36 84 33 57]
Now, i want to first find a maximum vector in the first dimension, which is easy:
[max_vector,~] = max(A,[],1);
max_vector=[67,85, 84, 71,57]
Now i want to get a "slimmed" matrix with values around the maxima (periodical indices):
Desired_Matrix =
[12 36 36 33 18]
[67 85 84 71 57]
[35 33 13 39 51]
This is the matrix with the vectors around the maximum values of matrix A. Can someone tell me how to do this without using a double for loop?
Thank you!
% Input.
A = [31 85 36 71 51; 12 33 74 39 12; 67 11 13 14 18; 35 36 84 33 57]
% Dimensions needed.
nRows = size(A, 1);
nCols = size(A, 2);
% Get maxima and corresponding indices in input.
[max_vector, ind] = max(A);
% Get neighbouring indices.
ind = [ind - 1; ind; ind + 1];
% Modulo indices to prevent dimension overflow.
ind = mod(ind, nRows);
% Correct zero indices.
ind(ind == 0) = nRows;
% Calculate correct indices in A.
temp = repmat(0:nRows:nRows*(nCols-1), 3, 1);
ind = ind + temp;
% Output.
B = A(ind)
Since we have max indices per column, but later want to access these elements in the original array A, we need proper linear indices for A. Here, the trick is to add the number of rows multiplied by the column index (starting by 0). The easiest way to understand might be to remove the semicolons, and inspect the intermediate values of ind.
#HansHirse's answer is more efficient, as it does not create an intermediate matrix.
Try this:
[~, ind_max] = max(A,[],1);
A_ext = A([end 1:end 1],:);
ind_lin = bsxfun(#plus, bsxfun(#plus, ind_max, (0:2).'), (0:size(A_ext,2)-1)*size(A_ext,1));
result = reshape(A_ext(ind_lin), 3, []);
For Matlab R2016b or newer, you can simplify the third line:
[~, ind_max] = max(A,[],1);
A_ext = A([end 1:end 1],:);
ind_lin = ind_max + (0:2).' + (0:size(A_ext,2)-1)*size(A_ext,1);
result = reshape(A_ext(ind_lin), 3, []);
Here is another solution. This is similar to HansHirse's answer, with two improvements:
Slightly more elegantly handles the modular indexing
Is more flexible for specifying which neighbours your want
Code:
% Input
A = [31 85 36 71 51;
12 33 74 39 12;
67 11 13 14 18;
35 36 84 33 57];
% Relative rows of neighbours, i.e. this is [-1, 0, 1] for +/- one row
p = -1:1;
% Get A row and column counts for ease
[nr, nc] = size(A);
% Get max indices
[~,idx] = max( A, [], 1 );
% Handle overflowing indices to wrap around rows
% You don't have to redefine "idx", could use this directly in the indexing line
idx = mod( idx + p.' - 1, nr ) + 1;
% Output B. The "+ ... " is to convert to linear indices, as "idx"
% currently just refers to the row number.
B = A(idx + (0:nr:nr*nc-1));
You can use the Image Processing Toolbox to generate the result, though is less efficient than other solutions.
[~,idx] = max(A, [], 1);
d = imdilate( idx == (1:size(A,1) ).', [1;1;1], 'full');
p = padarray(A, 1, 'circular');
Desired_Matrix = reshape(p(d), 3, []);
just for your information, here is the generalized form for the 3D-Case:
A = zeros(3,5,5);
for id = 1: 20
A(:,:,id) = id;
if id == 10
A(:,:,id) = 100;
end
end
% Relative rows of neighbours, i.e. this is [-1, 0, 1] for +/- one row
p = -1:1;
% Get A row and column counts for ease
[nr, nc, nz] = size(A);
% Get max indices
[~,idx] = max( A, [], 3 );
% Handle overflowing indices to wrap around rows
% You don't have to redefine "idx", could use this directly in the indexing line
idx = mod( idx + reshape(p,1,1,3) - 1, nz ) + 1;
% Output B. The "+ ... " is to convert to linear indices, as "idx"
% currently just refers to the row number.
INDICES = ((idx-1) * (nr*nc)+1 )+ reshape(0:1:nc*nr-1,nr,nc);
B = A(INDICES);

Removing rows of a matrix based on rows of another matrix

Imagine I have two matrices with different sizes (let's say 6x2 and 5x2) as follows:
A = [47 10;
29 10;
23 10;
34 10;
12 10;
64 10];
B = [23 20;
12 20;
54 20
47 20;
31 20];
I need to compare A(:,1) with B(:,1) and delete the rows in matrix A whose first-column-element is different from matrix B's first-column-element (so my focus is only on first columns of the matrices). So I should eventually get something like this:
A = [47 10;
12 10;
23 10];
as "47", "12", and "23" are the only first-column-elements in A that also exist in B! I have written this but I get the error "Matrix dimensions must agree."!
TF = A(:,1) ~= B(:,1); %define indexes in A that A(:,1) is not equal to B(:,1)
A(TF,:) = [];
Any ideas how I could fix this?
You can use ismember:
result = A(ismember(A(:,1), B(:,1)), :);
Replacing this line
TF = A(:,1) ~= B(:,1);
with this line
[~,TF] = setdiff(A(:,1),B(:,1));
yields the desired result.

Tracking changes in a cell in Matlab

I have a cell with 3 different columns. The first is a simple ranking, the second is a code composed by X elements and the third is a code composed by Y elements that usually is the same for a certain combination of numbers in column two. So if in column two you have the number 345, it is likely that in column three you will always have 798. The thing is that sometimes it changes. So what I have, for instance, is:
1 453 4789
1 56 229
1 453 1246 %here the corresponding code has changed
2 43 31
2 453 1246 %here the code did not change
3 56 31 %here the corresponding code has changed (it was 229 previously)
What I want to have at the end is a new cell with three columns, only descriminating the cases in which a change in the code of the third column (correspondent to the code form the second column) was observed. For instance, in this simple example I would get:
1 453 1246
3 56 31
If you have your data in a matrix A you can use sorting:
[~, I] = sort(A(:,2));
B = A(I,:);
code_diff = logical(diff(B(:, 2)));
value_diff = logical(diff(B(:, 3)));
value_diff(code_diff) = false;
rows = sort(I([false; value_diff]));
ans = A(rows, :);
If the "codes" in the second column are all smallish integers, another possibility is to use a lookup table:
n = size(A, 1);
m = max(A(:, 2));
mask = false(n, 1);
lookup = inf(m, 1);
for i = 1:n
code = A(i,2);
if isinf(lookup(code))
lookup(code) = A(i,3);
elseif lookup(code) ~= A(i,3)
mask(i) = true;
lookup(code) = A(i,3);
end
end
ans = A(mask, :);
Assuming the values are in a matrix, this is a possible solution:
CJ2 = [1 453 4789
1 56 229
1 453 1246
2 43 31
2 453 1246
3 56 31];
changes = zeros(size(CJ2));
nChanges = 0;
for i = 2:size(CJ2,1)
pos = find(CJ2(1:i-1,2) == CJ2(i,2), 1, 'last');
if ~isempty(pos) && CJ2(pos,3) ~= CJ2(i,3)
nChanges = nChanges + 1;
changes(nChanges, :) = CJ2(i,:);
end
end
changes = changes(1:nChanges, :);
changes
Results:
>> changes
changes =
1 453 1246
3 56 31

overlapping feature values values in matlab

If I have a matrix
F=[ 24 3 17 1;
28 31 19 1;
24 13 25 2;
47 43 39 1;
56 41 39 2];
in the first three columns I have feature values a forth column is for class labels. my problem is to get rid of same feature values when class label is different for that particular values.
like for F matrix I have to remove the rows 1,3,4 and 5 ,because for first column there are 2 different values in column four and same is for third column (39 and 39)as class label again got changed.
so output should look like
F=[28 31 19 1];
The straightforward approach would be iterating over the columns, counting the number of different classes for each value, and removing the rows for values associated to more than one class.
Example
F = [24 3 17 1; 28 31 19 1; 24 13 25 2; 47 43 39 1; 56 41 39 2];
%// Iterate over columns
for col = 1:size(F, 2) - 1
%// Count number of different classes for each value
[vals, k, idx] = unique(F(:, col));
count = arrayfun(#(x)length(unique(F(F(:, col) == x, end))), vals);
%// Remove values associated to more than one class
F(count(idx) > 1, :) = [];
end
This results in:
F =
28 31 19 1
Another take at the problem, without arrayfun (edited)
F = [24 3 17 1; 28 31 19 1; 24 13 25 2; 47 43 39 1; 56 41 39 2];
Separate both classes:
A1 = F(F(:,4)==1,1:3);
A2 = F(F(:,4)==2,1:3);
Replicate them to a 3D matrix to compare each line of class1 with each line of class2:
B2 = repmat(shiftdim(A2',-1),size(A1,1),1);
B1 = repmat(A1,[1,1,size(A2,1)]);
D4 = squeeze(sum(B1 == B2,2));
remove rows duplicated rows
A1(logical(sum(D4,2)),:) = [];
A2(logical(sum(D4,1)),:) = [];
reconstruct original matrix
R = [A1 ones(size(A1,1),1);A2 2*ones(size(A2,1),1)];