डेटा की एक श्रृंखला में स्थानीय चोटियों / घाटियों को कैसे खोजें?


17

यहाँ मेरा प्रयोग है:

मैं क्वांटम पैकेज findPeaksमें फ़ंक्शन का उपयोग कर रहा हूं :

मैं एक सहिष्णुता 5 के भीतर "स्थानीय" चोटियों का पता लगाना चाहता हूं, जो कि स्थानीय चोटियों से श्रृंखला के गिरने के बाद पहले स्थान 5 हैं:

aa=100:1
bb=sin(aa/3)
cc=aa*bb
plot(cc, type="l")
p=findPeaks(cc, 5)
points(p, cc[p])
p

आउटपुट है

[1] 3 22 41

यह गलत लगता है, क्योंकि मैं 3 से अधिक "स्थानीय चोटियों" की उम्मीद कर रहा हूं ...

कोई विचार?


मेरे पास यह पैकेज नहीं है। क्या आप संख्यात्मक दिनचर्या का उपयोग कर सकते हैं?
एडमो

findPeaksमेरे उत्तर में पूर्ण स्रोत कोड @Adam में दिखाई देता है। BTW, पैकेज "क्वांटमॉड" है
whuber

क्रॉस R-SIG-Finance पर पोस्ट किया गया ।
जोशुआ उलरिक

जवाबों:


8

इस कोड का स्रोत R प्रांप्ट पर अपना नाम लिखकर प्राप्त किया जाता है। आउटपुट है

function (x, thresh = 0) 
{
    pks <- which(diff(sign(diff(x, na.pad = FALSE)), na.pad = FALSE) < 0) + 2
    if (!missing(thresh)) {
        pks[x[pks - 1] - x[pks] > thresh]
    }
    else pks
}

परीक्षण x[pks - 1] - x[pks] > threshप्रत्येक चोटी के मूल्य की तुलना श्रृंखला में तुरंत सफल होने वाले मूल्य से करता है (श्रृंखला में अगले गर्त में नहीं)। यह चोटी के तुरंत बाद फ़ंक्शन के ढलान के आकार का (क्रूड) अनुमान का उपयोग करता है और केवल उन चोटियों का चयन करता है जहां यह ढलान threshआकार से अधिक है। आपके मामले में, परीक्षण पास करने के लिए केवल पहले तीन चोटियाँ पर्याप्त रूप से तेज हैं। आप डिफ़ॉल्ट का उपयोग करके सभी चोटियों का पता लगाएंगे:

> findPeaks(cc)
[1]  3 22 41 59 78 96

30

मैं व्हिबर की प्रतिक्रिया से सहमत हूं, लेकिन कोड के "+2" भाग को जोड़ना चाहता था, जो कि नए पाए गए शिखर को वास्तव में 'ओवरशूट' से मिलाने के लिए सूचकांक को स्थानांतरित करने का प्रयास करता है और "+1" होना चाहिए। उदाहरण के लिए हाथ में हम प्राप्त करते हैं:

> findPeaks(cc)
[1]  3 22 41 59 78 96

जब हम एक ग्राफ पर इन पाया चोटियों पर प्रकाश डाला (बोल्ड लाल): यहाँ छवि विवरण दर्ज करें

हम देखते हैं कि वे वास्तविक शिखर से लगातार 1 बिंदु दूर हैं।

consequenty

pks[x[pks - 1] - x[pks] > thresh]

होना चाहिए pks[x[pks] - x[pks + 1] > thresh]याpks[x[pks] - x[pks - 1] > thresh]

बड़ा अद्यतन

मेरी अपनी खोज के बाद एक पर्याप्त चोटी खोजने के लिए मैंने यह लिखा है:

find_peaks <- function (x, m = 3){
    shape <- diff(sign(diff(x, na.pad = FALSE)))
    pks <- sapply(which(shape < 0), FUN = function(i){
       z <- i - m + 1
       z <- ifelse(z > 0, z, 1)
       w <- i + m + 1
       w <- ifelse(w < length(x), w, length(x))
       if(all(x[c(z : i, (i + 2) : w)] <= x[i + 1])) return(i + 1) else return(numeric(0))
    })
     pks <- unlist(pks)
     pks
}

एक 'चोटी' को स्थानीय मैक्सीमा के रूप में परिभाषित किया जाता है, जिसके mदोनों ओर बिंदु छोटे होते हैं। इसलिए, बड़ा पैरामीटर m, अधिक कठोर पीक फंडिंग प्रक्रिया है। इसलिए:

find_peaks(cc, m = 1)
[1]  2 21 40 58 77 95

समारोह भी किसी भी अनुक्रमिक वेक्टर के स्थानीय न्यूनतम खोजने के लिए इस्तेमाल किया जा सकता xके माध्यम से find_peaks(-x)

नोट: मैंने अब फ़ंक्शन को gitHub पर रखा है अगर किसी को इसकी आवश्यकता है: https://github.com/stas-g/findPeaks


