【问题标题】:list to matrix conversion with numpy使用 numpy 列表到矩阵转换
【发布时间】:2018-08-29 23:03:14
【问题描述】:

从浮点数列表开始,即

register = [11, 12, 13, 23, 24, 34]

我想生成对角线元素为零的对称矩阵,即

[[  0.  11.  12.  13.]
 [ 11.   0.  23.  24.]
 [ 12.  23.   0.  34.]
 [ 13.  24.  34.   0.]]

所以我选择创建一个尺寸为 4 x 4 的零,然后用列表中的元素填充。在设置进度指示器并考虑偏移量以不覆盖对角线的零点之后,我将向东(或向南)移动,直到消耗了先前确定的达到矩阵限制的步数。在增加进度并重置初始计步器后,我可以输入下一列(行)以继续进行。然而,我在我现在的代码中犯了错误(至少一次——这是我第一次接触 numpy)并且只收获了

[[  0.  11.  12.  13.]
 [ 11.   0.  23.   0.]
 [ 12.  23.   0.   0.]
 [ 13.   0.   0.   0.]]

我的代码:

 import numpy as np
 dimension = 4    # other matrices' dimensions will be larger
 matrix = np.zeros((dimension,dimension))

 register = [11, 12, 13, 23, 24, 34]

 progress = 0
 inner_step = 0
 i = 0

 for progress in range(0, (dimension + 1)):
 permitted_steps = dimension - progress
 for i in range(progress, permitted_steps-1):
     matrix[(progress, inner_step+1+offset)] = register[0]
     matrix[(inner_step+1+offset, progress)] = register[0]
     inner_step += 1
     del register[0]

 progress += 1
 inner_step = 0
 offset += 1

使用的目标环境是适用于 Windows 的 Python 2.7 (Continuum Anaconda)。

