3D NumPy阵列中每个子阵列或切片的频率计数

时间:2016-04-29 10:07:25

标签: python arrays numpy scipy frequency

我试图在一个numpy 3d数组中获得每个子数组的频率计数(没有零)。但是,scipy.stats.itemfreq工具返回二维数组中的频率计数。

我得到的是:

array_3d= array([[[1, 0, 0],
    [1, 0, 0],
    [0, 2, 0]],

   [[0, 0, 0],
    [0, 0, 3],
    [3, 3, 3]],

   [[0, 0, 4],
    [0, 0, 4],
    [0, 0, 4]]])

>>> itemfreq(array_3d)[1:,]
# outputs
array([ 1,  2],
   [ 2,  1],
   [ 3,  4],
   [ 4,  3]], dtype=int64)

虽然我想要输出:

array([[ 1,  2, 2, 1],
   [ 3,  4],
   [ 4,  3]], dtype=object)

这个想法是,不均匀的数字始终是唯一值,偶数是频率。

另一个输出可能是:

array([ 1,  2, 0],
   [ 2,  1, 0],
   [ 3,  4, 1],
   [ 4,  3, 2]], dtype=int64)

第三列表示3d数组中的子集编号。

我也对其他输出/解决方案持开放态度!

提前致谢!

3 个答案:

答案 0 :(得分:1)

方法#1

这是使用NumPy broadcasting -

的矢量化方法
# Get unique non-zero elements
unq = np.unique(array_3d[array_3d!=0])

# Get matches mask corresponding to all array_3d elements against all unq elements
mask = array_3d == unq[:,None,None,None]

# Get the counts
sums = mask.sum(axis=(2,3)).T

# Indices of non-zero(valid) counts
Rvalid,Cvalid = np.where(sums!=0)

# Finally, pressent the output in the desired format
out = np.column_stack((unq[Cvalid],sums[sums!=0],Rvalid))

请注意,这将是一种资源匮乏的方法。

方法#2

这是另一种资源匮乏的方法,因此可能更受欢迎 -

a2d = np.sort(array_3d.reshape(array_3d.shape[0],-1),axis=1)
start_mask = np.column_stack((a2d[:,0] !=0,np.diff(a2d,axis=1)>0))

unqID = a2d + ((np.arange(a2d.shape[0])*a2d.max())[:,None])
count = np.unique(unqID[a2d!=0],return_counts=True)[1]
out = np.column_stack((a2d[start_mask],count,np.where(start_mask)[0]))

请注意,count可以使用np.bincount计算,也可能更快,就像这样 -

C = np.bincount(unqID[a2d!=0])
count = C[C!=0]

答案 1 :(得分:1)

numpy_indexed包(免责声明:我是它的作者)可用于以优雅和矢量化的方式解决这个问题:

import numpy_indexed as npi
index = np.arange(array_3d.size) // array_3d[0].size
(value, index), count = npi.count((array_3d.flatten(), index))

然后给出:

index = [0 0 0 1 1 2 2]
value = [0 1 2 0 3 0 4]
count = [6 2 1 5 4 6 3]

可以通过索引值>进行后处理;如果需要,则为0

答案 2 :(得分:0)

熊猫也给出了这个结果的直观方式:

df = pd.DataFrame(array_3d.reshape(3,9))
stats = df.apply(lambda x : unique(x,return_counts=True),axis=1)
result = stats.apply(lambda x : vstack(x)[:,1:].ravel())

对于

#stats
0    ([0, 1, 2], [6, 2, 1])
1          ([0, 3], [5, 4])
2          ([0, 4], [6, 3])

#result
0    [1, 2, 2, 1]
1          [3, 4]
2          [4, 3]