Substitute a vector value with two values in MATLAB
Asked Answered
G

4

5

I have to create a function that takes as input a vector v and three scalars a, b and c. The function replaces every element of v that is equal to a with a two element array [b,c].

For example, given v = [1,2,3,4] and a = 2, b = 5, c = 5, the output would be:

out = [1,5,5,3,4]

My first attempt was to try this:

v = [1,2,3,4];
v(2) = [5,5];

However, I get an error, so I do not understand how to put two values in the place of one in a vector, i.e. shift all the following values one position to the right so that the new two values fit in the vector and, therefore, the size of the vector will increase in one. In addition, if there are several values of a that exist in v, I'm not sure how to replace them all at once.

How can I do this in MATLAB?

Gifford answered 1/5, 2015 at 16:55 Comment(6)
v = [v(1) [5,5] v(3:end)] would work though depending on how you want to use this there may be a better answerOcko
I have to create a function that takes as input a vector v and three scalars a, b and c. The function replaces every element of v that is equal to a with b and c.Gifford
@MartaSampietro - Let me get this straight. If we find a value a in the vector, does each element get replaced with a 2 element vector? For example, if v = [1,2,3,4,5] and a = 4 and b = 10, c = 11, does the new vector become v = [1,2,3,10,11,5]?Weathersby
Correct, that's exactly what it should do. But when there are several values equal to a in the vector I am having problems...Gifford
@MartaSampietro - I've taken the liberty to change your question to reflect your comment. The comment you made is a serious design element that was omitted from your first version of the question. With this, a lot of the comments made to you to solve your problem are now invalid.Weathersby
So did our answers help you?Weathersby
C
6

Here's a solution using cell arrays:

% remember the indices where a occurs
ind = (v == a);
% split array such that each element of a cell array contains one element
v = mat2cell(v, 1, ones(1, numel(v)));
% replace appropriate cells with two-element array
v(ind) = {[b c]};
% concatenate
v = cell2mat(v);

Like rayryeng's solution, it can replace multiple occurrences of a.

The problem mentioned by siliconwafer, that the array changes size, is here solved by intermediately keeping the partial arrays in cells of a cell array. Converting back to an array concenates these parts.

Coffle answered 1/5, 2015 at 17:21 Comment(5)
Good choice with mat2cell. This is a much easier solution to read rather than my pure indexing approach.Weathersby
Thanks @rayryeng. Yours should be more efficient for larger arrays though.Coffle
Nice, although there are 2 simplifications you can make: 1) Use num2cell to convert v to a cell array: v = num2cell(v); 2) convert v back to a numeric vector using a comma separated list instead of cell2mat: v = [v{:}];Lothaire
@gnovice, thanks for the comment! I already wondered why such a simple thing as putting each array element in a cell needs such a complex syntax.Coffle
Thank you very much, it does just what I needed it to do! I had not thought about using cell arrays, and this approach seems to work out pretty well!Gifford
W
5

Something I would do is to first find the values of v that are equal to a which we will call ind. Then, create a new output vector that has the output size equal to numel(v) + numel(ind), as we are replacing each value of a that is in v with an additional value, then use indexing to place our new values in.

Assuming that you have created a row vector v, do the following:

%// Find all locations that are equal to a
ind = find(v == a);

%// Allocate output vector
out = zeros(1, numel(v) + numel(ind));

%// Determine locations in output vector that we need to
%// modify to place the value b in
indx = ind + (0:numel(ind)-1);

%// Determine locations in output vector that we need to
%// modify to place the value c in
indy = indx + 1;

%// Place values of b and c into the output
out(indx) = b;
out(indy) = c;

%// Get the rest of the values in v that are not equal to a
%// and place them in their corresponding spots.
rest = true(1,numel(out));
rest([indx,indy]) = false;
out(rest) = v(v ~= a);

The indx and indy statements are rather tricky, but certainly not hard to understand. For each index in v that is equal to a, what happens is that we need to shift the vector over by 1 for each index / location of v that is equal to a. The first value requires that we shift the vector over to the right by 1, then the next value requires that we shift to the right by 1 with respect to the previous shift, which means that we actually need to take the second index and shift by the right by 2 as this is with respect to the original index.

The next value requires that we shift to the right by 1 with respect to the second shift, or shifting to the right by 3 with respect to the original index and so on. These shifts define where we're going to place b. To place c, we simply take the indices generated for placing b and move them over to the right by 1.

What's left is to populate the output vector with those values that are not equal to a. We simply define a logical mask where the indices used to populate the output array have their locations set to false while the rest are set to true. We use this to index into the output and find those locations that are not equal to a to complete the assignment.


Example:

v = [1,2,3,4,5,4,4,5];
a = 4;
b = 10;
c = 11;

Using the above code, we get:

out =

     1     2     3    10    11     5    10    11    10    11     5

This successfully replaces every value that is 4 in v with the tuple of [10,11].

Weathersby answered 1/5, 2015 at 17:11 Comment(11)
Why search v ? OP seems to already know the desired indices for the new values.Hylotheism
plus one for out(rest) = v(~(v==a));Coffle
@Hylotheism - No she doesn't. Read her comment in her question. The OP is only given a, b, c and is required a function that takes in a vector and these three constants only.Weathersby
@siliconwafer, otherwise the simplest solution would be x = [1 2 5 5 3 4]Coffle
@A.Donda - Thank you sir :) +1 for you as well.Weathersby
@Weathersby I see it now, I missed that comment.Hylotheism
@Hylotheism - no worries at all... classic case where there is more information in the comments rather than the question itself. I'm going to change her question to reflect this.Weathersby
Love masks! Good choice going with it.Ruth
@A.Donda - I replaced it with v(v ~= a). Easier to read I think.Weathersby
@Ruth - Thanks :) I wanted to employ some clever bsxfun or accumarray here but couldn't think of a good solution.Weathersby
Those won't be as efficient as this one, might be compact though!Ruth
P
4

I think that strrep deserves a mention here. Although it's called string replacement and warns for non-char input, it still works perfectly fine for other numbers as well (including integers, doubles and even complex numbers).

v = [1,2,3,4] 
a = 2, b = 5, c = 5
out = strrep(v, a, [b c])

Warning: Inputs must be character arrays or cell arrays of strings.
out =
     1     5     5     3     4
Principium answered 4/5, 2015 at 20:20 Comment(1)
Interesting approach indeed.Weathersby
H
2

You are not attempting to overwrite an existing value in the vector. You're attempting to change the size of the vector (meaning the number of rows or columns in the vector) because you're adding an element. This will always result in the vector being reallocated in memory.

Create a new vector, using the first and last half of v.

Let's say your index is stored in the variable index.

index = 2;
newValues = [5, 5];
x = [ v(1:index), newValues,  v(index+1:end) ]

x =

     1     2     5     5     3     4
Hylotheism answered 1/5, 2015 at 17:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.