4

我正在处理一些结构如下的海潮数据:

$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),它会在方向逻辑中产生“摆动”。这会产生错误的峰值和谷值。我怎样才能避免/纠正这种情况?

注意:我的方法非常“临时”。我认为我不需要任何很棒的数学东西,因为我不想找到任何平均值、近似值或未来估计。我真的很感激一个更好方法的代码示例,即使这意味着扔掉我迄今为止编写的代码。

4

7 回答 7

3

I've had to perform similar tasks on a noisy physiological data. In my opinion, you have a signal conditioning problem. Here is a process that worked for me.

  1. Convert your time values to seconds, i.e. (HH*3600)+(MM*60)+(SS), to generate a numeric "X" value.
  2. Smooth the resulting X and Y arrays with a sliding window, say 10 points in width. You might also consider filtering data with redundant and/or bogus timestamps in this step.
  3. Perform an indication phase detection by comparing the smoothed Y[1] and Y[0]. Similar to the post above, if (Y[1] > Y[0]), you may assume the data are climbing to a peak. If (Y[1] < Y[0]), you may assume the data are descending to a trough.
  4. Once you know the initial phase, peak and trough detection may be performed as described above: if Y[i] > Y[i+1] and Y[i] < Y[i-1], you have encountered a peak.
  5. You can estimate the peak/trough time by projecting the smoothed X value back to the original X data by considering the sliding window size (in order to compensate for "signal lag induced" by the sliding window). The resulting time value (in seconds) can then be converted back to an HH:MM:SS format for reporting.
于 2010-11-15T21:21:15.690 回答
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,这可能会给您的数据集带来误报。 明智地选择半径。

您必须将其破解到您的脚本中,但这一点也不难。

祝你好运!

于 2010-11-22T18:33:52.083 回答
1

一个问题是我认为观察是观察并且可能包含小错误。至少需要考虑到这一点。例如:

  • 仅当至少接下来的 2 个条目也处于相同方向时才更改方向。

  • 不要让数据根据太小的差异做出决定。扔掉无关紧要的数字。$error = 0.10;当您说并将您的条件更改为if $previous - $error > $current等等时,它可能会好得多。

于 2010-11-22T20:11:08.253 回答
1

我没有详细阅读它,但是您的方法似乎非常临时。更正确的方法可能是将其拟合到函数中

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

使用诸如非线性最小二乘之类的方法(不幸的是,必须使用迭代方法来解决)。查看您的示例数据,它似乎很合适。计算出 w 和 p 后,只需对函数求时间导数并求解零,就很容易找到峰和谷:

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

但我想,如果你的代码真的做了你想要的,那么把事情复杂化是没有用的。不要再猜测自己了。:)

于 2010-11-12T20:05:39.633 回答
0

一种方法可能是定义一个绝对或相对偏差,将进一步的峰值/谷值分类为新的峰值/谷值,而不是现有峰值/谷值周围的波动。

目前,$direction确定您是在寻找峰值还是谷底,因此一旦导数的符号发生变化,您就可以考虑仅在与当前峰值/谷底的偏差时才改变状态,而不是转换到另一个状态(寻找谷底或峰值)足够“大”。

于 2010-11-22T18:02:19.107 回答
0

鉴于您永远不会在不到 12 小时内看到两个最大值或 2 分钟,一个简单的解决方案是使用 3-5 小时左右的滑动窗口并找到最大值和最小值。如果它最终在第一个或最后 30 分钟内出现,请忽略它。

例如,给定以下数据:

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

和一个大小为 8 的窗口,第一个和最后两个被忽略,只看一眼你会看到:

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
于 2010-11-22T19:55:48.330 回答
0

峰/谷检测必须有多准确?如果您只需要找到出现峰值或谷值的确切记录,那么检查拐点还不够吗?

例如,考虑位置'i' 的记录,如果记录[i-1] 和记录[i+1] 都“高于”记录[i],那么你就有了一个谷。如果记录[i-1] 和记录[i+1] 都低于记录[i],那么你就有了一个峰值。只要您的采样率快于潮汐变化(查找Nyquist 频率),该过程就会为您提供数据的峰值/谷值。

如果您需要从中生成图表并尝试推断更准确的峰/谷时间点,那么您需要做更多的工作。

于 2010-11-12T21:31:04.947 回答