I've written a function in matlab which generates a matrix using a loop. I was wondering if it is possible to generate the same results without a loop. X can either be a 1 x 50, 2 x 50, 3 x 50, etc... the values range from 1 to 50 incrementally for each column per row.
For example
1 x 1 = 1,
2 x 1 = 1,
3 x 1 = 1,
1 x 2 = 2,
2 x 2 = 2,
3 x 2 = 2,
.....................
1 x 50 = 50,
2 x 50 = 50,
3 x 50 = 50,
My function:
function [i] = m(x)
[a, b] = size(x);
i = zeros(a, b);
for c = 1 : a
i(c, :) = (1:size(x,2));
end
end
Thanks.
Try this:
N = 3;
M = 50;
x = repmat((1:N)',M,1);
y = reshape(repmat((1:M)',1,N)',N*M,1);
%z = x.*y
z = strcat(num2str(x),'x',num2str(y),'=',num2str(x.*y))
This will give the same format in your question.
Use repmat:
output = repmat(1:size(x,2), size(x,1), 1);
Some alternatives are
output = ones(size(x,1),1)*(1:size(x,2));
and
output = cumsum(ones(size(x)),2);
One alternate to repmat(Luis's answer) is bsxfun
out = bsxfun(#times,ones(size(x,1),1),1:size(x,2))
Related
Given this array for example:
a = [1 2 2 2 1 3 2 1 4 4 4 5 1]
I want to find a way to check which numbers are repeated consecutively most often. In this example, the output should be [2 4] since both 2 and 4 are repeated three times consecutively.
Another example:
a = [1 1 2 3 1 1 5]
This should return [1 1] because there are separate instances of 1 being repeated twice.
This is my simple code. I know there is a better way to do this:
function val=longrun(a)
b = a(:)';
b = [b, max(b)+1];
val = [];
sum = 1;
max_occ = 0;
for i = 1:max(size(b))
q = b(i);
for j = i:size(b,2)
if (q == b(j))
sum = sum + 1;
else
if (sum > max_occ)
max_occ = sum;
val = [];
val = [val, q];
elseif (max_occ == sum)
val = [val, q];
end
sum = 1;
break;
end
end
end
if (size(a,2) == 1)
val = val'
end
end
Here's a vectorized way:
a = [1 2 2 2 1 3 2 1 4 4 4 5 1]; % input data
t = cumsum([true logical(diff(a))]); % assign a label to each run of equal values
[~, n, z] = mode(t); % maximum run length and corresponding labels
result = a(ismember(t,z{1})); % build result with repeated values
result = result(1:n:end); % remove repetitions
One solution could be:
%Dummy data
a = [1 2 2 2 1 3 2 1 4 4 4 5 5]
%Preallocation
x = ones(1,numel(a));
%Loop
for ii = 2:numel(a)
if a(ii-1) == a(ii)
x(ii) = x(ii-1)+1;
end
end
%Get the result
a(find(x==max(x)))
With a simple for loop.
The goal here is to increase the value of x if the previous value in the vector a is identical.
Or you could also vectorized the process:
x = a(find(a-circshift(a,1,2)==0)); %compare a with a + a shift of 1 and get only the repeated element.
u = unique(x); %get the unique value of x
h = histc(x,u);
res = u(h==max(h)) %get the result
I have a list of coordinates (x,y) and I need to find the index of the first and last occurrence of each coordinate in the list.
Example (in my use-cast I have ~30M coordinates):
x = [1 3 7 1 3];
y = [5 1 6 5 1];
first = [1 2 3 1 2];
last = [4 5 3 4 5];
I implemented it using a Matrix and a loop, it looks like this, but it is slow:
x1 = min(x);
y1 = min(y);
x2 = max(x);
y2 = max(y);
tic
Mlast = zeros(y2-y1+1, x2-x1+1);
Mfirst = Mlast;
ind = sub2ind(size(Mlast),y-y1+1, x-x1+1);
for i1=1:length(ind)
first = Mfirst(ind(i1));
if first == 0
first = i1;
end
Mlast(ind(i1)) = i1;
Mfirst(ind(i1)) = first;
end
I tried to vectorize the whole process, but I only succeed with Mlast:
ind = sub2ind(size(Mlast),y-y1+1, x-x1+1);
t = (1:length(x))';
Mlast(ind) = t;
Mfirst = ???
Is there a way to get this for the first occurrence as well?
The unique function can do that:
[~, b, c] = unique([x(:) y(:)], 'rows', 'first');
first = b(c).';
[~, b, c] = unique([x(:) y(:)], 'rows', 'last');
last = b(c).';
Assuming that coordinates are positive integers and specially when the range of coordinates is small you can use accumarray:
x1 = min(x);
y1 = min(y);
x2 = max(x);
y2 = max(y);
sz = [y2-y1+1, x2-x1+1];
ind = sub2ind(sz,y-y1+1, x-x1+1);
ifirst = accumarray(ind(:), 1:numel(ind), [], #min);
ilast = accumarray(ind(:), 1:numel(ind), [], #max);
Mfirst = ifirst(ind);
Mlast = ilast(ind);
For higher ranges you can use the sparse option:
ifirst = accumarray(ind(:), 1:numel(ind), [], #min,[],1);
ilast = accumarray(ind(:), 1:numel(ind), [], #max,[],1);
If you've got 30M points then you likely don't have enough memory for this method... but it's pretty quick for smaller arrays
x = [1 3 7 1 3];
y = [5 1 6 5 1];
xy = cat( 3, x, y );
chk = all( xy == permute( xy, [2 1 3] ), 3 );
[~,first] = max( chk );
[~,last] = max( flipud( chk ) );
last = size(chk,1) - last + 1;
Edit You can also do this with findgroups, and looping over the unique coordinates instead of each coordinate to have a potentially much shorter loop...
x = [1 3 7 1 3];
y = [5 1 6 5 1];
g = findgroups( x, y );
first = zeros( size( x ) );
last = first;
for ii = 1:max(g)
idx = (ii==g);
first( idx ) = find( idx, 1, 'first' );
last( idx ) = find( idx, 1, 'last' );
end
Edit2 I think these are both pretty slow relative to other answers here...
I have 5 columns x, y, r, g, b with values of line number, column number, red, green and blue. The lines of this n by 5 matrix are not in a particular order, however they are consistent with image(x,y) and the r,g,b.
I would like to do something like I=uint8(zeros(480,640,3) and just change those rgb values based on the n by 5 mat.
Something along the lines of I(mat(:,1), mat(:,2), 1)=mat(:,3) for red etc
The following uses the concept of linear indexing and the versatile bsxfun function:
m = 640; %// number of rows
n = 480; %// number of columns
I = zeros(m, n, 3, 'uint8'); %// initiallize directly as uint8
I(bsxfun(#plus, x(:)+(y(:)-1)*m, (0:2)*m*n)) = [r(:) g(:) b(:)]; %// fill values
Small example: for
m = 2;
n = 3;
x = [1 2 1];
y = [1 1 2];
r = [ 1 2 3];
g = [11 12 13];
b = [21 22 23];
the code produces
I(:,:,1) =
1 3 0
2 0 0
I(:,:,2) =
11 13 0
12 0 0
I(:,:,3) =
21 23 0
22 0 0
An alternative:
INDr = sub2ind([480, 640, 3], mat(:, 1), mat(:,2), ones([numel(mat(:,3)), 1]));
INDg = sub2ind([480, 640, 3], mat(:, 1), mat(:,2), 2*ones([numel(mat(:,3)), 1]));
INDb = sub2ind([480, 640, 3], mat(:, 1), mat(:,2), 3*ones([numel(mat(:,3)), 1]));
I=uint8(zeros(480,640, 3));
I(INDr)=mat(:,3);
I(INDg)=mat(:,4);
I(INDb)=mat(:,5);
Note that in Matlab, the convention between axes is different between images and arrays.
I'm developing a program with MatLab that calculates powers of numbers, adds them together, and then sees if any of the first set of numbers (numbers to powers) equals any of the added numbers to powers. I'm trying to check this for each value in the first array, however, I am getting an output like this:
m =
1
128
2187
16384
78125
279936
823543
2097152
4782969
10000000
for each m value, which is just the result of a simple for loop of the array. So when I go to check if m is in the array, it checks is [1, 128,2187,16384,78125...] in the array, and the answer is no. How can I get it to evaluate each individual entry, like this:
Array n is [1,128,2187,16384]
for m = n
m = 1
Is m in array? No
m = 128
Is m in array? No
m = 2187
Is m in array? Yes
m = 16384
Is m in array? No
end
My code is below:
C = [];
D = [];
E = [];
F = [];
numbers1 = [];
numbers2 = [];
numbers = 10;
powers = 10;
for i = 1:numbers
for j = 3:powers
C = [C;i^j];
end
C = transpose(C);
D = [D;C];
C = [];
end
[~,b] = unique(D(:,1)); % indices to unique values in first column of D
D(b,:); % values at these rows
for i = D
for a = D
E = [E;i+a];
end
E = transpose(E);
F = [F;E];
E = [];
end
[~,b] = unique(F(:,1)); % indices to unique values in first column of F
F(b,:); % values at these rows
for m = D % this is the for loop mentioned above
m
end
Example vectors:
>> m = [1 3 5 9];
n = [5 2 1 4 8];
To check if each element of vector m is in n, use ismember:
>>ismember(m,n)
ans =
1 0 1 0
To get the values, not the indices: use logical indexing on m:
>> m(ismember(m,n))
ans =
1 5
or directly use intersect:
>> intersect(m,n)
ans =
1 5
I have two arrays. One is a list of lengths within the other. For example
zarray = [1 2 3 4 5 6 7 8 9 10]
and
lengths = [1 3 2 1 3]
I want to average (mean) over parts the first array with lengths given by the second. For this example, resulting in:
[mean([1]),mean([2,3,4]),mean([5,6]),mean([7]),mean([8,9,10])]
I am trying to avoid looping, for the sake of speed. I tried using mat2cell and cellfun as follows
zcell = mat2cell(zarray,[1],lengths);
zcellsum = cellfun('mean',zcell);
But the cellfun part is very slow. Is there a way to do this without looping or cellfun?
Here is a fully vectorized solution (no explicit for-loops, or hidden loops with ARRAYFUN, CELLFUN, ..). The idea is to use the extremely fast ACCUMARRAY function:
%# data
zarray = [1 2 3 4 5 6 7 8 9 10];
lengths = [1 3 2 1 3];
%# generate subscripts: 1 2 2 2 3 3 4 5 5 5
endLocs = cumsum(lengths(:));
subs = zeros(endLocs(end),1);
subs([1;endLocs(1:end-1)+1]) = 1;
subs = cumsum(subs);
%# mean of each part
means = accumarray(subs, zarray) ./ lengths(:)
The result in this case:
means =
1
3
5.5
7
9
Speed test:
Consider the following comparison of the different methods. I am using the TIMEIT function by Steve Eddins:
function [t,v] = testMeans()
%# generate test data
[arr,len] = genData();
%# define functions
f1 = #() func1(arr,len);
f2 = #() func2(arr,len);
f3 = #() func3(arr,len);
f4 = #() func4(arr,len);
%# timeit
t(1) = timeit( f1 );
t(2) = timeit( f2 );
t(3) = timeit( f3 );
t(4) = timeit( f4 );
%# return results to check their validity
v{1} = f1();
v{2} = f2();
v{3} = f3();
v{4} = f4();
end
function [arr,len] = genData()
%#arr = [1 2 3 4 5 6 7 8 9 10];
%#len = [1 3 2 1 3];
numArr = 10000; %# number of elements in array
numParts = 500; %# number of parts/regions
arr = rand(1,numArr);
len = zeros(1,numParts);
len(1:end-1) = diff(sort( randperm(numArr,numParts) ));
len(end) = numArr - sum(len);
end
function m = func1(arr, len)
%# #Drodbar: for-loop
idx = 1;
N = length(len);
m = zeros(1,N);
for i=1:N
m(i) = mean( arr(idx+(0:len(i)-1)) );
idx = idx + len(i);
end
end
function m = func2(arr, len)
%# #user1073959: MAT2CELL+CELLFUN
m = cellfun(#mean, mat2cell(arr, 1, len));
end
function m = func3(arr, len)
%# #Drodbar: ARRAYFUN+CELLFUN
idx = arrayfun(#(a,b) a-(0:b-1), cumsum(len), len, 'UniformOutput',false);
m = cellfun(#(a) mean(arr(a)), idx);
end
function m = func4(arr, len)
%# #Amro: ACCUMARRAY
endLocs = cumsum(len(:));
subs = zeros(endLocs(end),1);
subs([1;endLocs(1:end-1)+1]) = 1;
subs = cumsum(subs);
m = accumarray(subs, arr) ./ len(:);
if isrow(len)
m = m';
end
end
Below are the timings. Tests were performed on a WinXP 32-bit machine with MATLAB R2012a. My method is an order of magnitude faster than all other methods. For-loop is second best.
>> [t,v] = testMeans();
>> t
t =
0.013098 0.013074 0.022407 0.00031807
| | | \_________ #Amro: ACCUMARRAY (!)
| | \___________________ #Drodbar: ARRAYFUN+CELLFUN
| \______________________________ #user1073959: MAT2CELL+CELLFUN
\__________________________________________ #Drodbar: FOR-loop
Furthermore all results are correct and equal -- differences are in the order of eps the machine precision (caused by different ways of accumulating round-off errors), therefore considered rubbish and simply ignored:
%#assert( isequal(v{:}) )
>> maxErr = max(max( diff(vertcat(v{:})) ))
maxErr =
3.3307e-16
Here is a solution using arrayfun and cellfun
zarray = [1 2 3 4 5 6 7 8 9 10];
lengths = [1 3 2 1 3];
% Generate the indexes for the elements contained within each length specified
% subset. idx would be {[1], [4, 3, 2], [6, 5], [7], [10, 9, 8]} in this case
idx = arrayfun(#(a,b) a-(0:b-1), cumsum(lengths), lengths,'UniformOutput',false);
means = cellfun( #(a) mean(zarray(a)), idx);
Your desired output result:
means =
1.0000 3.0000 5.5000 7.0000 9.0000
Following #tmpearce comment I did a quick time performance comparison between above's solution, from which I create a function called subsetMeans1
function means = subsetMeans1( zarray, lengths)
% Generate the indexes for the elements contained within each length specified
% subset. idx would be {[1], [4, 3, 2], [6, 5], [7], [10, 9, 8]} in this case
idx = arrayfun(#(a,b) a-(0:b-1), cumsum(lengths), lengths,'UniformOutput',false);
means = cellfun( #(a) mean(zarray(a)), idx);
and a simple for loop alternative, function subsetMeans2.
function means = subsetMeans2( zarray, lengths)
% Method based on single loop
idx = 1;
N = length(lengths);
means = zeros( 1, N);
for i = 1:N
means(i) = mean( zarray(idx+(0:lengths(i)-1)) );
idx = idx+lengths(i);
end
Using the next test scrip, based on TIMEIT, that allows checking performance varying the number of elements on the input vector and sizes of elements per subset:
% Generate some data for the performance test
% Total of elements on the vector to test
nVec = 100000;
% Max of elements per subset
nSubset = 5;
% Data generation aux variables
lenghtsGen = randi( nSubset, 1, nVec);
accumLen = cumsum(lenghtsGen);
maxIdx = find( accumLen < nVec, 1, 'last' );
% % Original test data
% zarray = [1 2 3 4 5 6 7 8 9 10];
% lengths = [1 3 2 1 3];
% Vector to test
zarray = 1:nVec;
lengths = [ lenghtsGen(1:maxIdx) nVec-accumLen(maxIdx)] ;
% Double check that nVec is will be the max index
assert ( sum(lengths) == nVec)
t1(1) = timeit(#() subsetMeans1( zarray, lengths));
t1(2) = timeit(#() subsetMeans2( zarray, lengths));
fprintf('Time spent subsetMeans1: %f\n',t1(1));
fprintf('Time spent subsetMeans2: %f\n',t1(2));
It turns out that the non-vectorised version without arrayfun and cellfun is faster, presumably due to the extra overhead of those functions
Time spent subsetMeans1: 2.082457
Time spent subsetMeans2: 1.278473