我一直在excel中使用一个非常简单的数组公式来处理一些数据集,但是当我更新计算时,它们变得太大并且绝对会破坏我的计算机性能。
excel表和MySQL数据库的布局如下:
+-Timestamp-+-value-+
| 1340816430| .02 |
---------------------
x600,000行
这是excel公式:
{=AVERAGEIFS(B:B,A:A,"<"&A1+1000,A:A,">"&A1-1000)}
返回值的平均值,并且是Excel工作表中的第三列。有没有合理的方法让我创建一个执行类似操作的MySQL查询,并返回一个列,如果我运行excel的公式,那么该列将包含在第三列中的值?
答案 0 :(得分:4)
如果您对使用Excel公式感到满意,可以加快计算速度(在我的系统上超过3000)。假设列A包含ASCENDING ORDER和B列中的时间戳值(如果尚未排序,则使用Excel排序)。
在C栏中放置= IFERROR(MATCH(A1-1000,$ A:$ A,1),1)并复制。这计算行1000时间戳的行号减去。
在D栏中输入= IFERROR(MATCH(A1 + 1000,$ A:$ A,1),1048576)并复制。这会计算行1000时间戳的行号
在列E put = AVERAGE(OFFSET(B1,C1-ROW(),0,D1-C1 + 1,1))中并向下复制。这将计算从第一行到最后一行的子集范围的平均值。
在我的系统上,这个完整计算在20秒内计算1000K行。
这种方法的缺点是它的易失性因此,无论何时进行更改,都会重新计算,但我认为您仍处于手动计算模式。
答案 1 :(得分: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。
答案 2 :(得分:0)
@Peter如果您愿意,可以坚持使用Excel。只需使用http://xllarray.codeplex.com即可。您想要的公式是=AVERAGE(ARRAY.MASK((A:A>A1 + 1000)*(A:A<A1 - 1000), B:B)
。我的垃圾笔记本电脑上的1MM行计算在1秒以内。请务必按Ctrl键移位 - 将其作为数组公式输入。
如果您不想构建代码,可以从我的SkyDrive中获取加载项和帮助文件:http://sdrv.ms/JtaMIV
答案 3 :(得分:0)
@Charles。啊,不。它只适用于一个公式。误读了规范。
如果您想将计算推送到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();
}