Replace part of an array with the average of its neighbors

828 Views Asked by At

I created this variable in the workspace:

z=

0.000894000000000000

-0.000929000000000000

0.00101500000000000

0.000747000000000000

0.00103900000000000

0.000888000000000000

0.000828000000000000

0.000737000000000000

0.000858000000000000

-0.000723000000000000

0.000874000000000000

I would like to write code that will replace the negative ones with the average of the numbers found directly above and below this number.

3

There are 3 best solutions below

2
On

Try this:

for index = 1 : length( z )
    if ( z( index ) < 0 ) // FIXED
        above = max( index, 1 );
        below = min( index, length( z ) );
        z( index ) = mean( z( above ), z( below ) ); // FIXED
    end
end

but this is not fully correct. What should happen in the following cases:

  • As in the question of @Ĭnfernal Seraphím when the neighbours are also negativ.
  • If there the first and last items are negativ ?
7
On

You can approach this in a linear interpolation viewpoint. Assuming that there are no consecutive negative numbers in z, you can do this with interp1:

%// Define your data
z= [...
0.000894000000000000
-0.000929000000000000
0.00101500000000000
0.000747000000000000
0.00103900000000000
0.000888000000000000
0.000828000000000000
0.000737000000000000
0.000858000000000000
-0.000723000000000000
0.000874000000000000];

keys = (1 : numel(z)).';
out = interp1(keys(z >= 0), z(z >= 0), keys, 'linear', 'extrap');

This also handles the case where there are negative values at the beginning and at the end, and the extrap flag simply extrapolates. I'm not sure if this is what you intended if there are values at the beginning and end that are negative, but I'll leave that for you to figure out.

Nevertheless, the beauty behind this approach is that we provide control points that are delineated from 1 up to as many points in z that we have. These are the x points. The output y points are the values in z. However, we remove those control points that are negative - the same is done for z.

Next, we specify the full spectrum of key points from 1 up to as many elements in z to achieve the final output. Using linear interpolation in this case effectively finds the average between the two points that are separated by a negative.

However, should there be consecutive negative numbers, then what will happen is that we take a look at the value that was positive before the chain of negative values began and look at the value that was positive after the chain, and in between the run of consecutive numbers, the values are smoothly interpolated. I'm not sure if this is what you want, so I'll leave that here for you to figure out.

This is the output we get:

>> format long g;
>> out

out =

              0.000894
             0.0009545
              0.001015
              0.000747
              0.001039
              0.000888
              0.000828
              0.000737
              0.000858
              0.000866
              0.000874

Alternatively, you can use find, but under the following assumptions:

This code works with the following assumptions:

  1. z does not have a negative number at the start and at the end
  2. There are no consecutive negative numbers in z.

Therefore:

%// Find negative values in z
ind = find(z < 0);

%// Replace each negative value with the
%// average of the values above and below each negative
z(ind) = (z(ind-1) + z(ind+1)) / 2;

The above code mutates z so that the negative values are replaced by the neighbouring average. We again get:

>> format long g;
>> z

z =

              0.000894
             0.0009545
              0.001015
              0.000747
              0.001039
              0.000888
              0.000828
              0.000737
              0.000858
              0.000866
              0.000874
1
On

Not the most efficient approach, but...

av = conv(z, [.5 0 .5], 'same');  %// compute average for all elements
ind = z<0;                        %// determine which are negative
z(ind) = av(ind);                 %// replace those only