【问题标题】:Converting an array of integers to a "vector"将整数数组转换为“向量”
【发布时间】:2017-01-26 03:05:13
【问题描述】:

我有一个长度为 150 的整数数组,整数范围从 1 到 3。例如,

array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3])

我想转换/映射/变换

1 to [0,0,1]

2 to [0,1,0]

3 to [1,0,0]

有没有有效的方法来做到这一点?

所以输出就像

[0,0,1],[0,0,1],[0,0,1]...[1,0,0]

【问题讨论】:

  • 你试过用字典吗?
  • hmm.. 我想我试过了,但我可以将它映射到一个数组吗? [0,0,1] ... 等等。我试过但无济于事。也许我错误地实现了它
  • 我认为你可以

标签: python arrays numpy


【解决方案1】:

首先,将你的变换编码为一个数组(因为你没有映射 0,所以使用一个虚拟的第一个元素):

>>> mapping = np.array([[0,0,0],[0,0,1],[0,1,0],[1,0,0]])

那么就很简单了:

>>> arr = np.array([1,1,2,3,3,3])
>>> mapping[arr]
array([[0, 0, 1],
      [0, 0, 1],
      [0, 1, 0],
      [1, 0, 0],
      [1, 0, 0],
      [1, 0, 0]])

【讨论】:

    【解决方案2】:

    您实际上可以只比较它们并设置适当的项目:

    >>> # a bit shorter so it's easier to demonstrate
    >>> arr = np.array([1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3])
    >>> arr2 = np.zeros([arr.size, 3], arr.dtype)
    >>> arr2[:, 0] = arr == 3
    >>> arr2[:, 1] = arr == 2
    >>> arr2[:, 2] = arr == 1
    
    >>> arr2
    array([[0, 0, 1],
           [0, 0, 1],
           [0, 1, 0],
           [0, 1, 0],
           [0, 1, 0],
           [1, 0, 0],
           [1, 0, 0],
           [1, 0, 0],
           [1, 0, 0],
           [1, 0, 0],
           [1, 0, 0],
           [1, 0, 0],
           [1, 0, 0],
           [1, 0, 0],
           [1, 0, 0],
           [1, 0, 0],
           [1, 0, 0],
           [1, 0, 0],
           [1, 0, 0]])
    

    你说你对效率感兴趣,所以我做了一些计时:

    my_dict = {
        1:[0,0,1],
        2:[0,1,0],
        3:[1,0,0]
        }
    
    mapping = np.array([[0,0,0],[0,0,1],[0,1,0],[1,0,0]])
    
    def mine(arr):
        arr2 = np.zeros([arr.size, 3], arr.dtype)
        arr2[:, 0] = arr == 3
        arr2[:, 1] = arr == 2
        arr2[:, 2] = arr == 1
        return arr2
    
    def JoaoAreias(arr):
        return [my_dict[i] for i in arr]
    
    def JohnZwinck(arr):
        return mapping[arr]
    
    def Divakar(arr):
        return (arr == np.arange(3,0,-1)[:,None]).T.astype(np.int8)
    
    def Divakar2(arr):
        return np.take(mapping, arr,axis=0)
    
    arr = np.random.randint(1, 4, (150))
    np.testing.assert_array_equal(mine(arr), JohnZwinck(arr))
    np.testing.assert_array_equal(mine(arr), mine_numba(arr))
    np.testing.assert_array_equal(mine(arr), Divakar(arr))
    np.testing.assert_array_equal(mine(arr), Divakar2(arr))
    %timeit mine(arr)        # 5. - 10000 loops, best of 3: 48.3 µs per loop
    %timeit JoaoAreias(arr)  # 6. - 10000 loops, best of 3: 179 µs per loop
    %timeit JohnZwinck(arr)  # 3. - 10000 loops, best of 3: 24.1 µs per loop
    %timeit mine_numba(arr)  # 1. - 100000 loops, best of 3: 6.02 µs per loop
    %timeit Divakar(arr)     # 4. - 10000 loops, best of 3: 34.2 µs per loop
    %timeit Divakar2(arr)    # 2. - 100000 loops, best of 3: 13.5 µs per loop
    
    arr = np.random.randint(1, 4, (10000))
    np.testing.assert_array_equal(mine(arr), JohnZwinck(arr))
    np.testing.assert_array_equal(mine(arr), mine_numba(arr))
    np.testing.assert_array_equal(mine(arr), Divakar(arr))
    np.testing.assert_array_equal(mine(arr), Divakar2(arr))
    %timeit mine(arr)        # 4. - 1000 loops, best of 3: 201 µs per loop
    %timeit JoaoAreias(arr)  # 6. - 100 loops, best of 3: 10.2 ms per loop
    %timeit JohnZwinck(arr)  # 5. - 1000 loops, best of 3: 455 µs per loop
    %timeit mine_numba(arr)  # 1. - 10000 loops, best of 3: 103 µs per loop
    %timeit Divakar(arr)     # 3. - 10000 loops, best of 3: 155 µs per loop
    %timeit Divakar2(arr)    # 2. - 10000 loops, best of 3: 146 µs per loop
    

    因此,这取决于您更喜欢的数据大小,如果它比@JohnZwinck 拥有最快的解决方案要小,那么对于“更大”的数据集,我的方法会获胜。 :)


    实际上,如果您要使用 (或 cython 或类似的)之类的方法,您可以击败所有其他方法:

    import numba as nb
    
    @nb.njit
    def mine_numba(arr):
        arr2 = np.zeros((arr.size, 3), arr.dtype)
        for idx in range(arr.size):
            item = arr[idx]
            if item == 1:
                arr2[idx, 2] = 1
            elif item == 2:
                arr2[idx, 1] = 1
            else:
                arr2[idx, 0] = 1
        return arr2
    

    【讨论】:

    • 不错的时机。通过将order='F' 添加到zeros() 调用中,您可以使您的版本更快一些。当您按列分配值时,这会提高效率。
    • 嗯!非常好。由于我正在处理的数据很小,John 的数据就足够了。但我会记住这一点!
    • @JohnZwinck 我实际上尝试过(或类似的方法:我创建了一个转置数组并返回了np.transpose),但对于小型数据集,这稍微慢了一点(同时速度提高了 10-20%在巨大的阵列上)。 numba 解决方案(可能是矫枉过正 :-))我所拥有的更有趣并且更快。
    • 我添加了两种方法,并针对其他方法进行计时。但无法测试 numba。您是否愿意将我的结果包含在您的结果中?
    • @Divakar 当然。你的第二种方法真的很快。我肯定需要更频繁地使用np.take。 :)
    【解决方案3】:

    这个怎么样?

    a = [1, 1, 1, 2, 2, 2, 3, 3, 3]
    b = []
    
    for i in a:
        if i == 1:
            b.append([0,0,1])
        elif i == 2:
            b.append([0,1,0])
        else:
            b.append([1,0,0])
    
    print(b)
    
    #[[0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 1, 0], [0, 1, 0], [0, 1, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0]]
    

    【讨论】:

      【解决方案4】:

      我会使用字典和列表理解来完成,就像这样

      '''
      This is a dictionary to map your values
      '''
      my_dict = {
          1:[0,0,1],
          2:[0,1,0],
          3:[1,0,0]
          }
      '''
      This is your original Array
      '''
      my_array = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
             1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
             1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
             2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
             2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
             3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
             3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
      '''
      Use list comprehention to map one to another
      '''
      my_new_array = [my_dict[i] for i in my_array]
      

      【讨论】:

      • 但与任何 numpy 解决方案相比,几乎可以肯定效率不高。然而,这可能是最快的纯 python 解决方案!
      • @JoãoAreias:Big-O 是不相关的——如果数据甚至中等大,NumPy 解决方案绝对会破坏 dict 解决方案。 OP 已经在使用 NumPy,所以导入已经完成。
      • @JoãoAreias:NumPy 使用优化的 C 和 Fortran 代码完成所有繁重的工作。在这种情况下,它比使用 dict 快 10 倍。更不用说 dict 解决方案每次都必须进行间接(哈希表)查找,而我发布的 NumPy 数组解决方案使用直接数组索引。
      • @JoãoAreias 我在answer 中做了一些基准测试。 NumPy 可以进行非常快速的 implicit 迭代。比python快得多。您的解决方案慢了 4 倍(到 50 倍)这一事实实际上非常惊人。
      • 酷,谢谢大家,我想我会比平时更多地开始使用 numpy。顺便说一句,你的回答很聪明。
      【解决方案5】:

      方法#1:使用NumPy broadcasting -

      (arr == np.arange(3,0,-1)[:,None]).T.astype(np.int8)
      

      方法#2: 类似于@John Zwinck 的索引概念,但np.take 沿第一个轴,这在这里很有帮助,因为索引被大量重复。这些时间在this previous post

      mapping = np.array([[0,0,0],[0,0,1],[0,1,0],[1,0,0]])
      out = np.take(mapping, arr,axis=0)
      

      使用@MSeifert 的基准设置进行运行时测试 -

      In [85]: arr = np.random.randint(1, 4, (10000))
      
      In [86]: %timeit MSeifert(arr)
          ...: %timeit JoaoAreias(arr)
          ...: %timeit JohnZwinck(arr)
          ...: 
      10000 loops, best of 3: 105 µs per loop
      100 loops, best of 3: 2.97 ms per loop
      1000 loops, best of 3: 240 µs per loop
      
      # Approach #1 
      In [87]: %timeit (arr == np.arange(3,0,-1)[:,None]).T.astype(np.int8)
      10000 loops, best of 3: 44.1 µs per loop
      
      # Approach #2
      In [88]: %timeit np.take(mapping, arr,axis=0)
      10000 loops, best of 3: 73 µs per loop
      

      【讨论】:

        【解决方案6】:

        如果您的范围从 1 到 3,则使用 列表理解 的解决方案:

        >>> [([0,0,1] if x==1 else [0,1,0] if x==2 else [1,0,0]) for x in c]
        
        [[0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0]]
        

        这更加pythonic和快速。

        【讨论】:

          猜你喜欢
          • 2012-05-10
          • 2018-06-26
          • 2017-08-21
          • 2020-12-08
          • 1970-01-01
          • 2016-07-10
          • 1970-01-01
          • 2013-09-27
          相关资源
          最近更新 更多