如何在Python中简化数组为唯一值
因为PHP在处理数组时有深度问题,所以从Python传过来的这个数组会被截断,显示成省略号("...")。我想在返回给PHP之前,先在Python中处理这个数组。
进一步说明一下:我需要保留里面的集合[135, 121, 81]。这些是红、绿、蓝的值,我想把出现超过一次的集合进行分组。集合里的值需要保持[1, 2, 3]的顺序,而不是像下面一些回答提到的[1,2,3,4,5,6,7,8]。
你会怎么把这个3D的numpy.ndarray
简化成一组独特的RGB三元组?
这是Python打印出来的数组:
[[[135 121 81]
[135 121 81]
[135 121 81]
...,
[135 121 81]
[135 121 81]
[135 121 81]]
[[135 121 81]
[135 121 81]
[135 121 81]
...,
[135 121 81]
[135 121 81]
[135 121 81]]
[[ 67 68 29]
[135 121 81]
[ 67 68 29]
...,
[135 121 81]
[135 121 81]
[135 121 81]]
...,
[[200 170 19]
[200 170 19]
[200 170 19]
...,
[ 67 68 29]
[ 67 68 29]
[ 67 68 29]]
[[200 170 19]
[200 170 19]
[200 170 19]
...,
[116 146 15]
[116 146 15]
[116 146 15]]
[[200 170 19]
[200 170 19]
[200 170 19]
...,
[116 146 15]
[116 146 15]
[116 146 15]]]
这是我尝试过的代码:
def uniquify(arr)
keys = []
for c in arr:
if not c in keys:
keys[c] = 1
else:
keys[c] += 1
return keys
result = uniquify(items)
4 个回答
假设你的 Python list
看起来像 [[[1,2,3], [4,5,6]], [[7,8,9]]]
(也就是说,这是一个包含整数的嵌套列表)
mylist = [[[1,2,3], [4,5,6]], [[7,8,9]]]
items = set()
for sublist in mylist:
for subsublist in sublist:
for item in subsublist:
items.add(item)
如果你需要一个 list
,可以这样转换:items = list(items)
set
是一种数据类型,和 list
类似,但它不允许有重复的元素。使用 set
的一个副作用是,它不会保留插入的顺序——如果你需要保持顺序,就需要用其他方法,比如:
mylist = [[[1,2,3], [4,5,6]], [[7,8,9]]]
items = []
for sublist in mylist:
for subsublist in sublist:
for item in subsublist:
if not item in items:
items.add(item)
补充:根据你的修改,你可能想要这个:
mylist = [[[1,2,3], [4,5,6]], [[7,8,9], [1,2,3]]]
items = []
for sublist in mylist:
for item in sublist:
if not item in items:
items.append(item)
# items = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
看看 itertools
文档里的例子。里面有 flatten
和 unique_everseen
这两个函数,正好能满足你的需求。
你可以直接复制粘贴这些函数,或者你也可以通过 pip install more-itertools
来安装它们,这样你只需要导入就行了。这样,你就可以把三维数组变成二维数组,还可以用 unique_everseen
来去重二维数组……
不过有一个问题。你的二维数组里的元素是 list
,而列表是不可哈希的,所以你需要把它们转换成可以哈希的类型。但这很简单:
def uniquify(arr3d):
return unique_everseen(flatten(arr3d), tuple)
就这样。
如果你在粘贴这些函数的时候看看它们的实现,其实也很简单。这里唯一的技巧就是使用 set
来存储已经看到的值:集合只会保存每个唯一元素的一份副本(而且可以很快判断一个元素是否已经在集合里)。
实际上,如果你不需要保持顺序,事情会更简单:
def uniquify(arr3d):
return set(tuple(x) for x in flatten(arr3d))
作为测试,我复制了你的字符串,把它变成了一个实际的 Python 列表,然后做了这个:
inarray = [[[135, 121, 81],
[135, 121, 81],
[135, 121, 81],
[135, 121, 81],
[135, 121, 81],
[135, 121, 81]],
[[135, 121, 81],
[135, 121, 81],
[135, 121, 81],
[135, 121, 81],
[135, 121, 81],
[135, 121, 81]],
[[67, 68, 29],
[135, 121, 81],
[67, 68, 29],
[135, 121, 81],
[135, 121, 81],
[135, 121, 81]],
[[200, 170, 19],
[200, 170, 19],
[200, 170, 19],
[67, 68, 29],
[67, 68, 29],
[67, 68, 29]],
[[200, 170, 19],
[200, 170, 19],
[200, 170, 19],
[116, 146, 15],
[116, 146, 15],
[116, 146, 15]],
[[200, 170, 19],
[200, 170, 19],
[200, 170, 19],
[116, 146, 15],
[116, 146, 15],
[116, 146, 15]]]
for val in uniquify(inarray):
print(val)
输出结果是:
[135, 121, 81]
[67, 68, 29]
[200, 170, 19]
[116, 146, 15]
这就是你想要的吗?
如果你想要的是一个包含 list
的 list
,那就只需要:
array2d = list(uniquify(array3d))
如果你用简单的 set
而不是 unique_everseen
,那么这些会变成 tuple
而不是 list
,所以如果你需要一个 list
的 list
:
array2d = [list(val) for val in uniquify(array3d)]
根据你提到的“数组”样子,看来你在使用 numpy.ndarray
。如果是这样的话,这个问题就简单多了——你可以通过使用 .flat
属性把它变成一个一维的可迭代对象。要让它变得唯一,你只需要用 set
就可以了:
set(array.flat)
这样你就得到了一个集合,但你也可以很容易地从中得到一个列表:
list(set(array.flat))
这是怎么回事:
>>> array = np.zeros((10,12,42,53))
>>> list(set(array.flat))
[0.0]
顺便提一下,还有 np.unique
,它也能给你数组中的唯一元素。
>>> array = np.zeros((10,12),dtype=int)
>>> print array
[[0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0]]
>>> np.unique(array)
array([0])
>>> array[0,5] = 1
>>> array[4,10] = 42
>>> np.unique(array)
array([ 0, 1, 42])
我想我终于搞明白这个问题了:
from itertools import product
items = set(tuple(a[itr+(slice(None),)]) for itr in product(*[range(x) for x in a.shape[:-1]]))
print items
看起来可以用。呼!
这是怎么工作的——你想保留的三元组可以这样访问:
array[X,Y,:]
所以,我们只需要遍历所有的 X
和 Y
的组合。这正是 itertools.product
的用武之地。我们可以在任意维度中获取有效的 X
和 Y
:
[range(x) for x in array.shape[:-1]]
所以我们把这个传给 product:
indices_generator = product(*[range(x) for x in array.shape[:-1]])
现在我们有了生成前两个索引的东西——我们只需要构造一个元组,传给 __getitem__
,让 numpy 理解为 (X,Y,:)
——这很简单,我们已经从 indices_generator 得到了 (X,Y)
,只需要加上一个空的切片:
all_items = ( array[idx+(slice(None),)] for idx in indices_generator )
现在我们可以遍历 all_items,使用集合来寻找唯一的项:
unique_items = set(tuple(item) for item in all_items)
现在把这个再变回一个列表,或者 numpy 数组,或者你想要的任何形式,以便传回 PHP。