填补雪花中缺失的数据

Posted

技术标签:

【中文标题】填补雪花中缺失的数据【英文标题】:Filling in missing data in Snowflake 【发布时间】:2021-07-23 21:37:04 【问题描述】:

我在雪花中有一张这样的桌子:

TIME   USER   ITEM
1      frank  1
2      frank  0
3      frank  0
4      frank  0
5      frank  2
6      alf    5
7      alf    0
8      alf    6
9      alf    0
10     alf    9

我希望能够用下一个非零值替换所有零,所以最后我有一个这样的表:

TIME   USER   ITEM
1      frank  1
2      frank  2
3      frank  2
4      frank  2
5      frank  2
6      alf    5
7      alf    6
8      alf    6
9      alf    9
10     alf    9

如何编写在 Snowflake 中执行此操作的查询?

【问题讨论】:

这张表中数据的顺序是什么?您应该注意 SQL 中没有“自然顺序”,因此每次您执行其中的SELECT * 时,它们都可以以任意随机顺序出现(包括 0) 抱歉,我只是没有列出时间戳。我将编辑帖子。 项目在您的订购中是否像您的示例中那样严格增加(不包括零)?如果是这样,一个简单的运行 max() 将起作用 【参考方案1】:

您可以为此使用 conditional_change_event 函数 - documented here:

with base_table as (
    select
        t1.*,
        conditional_change_event(item) over (order by time desc) event_num
    from test_table t1
    order by time desc
)
select
    t1.time,
    t1.user,
    t1.item                    old_item,
    coalesce(t2.item, t1.item) new_item
from base_table t1
   left join base_table t2 on t1.event_num = t2.event_num + 1 and t1.item = 0
order by t1.time asc

以上 SQL 结果:

+----+-----+--------+--------+
|TIME|USER |OLD_ITEM|NEW_ITEM|
+----+-----+--------+--------+
|1   |frank|1       |1       |
|2   |frank|0       |2       |
|3   |frank|0       |2       |
|4   |frank|0       |2       |
|5   |alf  |2       |2       |
|6   |alf  |5       |5       |
|7   |alf  |0       |6       |
|8   |alf  |6       |6       |
|9   |alf  |0       |9       |
|10  |alf  |9       |9       |
+----+-----+--------+--------+

【讨论】:

【参考方案2】:

你可以使用lead(ignore nulls):

select t.*,
       (case when item = 0
             then lead(nullif(item, 0) ignore nulls) over (partition by user order by time)
             else item
        end) as imputed_item
from t;

您也可以使用first_value()

select t.*,
       last_value(nullif(item, 0) ignore nulls) over (partition by user order by time desc)
from t;

【讨论】:

+1 用于使用 LEAD IGNORE NULLS(我会使用 ZEROIFNULL())。 LAST_VALUE 在当前形式下不起作用,请检查 (1,0,2,0,3) -> (1,3,2,3,3) 而不是 (1,2,2,3,3) @LukaszSzozda 。 . .我更喜欢NULLIF(),因为它是标准 SQL。第二个应该与first_value()一起使用。【参考方案3】:

如果您想在 Snowflake 中使用 first_value() 或 last_value(),请记住,Snowflake 支持的窗口框架不同于文档 here 中的 ANSI 标准。这意味着,如果您想使用默认窗口框架 RANGE BETWEEN UNBOUNDED PRECEDING 和 CURRENT ROW,您必须在语句中明确包含它,否则,默认值将是 ROWS BETWEEN UNBOUNDED PRECEDING 和 UNBOUNDED FOLLOWING,这就是为什么 LAST_VALUE 示例来自以前的答案将无法正常工作。这是一个可行的示例:

select t.*,
       last_value(nullif(item, 0) ignore nulls) over (partition by user order by time desc rows between unbounded preceding and current row)
from t;

【讨论】:

【参考方案4】:

上述解决方案没有错……但这里有一种不同的方法……我认为它更简单。

select * from good
union all
select 
     bad.time
    ,bad.user
    ,min(good.item) 
from  bad 
left outer join  
good on good.user=bad.user and good.time>bad.time 
group by
    1,2

完整复制|粘贴|运行 SQL:

with cte as (
select * from (
select 1  time, 'frank' user , 1 item union
select 2  time, 'frank' user , 0 item union
select 3  time, 'frank' user , 0 item union
select 4  time, 'frank' user , 0 item union
select 5  time, 'frank' user , 2 item union
select 6  time, 'alf' user ,   5 item union
select 7  time, 'alf' user ,   0 item union
select 8  time, 'alf' user ,   6 item union
select 9  time, 'alf' user ,   0 item union
select 10 time, 'alf' user ,   9) )
, good as (select * from cte where item<> 0) 
, bad as (select * from cte where item= 0) 


select *  from  good
union all
select 
     bad.time
    ,bad.user
    ,min(good.item ) 
from  bad 
left outer join  
    good on good.user=bad.user and good.time>bad.time 
group by
    1,2

【讨论】:

以上是关于填补雪花中缺失的数据的主要内容,如果未能解决你的问题,请参考以下文章

数据分析中缺失值填补的常见方法

使用 statsD 时,有啥方法可以填补石墨中缺失的数据?

oracle查询填补同一张表中缺失的数据

单元无回答的缺失数据处理方法

R语言:用R语言填补缺失的数据

特征工程-使用随机森林进行缺失值填补