【问题标题】:Duplicate the rows based on some criteria in SQL or R根据 SQL 或 R 中的某些条件复制行
【发布时间】:2018-01-04 17:51:11
【问题描述】:

我使用 R 生成玩具套装

data.frame(name = c("Tom", "Shane", "Daniel", "Akira", "Jack", "Zoe"), c1 = c(1,2,3,0,5,0), c2 = c(0, 3, 5, 0,4,0), c3 = c(0, 0,1,0,0,3), c4=c(0,0,0,1,0,0))

如下所示:

我只关心列c1, c2, c3, c4,如果特定行有多个值,大于0。我们需要复制行以确保只有一个值,大于0 ,然后删除原始行。

比如第二行有两个值都大于0(c1:2,c2:3),那么我们就要把那行复制成两个,如下所示

Shane 2 0 0 0

Shane 0 3 0 0

我正在尝试构建一个 SQL 查询来捕获这一点。但是,我不确定是否有任何 SQL 函数可以在不先查看结果的情况下检测特定行中的多个非零值。无论如何,如果存在任何神奇的 SQL 函数,最终结果应该是这样的:

我也想用 R 来完成它。我知道的唯一可以复制行的 R 函数是 do.call() 函数,然后将它与 rbind() 函数结合使用。但是,它不适用于我的情况。你能给我任何提示吗?非常感谢:)

【问题讨论】:

  • 请在发布数据时发布实际数据(例如,使用dput)而不是其图像。我无法突出显示此图像并粘贴到我的 R 会话中以尝试使用某些东西来帮助您。我还没有无聊到将你所有的数据转录成一个示例 data.frame 来播放。我建议你阅读reproducible questions 以获得更多关于如何做好这件事的指导,让我们其他人更容易。谢谢!
  • @r2evans:很抱歉。这是生成玩具套装的 R 代码:` data.frame(name = c("Tom", "Shane", "Daniel", "Akira", "Jack", "Zoe"), c1 = c(1 ,2,3,0,5,0), c2 = c(0, 3, 5, 0,4,0), c3 = c(0, 0,1,0,0,3), c4=c( 0,0,0,1,0,0))`
  • 请不要在 cmets 中发布代码。编辑问题,看起来会好很多。

标签: sql r rbind do.call


【解决方案1】:

也许是另一个使用CROSS APPLY的选项

示例

Select A.Name
      ,B.*
 From  YourTable A
 Cross Apply ( values (C1,0,0,0)
                     ,(0,C2,0,0)
                     ,(0,0,C3,0)
                     ,(0,0,0,C4)
             ) B (C1,C2,C3,C4)
 Where B.C1+B.C2+B.C3+B.C4<>0

退货

【讨论】:

  • 但是@John .. 你确定 OP 使用的是 SQL Server?
  • @VamsiPrabhala 昨晚为她回答了一个 SQL Server 问题,OP 也向我发送了这个问题。所以我 100% 确定...不,但我会 98% 确定:)
  • @JohnCappelletti 非常感谢。我以前从未听说过“交叉应用”功能,而且效果很好。今天学到了很多很酷的东西!
  • @AkiraKaneshiro 非常有用的功能。我倾向于将其视为子程序。交叉应用将排除 NULL 记录集,而外部应用将包括 NULL 记录集。我每天都在 SO 上学到一些东西。这就是有趣的部分
【解决方案2】:

您可以使用几个tidyverse 函数来完成此操作。首先,我们输入您的样本数据

library(tidyverse)
dd <- tribble(~name, ~c1, ~c2, ~c3, ~c4,
        "Tom", 1, 0, 0, 0,
        "Shane", 2, 3, 0, 0,
        "Daniel", 3, 5, 1, 0,
        "Akira", 0, 0, 0 ,1,
        "Jack", 5, 4, 0, 0,
        "Zoe", 0, 0, 3, 0)

然后我们收集、过滤和传播以获得您想要的行。通过添加行 id,我们将不同的值保留在不同的行上。

dd %>% 
  gather("var", "val", -name) %>% 
  rowid_to_column() %>% 
  filter(val>0) %>% 
  spread(var, val, fill=0) %>% 
  select(-rowid)
# A tibble: 10 x 5
#      name    c1    c2    c3    c4
#  *  <chr> <dbl> <dbl> <dbl> <dbl>
#  1    Tom     1     0     0     0
#  2  Shane     2     0     0     0
#  3 Daniel     3     0     0     0
#  4   Jack     5     0     0     0
#  5  Shane     0     3     0     0
#  6 Daniel     0     5     0     0
#  7   Jack     0     4     0     0
#  8 Daniel     0     0     1     0
#  9    Zoe     0     0     3     0
# 10  Akira     0     0     0     1

【讨论】:

  • 非常感谢优雅而美丽的解决方案。一个问题是代码中的“val”和“var”是什么意思?
  • 收集步骤添加了新列,这些只是我为这些新列选择的名称。如果你愿意,你可以给他们起别的名字。
【解决方案3】:

使用union all 的另一个选项。

select name,c1,0 as c2,0 as c3,0 as c4 from tbl where c1>0
union all
select name,0,c2,0,0 from tbl where c2>0
union all
select name,0,0,c3,0 from tbl where c3>0
union all
select name,0,0,0,c4 from tbl where c4>0

【讨论】:

    【解决方案4】:
    df1 = data.frame(name = c("Tom", "Shane", "Daniel", "Akira", "Jack", "Zoe"),
                     c1 = c(1,2,3,0,5,0),
                     c2 = c(0, 3, 5, 0,4,0),
                     c3 = c(0, 0,1,0,0,3),
                     c4=c(0,0,0,1,0,0))
    
    df2 = df1[rep(1:NROW(df1), apply(df1, 1, function(x) sum(x[-(1)] > 0))),]
    df3 = df2
    df3[-1] = df3[-1] * 0
    df3[ave(1:NROW(df2), df2$name, FUN = length) == 1,] = df2[ave(1:NROW(df2), df2$name, FUN = length) == 1,]
    replace(x = df3,
            list = cbind(1:NROW(df3), 1+ave(1:NROW(df2), df2$name, FUN = seq_along)),
            values = df2[cbind(1:NROW(df3), 1+ave(1:NROW(df2), df2$name, FUN = seq_along))])
    #      name c1 c2 c3 c4
    #1      Tom  1  0  0  0
    #2    Shane  2  0  0  0
    #2.1  Shane  0  3  0  0
    #3   Daniel  3  0  0  0
    #3.1 Daniel  0  5  0  0
    #3.2 Daniel  0  0  1  0
    #4    Akira  0  0  0  1
    #5     Jack  5  0  0  0
    #5.1   Jack  0  4  0  0
    #6      Zoe  0  0  3  0
    

    【讨论】:

      【解决方案5】:

      考虑带有by 的base R,它为每个不同的名称构建一个零填充数据框,然后将所有数据框绑定到最后一个,类似于联合SQL:

      df_list <- by(df, df$name, FUN = function(d){
      
        tmp <- data.frame(name = d$name[1],
                   c1 = c(max(d$c1), rep(0, 3)),
                   c2 = c(0, max(d$c2), rep(0, 2)),
                   c3 = c(rep(0, 2), max(d$c3), 0),
                   c4 = c(rep(0, 3), max(d$c4)))
      
        tmp <- tmp[rowSums(tmp[-1])!=0,]
        row.names(tmp) <- NULL
        tmp
      
      })
      
      final_df <- do.call(rbind, unname(df_list))
      final_df
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-11-03
        • 1970-01-01
        • 2018-07-11
        • 2015-07-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多