子集和组合不同长度数组的有效方法

时间:2018-08-16 21:32:26

标签: python arrays loops numpy scipy

给出3维布尔数据:

np.random.seed(13)
bool_data = np.random.randint(2, size=(2,3,6))

>> bool_data 
array([[[0, 0, 0, 0, 0, 0],
        [0, 1, 0, 0, 1, 0],
        [0, 0, 0, 0, 0, 1]],

       [[1, 0, 1, 1, 0, 0],
        [0, 1, 1, 1, 1, 0],
        [1, 1, 1, 0, 0, 0]]])

我希望计算每行(沿轴= 1)中被两个0包围的连续1的数目,并返回一个带有计数的数组。对于bool_data,这将得到array([1, 1, 2, 4])

由于bool_data的3D结构和每行的变量记数,我不得不笨拙地将记数转换为嵌套列表,使用itertools.chain将其展平,然后将列表反向转换为一个数组:

# count consecutive 1's bounded by two 0's
def count_consect_ones(input):
    return np.diff(np.where(input==0)[0])-1

# run tallies across all rows in bool_data
consect_ones = []
for i in range(len(bool_data)):
    for j in range(len(bool_data[i])):
        res = count_consect_ones(bool_data[i, j])
        consect_ones.append(list(res[res!=0]))

>> consect_ones
[[], [1, 1], [], [2], [4], []]

# combines nested lists
from itertools import chain
consect_ones_output = np.array(list(chain.from_iterable(consect_ones)))

>> consect_ones_output
array([1, 1, 2, 4])

是否有更有效或更聪明的方法?

2 个答案:

答案 0 :(得分:2)

  

consect_ones.append(list(res [res!= 0]))

如果改用.extend,则序列的内容将直接追加。这样就省去了随后合并嵌套列表的步骤:

consect_ones.extend(res[res!=0])

此外,您可以跳过索引编制,直接遍历维度:

consect_ones = []
for i in bool_data:
    for j in i:
        res = count_consect_ones(j)
        consect_ones.extend(res[res!=0])

答案 1 :(得分:1)

我们可以使用技巧在列上填充零,然后在展平版本上查找上坡和下坡索引,最后过滤掉与边界索引相对应的索引,从而为自己提供矢量化解决方案,如下所示-

# Input 3D array : a
b = np.pad(a, ((0,0),(0,0),(1,1)), 'constant', constant_values=(0,0))

# Get ramp-up and ramp-down indices/ start-end indices of 1s islands
s0 = np.flatnonzero(b[...,1:]>b[...,:-1])
s1 = np.flatnonzero(b[...,1:]<b[...,:-1])

# Filter only valid ones that are not at borders
n = b.shape[2]
valid_mask = (s0%(n-1)!=0) & (s1%(n-1)!=a.shape[2])
out = (s1-s0)[valid_mask]

说明-

每行两端填充零作为“句法”的想法是,当我们获得一次性切片数组版本并进行比较时,我们可以使用b[...,1:]>b[...,:-1]来检测上升和下降位置和b[...,1:]<b[...,:-1]。因此,我们得到s0s1作为1s每个岛的开始和结束索引。现在,我们不需要边框的,因此我们需要使它们的列索引追溯到原始的未填充的输入数组,因此该位是s0%(n-1)s1%(n-1)。我们需要删除所有1s岛的开始都在左侧边界而1s每个岛的终点都在右侧边界的所有情况。开始和结束是s0s1。因此,我们使用它们来检查s0是否为0s1是否为a.shape[2]。这些给我们有效的。岛的长度是通过s1-s0获得的,因此请使用有效掩码对其进行掩码以获得所需的输出。

样本输入,输出-

In [151]: a
Out[151]: 
array([[[0, 0, 0, 0, 0, 0],
        [0, 1, 0, 0, 1, 0],
        [0, 0, 0, 0, 0, 1]],

       [[1, 0, 1, 1, 0, 0],
        [0, 1, 1, 1, 1, 0],
        [1, 1, 1, 0, 0, 0]]])

In [152]: out
Out[152]: array([1, 1, 2, 4])