อัลกอริทึมการค้นหาจุดสูงสุดสำหรับ Python / SciPy


141

ฉันสามารถเขียนอะไรบางอย่างได้ด้วยตัวเองโดยการหาจุดตัดของอนุพันธ์อันดับหนึ่งหรือบางอย่างเป็นศูนย์ แต่ดูเหมือนว่าจะเป็นฟังก์ชันทั่วไปที่เพียงพอที่จะรวมอยู่ในไลบรารีมาตรฐาน มีใครรู้จักไหม

แอปพลิเคชันเฉพาะของฉันคืออาร์เรย์ 2 มิติ แต่โดยปกติแล้วจะใช้สำหรับการค้นหาจุดสูงสุดใน FFT เป็นต้น

โดยเฉพาะอย่างยิ่งในปัญหาประเภทนี้มียอดเขาที่แข็งแกร่งหลายจุดและมี "ยอด" ขนาดเล็กจำนวนมากที่เกิดจากเสียงรบกวนที่ควรละเว้น นี่เป็นเพียงตัวอย่างเท่านั้น ไม่ใช่ข้อมูลจริงของฉัน:

จุดสูงสุด 1 มิติ:

เอาต์พุต FFT พร้อมจุดสูงสุด

จุดสูงสุด 2 มิติ:

เรดอนแปลงเอาต์พุตที่มีจุดสูงสุดเป็นวงกลม

อัลกอริธึมการค้นหาจุดสูงสุดจะค้นหาตำแหน่งของยอดเขาเหล่านี้ (ไม่ใช่แค่ค่าของมัน) และตามหลักการแล้วจะหาจุดสูงสุดระหว่างตัวอย่างที่แท้จริงไม่ใช่แค่ดัชนีที่มีค่าสูงสุดเท่านั้นอาจใช้การแก้ไขกำลังสองหรืออะไรบางอย่าง

โดยปกติคุณจะดูแลเกี่ยวกับยอดที่แข็งแกร่งไม่กี่ดังนั้นพวกเขาต้องการอย่างใดอย่างหนึ่งได้รับเลือกเพราะพวกเขากำลังเหนือเกณฑ์ที่กำหนดหรือเพราะพวกเขาเป็นคนแรกnยอดรายการสั่งซื้อ, การจัดอันดับโดยกว้าง

อย่างที่บอกว่าฉันรู้วิธีเขียนอะไรแบบนี้ด้วยตัวเอง ฉันแค่ถามว่ามีฟังก์ชั่นหรือแพ็คเกจที่มีอยู่แล้วซึ่งทราบว่าทำงานได้ดีหรือไม่

อัปเดต:

ฉันแปลสคริปต์ MATLABและใช้งานได้ดีสำหรับกรณี 1-D แต่อาจจะดีกว่านี้

อัปเดตการอัปเดต:

sixtenbe สร้างเวอร์ชันที่ดีกว่าสำหรับเคส 1 มิติ


@endolith คุณมีไฟล์ MATLAB ดั้งเดิมที่คุณแปลเป็น python สำหรับสิ่งนี้หรือไม่? ขอบคุณ!
Spacey


2
อะไรประมาณนี้: docs.scipy.org/doc/scipy/reference/generated/…
dashesy

1
@endolith ฉันรู้ว่าคำถามนี้ค่อนข้างเก่า แต่ก็มีประโยชน์มากเมื่อเช้านี้ฉันใช้เวลาสองสามชั่วโมงfind_peaksดังนั้นฉันจึงเพิ่มคำตอบนี้ซึ่งอาจเป็นประโยชน์สำหรับการอ้างอิงในอนาคต (ฉันแน่ใจว่าคุณเจอสิ่งนี้แล้วตั้งแต่ปี 2009 แต่สำหรับคนอื่น + ตัวฉันเองเมื่อฉันจะถามตัวเองอีกครั้งในอีกไม่กี่ปี!)
Basj

คำตอบ:


85

ฟังก์ชันscipy.signal.find_peaksตามชื่อที่แนะนำมีประโยชน์สำหรับสิ่งนี้ แต่สิ่งสำคัญคือต้องเข้าใจพารามิเตอร์widthให้thresholdดีdistance และเหนือสิ่งอื่นใดprominenceเพื่อให้ได้การสกัดสูงสุดที่ดี

