【问题标题】:ANSI SQL equivalent of pandas `factorize()`?ANSI SQL相当于熊猫`factorize()`?
【发布时间】:2021-06-24 14:52:58
【问题描述】:

所以我必须从用户的电子邮件中创建用户 ID,并且所有数据都存在于 BigQuery 中。在 python、pandas 中,它是一个简单的单行代码:

all_data['user_id'] = all_data['email'].factorize()[0]

但我想不出在 BigQuery SQL 中执行此操作的方法。我尝试使用RANK() 函数,但它离得很近。目前,我正在尝试使用 RANK() 的 Window 函数,但对于这样一个简单的任务,使用这种方法似乎有点牵强。所有数据都已经在 BigQuery 上,所以即使在 SQL 之外以其他方式执行此操作的任何建议也很好。

一点上下文...

  1. pandas factorize() 函数根据提供的列分配唯一 ID,因此如果电子邮件类似于 email1@example.com, email2@example.com, email1@example.com, email3@example.com, email1@example.com, email2@example.com,它将返回:[0, 1, 0, 2, 0, 1] 等等。

  2. 我在数据库中还有其他列,所以 RANK()ROW_NUMBER() 似乎不能单独提供帮助。我正在尝试绕过。

【问题讨论】:

  • 你的意思是ROW_NUMBER()
  • 没有。因为电子邮件可以重复。 ROW_NUMBER() 一个人是不行的。这就是我现在想要实现的目标
  • 那么也许DENSE_RANK?
  • 这仍然包含整行。我需要的是从电子邮件中生成基于列的 ID,同时仅考虑电子邮件
  • 使用 DENSE_RANK() OVER (ORDER BY email) 应该就是这样 AFAIK。

标签: python pandas google-bigquery


【解决方案1】:

您可以为此目的使用DENSE_RANK() 窗口函数:

select dataset.*, DENSE_RANK() OVER (ORDER BY email)
from dataset
order by sent;

这会产生类似(使用Mikhail Berlyant's example data 作为起点):

SENT EMAIL DENSE_RANK
2021-01-01 00:01:00 email4@example.com 3
2021-01-01 00:02:00 email2@example.com 1
2021-01-01 00:03:00 email4@example.com 3
2021-01-01 00:04:00 email3@example.com 2
2021-01-01 00:05:00 email4@example.com 3
2021-01-01 00:06:00 email2@example.com 1

【讨论】:

    【解决方案2】:

    考虑以下两个选项

    注意,我使用的是稍微修改过的数据示例 - 你会明白为什么(我希望)

    with `project.dataset.table` as (
      select '2021-01-01 00:01:00' sent , 'email4@example.com' recipient  union all 
      select '2021-01-01 00:02:00', 'email2@example.com' union all 
      select '2021-01-01 00:03:00', 'email4@example.com' union all 
      select '2021-01-01 00:04:00', 'email3@example.com' union all 
      select '2021-01-01 00:05:00', 'email4@example.com' union all 
      select '2021-01-01 00:06:00', 'email2@example.com'
    )
    

    选项 1:

    如果在分配 unique_id 之前应该设置这些电子邮件的顺序 - 例如通过 sent 列。在这种情况下,请考虑以下

    #standardSQL
    create temp function factorize(item string, list any type) as ((
      select unique_id from (
        select as struct recipient, row_number() over(order by min(sent)) - 1 unique_id
        from unnest(list)
        group by recipient
      ) 
      where recipient = item
    ));
    select t.*, 
      factorize(recipient, array_agg(struct(recipient, sent)) over()) unique_id 
    from `project.dataset.table` t
    

    有输出

    选项 2:

    如果排序无关紧要,您可以按字母顺序排序 - 考虑以下稍微 [在我看来] 使用内置 range_bucket 函数的更简单查询

    #standardSQL
    create temp function factorize(item string, list any type) as (
      range_bucket(item, list) - 1 
    );
    with all_recipients as (
      select array_agg(recipient order by recipient) recipients from (
        select recipient
        from `project.dataset.table`
        group by recipient
      )
    )
    select t.*,
      factorize(recipient, recipients) unique_id
    from `project.dataset.table` t, all_recipients         
    

    有输出

    显然,在这种情况下,您可以跳过使用 udf,而只需在最终选择中使用 rabge_bucket(而不是在 udf 中)

    select t.*,
      range_bucket(recipient, recipients) - 1 unique_id
    

    【讨论】:

    • 你试过了吗?如果它对你有用(我认为应该) - 考虑投票并接受答案
    猜你喜欢
    • 2016-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-01
    • 2018-02-02
    • 2016-08-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多