This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Count number of values in matrix within given range
I have the following matrix
A=[ 1 2 3 4 5 6 7;
3 4 5 6 8 9 1;
3 4 2 5 6 7 8]
How to find out how many element there are in the first row, which have a value in the range >2 & <6?
If I understood well what you mean with account, if you want to find elements that meet your criteria:
B = A(1,:)
find(B > 2 & B < 6)
or in one line:
find(A(1,:) > 2 & A(1,:) < 6)
You must know also that find gives you the indices, not the actual elements, of the array that satisfy the logical expression given.
You need to think in terms of matrices, using vectorized computations. (Using find is not a good solution here, since it is not vectorized to operate independently on the rows.)
First, what happens if you try the test, A > 2, in MATLAB? TRY IT!
A=[ 1 2 3 4 5 6 7;
3 4 5 6 8 9 1;
3 4 2 5 6 7 8];
>> A > 2
ans =
0 0 1 1 1 1 1
1 1 1 1 1 1 0
1 1 0 1 1 1 1
MATLAB generates a logical array, true where an element of A was greater than 2. Similarly, if we type A < 6, we get another logical array.
>> A < 6
ans =
1 1 1 1 1 0 0
1 1 1 0 0 0 1
1 1 1 1 0 0 0
What if we combine the two ops into one expression?
>> (A > 2) & (A < 6)
ans =
0 0 1 1 1 0 0
1 1 1 0 0 0 0
1 1 0 1 0 0 0
Ok, so this array tells us where both sub-expressions were true. Note that we can operate on a logical array using sum and other arithmetic ops.
>> sum((A > 2) & (A < 6),2)
ans =
3
3
3
So the above expression counts the number of elements that satisfy your goal for each row, and it will be quite fast. Learn to use MATLAB as it should be used. Your code will get better.
Related
I am essentially trying to figure out how to generate code for basis vectors of different configurations of M objects into N different states (for example, if I had 2 snacks between 2 kids, I could have (2,0) (0,2) or (1,1), terrible example, but thats the idea)
I am struggling to figure out how to do this without going into many different loops (I want this to be automatic). The idea would be to create a Matrix where each row is a vector of length M. I would start with vec(1) = N then an if loop where if sum(vec) == N, Matrix(1,:)=vec; Then I could take vec(1)=N-i and do the same.
My only issue is I do not see how to use the if and forget it so that if I had maybe 2 objects in 5 locations, how would I do this to get (1 0 0 0 1).
I am not seeing how to do this.
You could use a recursive function:
function out = combos(M,N)
if N == 1
out = M;
else
out = [];
for i = 0:M
subout = combos(M-i,N-1);
subout(:,end+1) = i;
out = [out;subout];
end
end
I think this does what you want.
The key idea is to generate not the number of elements in each group, but the split points between groups. This can be done via combinations with repetition. Matlab's nchoosek generates combinations without repetition, but these are easily converted into what we need.
M = 5; % number of objects
N = 3; % number of groups
t = nchoosek(1:M+N-1, N-1); % combinations without repetition...
t = bsxfun(#minus, t, 1:N-1); % ...convert into combinations with repetition
t = diff([zeros(size(t,1), 1) t repmat(M, size(t,1), 1) ], [], 2); % the size of each
% group is the distance between split points
In this example, the result is
t =
0 0 5
0 1 4
0 2 3
0 3 2
0 4 1
0 5 0
1 0 4
1 1 3
1 2 2
1 3 1
1 4 0
2 0 3
2 1 2
2 2 1
2 3 0
3 0 2
3 1 1
3 2 0
4 0 1
4 1 0
5 0 0
This is a similar approach to Luis' without bsxfun. Because we don't like fun.
n = 5;
k = 3;
c = nchoosek(n+k-1, k-1);
result = diff([zeros(c, 1) nchoosek(1:(n+k-1), k-1) ones(c, 1)*(n+k)], [], 2) - 1;
This creates the partitions of the integer n with length k. Given an array of length n + (k-1), we find all combinations of (k-1) places to place partitions between the (unary) integers. For 5 items and 3 locations, we have 7 choices of where to put the partitions:
[ 0 0 0 0 0 0 0 ]
If our chosen combination is [2 4], we replace positions 2 and 4 with partitions to look like this:
[ 0 | 0 | 0 0 0 ]
The O's give the value in unary, so this combination is 1 1 3. To recover the values easily, we just augment the combinations with imaginary partitions at the next values to the left and right of the array (0 and n+k) and take the difference and subtract 1 (because the partitions themselves don't contribute to the value):
diff([0 2 4 8]) - 1
ans =
1 1 3
By sliding the partitions in to each possible combination of positions, we get all of the partitions of n.
Output:
result =
0 0 5
0 1 4
0 2 3
0 3 2
0 4 1
0 5 0
1 0 4
1 1 3
1 2 2
1 3 1
1 4 0
2 0 3
2 1 2
2 2 1
2 3 0
3 0 2
3 1 1
3 2 0
4 0 1
4 1 0
5 0 0
[1 2 3 4 5 6 7 8 9 ;
9 8 7 6 5 4 3 2 1 ;
1 2 0 0 1 0 0 0 1 ]
The last row has five columns with zeros. I would like to keep only one column per zero crossing.
like this
[1 2 3 5 8 9 ;
9 8 7 5 2 1 ;
1 2 0 1 0 1 ]
Is this possible with fast Matlab functions or do I have to write some slow complicated for loop ?
You can create a logical array many different ways to find the columns to remove. Something like this would work
% Find the zeros that are not the first zero
cols_to_remove = data(end,:) == 0 & ~diff([false, data(end,:) == 0]) == 1;
% Now remove them
data(:, cols_to_remove) = [];
I have a two long vector. Vector one contains values of 0,1,2,3,4's, 0 represent no action, 1 represent action 1 and 2 represent the second action and so on. Each action is 720 sample point which means that you could find 720 consecutive twos then 720 consecutive 4s for example. Vector two contains raw data corresponding to each action. I need to create a matrix for each action ( 1, 2, 3 and 4) which contains the corresponding data of the second vector. For example matrix 1 should has all the data (vector 2 data) which occurred at the same indices of action 1. Any Help??
Example on small amount of data:
Vector 1: 0 0 1 1 1 0 0 2 2 2 0 0 1 1 1 0 0 2 2 2
Vector 2: 6 7 5 6 4 6 5 9 8 7 9 7 0 5 6 4 1 5 8 0
Result:
Matrix 1:
5 6 4
0 5 6
Matrix 2:
9 8 7
5 8 0
Here is one approach. I used a cell array to store the output matrices, hard-coding names for such variables isn't a good plan.
V1=[0 0 1 1 1 0 0 2 2 2 0 0 1 1 1 0 0 2 2 2]
V2=[6 7 5 6 4 6 5 9 8 7 9 7 0 5 6 4 1 5 8 0]
%// Find length of sequences of 1's/2's
len=find(diff(V1(find(diff(V1)~=0,1)+1:end))~=0,1)
I=unique(V1(V1>0)); %// This just finds how many matrices to make, 1 and 2 in this case
C=bsxfun(#eq,V1,I.'); %// The i-th row of C contains 1's where there are i's in V1
%// Now pick out the elements of V2 based on C, and store them in cell arrays
Matrix=arrayfun(#(m) reshape(V2(C(m,:)),len,[]).',I,'uni',0);
%// Note, the reshape converts from a vector to a matrix
%// Display results
Matrix{1}
Matrix{2}
Since, there is a regular pattern in the lengths of groups within Vector 1, that could be exploited to vectorize many things while proposing a solution. Here's one such implementation -
%// Form new vectors out of input vectors for non-zero elements in vec1
vec1n = vec1(vec1~=0)
vec2n = vec2(vec1~=0)
%// Find positions of group shifts and length of groups
df1 = diff(vec1n)~=0
grp_change = [true df1]
grplen = find(df1,1)
%// Reshape vec2n, so that we end up with N x grplen sized array
vec2nr = reshape(vec2n,grplen,[]).' %//'
%// ID/tag each group change based on their unique vector 2 values
[R,C] = sort(vec1n(grp_change))
%// Re-arrange rows of reshaped vector2, s.t. same ID rows are grouped succesively
vec2nrs = vec2nr(C,:)
%// Find extents of each group & use those extents to have final cell array output
grp_extent = diff(find([1 diff(R) 1]))
out = mat2cell(vec2nrs,grp_extent,grplen)
Sample run for the given inputs -
>> vec1
vec1 =
0 0 1 1 1 0 0 2 2 2 ...
0 0 1 1 1 0 0 2 2 2
>> vec2
vec2 =
6 7 5 6 4 6 5 9 8 7 ...
9 7 0 5 6 4 1 5 8 0
>> celldisp(out)
out{1} =
5 6 4
0 5 6
out{2} =
9 8 7
5 8 0
Here is another solution:
v1 = [0 0 1 1 1 0 0 2 2 2 0 0 1 1 1 0 0 2 2 2];
v2 = [6 7 5 6 4 6 5 9 8 7 9 7 0 5 6 4 1 5 8 0];
m1 = reshape(v2(v1 == 1), 3, [])'
m2 = reshape(v2(v1 == 2), 3, [])'
EDIT: David's solution is more flexible and probably more efficient.
Suppose an index vector in binary like below
Input
1 1 0 0 1 0 1
1 2 3 4 5 6 7
Intended output
1 2 5 7
which denotes nth number to be chosen. So I want to change 1 1 0 0 1 0 1 to 1 2 5 7, is there some easy way for this?
If you actually want to use your output to index another vector, do it directly.
You just need to transform your binary vector to logical
A = [1 1 0 0 1 0 1]; %assuming its double
B = [1 2 3 4 5 6 7];
C = B( logical(A) )
C =
1 2 5 7
The solution is using the function find(indicesBinary)
I have a matrix like the following (arbitrary cols/rows):
1 0 0 0 0
1 2 0 0 0
1 2 3 0 0
1 2 3 4 0
1 2 3 4 5
1 2 5 0 0
1 2 5 3 0
1 2 5 3 4
1 4 0 0 0
1 4 2 0 0
1 4 2 3 0
1 4 2 5 0
1 4 2 5 3
1 4 5 0 0
1 4 5 3 0
2 0 0 0 0
2 3 0 0 0
2 3 4 0 0
2 3 4 5 0
2 5 0 0 0
2 5 3 0 0
2 5 3 4 0
3 0 0 0 0
3 4 0 0 0
3 4 2 0 0
3 4 2 5 0
3 4 5 0 0
and now I want to get all rows where the first element is a certain value X and the last element (that is the last element != 0) is a certain value Y, OR turned around: the first is Y and the last is X.
Can't see any speedful code which does NOT use a for-loop :(
Thanks!
EDIT: To filter all rows with a certain first element is really easy, you don't need to help me here. So let's assume I only want to do the following: Filter all rows where the last element (i.e. the last element != 0 in each row) is either X or Y.
EDIT
Thanks a lot for your posts. I benchmarked the three possible solutions with a matrix of 473408*10 elements. Here's the benchmarkscript:
http://pastebin.com/9hEAWw9a
The results were:
t1 = 2.9425 Jonas
t2 = 0.0999 Brendan
t3 = 0.0951 Oli
So thanks a lot you guys, I'm sticking with Oli's solution and thus accept it. Thanks though for all the other solutions!
All you need to do is to find the linear indices of the last non-zero element of every row. The rest is easy:
[nRows,nCols] = size(A);
[u,v] = find(A); %# find all non-zero elements in A
%# for each row, find the highest column index with accumarray
%# and convert to linear index with sub2ind
lastIdx = sub2ind([nRows,nCols],(1:nRows)',accumarray(u,v,[nRows,1],#max,NaN));
To filter rows, you can then write
goodRows = A(:,1) == X & A(lastIdx) == Y
Here is a trick: Look for numbers with a 0 on the right, and sum them all:
H=[1 2 0 0 0;
2 3 1 0 0;
4 5 8 0 0;
8 5 4 2 2];
lastNumber=sum(H.*[H(:,2:end)==0 true(size(H,1),1)],2)
ans =
2
1
8
2
The rest is easy:
firstNumber=H(:,1);
find( (firstNumber==f) & (lastNumber==l) )
This works only if the numbers in each row are x number of non-zeros followed by a series of zeros. i.e. it will not work if the following is possible 1 0 3 4 0 0, I assume that isn't possible based on the sample input you gave ...
% 'a' is your array
[nx, ny] = size(a);
inds = [0:ny:ny*(nx-1)]' + sum(a ~= 0, 2);
% Needs to transpose so that the indexing reads left-to-right
aT = a';
valid1 = aT(inds) == Y;
valid2 = a(:,1) == X;
valid = valid1 & valid2;
valid_rows = a(valid,:);
This is messy I know ...