【问题标题】:Convert array of indices to 1-hot encoded numpy array将索引数组转换为 1-hot 编码的 numpy 数组
【发布时间】:2015-07-02 02:43:22
【问题描述】:

假设我有一个 1d numpy 数组

a = array([1,0,3])

我想将此编码为 2D one-hot 数组

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

有没有快速的方法来做到这一点?比循环 a 来设置 b 的元素更快,也就是说。

【问题讨论】:

    标签: python numpy machine-learning numpy-ndarray one-hot-encoding


    【解决方案1】:

    您的数组a 定义了输出数组中非零元素的列。您还需要定义行,然后使用精美的索引:

    >>> a = np.array([1, 0, 3])
    >>> b = np.zeros((a.size, a.max()+1))
    >>> b[np.arange(a.size),a] = 1
    >>> b
    array([[ 0.,  1.,  0.,  0.],
           [ 1.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  1.]])
    

    【讨论】:

    • @JamesAtwood 这取决于应用程序,但我会将最大值设为参数,而不是从数据中计算出来。
    • 如果 'a' 是 2d 怎么办?你想要一个 3-d one-hot 矩阵吗?
    • 谁能解释一下为什么会这样,但是带有 [:, a] 的切片却不行?
    • @A.D. 2d -> 3d 案例的解决方案:stackoverflow.com/questions/36960320/…
    • 你也可以使用 scipy.sparse。
    【解决方案2】:
    >>> values = [1, 0, 3]
    >>> n_values = np.max(values) + 1
    >>> np.eye(n_values)[values]
    array([[ 0.,  1.,  0.,  0.],
           [ 1.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  1.]])
    

    【讨论】:

    • 这个解决方案是唯一一个对输入 N-D 矩阵到 one-hot N+1D 矩阵有用的解决方案。示例: input_matrix=np.asarray([[0,1,1] , [1,1,2]]) ; np.eye(3)[input_matrix] # 输出 3D 张量
    • +1 因为这应该比公认的解决方案更受欢迎。不过,对于更通用的解决方案,values 应该是一个 Numpy 数组而不是 Python 列表,然后它适用于所有维度,而不仅仅是一维。
    • 请注意,如果您的数据集是随机抽样的,并且碰巧它可能不包含最大值,则可能不希望将 np.max(values) + 1 作为桶数。桶数应该是一个参数,并且可以进行断言/检查以检查每个值是否在 0(包括)和桶数(不包括)内。
    • 对我来说,这个解决方案是最好的,可以很容易地推广到任何张量:def one_hot(x, depth=10): return np.eye(depth)[x]。请注意,将张量 x 作为索引会返回 x.shape 眼睛行的张量。
    • “理解”这个解决方案以及为什么它适用于 N-dims 的简单方法(无需阅读 numpy 文档):在原始矩阵 (values) 中的每个位置,我们都有一个整数k,我们将 1-hot 向量 eye(n)[k]“放入”该位置。这增加了一个维度,因为我们将一个向量“放入”了原始矩阵中一个标量的位置。
    【解决方案3】:

    这是一个将一维向量转换为二维单热数组的函数。

    #!/usr/bin/env python
    import numpy as np
    
    def convertToOneHot(vector, num_classes=None):
        """
        Converts an input 1-D vector of integers into an output
        2-D array of one-hot vectors, where an i'th input value
        of j will set a '1' in the i'th row, j'th column of the
        output array.
    
        Example:
            v = np.array((1, 0, 4))
            one_hot_v = convertToOneHot(v)
            print one_hot_v
    
            [[0 1 0 0 0]
             [1 0 0 0 0]
             [0 0 0 0 1]]
        """
    
        assert isinstance(vector, np.ndarray)
        assert len(vector) > 0
    
        if num_classes is None:
            num_classes = np.max(vector)+1
        else:
            assert num_classes > 0
            assert num_classes >= np.max(vector)
    
        result = np.zeros(shape=(len(vector), num_classes))
        result[np.arange(len(vector)), vector] = 1
        return result.astype(int)
    

    以下是一些用法示例:

    >>> a = np.array([1, 0, 3])
    
    >>> convertToOneHot(a)
    array([[0, 1, 0, 0],
           [1, 0, 0, 0],
           [0, 0, 0, 1]])
    
    >>> convertToOneHot(a, num_classes=10)
    array([[0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
           [1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
           [0, 0, 0, 1, 0, 0, 0, 0, 0, 0]])
    

    【讨论】:

    • 请注意,这只适用于矢量(并且没有assert 可以检查矢量形状;)。
    • +1 用于通用方法和参数检查。但是,作为一种常见做法,我建议不要使用断言对输入进行检查。仅使用断言来验证内部中间条件。而是将所有assert ___ 转换为if not ___ raise Exception(<Reason>)
    【解决方案4】:

    我认为简短的回答是否定的。对于n 维度的更通用案例,我想出了这个:

    # For 2-dimensional data, 4 values
    a = np.array([[0, 1, 2], [3, 2, 1]])
    z = np.zeros(list(a.shape) + [4])
    z[list(np.indices(z.shape[:-1])) + [a]] = 1
    

    我想知道是否有更好的解决方案——我不喜欢我必须在最后两行创建这些列表。无论如何,我对timeit 进行了一些测量,似乎基于numpyindices/arange)和迭代版本的性能大致相同。

    【讨论】:

      【解决方案5】:

      你可以使用sklearn.preprocessing.LabelBinarizer:

      例子:

      import sklearn.preprocessing
      a = [1,0,3]
      label_binarizer = sklearn.preprocessing.LabelBinarizer()
      label_binarizer.fit(range(max(a)+1))
      b = label_binarizer.transform(a)
      print('{0}'.format(b))
      

      输出:

      [[0 1 0 0]
       [1 0 0 0]
       [0 0 0 1]]
      

      除其他外,您还可以初始化sklearn.preprocessing.LabelBinarizer(),以便transform 的输出是稀疏的。

      【讨论】:

        【解决方案6】:

        如果您使用的是 keras,则有一个内置实用程序:

        from keras.utils.np_utils import to_categorical   
        
        categorical_labels = to_categorical(int_labels, num_classes=3)
        

        它的作用与@YXD's answer 几乎相同(参见source-code)。

        【讨论】:

          【解决方案7】:

          这是我根据上面的答案和我自己的用例编写的一个示例函数:

          def label_vector_to_one_hot_vector(vector, one_hot_size=10):
              """
              Use to convert a column vector to a 'one-hot' matrix
          
              Example:
                  vector: [[2], [0], [1]]
                  one_hot_size: 3
                  returns:
                      [[ 0.,  0.,  1.],
                       [ 1.,  0.,  0.],
                       [ 0.,  1.,  0.]]
          
              Parameters:
                  vector (np.array): of size (n, 1) to be converted
                  one_hot_size (int) optional: size of 'one-hot' row vector
          
              Returns:
                  np.array size (vector.size, one_hot_size): converted to a 'one-hot' matrix
              """
              squeezed_vector = np.squeeze(vector, axis=-1)
          
              one_hot = np.zeros((squeezed_vector.size, one_hot_size))
          
              one_hot[np.arange(squeezed_vector.size), squeezed_vector] = 1
          
              return one_hot
          
          label_vector_to_one_hot_vector(vector=[[2], [0], [1]], one_hot_size=3)
          

          【讨论】:

            【解决方案8】:

            只是为了详细说明K3---rnc中的excellent answer,这里是一个更通用的版本:

            def onehottify(x, n=None, dtype=float):
                """1-hot encode x with the max value n (computed from data if n is None)."""
                x = np.asarray(x)
                n = np.max(x) + 1 if n is None else n
                return np.eye(n, dtype=dtype)[x]
            

            另外,这里是这个方法的快速和肮脏的基准测试和来自YXDcurrently accepted answer 的方法(略有变化,因此它们提供相同的API,除了后者仅适用于一维ndarrays) :

            def onehottify_only_1d(x, n=None, dtype=float):
                x = np.asarray(x)
                n = np.max(x) + 1 if n is None else n
                b = np.zeros((len(x), n), dtype=dtype)
                b[np.arange(len(x)), x] = 1
                return b
            

            后一种方法要快约 35%(MacBook Pro 13 2015),但前者更通用:

            >>> import numpy as np
            >>> np.random.seed(42)
            >>> a = np.random.randint(0, 9, size=(10_000,))
            >>> a
            array([6, 3, 7, ..., 5, 8, 6])
            >>> %timeit onehottify(a, 10)
            188 µs ± 5.03 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
            >>> %timeit onehottify_only_1d(a, 10)
            139 µs ± 2.78 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
            

            【讨论】:

              【解决方案9】:

              我最近遇到了一个相同类型的问题,并找到了上述解决方案,结果证明只有当你的数字符合特定格式时才会令人满意。例如,如果您想对以下列表进行 one-hot 编码:

              all_good_list = [0,1,2,3,4]
              

              继续,上面已经提到了发布的解决方案。但是如果考虑这些数据会怎样:

              problematic_list = [0,23,12,89,10]
              

              如果您使用上述方法,您最终可能会得到 90 个 one-hot 列。这是因为所有答案都包含n = np.max(a)+1 之类的内容。我找到了一个对我有用的更通用的解决方案,并希望与您分享:

              import numpy as np
              import sklearn
              sklb = sklearn.preprocessing.LabelBinarizer()
              a = np.asarray([1,2,44,3,2])
              n = np.unique(a)
              sklb.fit(n)
              b = sklb.transform(a)
              

              我希望有人在上述解决方案中遇到相同的限制,这可能会派上用场

              【讨论】:

                【解决方案10】:

                以下是我认为有用的:

                def one_hot(a, num_classes):
                  return np.squeeze(np.eye(num_classes)[a.reshape(-1)])
                

                这里的num_classes 代表您拥有的课程数量。因此,如果您有形状为 (10000,)a 向量,此函数会将其转换为 (10000,C)。请注意,a 是零索引的,即one_hot(np.array([0, 1]), 2) 将给出[[1, 0], [0, 1]]

                正是你想要的,我相信。

                PS:来源是Sequence models - deeplearning.ai

                【讨论】:

                • 另外,执行 np.squeeze() 的原因是什么,因为使用 np.eye(num_classes)[a.reshape(-1)]. What you are simply doing is using np.eye 获取(向量 a 的大小)多个热编码数组,您正在为每个类创建一个对角矩阵索引为 1 其余零,然后使用a.reshape(-1) 提供的索引生成与np.eye() 中的索引相对应的输出。我不明白np.sqeeze 的需要,因为我们使用它来简单地删除我们永远不会拥有的单个维度,因为输出的维度将始终是(a_flattened_size, num_classes)
                【解决方案11】:

                你也可以使用numpy的eye函数:

                numpy.eye(number of classes)[vector containing the labels]

                【讨论】:

                • 为了更清晰,使用np.identity(num_classes)[indices] 可能会更好。不错的答案!
                • 这是唯一简洁的绝对pythonic答案。
                • 这重复了两年后K3---rnc的答案,似乎没人看。
                【解决方案12】:

                我正在添加一个简单的函数来完成,只使用 numpy 运算符:

                   def probs_to_onehot(output_probabilities):
                        argmax_indices_array = np.argmax(output_probabilities, axis=1)
                        onehot_output_array = np.eye(np.unique(argmax_indices_array).shape[0])[argmax_indices_array.reshape(-1)]
                        return onehot_output_array
                

                它以一个概率矩阵作为输入:例如:

                [[0.03038822 0.65810204 0.16549407 0.3797123] ... [0.02771272 0.2760752 0.3280924 0.33458805]]

                它会返回

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

                【讨论】:

                  【解决方案13】:

                  这是一个与维度无关的独立解决方案。

                  这会将任何 N 维数组 arr 的非负整数转换为单热 N+1 维数组 one_hot,其中 one_hot[i_1,...,i_N,c] = 1 表示 arr[i_1,...,i_N] = c。您可以通过np.argmax(one_hot, -1)恢复输入

                  def expand_integer_grid(arr, n_classes):
                      """
                  
                      :param arr: N dim array of size i_1, ..., i_N
                      :param n_classes: C
                      :returns: one-hot N+1 dim array of size i_1, ..., i_N, C
                      :rtype: ndarray
                  
                      """
                      one_hot = np.zeros(arr.shape + (n_classes,))
                      axes_ranges = [range(arr.shape[i]) for i in range(arr.ndim)]
                      flat_grids = [_.ravel() for _ in np.meshgrid(*axes_ranges, indexing='ij')]
                      one_hot[flat_grids + [arr.ravel()]] = 1
                      assert((one_hot.sum(-1) == 1).all())
                      assert(np.allclose(np.argmax(one_hot, -1), arr))
                      return one_hot
                  

                  【讨论】:

                    【解决方案14】:

                    这种类型的编码通常是 numpy 数组的一部分。如果您使用这样的 numpy 数组:

                    a = np.array([1,0,3])
                    

                    那么有一种非常简单的方法可以将其转换为 1-hot 编码

                    out = (np.arange(4) == a[:,None]).astype(np.float32)
                    

                    就是这样。

                    【讨论】:

                      【解决方案15】:
                      • p 将是一个 2d ndarray。
                      • 我们想知道哪一个值是连续最高的,把 1 放在那里,其他地方放 0。

                      干净简单的解决方案:

                      max_elements_i = np.expand_dims(np.argmax(p, axis=1), axis=1)
                      one_hot = np.zeros(p.shape)
                      np.put_along_axis(one_hot, max_elements_i, 1, axis=1)
                      

                      【讨论】:

                        【解决方案16】:

                        使用以下代码。效果最好。

                        def one_hot_encode(x):
                        """
                            argument
                                - x: a list of labels
                            return
                                - one hot encoding matrix (number of labels, number of class)
                        """
                        encoded = np.zeros((len(x), 10))
                        
                        for idx, val in enumerate(x):
                            encoded[idx][val] = 1
                        
                        return encoded
                        

                        Found it here P.S 你不需要进入链接。

                        【讨论】:

                        • 你应该避免在 numpy 中使用循环
                        • 它没有回答这个问题:“有没有一种快速的方法来做到这一点?也就是说,比循环 a 来设置 b 的元素要快。”
                        • @AlexandreHuat 可以使用numpy函数np.eye()
                        • 那么你应该回答你说可以使用`numpy.eye()(但它已经由另一个用户完成)。请务必仔细阅读问题和已发布的答案,以维护 stackoverflow 和社区的质量。
                        【解决方案17】:

                        您可以使用以下代码转换为 one-hot 向量:

                        让 x 是具有单列的普通类向量,类别为 0 到某个数字:

                        import numpy as np
                        np.eye(x.max()+1)[x]
                        

                        如果 0 不是一个类;然后删除 +1。

                        【讨论】:

                        • 这重复了三年后K3---rnc的答案。
                        【解决方案18】:

                        使用Neuraxle 管道步骤:

                        1. 设置您的示例
                        import numpy as np
                        a = np.array([1,0,3])
                        b = np.array([[0,1,0,0], [1,0,0,0], [0,0,0,1]])
                        
                        1. 进行实际转换
                        from neuraxle.steps.numpy import OneHotEncoder
                        encoder = OneHotEncoder(nb_columns=4)
                        b_pred = encoder.transform(a)
                        
                        1. 断言它有效
                        assert b_pred == b
                        

                        文档链接:neuraxle.steps.numpy.OneHotEncoder

                        【讨论】:

                          【解决方案19】:

                          1-热编码

                             one_hot_encode=pandas.get_dummies(array)
                          

                          For Example

                          享受编码

                          【讨论】:

                          • 感谢您的评论,但简要说明代码正在做什么会很有帮助!
                          • 请参考示例
                          • @Clarus 查看以下示例。您可以通过执行 one_hot_encode[value] 来访问 np 数组中每个值的一种热编码。 >>> import numpy as np >>> import pandas >>> a = np.array([1,0,3]) >>> one_hot_encode=pandas.get_dummies(a) >>> print(one_hot_encode) 0 1 3 0 0 1 0 1 1 0 0 2 0 0 1 >>> print(one_hot_encode[1]) 0 1 1 0 2 0 Name: 1, dtype: uint8 >>> print(one_hot_encode[0]) 0 0 1 1 2 0 Name: 0, dtype: uint8 >>> print(one_hot_encode[3]) 0 0 1 0 2 1 Name: 3, dtype: uint8
                          • 不是理想的工具
                          【解决方案20】:

                          如果使用tensorflow,则有one_hot()

                          import tensorflow as tf
                          import numpy as np
                          
                          a = np.array([1, 0, 3])
                          depth = 4
                          b = tf.one_hot(a, depth)
                          # <tf.Tensor: shape=(3, 3), dtype=float32, numpy=
                          # array([[0., 1., 0.],
                          #        [1., 0., 0.],
                          #        [0., 0., 0.]], dtype=float32)>
                          

                          【讨论】:

                            【解决方案21】:
                            def one_hot(n, class_num, col_wise=True):
                              a = np.eye(class_num)[n.reshape(-1)]
                              return a.T if col_wise else a
                            
                            # Column for different hot
                            print(one_hot(np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 8, 7]), 10))
                            # Row for different hot
                            print(one_hot(np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 8, 7]), 10, col_wise=False))
                            

                            【讨论】:

                              【解决方案22】:

                              我发现最简单的解决方案结合了np.takenp.eye

                              def one_hot(x, depth: int):
                                return np.take(np.eye(depth), x, axis=0)
                              

                              适用于任何形状的x

                              【讨论】:

                                猜你喜欢
                                • 2022-01-02
                                • 1970-01-01
                                • 2014-10-28
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 2020-02-12
                                • 1970-01-01
                                • 2020-09-07
                                相关资源
                                最近更新 更多