如何在Python中简化数组为唯一值

1 投票
4 回答
6127 浏览
提问于 2025-04-17 14:08

因为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 个回答

0

假设你的 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]]
1

看看 itertools 文档里的例子。里面有 flattenunique_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]

这就是你想要的吗?

如果你想要的是一个包含 listlist,那就只需要:

array2d = list(uniquify(array3d))

如果你用简单的 set 而不是 unique_everseen,那么这些会变成 tuple 而不是 list,所以如果你需要一个 listlist

array2d = [list(val) for val in uniquify(array3d)]
4

根据你提到的“数组”样子,看来你在使用 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,:]

所以,我们只需要遍历所有的 XY 的组合。这正是 itertools.product 的用武之地。我们可以在任意维度中获取有效的 XY

[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。

撰写回答