如何将numpy数组中具有相同值的元素分组到不同的numpy数组中
大家好,我是个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 个回答
看起来这个函数正好符合你的需求(来自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
。
谢谢大家的精彩回答!这里有一点小更正。如果你看到土地覆盖的数组,我实际上没有用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.]])
你可以使用 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]])]
现在你的建筑数据在 labels
和 coords
中。例如,第一个建筑被标记为 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]])