【问题标题】:Can numpy bincount work with 2D arrays?numpy bincount 可以与二维数组一起使用吗?
【发布时间】:2013-10-05 19:47:19
【问题描述】:

我看到我无法理解的 numpy bincount 行为。我想以行方式将 2D 数组中的值合并,并查看下面的行为。为什么它可以与 dbArray 一起工作,但与 simarray 失败?

>>> dbArray
array([[1, 0, 1, 0, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 0, 1, 1],
       [1, 0, 0, 0, 0],
       [0, 0, 0, 1, 1],
       [0, 1, 0, 1, 0]])
>>> N.apply_along_axis(N.bincount,1,dbArray)
array([[2, 3],
       [0, 5],
       [1, 4],
       [4, 1],
       [3, 2],
       [3, 2]], dtype=int64)
>>> simarray
array([[2, 0, 2, 0, 2],
       [2, 1, 2, 1, 2],
       [2, 1, 1, 1, 2],
       [2, 0, 1, 0, 1],
       [1, 0, 1, 1, 2],
       [1, 1, 1, 1, 1]])
>>> N.apply_along_axis(N.bincount,1,simarray)

Traceback (most recent call last):
  File "<pyshell#31>", line 1, in <module>
    N.apply_along_axis(N.bincount,1,simarray)
  File "C:\Python27\lib\site-packages\numpy\lib\shape_base.py", line 118, in apply_along_axis
    outarr[tuple(i.tolist())] = res
ValueError: could not broadcast input array from shape (2) into shape (3)

【问题讨论】:

    标签: python arrays numpy


    【解决方案1】:

    问题在于bincount 并不总是返回相同形状的对象,尤其是在缺少值时。例如:

    >>> m = np.array([[0,0,1],[1,1,0],[1,1,1]])
    >>> np.apply_along_axis(np.bincount, 1, m)
    array([[2, 1],
           [1, 2],
           [0, 3]])
    >>> [np.bincount(m[i]) for i in range(m.shape[1])]
    [array([2, 1]), array([1, 2]), array([0, 3])]
    

    有效,但是:

    >>> m = np.array([[0,0,0],[1,1,0],[1,1,0]])
    >>> m
    array([[0, 0, 0],
           [1, 1, 0],
           [1, 1, 0]])
    >>> [np.bincount(m[i]) for i in range(m.shape[1])]
    [array([3]), array([1, 2]), array([1, 2])]
    >>> np.apply_along_axis(np.bincount, 1, m)
    Traceback (most recent call last):
      File "<ipython-input-49-72e06e26a718>", line 1, in <module>
        np.apply_along_axis(np.bincount, 1, m)
      File "/usr/local/lib/python2.7/dist-packages/numpy/lib/shape_base.py", line 117, in apply_along_axis
        outarr[tuple(i.tolist())] = res
    ValueError: could not broadcast input array from shape (2) into shape (1)
    

    不会。

    您可以使用minlength 参数并使用lambdapartial 或其他方式传递它:

    >>> np.apply_along_axis(lambda x: np.bincount(x, minlength=2), axis=1, arr=m)
    array([[3, 0],
           [1, 2],
           [1, 2]])
    

    【讨论】:

    【解决方案2】:

    正如@DSM 已经提到的,在不知道数组的最大值的情况下无法完成二维数组的 bincount,因为这意味着数组大小不一致。

    但是由于 numpy 索引的强大功能,可以很容易地更快地实现 2d bincount,因为它不使用连接或任何东西。

    def bincount2d(arr, bins=None):
        if bins is None:
            bins = np.max(arr) + 1
        count = np.zeros(shape=[len(arr), bins], dtype=np.int64)
        indexing = np.arange(len(arr))
        for col in arr.T:
            count[indexing, col] += 1
        return count
    
    
    t = np.array([[1,2,3],[4,5,6],[3,2,2]], dtype=np.int64)
    print(bincount2d(t))
    

    附言

    这个:

    t = np.empty(shape=[10000, 100], dtype=np.int64)
    s = time.time()
    bincount2d(t)
    e = time.time()
    print(e - s)
    

    给出的结果比这快约 2 倍:

    t = np.empty(shape=[100, 10000], dtype=np.int64)
    s = time.time()
    bincount2d(t)
    e = time.time()
    print(e - s)
    

    因为 for 循环遍历列。所以,如果shape[0] &lt; shape[1],最好转置你的二维数组。

    UPD

    比这做不到更好(我的意思是单独使用 python):

    def bincount2d(arr, bins=None):
        if bins is None:
            bins = np.max(arr) + 1
        count = np.zeros(shape=[len(arr), bins], dtype=np.int64)
        indexing = (np.ones_like(arr).T * np.arange(len(arr))).T
        np.add.at(count, (indexing, arr), 1)
    
        return count
    

    【讨论】:

      【解决方案3】:

      这是一个完全符合您要求的函数,但没有任何循环。

      def sub_sum_partition(a, partition):
          """
          Generalization of np.bincount(partition, a).
          Sums rows of a matrix for each value of array of non-negative ints.
      
          :param a: array_like
          :param partition: array_like, 1 dimension, nonnegative ints
          :return: matrix of shape ('one larger than the largest value in partition', a.shape[1:]). The i's element is
          the sum of rows j in 'a' s.t. partition[j] == i
          """
          assert partition.shape == (len(a),)
          n = np.prod(a.shape[1:], dtype=int)
          bins = ((np.tile(partition, (n, 1)) * n).T + np.arange(n, dtype=int)).reshape(-1)
          sums = np.bincount(bins, a.reshape(-1))
          if n > 1:
              sums = sums.reshape(-1, *a.shape[1:])
          return sums
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-07-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-27
        • 1970-01-01
        相关资源
        最近更新 更多