6
$\begingroup$

I have vectors of equal lengths where I want to replace the zeroes (missing data) with their preceding values. So far I have written:

dt = {1, 0, 2, 0, 3};

MapThread[(dt[[#1]] = dt[[#2]]) &, {#, #-1}] & [Position[dt, 0]];

dt

{1, 1, 2, 2, 3}

I have 3 questions:

(1) Are there alternatives to the above code, which I don't like aesthetically ?

(2) How can I expand it to go over matrices of equal-length-vectors ?

(3) How can I deal with a zero in the first position (replace by the following element) ?

$\endgroup$
5
  • $\begingroup$ Should multiple zeros in a row all be replaced, e.g. {2, 0, 0, 7} -> {2, 2, 2, 7}? $\endgroup$
    – Mr.Wizard
    Mar 20, 2015 at 16:17
  • $\begingroup$ Yes. The problem seems familiar to me too. Wasn't able to locate it. $\endgroup$
    – eldo
    Mar 20, 2015 at 16:32
  • $\begingroup$ Related: (26723). Somewhat related: (57526) $\endgroup$
    – Mr.Wizard
    Mar 20, 2015 at 16:41
  • $\begingroup$ Possible duplicate: (2952) $\endgroup$
    – Mr.Wizard
    Mar 20, 2015 at 16:49
  • $\begingroup$ What if the first element is zero? $\endgroup$ Jan 22, 2018 at 19:00

3 Answers 3

5
$\begingroup$

I know this is a duplicate but since I failed to find it with ten minutes of searching:

x = {2, 0, 0, 7, 0, 3, 0, 0, 1};

FoldList[If[#2 == 0, #, #2] &, x]
{2, 2, 2, 7, 7, 3, 3, 3, 1}

To handle leading zeros:

x = {0, 7, 0, 3, 0, 0, 1};

FoldList[If[#2 == 0, #, #2] &, SelectFirst[x, # != 0 &], Rest @ x]
{7, 7, 7, 3, 3, 3, 1}

The case without leading zeroes can also be handled nicely using Split, borrowed from Leonid's method for Fill out blanks with a upcoming number in a list?:

x = {2, 0, 0, 7, 0, 3, 0, 0, 1};

Join @@ Accumulate /@ Split[x, #2 == 0 &]
{2, 2, 2, 7, 7, 3, 3, 3, 1}

The leading zeros can be handled by prepending the first non-zero value to the list, then applying Rest at the end.

For Q2 I think all you need is to convert either method to a function and map it:

fn[x_] := FoldList[If[#2 == 0, #, #2] &, SelectFirst[x, # != 0 &], Rest @ x]

fn /@ {{2, 0, 0, 7, 0, 3, 0, 0, 1}, {0, 1, 2, 7, 0, 3, 0, 0, 1}}
{{2, 2, 2, 7, 7, 3, 3, 3, 1}, {1, 1, 2, 7, 7, 3, 3, 3, 1}}
$\endgroup$
8
  • $\begingroup$ Phantastic !!! How would you map it over matrices ? (Q2) $\endgroup$
    – eldo
    Mar 20, 2015 at 16:36
  • $\begingroup$ @eldo Would you please provide an example? $\endgroup$
    – Mr.Wizard
    Mar 20, 2015 at 16:41
  • $\begingroup$ --------------{x,x} $\endgroup$
    – eldo
    Mar 20, 2015 at 16:50
  • $\begingroup$ @eldo Sorry, I still don't understand. $\endgroup$
    – Mr.Wizard
    Mar 20, 2015 at 16:53
  • $\begingroup$ Dear Wizard, my real situation is like this: x = {{2, 0, 0, 7, 0, 3, 0, 0, 1}, {0, 1, 2, 7, 0, 3, 0, 0, 1}} $\endgroup$
    – eldo
    Mar 20, 2015 at 17:01
3
$\begingroup$

TemporalData + MissingDataMethod

ClearAll[interpolateMissing]
interpolateMissing = Quiet @ TemporalData[# /. 0 -> Missing[], Automatic, 
  MissingDataMethod -> {"Interpolation", InterpolationOrder -> 0}]["States"] &;

Examples:

interpolateMissing  @ {1, 0, 2, 0, 3}

{{1, 1, 2, 2, 3}}

interpolateMissing  @ {0, 7, 0, 3, 0, 0, 1}

{{7, 7, 7, 3, 3, 3, 1}}

interpolateMissing  @ {0, 0, 0, 0, 7, 0, 3, 0, 0, 1}

{{7, 7, 7, 7, 7, 7, 3, 3, 3, 1}}

At the price of extra pair braces in the output when the input is a list, we get the second requirement

How can I expand it to go over matrices of equal-length-vectors?

for free, since interpolateMissing works as is when the input is an array:

interpolateMissing  @ {{1, 0, 2, 0, 3, 0, 1}, {0, 7, 0, 3, 0, 0, 1}}

{{1, 1, 2, 2, 3, 3, 1}, {7, 7, 7, 3, 3, 3, 1}}

Note: Quiet is used to suppress the following warning:

InterpolatingFunction::dmval: Input value {0} lies outside the range of data in the interpolating function. Extrapolation will be used. >>

By the way, Extrapolation is exactly what we need to handle cases with leading zeros according to requirement (3).

$\endgroup$
2
  • $\begingroup$ Maybe "replaceMissing" should be "interpolateMissing"? $\endgroup$
    – Rabbit
    Jan 22, 2018 at 16:29
  • $\begingroup$ @Rabbit, yes, thank you. $\endgroup$
    – kglr
    Jan 22, 2018 at 18:26
1
$\begingroup$

You can also use repeated replacement 1,3) The first two rules handle leading zeros, the first one handles several leading zeros and the second one handles a single leading zero.:

cleanVector[v_]:=v//.{ {0, x:0___,y_,z___}:>{y,x,y,z},
                       {0, x:0...,y_,z___}:>{y,x,y,z},
                       {x___,y_,0,z___} :> {x,y,y,z} };

2) Depending on what you mean by 'go over matrices' I'd just map this over the matrix to clean each vector independently.

m={v1,v2,v3};
cleanVector/@m;
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.