How to make matlab repeat a procedure independently? - matlab

I have a matrix like this:
A =
1 1 1 0 1
0 1 1 0 0
0 0 0 0 1
1 0 0 0 0
0 1 0 1 1
I want to replace for example 30% of 1 elements in this matrix with 0 randomly and repeat this procedure independently 10 times for instance, and at the end of the work I must have 10 independent matrices which each one of them should has 30% of 1 elements less than the original matrix.
here's the code I use to do this:
for i=1:10
f=.3;
A_ones=find(A);
n = round(f*length(A_ones));
A_ones_change = randsample(A_ones,n);
A(A_ones_change) = 0;
end
A
But the thing that matlab does with this code is that it takes the original matrix A at the begining and replaces 30% of its 1 elements with 0. But for the second time it takes the resultant matrix from previous step as A (not the original matrix) and replaces 30% of remained 1 elements in that matrix with 0 and does it again and again for 10 times and at the end it gives me only 1 matrix like below:
A =
0 0 0 0 0
1 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
How can I solve this problem and make matlab to do this procedure on 'original matrix A' for each i?

Your original A is changing every time because of the last line in your loop A(A_ones_change) = 0;. Change it to some copy of A (say, A1 = A) you make in the beginning of the loop.

Related

Performing an averaging operation over every n elements in a vector

I have a logical vector in which I would like to iterate over every n-elements. If in any given window at least 50% are 1's, then I change every element to 1, else I keep as is and move to the next window. For example.
n = 4;
input = [0 0 0 1 0 1 1 0 0 0 0 1 0 1 0 1 0 0 0 1];
output = func(input,4);
output = [0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 0 0 0 1];
This function is trivial to implement but is it possible to apply a vectorized implementation using logical indexing?. I am trying to build up the intuition of applying this technique.
here's a one liner (that works for your input):
func = #(input,n) input | kron(sum(reshape(input ,n,[]))>=n/2,ones(1,n));
of course, there are cases to solve that this doesnt answer, what if the size of the input is not commensurate in n? etc...
i'm not sure if that's what you meant by vectorization, and I didnt benchmark it vs a for loop...
Here is one way of doing it. Once understood you can compact it in less lines but I'll details the intermediate steps for the sake of clarity.
%% The inputs
n = 4;
input = [0 0 0 1 0 1 1 0 0 0 0 1 0 1 0 1 0 0 0 1];
1) Split your input into blocks of size n (note that your final function will have to check that the number of elements in input is a integer multiple of n)
c = reshape(input,n,[]) ;
Gives you a matrix with your blocks organized in columns:
c =
0 0 0 0 0
0 1 0 1 0
0 1 0 0 0
1 0 1 1 1
2) Perform your test condition on each of the block. For this we'll take advantage that Matlab is working column wise for the sum function:
>> cr = sum(c) >= (n/2)
cr =
0 1 0 1 0
Now you have a logical vector cr containing as many elements as initial blocks. Each value is the result of the test condition over the block. The 0 blocks will be left unchanged, the 1 blocks will be forced to value 1.
3) Force 1 columns/block to value 1:
>> c(:,cr) = 1
c =
0 1 0 1 0
0 1 0 1 0
0 1 0 1 0
1 1 1 1 1
4) Now all is left is to unfold your matrix. You can do it several ways:
res = c(:) ; %% will give you a column vector
OR
>> res = reshape(c,1,[]) %% will give you a line vector
res =
0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 0 0 0 1

Using a Matrix to Populate Every Other Value in a Column

