a contains indices and their occurrences. Now the indexing needs to be changed with Hamming weight so that indices with equal hamming weight will be summed up. How to do the Hamming weight indexing? Any ready command for this in Matlab?
>> a=[1,2;2,3;5,2;10,1;12,2]
1 2
2 3
5 2
10 1
12 2
13 8
>> dec2bin(a(:,1))
ans =
0001
0010
0101
1010
1100
1101
Goal: index things by Hamming weight
HW Count
1 5 (=2+3)
2 5 (=2+1+2)
3 8 (=8)
You can do it as follows:
a = [1,2;2,3;5,2;10,1;12,2;13,8]
the following line needs to be added, to consider also a hammingweight of zero:
if nnz(a(:,1)) == numel(a(:,1)); a = [0,0;a]; end
% or just
a = [0,0;a]; %// wouldn't change the result
to get the indices
rowidx = sum( de2bi(a(:,1)), 2 )
to get the sums
sums = accumarray( rowidx+1, a(:,2) ) %// +1 to consider Hammingweight of zero
to get the Hammingweight vector
HW = unique(rowidx)
returns:
rowidx =
1
1
2
2
2
3
sums =
5
5
8
and all together:
result = [HW, sums]
%or
result = [unique(rowidx), accumarray(rowidx+1,a(:,2))]
result =
0 0
1 5
2 5
3 8
If you are bothered by the 0 0 line, filter it out
result(sum(result,2)>0,:)
The result for a = [0,2;2,3;5,2;10,1;12,2;13,8] would be:
result =
0 2
1 3
2 5
3 8
Try this -
a = [1 2
2 3
5 2
10 1
12 2
13 8]
HW = dec2bin(a(:,1)) - '0';
out = accumarray(sum(HW,2), a(:,2), [], #sum);%%// You don't need that "sum" option it seems, as that's the default operation with accumarray
final_out = [unique(sum(HW,2)) out]
Output -
a =
1 2
2 3
5 2
10 1
12 2
13 8
final_out =
1 5
2 5
3 8
Related
I have a vector of 13 entities in Matlab.
a=[3 4 6 8 1 5 8 9 3 7 3 6 2]
I want to append values [1 2 3 4 5] at regular intervals at position 1 5 9 13 & 17.
The final value of a looks like this.
a=[1 3 4 6 2 8 1 5 3 8 9 3 4 7 3 6 5 2].
The values with italics show the appended values.
How can I do it?
Since you are looking for regular intervals, you can take advantage of the reshape and cat function:
a = [3 4 6 8 1 5 8 9 3 7 3 6 2];
v = [1 2 3 4 5];
l = [1 5 9 13 17];
interval = l(2)-l(1)-1; %computes the interval between inserts
amax = ceil(size(a,2)/interval) * interval; %calculating maximum size for zero padding
a(amax) = 0; %zero padding to allow `reshape`
b = reshape (a,[interval,size(v,2)]); %reshape into matrix
result = reshape(vertcat (v,b), [1,(size(b,1)+1)*size(b,2)]); %insert the values into the right position and convert back into vector
%remove padded zeros
final = result(result ~= 0) %remove the zero padding.
>>final =
Columns 1 through 16
1 3 4 6 2 8 1 5 3 8 9 3 4 7 3 6
Columns 17 through 18
5 2
Here's an approach using boolean-indexing -
% Inputs
a = [3 4 6 8 1 5 8 9 3 7 3 6 2]
append_vals = [1 2 3 4 5]
append_interval = 4 % Starting at 1st index
% Find out indices of regular intervals where new elements are to be inserted.
% This should create that array [1,5,9,13,17]
N_total = numel(a) + numel(append_vals)
append_idx = find(rem(0:N_total-1,append_interval)==0)
% Get boolean array with 1s at inserting indices, 0s elsewhere
append_mask = ismember(1:N_total,append_idx)
% Setup output array and insert new and old elements
out = zeros(1,N_total)
out(~append_mask) = a
out(append_mask) = append_vals
Alternatively, we can also use linear-indexing and avoid creating append_mask, like so -
% Setup output array and insert new and old elements
out = zeros(1,N_total)
out(append_idx) = append_vals
out(setdiff(1:numel(out),append_idx)) = a
a=[3 4 6 8 1 5 8 9 3 7 3 6 2]; % // Your original values
pos = [1 5 9 13 17]; % // The position of the values you want to insert
b=[1 2 3 4 5]; % // The values you want to insert
% // Pre-allocate a vector with the total size to hold the resulting values
r = zeros(size(a,2)+size(pos,2),1);
r(pos) = b % // Insert the appended values into the resulting vector first
r3 = r.' <1 % // Find the indices of the original values. These will be zero in the variable r but 1 in r3
ans =
0 1 1 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1
ind= find(r3==1) % // Find the indices of the original values
ind =
2 3 4 6 7 8 10 11 12 14 15 16 18
r(ind) = a; % // Insert those into the resulting vector.
r.'
ans =
1 3 4 6 2 8 1 5 3 8 9 3 4 7 3 6 5 2
You can use this function to append a bunch of values to an existing vector, given their positions in the new vector:
function r=append_interval(a,v,p)
% a - vector with initial values
% v - vector containing values to be inserted
% p - positions for values in v
lv=numel(v); % number of elements in v vector
la=numel(a); % number of elements in a vector
column_a=iscolumn(a); % check if a is a column- or row- wise vector
tot_elements=la+lv;
% size of r is tha max between the total number of elements in the two vectors and the higher positin in vector p (in this case missing positions in a are filled with zeros)
lr=max([max(p) tot_elements]);
% initialize r as nan vector
r=zeros(column_a*(lr-1)+1,~column_a*(lr-1)+1)/0;
% set elements in p position to the corresponding values in v
r(p)=v;
% copy values in a in the remaining positions and fill with zeros missing entries (if any)
tot_missing_values=lr-tot_elements;
if(tot_missing_values)
remaining_values=cat(2-iscolumn(a),a,zeros(column_a*(tot_missing_values-1)+1,~column_a*(tot_missing_values-1)+1));
else
remaining_values=a;
end
% insert values
r(isnan(r))=remaining_values;
You can use row-wise or column-wise vectors; the orientation of r will be the same of that of a.
Input:
a =
3 4 6 8 1 5 8 9 3 7 3 6 2
v =
1 2 3 4 5
p =
1 5 9 13 17
Output:
>> append_interval(a,v,p)
ans =
1 3 4 6 2 8 1 5 3 8 9 3 4 7 3 6 5 2
Every sequence of positive positions is allowed and the function will pad for you with zeros the final vector, in case you indicate a position exceding the sum of the original vector and added items.
For example, if:
v3 =
1 2 3 4 5 6 90
p3 =
1 5 9 13 17 30 33
you get:
append_interval(a,v3,p3)
ans =
Columns 1 through 19
1 3 4 6 2 8 1 5 3 8 9 3 4 7 3 6 5 2 0
Columns 20 through 33
0 0 0 0 0 0 0 0 0 0 6 0 0 90
Hope this will help.
So here's my problem i want to count the numbers of same values in a column, this my data:
a b d
2 1 5
1 3 10
1 -2 5
0 5 25
5 0 25
1 1 2
-1 -1 2
i want to count the same values of d (where d = a^2 + b^2), so this is the output i want:
a b d count
2 1 5 2
1 3 10 1
0 5 25 2
1 1 2 2
so as you can see, only positive combinations of a and b displayed. so how can i do that? thanks.
Assuming your data is a matrix, here's an accumarray-based approach. Note this doesn't address the requirement "only positive combinations of a and b displayed".
M = [2 1 5
1 3 10
1 -2 5
0 5 25
5 0 25
1 1 2
-1 -1 2]; %// data
[~, jj, kk] = unique(M(:,3),'stable');
s = accumarray(kk,1);
result = [M(jj,:) s];
Assuming your input data to be stored in a 2D array, this could be one approach -
%// Input
A =[
2 1 5
1 3 10
1 -2 5
0 5 25
5 0 25
1 1 2
-1 -1 2]
[unqcol3,unqidx,allidx] = unique(A(:,3),'stable')
counts = sum(bsxfun(#eq,A(:,3),unqcol3.'),1) %//'
out =[A(unqidx,:) counts(:)]
You can also get the counts with histc -
counts = histc(allidx,1:max(allidx))
Note on positive combinations of a and b: If you are looking to have positive combinations of A and B, you can select only those rows from A that fulfill this requirement and save back into A as a pre-processing step -
A = A(all(A(:,1:2)>=0,2),:)
i want to control the creation of random numbers in this matrix :
Mp = floor(1+(10*rand(2,20)));
mp1 = sort(Mp,2);
i want to modify this code in order to have an output like this :
1 1 2 2 3 3 3 4 5 5 6 7 7 8 9 9 10 10 10 10
1 2 3 3 3 3 3 3 4 5 6 6 6 6 7 8 9 9 9 10
i have to fill each row with all the numbers going from 1 to 10 in an increasing order and the second matrix that counts the occurences of each number should be like this :
1 2 1 2 1 2 3 1 1 2 1 1 2 1 1 2 1 2 3 4
1 1 1 2 3 4 5 6 1 1 1 2 3 4 1 1 1 2 3 1
and the most tricky matrix that i'v been looking for since the last week is the third matrix that should skim through each row of the first matrix and returns the numbers of occurences of each number and the position of the last occcurence.here is an example of how the code should work. this example show the intended result after running through the first row of the first matrix.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 (positions)
1 2
2 2
3 3
4 1
5 2
6 1
7 2
8 1
9 2
10 4
(numbers)
this example show the intended result after running through the second row of the first matrix.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 (positions)
1 1 2
2 1 2
3 3 6
4 1 1
5 3
6 1 4
7 2 1
8 1 1
9 2 3
10 4
(numbers)
so the wanted matrix must be filled up with zeros from the beginning and each time after running through each row of the first matrix, we add the new result to the previous one...
I believe the following code does everything you asked for. If I didn't understand, you need to get a lot clearer in how you pose your question...
Note - I hard coded some values / sizes. In "real code" you would never do that, obviously.
% the bit of code that generates and sorts the initial matrix:
Mp = floor(1+(10*rand(2,20)));
mp1 = sort(Mp, 2);
clc
disp(mp1)
occCount = zeros(size(mp1));
for ii = 1:size(mp1,1)
for jj = 1:size(mp1,2)
if (jj == 1)
occCount(ii,jj) = 1;
else
if (mp1(ii,jj) == mp1(ii,jj-1))
occCount(ii,jj) = occCount(ii, jj-1) + 1;
else
occCount(ii,jj) = 1;
end
end
end
end
% this is the second matrix you asked for
disp(occCount)
% now the third:
big = zeros(10, 20);
for ii = 1:size(mp1,1)
for jj = 1:10
f = find(mp1(ii,:) == jj); % index of all of them
if numel(f) > 0
last = f(end);
n = numel(f);
big(jj, last) = big(jj, last) + n;
end
end
end
disp(big)
Please see if this is indeed what you had in mind.
The following code solves both the second and third matrix generation problems with a single loop. For clarity, the second matrix M2 is the 2-by-20 array in the example containing the cumulative occurrence count. The third matrix M3 is the sparse matrix of size 10-by-20 in the example that encodes the number and position of the last occurrence of each unique value. The code only loops over the rows, using accumarray to do most of the work. It is generalized to any size and content of mp1, as long as the rows are sorted first.
% data
mp1 = [1 1 2 2 3 3 3 4 5 5 6 7 7 8 9 9 10 10 10 10;
1 2 3 3 3 3 3 3 4 5 6 6 6 6 7 8 9 9 9 10]; % the example first matrix
nuniq = max(mp1(:));
% accumulate
M2 = zeros(size(mp1));
M3 = zeros(nuniq,size(mp1,2));
for ir=1:size(mp1,1),
cumSums = accumarray(mp1(ir,:)',1:size(mp1,2),[],#numel,[],true)';
segments = arrayfun(#(x)1:x,nonzeros(cumSums),'uni',false);
M2(ir,:) = [segments{:}];
countCoords = accumarray(mp1(ir,:)',1:size(mp1,2),[],#max,[],true);
[ii,jj] = find(countCoords);
nzinds = sub2ind(size(M3),ii,nonzeros(countCoords));
M3(nzinds) = M3(nzinds) + nonzeros(cumSums);
end
I won't print the outputs because they are a bit big for the answer, and the code is runnable as is.
NOTE: For new test data, I suggest using the commands Mp = randi(10,[2,20]); mp1 = sort(Mp,2);. Or based on your request to user2875617 and his response, ensure all numbers with mp1 = sort([repmat(1:10,2,1) randi(10,[2,10])],2); but that isn't really random...
EDIT: Error in code fixed.
I am editing the previous answer to check if it is fast when mp1 is large, and apparently it is:
N = 20000; M = 200; P = 100;
mp1 = sort([repmat(1:P, M, 1), ceil(P*rand(M,N-P))], 2);
tic
% Initialise output matrices
out1 = zeros(M, N); out2 = zeros(P, N);
for gg = 1:M
% Frequencies of each row
freqs(:, 1) = mp1(gg, [find(diff(mp1(gg, :))), end]);
freqs(:, 2) = histc(mp1(gg, :), freqs(:, 1));
cumfreqs = cumsum(freqs(:, 2));
k = 1;
for hh = 1:numel(freqs(:, 1))
out1(gg, k:cumfreqs(hh)) = 1:freqs(hh, 2);
out2(freqs(hh, 1), cumfreqs(hh)) = out2(freqs(hh, 1), cumfreqs(hh)) + freqs(hh, 2);
k = cumfreqs(hh) + 1;
end
end
toc
A = [1,4,2,5,10
2,4,5,6,2
2,1,5,6,10
2,3,5,4,2]
And I want split it into two matrix by the last column
A ->B and C
B = [1,4,2,5,10
2,1,5,6,10]
C = [2,4,5,6,2
2,3,5,4,2]
Also, this method could be applied to a big matrix, like matrix 100*22 according to the last column value into 9 groups by matlab.
Use logical indexing
B=A(A(:,end)==10,:);
C=A(A(:,end)==2,:);
returns
>> B
B =
1 4 2 5 10
2 1 5 6 10
>> C
C =
2 4 5 6 2
2 3 5 4 2
EDIT: In reply to Dan's comment here is the extension for general case
e = unique(A(:,end));
B = cell(size(e));
for k = 1:numel(e)
B{k} = A(A(:,end)==e(k),:);
end
or more compact way
B=arrayfun(#(x) A(A(:,end)==x,:), unique(A(:,end)), 'UniformOutput', false);
so for
A =
1 4 2 5 10
2 4 5 6 2
2 1 5 6 10
2 3 5 4 2
0 3 1 4 9
1 3 4 5 1
1 0 4 5 9
1 2 4 3 1
you get the matrices in elements of cell array B
>> B{1}
ans =
1 3 4 5 1
1 2 4 3 1
>> B{2}
ans =
2 4 5 6 2
2 3 5 4 2
>> B{3}
ans =
0 3 1 4 9
1 0 4 5 9
>> B{4}
ans =
1 4 2 5 10
2 1 5 6 10
Here is a general approach which will work on any number of numbers in the last column on any sized matrix:
A = [1,4,2,5,10
2,4,5,6,2
1,1,1,1,1
2,1,5,6,10
2,3,5,4,2
0,0,0,0,2];
First sort by the last column (many ways to do this, don't know if this is the best or not)
[~, order] = sort(A(:,end));
As = A(order,:);
Then create a vector of how many rows of the same number appear in that last col (i.e. how many rows per group)
rowDist = diff(find([1; diff(As(:, end)); 1]));
Note that for my example data rowDist will equal [1 3 2] as there is 1 1, 3 2s and 2 10s.
Now use mat2cell to split by these row groupings:
Ac = mat2cell(As, rowDist);
If you really want to you can now split it into separate matrices (but I doubt you would)
Ac{:}
results in
ans =
1 1 1 1 1
ans =
0 0 0 0 2
2 3 5 4 2
2 4 5 6 2
ans =
1 4 2 5 10
2 1 5 6 10
But I think you would find Ac itself more useful
EDIT:
Many solutions so might as well do a time comparison:
A = [...
1 4 2 5 10
2 4 5 6 2
2 1 5 6 10
2 3 5 4 2
0 3 1 4 9
1 3 4 5 3
1 0 4 5 9
1 2 4 3 1];
A = repmat(A, 1000, 1);
tic
for l = 1:100
[~, y] = sort(A(:,end));
As = A(y,:);
rowDist = diff(find([1; diff(As(:, end)); 1]));
Ac = mat2cell(As, rowDist);
end
toc
tic
for l = 1:100
D=arrayfun(#(x) A(A(:,end)==x,:), unique(A(:,end)), 'UniformOutput', false);
end
toc
tic
for l = 1:100
for k = 1:numel(e)
B{k} = A(A(:,end)==e(k),:);
end
end
toc
tic
for l = 1:100
Bb = sort(A(:,end));
[~,b] = histc(A(:,end), Bb([diff(Bb)>0;true]));
C = accumarray(b, (1:size(A,1))', [], #(r) {A(r,:)} );
end
toc
resulted in
Elapsed time is 0.053452 seconds.
Elapsed time is 0.17017 seconds.
Elapsed time is 0.004081 seconds.
Elapsed time is 0.22069 seconds.
So for even for a large matrix the loop method is still the fastest.
Use accumarray in combination with histc:
% Example data (from Mohsen Nosratinia)
A = [...
1 4 2 5 10
2 4 5 6 2
2 1 5 6 10
2 3 5 4 2
0 3 1 4 9
1 3 4 5 1
1 0 4 5 9
1 2 4 3 1];
% Get the proper indices to the specific rows
B = sort(A(:,end));
[~,b] = histc(A(:,end), B([diff(B)>0;true]));
% Collect all specific rows in their specific groups
C = accumarray(b, (1:size(A,1))', [], #(r) {A(r,:)} );
Results:
>> C{:}
ans =
1 3 4 5 1
1 2 4 3 1
ans =
2 3 5 4 2
2 4 5 6 2
ans =
0 3 1 4 9
1 0 4 5 9
ans =
2 1 5 6 10
1 4 2 5 10
Note that
B = sort(A(:,end));
[~,b] = histc(A(:,end), B([diff(B)>0;true]));
can also be written as
[~,b] = histc(A(:,end), unique(A(:,end)));
but unique is not built-in and is therefore likely to be slower, especially when this is all used in a loop.
Note also that the order of the rows has changed w.r.t. the order they had in the original matrix. If the order matters, you'll have to throw in another sort:
C = accumarray(b, (1:size(A,1))', [], #(r) {A(sort(r),:)} );
We have p.e. i = 1:25 iterations.
Each iteration result is a 1xlength(N) cell array, where 0<=N<=25.
iteration 1: 4 5 9 10 20
iteration 2: 3 8 9 13 14 6
...
iteration 25: 1 2 3
We evaluate the results of all iterations to one matrix sorted according to frequency each value is repeated in descending order like this example:
Matrix=
Columns 1 through 13
16 22 19 25 2 5 8 14 17 21 3 12 13
6 5 4 4 3 3 3 3 3 3 2 2 2
Columns 14 through 23
18 20 1 6 7 9 10 11 15 23
2 2 1 1 1 1 1 1 1 1
Result explanation: Column 1: N == 16 is present in 6 iterations, column 2: N == 22 is present in 5 iterations etc.
If a number N isn't displayed (in that paradigm N == 4, N == 24) in any iteration, is not listed with frequency index of zero either.
I want to associate each iteration (i) to the first N it is displayed p.e. N == 9 to be present only in first iteration i = 1 and not in i = 2 too, N == 3 only to i = 2 and not in i = 25 too etc until all i's to be unique associated to N's.
Thank you in advance.
Here's a way that uses a feature of unique (i.e. that it returns the index to the first value) that was introduced in R2012a
%# make some sample data
iteration{1} = [1 2 4 6];
iteration{2} = [1 3 6];
iteration{3} = [1 2 3 4 5 6];
nIter= length(iteration);
%# create an index vector so we can associate N's with iterations
nn = cellfun(#numel,iteration);
idx = zeros(1,sum(nn));
idx([1,cumsum(nn(1:end-1))+1]) = 1;
idx = cumsum(idx); %# has 4 ones, 3 twos, 6 threes
%# create a vector of the same length as idx with all the N's
nVec = cat(2,iteration{:});
%# run `unique` on the vector to identify the first occurrence of each N
[~,firstIdx] = unique(nVec,'first');
%# create a "cleanIteration" array, where each N only appears once
cleanIter = accumarray(idx(firstIdx)',firstIdx',[nIter,1],#(x){sort(nVec(x))},{});
cleanIter =
[1x4 double]
[ 3]
[ 5]
>> cleanIter{1}
ans =
1 2 4 6
Here is another solution using accumarray. Explanations in the comments
% example data (from your question)
iteration{1} = [4 5 9 10 20 ];
iteration{2} = [3 8 9 13 14 6];
iteration{3} = [1 2 3];
niterations = length(iteration);
% create iteration numbers
% same as Jonas did in the first part of his code, but using a short loop
for i=1:niterations
idx{i} = i*ones(size(iteration{i}));
end
% count occurences of values from all iterations
% sort them in descending order
occurences = accumarray([iteration{:}]', 1);
[occ val] = sort(occurences, 1, 'descend');
% remove zero occurences and create the Matrix
nonzero = find(occ);
Matrix = [val(nonzero) occ(nonzero)]'
Matrix =
3 9 1 2 4 5 6 8 10 13 14 20
2 2 1 1 1 1 1 1 1 1 1 1
% find minimum iteration number for all occurences
% again, using accumarray with #min function
assoc = accumarray([iteration{:}]', [idx{:}]', [], #min);
nonzero = find(assoc);
result = [nonzero assoc(nonzero)]'
result =
1 2 3 4 5 6 8 9 10 13 14 20
3 3 2 1 1 2 2 1 1 2 2 1