matlab replace value in matrix - matlab

I am trying to replace the first and second column of an edgelist matrix (edgenumber x 3) by specific node numbers as such:
5 1 1
1 38 1
2 1 1
28 17 1
18 1 1
25 1 1
that the node numbers (connection from node 5 to node 1) are replaced by the corresponding values from a vector. The edgelist is generated from an unweighted 40x40 adjacency matrix.
The vector degree_list of size 40x1 contains the "real"node numbers of this edgelist which i want to add to a larger 321x321 adjacency matrix. (if there's an easier way to do that then by concatenating the edgelists, that would also be great).
degree_list=[183,150,151,39,184,185,152,...];
So of the above edgelist I would like to replace all 1s in coll 1 and 2 by 183, all 2s by 150, etc.
Then I need to keep this new edgelist which I the add to the larger edgelist, transform it back to an adjacency matrix and have my new correct bigger adjM.
I have tried to find a solution here and on other websites but not been successful. Thank you very much for your help,
Chris

Code
a1 = [
5 1 1
1 38 1
2 1 1
28 17 1
18 1 1
25 1 1]
degree_list=[183,150,151,39,184,185,152];
col12 = a1(:,[1 2])
col12_uniq = unique(col12)
degree_list = [degree_list numel(degree_list)+1:max(col12_uniq)]
uniq_dim3 = bsxfun(#eq,col12,permute(repmat(col12_uniq,1,2),[3 2 1]))
match_dim3 = bsxfun(#times,uniq_dim3,permute(degree_list(col12_uniq),[3 1 2]))
a1_out = [sum(match_dim3,3) a1(:,3)]
Output
a1 =
5 1 1
1 38 1
2 1 1
28 17 1
18 1 1
25 1 1
a1_out =
184 183 1
183 38 1
150 183 1
28 17 1
18 183 1
25 183 1

Related

How can I make a diamond of zeroes in a matrix of any size? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I have a square Matrix N x M, odd dimensions, and I want to put a diamond of zeroes, for example, for a 5 x 5 matrix:
1 3 2 4 2
5 7 8 9 5
3 2 4 6 3
6 8 2 1 3
3 3 3 3 3
Is transform to:
1 3 0 4 2
5 0 8 0 5
0 2 4 6 0
6 0 2 0 3
3 3 0 3 3
How can this be done efficiently?
I'll bite, here is one approach:
% NxN matrix
N = 5;
assert(N>1 && mod(N,2)==1);
A = magic(N);
% diamond mask
N2 = fix(N/2);
[I,J] = meshgrid(-N2:N2);
mask = (abs(I) + abs(J)) == N2;
% fill with zeros
A(mask) = 0;
The result:
>> A
A =
17 24 0 8 15
23 0 7 0 16
0 6 13 20 0
10 0 19 0 3
11 18 0 2 9
I also had some time to play around. For my solution there are no limits concerning A being odd or even or larger than 1. Every integer is fine (even 0 works, though it does not make sense).
% NxN matrix
N = 7;
A = magic(N);
half = ceil( N/2 );
mask = ones( half );
mask( 1 : half+1 : half*half ) = 0;
mask = [ fliplr( mask ) mask ];
mask = [ mask; flipud( mask ) ];
if( mod(N,2) == 1 )
mask(half, :) = []
mask(:, half) = []
end
A( ~mask ) = 0;
A
I am first creating a square sub-matrix mask of "quarter" size (half the number of columns and half the number of rows, ceil() to get one more in the case N is odd).
Example for N=7 -> half=4.
mask =
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
I then set it's diagonal values to zero:
mask =
0 1 1 1
1 0 1 1
1 1 0 1
1 1 1 0
Mirror the mask horizontally:
mask =
1 1 1 0 0 1 1 1
1 1 0 1 1 0 1 1
1 0 1 1 1 1 0 1
0 1 1 1 1 1 1 0
Then mirror it vertically:
mask =
1 1 1 0 0 1 1 1
1 1 0 1 1 0 1 1
1 0 1 1 1 1 0 1
0 1 1 1 1 1 1 0
0 1 1 1 1 1 1 0
1 0 1 1 1 1 0 1
1 1 0 1 1 0 1 1
1 1 1 0 0 1 1 1
As N is odd we got a redundant row and redundant column that are then removed:
mask =
1 1 1 0 1 1 1
1 1 0 1 0 1 1
1 0 1 1 1 0 1
0 1 1 1 1 1 0
1 0 1 1 1 0 1
1 1 0 1 0 1 1
1 1 1 0 1 1 1
The logical not is then used as a mask to select the values in the original matrix that are set to 0.
Probably not as efficient as #Amro's solution, but it works. :D
My solution:
looking at the first left half of the matrix
in the first row 0 is in the middle column (let's call it mc)
in the second row the 0is in column mc-1
and so on while the rows increase
when you reach column 1 the sequence continue but with mc+1 but the rows decrease
In a similar way for the right half of the matrix
n=7
a=randi([20 30],n,n)
% Centre of the matrix
p=ceil(n/2)
% Identify the column sequence
col=[p:-1:1 2:p p+1:n n-1:-1:p]
% Identify the row sequence
row=[1:n n-1:-1:1]
% Transorm the row and column index in linear index
idx=sub2ind(size(a),row,col)
% Set the 0'
a(idx)=0
a =
22 29 23 27 27 21 23
29 29 21 27 24 26 24
30 28 21 27 29 28 25
28 22 24 20 27 24 25
23 26 21 20 30 20 29
26 20 26 23 25 22 25
21 24 25 25 23 21 30
a =
22 29 23 0 27 21 23
29 29 0 27 0 26 24
30 0 21 27 29 0 25
0 22 24 20 27 24 0
23 0 21 20 30 0 29
26 20 0 23 0 22 25
21 24 25 0 23 21 30
Hope this helps.
Qapla'
Using indexing (only works when N is odd):
N = 7;
% Random matrix
A = randi(100, N);
idx = [N-1:-2:1; 2:2:N];
A(cumsum([ceil(N/2) idx(:)' idx(end-1:-1:1)])) = 0
A =
60 77 74 0 54 83 9
8 48 0 76 0 28 67
6 0 32 78 83 0 10
0 27 25 5 11 39 0
76 0 49 43 67 0 16
79 7 0 86 0 70 78
57 28 85 0 81 44 81

Sum of groups of four in a matrix

I have the following matrix: first column are the values of 1 to 5, second column is 1 to 20, and the third column are random values.
1 1 2545
1 2 0
1 3 0
1 4 0
2 5 0
2 6 0
2 7 231
2 8 54587
3 9 41
3 10 1111
3 11 0
3 12 1213
4 13 0
4 14 0
4 15 0
4 16 0
5 17 898
5 18 6887
5 19 522
5 20 23
What I am trying to do is to get the sum in groups of four when all values are different of zero. As an example, in the matrix the output I want is:
1 NaN
2 NaN
3 NaN
4 NaN
5 8330
Assuming that the first column delineates what values in the third column belong to what group, the easiest would be to change all values that are zero to NaN, then use accumarray to sum all of the values that belong to each group. This is crucial because as soon as you sum over a matrix / array and any value is NaN, the result will be NaN. This is nice because if you sum over each group, you will get a NaN result if at least one of the values in the group was equal to 0 before the change.
I'm going to assume that your matrix is stored in X like so:
X = [1 1 2545
1 2 0
1 3 0
1 4 0
2 5 0
2 6 0
2 7 231
2 8 54587
3 9 41
3 10 1111
3 11 0
3 12 1213
4 13 0
4 14 0
4 15 0
4 16 0
5 17 898
5 18 6887
5 19 522
5 20 23 ];
Make a copy of the third column, and let's do some magic:
col = X(:,3);
col(col == 0) = NaN;
out = accumarray(X(:,1), col);
We thus get:
out =
NaN
NaN
NaN
NaN
8330
The nice thing about this approach is that the group ID for each value in your matrix doesn't have to be in order as you have placed in your post.
If however your matrix is guaranteed to have the order where each group consists of consecutive 4-tuples of elements, you can do the same thing with the NaN assignment, but you can avoid using accumarray and reshape the third column into a matrix of four rows then sum over each row individually:
col = X(:,3);
col(col == 0) = NaN;
out = sum(reshape(col, 4, []), 1);

How to add noise to an image loaded from .mat file?

So I have loaded the .mat file:
load Yale_32x32.mat;
X = fea';
I then can view the image using:
imshow(reshape(X(:,1),32,32),[])
There are 165 images, so the second dimension can be any number in the range from 1 to 165. Suppose, I want to add 'salt-pepper' noise to one of the images. If I try to do:
J = imnoise(reshape(X(:,1),32,32),'salt & pepper', 0.05);
and then:
imshow(J,[]);
... it will display to me a noise on the purely white background. What am I doing wrong?
EDIT
X(:,1) gives me:
70
68
49
53
50
50
37
33
26
13
17
61
69
109
....
etc.
After applying J = imnoise(X(:,1),'salt & pepper', 0.05);, I get:
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
0
1
1
etc.
EDIT
Fixed: I had to normalize my image. Thank you.

Comparing two matrix and took the value if match the criteria

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)

Comparing content of two columns in MATLAB

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'