【问题标题】:Sum values from the previous N number of days in KDB?KDB中前N天的总和值?
【发布时间】:2019-07-15 06:19:55
【问题描述】:

我有一个包含以下两列的表格:

初始表格

Date        Value
-------------------
2019.01.01 | 150  
2019.01.02 | 100  
2019.01.04 | 200  
2019.01.07 | 300  
2019.01.08 | 100  
2019.01.10 | 150  
2019.01.14 | 200  
2019.01.15 | 100  

对于每一行,我想对前一个 N 天数的值求和。在这种情况下,N = 5。

结果表

Date        Value  Sum
------------------------
2019.01.01 | 150 | 150 (01 -> ..)
2019.01.02 | 100 | 250 (02 -> 01)
2019.01.04 | 200 | 450 (04 -> 01)
2019.01.07 | 300 | 600 (07 -> 02)
2019.01.08 | 100 | 600 (08 -> 04)
2019.01.10 | 150 | 550 (10 -> 07)
2019.01.14 | 200 | 350 (14 -> 10)
2019.01.15 | 100 | 450 (15 -> 10)

查询

t:([] Date: 2019.01.01 2019.01.02 2019.01.04 2019.01.07 2019.01.08 2019.01.10 2019.01.14 2019.01.15; Value: 150 100 200 300 100 150 200 100)

我该怎么做呢?

【问题讨论】:

  • 您确定您的预期结果吗?总和中考虑的天数似乎从一天变化到另一天,并且不是恒定的,等于N=5

标签: kdb


【解决方案1】:

您可以解决此问题的一种方法是使用如下更新语句:

q)N:5
q)update Sum:sum each Value where each Date within/:flip(Date-N;Date)from t
Date       Value Sum
--------------------
2019.01.01 150   150
2019.01.02 100   250
2019.01.04 200   450
2019.01.07 300   600
2019.01.08 100   600
2019.01.10 150   550
2019.01.14 200   350
2019.01.15 100   450

within关键字检查Date列中的每个日期是否在当前日期和当前日期-N的窗口内,这可以通过每个权限来实现。

q)flip(-5+t`Date;t`Date)
2018.12.27 2019.01.01
2018.12.28 2019.01.02
2018.12.30 2019.01.04
2019.01.02 2019.01.07
2019.01.03 2019.01.08
2019.01.05 2019.01.10
2019.01.09 2019.01.14
2019.01.10 2019.01.15
q)t[`Date]within/:flip(-5+t`Date;t`Date)
10000000b
11000000b
11100000b
01110000b
00111000b
00011100b
00000110b
00000111b

这将返回一个布尔列表列表,可以使用where each 将其转换为索引(每个都因为它是一个列表列表),然后索引回值。

q)where each t[`Date]within/:flip(-5+t`Date;t`Date)
,0
0 1
0 1 2
1 2 3
2 3 4
3 4 5
5 6
5 6 7
q)t[`Value]where each t[`Date]within/:flip(-5+t`Date;t`Date)
,150
150 100
150 100 200
100 200 300
200 300 100
300 100 150
150 200
150 200 100

然后使用sum each,您可以对每个数字列表求和以获得您想要的结果。

q)sum each t[`Value]where each t[`Date]within/:flip(-5+t`Date;t`Date)
150 250 450 600 600 550 350 450

【讨论】:

  • 解释得很好。
【解决方案2】:

您也可以使用如下所示的更新语句来实现此目的。它不需要翻转,因此应该执行得更快。

q)N:5
q)delete s from update runningSum:s-0^s[Date bin neg[1]+Date-N] from update s:sums Value from t
Date       Value runningSum
---------------------------
2019.01.01 150   150
2019.01.02 100   250
2019.01.04 200   450
2019.01.07 300   600
2019.01.08 100   600
2019.01.10 150   550
2019.01.14 200   350
2019.01.15 100   450

这可以在 Value 列上使用 sums,然后使用 bin 来查找 N 天前的运行计数。 delete 关键字然后删除求和值列以获得所需的结果

q)\t:1000 delete s from update runningSum:s-0^s[Date bin neg[1]+Date-N] from update s:sums Value from t
7

