2010-11-12 1 views
3

Я работаю с некоторыми данными океана прилив, который структурирован так:Как найти все пики и впадины приливных данных?

$data = array('date' => array('time' => array('predicted','observed'))); 

Вот пример реальных данных, которые я использую: http://pastebin.com/raw.php?i=bRc2rmpG

И это моя попытка найти высокое/низкие значения: http://pastebin.com/8PS1frc0

Текущие выпуски с моим кодом:

  • Когда показания изменяются (как видно из диапазона 11/14/2010=>11:30:00 - 11/14/2010=>11:54:00 в образце данных), он создает «колебание» в логике направления. Это создает ошибочный пик и прогиб. Как я могу избежать/исправить это?

Примечания: Мой метод очень «одноранговый» .. Я предположил, что я бы не нужен огромная математику материала, так как я не пытаюсь найти какие-либо средние, приближение, или будущие оценки. Я действительно ценю пример кода лучшего метода, даже если это означает выбросить код, который я написал до сих пор.

+0

Если у вас нет ошибки, маловероятно, что люди просмотрят ваш код. – Fosco

+0

Я обновил свой вопрос двумя проблемами, которые возникли, когда я начал использовать фактические данные, а не тестовые значения. Я предоставил экспорт некоторых реальных данных, которые я использую. – drudge

+0

смотрите http://stackoverflow.com/a/10303971/987850 Этот метод существует в Python, C и Fortran - http://billauer.co.il/peakdet.html – 23W

ответ

2

Вы ищете местные минимумы и максимумы, я полагаю? Это очень легко сделать:

<?php 

$data = array(1, 9, 4, 5, 6, 9, 9, 1); 

function minima($data, $radius = 2) 
{ 
    $minima = array(); 

    for ($i = 0; $i < count($data); $i += $radius) 
    { 
    $minima[] = min(array_slice($data, $i, $radius)); 
    } 

    return $minima; 
} 

function maxima($data, $radius = 2) 
{ 
    $maxima = array(); 

    for ($i = 0; $i < count($data); $i += $radius) 
    { 
    $maxima[] = max(array_slice($data, $i, $radius)); 
    } 

    return $maxima; 
} 

print_r(minima($data)); 
print_r(maxima($data)); 

?> 

Вы просто должны указать радиус поиска, и это даст Вам множество локальных минимумов и максимумов данных. Он работает простым способом: он разрезает массив на сегменты длины $radius и находит минимум этого сегмента. Этот процесс повторяется для всего набора данных.

Будьте осторожны с радиусом: обычно вы хотите выбрать радиус, чтобы быть средним расстоянием от пика до корыта данных, но вам нужно будет найти это вручную. Он по умолчанию равен 2, и он будет искать только минимумы/максимумы в радиусе 2, что, вероятно, даст ложные срабатывания с вашим набором данных. Правильно выберите радиус.

Вам придется взломать его в свой скрипт, но это не должно быть слишком тяжелым.

Удачи вам!

1

Я не читал его подробно, но ваш подход кажется очень ad-hoc. Более правильный способ, вероятно, будет соответствовать его функции

f(A,B,w,p;t)=Asin(wt+p)+B 

с использованием такого способа, как non-linear least squares (который, к сожалению, должен быть решен с помощью итерационного метода). Глядя на ваши образцы данных, похоже, что это будет хорошо. Когда вы рассчитали ш и р, легко найти вершины и долины, просто принимая производной по времени функции и решения для нуля:

t = (pi(1+2n)-2p)/w 

Но я полагаю, что если ваш код действительно делает то, что вы хотите , нет смысла усложнять ситуацию. Остановите второе предположение. :)

+0

Я не думаю, что это совсем что Я ищу .. Мне просто нужно найти значения из предоставленных данных. – drudge

0

Насколько точны обнаружение пика/долины? Если вам просто нужно найти точную запись, где встречается пик или долина, разве это недостаточно, чтобы проверить точки перегиба?

например. учитывая запись в позиции «i», если запись [i-1] и запись [i + 1] являются «более высокими», чем запись [i], у вас есть долина. и если запись [i-1] и запись [i + 1] ниже, чем запись [i], у вас есть пик. До тех пор, пока ваша частота дискретизации будет быстрее, чем изменения приливов (посмотрите вверх Nyquist frequency), этот процесс должен получить ваши пики/впадины ваших данных.

Если вам нужно сгенерировать график из этого и попытаться экстраполировать более точные точки времени для пиков/желобов, тогда вам нужно больше работать.

+0

Он должен быть точным только для данных, которые предоставляются. Массив данных (который я не могу изменить структуру) не использует числовой индекс. Существуют также (редко, но они существуют) в некоторых случаях, когда значение одинаково между двумя записями, поэтому проверка с помощью, например, '[i-1] < [i] > [i + 1]' не обязательно будет работать. – drudge

2

