将二进制列表中的 0 更改为 1,这样就不会有超过 N 个连续的零

Change 0's to 1's in a binary list so that there wouldn't be more than N consecutive zeros

考虑以下列表:

l = [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0]

1 将列表细分为 5 个部分:

l = [0, 0, 0, 1,   0, 0, 0, 0, 0, 0, 1,   0, 0, 1,   0, 1,   0, 0, 0] 

我希望每个部分在 1 之前的连续零(如果可能)不超过 n 个,但您不能删除当前的 1。也不能有 1 相互跟随。

简单示例:假设 n = 3,l应该是:

l = [0, 0, 0, 1,   0, 0, 0, 1, 0, 0, 1,   0, 0, 1,   0, 1,   0, 0, 0] 

对于 n = 2 它将是:

l = [0, 0, 0, 1,   0, 0, 1, 0, 0, 0, 1,   0, 0, 1,   0, 1,   0, 0, 1]

对于第一部分,我没有在两个零之后包含一个 1,因为这样你就会有两个 1 彼此跟随。

知道我该怎么做吗?

这是我尝试过的:

import numpy as np
max_number_of_cells_per_list = 3
l = [0, 0, 0, 1, 0, 0, 0,0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0]
print(l)
# Find position of 1
pos_1 =[i for i,x in enumerate(l) if x == 1]
# Get number of cells 
pos_1.insert(0,0)
numb_cells = np.diff(pos_1)

n = np.round(np.divide(numb_cells,max_number_of_cells_per_list))

k = 0
j = 0
for i,li in enumerate(l):
    if l[i] == 1:
        if n[k] > 1:
            add = int((i-j)/n[k])

            for jj in range(int(n[k])):

                if jj == n[k]-1:
                    jj = i
                else:
                    jj += add

                l[jj] = 1

        k += 1
        j = i



print(l)

如果您尝试 运行 代码,您会发现它与 l 没有区别。我不明白为什么...但如果您有 better/different 个想法,我不太愿意找出我的错误。 :)

由于您使用的是 NumPy,这里有一个使用它的解决方案。请注意,它没有矢量化,我不确定你是否可以对其进行矢量化,因为我们必须对数组执行分组操作,而 NumPy 没有太多的功能(尽管我可能只是不这样做)看到它了)。

我将使用 np.split 获取 [0, ..., 1] 组,然后检查两种情况:首先,对于实际上不以 1 结尾的数组(一个可能的组在数组的末尾),以及具有超过 n + 2 个零的数组。然后我在每个 n + 1 位置插入 1 以确保不会有两个 1 在一起。

import numpy as np

a = np.array([0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0])
n = 3  # or n = 2, or any other n >= 0 value

result = []
for array in np.split(a.copy(), np.where(a == 1)[0] + 1):
    last_index = -2 if array[-1] == 1 else None
    array[n:last_index:n + 1] = 1
    result.append(array)
np.concatenate(result)
# for n = 3: array([0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0])
# for n = 2: array([0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1])

或者,我们可以只对 1 的索引进行操作,而不是将数组分成多个部分并对它们进行操作。例如,这里我得到 1 的初始索引,并在使用 range:

之间添加更多索引
from itertools import tee

l = [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0]
n = 3 


def pairwise(iterable):
    """s -> (s0, s1), (s1, s2), (s2, s3), ..."""
    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)


def one_indices(seq, n):   
    """Returns new indices where we will put 1s"""
    indices = [index + 1 for index, value in enumerate(seq) if value == 1]
    complete_groups_count = len(indices)  # those that end with 1
    indices = [0, *indices, len(seq)]

    for group_index, (start, end) in enumerate(pairwise(indices), start=1):
        if group_index <= complete_groups_count:
            yield from range(start + n, end - 2, n + 1)
            yield end - 1
        else:  # last group that doesn't end with 1
            yield from range(start + n, end, n + 1)


result = [0] * len(l)
for index in one_indices(l, 3):
    result[index] = 1
result
# for n = 3: [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0]
# for n = 2: [0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1]

这可能比第一个示例中的拆分和连接数组更有效,但也更难阅读。


最后,作为奖励,这里有一个使用 pandas 的解决方案。我在你之前的相关问题中看到你正在使用它,所以你可能会觉得它有用:

from functools import partial

import pandas as pd


def fill_ones(series, n):
    last_index = -2 if series.iloc[-1] == 1 else None
    series.iloc[n:last_index:n + 1] = 1
    return series


l = [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0]
s = pd.Series(l)
groups = s.shift().eq(1).cumsum()
fill_w_distance_3 = partial(fill_ones, n=3)
s.groupby(groups).transform(fill_w_distance_3).tolist()
# [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0]

fill_w_distance_2 = partial(fill_ones, n=2)
s.groupby(groups).transform(fill_w_distance_2).tolist()
# [0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1]