I need help with the following functions: histc and numel in either a for loop or vectorized code. I have a matrix which could be of any dimension. The code needs to output the number of occurrences an element occurs in an interval until the the end of each row. So for the following example, I want to find how many occurrences the number 1 occurs in row 1. So in row 1, the number one occurs two times before being interrupted by two 0's. Then it occurs once more in the last column. So the output would be 2 1.
I appreciate any help. Thank you
x = hist( data, numel(unique(data)) );
y = histc( data, unique(data) );
data (input) 5x5
1 1 0 0 1
1 1 1 1
0 0 1
0 0 1 1 1
1 0 1 1 1
y (output)
2 1
2 2
1
3
1 3
Assuming x to be the input array, this could be one approach -
[nrows,ncols] = size(x);
new_ncols = ncols + 2;
%// Pad one column of zeros on left and right sides of x
x_pz = [zeros(nrows,1) x zeros(nrows,1)]
%// Flatten padded x
x_pzf = reshape(x_pz',[],1)'
%// Start & end indices of islands of ones for flattened padded x
starts = strfind(x_pzf,[1 0]);
ends = strfind(x_pzf,[0 1])
row_ids = ceil(starts/new_ncols); %// row IDs for each island of ones
%// Start & end indices of islands of ones for flattened non-padded (corrected) x
starts_cor = ends - 2*(row_ids-1)
ends_cor = starts - (2*row_ids-1)
%// Get number of elements in each island of ones
counts = ends_cor - starts_cor + 1
%// Bin row_ids for each row of input array
counts_per_row = histc(row_ids,1:nrows)
%// Now setup output array with conts for each island corresponding to each
%// row ending up in its each row and setting the blank spaces as NaNs
mask = bsxfun(#ge,counts_per_row,(1:max(counts_per_row))') %//'
y = NaN(size(mask))
y(mask) = counts
y = y'
Code run -
>> x (modified from the original one to test out more varied situations)
x =
1 1 0 0 1 1
1 1 0 1 1 0
0 0 0 0 1 0
0 0 1 1 1 0
1 0 1 1 1 1
1 1 1 1 1 1
0 0 0 0 0 0
0 1 0 1 0 1
>> y
y =
2 2 NaN
2 2 NaN
1 NaN NaN
3 NaN NaN
1 4 NaN
6 NaN NaN
NaN NaN NaN
1 1 1
If you are looking for a more intuitive and concise way to obtain and display the final output, you can use cell array for that. So, you can do something like this -
>> ycell = arrayfun(#(n) counts(row_ids==n),1:nrows,'Uni',0);
>> celldisp(ycell)
ycell{1} =
2 2
ycell{2} =
2 2
ycell{3} =
1
ycell{4} =
3
ycell{5} =
1 4
ycell{6} =
6
ycell{7} =
[]
ycell{8} =
1 1 1
Related
I have 2 matrices. Matrix A is already defined.
Matrix A = [10 7 8 4 1 6;
2 6 4 3 5 1;
7 3 2 2 8 7;
6 2 3 10 11 4;
1 5 1 2 4 5]
Matrix B = [1 1 1 0 0 0;
0 0 0 0 0 0;
0 0 0 0 0 1;
0 0 0 1 1 0;
0 0 0 0 0 0]
Here each row has to have at least one '1'. This is our main target, so if there is a row with no 1's in it, we will have to move a '1' from another row.
How will we move the 1?
1) Check which rows that are all zeros. In this example, row 2 and 5.
2) We will subtract these 2 rows with all other rows that contain 1's in Matrix A. Which means that row 2 and row 5 in matrix A will be subtracted by all the other rows.
3) After the subtraction, we will check the change between each 2 subtracted rows, in the places of the 1's.
For example:
subtracting row 2 (all zeros) from row 1 will give us this [8 1 4] and
subtracting row 5 (all zeros) from row 1 will give us this [9 2 7].
subtracting row 2 (all zeros) from row 3 will give us this [6] and
subtracting row 5 (all zeros) from row 3 will give us this [2]
subtracting row 2 (all zeros) from row 4 will give us this [7 6] and
subtracting row 5 (all zeros) from row 4 will give us this [8 7]..
4) In the places of the 1's we will check the change between the rows and see the minimum change. The column that satisfied the minimum change, we will put the 1 in its place and remove it from the old place.
For example here:
For row 2, we will see where was the minimum change. Here the minimum change for row 2 was 1, which is in row 1. So we will remove the 1 of this column, and move it to the same column in row 2.
[1 0 1 0 0 0;
0 1 0 0 0 0;
0 0 0 0 0 1;
0 0 0 1 1 0;
0 0 0 0 0 0]
subtracting row 5 (all zeros) from row 1 will give us this [8 4].
subtracting row 5 (all zeros) from row 3 will give us this [2]
subtracting row 5 (all zeros) from row 4 will give us this [8 7]..
The same for row 5, check the minimum change. Here it is 2 but it is the only one in the array which is in row 3. So for this case we want to add a condition, that we don't do the subtraction method unless there is two 1's or more in the row. so we will move to another minimum change which is here the 4 which is the change from row 1 so will remove the 1 in row 1 and put it in row 5
so here the output will be =
[1 0 0 0 0 0;
0 1 0 0 0 0;
0 0 0 0 0 1;
0 0 0 1 1 0;
0 0 1 0 0 0]
and now the condition is satisfied, each row has at least one 1.
This is what i wrote in the code
%search for zero-rows in matrix B
minim = max(A) % Set the minimum value as an initial solution
zeroRows = find(sum(B,2)==0);
nonZeroRows = find(sum(B,2)~=0);
x = [];
y = [];
for zi = zeroRows'
for nZi = nonZeroRows'
%gives row vector of A with elements, where corresponding B elements are 1
%this row nZi in B is not zero
nonZeroRow = A(nZi,B(nZi,:) ==1);
nonZeroFull = A(nZi,:)
%gives row vector of A with elements, where corresponding B elements are 1
%this row zi in B is zero
zeroRow =A(zi, B(nZi,:) == 1);
zeroFull = A(zi,:)
%calculate the distance
disp(strcat('row ',num2str(nZi), ' - row ', num2str(zi)))
change = abs(nonZeroRow - zeroRow)
changeFull = nonZeroFull - zeroFull
x = [x change]
y = [y;changeFull]
Minimumchange = min(x)
if(Minimumchange < minim)
minim = Minimumchange
intersection = intersect(y,minim)
for i = 1 : length(intersection)
[w Index_intersection] = find(y == intersection(i))
B(zi,Index_intersection) = 0
B(nZi,Index_intersection) = 1
end
end
end
end
This is the code so far but its not giving the right output
Ask me if the question is still not clear.!
Since I can't comment due to the lack of my reputation I'll try it this way...
this would be your steps 1,2 and 3:
A = [10 7 8 4 1 6;
2 6 4 3 5 1;
7 3 2 2 8 7;
6 2 3 10 11 4;
1 5 1 2 4 5];
B = [1 1 1 0 0 0;
0 0 0 0 0 0;
0 0 0 0 0 1;
0 0 0 1 1 0;
0 0 0 0 0 0];
%search for zero-rows in matrix B
zeroRows = find(sum(B,2)==0);
nonZeroRows = find(sum(B,2)~=0);
for nZi = nonZeroRows'
for zi = zeroRows'
%gives row vector of A with elements, where corresponding B elements are 1
%this row nZi in B is not zero
nonZeroRow = A(nZi,B(nZi,:) ==1);
%gives row vector of A with elements, where corresponding B elements are 1
%this row zi in B is zero
zeroRow =A(zi, B(nZi,:) == 1);
%calculate the distance
disp(strcat('row ',num2str(nZi), ' - row ', num2str(zi)))
abs(nonZeroRow - zeroRow)
end
end
However I don't understand what you mean in step 4. Could you formulated your step 4 shorter and more precisely? Or give some examples with intermediate steps?
I have a matrix with some zero values I want to erase.
a=[ 1 2 3 0 0; 1 0 1 3 2; 0 1 2 5 0]
>>a =
1 2 3 0 0
1 0 1 3 2
0 1 2 5 0
However, I want to erase only the ones after the last non-zero value of each line.
This means that I want to retain 1 2 3 from the first line, 1 0 1 3 2 from the second and 0 1 2 5 from the third.
I want to then store the remaining values in a vector. In the case of the example this would result in the vector
b=[1 2 3 1 0 1 3 2 0 1 2 5]
The only way I figured out involves a for loop that I would like to avoid:
b=[];
for ii=1:size(a,1)
l=max(find(a(ii,:)));
b=[b a(ii,1:l)];
end
Is there a way to vectorize this code?
There are many possible ways to do this, here is my approach:
arotate = a' %//rotate the matrix a by 90 degrees
b=flipud(arotate) %//flips the matrix up and down
c= flipud(cumsum(b,1)) %//cumulative sum the matrix rows -and then flip it back.
arotate(c==0)=[]
arotate =
1 2 3 1 0 1 3 2 0 1 2 5
=========================EDIT=====================
just realized cumsum can have direction parameter so this should do:
arotate = a'
b = cumsum(arotate,1,'reverse')
arotate(b==0)=[]
This direction parameter was not available on my 2010b version, but should be there for you if you are using 2013a or above.
Here's an approach using bsxfun's masking capability -
M = size(a,2); %// Save size parameter
at = a.'; %// Transpose input array, to be used for masked extraction
%// Index IDs of last non-zero for each row when looking from right side
[~,idx] = max(fliplr(a~=0),[],2);
%// Create a mask of elements that are to be picked up in a
%// transposed version of the input array using BSXFUN's broadcasting
out = at(bsxfun(#le,(1:M)',M+1-idx'))
Sample run (to showcase mask usage) -
>> a
a =
1 2 3 0 0
1 0 1 3 2
0 1 2 5 0
>> M = size(a,2);
>> at = a.';
>> [~,idx] = max(fliplr(a~=0),[],2);
>> bsxfun(#le,(1:M)',M+1-idx') %// mask to be used on transposed version
ans =
1 1 1
1 1 1
1 1 1
0 1 1
0 1 0
>> at(bsxfun(#le,(1:M)',M+1-idx')).'
ans =
1 2 3 1 0 1 3 2 0 1 2 5
This question already has answers here:
General method to find submatrix in matlab matrix
(3 answers)
Closed 8 years ago.
I have the vector:
1 2 3
and the matrix:
4 1 2 3 5 5
9 8 7 6 3 1
1 4 7 8 2 3
I am trying to find a simple way of locating the vector [1 2 3] in my matrix.
A function returning either coordinates (Ie: (1,2:4)) or a matrix of 1s where there is a match a 0s where there isn't, Ie:
0 1 1 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0
So far, the only function I've found is is 'ismember', which however only tells me if the individual components of the vector appear in the matrix. Suggestions?
Use strfind with a linearized version of the matrix, and then convert linear indices to subindices. Care should be taken to remove matches of the vector spanning different rows.
mat = [1 2 3 1 2 3 1 2;
3 0 1 2 3 5 4 4]; %// data
vec = [1 2 3]; %// data
ind = strfind(reshape(mat.',[],1).', vec);
[col row] = ind2sub(fliplr(size(mat)), ind);
keep = col<=size(mat,2)-length(vec)+1; %// remove result split across rows
row = row(keep);
col = col(keep);
Result for this example:
>> row, col
row =
1 1 2
col =
1 4 3
meaning the vector appears three times: row 1, col 1; row 1, col 4; row 2, col 3.
The result can be expressed in zero-one form as follows:
result = zeros(fliplr(size(mat)));
ind_ones = bsxfun(#plus, ind(keep).', 0:numel(vec)-1);
result(ind_ones) = 1;
result = result.';
which gives
>> result
result =
1 1 1 1 1 1 0 0
0 0 1 1 1 0 0 0
One way to get the starting location of the vector in the matrix is using colfilt:
>> A = [1 2 3 1 2 3 1 2; ...
3 0 1 2 3 5 4 4]; % matrix from Luis Mendo
>> T = [1 2 3];
>> colFun = #(x,t) all(x==repmat(t,1,size(x,2)),1);
>> B = colfilt(A,size(T),'sliding',colFun,T(:))
B =
0 1 0 0 1 0 0 0
0 0 0 1 0 0 0 0
That gives a mask of the center points, which translate to (row,col) coordinates:
>> [ii,jj]=find(B);
>> locs = bsxfun(#minus,[ii jj],floor((size(T)-1)/2))
locs =
1 1
2 3
1 4
I have a matrix including 1 and 0 elements like below which is used as a network adjacency matrix.
A =
0 1 1 1
1 1 0 1
1 1 0 1
1 1 1 0
I want to simulate an attack on the network, so I must replace some specific percent of 1 elements randomly with 0. How can I do this in MATLAB?
I know how to replace a percentage of elements randomly with zeros, but I must be sure that the element that is replaced randomly, is one of the 1 elements of matrix not zeros.
If you want to change each 1 with a certain probability:
p = 0.1%; % desired probability of change
A_ones = find(A); % linear index of ones in A
A_ones_change = A_ones(rand(size(A_ones))<=p); % entries to be changed
A(A_ones_change) = 0; % apply changes in those entries
If you want to randomly change a fixed fraction of the 1 entries:
f = 0.1; % desired fraction
A_ones = find(A);
n = round(f*length(A_ones));
A_ones_change = randsample(A_ones,n);
A(A_ones_change) = 0;
Note that in this case the resulting fraction may be different to that intended, because of the need to round to an integer number of entries.
#horchler's point is a good one. However, if we keep it simple, then you can just multiple your input matrix to a mask matrix.
>> a1=randint(5,5,[0 1]) #before replacing 1->0
a1 =
1 1 1 0 1
0 1 1 1 0
0 1 0 0 1
0 0 1 0 1
1 0 1 0 1
>> a2=random('unif',0,1,5,5) #Assuming frequency distribution is uniform ('unif')
a2 =
0.7889 0.3200 0.2679 0.8392 0.6299
0.4387 0.9601 0.4399 0.6288 0.3705
0.4983 0.7266 0.9334 0.1338 0.5751
0.2140 0.4120 0.6833 0.2071 0.4514
0.6435 0.7446 0.2126 0.6072 0.0439
>> a1.*(a2>0.1) #And the replacement prob. is 0.1
ans =
1 1 1 0 1
0 1 1 1 0
0 1 0 0 1
0 0 1 0 1
1 0 1 0 0
And other trick can be added to the mask matrix (a2). Such as a different freq. distribution, or a structure (e.g. once a cell is replaced, the adjacent cells become less likely to be replaced and so on.)
Cheers.
The function find is your friend:
indices = find(A);
This will return an array of the indices of 1 elements in your matrix A and you can use your method of replacing a percent of elements with zero on a subset of this array. Then,
A(subsetIndices) = 0;
will replace the remaining indices of A with zero.
I want to create a variable that finds a pattern (let's say [1 1]) in different rows of a matrix (A). Of course there aren't an equal number of occurrences of this string in each row.
A = [ 0 0 0 1 1
1 1 1 0 0
0 1 0 1 1
1 1 1 0 0
0 1 0 0 1
1 0 1 1 1
0 1 0 1 0
1 1 1 0 1];
I could do:
for i = 1:n
var(i,:) = strfind(A(i,:),[1 1]);
end
but then both sides of the equation won't be equal.
ERROR: ??? Subscripted assignment dimension mismatch.
I try to preallocate. I create a matrix with what I think would be the maximum number of occurrences of this string in each row of matrix A (let's say 50).
for i = 1:n
var(i, :) = NaN(1,50)
end
That's followed by the previous bit of code and it's no good either.
I've also tried:
for i = 1:n
var(i,1:numel(strfind(A(i,:),[1 1])) = strfind(A(i,:),[1 1])
end
Error: The expression to the left of the equals sign is not a valid
target for an assignment.
How should I go about doing this?
The output I expect is a matrix var(i,:) that gives me the position in the matrix where each of these patterns occur. It works fine for just one row.
For example:
var(1,:) = [1 2 5 8 10 22 48]
var(2,:) = [2 3 4 7 34 45 NaN]
var(3,:) = [4 5 21 32 33 NaN]
Thanks!
In your first try: you tried to build a matrix with different length of rows.
In your second try: you pre-allocated, but then run it over by re-definning var(i,:), while you tried to put there your desired result.
In your third try: unfortunately you just missed one brackets- ) at the end of left expression.
This code suppose to work (what you did at 2nd and 3rd attempts, with pre-allocate and fixed brackets):
var=NaN(1,50);
for i = 1:n
var(i,1:numel(strfind(A(i,:),[1 1]))) = strfind(A(i,:),[1 1])
end