如何输出最大值的列

时间:2018-08-09 22:12:23

标签: r dplyr

我有一个数据,我想找出哪一列具有最大值并输出该列名称。一个问题是,如果没有最大值(例如,所有数字均相等),则返回all_equal注释,或者如果两列的最大值与第三列的最大值相等,则返回该两列的名称。

这是示例数据

test <- data.frame(A=c(5,NA,NA,1,NA,NA,3,NA,NA),B=c(NA,2,NA,NA,1,NA,NA,1,NA),C=c(NA,NA,1,NA,NA,1,NA,NA,3),gr=gl(3,3))

   A  B  C gr
1  5 NA NA  1
2 NA  2 NA  1
3 NA NA  1  1
4  1 NA NA  2
5 NA  1 NA  2
6 NA NA  1  2
7  3 NA NA  3
8 NA  1 NA  3
9 NA NA  3  3

在每个gr中,列AB and C中都有值。我的目的是找到该组中哪个列的最大值,然后将该列名称输出到名为col_name的新列。

如果所有值都相同,如gr=2,则输出为all_equal

如果该列中的两个列与第三列相比具有最大值,例如gr=3A&C的输出列名。

我意识到没有col_name

可能很难建立管道。

所以我尝试了

gather

我在这里遇到的问题是,如果A,B和C列中的所有最大值都相等,则如何输出library(dplyr) test%>% group_by(gr)%>% gather(variable, value, -gr) %>% arrange(gr)%>% mutate(col_name=variable[which.max(value)]) # A tibble: 18 x 4 # Groups: gr [2] r variable value col_name <fct> <chr> <dbl> <chr> 1 1 A 5 A 2 1 A NA A 3 1 A NA A 4 1 B NA A 5 1 B 2 A 6 1 B NA A 7 1 C NA A 8 1 C NA A 9 1 C 1 A 10 2 A 1 A 11 2 A NA A 12 2 A NA A 13 2 B NA A 14 2 B 1 A 15 2 B NA A 16 2 C NA A 17 2 C NA A 18 2 C 1 A 注释,

如果2列最大值等于(gr = 3中的A和C),则以all_equal格式A&C输出那些相等的列名称

预期输出为

col_name

提前谢谢!

5 个答案:

答案 0 :(得分:2)

这里是一种ScrollView.frame = CGRect(x: 0, y: 0, width: ScrollView.frame.width, height: 3000) 的方法,我尝试使其更通用一些,以容纳不同数量的关注列。从上面的dplyr数据帧开始,首先定义一个函数,该函数查找当前组的最大值,获取具有匹配值的列的索引,然后根据匹配的列数构建输出:

test

