numpy 中的累积模式
Cumulative Mode in numpy
我想在 numpy 中有效地计算沿轴的累积模式。
例如
>>> arr = np.random.RandomState(3).randint(3, size = (2, 5))
>>> arr
array([[2, 0, 1, 0, 0],
[0, 1, 1, 2, 1]])
>>> assert np.array_equal(cummode(arr, axis = 1), [[2,2,2,0,0],[0,0,1,1,1])
有没有有效的方法来做到这一点?我想它可以通过返回第一个数字来达到给定计数来处理平局。
好的,它不是完全通用的,因为它不能沿任何轴工作,但现在我已经制作了一个可以使用 scipy-weave 的版本。
from scipy import weave
def cummode(x, axis = 1):
assert x.ndim == 2 and axis == 1, 'Only implemented for a special case!'
all_values, element_ids = np.unique(x, return_inverse=True)
n_unique = len(all_values)
element_ids = element_ids.reshape(x.shape)
result = np.zeros(x.shape, dtype = int)
counts = np.zeros(n_unique, dtype = int)
code = """
int n_samples = Nelement_ids[0];
int n_events = Nelement_ids[1];
for (int i=0; i<n_samples; i++){
int maxcount = 0;
int maxel = -1;
for (int k=0; k<n_unique; k++)
counts[k] = 0;
for (int j=0; j<n_events; j++){
int ix = i*n_events+j;
int k = element_ids[ix];
counts[k]+=1;
if (counts[k] > maxcount){
maxcount = counts[k];
maxel = k;
}
result[ix]=maxel;
}
}
"""
weave.inline(code, ['element_ids', 'result', 'n_unique', 'counts'], compiler = 'gcc')
mode_values = all_values[result]
return mode_values
这是一个纯 Python 函数,可用于列表或任何可迭代对象:
from collections import defaultdict
def cummode(alist):
dd = defaultdict(int)
mode = [(None,0)]
for i in alist:
dd[i] += 1
if dd[i]>mode[-1][1]:
newmode = (i,dd[i])
else:
newmode = mode[-1]
mode.append(newmode)
mode.pop(0)
return mode, dd
mode,dd = cummode([0,1,3,6,1,2,3,3,2,1,10,0])
print(dd)
print(mode)
对于测试用例,产生
defaultdict(<type 'int'>, {0: 2, 1: 3, 2: 2, 3: 3, 6: 1, 10: 1})
[(0, 1), (0, 1), (0, 1), (0, 1), (1, 2), (1, 2), (1, 2), (3, 3), (3, 3), (3, 3), (3, 3), (3, 3)]
A detaultdict
是一种在您事先不知道所有键时累积值的干净快速的方法。对于小型列表和数组,它可能胜过基于 numpy
的版本,即使使用 weave
,只是因为它不会产生创建数组的开销。但是对于大的,它可能会滞后。另外,我还没有将其概括为处理多个值(行)。
我想在 numpy 中有效地计算沿轴的累积模式。
例如
>>> arr = np.random.RandomState(3).randint(3, size = (2, 5))
>>> arr
array([[2, 0, 1, 0, 0],
[0, 1, 1, 2, 1]])
>>> assert np.array_equal(cummode(arr, axis = 1), [[2,2,2,0,0],[0,0,1,1,1])
有没有有效的方法来做到这一点?我想它可以通过返回第一个数字来达到给定计数来处理平局。
好的,它不是完全通用的,因为它不能沿任何轴工作,但现在我已经制作了一个可以使用 scipy-weave 的版本。
from scipy import weave
def cummode(x, axis = 1):
assert x.ndim == 2 and axis == 1, 'Only implemented for a special case!'
all_values, element_ids = np.unique(x, return_inverse=True)
n_unique = len(all_values)
element_ids = element_ids.reshape(x.shape)
result = np.zeros(x.shape, dtype = int)
counts = np.zeros(n_unique, dtype = int)
code = """
int n_samples = Nelement_ids[0];
int n_events = Nelement_ids[1];
for (int i=0; i<n_samples; i++){
int maxcount = 0;
int maxel = -1;
for (int k=0; k<n_unique; k++)
counts[k] = 0;
for (int j=0; j<n_events; j++){
int ix = i*n_events+j;
int k = element_ids[ix];
counts[k]+=1;
if (counts[k] > maxcount){
maxcount = counts[k];
maxel = k;
}
result[ix]=maxel;
}
}
"""
weave.inline(code, ['element_ids', 'result', 'n_unique', 'counts'], compiler = 'gcc')
mode_values = all_values[result]
return mode_values
这是一个纯 Python 函数,可用于列表或任何可迭代对象:
from collections import defaultdict
def cummode(alist):
dd = defaultdict(int)
mode = [(None,0)]
for i in alist:
dd[i] += 1
if dd[i]>mode[-1][1]:
newmode = (i,dd[i])
else:
newmode = mode[-1]
mode.append(newmode)
mode.pop(0)
return mode, dd
mode,dd = cummode([0,1,3,6,1,2,3,3,2,1,10,0])
print(dd)
print(mode)
对于测试用例,产生
defaultdict(<type 'int'>, {0: 2, 1: 3, 2: 2, 3: 3, 6: 1, 10: 1})
[(0, 1), (0, 1), (0, 1), (0, 1), (1, 2), (1, 2), (1, 2), (3, 3), (3, 3), (3, 3), (3, 3), (3, 3)]
A detaultdict
是一种在您事先不知道所有键时累积值的干净快速的方法。对于小型列表和数组,它可能胜过基于 numpy
的版本,即使使用 weave
,只是因为它不会产生创建数组的开销。但是对于大的,它可能会滞后。另外,我还没有将其概括为处理多个值(行)。