MySQL 累积产品组按

Posted

技术标签:

【中文标题】MySQL 累积产品组按【英文标题】:MySQL cumulative product group by 【发布时间】:2018-02-25 04:29:27 【问题描述】:

我一直在使用 WRDS/CRSP 数据集(由 UPenn 维护的用于学术研究的股票价格数据库)。我一直在用 Python 下载数据并将其插入到我的本地 mysql 数据库中。

数据如下所示,主键为 (quote_date, security_id):

quote_date  security_id tr              accum_index
10-Jan-86   10002       null            1000
13-Jan-86   10002       -0.026595745    973.4042548
14-Jan-86   10002       0.005464481     978.7234036
15-Jan-86   10002       -0.016304348    962.7659569
16-Jan-86   10002       0               962.7659569
17-Jan-86   10002       0               962.7659569
20-Jan-86   10002       0               962.7659569
21-Jan-86   10002       0.005524862     968.0851061
22-Jan-86   10002       -0.005494506    962.765957
23-Jan-86   10002       0               962.765957
24-Jan-86   10002       -0.005524862    957.4468078
27-Jan-86   10002       0.005555556     962.7659569
28-Jan-86   10002       0               962.7659569
29-Jan-86   10002       0               962.7659569
30-Jan-86   10002       0               962.7659569
31-Jan-86   10002       0.027624309     989.3617013
3-Feb-86    10002       0.016129032     1005.319148
4-Feb-86    10002       0.042328041     1047.872338
5-Feb-86    10002       0.04568528      1095.744679

我需要计算accum_index列,它基本上是股票总收益的一个指标,计算如下:

accum_index_t = accum_index_t-1 * (1 + tr_t)

该表有 80m 行。我编写了一些代码来遍历每个 security_id 并计算累积乘积,如下所示:

select @sid := min(security_id)
from stock_prices;

create temporary table prices (
    quote_date datetime,
    security_id int,
    tr double null,
    accum_index double null,
    PRIMARY KEY (quote_date, security_id)
);

while @sid is not null
do

    select 'security_id', @sid;
    select @accum := null;

    insert into prices
    select quote_date, security_id, tr, accum_index
    from stock_prices
    where security_id = @sid
    order by quote_date asc;

    update prices
    set accum_index = (@accum := ifnull(@accum * (1 + tr), 1000.0));

    update stock_prices p use index(PRIMARY), prices a use index(PRIMARY)
    set p.accum_index = a.accum_index
    where p.security_id = a.security_id
    and p.quote_date = a.quote_date;

    select @sid := min(security_id)
    from stock_prices
    where security_id > @sid;

    delete from prices;

end while;

drop table prices;

但这太慢了,我的笔记本电脑上的每个安全性大约需要一分钟,而计算这个系列需要数年时间。有没有办法将其矢量化?

干杯, 史蒂夫

【问题讨论】:

您能否向我们展示几行示例数据以及预期的输出?请省略输入中与您的问题无关的列。 是的,添加了更多示例。谢谢 见meta.***.com/questions/333952/… 谢谢,我通过重写我的代码并用 Python 做所有事情来解决这个问题,但我会记住这一点(MCVE)。 WRDS 还使这些数据在云上的 PostgreSQL 中可用。您可以使用您的凭据连接到端口 9737 上的 wrds-pgdata.wharton.upenn.edu 【参考方案1】:

如果您使用的是 MySQL 8,则可以使用 window functions 创建累积产品。不幸的是,我知道的任何 SQL 数据库中都没有 PROD() 聚合/窗口函数,但是 you can emulate it using EXP(SUM(LOG(factor))):

SELECT
  quote_date,
  security_id,
  tr,
  1000 * (EXP(SUM(LOG(1 + COALESCE(tr, 0)))
    OVER (PARTITION BY security_id ORDER BY quote_date)))
    AS accum_index
FROM stock_prices

dbfiddle here.

【讨论】:

我正在处理类似的问题,并尝试重新创建示例输出以确保我理解它并且无法复制它。原来1000的值应该在外面:1000 *EXP(SUM(LOG( (1 + COALESCE(tr, 1)))) OVER (PARTITION BY security_id ORDER BY quote_date)) 谢谢,@Chris,你是对的。不仅如此,还有一个偏离 1 的错误。我已经解决了这个问题并添加了一个 dbfiddle @LukasEder 小提琴似乎没有产生正确的输出? (例如 1986-01-13 的值是 946.8 与 973.4)。小提琴也使用LN,而上面的答案使用LOG @Dylan:谢谢。在 MySQL 中,LOG(x)LN(x) 相同。我已经更新了 dbfiddle 和代码来修复 off-by-1 错误,我之前错误地修复了这个错误。【参考方案2】:

如果您使用的是 MySQL 5,您可以模拟这个函数,将 current 与 last tr 逐行相乘。之后我们取最后一行的累加值。

tr 是百分比值,现在? 所以让我们给每个 tr 加 1。

第一个存储的值将是中性 1。

试试这个:

SET @variation = 1;
SET @row_number = 0;

SELECT accumulateTr
FROM
    (SELECT
        @row_number := (@row_number + 1) AS rowNumber,
        @variation := (1 + variation) * @variation AS accumulateTr
     FROM
        prices) accumulatedTrs
ORDER BY rowNumber DESC
LIMIT 1;

【讨论】:

以上是关于MySQL 累积产品组按的主要内容,如果未能解决你的问题,请参考以下文章

SQL - 计算产品的累积平均成本价

R - 按组的累积产品和总和

什么是学习曲线效应?

数据库流行度7月排行榜:Oracle 和 MySQL 暴跌创历史新低

我如何在 php 中从 mysql 查询结果组按日期给出符号(如更改颜色或 fa-up/fa-down)

<Anylogic>如何指定每种产品的应计率