如何将numpy数组中具有相同值的元素分组到不同的numpy数组中

7 投票
3 回答
16031 浏览
提问于 2025-04-18 01:18

大家好,我是个Python新手。不过,我现在有一个比较大的项目要做。这个项目是一个表面流动模型,使用的是细胞自动机。顺便说一下,我还想在模型中加入建筑物的屋顶。想象一下,你有一个ASCII文件,用1表示建筑物,0表示其他地方。就只有这两种状态。现在,我想找到所有相邻的单元格,这些单元格表示同一栋建筑,并把它们的信息(也就是y和x坐标,还有一个可能是高度的信息,总共三列)存储在一个单独的建筑数组里。需要注意的是,建筑物的形状可以是各种各样的,但对角线相连的单元格不算在同一栋建筑里。所以,只有北、南、西、东方向相邻的单元格才能算作同一栋建筑。

我做了一些功课,也在网上查了资料,但到目前为止还没有找到令人满意的答案。

例如:初始的土地覆盖数组:

([0,0,0,0,0,0,0]
 [0,0,1,0,0,0,0]
 [0,1,1,1,0,1,1]
 [0,1,0,1,0,0,1]
 [0,0,0,0,0,0,0])

输出(我需要知道我初始数组中单元格的坐标):

 building_1=([1,2],[2,1],[2,2],[2,3],[3,1],[3,3])
 building_2=([2,5],[2,6],[3,6])

任何帮助都非常感谢!

3 个回答

0

看起来这个函数正好符合你的需求(来自numpy文档):

numpy.argwhere(a):

找到数组中非零元素的索引,并按元素分组。

>>> x = np.arange(6).reshape(2,3)
>>> x
array([[0, 1, 2],
       [3, 4, 5]])
>>> np.argwhere(x>1)
array([[0, 2],
       [1, 0],
       [1, 1],
       [1, 2]])

另外,你的使用场景似乎需要用返回的坐标来索引数组。

argwhere的输出不适合用来索引数组。为此,建议使用where(a)。

你可以试试numpy.where

1

谢谢大家的精彩回答!这里有一点小更正。如果你看到土地覆盖的数组,我实际上没有用0作为背景信息,而是用-9999(因为0对GIS的人来说太重要了)。我忘了提这点。不过多亏了machine yearning的提示,我通过将所有-9999替换为0来解决这个问题,代码是landcover = np.where(landcover > -9999, landcover, 0)。这样我就可以使用标签了。我的实际目的是找到最低的单元格,并将其指定为出口。如果有人有更有效的方法,请告诉我!

import numpy as np
from scipy.ndimage import label

原始数据集中,背景信息是-9999,建筑单元是1。

landcover = np.array([[-9999,-9999,-9999,-9999,-9999,-9999,1], 
                       [-9999,-9999,1,-9999,-9999,-9999,-9999],
                       [-9999,1,1,1,-9999,1,1], 
                       [-9999,1,-9999,1,-9999,-9999,1], 
                       [-9999,-9999,-9999,-9999,-9999,-9999,-9999]],dtype=int)

这里有一张随机的数字高程图。

DEM = np.array([[7,4,3,2,4,5,4], 
               [4,5,5,3,5,6,7],
               [2,6,4,7,4,4,4],
               [3,7,8,8,10,9,7],
               [2,5,7,7,9,10,8]],dtype=float)

我把所有的-9999都改成了0,这样就可以使用标签了,感谢machine yearning。

 landcover = np.where(landcover > -9999, landcover, 0)

然后我给不同的建筑物打上标签,并计算这些标签的数量,感谢Warren Weckesser,其余的基本上都是你的了,谢谢!

 lbl, nlbls = label(landcover)
 bldg_number = range(1, nlbls+1)
 bldg_coord = [np.column_stack(where(lbl == k)) for k in bldg_no]
 outlets=np.zeros([nlbls,3])

我正在遍历bldg_coord列表,以确定最低的单元格,这些单元格将被指定为出口。

 for i in range(0, nlbls):
     building=np.zeros([bldg_coord[i].shape[0],3])
     for j in range(0,bldg_coord[i].shape[0]):
         building[j][0]=bldg_coord[i][j][0]
         building[j][1]=bldg_coord[i][j][1]
         building[j][2]=DEM[bldg_coord[i][j][0],bldg_coord[i][j][1]]

我根据每个建筑单元在数字高程图中的信息,将建筑数组按升序排序,以找到最低的建筑单元。

  building=building[building[:,2].argsort()]

最低的建筑单元将用作雨水的屋顶出口。

  outlets[i][0]=building[0][0]
  outlets[i][1]=building[0][1]
  outlets[i][2]=bldg_coord[i].shape[0]

这是输出结果。前两列是土地覆盖数组中的索引,最后一列是相邻建筑单元的数量。

>>> outlets
array([[ 0.,  6.,  1.],
       [ 2.,  2.,  6.],
       [ 2.,  5.,  3.]])
7

你可以使用 label 函数,这个函数来自 scipy.ndimage,来识别不同的建筑物。

这里有一个示例数组,里面包含了两个建筑:

In [57]: a
Out[57]: 
array([[0, 0, 0, 0, 0, 0, 0],
       [0, 0, 1, 0, 0, 0, 0],
       [0, 1, 1, 1, 0, 1, 1],
       [0, 1, 0, 1, 0, 0, 1],
       [0, 0, 0, 0, 0, 0, 0]])

首先导入 label

In [58]: from scipy.ndimage import label

然后把 label 应用到 a 上。它会返回两个值:一个是标记位置的数组,另一个是找到的不同对象(在这个例子中是建筑物)的数量。

In [59]: lbl, nlbls = label(a)

In [60]: lbl
Out[60]: 
array([[0, 0, 0, 0, 0, 0, 0],
       [0, 0, 1, 0, 0, 0, 0],
       [0, 1, 1, 1, 0, 2, 2],
       [0, 1, 0, 1, 0, 0, 2],
       [0, 0, 0, 0, 0, 0, 0]], dtype=int32)

In [61]: nlbls
Out[61]: 2

如果你想获取某个建筑的坐标,可以使用 np.where。比如:

In [64]: np.where(lbl == 2)
Out[64]: (array([2, 2, 3]), array([5, 6, 6]))

它会返回一个元组,里面有多个数组;第 k 个数组保存了第 k 个维度的坐标。你可以使用 np.column_stack 把这些坐标合并成一个数组:

 In [65]: np.column_stack(np.where(lbl == 2))
 Out[65]: 
 array([[2, 5],
        [2, 6],
        [3, 6]])

你可能想要一个包含所有坐标数组的列表。这里有一种方法可以创建这样的列表。

为了方便,首先创建一个标签列表:

In [66]: labels = range(1, nlbls+1)

In [67]: labels
Out[67]: [1, 2]

使用列表推导式来创建坐标数组的列表。

In [68]: coords = [np.column_stack(where(lbl == k)) for k in labels]

In [69]: coords
Out[69]: 
[array([[1, 2],
       [2, 1],
       [2, 2],
       [2, 3],
       [3, 1],
       [3, 3]]),
 array([[2, 5],
       [2, 6],
       [3, 6]])]

现在你的建筑数据在 labelscoords 中。例如,第一个建筑被标记为 labels[0],它的坐标在 coords[0] 中:

In [70]: labels[0]
Out[70]: 1

In [71]: coords[0]
Out[71]: 
array([[1, 2],
       [2, 1],
       [2, 2],
       [2, 3],
       [3, 1],
       [3, 3]])

撰写回答