【问题讨论】:

    标签: python numpy type-conversion


    【解决方案1】:

    只需拨打scipy.spatial.distance.squareform

    >>> import scipy.spatial.distance
    >>> scipy.spatial.distance.squareform([11, 12, 13, 23, 24, 34])
    array([[ 0, 11, 12, 13],
           [11,  0, 23, 24],
           [12, 23,  0, 34],
           [13, 24, 34,  0]])
    

    您想要的转换与从压缩距离矩阵到方形距离矩阵的转换相同,scipy.spatial.distance.squareform 执行该转换(及其逆转换)。不过要小心 dtypes; [11, 12, 13, 23, 24, 34] 是一个整数列表,而不是浮点数,将它传递给 squareform 将为您提供一个整数数组。如果需要浮点数,您可以将输入转换为浮点数或调用result.astype(float)

    【讨论】:

    • 在问题中选择整数而不是真正的浮点数旨在有用地简化所面临的问题。但我同意你的看法,我会更清楚地辨别它们,尤其是在 Python 和未来使用 numpy 以及最终使用 scipy 方面。
    【解决方案2】:

    这是一种利用 broadcastingmasking/boolean-indexing 的矢量化方法 -

    r = np.arange(dimension)
    mask = r[:,None] < r # Or in one step : ~np.tri(dimension,dtype=bool)
    matrix[mask] = register
    matrix.T[mask] = register
    

    如果您需要从给定的register 计算dimension,我们可以使用:

    dimension = int(np.ceil(np.sqrt(2*len(register))))
    

    并且断言长度,给定dimension,我们可以:

    assert dimension*(dimension-1)//2 == len(register)
    

    此外,为了提高性能,请考虑输入 register 的数组版本。

    示例运行 -

    In [43]: import numpy as np
        ...: dimension = 4    # other matrices' dimensions will be larger
        ...: matrix = np.zeros((dimension,dimension))
        ...: 
        ...: register = [11, 12, 13, 23, 24, 34]
    
    In [44]: r = np.arange(dimension)
        ...: mask = r[:,None] < r
        ...: matrix[mask] = register
        ...: matrix.T[mask] = register
    
    In [45]: matrix
    Out[45]: 
    array([[ 0., 11., 12., 13.],
           [11.,  0., 23., 24.],
           [12., 23.,  0., 34.],
           [13., 24., 34.,  0.]])
    

    masking 如何比生成所有三角形索引更好

    与创建布尔数组相比,生成索引会占用更多内存,布尔数组本质上是内存高效的,因此可以转化为更好的性能,尤其是在大型数组上。这方面的时间安排将试图证明这一点 -

    In [3]: import numpy as np
       ...: dimension = 5000    # other matrices' dimensions will be larger
       ...: register = np.random.randint(0,10,dimension*(dimension-1)//2)
    
    # With masking and boolean-indexing
    In [4]: %%timeit
       ...: matrix = np.zeros((dimension,dimension),dtype=int)
       ...: r = np.arange(dimension)
       ...: mask = r[:,None] < r
       ...: matrix[mask] = register
       ...: matrix.T[mask] = register
    10 loops, best of 3: 108 ms per loop
    
    # With triangular indices indexing
    In [5]: %%timeit
       ...: N = dimension
       ...: matrix = np.zeros((dimension,dimension),dtype=int)
       ...: idx = np.triu_indices(N, k=1)
       ...: matrix = np.zeros((N, N))
       ...: matrix[idx] = register
       ...: matrix.T[idx] = register
    1 loop, best of 3: 364 ms per loop
    

    【讨论】:

    • np.triu_indices()相比有什么优势吗?
    • @norok2 好问题。在末尾添加了包括时间在内的部分。
    • 对于刚开始使用 numpy,这远远超出了预期——而且比预期的更深入。尽管要构建的矩阵较小(最多约 80..100 列/行),但将这项工作委托给 Python 显然比手动矩形标记和跨 Emacs 缓冲区的拖拽以及在 gnumeric 中的转置粘贴更节省了大量时间。 ..
    【解决方案3】:

    这是triu_indices 的另一种矢量化方法:

    N = 4
    idx = np.triu_indices(N, k=1)
    matrix = np.zeros((N, N))
    matrix[idx] = register
    matrix.T[idx] = register
    

    为对角偏移为1的上三角矩阵生成一个索引,然后进行相应的赋值。

    确保寄存器有足够的元素来执行此操作。您可以使用

    强制执行前提条件检查
    assert len(register) == np.count_nonzero(1 - np.tri(N))
    

    【讨论】:

    【解决方案4】:

    您非常接近,当progress 等于或大于permitted_steps-1 时会出现您的问题,因为那里没有可以迭代的范围。试试这个:

        import numpy as np
    
        dimension = 4
        matrix = np.zeros((dimension,dimension))
    
        register = [11, 12, 13, 23, 24, 34]
        progress = 0
        inner_step = 0
        i = 0
        offset = 0
        for progress in range(0, (dimension + 1)):
            permitted_steps = dimension - progress
            for i in range(0, permitted_steps-1):
                matrix[(progress, inner_step+1+offset)] = register[0]
                matrix[(inner_step+1+offset, progress)] = register[0]
                inner_step += 1
                del register[0]
            progress += 1
            inner_step = 0
            offset +=1
        print matrix
    

    然后打印出来:

    [[  0.  11.  12.  13.]
     [ 11.   0.  23.  24.]
     [ 12.  23.   0.  34.]
     [ 13.  24.  34.   0.]]
    

    如果您将寄存器数组更改为长度 10:

    register = [11, 12, 13, 23, 24, 34, 37, 39, 40, 43]
    

    你会得到:

    [[  0.  11.  12.  13.  23.]
     [ 11.   0.  24.  34.  37.]
     [ 12.  24.   0.  39.  40.]
     [ 13.  34.  39.   0.  43.]
     [ 23.  37.  40.  43.   0.]]
    

    【讨论】:

    • 考虑到自己几乎是 Python 的初学者,我真的很喜欢这个答案,因为它表明除了考虑成功执行任务之外几乎没有什么。即使其他答案更简洁,因为它们使用 numpy(和 scipy)中的函数,我还不知道。
    猜你喜欢
    • 2017-05-17
    • 1970-01-01
    • 1970-01-01
    • 2020-03-03
    • 2016-11-05
    • 2018-09-18
    • 2015-04-30
    • 2019-12-12
    • 2021-12-14
    相关资源
    最近更新 更多