จากการทดสอบและเอกสารของฉันแนวคิดของความโดดเด่นคือ "แนวคิดที่มีประโยชน์" เพื่อรักษายอดที่ดีและทิ้งยอดที่มีเสียงดัง

คืออะไร(ภูมิประเทศ) โดดเด่น ? มันคือ"ความสูงขั้นต่ำที่จำเป็นในการลงไปเพื่อขึ้นจากยอดเขาไปยังภูมิประเทศที่สูงกว่า"ดังที่เห็นได้ที่นี่:

ป้อนคำอธิบายภาพที่นี่

แนวคิดคือ:

ความโดดเด่นยิ่งสูงความพีคยิ่ง "สำคัญ"

ทดสอบ:

ป้อนคำอธิบายภาพที่นี่

ฉันใช้ไซนัสแบบแปรผันความถี่ (ที่มีเสียงดัง) โดยตั้งใจเพราะมันแสดงให้เห็นถึงปัญหามากมาย เราจะเห็นว่าwidthพารามิเตอร์ไม่มีประโยชน์มากนักที่นี่เพราะถ้าคุณตั้งค่าต่ำสุดไว้widthสูงเกินไปก็จะไม่สามารถติดตามจุดสูงสุดที่ใกล้มากในส่วนความถี่สูงได้ หากคุณตั้งค่าwidthต่ำเกินไปคุณจะมีจุดสูงสุดที่ไม่ต้องการจำนวนมากทางด้านซ้ายของสัญญาณ ปัญหาเดียวกันกับdistance. thresholdเปรียบเทียบกับเพื่อนบ้านโดยตรงเท่านั้นซึ่งไม่เป็นประโยชน์ที่นี่ prominenceเป็นทางออกที่ดีที่สุด โปรดทราบว่าคุณสามารถรวมพารามิเตอร์เหล่านี้ได้มากมาย!

รหัส:

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()

นี่คือสิ่งที่ฉันเป็น แต่คุณเคยรู้หรือไม่ว่าการนำไปใช้งานใด ๆ ที่มีความโดดเด่นในอาร์เรย์ 2 มิติ?
Jason

@ Jason ฉันเพิ่งเจอPeak detection ในอาร์เรย์ 2 มิติและมันก็คุ้มค่าที่จะอ่าน!
Basj

44

ฉันกำลังมองหาปัญหาที่คล้ายกันและฉันพบว่าการอ้างอิงที่ดีที่สุดบางส่วนมาจากเคมี (จากจุดสูงสุดที่พบในข้อมูลจำเพาะมวล) สำหรับการตรวจสอบอย่างละเอียดที่ดีของอัลกอริทึมการหาจุดอ่านนี้ นี่เป็นหนึ่งในบทวิจารณ์ที่ชัดเจนที่สุดเกี่ยวกับเทคนิคการค้นหาจุดสูงสุดที่ฉันเคยใช้ (Wavelets เป็นวิธีที่ดีที่สุดสำหรับการค้นหาจุดสูงสุดของประเภทนี้ในข้อมูลที่มีเสียงดัง)

ดูเหมือนว่าจุดสูงสุดของคุณจะถูกกำหนดไว้อย่างชัดเจนและไม่ได้ซ่อนอยู่ในเสียงรบกวน ในกรณีนี้ฉันขอแนะนำให้ใช้อนุพันธ์ savtizky-golay ที่ราบรื่นเพื่อหาจุดสูงสุด (หากคุณเพียงแค่แยกความแตกต่างของข้อมูลข้างต้นคุณจะมีผลบวกที่ผิดพลาด) นี่เป็นเทคนิคที่มีประสิทธิภาพมากและค่อนข้างง่ายในการนำไปใช้ (คุณต้องมีคลาสเมทริกซ์ที่มีการดำเนินการพื้นฐาน) หากคุณพบเพียงแค่การข้ามศูนย์ของอนุพันธ์ SG แรกฉันคิดว่าคุณจะมีความสุข


2
ฉันกำลังมองหาโซลูชันสำหรับวัตถุประสงค์ทั่วไปไม่ใช่โซลูชันที่ใช้ได้เฉพาะกับรูปภาพเหล่านั้นเท่านั้น ฉันปรับสคริปต์ MATLAB เป็น Python และใช้งานได้ดี
endolith