Im learning matlab as I have free time.
Matrix B is half the length of Matrix C
I want Every Value in the first column of matrix B to populate every other Value in the first column of Matrix C
To do this Im using a for loop. Currently I get every other value populated with the last number in the column of matrix B. What do I need to change?
Z=length(A);
X = length(B);
C = zeros(size(A);
for n = 1:2:Z
for m = 1:1:X
C(n,1) = B(m,1);
end
end
You can use the kron function:
reps = 2;
delta = zeros(reps,1);
B = magic(5);
C = kron(B,delta);
Here rep indicates that every second value should be taken from B. But you can also duplicate the values by using delta = ones(reps,1);, or any mix that you are interested in, by modifying delta.
Additionally you can change the dimension of delta to a row vector and then can duplicate values along the rows. Or by allowing delta to be a matrix you can be get more fancy stuff.
You can simply write this:
B = magic(4); % The small matrix with the values
C = zeros(size(A)*2); % The matrix to fill the values in
C(1:2:end,1) = B(:,1) % populating the matrix
and you get:
C =
16 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
5 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
9 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0

Get the indexes of the boundary cells of a subset of a matrix. Matlab

Given a matrix where 1 is the current subset
test =
0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 1 0 0
0 0 1 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0
Is there a function, or quick method to get change the subset to the boundary of the current subset?
Eg. Get this subset from 'test' above
test =
0 0 0 0 0 0
0 1 1 1 1 0
0 1 0 0 1 0
0 1 0 0 1 0
0 1 1 1 1 0
0 0 0 0 0 0
In the end I just want to get the minimum of the cells surrounding a subset of a matrix. Sure I could loop through and get the minimum of the boundary (cell by cell), but there must be a way to do it with the method i've shown above.
Note the subset WILL be connected, but may not be rectangular. This may be the big catch.
This is a possible subset.... (Would pad this with a NaN border)
test =
0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 1 0 0
0 0 1 1 0 0
0 0 1 1 1 1
0 0 1 1 1 1
Ideas?
The basic steps I'd use are:
Perform a dilation on the shape to get a new area which is the shape plus its boundary
Subtract the original shape from the dilated shape to leave just the boundary
Use the boundary to index your data matrix, then take the minimum.
Dilation
What I want to do here is pass a 3x3 window over each cell and take the maximum value in that window:
[m, n] = size(A); % assuming A is your original shape matrix
APadded = zeros(m + 2, n + 2);
APadded(2:end-1, 2:end-1) = A; % pad A with zeroes on each side
ADilated = zeros(m + 2, n + 2); % this will hold the dilated shape.
for i = 1:m
for j = 1:n
mask = zeros(size(APadded));
mask(i:i+2, j:j+2) = 1; % this places a 3x3 square of 1's around (i, j)
ADilated(i + 1, j + 1) = max(APadded(mask));
end
end
Shape subtraction
This is basically a logical AND and a logical NOT to remove the intersection:
ABoundary = ADilated & (~APadded);
At this stage you may want to remove the border we added to do the dilation, since we don't need it any more.
ABoundary = ABoundary(2:end-1, 2:end-1);
Find the minimum data point along the boundary
We can use our logical boundary to index the original data into a vector, then just take the minimum of that vector.
dataMinimum = min(data(ABoundary));
You should look at this as morphology problem, not set theory. This can be solved pretty easily with imdilate() (requires the image package). You basically only need to subtract the image to its dilation with a 3x3 matrix of 1.
octave> test = logical ([0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 1 0 0
0 0 1 1 0 0
0 0 1 1 1 1
0 0 1 1 1 1]);
octave> imdilate (test, true (3)) - test
ans =
0 0 0 0 0 0
0 1 1 1 1 0
0 1 0 0 1 0
0 1 0 0 1 1
0 1 0 0 0 0
0 1 0 0 0 0
It does not, however, pads with NaN. If you really want that, you could pad your original matrix with false, do the operation, and then check if there's any true values in the border.
Note that you don't have to use logical() in which case you'll have to use ones() instead of true(). But that takes more memory and has worse performance.
EDIT: since you are trying to do it without using any matlab toolbox, take a look at the source of imdilate() in Octave. For the case of logical matrices (which is your case) it's a simple usage of filter2() which belongs to matlab core. That said, the following one line should work fine and be much faster
octave> (filter2 (true (3), test) > 0) - test
ans =
0 0 0 0 0 0
0 1 1 1 1 0
0 1 0 0 1 0
0 1 0 0 1 1
0 1 0 0 0 0
0 1 0 0 0 0
One possible solution is to take the subset and add it to the original matrix, but ensure that each time you add it, you offset its position by +1 row, -1 row and +1 column, -1 column. The result will then be expanded by one row and column all around the original subset. You then use the original matrix to mask the original subet to zero.
Like this:
test_new = test + ...
[[test(2:end,2:end);zeros(1,size(test,1)-1)],zeros(size(test,1),1)] + ... %move subset up-left
[[zeros(1,size(test,1)-1);test(1:end-1,2:end)],zeros(size(test,1),1)] + ... %move down-left
[zeros(size(test,1),1),[test(2:end,1:end-1);zeros(1,size(test,1)-1)]] + ... %move subset up-right
[zeros(size(test,1),1),[zeros(1,size(test,1)-1);test(1:end-1,1:end-1)]]; %move subset down-right
test_masked = test_new.*~test; %mask with original matrix
result = test_masked;
result(result>1)=1; % ensure that there is only 1's, not 2, 3, etc.
The result for this on your test matrix is:
result =
0 0 0 0 0 0
0 1 1 1 1 0
0 1 0 0 1 0
0 1 0 0 1 1
0 1 0 0 0 0
0 1 0 0 0 0
Edited - it now grabs the corners as well, by moving the subset up and to the left, up and to the right, down then left and down then right.
I expect this would be a very quick way to achieve this - it doesn't have any loops, nor functions - just matrix operations.

Working on Binary Data in Matlab

I have loaded a text file test file in matlab. The load function has now created a variable test with values
1 2 3 4 5
2 3 NaN NaN NaN
Now I have a initialized variable X = [0 0 0 0 0 0 0 0 0 0 0] and Y = [0 0 0 0 0 0 0 0 0 0 0].
I want X to read the first row of test variable and corresponding to the number it reads change its value of the element from 0 to 1. Suppose it reads 1, it should change its element X(1) from 0 to 1. Similarly Y should do the same wrt second row of test.
Any idea how should I proceed?
You can index directly into a vector like so:
x=zeros(1,10);%# your zero vector
a=[1,3,7,8]; %# the index of sports you like
x(a)=1 %# change from 0 to 1
x =
1 0 1 0 0 0 1 1 0 0
Going by your comment, I think this is what you want.

Using find function on columns and rows in matlab

I am having some problems with the find function in MATLAB. I have a matrix consisting of zeros and ones (representing the geometry of a structural element), where material is present when the matrix element = 1, and where no material is present when the matrix element = 0. The matrix may have the general form shown below (it will update as the geometry is changed, but that isn't too important).
Geometry = [0 0 0 0 0 0 0 0 0 0;
0 0 1 0 1 0 1 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 1 0 0;
0 0 0 0 0 0 0 0 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 1 1 1 0 1 0 0;
0 0 0 0 0 0 0 0 0 0;]
I'm trying to find the the rows and columns that are not continuously connected (i.e. where the row and columns are not all equal to 1 between the outer extents of the row or column) and then update them so they are all connected. I.e. the matrix above becomes:
Geometry = [0 0 0 0 0 0 0 0 0 0;
0 0 1 1 1 1 1 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 0 0 0 0 1 0 0;
0 0 1 1 1 1 1 1 0 0;
0 0 0 0 0 0 0 0 0 0;]
The problem I am having is I want to be able to find the indices of the first and last element that is equal to 1 in each row (and column), which will then be used to update the geoemtry matrix.
Ideally, I want to represent these in vectors, so going across the columns, find the row number of the first element equal to 1 and store this in a vector called rowfirst.
I.e.:
rowfirst = zeros(1,numcols)
for i = 1:numcols % Going across the columns
rowfirst(i) = find(Geometry(i,1) == 1, 1,'first')
% Store values in vector called rowfirst
end
and the repeat this for the columns and to find the last elements in each row.
For some reason, I can't get the values to store properly in the vector, does anyone have an idea of where I'm going wrong?
Thanks in advance. Please let me know if that isn't clear, as I may not have explained the problem very well.
0) bwmorph(Geometry,'close') dose it all in one line. If the holes may be bigger, try bwmorph(Geometry,'close',Inf).
Regarding your attempt:
1) It should be Geometry(i,:) instead of Geometry(i,1).
2) Your real problem here is empty matrices. Actually, what do you want rowfirst(i) to be if there are no 1s in the i'th row?
Ok, I can spot two mistakes:
You should use an array as the first argument of find. So, if you want to find the row number of the first element of each column, then you should use find(Geometry(:, i), 1, 'first').
Find returns an empty array if the column contains only zeros. You should handle this case and decide what number you want to put into rownumber (e.g. you can put -1, to indicate that the corresponding column contains no non-zero elements).
Following the above, you can try this:
for i = 1:numcols
tmp = find(Geometry(:, i), 1, 'first');
if(tmp)
rowfirst(i) = tmp;
else
rowfirst(i) = -1;
end;
end;
I'm pretty sure there's a more efficient way of doing this, but if you replace your call to find with this, it should work ok:
find(Geometry(i,:), 1,'first')
(otherwise you're just looking at the first cell of the ith row. And the == 1 is useless, since find already returns only non-zero elements, and your matrix is binary)
Use the AccumArray() function to find the min and max col (row) number.
Imagine finding the last (first) row in each column that contains a NaN.
a = [1 nan nan nan ;
2 2 3 4;
3 nan 3 3;
4 nan 4 4]
This code gets the row indices for the last NaN in each column.
[row,col] = find(isnan(a))
accumarray(col,row,[],#max)
This code gets the row indices for the first NaN in each column.
[row,col] = find(isnan(a))
accumarray(col,row,[],#min)
Swap the row and col variables to scan row-wise instead of column-wise.
This answer inspired by Finding value and index of min value in a matrix, grouped by column values