从 numPy 数组列表中删除重复项
Removing duplicates from a list of numPy arrays
我有一个普通的 Python 列表,其中包含(多维)numPy 数组,所有数组都具有相同的形状和相同数量的值。列表中的一些数组与之前的数组重复。
我有一个问题,我想删除所有重复项,但数据类型是 numPy 数组这一事实使这有点复杂...
• 我不能使用 set(),因为 numPy 数组不可哈希。
• 我无法在插入过程中检查重复项,因为数组是由函数批量生成并使用 .extend() 添加到列表中的。
• 如果不求助于 numPy 自己的函数之一,numPy 数组不能直接比较,所以我不能只使用 "if x in list"...
• 列表的内容需要在进程结束时保留为numPy 数组;我可以比较转换为嵌套列表的数组副本,但我无法将数组永久转换为直接 python 列表。
关于如何在此处有效删除重复项的任何建议?
使用此处的解决方案:Most efficient property to hash for numpy array 我们发现,如果 a 是一个 numpy 数组,则哈希与 a.tostring() 配合使用效果最佳。所以:
import numpy as np
arraylist = [np.array([1,2,3,4]), np.array([1,2,3,4]), np.array([1,3,2,4])]
L = {array.tostring(): array for array in arraylist}
L.values() # [array([1, 3, 2, 4]), array([1, 2, 3, 4])]
这是使用 tuple
的一种方法:
>>> import numpy as np
>>> t = [np.asarray([1, 2, 3, 4]),
np.asarray([1, 2, 3, 4]),
np.asarray([1, 1, 3, 4])]
>>> map(np.asarray, set(map(tuple, t)))
[array([1, 1, 3, 4]), array([1, 2, 3, 4])]
如果您的数组是多维的,那么首先将它们展平为 1 乘以任何数组,然后使用相同的想法,并在最后重新整形:
def to_tuple(arr):
return tuple(arr.reshape((arr.size,)))
def from_tuple(tup, original_shape):
np.asarray(tup).reshape(original_shape)
示例:
In [64]: t = np.asarray([[[1,2,3],[4,5,6]],
[[1,1,3],[4,5,6]],
[[1,2,3],[4,5,6]]])
In [65]: map(lambda x: from_tuple(x, t[0].shape), set(map(to_tuple, t)))
Out[65]:
[array([[1, 2, 3],
[4, 5, 6]]),
array([[1, 1, 3],
[4, 5, 6]])]
另一种选择是从 ndarrays
中创建一个 pandas.DataFrame
(如果需要,通过重塑将它们视为行)并使用 pandas 内置函数来唯一化行。
In [34]: t
Out[34]: [array([1, 2, 3, 4]), array([1, 2, 3, 4]), array([1, 1, 3, 4])]
In [35]: pandas.DataFrame(t).drop_duplicates().values
Out[35]:
array([[1, 2, 3, 4],
[1, 1, 3, 4]])
总的来说,我认为尝试将 tostring()
用作准哈希函数是个坏主意,因为您将需要比我的方法更多的样板代码,只是为了防止某些在某些 dict
中为它们分配 "hash" 密钥后,内容会发生变化。
如果考虑到数据的大小,重塑和转换为 tuple
的速度太慢,我的感觉是这揭示了一个更根本的问题:应用程序的设计没有很好地满足需求(比如 de -duping) 并试图将它们塞进内存中的某些 Python 进程 运行 中可能不是正确的方法。到那时,我会停止考虑像 Cassandra 这样可以在浮点(或其他)数据的大列(或多维数组)之上轻松构建数据库索引的东西是否是更明智的方法。
根据您的数据结构,直接比较所有数组可能比寻找某种方法对数组进行散列运算更快。该算法是 O(n^2),但每个单独的比较将比创建字符串或数组的 python 列表快得多。所以这取决于你要检查多少个数组。
例如
uniques = []
for arr in possible_duplicates:
if not any(numpy.array_equal(arr, unique_arr) for unique_arr in uniques):
uniques.append(arr)
我有一个普通的 Python 列表,其中包含(多维)numPy 数组,所有数组都具有相同的形状和相同数量的值。列表中的一些数组与之前的数组重复。
我有一个问题,我想删除所有重复项,但数据类型是 numPy 数组这一事实使这有点复杂...
• 我不能使用 set(),因为 numPy 数组不可哈希。
• 我无法在插入过程中检查重复项,因为数组是由函数批量生成并使用 .extend() 添加到列表中的。
• 如果不求助于 numPy 自己的函数之一,numPy 数组不能直接比较,所以我不能只使用 "if x in list"...
• 列表的内容需要在进程结束时保留为numPy 数组;我可以比较转换为嵌套列表的数组副本,但我无法将数组永久转换为直接 python 列表。
关于如何在此处有效删除重复项的任何建议?
使用此处的解决方案:Most efficient property to hash for numpy array 我们发现,如果 a 是一个 numpy 数组,则哈希与 a.tostring() 配合使用效果最佳。所以:
import numpy as np
arraylist = [np.array([1,2,3,4]), np.array([1,2,3,4]), np.array([1,3,2,4])]
L = {array.tostring(): array for array in arraylist}
L.values() # [array([1, 3, 2, 4]), array([1, 2, 3, 4])]
这是使用 tuple
的一种方法:
>>> import numpy as np
>>> t = [np.asarray([1, 2, 3, 4]),
np.asarray([1, 2, 3, 4]),
np.asarray([1, 1, 3, 4])]
>>> map(np.asarray, set(map(tuple, t)))
[array([1, 1, 3, 4]), array([1, 2, 3, 4])]
如果您的数组是多维的,那么首先将它们展平为 1 乘以任何数组,然后使用相同的想法,并在最后重新整形:
def to_tuple(arr):
return tuple(arr.reshape((arr.size,)))
def from_tuple(tup, original_shape):
np.asarray(tup).reshape(original_shape)
示例:
In [64]: t = np.asarray([[[1,2,3],[4,5,6]],
[[1,1,3],[4,5,6]],
[[1,2,3],[4,5,6]]])
In [65]: map(lambda x: from_tuple(x, t[0].shape), set(map(to_tuple, t)))
Out[65]:
[array([[1, 2, 3],
[4, 5, 6]]),
array([[1, 1, 3],
[4, 5, 6]])]
另一种选择是从 ndarrays
中创建一个 pandas.DataFrame
(如果需要,通过重塑将它们视为行)并使用 pandas 内置函数来唯一化行。
In [34]: t
Out[34]: [array([1, 2, 3, 4]), array([1, 2, 3, 4]), array([1, 1, 3, 4])]
In [35]: pandas.DataFrame(t).drop_duplicates().values
Out[35]:
array([[1, 2, 3, 4],
[1, 1, 3, 4]])
总的来说,我认为尝试将 tostring()
用作准哈希函数是个坏主意,因为您将需要比我的方法更多的样板代码,只是为了防止某些在某些 dict
中为它们分配 "hash" 密钥后,内容会发生变化。
如果考虑到数据的大小,重塑和转换为 tuple
的速度太慢,我的感觉是这揭示了一个更根本的问题:应用程序的设计没有很好地满足需求(比如 de -duping) 并试图将它们塞进内存中的某些 Python 进程 运行 中可能不是正确的方法。到那时,我会停止考虑像 Cassandra 这样可以在浮点(或其他)数据的大列(或多维数组)之上轻松构建数据库索引的东西是否是更明智的方法。
根据您的数据结构,直接比较所有数组可能比寻找某种方法对数组进行散列运算更快。该算法是 O(n^2),但每个单独的比较将比创建字符串或数组的 python 列表快得多。所以这取决于你要检查多少个数组。
例如
uniques = []
for arr in possible_duplicates:
if not any(numpy.array_equal(arr, unique_arr) for unique_arr in uniques):
uniques.append(arr)