Python 在numpy.array中查找唯一行
我需要在Python 在numpy.array中查找唯一行,python,arrays,numpy,unique,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]
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]])
我知道我可以在阵列上创建一个集合和循环,但我正在寻找一个高效的纯
numpy
解决方案。我相信有一种方法可以将数据类型设置为void,然后我可以只使用numpy.unique
,但我不知道如何使其工作。np.unique在给定元组列表的情况下工作:
>>> np.unique([(1, 1), (2, 2), (3, 3), (4, 4), (2, 2)])
Out[9]:
array([[1, 1],
[2, 2],
[3, 3],
[4, 4]])
对于列表列表,当我在
np.random.random(100)上运行它时,它会引发一个TypeError:unhable类型:“list'
np.unique
。重塑(10,10)
返回所有唯一的单个元素,但您需要唯一的行,因此首先需要将它们放入元组:
array = #your numpy array of lists
new_array = [tuple(row) for row in array]
uniques = np.unique(new_array)
这是我看到您更改类型以执行所需操作的唯一方法,我不确定更改为元组的列表迭代是否适合您的“不循环”如果您希望避免转换为一系列元组或其他类似数据结构的内存开销,您可以利用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
要了解发生了什么,请查看中间结果 一旦我们将事物视为结构化数组,数组中的每个元素就是原始数组中的一行。(基本上,它是一个类似于元组列表的数据结构。) 然后我们需要将其视为“普通”数组(
\uu
将上一次计算的结果存储在ipython
中,这就是为什么您会看到\uView…
):
然后重新整形为二维数组(-1
是一个占位符,告诉numpy计算正确的行数,给出列数):
显然,如果你想更简洁,你可以这样写:
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)
其结果是:
[[0 1 1 1 0 0]
[1 1 1 0 0 0]
[1 1 1 1 1 0]]
[[0 1 1 1 0 0]
[1 1 1 0 0 0]
[1 1 1 1 1 0]]
np.unique的工作原理是对扁平数组进行排序,然后查看每个项是否与前一项相等。这可以在不展平的情况下手动完成:
ind = np.lexsort(a.T)
a[ind[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]]
这个方法不使用元组,应该比这里给出的其他方法更快、更简单
注意:此版本的早期版本在A[]之后没有ind,这意味着使用了错误的索引。此外,Joe Kington很好地指出,这确实会生成多种中间副本。以下方法通过生成排序副本,然后使用其视图来减少索引的数量:
b = a[np.lexsort(a.T)]
b[np.concatenate(([True], np.any(b[1:] != b[:-1],axis=1)))]
这样速度更快,占用的内存更少
此外,如果希望在数据数组中查找唯一的行,而不管数组中有多少个维度,则以下操作将起作用:
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)))))]
另一个有趣的问题是,如果您想沿着任意维数组的任意轴进行排序/唯一,这将更加困难
编辑:
为了演示速度差异,我在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
[87]中的
:%timeit unique(a.view(dtype)).view(“使用结构化数组的另一个选项是使用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]])
编辑
根据@seberg的建议添加了np.ascontiguousarray
。如果数组尚未连续,则会降低方法的速度
编辑
通过执行以下操作,可以稍微加快上述速度,但可能会以清晰度为代价:
unique_a = np.unique(b).view(a.dtype).reshape(-1, a.shape[1])
此外,至少在我的系统中,性能比BLASTRAY方法更合适,甚至更好:
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
另一种可能的解决办法
np.vstack({tuple(row) for row in a})
根据本页中的答案,我编写了一个函数,该函数复制了MATLAB的unique(输入,'rows')
函数的功能,具有接受唯一性检查公差的附加功能。它还返回索引,例如c=data[ia,:]
和data=c[ic,:]
。如果发现任何差异或错误,请报告
def unique_rows(data, prec=5):
import numpy as np
d_r = np.fix(data * 10 ** prec) / 10 ** prec + 0.0
b = np.ascontiguousarray(d_r).view(np.dtype((np.void, d_r.dtype.itemsize * d_r.shape[1])))
_, ia = np.unique(b, return_index=True)
_, ic = np.unique(b, return_inverse=True)
return np.unique(b).view(d_r.dtype).reshape(-1, d_r.shape[1]), ia, ic
这里是@Greg pythonic答案的另一个变体
np.vstack(set(map(tuple, a)))
为什么不从熊猫中使用删除重复项
:
>>> timeit pd.DataFrame(image.reshape(-1,3)).drop_duplicates().values
1 loops, best of 3: 3.08 s per loop
>>> timeit np.vstack({tuple(r) for r in image.reshape(-1,3)})
1 loops, best of 3: 51 s per loop
该软件包(免责声明:我是它的作者)将Jaime发布的解决方案包装在一个漂亮且经过测试的界面中,并提供了更多功能:
import numpy_indexed as npi
new_a = npi.unique(a) # unique elements over axis=0 (rows) by default
对于一般用途(如三维或更高的多维嵌套数组),请尝试以下操作:
import numpy as np
def unique_nested_arrays(ar):
origin_shape = ar.shape
origin_dtype = ar.dtype
ar = ar.reshape(origin_shape[0], np.prod(origin_shape[1:]))
ar = np.ascontiguousarray(ar)
unique_ar = np.unique(ar.view([('', origin_dtype)]*np.prod(origin_shape[1:])))
return unique_ar.view(origin_dtype).reshape((unique_ar.shape[0], ) + origin_shape[1:])
满足您的2D数据集的:
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]])
unique_nested_arrays(a)
给出:
array([[0, 1, 1, 1, 0, 0],
[1, 1, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 0]])
array([[[0, 1, 1], [1, 1, 1]],
[[1, 1, 1], [0, 1, 1]],
[[1, 1, 1], [1, 1, 1]]])
还有3D阵列,如:
b = np.array([[[1, 1, 1], [0, 1, 1]],
[[0, 1, 1], [1, 1, 1]],
[[1, 1, 1], [0, 1, 1]],
[[1, 1, 1], [1, 1, 1]]])
unique_nested_arrays(b)
给出:
array([[0, 1, 1, 1, 0, 0],
[1, 1, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 0]])
array([[[0, 1, 1], [1, 1, 1]],
[[1, 1, 1], [0, 1, 1]],
[[1, 1, 1], [1, 1, 1]]])
我不喜欢这些答案中的任何一个,因为它们都不能处理线性代数或向量空间意义上的浮点数组,其中两行“相等”意味着“在某些中,最简单的解决方案是通过将行设置为字符串将行设置为单个项目。然后,可以使用numpy将每行作为一个整体进行比较,以确定其唯一性。此解决方案是通用的,您只需重新设置数组的形状并将其转换为其他组合。以下是针对所提供问题的解决方案
import numpy as np
original = 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]])
uniques, index = np.unique([str(i) for i in original], return_index=True)
cleaned = original[index]
print(cleaned)
将提供:
array([[0, 1, 1, 1, 0, 0],
[1, 1, 1, 0, 0, 0],
[1, 1, 1, 1, 1, 0]])
通过邮件发送我的诺贝尔奖这些答案中没有一个对我有效。我假设我的唯一行包含字符串而不是数字。但是,来自另一个线程的答案确实有效:
资料来源:
可以使用.count()和.index()列表的方法
coor = np.array([[10, 10], [12, 9], [10, 5], [12, 9]])
coor_tuple = [tuple(x) for x in coor]
unique_coor = sorted(set(coor_tuple), key=lambda x: coor_tuple.index(x))
unique_count = [coor_tuple.count(x) for x in unique_coor]
unique_index = [coor_tuple.index(x) for x in unique_coor]
除了@Jaime极好的答案之外,另一种折叠行的方法是使用a.strips[0]
(假设a
是C-连续的),它等于a.dtype.itemsize*a.shape[0]
。此外void(n)
是dtype((void,n))的快捷方式。我们最终得到了这个最短的版本:
a[unique(a.view(void(a.strides[0])),1)[1]]
为了
从NumPy 1.13开始,只需选择轴即可在任意N-dim数组中选择唯一值。要获得唯一行,可以执行以下操作:
unique\u rows=np.unique(原始数组,轴=0)
我们实际上可以转动m
coor = np.array([[10, 10], [12, 9], [10, 5], [12, 9]])
coor_tuple = [tuple(x) for x in coor]
unique_coor = sorted(set(coor_tuple), key=lambda x: coor_tuple.index(x))
unique_count = [coor_tuple.count(x) for x in unique_coor]
unique_index = [coor_tuple.index(x) for x in unique_coor]
a[unique(a.view(void(a.strides[0])),1)[1]]
[[0 1 1 1 0 0]
[1 1 1 0 0 0]
[1 1 1 1 1 0]]
import numpy as np
original = 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]])
# create a view that the subarray as tuple and return unique indeies.
_, unique_index = np.unique(original.view(original.dtype.descr * original.shape[1]),
return_index=True)
# get unique set
print(original[unique_index])
import numpy as np
def uniqueRow(a):
#This function turn m x n numpy array into m x 1 numpy array storing
#string, and so the np.unique can be used
#Input: an m x n numpy array (a)
#Output unique m' x n numpy array (unique), inverse_indx, and counts
s = np.chararray((a.shape[0],1))
s[:] = '-'
b = (a).astype(np.str)
s2 = np.expand_dims(b[:,0],axis=1) + s + np.expand_dims(b[:,1],axis=1)
n = a.shape[1] - 2
for i in range(0,n):
s2 = s2 + s + np.expand_dims(b[:,i+2],axis=1)
s3, idx, inv_, c = np.unique(s2,return_index = True, return_inverse = True, return_counts = True)
return a[idx], inv_, c
A = np.array([[ 3.17 9.502 3.291],
[ 9.984 2.773 6.852],
[ 1.172 8.885 4.258],
[ 9.73 7.518 3.227],
[ 8.113 9.563 9.117],
[ 9.984 2.773 6.852],
[ 9.73 7.518 3.227]])
B, inv_, c = uniqueRow(A)
Results:
B:
[[ 1.172 8.885 4.258]
[ 3.17 9.502 3.291]
[ 8.113 9.563 9.117]
[ 9.73 7.518 3.227]
[ 9.984 2.773 6.852]]
inv_:
[3 4 1 0 2 4 0]
c:
[2 1 1 1 2]
matrix_as_list=data.tolist()
matrix_as_list:
[[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]]
uniq_list=list()
uniq_list.append(matrix_as_list[0])
[uniq_list.append(item) for item in matrix_as_list if item not in uniq_list]
unique_matrix=np.array(uniq_list)
unique_matrix:
array([[1, 1, 1, 0, 0, 0],
[0, 1, 1, 1, 0, 0],
[1, 1, 1, 1, 1, 0]])