2

我一直在 excel 中使用一个非常简单的数组公式来处理一些数据集,但是它们变得太大,并且在我更新计算时绝对会破坏我的计算机性能。

excel表和MySQL数据库的布局如下:

+-Timestamp-+-value-+
| 1340816430|  .02  |
---------------------

x600,000 行

这是excel公式:

{=AVERAGEIFS(B:B,A:A,"<"&A1+1000,A:A,">"&A1-1000)}

这将返回值的平均值,并且是 excel 表中的第三列。我有什么可行的方法来创建一个 MySQL 查询,该查询执行类似的操作并返回一个列,如果我运行 excel 的公式,第三列中的值会是多少?

4

4 回答 4

4

如果您对使用 Excel 公式感到满意,则可以大大加快此计算速度(在我的系统上超过 3000 倍)。假设 A 列包含 ASCENDING ORDER 中的时间戳,B 列包含值(如果尚未排序,则使用 E​​xcel 排序)。
在 C 列中放入 =IFERROR(MATCH(A1-1000,$A:$A,1),1) 并复制下来。这将计算行号少 1000 个时间戳的行号。
在 D 列中放入 =IFERROR(MATCH(A1+1000,$A:$A,1),1048576) 并复制下来。这将计算第 1000 行时间戳的行号。
在 E 列中放入 =AVERAGE(OFFSET(B1,C1-ROW(),0,D1-C1+1,1)) 并向下复制。这将计算从第一行到最后一行的子集范围的平均值。

在我的系统上,这个完整的计算 1000K 行在 20 秒内。
这种方法的缺点是它的易失性,因此每当您进行更改时都会重新计算,但我假设您无论如何都处于手动计算模式。

于 2012-07-09T14:07:39.560 回答
2

mysql代码:

select
  a.timestamp t1,
  avg(x.value) average_value
from
  mydata a inner join (
    select 
      timestamp, 
      value
    from mydata
    ) x
    on x.timestamp between a.timestamp - 1000 and a.timestamp + 1000
group by 
  a.timestamp
order by
  t1
;

我想认为如果没有 Excel 开销,它的性能会好得多,但我不能保证它在 600k 行上会快如闪电。你肯定会想要索引Timestamp. 另请参阅我创建的SQL Fiddle 。

于 2012-07-08T22:29:54.360 回答
0

@Peter 如果愿意,您可以坚持使用 Excel。只需使用http://xllarray.codeplex.com。你想要的公式是=AVERAGE(ARRAY.MASK((A:A>A1 + 1000)*(A:A<A1 - 1000), B:B)。我的垃圾笔记本电脑上的 1MM 行计算不到 1 秒。请务必按 Ctrl-Shift-Enter 作为数组公式。

如果您不想构建代码,您可以从我的 SkyDrive 中获取加载项和帮助文件:http: //sdrv.ms/JtaMIV

于 2012-07-09T18:26:28.180 回答
0

@查尔斯。啊,不。它仅适用于一个公式。误读了规范。

如果您想将计算推送到 C++ 中并将其公开为 xll,您可以这样做:

#include <algorithm>
#include <numeric>
#include "xll/xll.h"

using namespace xll;

typedef traits<XLOPER12>::xword xword;

static AddIn12 xai_windowed_average(
    L"?xll_windowed_average", XLL_FP12 XLL_FP12 XLL_FP12 XLL_DOUBLE12,
    L"WINDOWED.AVERAGE", L"Time, Value, Window"
);
_FP12* WINAPI
xll_windowed_average(_FP12* pt, _FP12* pv, double dt)
{
#pragma XLLEXPORT
static xll::FP12 a(size(*pt), 1);

double* bt0 = &pt->array[0];
double* bv0 = &pv->array[0];
double* bt = std::lower_bound(begin(*pt), end(*pt), *bt0 - dt);
double* et = std::lower_bound(begin(*pt), end(*pt), *bt0 + dt);

for (xword i = 0; i < size(*pt); ++i) {
    a[i] = (bt == et) ? 0 : std::accumulate(bv0 + (bt - bt0), bv0 + (et - bt0), 0)/(et - bt); 

    // update the window
    bt = std::lower_bound(bt, end(*pt), pt->array[i] - dt);
    et = std::lower_bound(bt, end(*pt), pt->array[i] + dt);
}

return a.get();
}   
于 2012-07-10T14:33:40.180 回答