【问题标题】:How do I swap elements of rows of a Nx2 numpy array if a condition is satisfied?如果满足条件,如何交换 Nx2 numpy 数组的行的元素?
【发布时间】:2020-01-21 16:20:39
【问题描述】:

如果条件 swap[i]True,我想将第 i 行的元素交换为 Nx2 numpy 数组 my_array

我的尝试:

def swap_positions_conditionally(my_array, swap):
    for i in range(np.shape(my_array)[0]):
        if swap[i]:
            my_array[i] = my_array[i][::-1]
    return my_array

工作正常,例如给定

my_array = array([[0, 1],
                  [2, 3],
                  [4, 5],
                  [6, 7],
                  [8, 9]])

swap = array([0, 0, 1, 1, 0])

产生预期的结果

[[0 1]
 [2 3]
 [5 4]
 [7 6]
 [8 9]]

但是,可能有一个更惯用的表达方式来重写我的swap_position_conditionally
有什么更好(更高效)的方式来编写它?

【问题讨论】:

    标签: python numpy swap idioms


    【解决方案1】:

    这是一个直接使用 Numpy 的布尔索引的方法:

    import numpy as np
    
    my_array = np.asarray([[0, 1],
                      [2, 3],
                      [4, 5],
                      [6, 7],
                      [8, 9]])
    
    swap = np.array([0, 0, 1, 1, 0], dtype=bool)
    
    my_array[swap, :] = my_array[swap,:][:,(1,0)]
    

    分解关键线:

    • my_array[swap, :] = 表示“分配给 swap 为真的行”
    • my_array[swap,:] 表示“选择swap 为真的整行”
    • [:,(1,0)] 表示“对于左边的每一行,交换第 0 列和第 1 列”

    关于问题的“更高效”部分...

    所有测试的通用设置(种子确保序列相同):

    import timeit
    setup= '''
    import numpy as np
    
    np.random.seed(42)
    my_array = np.random.random([10000,2])
    swap = np.random.random([10000]) > 0.5
    '''
    

    所有测试运行 1000 次迭代

    原码:5.621秒

    timeit.timeit('swap_positions_conditionally(my_array, swap)', setup=setup, number=1000)
    

    swap_positions_conditionally的定义添加到setup,如问题所示。

    这个答案:0.2657 秒

    timeit.timeit('my_array[swap, :] = my_array[swap,:][:,(1,0)]', setup=setup, number=1000)
    

    Divakar 的回答:0.176 秒

    timeit.timeit('np.where(swap[:,None]!=1,my_array,my_array[:,::-1])', setup=setup, number=1000)
    

    亚图第一个答案:0.214秒

    timeit.timeit('np.take_along_axis(my_array, np.c_[swap, 1-swap], axis=1)', setup=setup, number=1000)
    

    亚图的第二个答案:0.2547秒

    timeit.timeit('my_array[swap,:] = my_array[swap,::-1]', setup=setup, number=1000)
    

    结论

    分析显示 Divakar 的版本是最快的。哪个更直观或更易读是口味的问题,您可以选择您喜欢的那个(我个人是索引符号可读性的粉丝......)

    【讨论】:

    • @yatu 编辑在 ;) 我最初看到它时没有放它,因为它与我的非常相似,但后来我认为::-1 可能会以一种有趣的方式影响计时。事实证明,时间差异似乎并不显着(重新运行的差异比您的第二种方法和我的建议之间的差异更大)
    【解决方案2】:

    这是一种交换 Nx2 数组并在您尝试时使用负步长切片翻转更多列的方法 -

    In [56]: np.where(swap[:,None]==1, my_array[:,::-1], my_array)
    Out[56]: 
    array([[0, 1],
           [2, 3],
           [5, 4],
           [7, 6],
           [8, 9]])
    

    语法是:np.where(conditional_statement, choose_for_True, choose_for_False)。所以,在我们的例子中,当swap1 时,我们想要翻转/交换,否则不要。需要[:,None] 部分在每一行中按元素执行此操作。如果swap 已经是一个布尔数组,则跳过比较部分。

    【讨论】:

      【解决方案3】:

      这是一个使用np.take_along_axis的:

      np.take_along_axis(my_array, np.c_[swap, 1-swap], axis=1)
      
      array([[0, 1],
             [2, 3],
             [5, 4],
             [7, 6],
             [8, 9]])
      

      或基于布尔索引的索引:

      swap = swap.astype(bool)
      my_array[swap,:] = my_array[swap,::-1]
      

      【讨论】:

        猜你喜欢
        • 2013-11-15
        • 2018-06-22
        • 2018-07-13
        • 2012-01-14
        • 2021-12-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多