【问题标题】:Subset dataframe based on duplicate values in multiple rows基于多行中的重复值的子集数据框
【发布时间】:2018-04-28 23:10:10
【问题描述】:

如何子集下面的数据框以仅显示行是列 AAA:CCC 都具有相同的值并保留 IndID 字段?

Dat <- data.frame(IndID = LETTERS[seq(1,10)],
                  AAA = c(1,5,3,2,3,1,5,4,6,2),
                  BBB = c(1,8,5,2,5,4,8,4,4,5),
                  CCC = c(1,5,3,2,3,5,5,4,6,5))

> Dat
   IndID AAA BBB CCC
1      A   1   1   1
2      B   5   8   5
3      C   3   5   3
4      D   2   2   2
5      E   3   5   3
6      F   1   4   5
7      G   5   8   5
8      H   4   4   4
9      I   6   4   6
10     J   2   5   5

我想返回以下结果。

Result <- data.frame(IndID = c("A", "D", "H"),
                  AAA = c(1,2,4),
                  BBB = c(1,2,4),
                  CCC = c(1,2,4))

> Result
  IndID AAA BBB CCC
1     A   1   1   1
2     D   2   2   2
3     H   4   4   4

我找到了许多相关的帖子,包括Find duplicated rows (based on 2 columns) in Data Frame in RFind duplicated elements with dplyr 等,但无法用三列重现所需的结果。例如,虽然关闭下面的代码显示所有 distinct 行,但包含行的不良结果只有两个值相等。

Dat %>% distinct(AAA, BBB, CCC) 

我怀疑解决方案涉及filter,但不确定如何从示例mentioned above 中获得所需的结果。首选dplyr 解决方案。

加法 我也想知道这是否可以应用于多个因素。例如下面的数据,其中包含一个IndID 和三个存储为因子的日期,是否可以通过允许因子或将因子更改为数值来产生类似的结果?

Dat <- structure(list(GenIndID = structure(c(1L, 2L, 6L, 7L, 3L, 4L, 
8L, 5L), .Label = c("BHS_601", "BHS_603", "BHS_604", "BHS_605", 
"BHS_631", "BHS_635", "BHS_636", "BHS_637"), class = "factor"), 
    CptrDate = structure(c(1L, 2L, 3L, 3L, 2L, 2L, 3L, 4L), .Label = c("2016-02-01", 
    "2016-02-02", "2016-02-04", "2016-12-11"), class = "factor"), 
    DtLastAlive = structure(c(2L, 2L, 1L, 1L, 2L, 2L, 1L, 3L), .Label = c("2016-02-04", 
    "2017-07-13", "2017-08-27"), class = "factor"), DtFnlFate = structure(c(2L, 
    2L, 1L, 1L, 2L, 2L, 1L, 3L), .Label = c("2016-02-04", "2017-07-13", 
    "2017-08-27"), class = "factor")), .Names = c("GenIndID", 
"CptrDate", "DtLastAlive", "DtFnlFate"), row.names = c(82L, 83L, 
224L, 225L, 84L, 85L, 226L, 360L), class = "data.frame")


> Dat
    GenIndID   CptrDate DtLastAlive  DtFnlFate
82   BHS_601 2016-02-01  2017-07-13 2017-07-13
83   BHS_603 2016-02-02  2017-07-13 2017-07-13
224  BHS_635 2016-02-04  2016-02-04 2016-02-04
225  BHS_636 2016-02-04  2016-02-04 2016-02-04
84   BHS_604 2016-02-02  2017-07-13 2017-07-13
85   BHS_605 2016-02-02  2017-07-13 2017-07-13
226  BHS_637 2016-02-04  2016-02-04 2016-02-04
360  BHS_631 2016-12-11  2017-08-27 2017-08-27

想要的结果是

> Dat[c(3, 4, 7),]
    GenIndID   CptrDate DtLastAlive  DtFnlFate
224  BHS_635 2016-02-04  2016-02-04 2016-02-04
225  BHS_636 2016-02-04  2016-02-04 2016-02-04
226  BHS_637 2016-02-04  2016-02-04 2016-02-04

【问题讨论】:

  • Dat[apply(Dat[-1], 1, function(x) length(unique(x)) == 1),]

