【问题标题】:Efficient way to subset and combine arrays of different lengths对不同长度的数组进行子集化和组合的有效方法
【发布时间】:2019-01-23 22:03:34
【问题描述】:

给定一个 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])

有没有更有效或更聪明的方法来做到这一点?

【问题讨论】:

    标签: python arrays loops numpy scipy


    【解决方案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]
    

    解释-

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

    样本输入、输出-

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

    【讨论】:

    • 难以置信的解决方案!但是,即使在研究了脚本之后,我仍然不明白这里的工作逻辑。例如,获取加速和减速指数的目的是什么?为什么s0%(n-1)%(n-3)——还有,“-1”和“-3”是从哪里来的?最后,(s1-s0)[valid_mask] 的原因是什么?
    • 很好的解释!谢谢!!我仍在研究细节,希望尽快了解所有内容。
    【解决方案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])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-16
      • 1970-01-01
      • 2010-10-02
      • 2021-12-27
      • 1970-01-01
      • 2022-01-18
      相关资源
      最近更新 更多