How to fill elements between intervals of a list

后端 未结 6 2314
无人及你
无人及你 2021-02-14 18:19

I have a list like this:

list_1 = [np.NaN, np.NaN, 1, np.NaN, np.NaN, np.NaN, 0, np.NaN, 1, np.NaN, 0, 1, np.NaN, 0, np.NaN,  1, np.NaN]

So the

6条回答
  •  伪装坚强ぢ
    2021-02-14 18:27

    Here's a NumPy based one -

    def fill_inbetween(a):
        m1 = a==1
        m2 = a==0
        id_ar = m1.astype(int)-m2
        idc = id_ar.cumsum()
        idc[len(m1)-m1[::-1].argmax():] =  0
        return np.where(idc.astype(bool), 1, a)
    

    Sample run -

    In [44]: a # input as array
    Out[44]: 
    array([nan, nan,  1., nan, nan, nan,  0., nan,  1., nan,  0.,  1., nan,
            0., nan,  1., nan])
    
    In [45]: fill_inbetween(a)
    Out[45]: 
    array([nan, nan,  1.,  1.,  1.,  1.,  0., nan,  1.,  1.,  0.,  1.,  1.,
            0., nan,  1., nan])
    

    Benchmarking on NumPy solutions with array input

    To keep things simple, we will just scaled up the given sample to 10,000x by tiling and test out the NumPy based ones.

    Other NumPy solutions -

    #@yatu's soln
    def func_yatu(a):
        ix0 = (a == 0).cumsum()
        ix1 = (a == 1).cumsum()
        dec = (ix1 - ix0).astype(float)
        ix = len(a)-(a[::-1]==1).argmax()
        last = ix1[-1]-ix0[-1]
        if last > 0:
            dec[ix:] = a[ix:]
        out = np.where(dec==1, dec, a)
        return out
    
    # @FBruzzesi's soln (with the output returned in a separate array)
    def func_FBruzzesi(a, value=1):
        ones = np.squeeze(np.argwhere(a==1))
        zeros = np.squeeze(np.argwhere(a==0))   
        if ones[0]>zeros[0]:
            zeros = zeros[1:]   
        out = a.copy()
        for i,j in zip(ones,zeros):
            out[i+1:j] = value
        return out
    
    # @Ehsan's soln (with the output returned in a separate array)
    def func_Ehsan(list_1):
        zeros_ind = np.where(list_1 == 0)[0]
        ones_ind = np.where(list_1 == 1)[0]
        ones_ind = ones_ind[:zeros_ind.size]        
        indexer = np.r_[tuple([np.s_[i:j] for (i,j) in zip(ones_ind,zeros_ind)])]
        out = list_1.copy()
        out[indexer] = 1
        return out
    

    Timings -

    In [48]: list_1 = [np.NaN, np.NaN, 1, np.NaN, np.NaN, np.NaN, 0, np.NaN, 1, np.NaN, 0, 1, np.NaN, 0, np.NaN,  1, np.NaN]
        ...: a = np.array(list_1)
    
    In [49]: a = np.tile(a,10000)
    
    In [50]: %timeit func_Ehsan(a)
        ...: %timeit func_FBruzzesi(a)
        ...: %timeit func_yatu(a)
        ...: %timeit fill_inbetween(a)
    4.86 s ± 325 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    253 ms ± 29.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    3.39 ms ± 205 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    2.01 ms ± 168 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    The copying process doesn't take much of runtime, so that can be ignored -

    In [51]: %timeit a.copy()
    78.3 µs ± 571 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    

提交回复
热议问题