【问题标题】:Match a value within a subset and return a value匹配子集中的一个值并返回一个值
【发布时间】:2018-09-11 09:51:56
【问题描述】:

我想在满足某些条件时返回 ProjectID。例如,在每个集群中存在项目 C 和项目 A 的以下数据中,C 的 ProjectID 将在项目 A 的行中返回。我在下面放置了一个示例,ProjectID 在 Dependent 列中返回。我试图通过使用 dplyr 中的 group_by 函数将每个集群分成组来解决问题,但是我不确定如何在每个组中查找项目以查看是否满足条件(在这种情况下,项目 C 在集群内)并返回 ProjectID。非常感谢任何有关如何解决的建议。

Cluster Project ProjectID Dependent
Aaa        A         1       3
Aaa        B         2
Aaa        C         3
Bbb        A         4
Bbb        B         5
Ccc        A         6       8
Ccc        B         7
Ccc        C         8
Ccc        D         9

【问题讨论】:

  • 与 C 相关的 Anis B 和 D 呢?您是否还应该在存在 C 的依赖项下输出 1 ?
  • 只有在集群中有项目C的情况下,项目A才需要。 B 和 D 不依赖于项目 C,因此不需要任何值。

标签: r if-statement dplyr match


【解决方案1】:

另一种dplyr的方式,好像比@missuse的方式快一点:

df1 %>%
  group_by(Cluster) %>%
  mutate(Dependent= `[<-`(rep_len(NA_integer_,n()),
                          Project=="A",
                          value=ProjectID[match("C",Project)]))

# # A tibble: 9 x 4
# # Groups:   Cluster [3]
# Cluster Project ProjectID Dependent
# <fctr>  <fctr>     <int>     <int>
# 1     Aaa       A         1         3
# 2     Aaa       B         2        NA
# 3     Aaa       C         3        NA
# 4     Bbb       A         4        NA
# 5     Bbb       B         5        NA
# 6     Ccc       A         6         8
# 7     Ccc       B         7        NA
# 8     Ccc       C         8        NA
# 9     Ccc       D         9        NA

