【问题标题】:For each group find observations with max value of several columns对于每个组,找到具有几列最大值的观察值
【发布时间】:2018-01-10 18:13:42
【问题描述】:

假设我有一个像这样的数据框:

set.seed(4)
df<-data.frame(
    group = rep(1:10, each=3),
    id = rep(sample(1:3), 10),
    x = sample(c(rep(0, 15), runif(15))),
    y = sample(c(rep(0, 15), runif(15))),
    z = sample(c(rep(0, 15), runif(15)))
)

如上所示,xyz 向量的某些元素取值为零,其余的取自 0 和 1 之间的均匀分布。

对于由第一列确定的每个组,我想从第二列中找到三个 ID,分别指向组中变量 xyz 的最大值。假设没有平局,除了变量在给定组的所有观察中取值为 0 的情况 - 在这种情况下,我不想返回任何数字作为具有最大值的行的 id。

输出如下所示:

group  x  y  z
  1    2  2  1
  2    2  3  1
 ...  .........

我的第一个想法是为每个变量分别选择具有最大值的行,然后使用merge 将其放入一个表中。但是,我想知道是否可以在没有 merge 的情况下完成它,例如使用标准的 dplyr 函数。

【问题讨论】:

  • 使用data.table 你可以试试setDT(df)[,lapply(.SD,function(x) id[which.max(x)]),by=group,.SDcols=c("x","y","z")]
  • 在使用samplerunif等功能时请使用set.seed。你可以试试library(dplyr); df %&gt;% group_by(group) %&gt;% summarise_at(vars(-id), funs(which.max))
  • 预期输出“1 5 2 4”中的第一行是否意味着组 1 在 X 列中的 id 5、y 列中的 id2 和 z 列中的 id 4 具有最高值?如果是,那么您是否期望只有 2 行输出?那么为什么预期输出中的延续点?
  • @Aramis7d 输出中的行数应等于组数。我使用了这些点,因为我希望在这个例子中输出有 10 行。至于第一个问题,对于每个组,我想获得三个值,它们来自id 列,它们指向给定组中xyz 的最大值。我不确定你问的是不是这个。另外,请注意我更改了示例,以便预期输出与生成示例的代码匹配。
  • @Sotos 根据建议添加了set.seed,谢谢。还要感谢您的代码,它似乎正在工作。您为什么不将其发布为答案?我很乐意批准它。

标签: r dataframe dplyr


【解决方案1】:

这是我使用plyr 提出的解决方案:

ddply(df,.variables = c("group"),
.fun = function(t){apply(X = t[,c(-1,-2)],MARGIN = 2,
function(z){ifelse(sum(abs(z))==0,yes = NA,no = t$id[which.max(z)])})})

#   group  x  y  z
#1      1  2  2  1
#2      2  2  3  1
#3      3  1  3  2
#4      4  3  3  1
#5      5  2  3 NA
#6      6  3  1  3
#7      7  1  1  2
#8      8 NA  2  3
#9      9  2  1  3
#10    10  2 NA  2

【讨论】:

    【解决方案2】:

    解决方案使用dplyrtidyr。请注意,如果所有数字都相同,我们无法决定应该选择哪个id。因此添加了filter(n_distinct(Value) &gt; 1) 以删除这些记录。在最终输出df2 中,NA 表示所有数字都相同的情况。如果我们愿意,我们可以决定是否在以后估算那些NA。此解决方案适用于任意数量的id 或列(xyz、...)。

    library(dplyr)
    library(tidyr)
    
    df2 <- df %>%
      gather(Column, Value, -group, -id) %>%
      arrange(group, Column, desc(Value)) %>%
      group_by(group, Column) %>%
      # If all values from a group-Column are all the same, remove that group-Column
      filter(n_distinct(Value) > 1) %>%
      slice(1) %>%
      select(-Value) %>%
      spread(Column, id)
    

    【讨论】:

    • @docendodiscimus 谢谢你的建议。我用filter(length(unique(Value)) &gt; 1)替换了那行。
    • @docendodiscimus 顺便说一句,当filter(Value != mean(Value)) 失败时,我能想到的唯一情况是原始df 中是否存在缺失值。但是检查是否只有一个唯一值仍然效果更好。
    • @docendodiscimus 谢谢。然后请检查我当前的解决方案,看看它是否适用于 OP 的数据。
    • 过滤器语句现在看起来不错。由于您使用的是 dplyr,您可以也使用n_distinct,但这当然不是必需的。
    • @docendodiscimus 谢谢。 n_distinct 更简洁。我已经更新了我的答案。
    【解决方案3】:

    如果您只想坚持使用dplyr,可以使用多列summarize/mutate 函数。无论id 的形式如何,这都应该有效;我最初的尝试稍微干净一些,但假设 id 为零是无效的。

    df %>%
      group_by(group) %>%
      mutate_at(vars(-id), 
                # If the row is the max within the group, set the value
                # to the id and use NA otherwise
                funs(ifelse(max(.) != 0 & . == max(.),
                            id,
                            NA))) %>%
      select(-id) %>%
      summarize_all(funs(
        # There are zero or one non-NA values per group, so handle both cases
        if(any(!is.na(.)))
          na.omit(.) else NA))
    ## # A tibble: 10 x 4
    ##    group     x     y     z
    ##    <int> <int> <int> <int>
    ##  1     1     2     2     1
    ##  2     2     2     3     1
    ##  3     3     1     3     2
    ##  4     4     3     3     1
    ##  5     5     2     3    NA
    ##  6     6     3     1     3
    ##  7     7     1     1     2
    ##  8     8    NA     2     3
    ##  9     9     2     1     3
    ## 10    10     2    NA     2
    

    【讨论】:

    • 您的解决方案生成的输出与预期的不同。您可能希望在运行代码之前对 id 进行排序。
    • @ycw,我认为这是由于 Jean 的编辑更新了 ID 的顺序。第一组现在在 id 2 上具有 x 和 y 的最大值,在 id 1 上具有 z 的最大值。
    • group_by(group)之后添加arrange(id),那么你就可以得到和预期一样的输出了。但是,请注意,您的解决方案报告的数字是行索引,而不是实际的 id 数字。
    • 呸,你是对的@ycw——我正看对了!这是试图将其修复为不需要 0 作为无效 id 的工件。我不想使用安排,因为我假设一个组中可能有多个相同的 id。有趣的是,实际的修复甚至不需要修改相关的评论,因为评论解释了我的意思,而不是我做了什么。谢谢!
    猜你喜欢
    • 2019-07-06
    • 2022-11-04
    • 1970-01-01
    • 2018-03-12
    • 1970-01-01
    • 2022-11-07
    • 2021-05-23
    • 2013-10-24
    • 1970-01-01
    相关资源
    最近更新 更多