标签: r dplyr


【解决方案1】:

你可以有一个矢量化的解决方案:

Dat[do.call(function(...) pmax(...) - pmin(...), Dat[, -1]) == 0,]
#  IndID AAA BBB CCC
#1     A   1   1   1
#4     D   2   2   2
#8     H   4   4   4

【讨论】:

    【解决方案2】:

    这是applyall 的另一个解决方案:

    Dat[apply(Dat[,-1], 1, function(x) all(x==x[1])),]
    

    或与filter_at 来自dplyr

    library(dplyr)
    Dat %>% 
      filter_at(vars(AAA:CCC), all_vars(. == .data$AAA))
    

    结果:

      IndID AAA BBB CCC
    1     A   1   1   1
    4     D   2   2   2
    8     H   4   4   4
    

    编辑:

    作为对 OP 的附加示例的响应,apply 示例无论变量类型如何都可以工作。因此,以下内容适用于新示例:

    Dat[apply(Dat[,-1], 1, function(x) all(x==x[1])),]
    

    结果:

        GenIndID   CptrDate DtLastAlive  DtFnlFate
    224  BHS_635 2016-02-04  2016-02-04 2016-02-04
    225  BHS_636 2016-02-04  2016-02-04 2016-02-04
    226  BHS_637 2016-02-04  2016-02-04 2016-02-04
    

    但是,对于filter_at,如果要比较的列是factor,则需要先转换为character

    Dat %>% 
      filter_at(vars(-1), all_vars(as.character(.) == .data$CptrDate))
    

    请注意,您只需要将. 转换为字符,而不需要.data$CptrDate,因为因子可以与字符组合,但不能与另一个不同级别的因子。

    另一种选择是:

    Dat %>%
      mutate_at(vars(-1), as.character) %>%
      filter_at(vars(-1), all_vars(. == .data$CptrDate))
    

    结果:

      GenIndID   CptrDate DtLastAlive  DtFnlFate
    1  BHS_635 2016-02-04  2016-02-04 2016-02-04
    2  BHS_636 2016-02-04  2016-02-04 2016-02-04
    3  BHS_637 2016-02-04  2016-02-04 2016-02-04
    

    【讨论】:

    • filter_atdplyr 的新成员吗?
    • @B.Davis 我认为发布已经有一段时间了。你有什么版本的dplyr?检查sessionInfo()
    • 是的。我有 0.5.0 但刚刚重新安装,现在有 filter_at
    • @B.Davis 太好了。还有filter_allsummarize_atsummarize_all 等。例如,如果您没有IndID 列,我会使用filter_all
    • 这适用于我的数字数据!完美的!我也想知道它是否可以应用于因子并在上面添加了一个附加项。提前致谢!
    【解决方案3】:

    如果您只需要 3 列的子集,则可以使用 DF[,] 运算符。

    # DF[where rows have a value, select columns]
    # [where rows where AAA==BBB==CCC, select all columns with ""]
    temp <- Dat[Dat$AAA == Dat$BBB & Dat$BBB == Dat$CCC,] 
    

    【讨论】:

      【解决方案4】:

      您可以使用range()diff() 的组合。

      Dat[apply(Dat[ ,-1], 1, function(x) diff(range(x)))==0, ] 
      #   IndID AAA BBB CCC
      # 1     A   1   1   1
      # 4     D   2   2   2
      # 8     H   4   4   4
      

      range() 为您提供向量的最小值和最大值。 diff() 为您提供向量中值之间的差异。如果min-valuemax-value 之间的差为零,则您知道所有值都相等。如果我们使用apply 逐行应用这个,我们会得到一个TRUE/FALSE 向量,我们可以用它来索引Dat


      小例子说明逻辑:

      test <- c(1, 5, 3)
      minmax <- range(test)    # gives c(1,5)
      diff(minmax)             # gives 4
      diff(range(c(1, 1, 1)))  # gives 0 
      

      如果我们检查每一行,无论 diff(range(your_row)) 是否等于 0,我们都可以使用它的输出来索引 Dat,就像我们上面所做的那样。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-12-21
        • 2020-02-08
        • 2017-04-06
        • 2013-02-20
        相关资源
        最近更新 更多