如何在列表的间隔之间填充元素

时间:2020-04-18 16:45:23

标签: python pandas numpy dataframe

我有一个这样的列表:

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]

因此,间隔从1开始,到0结尾。 我如何替换这些间隔中的值,例如1?结果将如下所示:

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

在此示例中,我使用NaN,但是适用于任何值的通用解决方案也很好

6 个答案:

答案 0 :(得分:5)

熊猫解决方案:

s = pd.Series(list_1)
s1 = s.eq(1)
s0 = s.eq(0)
m = (s1 | s0).where(s1.cumsum().ge(1),False).cumsum().mod(2).eq(1)
s.loc[m & s.isna()] = 1
print(s.tolist())
#[nan, nan, 1.0, 1.0, 1.0, 1.0, 0.0, nan, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, nan, 1.0, 1.0]

但是如果只有10NaN,则可以:

s = pd.Series(list_1)
s.fillna(s.ffill().where(lambda x: x.eq(1))).tolist()

输出

[nan,
 nan,
 1.0,
 1.0,
 1.0,
 1.0,
 0.0,
 nan,
 1.0,
 1.0,
 0.0,
 1.0,
 1.0,
 0.0,
 nan,
 1.0,
 1.0]

答案 1 :(得分:4)

以下是使用np.cumsum的基于numpy的方法:

self.pos.gui.show_popup('error',{
                title :_t('Modification Resctricted'),
                body  :_t('Booked Order cannot be modified'),
            });

a = np.array([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])

ix0 = (a == 0).cumsum()
ix1 = (a == 1).cumsum()
dec = (ix1 - ix0).astype(float)
# Only necessary if the seq can end with an unclosed interval
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)

答案 2 :(得分:3)

这是一个基于NumPy的游戏-

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)

样品运行-

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])

使用数组输入对NumPy解决方案进行基准测试

为简单起见,我们将通过平铺并测试基于NumPy的示例,将给定的示例放大到10,000x

其他NumPy解决方案-

#@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

时间-

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)

复制过程不会花费很多时间,因此可以忽略-

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

答案 3 :(得分:2)

这是一个代码,其中变量replace将确定是否应替换该元素,并且for将从间隔的0len进行迭代,以及是否找到1,然后replace将为true,然后元素将被替换,当它找到下一个0时,替换将是掉落的,元素将不会替换,直到再次出现1

  replace = False
    for i in (len(interval)-1):
        if interval[i]==1:
            replace = True
        elif interval[i]==0:
            replace = False
        if replace:
            list[i]=inerval[i]

答案 4 :(得分:2)

假设每个1后跟0(减去最后1个):

list_1 = np.array([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])
zeros_ind = np.where(list_1 == 0)[0]
ones_ind = np.where(list_1 == 1)[0]
ones_ind = ones_ind[:zeros_ind.size]

#create a concatenated list of ranges of indices you desire to slice
indexer = np.r_[tuple([np.s_[i:j] for (i,j) in zip(ones_ind,zeros_ind)])]
#slice using numpy indexing
list_1[indexer] = 1

输出:

[nan nan  1.  1.  1.  1.  0. nan  1.  1.  0.  1.  1.  0. nan  1. nan]

答案 5 :(得分:1)

您可以使用np.argwhere检索索引1和0,然后在每个切片中填充值:

import numpy as np

a = np.array([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])

ones = np.squeeze(np.argwhere(a==1))
zeros = np.squeeze(np.argwhere(a==0))

if ones[0]>zeros[0]:
    zeros = zeros[1:]

value = -999
for i,j in zip(ones,zeros):
    a[i+1:j] = value

a
array([  nan,   nan,    1., -999., -999., -999.,    0.,   nan,    1.,
       -999.,    0.,    1., -999.,    0.,   nan,    1.,   nan])
相关问题