【讨论】:

    【解决方案2】:

    data.table 的解决方法

    df1[Project == 'A'][df1[Project == 'C'], 
    Dependent := i.ProjectID, 
    on = 'Cluster'][df1, on =
    c('Cluster', 'Project', 'ProjectID')]
    

    子集 df1 其中project=='A',

    然后右连接 df1 的子集,其中Project =='C',在'Cluster'

    然后在集群、项目和项目 ID 上再次右加入自身。

    我不确定这是否会在大型数据集上运行,因为它有很多自联接,尤其是在数据重复时,因为如果您有重复的键值,数据表将不允许您加入。

       Cluster Project ProjectID Dependent
    1:     Aaa       A         1         3
    2:     Aaa       B         2        NA
    3:     Aaa       C         3        NA
    4:     Bbb       A         4        NA
    5:     Bbb       B         5        NA
    6:     Ccc       A         6         8
    7:     Ccc       B         7        NA
    8:     Ccc       C         8        NA
    9:     Ccc       D         9        NA
    

    希望对你有帮助

    【讨论】:

      【解决方案3】:

      我认为这将提供预期的输出:

      library(tidyverse)
      df1 %>%
        group_by(Cluster) %>%
        mutate(Dependent = ifelse(Project == "A", ProjectID[Project=="C"], NA))
      #output
      # A tibble: 9 x 4
      # Groups: Cluster [3]
        Cluster Project ProjectID Dependent
        <fct>   <fct>       <int>     <int>
      1 Aaa     A               1         3
      2 Aaa     B               2        NA
      3 Aaa     C               3        NA
      4 Bbb     A               4        NA
      5 Bbb     B               5        NA
      6 Ccc     A               6         8
      7 Ccc     B               7        NA
      8 Ccc     C               8        NA
      9 Ccc     D               9        NA
      

      在每个集群中,如果项目 A 返回项目 C 的项目 ID,否则返回 NA

      数据:

      df1 <- read.table(text="Cluster Project ProjectID 
      Aaa        A         1       
      Aaa        B         2
      Aaa        C         3
      Bbb        A         4
      Bbb        B         5
      Ccc        A         6       
      Ccc        B         7
      Ccc        C         8
      Ccc        D         9", header = TRUE)
      

      基准测试提供了小数据集的答案

      library(microbenchmark)
      
      microbenchmark(missuse = df1 %>%
                       group_by(Cluster) %>%
                       mutate(Dependent = ifelse(Project == "A", ProjectID[Project=="C"], NA)),
                     Rui_Barradas = lapply(split(df1, df1$Cluster), function(DF){
                       DF$Dependent <- NA
                       if(any(DF$Project == "A") && any(DF$Project == "C"))
                         DF$Dependent[DF$Project == "A"] <- DF$ProjectID[DF$Project == "C"]
                       DF
                     }),
                     MKR = left_join(df1,filter(df1, Project=="C"),  by="Cluster") %>%
                         mutate(Dependent = ifelse(Project.x == "A",  ProjectID.y, NA)) %>%
                         select(Cluster, Project = Project.x, ProjectID = ProjectID.x, Dependent)
                     )
      
      Unit: milliseconds
               expr      min       lq      mean    median        uq      max neval cld
            missuse 3.525404 3.566450  4.220243  3.604535  3.785439 40.69046   100  b 
       Rui_Barradas 1.390526 1.423534  1.685952  1.495683  1.511552 16.30843   100 a  
                MKR 9.770077 9.959867 10.605632 10.215248 10.592078 21.14565   100   c
      

      更大数据集(90k 行)的基准测试

      df1 <- df1[rep(1:nrow(df1), times = 10000),]
      
      microbenchmark(missuse = df1 %>%
                       group_by(Cluster) %>%
                       mutate(Dependent = ifelse(Project == "A", ProjectID[Project=="C"], NA)),
                     Rui_Barradas = lapply(split(df1, df1$Cluster), function(DF){
                       DF$Dependent <- NA
                       if(any(DF$Project == "A") && any(DF$Project == "C"))
                         DF$Dependent[DF$Project == "A"] <- DF$ProjectID[DF$Project == "C"]
                       DF
                     }), times = 20)
      
      Unit: milliseconds
               expr      min       lq     mean   median       uq      max neval cld
            missuse 25.05783 25.53072 29.95501 25.83243 28.49352 55.34345    20  a 
       Rui_Barradas 35.42203 36.85572 47.61315 39.87882 56.25432 95.80752    20   b
      

      在 900k 行上:

      df1 <- df1[rep(1:nrow(df1), times = 100000),] #original df1
      
      Unit: milliseconds
               expr      min       lq      mean    median       uq      max neval cld
            missuse 466.6968 721.9709  945.8628 1062.6262 1101.914 1255.214    20   a
       Rui_Barradas 718.8869 768.0912 1077.7594  934.1785 1308.145 1854.415    20   a
      

      我在最后两个基准测试中遗漏了 MKR 答案,因为它使我的会话崩溃。

      免责声明:我在土豆 PC 上运行基准测试。稍后我将在另一台更新的 PC 上重新测试,如果结果不同(就相对性能而言),我将更新答案。

      更新:我觉得(由我)生成的数据有点欺骗性。这是另一个尝试:

      df1 <- df1[rep(1:nrow(df1), times = 10000),]
      
      df1 %>%
        mutate(rle = rleid(Cluster)) %>%
        mutate(Cluster = paste(Cluster, rle, sep = "_")) %>%
        select(-rle) -> df1
      
      MKR2 <- function(df1){
      setDT(df1)
      df1[Project == "A"][df1[Project == "C"], on="Cluster", nomatch=0][
        df1, on=.(Cluster, Project)][
          ,.(Cluster, Project, ProjectID = i.ProjectID.1, Dependent = i.ProjectID)]
      }
      

      所以有很多小组的数据

      这里我不得不省略 Rui Barradas 解决方案,因为它花费的时间太长:

      microbenchmark(missuse = df1 %>%
                       group_by(Cluster) %>%
                       mutate(Dependent = ifelse(Project == "A", ProjectID[Project=="C"], NA)),
                     MKR = left_join(df1,filter(df1, Project=="C"),  by="Cluster") %>%
                         mutate(Dependent = ifelse(Project.x == "A",  ProjectID.y, NA)) %>%
                         select(Cluster, Project = Project.x, ProjectID = ProjectID.x, Dependent),
                     MKR2(df1),
                     times = 10
                     )
      
      Unit: milliseconds
            expr        min        lq      mean    median        uq        max neval cld
         missuse 7445.97748 7815.2364 9609.4009 8350.0508 9565.2411 19965.5040    10   b
             MKR   55.61109   59.9900  123.2263   80.4056  191.7361   250.5065    10  a 
       MKR2(df1)  100.97692  216.4811  994.8457  277.3159 1452.0668  4011.1804    10  a 
      

      有趣的东西

      【讨论】:

      • 谢谢伙计。显然,在这种情况下,自我加入没有帮助。我认为加入本可以更聪明一点。
      • 我知道。仅在data.table 中自联接更快。我需要修改我的答案。
      • 只有最后一个select 部分占用了50% 的总时间。
      • 这让我感到惊讶。什么是 MKR2?是data.table吗?
      • 啊,好吧。也许 (setDT) 已经放慢了速度,否则它应该更快。谢谢你所有的努力伙伴。它给出了有趣的结果。
      【解决方案4】:

      已经提供了一些好的答案,但我认为另一种方法可以使用 left_joinself 作为:

      library(dplyr)
      left_join(df1,filter(df1, Project=="C"),  by="Cluster") %>%
        mutate(Dependent = ifelse(Project.x == "A",  ProjectID.y, NA)) %>%
        select(Cluster, Project = Project.x, ProjectID = ProjectID.x, Dependent)
      

      data.table 方法:

      library(data.table)
      setDT(df1)
      df1[Project == "A"][df1[Project == "C"], on="Cluster", nomatch=0][
      df1, on=.(Cluster, Project)][
      ,.(Cluster, Project, ProjectID = i.ProjectID.1, Dependent = i.ProjectID)]
      
      
      #       Cluster Project ProjectID Dependent
      # 1     Aaa       A         1         3
      # 2     Aaa       B         2        NA
      # 3     Aaa       C         3        NA
      # 4     Bbb       A         4        NA
      # 5     Bbb       B         5        NA
      # 6     Ccc       A         6         8
      # 7     Ccc       B         7        NA
      # 8     Ccc       C         8        NA
      # 9     Ccc       D         9        NA
      

      希望这些方法的性能值得考虑。

      【讨论】:

        【解决方案5】:

        只有使用基础 R,您才能执行以下操作。

        sp <- lapply(split(dat, dat$Cluster), function(DF){
            DF$Dependent <- NA
            if(any(DF$Project == "A") && any(DF$Project == "C"))
                DF$Dependent[DF$Project == "A"] <- DF$ProjectID[DF$Project == "C"]
            DF
        })
        
        result <- do.call(rbind, sp)
        row.names(result) <- NULL
        result
        #  Cluster Project ProjectID Dependent
        #1     Aaa       A         1         3
        #2     Aaa       B         2        NA
        #3     Aaa       C         3        NA
        #4     Bbb       A         4        NA
        #5     Bbb       B         5        NA
        #6     Ccc       A         6         8
        #7     Ccc       B         7        NA
        #8     Ccc       C         8        NA
        #9     Ccc       D         9        NA
        

        数据。

        dat <-
        structure(list(Cluster = structure(c(1L, 1L, 1L, 2L, 2L, 3L, 
        3L, 3L, 3L), .Label = c("Aaa", "Bbb", "Ccc"), class = "factor"), 
            Project = structure(c(1L, 2L, 3L, 1L, 2L, 1L, 2L, 3L, 4L), .Label = c("A", 
            "B", "C", "D"), class = "factor"), ProjectID = 1:9), .Names = c("Cluster", 
        "Project", "ProjectID"), class = "data.frame", row.names = c(NA, 
        -9L))
        

        【讨论】:

          猜你喜欢
          • 2013-02-18
          • 1970-01-01
          • 2022-01-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-09-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多