虽然对于较小的 N 值,此答案与 Elliot 之间的时间差可以忽略不计,但对于较大的值,例如1000,这个更快

q)\t:1000 update Sum:sum each Value where each Date within/:flip(Date-1000;Date)from t
11
q)\t:1000 delete s from update runningSum:s-0^s[Date bin neg[1]+Date-1000] from update s:sums Value from t
7

需要注意的是,这个答案需要对日期字段进行排序,而 Elliot 的则不需要。

另一种稍慢的方法是为介于 min 和 max Date 之间的所有日期生成 0 值。 然后可以使用移动总和 msums 来获取过去 5 天的值。

它首先从表中获取minmax 日期,并列出它们之间的日期。

q)update t: 0^Value from ([]Date:{[x]  x[0]+til 1+x[1]-x[0]} exec (min[Date], max Date) from t) lj `Date xkey t
Date       Value t
--------------------
2019.01.01 150   150
2019.01.02 100   100
2019.01.03       0
2019.01.04 200   200
2019.01.05       0
2019.01.06       0
2019.01.07 300   300
2019.01.08 100   100
2019.01.09       0
2019.01.10 150   150

然后它将它们添加到表中并填充空值。考虑到任何丢失的数据,这将仅适用于前 N 天

q){[x] select from x where not null Value } update t: 5 msum 0^Value from ([]Date:{[x]  x[0]+til 1+x[1]-x[0]} exec (min[Date], max Date) from t) lj `Date xkey t
Date       Value t
--------------------
2019.01.01 150   150
2019.01.02 100   250
2019.01.04 200   450
2019.01.07 300   500
2019.01.08 100   600
2019.01.10 150   550
2019.01.14 200   350
2019.01.15 100   300

在使用 Value 作为列名时我也会小心,因为value 关键字可能会遇到问题

我希望这能回答你的问题

【讨论】:

    【解决方案3】:

    窗口连接在这里非常合适。见:https://code.kx.com/v2/ref/wj/

    q)wj1[-5 0+\:t`Date;`Date;t;(t;(sum;`Value))]
    Date       Value
    ----------------
    2019.01.01 150
    2019.01.02 250
    2019.01.04 450
    2019.01.07 600
    2019.01.08 600
    2019.01.10 550
    2019.01.14 350
    2019.01.15 450
    

    要返回 5 个观察而不是 5 个日历日,您可以这样做:

    q)wj1[{(4 xprev x;x)}t`Date;`Date;t;(t;(sum;`Value))]
    Date       Value
    ----------------
    2019.01.01 150
    2019.01.02 250
    2019.01.04 450
    2019.01.07 750
    2019.01.08 850
    2019.01.10 850
    2019.01.14 950
    2019.01.15 850
    

    【讨论】:

    • 谢谢,有没有办法也按天切断?
    • 您的意思是让它返回 5 个观察而不是 5 个日历日?如果是这样,那么是的,你可以这样做wj1[{(4 xprev x;x)}t`Date;`Date;t;(t;(sum;`Value))]
    【解决方案4】:

    您可以使用移动窗口mwin功能来实现:

    mwin:{[f;w;l] f each {1_x,y}\[w#0n;`float$l]}
    

    然后,您可以将函数 f 设置为 sum 并在过去的 w:5 天获得所需的值列表 l(此处为 l:exec Value from t):

    update Sum:(mwin[sum;5;] exec Value from t) from t
    
    Date       Value Sum
    --------------------
    2019.01.01 150   150
    2019.01.02 100   250
    2019.01.04 200   450
    2019.01.07 300   750
    2019.01.08 100   850
    2019.01.10 150   850
    2019.01.14 200   950
    2019.01.15 100   850
    

    【讨论】:

    • 请注意,无论日期跨度如何,这都会采用最后 5 条记录,而不是日历日期减 5。这可能是也可能不是原始问题所要查找的内容
    猜你喜欢
    • 1970-01-01
    • 2019-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-05
    • 2019-03-07
    • 1970-01-01
    相关资源
    最近更新 更多