2

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?

Stewie Griffin
  • 14,889
  • 11
  • 39
  • 70
H.K
  • 231
  • 1
  • 2
  • 15
  • I have never seen this done in a good way, but that does not mean it cannot be done. I would do something like this. `b = 1:5; bind = [1,5,9,13,17]; c = zeros(length(a)+length(b),1); c(bind) = 1:5; c(c==0) = a;`. This is not an answer though, since it does not append. It is also possible to add elements at the end and move the elements int the vector. This will cause less memory overhead, but is more complicated to write effective code for. By the way, the word you seek for is rather "insert" than "append", since you do not want to add elements to the end of the vector. – patrik Aug 09 '16 at 09:04
  • Yes, insert will be the right word. Actually my vector a has more than 5000 entities. So inserting the way you suggested will result in large overheads. – H.K Aug 09 '16 at 09:07
  • I would not call 5000 entities a lot of overhead in Matlab. This amount of memory is allocated just about any time you use the vector. However it is your call. I cannot say what is good or bad for you. In case you get memory overflow you certainly need to do it another way. – patrik Aug 09 '16 at 09:15

4 Answers4

7

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
GameOfThrows
  • 4,510
  • 2
  • 27
  • 44
  • Nice solution! I am not sure if this actually is more efficient than copying the vector (this is probably trade of between memory allocation and computational efficiency), but it is solves the problem from the post in an elegant way. – patrik Aug 09 '16 at 09:12
  • @patrik thanks! I think rebuilding a new vector would be much easier, but I noticed the OP wanted to insert values at even intervals and thought this was an interesting approach :D – GameOfThrows Aug 09 '16 at 09:13
  • Right, I would assume that the zero-padding actually does a copy anyway somewhere, but since this is abstracted away I would not bother too much about _how_ the zero-padding is done. – patrik Aug 09 '16 at 09:25
1

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
Divakar
  • 218,885
  • 19
  • 262
  • 358
  • Right, this works, but is not really inserting. It is definitely the standard way to handle arrays and vectors, but it is still not inserting. – patrik Aug 09 '16 at 09:23
  • 1
    @patrik Assuming MATLAB stores elements in contiguous memory locations for an array, inserting elements has no meaning. It essentially has to re-assign into that variable name or create a new one. MATLAB rarely uses the view copcept as used with NumPy/Python, except in cases like reshaping. Back to our case, I am pretty sure we would be making copies whatever approach you follow, unless you are doing deeper and working at mex/C level. – Divakar Aug 09 '16 at 09:34
0
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
kkuilla
  • 2,226
  • 3
  • 34
  • 37
  • This is about how I would do, but I am not sure this actually is to insert. You made a new vector and added all elements. – patrik Aug 09 '16 at 09:09
  • @patrik Well, I guess you have a point in a way but this is how I would do it. – kkuilla Aug 09 '16 at 09:13
0

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.

PieCot
  • 3,564
  • 1
  • 12
  • 20