6

Eek: लघु अद्यतन। मुझे कोड की दो पंक्तियों को बदलना था, सीमाएं (-1 और +1 जोड़ें) Stas_G के फ़ंक्शन के साथ समकक्षता तक पहुंचने के लिए (यह वास्तविक डेटा-सेटों में कुछ बहुत अधिक 'अतिरिक्त चोटियों' को ढूंढ रहा था)। मेरे मूल पद से किसी के लिए क्षमा याचना बहुत मामूली रूप से भटकती है।

मैं काफी समय से Stas_g की पाइक एल्गोरिथ्म का उपयोग कर रहा हूं। इसकी सादगी के कारण मेरी बाद की परियोजनाओं में से यह मेरे लिए फायदेमंद था। फिर भी, मुझे गणना के लिए लाखों बार इसका उपयोग करने की आवश्यकता थी, इसलिए मैंने इसे आरसीपीपी (आरसीपी पैकेज देखें) में फिर से लिखा। यह लगभग 6x तेज है तो सरल परीक्षणों में आर संस्करण। अगर किसी को दिलचस्पी है तो मैंने नीचे कोड जोड़ा है। उम्मीद है कि मैं किसी की मदद, चीयर्स!

कुछ मामूली कैविएट। यह फ़ंक्शन R कोड के रिवर्स ऑर्डर में पीक इंडेक्स लौटाता है। इसके लिए एक घर के अंदर C ++ साइन फंक्शन की आवश्यकता होती है, जिसे मैंने शामिल किया। यह पूरी तरह से अनुकूलित नहीं किया गया है, लेकिन किसी भी आगे के प्रदर्शन लाभ की उम्मीद नहीं है।

//This function returns the sign of a given real valued double.
// [[Rcpp::export]]
double signDblCPP (double x){
  double ret = 0;
  if(x > 0){ret = 1;}
  if(x < 0){ret = -1;}
  return(ret);
}

//Tested to be 6x faster(37 us vs 207 us). This operation is done from 200x per layer
//Original R function by Stas_G
// [[Rcpp::export]]
NumericVector findPeaksCPP( NumericVector vY, int m = 3) {
  int sze = vY.size();
  int i = 0;//generic iterator
  int q = 0;//second generic iterator

  int lb = 0;//left bound
  int rb = 0;//right bound

  bool isGreatest = true;//flag to state whether current index is greatest known value

  NumericVector ret(1);
  int pksFound = 0;

  for(i = 0; i < (sze-2); ++i){
    //Find all regions with negative laplacian between neighbors
    //following expression is identical to diff(sign(diff(xV, na.pad = FALSE)))
    if(signDblCPP( vY(i + 2)  - vY( i + 1 ) ) - signDblCPP( vY( i + 1 )  - vY( i ) ) < 0){
      //Now assess all regions with negative laplacian between neighbors...
      lb = i - m - 1;// define left bound of vector
      if(lb < 0){lb = 0;}//ensure our neighbor comparison is bounded by vector length
      rb = i + m + 1;// define right bound of vector
      if(rb >= (sze-2)){rb = (sze-3);}//ensure our neighbor comparison is bounded by vector length
      //Scan through loop and ensure that the neighbors are smaller in magnitude
      for(q = lb; q < rb; ++q){
        if(vY(q) > vY(i+1)){ isGreatest = false; }
      }

      //We have found a peak by our criterion
      if(isGreatest){
        if(pksFound > 0){//Check vector size.
         ret.insert( 0, double(i + 2) );
       }else{
         ret(0) = double(i + 2);
        }
        pksFound = pksFound + 1;
      }else{ // we did not find a peak, reset location is peak max flag.
        isGreatest = true;
      }//End if found peak
    }//End if laplace condition
  }//End loop
  return(ret);
}//End Fn

इस के लिए पाश दोषपूर्ण लगता है, @caseyk: for(q = lb; q < rb; ++q){ if(vY(q) > vY(i+1)){ isGreatest = false; } }: लूप "जीत" के माध्यम से पिछले रन के बराबर कर के रूप में isGreatest = vY(rb-1) <= vY(rb)। उस पंक्ति के ठीक ऊपर की गई टिप्पणी का दावा करने के लिए, लूप को बदलने की आवश्यकता होगी:for(q = lb; isGreatest && (q < rb); ++q){ isGreatest = (vY(q) <= vY(i+1)) }
बर्नहार्ड वैगनर

हममम। जब से मैंने यह कोड लिखा है यह एक वास्तविक लंबा समय है। IIRC यह सीधे Stas_G के फ़ंक्शन के साथ परीक्षण किया गया और सटीक समान परिणाम बनाए रखा। हालांकि मैं देख रहा हूं कि आप क्या कह रहे हैं, मुझे यकीन नहीं है कि आउटपुट में क्या अंतर होगा। यह आपके लिए एक समाधान के योग्य होगा कि आप अपने समाधान की जांच करें जो मैंने प्रस्तावित / अनुकूलित किया था।
केसी

