【问题标题】:How do I find all peaks and troughs of tidal data?如何找到潮汐数据的所有波峰和波谷?
【发布时间】:2010-11-12 19:38:31
【问题描述】:

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

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

这是我正在使用的真实数据示例:http://pastebin.com/raw.php?i=bRc2rmpG

这是我寻找高/低值的尝试:http://pastebin.com/8PS1frc0

我的代码当前存在的问题:

  • 当读数波动时(如示例数据中的11/14/2010=>11:30:0011/14/2010=>11:54:00 范围所示),它会在方向逻辑中产生“摆动”。这会产生错误的峰值和谷值。我该如何避免/纠正这种情况?

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

【问题讨论】:

  • 如果您没有遇到错误,人们不太可能会查看您的代码。
  • 我用我开始使用实际数据而不是测试值时出现的两个问题更新了我的问题。我提供了一些我正在使用的真实数据的导出。
  • 看看stackoverflow.com/a/10303971/987850这个方法存在于Python、C和Fortran——billauer.co.il/peakdet.html

标签: php multidimensional-array inflection


【解决方案1】:

我不得不对嘈杂的生理数据执行类似的任务。在我看来,你有一个信号调理问题。这是一个对我有用的过程。

  1. 将您的时间值转换为秒,即 (HH*3600)+(MM*60)+(SS),以生成数字“X”值。
  2. 使用滑动窗口平滑生成的 X 和 Y 数组,例如宽度为 10 个点。您还可以考虑在此步骤中过滤具有冗余和/或虚假时间戳的数据。
  3. 通过比较平滑的 Y[1] 和 Y[0] 来执行指示相位检测。与上面的帖子类似,如果 (Y[1] > Y[0]),您可能会假设数据正在攀升至峰值。如果 (Y[1]
  4. 一旦您知道了初始相位,就可以按上述方法进行峰谷检测:如果 Y[i] > Y[i+1] 且 Y[i]
  5. 您可以通过考虑滑动窗口大小将平滑的 X 值投影回原始 X 数据来估计峰/谷时间(以补偿由滑动窗口引起的“信号滞后”)。然后可以将生成的时间值(以秒为单位)转换回 HH:MM:SS 格式以进行报告。

【讨论】:

  • 1) 我不明白这有什么用。 2) 我的数据不会有任何虚假或冗余的时间戳。 3) 我已经在使用 $direction 变量进行此操作。 4) 我已经在使用 $last$current 变量进行此操作。 5) 如问题所述,我没有试图估计任何东西
  • 从您最初的问题陈述中,我理解 00:00:00 和 23:54:00 是无关值(即,您的问题“我如何忽略这些”)。因此,我对“虚假”数据的假设。重读了几次帖子后,我想我理解这些值本身是合理的,但是您的算法错误地选择了这些。
  • wrt to comment 4 - 您没有对 smoothed 数据执行波峰和波谷检测。平滑数据可以帮助您解决虚假检测问题 - 请参阅您关于“方向逻辑摆动”的评论
  • wrt to comment 5 - 如果您对平滑数据执行峰值/谷值检测,那么您检测到的拐点可能位于您的实际采样点之一之间(此外,它将落后于原始数据平滑窗口宽度的 1/2)。因此,您必须将拐点“投影”回原始数据,以获取您的原始数据样本之一。因此,我使用了“估计”这个词。
  • wrt to comment 1 - 你在检测你想要的拐点时遇到问题,因为你没有看到平滑数据的实用程序;-)
【解决方案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 的半径内搜索最小值/最大值,这可能会给您的数据集带来误报。 明智地选择半径。

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

祝你好运!

【讨论】:

    【解决方案3】:

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

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

    使用诸如non-linear least squares 之类的方法(不幸的是,必须使用迭代方法来解决)。查看您的示例数据,它似乎很合适。计算出 w 和 p 后,只需对函数求时间导数并求解零即可轻松定位峰谷:

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

    但我想,如果您的代码真的按照您的意愿行事,那么将事情复杂化是没有用的。不要再猜测自己了。 :)

    【讨论】:

    • 我认为这不是我要找的。我只需要从提供的数据中找到值。
    【解决方案4】:

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

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

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

    【讨论】:

      【解决方案5】:

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

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

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

      【讨论】:

      • 它只需要准确到所提供的数据。数据数组(我无法修改其结构)不使用数字索引。还有(不常见,但确实存在)某些情况下两条记录之间的值相同,因此请检查例如[i-1] &lt; [i] &gt; [i+1] 不一定有效。
      【解决方案6】:

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

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

      【讨论】:

        【解决方案7】:

        鉴于您永远不会在不到 12 小时内看到两个最大值或 2 分钟,一个简单的解决方案是使用 3-5 小时左右的滑动窗口并找到最大值和最小值。如果它最终在前 30 分钟或最后 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
        

        【讨论】:

        • 我尝试了一些类似的方法,但是当高峰/低谷确实出现在这些时间段内时,它就崩溃了。
        • @jnpcl:如果“那些时间段”是指第一个/最后 30 分钟,那么我看不到问题所在。您似乎拥有分辨率为 6 分钟的数据,因此我希望每个峰值/谷值在这些时期内出现 10 次左右,但每个峰值/谷底也会在内部期间出现 20 次,因此您仍然会得到它。
        猜你喜欢
        • 2012-09-07
        • 1970-01-01
        • 1970-01-01
        • 2019-09-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多