【问题标题】:Get all rows before and n rows after a specific value within group获取组内特定值之前的所有行和之后的 n 行
【发布时间】:2020-02-27 01:09:59
【问题描述】:

我正在寻找执行以下操作的简单 R/SQL 代码。在特定公司中,它会在 VALUE 列中查找 1 的第一个实例,然后

  1. 提取之前的所有行(VALUE 列中的所有行都为 0)和
  2. 只要这两行的 VALUE 为 0,则正好是两行。

代码为所有公司执行此操作。

所以这张桌子....

----------------------
| FIRM | YEAR | VALUE |
----------------------
|  A   | 2007 |  0   |
----------------------
|  A   | 2008 |  0   |
----------------------
|  A   | 2009 |  0   |
----------------------
|  A   | 2010 |  1   |
----------------------
|  A   | 2011 |  0   |
----------------------
|  A   | 2012 |  0   |
----------------------
|  B   | 2009 |  0   |
----------------------
|  B   | 2010 |  1   |
----------------------
|  B   | 2011 |  0   |
----------------------
|  C   | 2010 |  0   |
----------------------
|  C   | 2011 |  1   |
----------------------
|  C   | 2012 |  1   |
----------------------

看起来像这样......

--------------------------
| FIRM  | YEAR  | VALUE  |
--------------------------
|  A   | 2007 |  0   |
----------------------
|  A   | 2008 |  0   |
----------------------
|  A   | 2009 |  0   |
----------------------
|  A   | 2010 |  1   |
----------------------
|  A   | 2011 |  0   |
----------------------
|  A   | 2012 |  0   |
----------------------

非常感谢您的帮助。谢谢。

【问题讨论】:

    标签: sql r conditional-statements extract


    【解决方案1】:

    使用 R,您可以创建一个函数,该函数将返回要选择的行号。

    get_rows <- function(VALUE) {
       ind <- which(VALUE == 1)[1]
       if ((ind + 2) <= length(VALUE) && all(VALUE[c(ind + 1,ind + 2)] == 0))
         sort(c(which(VALUE[seq_len(ind + 2)] == 0), ind))
       else 0
    }
    

    并将其应用于每个FIRM

    library(dplyr)
    df %>% group_by(FIRM) %>% slice(get_rows(VALUE))
    
    #  FIRM   YEAR VALUE
    #  <fct> <int> <int>
    #1 A      2007     0
    #2 A      2008     0
    #3 A      2009     0
    #4 A      2010     1
    #5 A      2011     0
    #6 A      2012     0
    

    数据

    df <- structure(list(FIRM = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 2L, 
    2L, 2L, 3L, 3L, 3L), .Label = c("A", "B", "C"), class = "factor"), 
    YEAR = c(2007L, 2008L, 2009L, 2010L, 2011L, 2012L, 2009L, 
    2010L, 2011L, 2010L, 2011L, 2012L), VALUE = c(0L, 0L, 0L, 
    1L, 0L, 0L, 0L, 1L, 0L, 0L, 1L, 1L)), class = "data.frame",row.names = c(NA, -12L))
    

    【讨论】:

    • 您好 Ronak,感谢您提供此解决方案。我在我的示例上进行了尝试,它引发了以下错误-“seq_len(ind + 2) 中的错误:参数必须强制转换为非负整数”。会知道是什么问题吗?
    • @M1001 看起来你有某些FIRM,其中甚至没有一个1。在这种情况下你想做什么?
    • 嗨 Ronak,所以我修复了上一个问题。我确保我有一个样本,其中至少有一个 1。代码现在运行。但是,它甚至包括第一个 1 之后没有两个 0 的情况。所以它包括 1 之前有 0,但之后没有 0 的情况。您能否修改代码以确保它仅选择 1 后强制为两个 0 的情况。再次感谢您的帮助。
    • @M1001 因此,如果 1 之前有 0 而 1 之后没有 2 个 0,您不想包含任何行或只是忽略 1 之后的下一行?例如,在您的示例中,组“B”和“C”会输出什么?不会为它们选择任何行/?
    • 嗨,罗纳克,正是。如果在 1 的第一个实例之后没有两个 0,则应忽略整个 FIRM。在 1 之前没有任何 0 是可以的,但在 1 之后必须正好有两个 0。正是出于这个原因,在上面的示例中,只选择了 A。 B 和 C 没有,因为它们在 1 之后没有 2 个 0。谢谢。
    【解决方案2】:

    您可以计算最小年份,然后使用此信息:

    with t as (
          select firm, min(year) as min_year_1
          from tab t
          where value = 1
          group by firm
         )
    select t.*
    from (select t.*,
                 lag(value) over (partition by firm order by year) as prev_value,
                 lead(value) over (partition by firm order by year) as next_value
          from tab t
         ) t join
         tt
         on tt.firm = t.firm
    where t.year <= tt.min_year or
          (t.year = tt.min_year + 1 and
           t.value = 0 and
           t.next_value = 0
          ) or
          (t.year = tt.min_year + 2 and
           t.value = 0 and
           t.prev_value = 0
          );
    

    0 之后两行的最后一个条件相当棘手。

    这假设年份是连续的,没有间隔,这与您问题中的数据一致。

    编辑:

    您可以只使用窗口函数来做到这一点:

    select t.*
    from (select t.*,
                 count(*) over (partition by firm, running_value) as cnt,
                 row_number() over (partition by firm, running_value) as seqnum
          from (select t.*,
                       sum(value) over (partition by firm order by year) as running_value
                from tab t
               ) t
          ) t
    where running_value = 0 or
          (running_value = 1 and seqnum = 1) or  -- first "1"
          (running_value = 1 and seqnum <= 3 and
           cnt >= 3);
    

    【讨论】:

    • 关于 CTE 的注意事项:不确定这是否适用于 R 中的 sqldf
    • 嗨,戈登,感谢您的代码。如果我的表名是 TAB,我应该用 TAB 替换 t 和 tt。也不应该“min_year_1”实际上是“min_year”。感谢您提供帮助。
    • @M1001 。 . .我将表名添加到查询中。
    猜你喜欢
    • 1970-01-01
    • 2020-09-11
    • 2020-10-08
    • 2021-12-22
    • 2017-10-21
    • 1970-01-01
    • 2020-10-06
    • 1970-01-01
    • 2022-12-24
    相关资源
    最近更新 更多