计算MySQL中风向的平均值

时间:2012-05-31 10:59:38

标签: mysql trigonometry degrees atan2 radians

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


目的

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

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

随着盛行风,获得这种'平均'要困难得多。


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

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

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

所以我的大脑中的三角学课程就在脑海中,我想,如果我将它转换为Radians,并计算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 个答案:

答案 0 :(得分:3)

初始建议的问题是它总是增加360,而实际上你只想添加360,如果atan2给出负面结果。这可以使用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;

我已将该小组纳入其中,以表明平均程序适用于任何平均时段。

我怀疑,实际的最佳答案是使用用户定义的功能,但我无法管理,因为它必须在C或C ++中完成,而且我没有任何背景知识那些语言。

答案 1 :(得分:1)

如果我们在圆上绘制方向,我们会发现在这种情况下你所追求的“平均”落在圆的较短段(绿色段)的中间。较长段的中点位于另一段中间点的正好相对侧。

Circle illustration

简单的(A+B)/2计算可以给出较长或较短的段的中间点。如果它是较长的细分,则可以在S1 = (S2+180) % 360找到较短的细分,其中S2是较长的细分(而%modulo operator)。

现在剩下要做的就是确定我们何时获得较长的段以及何时获得较短的段,并且只需ABS(B-A) > 180

将所有这些放在一起,计算结果如下:

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

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

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

答案 2 :(得分: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);

答案 3 :(得分: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中的计算涉及找到连续数字之间的差异,包括最后一个值减零,这对我来说没有多大意义(并且可能只是因为它更容易比较连续值优于excel中的min和max。

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

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

请批评并纠正在哪里:)