在Python中识别列表中的峰值气Elevation
我需要定义一个函数,用来识别海拔数据中的峰值。峰值的定义是:一个值比它周围的8个值都要高(上面三个,左右各一个,下面三个)。
不允许导入任何库。
在下面的数据中:87是一个峰值,因为它比54、82、72、49、69、62、71、61都要高。
data = [ [20, 54, 50, 64, 60, 63, 60, 48, 20, 20],
[20, 56, 72, 76, 72, 52, 62, 53, 20, 20],
[20, 52, 62, 81, 67, 48, 67, 52, 23, 20],
[20, 54, 54, 82, 72, 42, 64, 50, 22, 20],
[20, 53, 49, 87, 69, 47, 48, 49, 21, 20],
[20, 20, 62, 71, 61, 36, 28, 31, 22, 20],
[20, 20, 20, 20, 20, 22, 21, 28, 24, 20],
[20, 20, 20, 20, 20, 20, 20, 20, 20, 20],
[20, 20, 20, 20, 20, 20, 20, 20, 20, 20],
[20, 20, 20, 20, 20, 20, 20, 20, 20, 20]
]
我有个大概的想法怎么做,但不太确定怎么写代码。
def peaks(list1):
for item in list1:
# if item > (item[row][column-1] and item[row][column+1] and item[row-1][column-1] and \
# item[row-1][column] and item[row-1][column+1] and item[row+1][column-1] and item[row+1][column] and item[row+1][column+1]):
我该如何把那段注释的内容转换成正确的Python代码呢?
编辑:好吧,为什么这个不工作?列表coords似乎被覆盖了,我搞不清楚为什么。
返回的结果应该是:[(2, 6), (4, 3)]
def peaks(list1):
for row in range(len(list1)):
for column in range (len(list1[row])):
if row != 0 and row != len(list1)-1 \
and column != 0 and column != len(list1[row])-1:
coords = []
if max(list1[row-1][column-1:column+2] + \
[list1[row][column-1]] + [list1[row][column+1]] + \
list1[row+1][column-1:column+2]) < list1[row][column]:
coords.append((row, column))
return coords
3 个回答
因为这看起来像是作业,所以我会给出两个提示,而不是提供完整的解决方案:
- 写两个嵌套的循环,一个用来处理行,另一个用来处理列(要仔细考虑循环的边界应该是什么)。
- 要检查
a
是否大于b
和c
,可以写a > b and a > c
。
你可以使用一些Python特有的写法,让这段代码更容易理解。我们可以把问题分成几个部分:
- 给定一个矩阵,返回所有的核心(非边界)索引;
- 给定一个在矩阵核心内的坐标,返回一个包含它邻居坐标的列表;
- 给定一对坐标和一个矩阵,检查这个坐标是否是一个峰值。
第一步可以这样实现:
def core_indexes(matrix):
row = matrix[0]
lines = len(matrix)
columns = len(row)
for i in range(1, lines - 1):
for j in range(1, columns - 1):
yield i, j
注意,这个方法没有返回值,而是用yield来生成值。使用yield的函数或方法是Python中写生成器的方式,也就是说,每次调用这个特殊的函数时,它会返回一个序列中的下一个值。
第二步更简单,我们可以用同样的技巧。
def neighbor_indexes(i, j):
for r in range(i - 1, i + 2):
for c in range(j - 1, j + 2):
if (i, j) != (r, c):
yield r, c
接下来(第三步),我们需要检查给定的位置是否是一个峰值。一个值只有在它大于所有邻居的最大值时,才算是峰值。我们来写这个:
def is_peak(i, j, matrix):
max_neighbor = max([matrix[i][j] for i, j in neighbor_indexes(i, j)])
return matrix[i][j] > max_neighbor
注意下面的语句:
[matrix[i][j] for i, j in neighbor_indexes(i, j)]
这叫做列表推导式。它的意思是“为每一对由邻居索引函数返回的i, j,构建一个包含matrix[i,j]的列表”。这是一种很Python风格的写法。
现在,是时候用这些函数来扫描矩阵了。可以这样做:
for i, j in core_indexes(data):
if is_peak(i, j, data):
print("%d at [%d, %d] is a peak" % (data[i][j], i, j))
它会打印:
67 at [2, 6] is a peak
87 at [4, 3] is a peak
注意我们的索引是从零开始的。
这是一个解决方案。我没有考虑边界上的值,因为你明确说过,最大值应该大于它周围的“八个”邻居。
for idxR in range(len(data)):
for idxC in range(len(data[idxR])):
if idxR != 0 and idxR != len(data)-1 \
and idxC != 0 and idxC != len(data[idxR])-1:
if max(data[idxR-1][idxC-1:idxC+2] +
[data[idxR][idxC-1]] + [data[idxR][idxC+1]] + \
data[idxR+1][idxC-1:idxC+2]) < data[idxR][idxC]:
print '%d is a maximum' % data[idxR][idxC]
好的。这是根据你的评论更新的版本,你提到希望能得到坐标。你版本的问题在于,你在循环内部重置了coords的值,并且在遍历所有元素之前就返回了。coords应该在一开始就定义好,并在最后返回。
def peaks(list1):
coords = []
for row in range(len(list1)):
for column in range (len(list1[row])):
if row != 0 and row != len(list1)-1 \
and column != 0 and column != len(list1[row])-1:
if max(list1[row-1][column-1:column+2] + \
[list1[row][column-1]] + [list1[row][column+1]] + \
list1[row+1][column-1:column+2]) < list1[row][column]:
coords.append((row, column))
return coords