Thuật toán tìm đỉnh cho Python / SciPy


136

Tôi có thể tự viết một cái gì đó bằng cách tìm các điểm giao nhau bằng 0 của đạo hàm đầu tiên hoặc một cái gì đó, nhưng có vẻ như đó là một hàm đủ phổ biến được đưa vào các thư viện chuẩn. Có ai biết ai không?

Ứng dụng cụ thể của tôi là một mảng 2D, nhưng thông thường nó sẽ được sử dụng để tìm các đỉnh trong FFT, v.v.

Cụ thể, trong các loại vấn đề này, có nhiều đỉnh mạnh và sau đó rất nhiều "đỉnh" nhỏ hơn gây ra bởi tiếng ồn nên bị bỏ qua. Đây chỉ là những ví dụ; không phải dữ liệu thực tế của tôi:

Đỉnh 1 chiều:

Đầu ra FFT với các đỉnh

Đỉnh 2 chiều:

Đầu ra biến đổi radon với đỉnh được khoanh tròn

Thuật toán tìm đỉnh sẽ tìm vị trí của các đỉnh này (không chỉ giá trị của chúng) và lý tưởng nhất là tìm đỉnh cực đại mẫu thực sự, không chỉ là chỉ số có giá trị tối đa, có thể sử dụng phép nội suy bậc hai hoặc một cái gì đó.

Thông thường bạn chỉ quan tâm đến một vài đỉnh núi mạnh, vì vậy họ hoặc muốn được lựa chọn bởi vì họ đang ở trên một ngưỡng nhất định, hoặc bởi vì họ là người đầu tiên n đỉnh của một danh sách đặt hàng, xếp hạng bởi biên độ.

Như tôi đã nói, tôi biết cách tự viết một cái gì đó như thế này. Tôi chỉ hỏi nếu có một chức năng hoặc gói có sẵn mà hoạt động tốt.

Cập nhật:

Tôi đã dịch một tập lệnh MATLAB và nó hoạt động tốt cho trường hợp 1-D, nhưng có thể tốt hơn.

Cập nhật cập nhật:

sixtenbe đã tạo ra một phiên bản tốt hơn cho trường hợp 1-D.


@endolith Bạn có tệp MATLAB gốc mà bạn đã dịch sang python cho việc này không? Cảm ơn!
Spacey



1
@endolith Tôi biết câu hỏi này khá cũ, nhưng nó khá hữu ích;) Tôi đã dành vài giờ sáng nay find_peaks, vì vậy tôi đã thêm câu trả lời này có thể hữu ích để tham khảo trong tương lai. (Tôi chắc chắn rằng bạn đã tìm thấy điều này từ năm 2009 nhưng nó dành cho người khác + chính tôi khi tôi sẽ tự hỏi lại câu hỏi sau vài năm nữa!)
Basj

Câu trả lời:


74

Hàm scipy.signal.find_peaks, như tên của nó cho thấy, rất hữu ích cho việc này. Nhưng điều quan trọng là phải hiểu tốt các thông số của nó width, threshold, distance và trên tất cảprominence để có được một khai thác đỉnh tốt.

Theo các thử nghiệm của tôi và tài liệu, khái niệm về sự nổi bật là "khái niệm hữu ích" để giữ các đỉnh tốt và loại bỏ các đỉnh ồn ào.

Sự nổi bật (địa hình) là gì? Đó là "chiều cao tối thiểu cần thiết để hạ xuống để đi từ đỉnh lên bất kỳ địa hình cao hơn nào" , như có thể thấy ở đây:

nhập mô tả hình ảnh ở đây

Ý tưởng là:

Độ nổi bật càng cao, đỉnh "càng quan trọng".

Kiểm tra:

nhập mô tả hình ảnh ở đây

Tôi đã sử dụng một hình sin thay đổi tần số (ồn ào) vì nó cho thấy nhiều khó khăn. Chúng ta có thể thấy rằng widththam số này không hữu ích ở đây vì nếu bạn đặt mức tối thiểu widthquá cao, thì nó sẽ không thể theo dõi các đỉnh rất gần trong phần tần số cao. Nếu bạn đặt widthquá thấp, bạn sẽ có nhiều đỉnh không mong muốn ở phần bên trái của tín hiệu. Cùng một vấn đề với distance. thresholdchỉ so sánh với hàng xóm trực tiếp, không hữu ích ở đây. prominencelà một trong đó đưa ra giải pháp tốt nhất. Lưu ý rằng bạn có thể kết hợp nhiều tham số này!

Mã số:

import numpy as np
import matplotlib.pyplot as plt 
from scipy.signal import find_peaks