Мне приходилось выполнять подобные задачи по шумным физиологическим данным. На мой взгляд, у вас проблема с настройкой сигнала. Вот процесс, который работал для меня.

  1. Преобразование ваших значений времени в секунды, то есть (HH * 3600) + (MM * 60) + (SS), для создания числового значения «X».
  2. Сгладить результирующие массивы X и Y с помощью скользящего окна, скажем, 10 точек по ширине. Вы также можете рассмотреть возможность фильтрации данных с избыточными и/или фиктивными отметками времени на этом шаге.
  3. Выполнение определения фазы индикации путем сравнения сглаженных Y [1] и Y [0]. Подобно сообщению выше, если (Y [1]> Y [0]), вы можете предположить, что данные поднимаются до пика. Если (Y [1] < Y [0]), вы можете предположить, что данные опускаются до желоба.
  4. Как только вы знаете начальную фазу, обнаружение пика и прохода может выполняться, как описано выше: если Y [i]> Y [i + 1] и Y [i] < Y [i-1], вы столкнулись с вершина горы.
  5. Вы можете оценить время пика/времени пролета, проецируя сглаженное значение X обратно на исходные данные X, рассматривая размер скользящего окна (чтобы компенсировать «задержку сигнала» в скользящем окне). Полученное значение времени (в секундах) затем может быть преобразовано обратно в формат HH: MM: SS для отчетности.
+0

1) Я не вижу, как это полезно. 2) Мои данные не будут иметь фиктивных или избыточных временных меток. 3) Я уже делаю это с переменной '$ direction'. 4) Я уже делаю это с переменными '$ last' и' $ current'. 5) Как указано в вопросе **, я ничего не пытаюсь оценить **. – drudge

+0

Из вашего первоначального заявления о проблеме я понял 00:00:00 и 23:54:00, чтобы быть посторонними значениями (т. Е. Ваш вопрос «Как я могу игнорировать эти»). Таким образом, мое предположение о «фиктивных» данных. Перечитав сообщение несколько раз, я думаю, что понимаю, что сами ценности разумны, но ваш алгоритм ошибочно выбирает их. – Throwback1986

+0

wrt to comment 4 - вы не выполняете обнаружение пика и корыта на * сглаженных * данных. Сглаживание данных может помочь с вашей проблемой ложного обнаружения - см. Ваш комментарий относительно «колебания в логике направления». – Throwback1986

0

Одним из способов может быть определение абсолютного или относительного отклонения, после которого вы классифицируете дополнительные пики/прогибы как новые, а не флуктуации вокруг существующего пика/желоба.

В настоящее время $direction определяет, находите ли вы пик или прогиб, поэтому вместо перехода в другое состояние (обнаружение желоба или пика), когда производная изменяется в знаке, вы можете рассмотреть изменение состояния только тогда, когда отклонение от текущий пик/прогиб достаточно «большой».

0

Учитывая, что вы никогда не должны видеть два макс или 2 минуты менее чем за 12 часов, простым решением было бы использовать раздвижные окна 3-5 часов или около того и найти максимальные и минимальные значения. Если он заканчивается в течение первого или последнего 30 минут, игнорируйте его.

В качестве примера приведены следующие данные:

1 2 3 4 5 6 5 6 7 8 7 6 5 4 3 2 1 2 

и окно размером 8, с первым и последним 2 игнорируется, и только глядя на заглядывает вы видите:

1 2 | 3 4 5 6 | 5 6, max = 6, ignore = Y 
2 3 | 4 5 6 5 | 6 7, max = 7, ignore = Y 
3 4 | 5 6 5 6 | 7 8, max = 8, ignore = Y 
4 5 | 6 5 6 7 | 8 7, max = 8, ignore = Y 
5 6 | 5 6 7 8 | 7 6, max = 8, ignore = N 
6 5 | 6 7 8 7 | 6 5, max = 8, ignore = N 
5 6 | 7 8 7 6 | 5 4, max = 8, ignore = N 
6 7 | 8 7 6 5 | 4 3, max = 8, ignore = N 
7 8 | 7 6 5 4 | 3 2, max = 8, ignore = Y 
8 7 | 6 5 4 3 | 2 1, max = 8, ignore = Y 
7 6 | 5 4 3 2 | 1 2, max = 7, ignore = Y 
+0

Я попытался что-то сделать в этих строках, но он развалился, когда пик/прогиб действительно произошел в течение этих периодов времени. – drudge

+0

@jnpcl: Если «эти периоды времени» относятся к первому/последнему 30мин, то я не вижу проблемы. Кажется, у вас есть данные с разрешением 6 минут, поэтому я ожидаю, что каждый пик/лоток окажется в те периоды примерно как 10 раз, но каждый из них будет отображаться во внутреннем периоде 20 раз каждый, чтобы вы все равно его получили. – BCS

1

Проблема в том, что я считаю, что наблюдения являются наблюдениями и могут содержать небольшие ошибки. Это, по крайней мере, нужно учитывать. Например:

  • Только изменить направление, если хотя бы следующие 2 позиции также находятся в одном направлении.

  • Не допускайте принятия решений по данным с слишком малой разницей. Отбросьте незначительные цифры. Это будет намного лучше, если вы скажете $error = 0.10; и смените ваши условия на if $previous - $error > $current и т. Д.