Aka*_*all 185 python arrays numpy unique
我需要找到一个独特的行numpy.array
.
例如:
>>> a # I have
array([[1, 1, 1, 0, 0, 0],
[0, 1, 1, 1, 0, 0],
[0, 1, 1, 1, 0, 0],
[1, 1, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 0]])
>>> new_a # I want to get to
array([[1, 1, 1, 0, 0, 0],
[0, 1, 1, 1, 0, 0],
[1, 1, 1, 1, 1, 0]])
Run Code Online (Sandbox Code Playgroud)
我知道我可以在阵列上创建一个集合并循环,但我正在寻找一个有效的纯numpy
解决方案.我相信有一种方法可以将数据类型设置为void然后我可以使用numpy.unique
,但我无法弄清楚如何使其工作.
Gre*_*kel 138
又一种可能的解决方案
np.vstack({tuple(row) for row in a})
Run Code Online (Sandbox Code Playgroud)
Jai*_*ime 110
使用结构化数组的另一个选择是使用void
将整行连接到单个项目的类型的视图:
a = np.array([[1, 1, 1, 0, 0, 0],
[0, 1, 1, 1, 0, 0],
[0, 1, 1, 1, 0, 0],
[1, 1, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 0]])
b = np.ascontiguousarray(a).view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))
_, idx = np.unique(b, return_index=True)
unique_a = a[idx]
>>> unique_a
array([[0, 1, 1, 1, 0, 0],
[1, 1, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 0]])
Run Code Online (Sandbox Code Playgroud)
编辑
添加np.ascontiguousarray
以下@ seberg的推荐.如果数组尚未连续,这将减慢方法的速度.
编辑 上面的内容可能会略微加快,可能是以清晰为代价,通过以下方式:
unique_a = np.unique(b).view(a.dtype).reshape(-1, a.shape[1])
Run Code Online (Sandbox Code Playgroud)
此外,至少在我的系统上,性能方面与lexsort方法相当甚至更好:
a = np.random.randint(2, size=(10000, 6))
%timeit np.unique(a.view(np.dtype((np.void, a.dtype.itemsize*a.shape[1])))).view(a.dtype).reshape(-1, a.shape[1])
100 loops, best of 3: 3.17 ms per loop
%timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]
100 loops, best of 3: 5.93 ms per loop
a = np.random.randint(2, size=(10000, 100))
%timeit np.unique(a.view(np.dtype((np.void, a.dtype.itemsize*a.shape[1])))).view(a.dtype).reshape(-1, a.shape[1])
10 loops, best of 3: 29.9 ms per loop
%timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]
10 loops, best of 3: 116 ms per loop
Run Code Online (Sandbox Code Playgroud)
aiw*_*bdn 93
从NumPy 1.13开始,人们可以简单地选择轴来选择任何N-dim阵列中的唯一值.要获得唯一的行,可以执行以下操作:
unique_rows = np.unique(original_array, axis=0)
Joe*_*ton 29
如果要避免转换为一系列元组或其他类似数据结构的内存开销,可以利用numpy的结构化数组.
诀窍是将原始数组视为结构化数组,其中每个项对应于原始数组的一行.这不会复制,而且效率很高.
作为一个简单的例子:
import numpy as np
data = np.array([[1, 1, 1, 0, 0, 0],
[0, 1, 1, 1, 0, 0],
[0, 1, 1, 1, 0, 0],
[1, 1, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 0]])
ncols = data.shape[1]
dtype = data.dtype.descr * ncols
struct = data.view(dtype)
uniq = np.unique(struct)
uniq = uniq.view(data.dtype).reshape(-1, ncols)
print uniq
Run Code Online (Sandbox Code Playgroud)
要了解发生了什么,请查看中间结果.
一旦我们将事物视为结构化数组,数组中的每个元素都是原始数组中的一行.(基本上,它是与元组列表类似的数据结构.)
In [71]: struct
Out[71]:
array([[(1, 1, 1, 0, 0, 0)],
[(0, 1, 1, 1, 0, 0)],
[(0, 1, 1, 1, 0, 0)],
[(1, 1, 1, 0, 0, 0)],
[(1, 1, 1, 1, 1, 0)]],
dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])
In [72]: struct[0]
Out[72]:
array([(1, 1, 1, 0, 0, 0)],
dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])
Run Code Online (Sandbox Code Playgroud)
一旦我们运行numpy.unique
,我们将得到一个结构化数组:
In [73]: np.unique(struct)
Out[73]:
array([(0, 1, 1, 1, 0, 0), (1, 1, 1, 0, 0, 0), (1, 1, 1, 1, 1, 0)],
dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])
Run Code Online (Sandbox Code Playgroud)
然后我们需要将其视为"普通"数组(_
存储最后一次计算的结果ipython
,这就是您所看到的原因_.view...
):
In [74]: _.view(data.dtype)
Out[74]: array([0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0])
Run Code Online (Sandbox Code Playgroud)
然后重新形成一个2D数组(-1
是一个占位符,告诉numpy计算正确的行数,给出列数):
In [75]: _.reshape(-1, ncols)
Out[75]:
array([[0, 1, 1, 1, 0, 0],
[1, 1, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 0]])
Run Code Online (Sandbox Code Playgroud)
显然,如果你想更简洁,你可以把它写成:
import numpy as np
def unique_rows(data):
uniq = np.unique(data.view(data.dtype.descr * data.shape[1]))
return uniq.view(data.dtype).reshape(-1, data.shape[1])
data = np.array([[1, 1, 1, 0, 0, 0],
[0, 1, 1, 1, 0, 0],
[0, 1, 1, 1, 0, 0],
[1, 1, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 0]])
print unique_rows(data)
Run Code Online (Sandbox Code Playgroud)
结果如下:
[[0 1 1 1 0 0]
[1 1 1 0 0 0]
[1 1 1 1 1 0]]
Run Code Online (Sandbox Code Playgroud)
Rya*_*axe 19
np.unique
当我运行它时np.random.random(100).reshape(10,10)
返回所有唯一的单个元素,但你想要唯一的行,所以首先你需要将它们放入元组中:
array = #your numpy array of lists
new_array = [tuple(row) for row in array]
uniques = np.unique(new_array)
Run Code Online (Sandbox Code Playgroud)
这是我看到你改变类型来做你想做的事情的唯一方法,我不确定更改为元组的列表迭代是否可以用你的"不循环"
cge*_*cge 16
np.unique通过对平顶数组进行排序,然后查看每个项是否与前一个相同来工作.这可以手动完成而不会展平:
ind = np.lexsort(a.T)
a[ind[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]]
Run Code Online (Sandbox Code Playgroud)
此方法不使用元组,并且应该比此处给出的其他方法更快更简单.
注意:之前版本的这个版本在[之后]没有ind,这意味着使用了错误的索引.此外,Joe Kington提出了一个很好的观点,即它确实制作了各种中间副本.以下方法通过制作排序副本然后使用它的视图来减少:
b = a[np.lexsort(a.T)]
b[np.concatenate(([True], np.any(b[1:] != b[:-1],axis=1)))]
Run Code Online (Sandbox Code Playgroud)
这更快,使用更少的内存.
此外,如果要在ndarray中查找唯一行,而不管数组中有多少维,则以下内容将起作用:
b = a[lexsort(a.reshape((a.shape[0],-1)).T)];
b[np.concatenate(([True], np.any(b[1:]!=b[:-1],axis=tuple(range(1,a.ndim)))))]
Run Code Online (Sandbox Code Playgroud)
一个有趣的遗留问题是,如果你想沿任意维数组的任意轴排序/唯一,这将更加困难.
编辑:
为了演示速度差异,我在ipython中对答案中描述的三种不同方法进行了一些测试.使用你的确切的a,没有太大的区别,虽然这个版本有点快:
In [87]: %timeit unique(a.view(dtype)).view('<i8')
10000 loops, best of 3: 48.4 us per loop
In [88]: %timeit ind = np.lexsort(a.T); a[np.concatenate(([True], np.any(a[ind[1:]]!= a[ind[:-1]], axis=1)))]
10000 loops, best of 3: 37.6 us per loop
In [89]: %timeit b = [tuple(row) for row in a]; np.unique(b)
10000 loops, best of 3: 41.6 us per loop
Run Code Online (Sandbox Code Playgroud)
然而,如果使用更大的a,这个版本会更快,更快:
In [96]: a = np.random.randint(0,2,size=(10000,6))
In [97]: %timeit unique(a.view(dtype)).view('<i8')
10 loops, best of 3: 24.4 ms per loop
In [98]: %timeit b = [tuple(row) for row in a]; np.unique(b)
10 loops, best of 3: 28.2 ms per loop
In [99]: %timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!= a[ind[:-1]],axis=1)))]
100 loops, best of 3: 3.25 ms per loop
Run Code Online (Sandbox Code Playgroud)
我比对速度的建议的替代和令人惊讶地发现,虚空视图unique
解决方案比numpy的原生快甚至有点unique
用axis
的说法.如果你正在寻找速度,你会想要的
numpy.unique(
a.view(numpy.dtype((numpy.void, a.dtype.itemsize*a.shape[1])))
).view(a.dtype).reshape(-1, a.shape[1])
Run Code Online (Sandbox Code Playgroud)
重现情节的代码:
import numpy
import perfplot
def unique_void_view(a):
return numpy.unique(
a.view(numpy.dtype((numpy.void, a.dtype.itemsize*a.shape[1])))
).view(a.dtype).reshape(-1, a.shape[1])
def lexsort(a):
ind = numpy.lexsort(a.T)
return a[ind[
numpy.concatenate((
[True], numpy.any(a[ind[1:]] != a[ind[:-1]], axis=1)
))
]]
def vstack(a):
return numpy.vstack({tuple(row) for row in a})
def unique_axis(a):
return numpy.unique(a, axis=0)
perfplot.show(
setup=lambda n: numpy.random.randint(2, size=(n, 20)),
kernels=[unique_void_view, lexsort, vstack, unique_axis],
n_range=[2**k for k in range(15)],
logx=True,
logy=True,
xlabel='len(a)',
equality_check=None
)
Run Code Online (Sandbox Code Playgroud)
我不喜欢这些答案中的任何一个因为没有处理线性代数或向量空间意义上的浮点数组,其中两行"相等"意味着"在某些内部".具有容差阈值的一个答案,/sf/answers/1880743511/,将阈值设置为元素和小数精度,这适用于某些情况,但不像数学上那样通用真矢量距离.
这是我的版本:
from scipy.spatial.distance import squareform, pdist
def uniqueRows(arr, thresh=0.0, metric='euclidean'):
"Returns subset of rows that are unique, in terms of Euclidean distance"
distances = squareform(pdist(arr, metric=metric))
idxset = {tuple(np.nonzero(v)[0]) for v in distances <= thresh}
return arr[[x[0] for x in idxset]]
# With this, unique columns are super-easy:
def uniqueColumns(arr, *args, **kwargs):
return uniqueRows(arr.T, *args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
上面的公共域函数scipy.spatial.distance.pdist
用于查找每对行之间的欧几里德(可自定义)距离.然后,它将每个距离与thresh
旧的比较,以找到彼此之间的行thresh
,并从每个thresh
集群返回一行.
如所暗示的那样,距离metric
不必是欧几里德 - pdist
可以计算各种距离,包括cityblock
(曼哈顿范数)和cosine
(矢量之间的角度).
如果thresh=0
(默认值),则行必须精确到位才能被视为"唯一".使用其他良好的值thresh
缩放机器精度,即thresh=np.spacing(1)*1e3
.
归档时间: |
|
查看次数: |
84173 次 |
最近记录: |