I would like to add the elements to the array using their index.
array_in = [1 5 6 8 9];
index = [2 4];
newElements = [25 67];
index = index + (0:length(index)-1);
expected output:
array_out = [1 25 5 6 67 8 9];
1.using for loop:
tmp = array_in;
for idx = 1:length(index)
array_out = cat(2,tmp(1:index(idx)-1),newElements(idx),tmp(index(idx):end));
tmp = array_out;
end
2.The code for Calling a Function Using Its Handle:
fcn_Insert = #(a, x, n) cat(2, x(1:n-1), a, x(n:end));
array_out = fcn_Insert(newElements,array_in,index)
The above code.2 (function) is not working? can any one suggest the solution. Are there any other better solutions?
Here's a sort-based approach:
array_out = [array_in newElements]; %// append new to old
[~, ind] = sort([1:numel(array_in) index-.5]); %// determine new order needed
array_out = array_out(ind); %// apply that order
I was working on this before Luis's answer was accepted. It ran over twice as fast, if people are interested.
function array_out = Insert(array_in, index, values)
array_out = zeros(length(array_in)+length(index), 1);
oldIndex = ~ismember(1:length(array_out), index);
array_out(index) = values;
array_out(oldIndex) = array_in;
end
Related
Suppose I have an array of length 15
x = randi([0 5], 1,15);
I want to sum every 3 elements of x together and put each sum in a new array called y, as in the following:
y = [y1 y2 y3 y4 y5];
Please help me in doing that in Matlab using for loops.
Here's a vectorized approach that automatically deals with a possible smaller last chunk:
x = randi([0 5], 1, 15); % example data
N = 3; % chunk size
y = accumarray(ceil((1:numel(x))/N).', x(:));
you can use reshape as follows:
y = sum(reshape(x,3,[]))
This reshapes your vector x to an array 3 by whatever is left, then sum along right dimension...
For the case the # of elements you want to sum doesnt add up to the total length of the vector, you can pad with zeros or NaN at the end to make it work. Here's I chose adding zeros:
x = randi([0 5], 1,15);
n = 4 ; % sum every n elements (which is the number of rows in the reshape)
try
y = sum(reshape(x, n, []));
catch
disp('added trailing zeros!')
x(numel(x) + (n - mod(numel(x), n))) = 0;
y = sum(reshape(x, n, []));
end
(you can do this with an if condition instead, I just like try catch more here)
Using for loops:
y = zeros(1,5);
for i = 1:5
idx = (i-1)*3 + 1:(i-1)*3 + 3;
y(i) = sum(x(idx));
end
Using a reference variable Target that is used to indicate the start position of each partition the loop below can be achieved. If you would only like to use only loops an alternative inner loop can be done. This method works almost on the same premise as windowing.
Method 1: Single For-Loop with Indexing
x = randi([0 5], 1,15);
y = zeros(1,length(x)/3);
Index = 1;
for Target = 1: +3: 15
Partition = x(Target:Target+2);
y(1,Index) = sum(Partition);
Index = Index + 1;
end
Method 2: Outer and Inner For-Loops
x = randi([0 5], 1,15);
y = zeros(1,length(x)/3);
Partition = zeros(1,3);
Index = 1;
for Target = 1: +3: 15
for Column = 1: +1: 3
Partition(1,Column) = x(1,Target+Column-1);
end
y(1,Index) = sum(Partition);
Index = Index + 1;
end
I wish to obtain a matrix consisting of every 3x3 square within a matrix. The matrix is a #channels by m by n. The end result is a matrix of size #channels x (size1-2)*(size2-2) x 9I can do this in a non-vector fashion via the following code:
clear
size1 = 10;
size2 = 10;
matrix = 1:size1*size2;
matrix =reshape(matrix,[size1 size2]);
matrix_withdraw = 1:(88*size1*size2);
matrix_withdraw = reshape(matrix_withdraw,[88 size1 size2]);
tic
iter = 1;
for ii = 1:size1-2
for jj = 1:size2-2
locs(ii,jj,:,:) = matrix(ii:ii+2,jj:jj+2);
method1(:,iter,:) = reshape(matrix_withdraw(:,ii:ii+2,jj:jj+2),[88,9]);
iter = iter+1;
end
end
locs = permute(locs,[3 4 1 2]);
This is obviously fairly slow for large sizes of matrices. I'm looking to vectorize this. I managed to obtain a solution which works but it's not very clean
locs2 = ones(size1*(size2-2),1)+(0:size1*(size2-2)-1)';
temp = 1:size1*(size2-2);
temp = mod(temp,size1);
temp = (temp>(size1-2)) | (temp==0);
locs2(temp,:) = [];
locs3 = reshape(locs2,[1 1 (size1-2) (size2-2)]);
locs3(2,1,:,:) = reshape(locs2+1,[1 1 (size1-2) (size2-2)]);
locs3(3,1,:,:) = reshape(locs2+2,[1 1 (size1-2) (size2-2)]);
locs3(:,2,:,:) = locs3(:,1,:,:)+size1;
locs3(:,3,:,:) = locs3(:,2,:,:)+size1;
locs3 = permute(locs3,[1 2 4 3]);
locs3 = vec(locs3);
method2 = matrix_withdraw(:,locs3);
method2 = reshape(method2,[88,9,64]);
method2 = permute(method2,[1 3 2]);
Method1 and method2 are equivalent and render the exact same results. Method2 is also 10x faster than method1. My question is, is there a cleaner way to do this?
If you have the Image Processing Toolbox, you can use im2col to get each block as a column, and then reshape as a 4-D array:
blockSize = [3 3];
locs = reshape(im2col(matrix, blockSize), [blockSize size(matrix)-blockSize+1]);
Or you can do it directly, building a 4-D indexing array using implicit singleton expansion. This doesn't require any toolboxes:
m = 3; n = 3; % block size
[M, N] = size(matrix);
ind = (1:m).'+(0:n-1)*M + reshape(0:M-m, 1, 1, []) + reshape((0:N-n)*M, 1, 1, 1, []);
locs = matrix(ind);
val(:,:,1) =
0.1068 0.7150 0.6987 0.5000
0.6538 0.9037 0.1978 0.4799
0.4942 0.8909 0.0305 0.9047
0.7791 0.3342 0.7441 0.6099
val(:,:,2) =
0.6177 0.1829 0.4899 0.5005
0.8594 0.2399 0.1679 0.4711
0.8055 0.8865 0.9787 0.0596
0.5767 0.0287 0.7127 0.6820
val(:,:,3) =
0.0424 0.8181 0.6596 0.8003
0.0714 0.8175 0.5186 0.4538
0.5216 0.7224 0.9730 0.4324
0.0967 0.1499 0.6490 0.8253
Row Col
4 1
1 2
3 3
Hi, i want to take multiple points from a 3d array, but i don't know any efficient methods without using loops. I've tried messing around with sub2ind but it doesn't seem very effective to do each 2d matrix separately.
If you want all values of the data in the third dimension you can easily acess it with:
Rows = [4, 1, 3]
Col = [1, 2 ,3]
val(Rows,Col,:)
Output:
>> val([4, 1, 3],[1, 2, 3],:)
ans(:,:,1) =
0.7791 0.3342 0.7441
0.1068 0.7150 0.6987
0.4942 0.8909 0.0305
ans(:,:,2) =
0.5767 0.0287 0.7127
0.6177 0.1829 0.4899
0.8055 0.8865 0.9787
ans(:,:,3) =
0.0967 0.1499 0.6490
0.0424 0.8181 0.6596
0.5216 0.7224 0.9730
You can obtain the desired output with:
x1 = [4,1,3];
x2 = [1,2,3];
x = val(x1,x2,:);
for ii = 1:size(val,3)
res(:,:,ii) = diag(x(:,:,ii)).';
end
Output:
res =
ans(:,:,1) =
0.779100 0.715000 0.030500
ans(:,:,2) =
0.57670 0.18290 0.97870
ans(:,:,3) =
0.096700 0.818100 0.973000
Or with sub2ind, and implicit expansion (matlab 2016b and newer)
x1 = [4,1,3];
x2 = [1,2,3];
len = length(x1);
ind = sub2ind(size(val),x1+zeros(len,1),x2+zeros(len,1),[1:size(val,3)].'+zeros(1,len));
res = A(ind)
%if you want to preserve the third dimension add: permute(res,[1,3,2])
%we can do that because an array in matlab has an infinite number of singleton dimension
Output:
res =
0.779100 0.715000 0.030500
0.576700 0.182900 0.978700
0.096700 0.818100 0.973000
I have an application with an array of matrices. I have to manipulate the diagonals several times. The other elements are unchanged. I want to do things like:
for j=1:nj
for i=1:n
g(i,i,j) = gd(i,j)
end
end
I have seen how to do this with a single matrix using logical(eye(n)) as a single index, but this does not work with an array of matrices. Surely there is a way around this problem. Thanks
Use a linear index as follows:
g = rand(3,3,2); % example data
gd = [1 4; 2 5; 3 6]; % example data. Each column will go to a diagonal
s = size(g); % size of g
ind = bsxfun(#plus, 1:s(1)+1:s(1)*s(2), (0:s(3)-1).'*s(1)*s(2)); % linear index
g(ind) = gd.'; % write values
Result:
>> g
g(:,:,1) =
1.000000000000000 0.483437118939645 0.814179952862505
0.154841697368116 2.000000000000000 0.989922194103104
0.195709075365218 0.356349047562417 3.000000000000000
g(:,:,2) =
4.000000000000000 0.585604389346560 0.279862618046844
0.802492555607293 5.000000000000000 0.610960767605581
0.272602365429990 0.551583664885735 6.000000000000000
Based on Luis Mendo's answer, a version that may perhaps be more easy to modify depending on one's specific purposes. No doubt his version will be more computationally efficient though.
g = rand(3,3,2); % example data
gd = [1 4; 2 5; 3 6]; % example data. Each column will go to a diagonal
sz = size(g); % Get size of data
sub = find(eye(sz(1))); % Find indices for 2d matrix
% Add number depending on location in third dimension.
sub = repmat(sub,sz(3),1); %
dim3 = repmat(0:sz(1)^2:prod(sz)-1, sz(1),1);
idx = sub + dim3(:);
% Replace elements.
g(idx) = gd;
Are we already playing code golf yet? Another slightly smaller and more readable solution
g = rand(3,3,2);
gd = [1 4; 2 5; 3 6];
s = size(g);
g(find(repmat(eye(s(1)),1,1,s(3))))=gd(:)
g =
ans(:,:,1) =
1.00000 0.35565 0.69742
0.85690 2.00000 0.71275
0.87536 0.13130 3.00000
ans(:,:,2) =
4.00000 0.63031 0.32666
0.33063 5.00000 0.28597
0.80829 0.52401 6.00000
Suppose I have two arrays ordered in an ascending order, i.e.:
A = [1 5 7], B = [1 2 3 6 9 10]
I would like to create from B a new vector B', which contains only the closest values to A values (one for each).
I also need the indexes. So, in my example I would like to get:
B' = [1 6 9], Idx = [1 4 5]
Note that the third value is 9. Indeed 6 is closer to 7 but it is already 'taken' since it is close to 4.
Any idea for a suitable code?
Note: my true arrays are much larger and contain real (not int) values
Also, it is given that B is longer then A
Thanks!
Assuming you want to minimize the overall discrepancies between elements of A and matched elements in B, the problem can be written as an assignment problem of assigning to every row (element of A) a column (element of B) given a cost matrix C. The Hungarian (or Munkres') algorithm solves the assignment problem.
I assume that you want to minimize cumulative squared distance between A and matched elements in B, and use the function [assignment,cost] = munkres(costMat) by Yi Cao from https://www.mathworks.com/matlabcentral/fileexchange/20652-hungarian-algorithm-for-linear-assignment-problems--v2-3-:
A = [1 5 7];
B = [1 2 3 6 9 10];
[Bprime,matches] = matching(A,B)
function [Bprime,matches] = matching(A,B)
C = (repmat(A',1,length(B)) - repmat(B,length(A),1)).^2;
[matches,~] = munkres(C);
Bprime = B(matches);
end
Assuming instead you want to find matches recursively, as suggested by your question, you could either walk through A, for each element in A find the closest remaining element in B and discard it (sortedmatching below); or you could iteratively form and discard the distance-minimizing match between remaining elements in A and B until all elements in A are matched (greedymatching):
A = [1 5 7];
B = [1 2 3 6 9 10];
[~,~,Bprime,matches] = sortedmatching(A,B,[],[])
[~,~,Bprime,matches] = greedymatching(A,B,[],[])
function [A,B,Bprime,matches] = sortedmatching(A,B,Bprime,matches)
[~,ix] = min((A(1) - B).^2);
matches = [matches ix];
Bprime = [Bprime B(ix)];
A = A(2:end);
B(ix) = Inf;
if(not(isempty(A)))
[A,B,Bprime,matches] = sortedmatching(A,B,Bprime,matches);
end
end
function [A,B,Bprime,matches] = greedymatching(A,B,Bprime,matches)
C = (repmat(A',1,length(B)) - repmat(B,length(A),1)).^2;
[minrows,ixrows] = min(C);
[~,ixcol] = min(minrows);
ixrow = ixrows(ixcol);
matches(ixrow) = ixcol;
Bprime(ixrow) = B(ixcol);
A(ixrow) = -Inf;
B(ixcol) = Inf;
if(max(A) > -Inf)
[A,B,Bprime,matches] = greedymatching(A,B,Bprime,matches);
end
end
While producing the same results in your example, all three methods potentially give different answers on the same data.
Normally I would run screaming from for and while loops in Matlab, but in this case I cannot see how the solution could be vectorized. At least it is O(N) (or near enough, depending on how many equally-close matches to each A(i) there are in B). It would be pretty simple to code the following in C and compile it into a mex file, to make it run at optimal speed, but here's a pure-Matlab solution:
function [out, ind] = greedy_nearest(A, B)
if nargin < 1, A = [1 5 7]; end
if nargin < 2, B = [1 2 3 6 9 10]; end
ind = A * 0;
walk = 1;
for i = 1:numel(A)
match = 0;
lastDelta = inf;
while walk < numel(B)
delta = abs(B(walk) - A(i));
if delta < lastDelta, match = walk; end
if delta > lastDelta, break, end
lastDelta = delta;
walk = walk + 1;
end
ind(i) = match;
walk = match + 1;
end
out = B(ind);
You could first get the absolute distance from each value in A to each value in B, sort them and then get the first unique value to a sequence when looking down in each column.
% Get distance from each value in A to each value in B
[~, minIdx] = sort(abs(bsxfun(#minus, A,B.')));
% Get first unique sequence looking down each column
idx = zeros(size(A));
for iCol = 1:numel(A)
for iRow = 1:iCol
if ~ismember(idx, minIdx(iRow,iCol))
idx(iCol) = minIdx(iRow,iCol);
break
end
end
end
The result when applying idx to B
>> idx
1 4 5
>> B(idx)
1 6 9