Following on from: Tallying co-incidences of numbers in columns of a matrix - MATLAB
I was wondering how best to go about the same task when the data is processed differently. Starting with a matrix of:
A =
201 202
203 204
201 203
201 203
205 201
I simply need a matrix that tallies the coincidence of each number from Column 1 to Column 2 i.e.
201 202
201 occurs with 202 once and so on (it ignores that 202 occurs with 201, removing duplicates). This is stored in a 2D matrix with min(A):max(A) running down both sides.
The desired output for the above example (A) would be:
201 202 203 204 205
201 0 1 2 0 0
202 0 0 0 0 0
203 0 0 0 1 0
204 0 0 0 0 0
205 1 0 0 0 0
See if this works for you -
A1 = A-min(A(:))+1 %// Get offsetted values
Aout = zeros(max(A1(:)))
%// For performance: Aout(max(A1(:)),max(A1(:)))=0;
%// But for this performance trick to work, make sure Aout isnt defined before.
%// Source - http://undocumentedmatlab.com/blog/preallocation-performance
idx = sub2ind(size(Aout),A1(:,1),A1(:,2))
%// For performance: idx = (A1(:,2)-1)*size(Aout,1) + A1(:,1)
unqidx = unique(idx) %// get unique indices
Aout(unqidx) = histc(idx,unqidx) %// put counts of indices into their places
Sample run with an extra added row of input data -
>> A
A =
201 202
203 204
201 203
201 203
205 201
202 201
>> Aout
Aout =
0 1 2 0 0
1 0 0 0 0
0 0 0 1 0
0 0 0 0 0
1 0 0 0 0
Related
whos.exit whos condition1 result
650 452 1 0
654 456 0 0
254 650 1 1
785 412 1 0
756 654 1 1
744 0 0
125 1 0
985 1 0
... ... ...
I wish obtain the result matrix.
Result matrix contains all "whos" which satify the condition1 and are present whos.exit but in no particular order. Note: all elements in whos.exit are unique and the result of whos(condition1) will give unique whos.
You can use ismember -
result = ismember(whos,whos.exit).*condition1
Or bsxfun -
result = any(bsxfun(#eq,whos,whos.exit.'),2).*condition1
Since whos is an in-built command in MATLAB, I would suggest using some other variable name there as a matter of good practice.
You could use intersect
intersect(whos.exit,whos.*condition1)
ans =
650
654
Or if you want a binary array (not as elegant asismember though)
A=zeros(size(whos.exit,1),1);
[~,~,iwe]=intersect(whos,whos.exit);
A(iwe) = 1;
A.*c1
ans =
0
0
1
0
1
0
0
0
or
[~,~,iwe]=intersect(whos,whos.exit);
sum((((c1.*whos.exit)./whos.exit(iwe)')==1)')'
ans =
0
0
1
0
1
0
0
0
Details
Find the indices in whose.exit whose values are in both arrays.
[~,~,iwe]=intersect(whos,whos.exit)
iwe =
3
5
Find where those values are. I just use a division because a value divided by itself will show a 1 and that tells us where the values are. Each row represents the value(s) we are looking for and the column the location of this value. The first value (whos.exit(iwe(1))) is location at position 3 and the second (whos.exit(iwe(2))) is location at position 5.
(((c1.*whos.exit)./whos.exit(iwe)')==1)'
ans =
0 0 1 0 0 0 0 0
0 0 0 0 1 0 0 0
The we just sum and transpose that to get the binary array
sum((((c1.*whos.exit)./whos.exit(iwe)')==1)')'
ans =
0
0
1
0
1
0
0
0
I wrote naive gauss elimination without pivoting:
function [x] = NaiveGaussianElimination(A, b)
N = length(b);
x = zeros(N,1);
mulDivOp = 0;
subAddOp = 0;
for column=1:(N-1)
for row = (column+1):N
mul = A(row,column)/A(column,column);
A(row,:) = A(row,:)-mul*A(column,:);
b(row) = b(row)-mul*b(column);
mulDivOp = mulDivOp+N-column+2;
subAddOp = subAddOp +N-column+1;
end
end
for row=N:-1:1
x(row) = b(row);
for i=(row+1):N
x(row) = x(row)-A(row,i)*x(i);
end
x(row) = x(row)/A(row,row);
mulDivOp = mulDivOp + N-row + 1;
subAddOp = subAddOp + N-row;
end
x = x';
mulDivOp
subAddOp
return
end
but I am curious if I can reduce the number of multiplications/divisions and additions/subtractions in case I know which elements of matrix are 0:
For N = 10:
A =
96 118 0 0 0 0 0 0 0 63
154 -31 -258 0 0 0 0 0 0 0
0 -168 257 -216 0 0 0 0 0 0
0 0 202 24 308 0 0 0 0 0
0 0 0 -262 -36 -244 0 0 0 0
0 0 0 0 287 -308 171 0 0 0
0 0 0 0 0 197 229 -258 0 0
0 0 0 0 0 0 -62 -149 186 0
0 0 0 0 0 0 0 -43 255 -198
-147 0 0 0 0 0 0 0 -147 -220
(non-zero values are from randi). In general, non-zero elements are a_{1, N}, a_{N,1} and a_{i,j} when abs(i-j) <= 1.
Probably not. There are nice algorithms for reducing tridiagonal matrices (which these aren't, but they are close) to diagonal matrices. Indeed, this is one way in which the SVD of a matrix is produced, using orthogonal similarity transformations, not Gaussian elimination.
The problem is that when you use Gaussian elimination to remove the nonzero entries in the first column, you will have introduced additional nonzero entries in the other columns. The further you proceed, the more you destroy the structure of the matrix. It may be that Gaussian elimination is simply the wrong approach for the problem you are trying to solve, at least if you are trying to exploit the structure of the matrix.
I would like to create a three dimensional surface using this:
>> a=X
a =
Columns 1 through 8
0 50 100 150 200 250 300 350
Columns 9 through 16
400 450 500 550 600 650 700 750
Columns 17 through 21
800 850 900 950 1000
>> b=Y
b =
0
50
100
150
200
250
300
350
400
>> c=Z
c =
Columns 1 through 8
0 0 0 0 0 0 0 0
16 32 67 98 127 164 194 234
120 171 388 773 1086 1216 1770 2206
189 270 494 1978 2755 3134 5060 10469
133 166 183 348 647 937 1446 2304
192 162 154 113 161 189 266 482
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
Columns 9 through 16
0 0 0 0 0 0 0 0
366 604 529 504 346 226 228 179
4027 11186 10276 5349 2560 1322 996 799
27413 76387 37949 15591 5804 2654 1803 1069
9844 24152 14772 4613 1777 849 459 290
1288 2623 1538 582 280 148 90 56
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
Columns 17 through 21
0 0 0 0 0
108 94 79 0 0
646 476 612 0 0
884 858 722 0 0
266 215 139 0 0
48 48 31 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
>> surf(X,Y,Z)
At the same time I would like to define that Z values < = 1803 will be shown with red color on the surface graph, 1803 < Z < 2755 yellow and Z > = 2755 green. The limits of the colorbar can be the min and max values of Z (from 0 to 76387). How can I set the ranges of the colorbar in order to get this result?
This will do as you ask:
%# add red (row 1), yellow (row 2), green (row 2)
map = [1 0 0; 0 1 1; 0 1 0];
%# set the new map as the current map
colormap(map);
colors = zeros(size(c)); %# create colors array
colors(c <= 1803) = 1; %# red (1)
colors(c > 1803 & c < 2755) = 2; %# yellow (2)
colors(c >= 2755) = 3; %# green (3)
%# and pass it into surf
surf(a,b,c, colors)
I was wondering if anyone could help me vectorize this piece of code.
fr_bw is a matrix.
for i=1:height
for j=1:width
[min_w, min_w_index] = min(w(i,j,:));
mean(i,j,min_w_index) = double(fr_bw(i,j));
sd(i,j,min_w_index) = sd_init;
end
end
I can't help you with this sif (match == 0) stuff -- if it's supposed to be if (match == 0) you're not changing match so it could be brought outside the loop.
Otherwise, how about this:
[min_w, min_w_index] = min(w, [], 3);
r = repmat((1:height)',1,width);
c = repmat(1:width,height,1);
ind = sub2ind(size(w),r(:),c(:),min_w_index(:));
w_mean(ind) = double(fr_bw);
w_sd(ind) = repmat(sd_init,height,width);
(Please note that mean is a built-in function so I renamed your variables to w_mean and w_sd.)
The sub2ind call gives you linear indices that correspond to subscripts. (Direct subscripts won't work; z([a1 a2 a3],[b1 b2 b3],[c1 c2 c3]) refers to 27 elements in the z array with subscripts that are the cartesian product of the specified subscripts, rather than z(a1,b1,c1) and z(a2,b2,c2) and z(a3,b3,c3) that you might expect.)
Here's an illustration of this technique:
>> height = 6; width = 4;
>> w = randi(1000,height,width,2)
w(:,:,1) =
426 599 69 719
313 471 320 969
162 696 531 532
179 700 655 326
423 639 408 106
95 34 820 611
w(:,:,2) =
779 441 638 696
424 528 958 68
91 458 241 255
267 876 677 225
154 519 290 668
282 944 672 845
>> [min_w, min_w_index] = min(w, [], 3);
>> min_w_index
min_w_index =
1 2 1 2
1 1 1 2
2 2 2 2
1 1 1 2
2 2 2 1
1 1 2 1
>> z = zeros(height,width,2);
>> r = repmat((1:height)',1,width);
>> c = repmat(1:width,height,1);
>> ind = sub2ind(size(w),r(:),c(:),min_w_index(:));
>> z(ind) = 1
z(:,:,1) =
1 0 1 0
1 1 1 0
0 0 0 0
1 1 1 0
0 0 0 1
1 1 0 1
z(:,:,2) =
0 1 0 1
0 0 0 1
1 1 1 1
0 0 0 1
1 1 1 0
0 0 1 0
A few comments on your code:
Did you mean if rather than sif?
The code isn't replicable at the moment, since you haven't provided examples of the variables w, fr_bw and sd_init. This makes it tricky to give an exact answer.
It looks like you are assigning things to a variable named mean. This will shadow the mean function, and probably cause you grief.
I'm just guessing, but I don't think double does what you think it does. You don't need to convert individual elements of a numeric matrix to type double; they are already the correct type. (On the other hand, if fr_bw is a different type, say integers, then you should create a new variable dbl_fr_bw = double(fr_bw); before the loops.
You might need to adjust the dimension over which you calculate the minimums, but the first line of the loop can be replaced with
[min_w, min_w_index] = min(w, [], 3)
The second line with
mean_values(:, :, min_w_index) = double(fr_bw)
Not sure about the third line, since I don't know what sd_init is/does.
I have a matrix in MATLAB. I want to check the 4-connected neighbours (left, right, top, bottom) for every element. If the current element is less than any of the neighbours then we set it to zero otherwise it will keep its value. It can easily be done with loop, but it is very expensive as I have thousands of these matrices.
You might recognize it as nonmaxima suppression after edge detection.
If you have the image processing toolbox, you can do this with a morpological dilation to find local maxima and suppress all other elements.
array = magic(6); %# make some data
msk = [0 1 0;1 0 1;0 1 0]; %# make a 4-neighbour mask
%# dilation will replace the center pixel with the
%# maximum of its neighbors
maxNeighbour = imdilate(array,msk);
%# set pix to zero if less than neighbors
array(array<maxNeighbour) = 0;
array =
35 0 0 26 0 0
0 32 0 0 0 25
31 0 0 0 27 0
0 0 0 0 0 0
30 0 34 0 0 16
0 36 0 0 18 0
edited to use the same data as #gnovice, and to fix the code
One way to do this is with the function NLFILTER from the Image Processing Toolbox, which applies a given function to each M-by-N block of a matrix:
>> A = magic(6) %# A sample matrix
A =
35 1 6 26 19 24
3 32 7 21 23 25
31 9 2 22 27 20
8 28 33 17 10 15
30 5 34 12 14 16
4 36 29 13 18 11
>> B = nlfilter(A,[3 3],#(b) b(5)*all(b(5) >= b([2 4 6 8])))
B =
35 0 0 26 0 0
0 32 0 0 0 25
31 0 0 0 27 0
0 0 0 0 0 0
30 0 34 0 0 16
0 36 0 0 18 0
The above code defines an anonymous function which uses linear indexing to get the center element of a 3-by-3 submatrix b(5) and compare it to its 4-connected neighbors b([2 4 6 8]). The value in the center element is multiplied by the logical result returned by the function ALL, which is 1 when the center element is larger than all of its nearest neighbors and 0 otherwise.
If you don't have access to the Image Processing Toolbox, another way to accomplish this is by constructing four matrices representing the top, right, bottom and left first differences for each point and then searching for corresponding elements in all four matrices that are non-negative (i.e. the element exceeds all of its neighbours).
Here's the idea broken down...
Generate some test data:
>> sizeA = 3;
A = randi(255, sizeA)
A =
254 131 94
135 10 124
105 191 84
Pad the borders with zero-elements:
>> A2 = zeros(sizeA+2) * -Inf;
A2(2:end-1,2:end-1) = A
A2 =
0 0 0 0 0
0 254 131 94 0
0 135 10 124 0
0 105 191 84 0
0 0 0 0 0
Construct the four first-difference matrices:
>> leftDiff = A2(2:end-1,2:end-1) - A2(2:end-1,1:end-2)
leftDiff =
254 -123 -37
135 -125 114
105 86 -107
>> topDiff = A2(2:end-1,2:end-1) - A2(1:end-2,2:end-1)
topDiff =
254 131 94
-119 -121 30
-30 181 -40
>> rightDiff = A2(2:end-1,2:end-1) - A2(2:end-1,3:end)
rightDiff =
123 37 94
125 -114 124
-86 107 84
>> bottomDiff = A2(2:end-1,2:end-1) - A2(3:end,2:end-1)
bottomDiff =
119 121 -30
30 -181 40
105 191 84
Find the elements that exceed all of the neighbours:
indexKeep = find(leftDiff >= 0 & topDiff >= 0 & rightDiff >= 0 & bottomDiff >= 0)
Create the resulting matrix:
>> B = zeros(sizeA);
B(indexKeep) = A(indexKeep)
B =
254 0 0
0 0 124
0 191 0
After wrapping this all into a function and testing it on 1000 random 100x100 matrices, the algorithm appears to be quite fast:
>> tic;
for ii = 1:1000
A = randi(255, 100);
B = test(A);
end; toc
Elapsed time is 0.861121 seconds.