【问题标题】:Is there a faster way than the for loop to label matrix(3D array) in python?在 python 中,有没有比 for 循环更快的方法来标记矩阵(3D 数组)?
【发布时间】:2019-07-29 19:02:00
【问题描述】:

我用 Python 写了一个标记矩阵(3D 数组)的代码。 代码的概念是

  1. 检查 3D 数组中的 2×2×2 矩阵(无论我想要什么大小)
  2. 如果矩阵有 1、2、3 作为元素,则矩阵中的所有元素都将变为矩阵中的“最大唯一数 + 1”。

    import numpy as np
    
    def label_A(input_field):
    labeling_A = np.copy(input_field)
    labeling_test = np.zeros((input_field.shape))
    for i in range(0,input_field.shape[0]-1):
        for j in range(0,input_field.shape[1]-1):
            for k in range(0,input_field.shape[2]-1):
                test_unit = input_field[i:i+2,j:j+2,k:k+2]
                if set(np.unique(test_unit).astype(int)) >= set((1,2,3)):
                    labeling_test[i:i+2,j:j+2,k:k+2] = np.max(input_field)+1
                    labeling_A[labeling_test == np.max(input_field)+1] = np.max(input_field)+1
        return labeling_A
    

这是一个简单的 3D 矩阵示例代码。

example = np.random.randint(0, 10, size=(10, 10, 10))
label_example = label_A(example)
label_example

在我看来,代码本身没有问题,实际上它可以工作。但是,我很好奇是否有更快的方法来为此执行相同的功能?

【问题讨论】:

  • 你能提供一个示例输入吗?
  • 示例不需要格式化,可以复制/粘贴吗?
  • 是的,有办法,在多个线程中做同样的事情。 ^-^

标签: python arrays performance numpy matrix


【解决方案1】:

此实现返回建议的结果并在 1.8 秒内处理 (140,140,​​140) 大小的张量。

import numpy as np
from scipy.signal import convolve

def strange_convolve(mat, f_shape, _value_set, replace_value):
    _filter =np.ones(tuple(s*2-1 for s in f_shape))
    replace_mat = np.ones(mat.shape)
    for value in _value_set:
        value_counts = convolve((mat==value),_filter,mode='same')
        replace_mat*=(value_counts>0)
    mat[replace_mat==1]=replace_value
    return mat
example = np.random.randint(0, 8, size=(10, 10, 10))
print('same_output validation is '+str((strange_convolve(example,(2,2,2),(1,2,3),4) == label_A(example)).min()))

import time 
example = np.random.randint(0, 10, size=(140, 140, 140))
timer = time.time()
strange_convolve(example,(2,2,2),(1,2,3),4)
print(time.time()-timer)

1.8871610164642334

【讨论】:

  • 我没用过convolve,所以不是很懂,但是我会说这个肯定更快。但是,您当前的代码似乎存在一些问题。首先: label_A 不包含在您的示例中。第二:s*2-1 创建一个 3x3x3 的过滤器。 s*2-2 将为您提供 OP 正在寻找的 2x2x2 块。第三:我还没有确定原因,但是即使使用您的设置进行了更改,我仍然会得到一些不同的结果。我相信你的方法绝对是要走的路(并告诉我我需要学习和使用 scipy)。只需几个问题即可返回正确结果。
【解决方案2】:

首先,您的代码存在一些问题,可以轻松解决并加快速度。
对于每个循环,您都重新计算 np.max(input_field)+1 三次。
你的矩阵越大,影响就越明显。注意测试 A 和 B 的差异。

我尝试使用上面的 convolve 示例运行测试,虽然速度很快,但结果与其他测试不同(在下面的设置中应该是相同的)。我相信它在 3x3x3 块中寻找 1、2 或 3。

大小为 10 的标签 A --- 0:00.015628
标签 B 大小为 10 --- 0:00.015621
标签 F 大小为 10 --- 0:00.015628

大小为 50 的标签 A --- 0:15.984662
标签 B 大小为 50 --- 0:10.093478
标签 F 大小为 50 --- 0:02.265621

大小为 80 的标签 A --- 4:02.564660
标签 B 大小为 80 --- 2:29.439298
标签 F 大小为 80 --- 0:09.437868

------ 已编辑 ------ convolve 方法肯定更快,但我认为 Peter 给出的代码存在一些问题。

Label A with size of 10 : 00.013985
[[ 2 10 10 10 10  4  9  0  8  7]
 [ 9 10 10 10 10  0  9  8  5  9]
 [ 3  8  4  0  9  4  2  8  7  1]
 [ 4  7  6 10 10  4  8  8  5  4]] 

Label B with size of 10 : 00.014002
[[ 2 10 10 10 10  4  9  0  8  7]
 [ 9 10 10 10 10  0  9  8  5  9]
 [ 3  8  4  0  9  4  2  8  7  1]
 [ 4  7  6 10 10  4  8  8  5  4]] 

Label Flat with size of 10 : 00.020001
[[ 2 10 10 10 10  4  9  0  8  7]
 [ 9 10 10 10 10  0  9  8  5  9]
 [ 3  8  4  0  9  4  2  8  7  1]
 [ 4  7  6 10 10  4  8  8  5  4]] 

