在Python中识别列表中的峰值气Elevation

1 投票
3 回答
753 浏览
提问于 2025-04-17 20:32

我需要定义一个函数,用来识别海拔数据中的峰值。峰值的定义是:一个值比它周围的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 个回答

1

因为这看起来像是作业,所以我会给出两个提示,而不是提供完整的解决方案:

  1. 写两个嵌套的循环,一个用来处理行,另一个用来处理列(要仔细考虑循环的边界应该是什么)。
  2. 要检查 a 是否大于 bc,可以写 a > b and a > c
2

你可以使用一些Python特有的写法,让这段代码更容易理解。我们可以把问题分成几个部分:

  1. 给定一个矩阵,返回所有的核心(非边界)索引;
  2. 给定一个在矩阵核心内的坐标,返回一个包含它邻居坐标的列表;
  3. 给定一对坐标和一个矩阵,检查这个坐标是否是一个峰值。

第一步可以这样实现:

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

注意我们的索引是从零开始的。

0

这是一个解决方案。我没有考虑边界上的值,因为你明确说过,最大值应该大于它周围的“八个”邻居。

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

撰写回答