x = np.sin(2*np.pi*(2**np.linspace(2,10,1000))*np.arange(1000)/48000) + np.random.normal(0, 1, 1000) * 0.15
peaks, _ = find_peaks(x, distance=20)
peaks2, _ = find_peaks(x, prominence=1)      # BEST!
peaks3, _ = find_peaks(x, width=20)
peaks4, _ = find_peaks(x, threshold=0.4)     # Required vertical distance to its direct neighbouring samples, pretty useless
plt.subplot(2, 2, 1)
plt.plot(peaks, x[peaks], "xr"); plt.plot(x); plt.legend(['distance'])
plt.subplot(2, 2, 2)
plt.plot(peaks2, x[peaks2], "ob"); plt.plot(x); plt.legend(['prominence'])
plt.subplot(2, 2, 3)
plt.plot(peaks3, x[peaks3], "vg"); plt.plot(x); plt.legend(['width'])
plt.subplot(2, 2, 4)
plt.plot(peaks4, x[peaks4], "xk"); plt.plot(x); plt.legend(['threshold'])
plt.show()

Đây là những gì tôi đang theo đuổi. Nhưng bạn có biết bất kỳ triển khai nào tìm thấy sự nổi bật trong mảng 2D không?
Jason

43

Tôi đang xem xét một vấn đề tương tự, và tôi đã tìm thấy một số tài liệu tham khảo tốt nhất đến từ hóa học (từ các đỉnh tìm thấy trong dữ liệu khối lượng lớn). Để xem xét kỹ lưỡng về các thuật toán tìm đỉnh đạt được, hãy đọc cái này . Đây là một trong những đánh giá rõ ràng nhất về các kỹ thuật tìm kiếm đỉnh cao mà tôi đã chạy qua. (Wavelets là tốt nhất để tìm các đỉnh thuộc loại này trong dữ liệu nhiễu.).

Có vẻ như các đỉnh của bạn được xác định rõ ràng và không bị ẩn trong tiếng ồn. Đó là trường hợp tôi khuyên bạn nên sử dụng các dẫn xuất savtizky-golay trơn tru để tìm các đỉnh (Nếu bạn chỉ phân biệt dữ liệu ở trên, bạn sẽ có một mớ hỗn độn dương tính giả.). Đây là một kỹ thuật rất hiệu quả và khá dễ thực hiện (bạn cần một lớp ma trận với các thao tác cơ bản). Nếu bạn chỉ đơn giản tìm thấy điểm giao nhau của đạo hàm SG đầu tiên, tôi nghĩ bạn sẽ hạnh phúc.


2
Tôi đang tìm kiếm một giải pháp cho mục đích chung, không phải là một giải pháp chỉ hoạt động trên những hình ảnh cụ thể đó. Tôi đã điều chỉnh một kịch bản MATLAB cho Python và nó hoạt động tốt.
endolith

1
Ngay trên. Matlab là một nguồn tốt cho các thuật toán. Kịch bản sử dụng kỹ thuật gì? (BTW, SG là một kỹ thuật có mục đích rất chung).
Paul

2
Tôi đã liên kết nó ở trên. Về cơ bản, nó chỉ tìm kiếm các cực đại cục bộ lớn hơn một ngưỡng nhất định trên các lân cận của chúng. Chắc chắn có những phương pháp tốt hơn.
endolith

1
@Paul Tôi đánh dấu trang đó. IYO và tóm lại, kỹ thuật cụ thể nào bạn nghĩ là hiệu quả nhất cho doanh nghiệp chọn cao điểm này?
Spacey

tại sao các số không của đạo hàm tốt hơn là chỉ kiểm tra nếu một trong ba điểm giữa lớn hơn hoặc nhỏ hơn hai điểm còn lại. tôi đã áp dụng sg transfor, có vẻ như là một chi phí thêm.
kirill_igum

20

Có một chức năng trong scipy có tên scipy.signal.find_peaks_cwtphù hợp với nhu cầu của bạn, tuy nhiên tôi không có kinh nghiệm với nó nên tôi không thể khuyên bạn nên ..

http://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.find_peaks_cwt.html


12
Vâng, điều đó đã không tồn tại khi tôi hỏi điều này và tôi vẫn không chắc chắn cách sử dụng nó
endolith

1
Bạn đã thêm điều này một thời gian trước, nhưng điều này làm việc tuyệt vời. Sử dụng nó là đơn giản như chiếc bánh. Chỉ cần vượt qua trong mảng và một mảng khác (ví dụ: np.arange (1,10)) liệt kê tất cả các chiều rộng của các đỉnh bạn muốn; lợi ích tốt đẹp để lọc cho đỉnh gầy hoặc rộng nếu cần. Cảm ơn một lần nữa!
Miles