Label Convolve with size of 10 : 00.083996
[[ 2  2 10  8  4 10  9  0  8  7]
 [ 9 10  0  4  7 10  9 10 10  9]
 [ 3  8  4  0  9  4  2 10  7 10]
 [ 4  7 10  5  0  4  8 10  5  4]] 

OP 希望将 2x2x2 矩阵的所有元素设置为更高的值。
请注意,当前设置中的 convolve 设置了一些单个空间元素,而不是 2x2x2 矩阵模式。

下面是我的代码:

import numpy as np
from scipy.signal import convolve
from pandas import datetime as dt

def label_A(input_field):
    labeling_A = np.copy(input_field)
    labeling_test = np.zeros((input_field.shape))
    for i in range(0,input_field.shape[0]-1):
        for j in range(0,input_field.shape[1]-1):
            for k in range(0,input_field.shape[2]-1):
                test_unit = input_field[i:i+2,j:j+2,k:k+2]
                if set(np.unique(test_unit).astype(int)) >= set((1,2,3)):
                    labeling_test[i:i+2,j:j+2,k:k+2] = np.max(input_field)+1
                    labeling_A[labeling_test == np.max(input_field)+1] = np.max(input_field)+1
    return labeling_A


def label_B(input_field):
    labeling_B = np.copy(input_field)
    labeling_test = np.zeros((input_field.shape))
    input_max = np.max(input_field)+1
    for i in range(0,input_field.shape[0]-1):
        for j in range(0,input_field.shape[1]-1):
            for k in range(0,input_field.shape[2]-1):
                test_unit = input_field[i:i+2,j:j+2,k:k+2]
                if set(np.unique(test_unit).astype(int)) >= set((1,2,3)):
                    labeling_test[i:i+2,j:j+2,k:k+2] = input_max
                    labeling_B[labeling_test == input_max] = input_max
    return labeling_B


def label_Convolve(input_field):
    _filter =np.ones([2,2,2])
    replace_mat = np.ones(input_field.shape)
    input_max = np.max(input_field)+1
    for value in (1,2,3):
        value_counts = convolve((input_field==value),_filter,mode='same')
        replace_mat*=(value_counts>0)
    input_field[replace_mat==1] = input_max
    return input_field


def flat_mat(matrix):
    flat = matrix.flatten()
    dest_mat = np.copy(flat)
    mat_width = matrix.shape[0]
    mat_length = matrix.shape[1]
    mat_depth = matrix.shape[2]
    input_max = np.max(matrix)+1

    block = 0
    for w in range(mat_width*(mat_length)*(mat_depth-1)):
        if (w+1)%mat_width != 0:
            if (block+1)%mat_length == 0:
                pass
            else:
                set1 = flat[w:w+2]
                set2 = flat[w+mat_width:w+2+mat_width]
                set3 = flat[w+(mat_width*mat_length):w+(mat_width*mat_length)+2]
                set4 = flat[w+(mat_width*mat_length)+mat_width:w+(mat_width*mat_length)+mat_width+2]
                fullblock = np.array([set1, set2, set3, set4])
                blockset = np.unique(fullblock)
                if set(blockset) >= set((1,2,3)):
                    dest_mat[w:w+2] = input_max
                    dest_mat[w+mat_width:w+2+mat_width] = input_max
                    dest_mat[w+(mat_width*mat_length):w+(mat_width*mat_length)+2] = input_max
                    dest_mat[w+(mat_width*mat_length)+mat_width:w+(mat_width*mat_length)+mat_width+2] = input_max
        else:
            block += 1
    return_mat = dest_mat.reshape(mat_width, mat_length, mat_depth)
    return(return_mat)


def speedtest(matrix,matrixsize):
    starttime = dt.now()
    label_A_example = label_A(matrix)
    print(f'Label A with size of {matrixsize} : {dt.now() - starttime}')
    print(label_A_example[0][0:4], '\n')

    starttime = dt.now()
    label_B_example = label_B(matrix)
    print(f'Label B with size of {matrixsize} : {dt.now() - starttime}')
    print(label_B_example[0][0:4], '\n')

    starttime = dt.now()
    label_Inline_example = flat_mat(matrix)
    print(f'Label Flat with size of {matrixsize} : {dt.now() - starttime}')
    print(label_Inline_example[0][0:4], '\n')

    starttime = dt.now()
    label_Convolve_example = label_Convolve(matrix)
    print(f'Label Convolve with size of {matrixsize} : {dt.now() - starttime}')
    print(label_Convolve_example[0][0:4], '\n')

tests = 1 #each test will boost matrix size by 10
matrixsize = 10
for i in range(tests):
    example = np.random.randint(0, 10, size=(matrixsize, matrixsize, matrixsize))
    speedtest(example,matrixsize)
    matrixsize += 10

【讨论】:

    猜你喜欢
    • 2022-08-05
    • 2019-07-13
    • 1970-01-01
    • 2020-12-19
    • 1970-01-01
    • 2015-01-21
    • 1970-01-01
    • 1970-01-01
    • 2022-12-14
    相关资源
    最近更新 更多