1
ขวา Matlab เป็นแหล่งข้อมูลที่ดีสำหรับอัลกอริทึม บทใช้เทคนิคอะไร (BTW, SG เป็นเทคนิคทั่วไป)
พอล

2
ฉันเชื่อมโยงไว้ด้านบน โดยทั่วไปแล้วจะค้นหา maxima ในท้องถิ่นที่มีขนาดใหญ่กว่าเกณฑ์ที่กำหนดเหนือเพื่อนบ้านของตน มีวิธีการที่ดีกว่าอย่างแน่นอน
endolith

1
@ พอลฉันบุ๊กมาร์กหน้านั้น IYO และโดยสรุปเทคนิคเฉพาะใดที่คุณคิดว่าได้ผลดีที่สุดสำหรับธุรกิจการเลือกซื้อสูงสุดนี้
Spacey

เหตุใดศูนย์อนุพันธ์จึงดีกว่าการทดสอบว่าจุดกึ่งกลางของสามจุดมีขนาดใหญ่กว่าหรือเล็กกว่าของอีกสองจุด ฉันได้ใช้ sg transfor แล้วดูเหมือนว่าจะมีค่าใช้จ่ายเพิ่มเติม
kirill_igum

20

มีฟังก์ชั่นในชื่อ scipy scipy.signal.find_peaks_cwtซึ่งฟังดูแล้วเหมาะกับความต้องการของคุณ แต่ฉันไม่มีประสบการณ์กับมันจึงไม่สามารถแนะนำได้ ..

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


12
ใช่ไม่มีอยู่จริงเมื่อฉันถามสิ่งนี้และฉันก็ยังไม่แน่ใจว่าจะใช้มันอย่างไร
endolith

1
คุณได้เพิ่มสิ่งนี้เมื่อสักครู่แล้ว แต่มันใช้งานได้ดี ใช้ง่ายเหมือนพาย เพียงแค่ส่งผ่านอาร์เรย์และอาร์เรย์อื่น (เช่น. np.arange (1,10)) ซึ่งแสดงรายการความกว้างทั้งหมดของจุดสูงสุดที่คุณต้องการ ประโยชน์ที่ดีในการกรองยอดผอมหรือกว้างหากต้องการ ขอบคุณอีกครั้ง!
Miles

15

สำหรับผู้ที่ไม่แน่ใจเกี่ยวกับอัลกอริทึมการค้นหาสูงสุดที่จะใช้ใน Python ต่อไปนี้เป็นภาพรวมโดยย่อของทางเลือก: https://github.com/MonsieurV/py-findpeaks

ต้องการให้ตัวเองเทียบเท่ากับfindpeaksฟังก์ชันMatLab ฉันพบว่าฟังก์ชัน detect_peaksจาก Marcos Duarte เป็นสิ่งที่ดี

ใช้งานง่ายมาก:

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))

ซึ่งจะทำให้คุณ:

ตรวจหาผลลัพธ์


1
ตั้งแต่โพสต์นี้ถูกเขียนในฟังก์ชั่นได้ถูกเพิ่มเข้าไปfind_peaks scipy
onewhaleid

6

การตรวจจับจุดสูงสุดในสเปกตรัมด้วยวิธีที่เชื่อถือได้นั้นได้รับการศึกษามาพอสมควรตัวอย่างเช่นงานทั้งหมดเกี่ยวกับการสร้างแบบจำลองไซน์สำหรับสัญญาณเพลง / เสียงในยุค 80 มองหา "Sinusoidal Modeling" ในวรรณกรรม

หากสัญญาณของคุณสะอาดดังตัวอย่างง่ายๆ "ขอบางอย่างที่มีแอมพลิจูดสูงกว่าเพื่อนบ้าน N" แบบง่ายๆน่าจะใช้ได้ดีพอสมควร หากคุณมีสัญญาณที่มีเสียงดังวิธีง่ายๆ แต่ได้ผลคือการมองไปที่จุดสูงสุดของคุณในเวลาเพื่อติดตามจากนั้นคุณจะตรวจจับเส้นสเปกตรัมแทนที่จะเป็นยอดสเปกตรัม IOW คุณคำนวณ FFT บนหน้าต่างบานเลื่อนของสัญญาณของคุณเพื่อให้ได้ชุดของสเปกตรัมในเวลา (หรือที่เรียกว่า spectrogram) จากนั้นคุณจะดูวิวัฒนาการของจุดสูงสุดของสเปกตรัมในช่วงเวลาหนึ่ง