由于该输出是一个数据帧,因此您需要利用foo <- function(df_, cols = 1:3) { # Get max m = max(df_[, cols], na.rm = TRUE) # Get columns ix <- as.data.frame(which(df_[, cols] == m, arr.ind = TRUE))[, 2] matchlen = length(ix) columns <- names(df_[,cols])[ix] # Get varname based on length out = ifelse(matchlen == length(cols), "all_equal", paste(columns, collapse = "&")) df_$col_name = out return(df_) } 将其应用于具有do的组:

dplyr

该函数应允许输入灵活的列数,只要它们是数字即可。例如,

test %>%
  group_by(gr) %>%
  do(foo(.))

# A tibble: 9 x 5
# Groups:   gr [3]
      A     B     C gr    col_name 
  <dbl> <dbl> <dbl> <fct> <chr>    
1     5    NA    NA 1     A        
2    NA     2    NA 1     A        
3    NA    NA     1 1     A        
4     1    NA    NA 2     all_equal
5    NA     1    NA 2     all_equal
6    NA    NA     1 2     all_equal
7     3    NA    NA 3     A&C      
8    NA     1    NA 3     A&C      
9    NA    NA     3 3     A&C 

test %>%
  group_by(gr) %>%
  do(foo(., cols = 1:2))

似乎都可以。

修改:

是的,我想你可以!

test %>%
  group_by(gr) %>%
  do(foo(., cols = c(1,3)))

答案 1 :(得分:2)

这里是使用gather的选项。我们将数据gather转换为“长”格式,arrange按组(gr)分组,“ val”按降序排列,按“ gr”分组,summarise创建“基于OP帖子中描述的条件为每个'gr'指定col_name',并为right_join和原始数据集

library(tidyverse)
test %>% 
   gather(key, val, -gr, na.rm = TRUE) %>%
   arrange(gr, desc(val)) %>%       
   group_by(gr) %>%         
   summarise(col_name = case_when(n_distinct(val)==1 ~ "all_equal",
                        TRUE ~ paste(key[val==max(val)], collapse = "&"))) %>% 
   right_join(test) %>%
   select(names(test), everything())
# A tibble: 9 x 5
#      A     B     C gr    col_name 
#  <dbl> <dbl> <dbl> <fct> <chr>    
#1     5    NA    NA 1     A        
#2    NA     2    NA 1     A        
#3    NA    NA     1 1     A        
#4     1    NA    NA 2     all_equal
#5    NA     1    NA 2     all_equal
#6    NA    NA     1 2     all_equal
#7     3    NA    NA 3     A&C      
#8    NA     1    NA 3     A&C      
#9    NA    NA     3 3     A&C      

或使用data.table

library(data.table)
library(stringr)
setDT(test)[, col_name := {
      v1 <- sort(na.omit(unlist(.SD)), decreasing = TRUE)
      if(uniqueN(v1)==1) "all_equal" else 
     paste(str_remove(names(v1)[v1==max(v1)], "\\d+"), collapse="&")
    }, by = gr]

test
#    A  B  C gr  col_name
#1:  5 NA NA  1         A
#2: NA  2 NA  1         A
#3: NA NA  1  1         A
#4:  1 NA NA  2 all_equal
#5: NA  1 NA  2 all_equal
#6: NA NA  1  2 all_equal
#7:  3 NA NA  3       A&C
#8: NA  1 NA  3       A&C
#9: NA NA  3  3       A&C

答案 2 :(得分:2)

类似于akrun的答案

library(tidyverse)

test <- data_frame(A=c(5,NA,NA,1,NA,NA,3,NA,NA),B=c(NA,2,NA,NA,1,NA,NA,1,NA),C=c(NA,NA,1,NA,NA,1,NA,NA,3),gr=gl(3,3))


test %>% gather(key, value, -gr, na.rm = TRUE) %>% 
  group_by(gr) %>% 
  arrange(gr) %>%
  mutate(col_name = if_else(length(which(value == max(value))) == length(unique(key)),
                     "all_equal",
                     paste(flatten(.[which(value == max(value)), "key"]), collapse = "&"))) %>% 
  spread(key, value)
#> # A tibble: 3 x 5
#> # Groups:   gr [3]
#>   gr    col_name      A     B     C
#>   <fct> <chr>     <dbl> <dbl> <dbl>
#> 1 1     A             5     2     1
#> 2 2     all_equal     1     1     1
#> 3 3     A&C           3     1     3

reprex package(v0.2.0)于2018-08-09创建。

这还将压缩数据帧以删除所有额外的NA

答案 3 :(得分:1)

使用自定义函数和data.table的解决方案:

myfun <- function(x) {
    x <- as.matrix(x)
    idx <- apply(x, 2, max, na.rm=T)==max(x, na.rm=T)
    who <- colnames(x)[idx]
    if(length(who)==1) return(who)
    if(length(who)==2) return(paste0(who, collapse = "&"))
    if(length(who)>2)  return("all_equal")
}

library(data.table)
dt <- data.table(test)

dt[ , new := myfun(cbind(A,B,C)), by=gr]

请注意,我必须使用==max()而不是which.max来处理领带。我还很难用aggregate之类的基本函数来实现这一点,所以我采用了data.table的方式。

答案 4 :(得分:0)

这里有2个基本的R解决方案

使用split<-aggregate

test0         <- aggregate(test[1:3], by = test[4], max,na.rm=T)[-1]
nms           <- apply(do.call(pmax,test0) == test0, 1 , function(x) names(which(x)))
test$col_name <- NA
split(test$col_name, test$gr) <- 
  ifelse(lengths(nms) == 3, "all_equal", lapply(nms,paste,collapse="&"))
test
# A  B  C gr  col_name
# 1  5 NA NA  1         A
# 2 NA  2 NA  1         A
# 3 NA NA  1  1         A
# 4  1 NA NA  2 all_equal
# 5 NA  1 NA  2 all_equal
# 6 NA NA  1  2 all_equal
# 7  3 NA NA  3       A&C
# 8 NA  1 NA  3       A&C
# 9 NA NA  3  3       A&C

详细信息

我们首先将测试合并到test0

test0
#   A B C
# 1 5 2 1
# 2 1 1 1
# 3 3 1 3

然后使用pmax获取最大行,使用which获取列索引,使用names获取列名称。

我们用"all_equal"指定粘贴的名称(split<-除外),它们被回收以形成输出。

使用stackave

stacked <- cbind(na.omit(stack(test,-gr)), gr=levels(test$gr))
test$col_name <-  with(stacked, ave(values, gr, FUN = function(x){
  nms <- paste(names(test)[which(x == max(x))],collapse="&")
  if (length(nms) == 3) "all_equal" else nms})[order(gr)])

#    A  B  C gr col_name
# 1  5 NA NA  1        A
# 2 NA  2 NA  1        A
# 3 NA NA  1  1        A
# 4  1 NA NA  2    A&B&C
# 5 NA  1 NA  2    A&B&C
# 6 NA NA  1  2    A&B&C
# 7  3 NA NA  3      A&C
# 8 NA  1 NA  3      A&C
# 9 NA NA  3  3      A&C