Matlab: How I can make this transformation on the matrix A? - matlab

I have a matrix A 4x10000, I want to use it to find another matrix C.
I'll simplify my problem with a simple example:
from a matrix A
20 4 4 74 20 20
36 1 1 11 36 36
77 1 1 15 77 77
3 4 2 6 7 8
I want, first, to find an intermediate entity B:
2 3 4 6 7 8
[20 36 77] 0 1 0 0 1 1 3
[4 1 1] 1 0 1 0 0 0 2
[74 11 15] 0 0 0 1 0 0 1
we put 1 if the corresponding value of the first line and the vector on the left, made ​​a column in the matrix A.
the last column of the entity B is the sum of 1 of each line.
at the end I want a matrix C, consisting of vectors which are left in the entity B, but only if the sum of 1 is greater than or equal to 2.
for my example:
20 4
C = 36 1
77 1
N.B: for my problem, I use a matrix A 4x10000

See if this works for you -
%// We need to replace this as its not available in your old version of MATLAB:
%// [unqcols,~,col_match] = unique(A(1:end-1,:).','rows','stable') %//'
A1 = A(1:end-1,:).'; %//'
[unqmat_notinorder,row_ind,labels] = unique(A1,'rows');
[tmp_sortedval,ordered_ind] = sort(row_ind);
unqcols = unqmat_notinorder(ordered_ind,:);
[tmp_matches,col_match] = ismember(labels,ordered_ind);
%// OR use - "[tmp2,col_match] = ismember(A1,out,'rows');"
C = unqcols(sum(bsxfun(#eq,col_match,1:max(col_match)),1)>=2,:).'; %//'
%// OR use - "C = out(accumarray(col_match,ones(1,numel(col_match)))>=2,:).'"

This should work:
[a,~,c] = unique(A(1:end-1,:).', 'rows', 'stable');
C=a(histc(c,unique(c))>=2, :).';
Edit: For older versions of MATLAB:
D=A(1:end-1,:);
C=unique(D(:,squeeze(sum(all(bsxfun(#eq, D, permute(D, [1 3 2])))))>=2).', 'rows').':

Related

Compare two vectors in MATLAB without using loop

Given two vector a , b of different length in MATLAB, I want the output as follows:
Example :
a = [3 5 10 20 45 80]
b = [3 5 80]
y = [1 1 0 0 0 1]
where y is of length similar to a in which 1's indicate existence of an item in b and 0 its non-existence.
I want to do this without using loops. Thanks
Note that all the numbers in each vector will be repeated only once as they correspond to some ids.
ismember()
Lia = ismember(A,B) returns an array containing 1 (true) where the data in A is found in B. Elsewhere, it returns 0 (false).
a = [3 5 10 20 45 80];
b = [3 5 80];
ismember(a,b)
ans =
1 1 0 0 0 1

Find if 2 vectors have 4 consecutive identical elements

I am trying to compare 2 vectors to discover if they share 4 consecutive values.
For example
w = [6 7 8 9 10 11 12 13 14]
v = [5 6 7 8 9]
Has 4 consecutive values 6 7 8 9
But
x = [6 7 8 9 10 11 12 13 14]
y = [6 7 1 2 3 4 5 6 13 14]
has four identical values (6 7 13 14) but they aren't consecutive.
The code I am currently using is:
if length(intersect(v, w)) >= 4
condition = true;
but this doesn't test for consecutive elements, so it would return true for both cases listed above whereas I want it to only return true for the first case.
Can somebody please help me find a way to test for identical consecutive elements rather than just identical elements.
Building on Marcos' answer:
Create all possible search vectors from your initial search (i.e. [5 6 7 8] [6 7 8 9]) - however we will make it a 3D matrix which will be m-by-1-by-n
n = 4;
m = numel(v)-n+1;
V = permute(v(bsxfun(#plus,(0:m-1)',1:n)),[1,3,2])
Check if any of these sub-vectors are a subset of the vector being searched
check = sum(any(bsxfun(#eq, V, w),3),2) >= n;
match = squeeze(V(check,:,:))' %'// The ' is debatable here, it depends on how many matches you get
you can compare
bsxfun(#eq, w,v')
Resulting with
ans =
0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0
As you can see four consecutive matching elements form a diagonal of length 4.
To find the location of this diagonal you can conv2 with a 4 diagonal filter (eye(4)):
[rr cc] = find( conv2( single(bsxfun(#eq, [1 2 3 w],v')), eye(4), 'same' ) == 4 )
compensating for the center of the filter
loc_in_w = cc - 1
loc_in_v = rr - 1
yielding
loc_in_w =
1
loc_in_v =
2
which are the first index of the sequence in w and v respectively.
This method can work with more than one occurrence of a 4-substring of v in w...
I haven't meddled in matlab for ages, but my "general" approach to this in computing terms would be splitting the problem into a needle-and-haystack solution with two parts:
Create all possible search vectors from your initial search (i.e. [5 6 7 8] [6 7 8 9])
Check if any of these sub-vectors are a subset of the vector being searched.
Basically just two set-operations in a row.
You could convert your vectors to strings, and use strfind.
If x and y are your vectors:
x_str = mat2str(x);
y_str = mat2str(y);
n = strfind(x_str(2:end-1), y_str(2:end-1))
Note that you have to remove the first and last characters of the string version, as they correspond to the square brackets of the vectors.

Matlab: How I can make this transformation on the matrix A? (part 2)

N.B: This question is more complex than my previous question: Matlab: How I can make this transformation on the matrix A?
I have a matrix A 4x10000, I want to use it to find another matrix C, based on a predefined vector U.
I'll simplify my problem with a simple example:
from a matrix A
20 4 4 74 20 20 4
36 1 1 11 36 36 1
77 1 1 15 77 77 1
3 4 2 6 7 8 15
and
U=[2 3 4 6 7 8 2&4&15 7&8 4|6].
& : AND
| : OR
I want, first, to find an intermediate entity B:
2 3 4 6 7 8 2&4&15 7&8 4|6
[20 36 77] 0 1 0 0 1 1 0 1 0 4
[4 1 1] 1 0 1 0 0 0 1 0 1 4
[74 11 15] 0 0 0 1 0 0 0 0 1 2
we put 1 if the corresponding value of the first line and the vector on the left, made ​​a column in the matrix A.
the last column of the entity B is the sum of 1 of each line.
at the end I want a matrix C, consisting of vectors which are left in the entity B, but only if the sum of 1 is greater than or equal to 3.
for my example:
20 4
C = 36 1
77 1
This was a complex one indeed and because of the many restrictions and labeling processes involved, it won't be as efficient as the solution to the previous problem. Here's the code to solve the posted problem -
find_labels1 = 2:8; %// Labels to be detected - main block
find_labels2 = {[2 4 15],[7 8],[4 6]}; %// ... side block
A1 = A(1:end-1,:); %// all of A except the last row
A2 = A(end,:); %// last row of A
%// Find unique columns and their labels for all of A execpt the last row
[unqmat_notinorder,row_ind,inv_labels] = unique(A1.','rows'); %//'
[tmp_sortedval,ordered_ind] = sort(row_ind);
unqcols = unqmat_notinorder(ordered_ind,:);
[tmp_matches,labels] = ismember(inv_labels,ordered_ind);
%// Assign labels to each group
ctl = numel(unique(labels));
labelgrp = arrayfun(#(x) find(labels==x),1:ctl,'un',0);
%// Work for the main comparisons
matches = bsxfun(#eq,A2,find_labels1'); %//'
maincols = zeros(ctl,numel(find_labels1));
for k = 1:ctl
maincols(k,:) = any(matches(:,labelgrp{k}),2);
end
%// Work for the extra comparisons added that made this problem extra-complex
lens = cellfun('length',find_labels2);
lens(end) = 1;
extcols = nan(ctl,numel(find_labels2));
for k = 1:numel(find_labels2)
idx = find(ismember(A2,find_labels2{k}));
extcols(:,k)=arrayfun(#(n) sum(ismember(labelgrp{n},idx))>=lens(k),1:ctl).'; %//'
end
C = unqcols(sum([maincols extcols],2)>=3,:).' %//'# Finally the output
I will give you a partial answer. I think you can take from here. Idea is to concatenate first 3 rows of A with each element of U replicated as last column. After you get the 3D matrix, replicate your original A and then just compare the rows. The rows which are equal, that is equivalent to putting one in your table.
B=(A(1:3,:).';
B1=repmat(B,[1 1 length(U)]);
C=permute(U,[3 1 2]);
D=repmat(C,[size(B1,1),1,1]);
E=[B1 D];
F=repmat(A',[1 1 size(E,3)]);
Now compare F and E, row-wise. If the rows are equal, then you put 1 in your table. For replicating & and |, you can form some kind of indicator vector.
Say,
indU=[1 2 3 4 5 6 7 7 7 8 8 -9 -9];
Same positive value indicates &, same negative value indicates |. Different value indicate different entries.
I hope you can take from here.

Grouping unique values in a vector and putting them in a matrix

I have a vector that contains repeated numbers like so:
[1 1 1 1 5 5 5 5 93 93 93 6 6 6 6 6 6] and so on. What I want to do is to group the similar values (1's, 5's, etc.). I would like to have each of the unique values in a row of a big matrix, like:
[ 1 1 1 1 0 0
5 5 5 5 0 0
93 93 93 0 0 0
6 6 6 6 6 6]
I don't know the maximum number of occurrence of a unique value, so it is ok to create an initial zero matrix with a large number of columns (that I am sure is bigger than the maximum number of occurrence of a unique value).
Any help is highly appreciated.
How about this?
A = [1 1 1 1 5 5 5 5 93 93 93 6 6 6 6 6 6];
[a,b] = hist(A,unique(A))
f = #(x) [ones(1,a(x)) zeros(1,max(a)-a(x))]
X = cell2mat( arrayfun(#(x) {f(x)*b(x)}, 1:numel(b) )' )
returns:
X =
1 1 1 1 0 0
5 5 5 5 0 0
6 6 6 6 6 6
93 93 93 0 0 0
I know the order is different, is that important? Otherwise:
n = hist(A,1:max(A)) % counts how often every number apperas
[a b] = unique(A,'stable') % gets all unique numbers
n = n(a) % correlates count and numbers
f = #(x) [ones(1,n(x)) zeros(1,max(n)-n(x))] % creates the logical index
% vector for every single row
X = cell2mat( arrayfun(#(x) {f(x)*b(x)}, 1:numel(b) )' ) %fills the rows
or inspired by Luis Mendo's Answer a little shorter:
n = hist(A,1:max(A));
a = unique(A,'stable')
n = n(a)
Y = repmat(a',1,max(n)).*bsxfun(#le, cumsum(ones(max(n),numel(n))), n)'
returns:
X =
1 1 1 1 0 0
5 5 5 5 0 0
93 93 93 0 0 0
6 6 6 6 6 6
For the bored people out there, there is a one-line solution:
X = getfield(cell2mat(arrayfun(#(x,y) padarray( padarray(x,[0 y],'replicate','pre'),[0 max(hist(A,1:max(A)))-y],'post'),1:max(A),hist(A,1:max(A)),'uni',0)'),{unique(A,'stable'),2:1+max(hist(A,1:max(A)))})
Or an almost lovely two-liner:
n = hist(A,1:max(A))
X = getfield(cell2mat(arrayfun(#(x,y) padarray( padarray(x,[0 y],'replicate',...
'pre'),[0 max(n)-y],'post'),1:max(A),n,'uni',0)'),...
{unique(A,'stable'),2:1+max(n)})
just for fun ;)
Vectorized solution (no loops):
x = [1 1 1 1 5 5 5 5 93 93 93 6 6 6 6 6 6]; %// data
ind = [find(diff(x)) numel(x)]; %// end of each run of equal values
values = x(ind); %// unique values (maintaining order)
count = diff([0 ind]); %// count of each value
result = bsxfun(#le, meshgrid(1:max(count),1:numel(values)), count.'); %'// mask
result = bsxfun(#times, result, values.'); %'// fill with the values
EDIT:
Alternative procedure that avoids the second bsxfun:
x = [1 1 1 1 5 5 5 5 93 93 93 6 6 6 6 6 6]; %// data
ind = [find(diff(x)) numel(x)];
values = x(ind); %// unique values (maintaining order)
count = diff([0 ind]); %// count of each value
mask = bsxfun(#le, ndgrid(1:max(count),1:numel(values)), count);
result = zeros(size(mask)); %// pre-allocate and pre-shape (transposed) result
result(mask) = x; %// fill in values
result = result.';
This could be one approach -
%%// Input
array1 = [1 1 1 1 5 5 5 5 93 93 93 6 6 6 6 6 6];
%// Main Processing
id = unique(array1,'stable'); %//Find the unique numbers/IDs
mat1 = zeros(numel(id),nnz(array1==mode(array1))); %%// Create a matrix to hold the final result
for k=1:numel(id)
extent_each_id = nnz(array1==id(k)); %%// Count of no. of occurances for each ID
mat1(k,1:extent_each_id)=id(k); %%// Starting from the left to the extent for each ID store that ID
end
Gives -
mat1 =
1 1 1 1 0 0
5 5 5 5 0 0
93 93 93 0 0 0
6 6 6 6 6 6

Matlab , matrix operations

I have for example this matrix
A=[
11 15 19 13
12 16 0 114
13 17 111 115
14 18 112 116
];
I want to find nonzero elements of two matrix of indices:
i1=[1 3];
i2=[2 4];
The result:
B=A(i2,i1);
B=[12 0
14 112];
index of matrix in A.
index=[2 4 12];
So, How to obtain the indices without loop?
Thanks.
There is a one-liner which is not quite readable of course:
index = find(diag(ismember(1:size(A,1), i2))*A*diag(ismember(1:size(A,2), i1)));
or alternatively
index=find(sparse(i2,i2,1,size(A,1),size(A,1))*A*sparse(i1,i1,1,size(A,2),size(A,2)));
and there is more elaborate and readable one:
z=zeros(size(A));
z(i2,i1) = A(i2,i1);
index=find(z);
Note that the first one-liner fails if the matrix contains Inf or NaN values because those values will be multiplied by zero, the second and third methods are more robust in that sense.
This is one solution:
% sub2ind does not work, use this hack instead
z = zeros(size(A));
z(i2,i1) = 1
ind = find(z) % get linear indices
%only keep the ones for which A is nonzero
ind = ind(A(ind) ~= 0)
Result:
z =
0 0 0 0
1 0 1 0
0 0 0 0
1 0 1 0
ind =
2
4
10
12
ind =
2
4
12
Slightly more compact than Bas Swinckels answer:
I=reshape(1:numel(A),size(A));
J=I(i2,i1);
J(~~B)