ดูจุดสูงสุดในเวลา? ตรวจจับเส้นสเปกตรัม? ฉันไม่แน่ใจว่านี่หมายถึงอะไร มันจะใช้ได้กับคลื่นสี่เหลี่ยมหรือไม่?
endolith

โอ้คุณกำลังพูดถึงการใช้ STFT แทน FFT คำถามนี้ไม่เกี่ยวกับ FFT โดยเฉพาะ นั่นเป็นเพียงตัวอย่าง มันเกี่ยวกับการหาจุดสูงสุดในอาร์เรย์ 1D หรือ 2D ทั่วไป
endolith

4

ฉันไม่คิดว่าสิ่งที่คุณกำลังมองหามีให้โดย SciPy ฉันจะเขียนโค้ดเองในสถานการณ์นี้

การแก้ไข spline และการปรับให้เรียบจาก scipy.interpolate นั้นค่อนข้างดีและอาจมีประโยชน์มากในการปรับจุดสูงสุดจากนั้นจึงหาตำแหน่งสูงสุด


16
ฉันขอโทษ แต่ฉันคิดว่านี่ควรเป็นความคิดเห็นไม่ใช่คำตอบ เพียงแค่แนะนำให้เขียนด้วยตัวเองพร้อมคำแนะนำที่คลุมเครือสำหรับฟังก์ชันที่อาจเป็นประโยชน์ (คำตอบของ Paul มีความเกี่ยวข้องมากขึ้นโดยบังเอิญ)
Ami Tavory

1

มีฟังก์ชันและวิธีการทางสถิติมาตรฐานในการค้นหาค่าผิดปกติของข้อมูลซึ่งอาจเป็นสิ่งที่คุณต้องการในกรณีแรก การใช้อนุพันธ์จะช่วยแก้ปัญหาของคุณได้ ฉันไม่แน่ใจสำหรับวิธีการที่แก้ทั้งฟังก์ชันต่อเนื่องและข้อมูลตัวอย่าง


1

ในการตรวจจับยอดเขาทั้งบวกและลบPeakDetectมีประโยชน์

from peakdetect import peakdetect

peaks = peakdetect(data, lookahead=20) 
# Lookahead is the distance to look ahead from a peak to determine if it is the actual peak. 
# Change lookahead as necessary 
higherPeaks = np.array(peaks[0])
lowerPeaks = np.array(peaks[1])
plt.plot(data)
plt.plot(higherPeaks[:,0], higherPeaks[:,1], 'ro')
plt.plot(lowerPeaks[:,0], lowerPeaks[:,1], 'ko')

PeakDetection


0

ประการแรกคำจำกัดความของ "จุดสูงสุด" นั้นคลุมเครือหากไม่มีข้อกำหนดเพิ่มเติม ตัวอย่างเช่นสำหรับซีรีส์ต่อไปนี้คุณจะเรียก 5-4-5 หนึ่งยอดหรือสอง?

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

ในกรณีนี้คุณจะต้องมีอย่างน้อยสองเกณฑ์: 1) เกณฑ์สูงเท่านั้นที่สามารถลงทะเบียนค่ามากเป็นจุดสูงสุดได้ และ 2) เกณฑ์ต่ำเพื่อให้ค่าสุดขั้วที่คั่นด้วยค่าเล็ก ๆ ด้านล่างจะกลายเป็นสองยอด

การตรวจจับจุดสูงสุดเป็นหัวข้อที่ได้รับการศึกษามาเป็นอย่างดีในวรรณกรรมทฤษฎีมูลค่าสูงสุดหรือที่เรียกว่า การใช้งานทั่วไปรวมถึงการระบุเหตุการณ์อันตรายโดยอาศัยการอ่านตัวแปรด้านสิ่งแวดล้อมอย่างต่อเนื่องเช่นการวิเคราะห์ความเร็วลมเพื่อตรวจจับเหตุการณ์พายุ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.