मुझे यह भी जोड़ना चाहिए कि मैंने 100x के आदेश पर व्यक्तिगत रूप से इस स्क्रिप्ट का परीक्षण किया (यह मानते हुए कि यह मेरी परियोजना में एक है) और इसे एक लाख से अधिक बार इस्तेमाल किया गया था और एक अप्रत्यक्ष परिणाम की पेशकश की थी जो एक साहित्य परिणाम के लिए पूरी तरह से सहमति थी एक विशिष्ट परीक्षण मामला। इसलिए, अगर यह 'त्रुटिपूर्ण' है, तो यह 'दोषपूर्ण' नहीं है;)
मामला

1

सबसे पहले: एल्गोरिथ्म भी एक सपाट पठार के दाईं ओर एक कॉल को झूठा कहता है, क्योंकि sign(diff(x, na.pad = FALSE)) 0 तब -1 होगा ताकि इसका अंतर भी -1 हो जाए। एक साधारण फिक्स यह सुनिश्चित करना है कि नकारात्मक प्रविष्टि से पहले साइन-डिफरेंस शून्य नहीं है, लेकिन सकारात्मक है:

    n <- length(x)
    dx.1 <- sign(diff(x, na.pad = FALSE))
    pks <- which(diff(dx.1, na.pad = FALSE) < 0 & dx.1[-(n-1)] > 0) + 1

दूसरा: एल्गोरिथ्म बहुत स्थानीय परिणाम देता है , उदाहरण के लिए अनुक्रम में तीन लगातार शब्दों के किसी भी भाग में एक 'डाउन' के बाद एक 'अप'। यदि कोई एक निरंतर निरंतर कार्य के स्थानीय मैक्सिमा में इसके बजाय रुचि रखता है, तो - वहाँ शायद अन्य बेहतर चीजें हैं, लेकिन यह मेरा सस्ता और तत्काल समाधान है


  1. कभी भी थोड़ा सा डेटा को सुचारू रूप से चलाने के लिए 3 लगातार बिंदुओं के औसत का उपयोग करके चोटियों की पहचान करें । इसके अलावा फ्लैट के बाद उपर्युक्त नियंत्रण को हटा दें।
  2. इन अभ्यर्थियों को फ़िल्टर करके, एक चिकने-चिकने संस्करण के लिए, एक खिड़की के अंदर का औसत प्रत्येक शिखर पर स्थानीय घटनाओं के औसत के साथ बाहर केंद्रित है।

    "myfindPeaks" <- 
    function (x, thresh=0.05, span=0.25, lspan=0.05, noisey=TRUE)
    {
      n <- length(x)
      y <- x
      mu.y.loc <- y
      if(noisey)
      {
        mu.y.loc <- (x[1:(n-2)] + x[2:(n-1)] + x[3:n])/3
        mu.y.loc <- c(mu.y.loc[1], mu.y.loc, mu.y.loc[n-2])
      }
      y.loess <- loess(x~I(1:n), span=span)
      y <- y.loess[[2]]
      sig.y <- var(y.loess$resid, na.rm=TRUE)^0.5
      DX.1 <- sign(diff(mu.y.loc, na.pad = FALSE))
      pks <- which(diff(DX.1, na.pad = FALSE) < 0 & DX.1[-(n-1)] > 0) + 1
      out <- pks
      if(noisey)
      {
        n.w <- floor(lspan*n/2)
        out <- NULL
        for(pk in pks)
        {
          inner <- (pk-n.w):(pk+n.w)
          outer <- c((pk-2*n.w):(pk-n.w),(pk+2*n.w):(pk+n.w))
          mu.y.outer <- mean(y[outer])
          if(!is.na(mu.y.outer)) 
            if (mean(y[inner])-mu.y.outer > thresh*sig.y) out <- c(out, pk)
        }
      }
      out
    }

0

यह सच है कि फ़ंक्शन प्लैटॉक्स के अंत की भी पहचान करता है, लेकिन मुझे लगता है कि एक और आसान फिक्स है: चूंकि एक वास्तविक चोटी के पहले अंतर का परिणाम '1' और फिर '-1' होगा, दूसरा अंतर '-2' होगा, और हम सीधे जाँच कर सकते हैं

    pks <- which(diff(sign(diff(x, na.pad = FALSE)), na.pad = FALSE) < 1) + 1

इस सवाल का जवाब नहीं लगता है।
माइकल आर। चेरिक

0

Numpy का उपयोग करना

ser = np.random.randint(-40, 40, 100) # 100 points
peak = np.where(np.diff(ser) < 0)[0]

या

double_difference = np.diff(np.sign(np.diff(ser)))
peak = np.where(double_difference == -2)[0]

पंडों का उपयोग करना

ser = pd.Series(np.random.randint(2, 5, 100))
peak_df = ser[(ser.shift(1) < ser) & (ser.shift(-1) < ser)]
peak = peak_df.index
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.