处理我要透视的数据。请注意,我仅限于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
]
使用numpy旋转数据:
rows, row_pos = np.unique(data[:, row_index], return_inverse=True)
cols, col_pos = np.unique(data[:, col_index], return_inverse=True)
pivot_table = np.zeros((len(rows), len(cols)), dtype=object)
pivot_table[row_pos, col_pos] = data[:, pivot_index]
结果格式为:
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>], ... ],
...
]
数据透视表最终呈现出来,它记录了零在哪里,并将创建正确数量的单元格,以便正确格式化表格。
这只是一个临时解决方法,因为最初我只是尝试用numpy数组(即[0,0])替换零
pivot_table[pivot_table == 0] = [0,0]
但是我遇到了以下错误:
TypeError: NumPy boolean array indexing assignment requires a 0 or 1-dimensional input, input has 2 dimensions
我的临时解决方案就足够了,但是当我想做某事(例如有一列列总和)时就受到了限制。我有很多方法,但是不知道如何执行它们:
np.unique
中的索引创建表时,请使用默认值填充表,而不是零。[ 1, a, <metric1>, <metric2> ]
。这可能是简化聚合函数的最佳解决方案。针对上述两种方法的任何解决方案?
答案 0 :(得分:2)
以下是使方法2起作用的方法:
fillvalue = np.empty((), 'O')
fillvalue[()] = [0, 0]
pivot_table = np.full((len(rows), len(cols)), fillvalue)
etc.
请注意,[0, 0]
都是相同的对象,因此,如果要更改其中的一个,则不应通过适当修改列表对象来实现,而应该创建一个新列表并分配它到数组位置。
如果要使用3D数字数组而不是列表数组,则快速解决方法是np.array(pivot_table.tolist())
。
答案 1 :(得分:1)
尝试重新创建案例:
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,)
此分配在源形状(和dtype)与目标形状(6,)匹配之间起作用。
In [197]: mask = pivot_table==0
In [198]: mask
Out[198]:
array([[False, False, True],
[ True, False, False],
[False, True, False]])
In [199]: pivot_table[mask]
Out[199]: array([0, 0, 0], dtype=object)
In [200]: pivot_table[mask] = [0,0]
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-200-83e0a7422802> in <module>()
----> 1 pivot_table[mask] = [0,0]
ValueError: NumPy boolean array indexing assignment cannot assign 2 input values to the 3 output values where the mask is true
不同的错误消息(不同的numpy版本?),但这表示我试图将2个值放入3个插槽中。它不会将[0,0]
视为单个项目,而是视为2。
分配标量元素没有问题:
In [203]: pivot_table[mask] = None
In [204]: pivot_table
Out[204]:
array([[list([100, 200]), list([100, 200]), None],
[None, list([100, 200]), list([100, 200])],
[list([100, 200]), None, list([100, 200])]], dtype=object)
过去,我曾成功使用frompyfunc
创建对象dtype数组。定义一个小功能。我可以测试0或类型,但是由于我已经插入了None,所以我们来测试一下:
In [205]: def fun(x):
...: if x is None: return [0,0]
...: return x
将其应用于pivot_table
的每个元素,产生一个新数组。
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
但是,如果我使用where
尝试相同的操作,它将起作用:
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_table
分配的内容。
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.where(mask)
索引,但是很明显,这里是对象dtype的相互作用,以及布尔索引的混乱。
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_table.tolist())
也可以。
相反,如果您构造了一个结构化的数据数组(假设metric
的值是数字):
In [265]: data1 = np.array([tuple(x.tolist()) for x in data],'i,i,2i')
In [266]: data1
Out[266]:
array([(1, 0, [100, 200]), (1, 1, [100, 200]), (2, 1, [100, 200]),
(2, 2, [100, 200]), (3, 0, [100, 200]), (3, 2, [100, 200])],
dtype=[('f0', '<i4'), ('f1', '<i4'), ('f2', '<i4', (2,))])
In [267]: data1['f2']
Out[267]:
array([[100, 200],
[100, 200],
[100, 200],
[100, 200],
[100, 200],
[100, 200]], dtype=int32)
这些值可以分配给3d数据透视表:
In [268]: p = np.zeros((len(rows), len(cols),2),int)
In [269]: p[row_pos, col_pos]=data1['f2']
使用保罗·潘泽(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_value, casting='unsafe')
,
我们的屏蔽作业可以写为:np.copyto(pivot_table, fillvalue, where=mask)
答案 2 :(得分:1)
您的输入数据类型不清楚,可能会带来不便。避免对象类型有助于数据结构分析。使用结构化数组可以帮助:
原始数据示例:
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]]]