Python Numpy:用Numpy数组替换Numpy数组中的零

Python Numpy:用Numpy数组替换Numpy数组中的零,python,numpy,matrix,Python,Numpy,Matrix,处理我想要透视的数据。请注意,我仅限于numpy,无法使用熊猫。原始数据如下所示: data = [ [ 1, a, [<metric1>, <metric2>] ], [ 1, b, [<metric1>, <metric2>] ], [ 2, b, [<metric1>, <metric2>] ], [ 2, c, [<metric1>, <metric2>] ], [ 3,

处理我想要透视的数据。请注意,我仅限于numpy,无法使用熊猫。原始数据如下所示:

data = [
  [ 1, a, [<metric1>, <metric2>] ],
  [ 1, b, [<metric1>, <metric2>] ],
  [ 2, b, [<metric1>, <metric2>] ],
  [ 2, c, [<metric1>, <metric2>] ],
  [ 3, a, [<metric1>, <metric2>] ],
  [ 3, c, [<metric1>, <metric2>] ],
  ...etc
]
结果格式为:

cols = [a, b, c, ...]
rows = [1, 2, 3, ...]
pivot_table = [
  [ [<metric1>, <metric2>], [<metric1>, <metric2>], 0, ... ],
  [ 0, [<metric1>, <metric2>], [<metric1>, <metric2>], ... ],
  [ [<metric1>, <metric2>], 0, [<metric1>, <metric2>], ... ],
  ...
]
但我得到了以下错误:

TypeError: NumPy boolean array indexing assignment requires a 0 or 1-dimensional input, input has 2 dimensions
我的临时修复已经足够了,但是当我想做一些事情时,比如有一行列和时,我的修复是有限的。我有很多方法,但不知道如何执行:

  • 如上所述,用一系列零替换事实之后的零
  • 最初使用
    np.unique
    中的索引创建表时,使用默认值来填充表,而不是零
  • 将度量从列表中拉出到数组中,即,
    [1,a,]
    。这可能是简化聚合函数的最佳解决方案

  • 上述任何一种方法都有解决方案吗?

    尝试重新创建您的案例:

    In [182]: a,b,c = 0,1,2
    In [183]: metric1, metric2 = 100,200
    In [186]: data = [
         ...:   [ 1, a, [metric1, metric2] ],
         ...:   [ 1, b, [metric1, metric2] ],
         ...:   [ 2, b, [metric1, metric2] ],
         ...:   [ 2, c, [metric1, metric2] ],
         ...:   [ 3, a, [metric1, metric2] ],
         ...:   [ 3, c, [metric1, metric2] ],
         ...: ]
    In [187]: 
    In [187]: data
    Out[187]: 
    [[1, 0, [100, 200]],
     [1, 1, [100, 200]],
     [2, 1, [100, 200]],
     [2, 2, [100, 200]],
     [3, 0, [100, 200]],
     [3, 2, [100, 200]]]
    
    In [189]: data = np.array(data,object)
    In [190]: rows, row_pos = np.unique(data[:, 0], return_inverse=True)
         ...: cols, col_pos = np.unique(data[:, 1], return_inverse=True)
         ...: pivot_table = np.zeros((len(rows), len(cols)), dtype=object)
    
    In [191]: pivot_table
    Out[191]: 
    array([[0, 0, 0],
           [0, 0, 0],
           [0, 0, 0]], dtype=object)
    In [192]: pivot_table[row_pos, col_pos] = data[:, 2]
    In [193]: pivot_table
    Out[193]: 
    array([[list([100, 200]), list([100, 200]), 0],
           [0, list([100, 200]), list([100, 200])],
           [list([100, 200]), 0, list([100, 200])]], dtype=object)
    In [194]: pivot_table[row_pos, col_pos]
    Out[194]: 
    array([list([100, 200]), list([100, 200]), list([100, 200]),
           list([100, 200]), list([100, 200]), list([100, 200])], dtype=object)
    In [195]: _.shape
    Out[195]: (6,)
    In [196]: data[:,2].shape
    Out[196]: (6,)
    
    此分配在源形状(和数据类型)与目标形状(6,)匹配之间工作


    在过去,我成功地使用
    frompyfunc
    创建对象数据类型数组。定义一个小函数。我本可以测试0或类型,但因为我已经插入了0或类型,所以让我们测试一下:

    In [205]: def fun(x):
         ...:     if x is None: return [0,0]
         ...:     return x
    
    将其应用于透视表的每个元素,生成一个新数组

    In [230]: arr1 = np.frompyfunc(fun,1,1)(pivot_table)
    In [231]: arr1
    Out[231]: 
    array([[list([100, 200]), list([100, 200]), list([0, 0])],
           [list([0, 0]), list([100, 200]), list([100, 200])],
           [list([100, 200]), list([0, 0]), list([100, 200])]], dtype=object)
    

    另一种方法是,让我们尝试分配一个列表:

    In [240]: pivot_table[mask] = [[0,0] for _ in range(3)]    
    TypeError: NumPy boolean array indexing assignment requires a 0 or 1-dimensional input, input has 2 dimensions
    
    但是如果我在
    中尝试同样的方法,它会起作用:

    In [241]: pivot_table[np.where(mask)] = [[0,0] for _ in range(3)]
    In [242]: pivot_table
    Out[242]: 
    array([[list([100, 200]), list([100, 200]), list([0, 0])],
           [list([0, 0]), list([100, 200]), list([100, 200])],
           [list([100, 200]), list([0, 0]), list([100, 200])]], dtype=object)
    
    使用
    where
    更像是您对
    pivot\u表的原始分配

    In [243]: np.where(mask)
    Out[243]: (array([0, 1, 2]), array([2, 0, 1]))
    
    此数组索引仍可能存在广播问题

    In [244]: pivot_table[np.where(mask)] = [0,0]
    ValueError: cannot copy sequence with size 2 to array axis with dimension 3
    
    通常,布尔掩码索引的行为类似于等价的
    np。其中(mask)
    索引,但很明显,在这里,对象数据类型和广播的相互作用会扰乱布尔索引


    Out[231]
    仍然是一个(3,3)数组,即使列有len 2的所有元素。要将其转换为数字数组,我们必须执行以下操作:

    In [248]: p = np.stack(pivot_table.ravel()).reshape(3,3,2)
    In [249]: p
    Out[249]: 
    array([[[100, 200],
            [100, 200],
            [  0,   0]],
    
           [[  0,   0],
            [100, 200],
            [100, 200]],
    
           [[100, 200],
            [  0,   0],
            [100, 200]]])
    
    np.concatenate
    (和
    *stack
    版本)可以将列表连接到一个数组中,但它必须从一个列表或平面数组开始,因此需要展开和重新成形

    np.array(pivot\u table.tolist())
    也可以工作


    相反,如果您构建了结构化数据数组(假设
    度量值
    为数值):


    使用Paul Panzer定义的
    fillvalue
    数组,您的初始屏蔽分配工作:

    In [322]: fillvalue = np.empty((), 'O')
         ...: fillvalue[()] = [0, 0]
         ...: 
    In [323]: fillvalue
    Out[323]: array(list([0, 0]), dtype=object)
    In [324]: mask
    Out[324]: 
    array([[False, False,  True],
           [ True, False, False],
           [False,  True, False]])
    In [325]: pivot_table[mask] = fillvalue
    
    他的
    full
    执行
    np.copyto(a,fill\u值,casting='safe')

    我们的蒙版作业可以写成:
    np.copyto(pivot\u table,fillvalue,where=mask)

    您的输入数据类型不清楚,可能会造成不便。避免对象类型有助于数据结构分析。使用结构化阵列有助于:

    原始数据样本:

    n=10
    data= [ [randint(5),'abcdef'[randint(6)],rand(2)] for _ in range(n)]
    
    手动键入和填写:

    dt=np.dtype([('i', 'i4'), ('j', 'U1'), ('val', 'f8', 2)])
    arr = ndarray(len(data),dtype=dt)
    for k,(a,b,c) in enumerate (data):
        arr[k]['i']=a
        arr[k]['j']=b
        arr[k]['val']=c
    
    现在一切都很简单:

    row=arr['i']
    col=arr['j']
    val=arr['val']
    
    (r,ri),(c,ci) = (np.unique(x,return_inverse=True) for x in (row,col))
    res=zeros((len(r),len(c),2)) # the good shape
    res[ri,ci]=val
    
    res现在是

    [[[ 0.87  0.96]
      [ 0.03  0.92]
      [ 0.45  0.55]
      [ 0.    0.  ]
      [ 0.    0.  ]]
    
     [[ 0.27  0.84]
      [ 0.    0.  ]
      [ 0.41  0.05]
      [ 0.47  0.67]
      [ 0.    0.  ]]
    
     [[ 0.3   0.05]
      [ 0.    0.  ]
      [ 0.    0.  ]
      [ 0.    0.  ]
      [ 0.37  0.76]]
    
     [[ 0.    0.  ]
      [ 0.    0.  ]
      [ 0.    0.  ]
      [ 0.    0.  ]
      [ 0.4   0.07]]]
    

    以下是如何让您的方法2发挥作用:

    fillvalue = np.empty((), 'O')
    fillvalue[()] = [0, 0]
    pivot_table = np.full((len(rows), len(cols)), fillvalue)
    
    etc.
    
    请注意,
    [0,0]
    都是同一个对象,因此,如果要更改其中一个对象,不应通过修改列表对象来完成,而应创建一个新列表并将其指定给数组位置


    如果您想要一个3D数字数组而不是列表数组,那么快速修复方法是
    np.array(pivot\u table.tolist())

    查看
    pivot\u table[pivot\u table==0]
    。它可能是一个0的1d数组。将标量或类似
    None
    的对象指定给这些位置应该可以正常工作。但分配一份清单将是一件棘手的事情
    numpy
    将列表转换为
    ndarray
    ,然后尝试应用广播。一般来说将列表分配给对象数组的单个元素效果很好,但分配给多个元素却很困难。虽然它不是0的1d数组。它们显示为每个项目的
    列表([metric1,metric2])
    。我试着只使用一个标准的2D数组,但这样我只能旋转其中一个指标。我想一个解决方案是为每个度量创建单独的表,然后将它们合并为一个。任何数组的布尔掩码都会生成一个1d数组。对于对象,原始掩码赋值有效,
    pivot\u table[pivot\u table==0]=fillvalue
    。对就地修改同样要小心。这很有用,但不幸的是,我的前两列具有可变长度,因此它不适用于所有数据用例。错误不同,因为我的错误来自动态生成
    np.zero((1,len(numMetrics)))
    。直接分配
    [0,0]
    时,我会遇到类似的错误,尽管我使用的是早期版本(由于标准的Google应用程序引擎限制)。您的一些初始解决方案在动态大小度量方面存在问题,或者由于数据本身的原因,无法构建数据类型。最后一个使用
    fillvalue
    的解决方案成功了。您没有提供一个解决方案,因此我不得不临时修改,使
    数据
    大致符合您的描述。正如您从答案中看到的,我们更喜欢使用真正的工作代码,而不仅仅是泛型描述。在编写伪代码时很容易出错。这是公平的,我感谢您提供的全面和大量的答案,只是想让您知道什么对我有效,什么对我无效。我确实需要对我的公司数据进行一些概括,但我会确保提供一些能够代表我未来问题的示例(我真的没有料到数据的
    dtype
    会产生如此大的影响)
    In [322]: fillvalue = np.empty((), 'O')
         ...: fillvalue[()] = [0, 0]
         ...: 
    In [323]: fillvalue
    Out[323]: array(list([0, 0]), dtype=object)
    In [324]: mask
    Out[324]: 
    array([[False, False,  True],
           [ True, False, False],
           [False,  True, False]])
    In [325]: pivot_table[mask] = fillvalue
    
    n=10
    data= [ [randint(5),'abcdef'[randint(6)],rand(2)] for _ in range(n)]
    
    dt=np.dtype([('i', 'i4'), ('j', 'U1'), ('val', 'f8', 2)])
    arr = ndarray(len(data),dtype=dt)
    for k,(a,b,c) in enumerate (data):
        arr[k]['i']=a
        arr[k]['j']=b
        arr[k]['val']=c
    
    row=arr['i']
    col=arr['j']
    val=arr['val']
    
    (r,ri),(c,ci) = (np.unique(x,return_inverse=True) for x in (row,col))
    res=zeros((len(r),len(c),2)) # the good shape
    res[ri,ci]=val
    
    [[[ 0.87  0.96]
      [ 0.03  0.92]
      [ 0.45  0.55]
      [ 0.    0.  ]
      [ 0.    0.  ]]
    
     [[ 0.27  0.84]
      [ 0.    0.  ]
      [ 0.41  0.05]
      [ 0.47  0.67]
      [ 0.    0.  ]]
    
     [[ 0.3   0.05]
      [ 0.    0.  ]
      [ 0.    0.  ]
      [ 0.    0.  ]
      [ 0.37  0.76]]
    
     [[ 0.    0.  ]
      [ 0.    0.  ]
      [ 0.    0.  ]
      [ 0.    0.  ]
      [ 0.4   0.07]]]
    
    fillvalue = np.empty((), 'O')
    fillvalue[()] = [0, 0]
    pivot_table = np.full((len(rows), len(cols)), fillvalue)
    
    etc.