6

我每 10 秒在新行上有一张风向表(以及其他天气值)。方向存储为度数:0 - 360。


目的

这个平均值是什么意思?数据库每 10 秒存储一行信息。对于性能问题,我想将 5 天之前的数据汇总到每小时一条(平均)行。

使用温度很容易实现: avg(temp) 可以解决问题,因为温度不会在不同的值之间跳跃。

在盛行的风中,要获得这个“平均值”要困难得多。


以度为单位计算平均风向并不像使用聚合函数 avg() 那样简单,因为它是一个圆,例如:

dir1 = 10; dir2 = 350;
avg() = (10+350)/2 = 180;

哪个不准确;它应该是:0 或 360。

因此,我在大学里的三角学课程在我的脑后想,如果我将它转换为弧度,并计算 x 和 y 分量,我将能够重新计算平均方向。

在 php 中,$w['w'] 是存储在数据库中的方向。

while($w = $stmt->fetch()){
    $x += cos(deg2rad($w['w']));
    $y += sin(deg2rad($w['w']));     
}

$angle = atan2($y, $x);
$angle = 360 + round(rad2deg($angle));

这个公式正确吗?

如果这个公式是正确的;理想情况下,我想获得 MySQL 的完整功能。我用它做了这个;但我发现很多()的......

(360 + degrees(atan2(sum(sin(radians(W))), sum(cos(radians(W))))))) AS angle
4

4 回答 4

3

最初建议的问题是它总是添加 360,而实际上,如果 atan2 给出否定结果,您只想添加 360。这可以用 IF 语句进行排序,但它比原来的还要笨重!

IF( degrees (atan2( sum(sin(radians(WindDirection))) , sum(cos(radians(WindDirection))) ) )<0,
360+degrees ( atan2( sum(sin(radians(WindDirection))), sum(cos(radians(WindDirection))) )),
degrees(atan2(sum(sin(radians(WindDirection))), sum(cos(radians(MastDirection))) )) ) AS angle 

第二种解决方案不能用于时间平均,因为它不是聚合函数。

我管理的最佳解决方案实际上使用了一个存储函数,如果您对数据库具有以下权限,则可以创建该函数:

CREATE FUNCTION Vavg (sumsindir FLOAT,sumcosdir FLOAT) 
RETURNS FLOAT DETERMINISTIC 
RETURN IF( DEGREES(ATAN2(sumsindir,sumcosdir))<0,360+DEGREES(ATAN2(sumsindir,sumcosdir)) , DEGREES(ATAN2(sumsindir,sumcosdir)))

然后,您可以通过在查询中调用函数来计算平均值:

SELECT VavgTest( sum(sin(radians(WindDirection))),sum(cos(radians(WindDirection))) ) AS vectorAverage FROM table GROUP BY HOUR(MyTimestamp),DAYOFYEAR(MyTimestamp),YEAR(MyTimestamp)
ORDER BY MyTimestamp;

我已经将 group by 包括在内,以表明平均程序随后适用于任何平均时间段。

我怀疑,实际的最佳答案是使用用户定义的函数,但我没有做到这一点,因为它必须在 C 或 C++ 中完成,而且我没有这些语言的任何背景。

于 2014-11-21T12:30:22.597 回答
2

如果我们在圆上绘制方向,我们会看到在这种情况下您所追求的“平均值”落在圆的较短部分(绿色部分)的中间。较长段的中点位于另一段的中点的完全相反侧。

圆图

一个简单的(A+B)/2计算给出较长或较短段的中点。如果它是较长的段,则可以找到较短的段,S1 = (S2+180) % 360其中S2较长的段在哪里(并且%模运算符)。

现在剩下要做的就是确定何时获得较长的段,何时获得较短的段,即何时ABS(B-A) > 180

将所有这些放在一起,计算将是:

( (A+B)/2 + IF( ABS(B-A) > 180, 180, 0 ) ) % 360

这很可能比三角函数快得多。

如评论中所述,如果两个方向彼此完全相反(如 0° 和 180°),则“平均值”可能位于圆的任一侧(90° 或 270°)。你只需要决定选择哪一个。在这种情况下,上述公式将选择 90°。如果您想要 270°,请将比较从 更改>>=

于 2012-05-31T13:12:03.217 回答
0

我有这个解决方案,用于计算 SQL Server 2008 中多个值的每日平均风向。##Day 是时间序列值的一天。

SELECT @DayMean =
CASE WHEN ABS(@DayMax - @DayMin) > 180 THEN --this line could be wrong, might need to compare consecutive values
  CASE WHEN AVG(CASE WHEN (Value > 180) THEN Value-360 ELSE Value END) < 0 
    THEN 
      360+AVG(CASE WHEN (Value > 180) THEN Value-360 ELSE Value END)
    ELSE
      AVG(CASE WHEN (Value > 180) THEN Value-360 ELSE Value END)
  END
ELSE
  AVG(Value)
END
FROM ##Day

灵感来自此页面上的倒数第二篇文章http://www.control.com/thread/1026210133,然后在 excel 中计算出来。

这条线可能是错误的,但是 excel 中的计算涉及找到连续数字之间的差异,包括最后一个值减去零,这对我来说没有多大意义(可能只是因为比较连续值比 min 更容易和max在excel中)。

CASE WHEN ABS(@DayMax - @DayMin) > 180 THEN

因此,也许应该是“如果任何值超过 180”,或者“如果连续点之间的最大“距离”超过 180”。后一种情况的解决方案可能会使计算速度慢得多。

请在可能的地方批评和纠正:)

于 2013-10-15T04:55:39.733 回答
0

看看这个: http ://www.h2ns.com/media/tech-notes/tn09.pdf

在我的情况下,我有一张每 20 或 30 秒记录一次风向(度)和速度的表格,所以我需要在 1 分钟内取平均值。

我使用这样的 SQL 语句:

select
    DATE_FORMAT(ut,'%Y-%m-%d %H:%i:00'),
    SQRT(POW(avg(WS*sin(RADIANS(WD))),2)+ POW(avg(WS*cos(RADIANS(WD))),2))
as UV, ATAN(avg(WS*sin(RADIANS(WD))) / avg(WS*cos(RADIANS(WD))))
as AV
from Weather
GROUP BY DATE(ut),HOUR(ut),MINUTE(ut);
于 2013-04-02T16:08:14.817 回答