15

Đối với những người không chắc chắn về việc sử dụng thuật toán tìm đỉnh nào trong Python, đây là tổng quan nhanh về các lựa chọn thay thế: https://github.com/MonsieurV/py-findpeaks

Muốn bản thân tương đương với findpeakschức năng MatLab , tôi đã thấy rằng hàm Det_peaks từ Marcos Duarte là một sản phẩm tốt.

Khá dễ sử dụng:

import numpy as np
from vector import vector, plot_peaks
from libs import detect_peaks
print('Detect peaks with minimum height and distance filters.')
indexes = detect_peaks.detect_peaks(vector, mph=7, mpd=2)
print('Peaks are: %s' % (indexes))

Mà sẽ cung cấp cho bạn:

kết quả dò tìm


1
Kể từ khi bài viết này được viết, find_peakschức năng đã được thêm vào scipy.
onewhaleid

6

Phát hiện các đỉnh trong phổ theo cách đáng tin cậy đã được nghiên cứu khá nhiều, ví dụ như tất cả các công việc về mô hình hình sin cho tín hiệu âm nhạc / âm thanh trong những năm 80. Tìm kiếm "Mô hình hình sin" trong tài liệu.

Nếu tín hiệu của bạn rõ ràng như ví dụ, một "đơn giản cho tôi thứ gì đó có biên độ cao hơn N lân cận" sẽ hoạt động tốt. Nếu bạn có tín hiệu nhiễu, một cách đơn giản nhưng hiệu quả là xem xét các đỉnh của bạn kịp thời, để theo dõi chúng: sau đó bạn phát hiện các vạch quang phổ thay vì các đỉnh phổ. IOW, bạn tính toán FFT trên cửa sổ trượt tín hiệu của bạn, để có được một tập hợp phổ theo thời gian (còn được gọi là phổ). Sau đó, bạn nhìn vào sự phát triển của đỉnh phổ theo thời gian (tức là trong các cửa sổ liên tiếp).


Nhìn vào đỉnh trong thời gian? Phát hiện đường quang phổ? Tôi không chắc điều này có nghĩa là gì. Nó sẽ làm việc cho sóng vuông?
endolith

Ồ, bạn đang nói về việc sử dụng STFT thay vì FFT. Câu hỏi này không phải là về FFT cụ thể; đó chỉ là một ví dụ. Đó là về việc tìm các đỉnh trong bất kỳ mảng 1D hoặc 2D chung nào.
endolith

4

Tôi không nghĩ rằng những gì bạn đang tìm kiếm được cung cấp bởi SciPy. Tôi sẽ tự viết mã, trong tình huống này.

Nội suy spline và làm mịn từ scipy.interpolate khá đẹp và có thể khá hữu ích trong việc điều chỉnh các đỉnh và sau đó tìm vị trí tối đa của chúng.


16
Tôi xin lỗi, nhưng tôi nghĩ rằng đây nên là một bình luận, không phải là một câu trả lời. Nó chỉ đề nghị tự viết nó, với một gợi ý mơ hồ cho các chức năng có thể hữu ích (những câu trả lời của Paul có liên quan nhiều hơn, tình cờ).
Ami Tavory

1

Có các hàm và phương pháp thống kê tiêu chuẩn để tìm các ngoại lệ đối với dữ liệu, đây có thể là những gì bạn cần trong trường hợp đầu tiên. Sử dụng các công cụ phái sinh sẽ giải quyết lần thứ hai của bạn. Tuy nhiên, tôi không chắc chắn về phương pháp giải quyết cả chức năng liên tục và dữ liệu được lấy mẫu.


0

Trước tiên, định nghĩa về "đỉnh" là mơ hồ nếu không có thêm thông số kỹ thuật. Ví dụ, đối với loạt sau, bạn sẽ gọi 5-4-5 một hoặc hai đỉnh?

1-2-1-2-1-1-5-4-5-1-1-5-1

Trong trường hợp này, bạn sẽ cần ít nhất hai ngưỡng: 1) chỉ một ngưỡng cao ở trên mức có thể đăng ký giá trị cực cao làm đỉnh; và 2) ngưỡng thấp để các giá trị cực trị được phân tách bằng các giá trị nhỏ bên dưới nó sẽ trở thành hai đỉnh.

Phát hiện cực đại là một chủ đề được nghiên cứu kỹ trong tài liệu Lý thuyết giá trị cực đoan, còn được gọi là "giải mã các giá trị cực đoan". Các ứng dụng điển hình của nó bao gồm xác định các sự kiện nguy hiểm dựa trên việc đọc các biến môi trường liên tục, ví dụ như phân tích tốc độ gió để phát hiện các sự kiện bão.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.