'Find all tied maximum values in a row and return true or false if column contains max value
I have some data containing numeric columns :
df <- data.frame(v1 = c(0,1,2,3,4,5,6,7,8,9),
v2 = c(2,1,4,7,6,7,8,9,0,1),
v3 = c(4,1,6,7,8,9,0,1,2,3),
v4 = c(0,1,2,7,4,5,6,7,8,9),
v5 = c(0,1,6,3,6,9,8,9,0,1))
I can find the first maximum value and return its column name using which.max:
df$max <- colnames(df)[apply(df,1,which.max)]
Instead I would like to add five new columns and insert TRUE if the corresponding column is the max value or tied for the max value, and FALSE if not:
v1 v2 v3 v4 v5 v1max v2max v3max v4max v5max
1 0 2 4 0 0 FALSE FALSE TRUE FALSE FALSE
2 1 1 1 1 1 TRUE TRUE TRUE TRUE TRUE
3 2 4 6 2 6 FALSE FALSE TRUE FALSE TRUE
4 3 7 7 7 3 FALSE TRUE TRUE TRUE FALSE
5 4 6 8 4 6 FALSE FALSE TRUE FALSE FALSE
6 5 7 9 5 9 FALSE FALSE TRUE FALSE TRUE
7 6 8 0 6 8 FALSE TRUE FALSE FALSE TRUE
8 7 9 1 7 9 FALSE TRUE FALSE FALSE TRUE
9 8 0 2 8 0 TRUE FALSE FALSE TRUE FALSE
10 9 1 3 9 1 TRUE FALSE FALSE TRUE FALSE
Is there a simple way to achieve this?
Solution 1:[1]
Write an auxiliary function is.max and apply it row wise to df.
is.max <- function(x, na.rm = TRUE){
x == max(x, na.rm = na.rm)
}
res <- t(apply(df, 1, is.max))
colnames(res) <- paste(colnames(res), "max", sep = ".")
res <- cbind(df, res)
res
# v1 v2 v3 v4 v5 v1.max v2.max v3.max v4.max v5.max
#1 0 2 4 0 0 FALSE FALSE TRUE FALSE FALSE
#2 1 1 1 1 1 TRUE TRUE TRUE TRUE TRUE
#3 2 4 6 2 6 FALSE FALSE TRUE FALSE TRUE
#4 3 7 7 7 3 FALSE TRUE TRUE TRUE FALSE
#5 4 6 8 4 6 FALSE FALSE TRUE FALSE FALSE
#6 5 7 9 5 9 FALSE FALSE TRUE FALSE TRUE
#7 6 8 0 6 8 FALSE TRUE FALSE FALSE TRUE
#8 7 9 1 7 9 FALSE TRUE FALSE FALSE TRUE
#9 8 0 2 8 0 TRUE FALSE FALSE TRUE FALSE
#10 9 1 3 9 1 TRUE FALSE FALSE TRUE FALSE
Solution 2:[2]
A cbind() evaluating each row to the max() of each row wil do the trick:
df2<-cbind(df,df == apply(df,1,max))
colnames(df2)<-c("v1", "v2" ,"v3", "v4" ,"v5", "v1max", "v2max", "v3max" ,"v4max", "v5max")
df2
# v1 v2 v3 v4 v5 v1max v2max v3max v4max v5max
# 1 0 2 4 0 0 FALSE FALSE TRUE FALSE FALSE
# 2 1 1 1 1 1 TRUE TRUE TRUE TRUE TRUE
# 3 2 4 6 2 6 FALSE FALSE TRUE FALSE TRUE
# 4 3 7 7 7 3 FALSE TRUE TRUE TRUE FALSE
# 5 4 6 8 4 6 FALSE FALSE TRUE FALSE FALSE
# 6 5 7 9 5 9 FALSE FALSE TRUE FALSE TRUE
# 7 6 8 0 6 8 FALSE TRUE FALSE FALSE TRUE
# 8 7 9 1 7 9 FALSE TRUE FALSE FALSE TRUE
# 9 8 0 2 8 0 TRUE FALSE FALSE TRUE FALSE
# 10 9 1 3 9 1 TRUE FALSE FALSE TRUE FALSE
Solution 3:[3]
Using max.col:
cbind(df, df==df[cbind( 1:nrow(df), max.col(df) )])
# v1 v2 v3 v4 v5 v1 v2 v3 v4 v5
# 1 0 2 4 0 0 FALSE FALSE TRUE FALSE FALSE
# 2 1 1 1 1 1 TRUE TRUE TRUE TRUE TRUE
# 3 2 4 6 2 6 FALSE FALSE TRUE FALSE TRUE
# 4 3 7 7 7 3 FALSE TRUE TRUE TRUE FALSE
# 5 4 6 8 4 6 FALSE FALSE TRUE FALSE FALSE
# 6 5 7 9 5 9 FALSE FALSE TRUE FALSE TRUE
# 7 6 8 0 6 8 FALSE TRUE FALSE FALSE TRUE
# 8 7 9 1 7 9 FALSE TRUE FALSE FALSE TRUE
# 9 8 0 2 8 0 TRUE FALSE FALSE TRUE FALSE
# 10 9 1 3 9 1 TRUE FALSE FALSE TRUE FALSE
Solution 4:[4]
One tidyverse possibility could be:
df %>%
mutate_all(list(max = ~ . == exec(pmax, !!!.)))
v1 v2 v3 v4 v5 v1_max v2_max v3_max v4_max v5_max
1 0 2 4 0 0 FALSE FALSE TRUE FALSE FALSE
2 1 1 1 1 1 TRUE TRUE TRUE TRUE TRUE
3 2 4 6 2 6 FALSE FALSE TRUE FALSE TRUE
4 3 7 7 7 3 FALSE TRUE TRUE TRUE FALSE
5 4 6 8 4 6 FALSE FALSE TRUE FALSE FALSE
6 5 7 9 5 9 FALSE FALSE TRUE FALSE TRUE
7 6 8 0 6 8 FALSE TRUE FALSE FALSE TRUE
8 7 9 1 7 9 FALSE TRUE FALSE FALSE TRUE
9 8 0 2 8 0 TRUE FALSE FALSE TRUE FALSE
10 9 1 3 9 1 TRUE FALSE FALSE TRUE FALSE
Since dplyr 1.0.0
df %>%
mutate(across(everything(),
~ . == exec(pmax, !!!.),
.names = "{.col}_max"))
Or using dplyr only:
df %>%
rowwise() %>%
mutate(across(everything(),
~ . == max(c_across(everything())),
.names = "{.col}_max"))
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Rui Barradas |
| Solution 2 | Carles S |
| Solution 3 | 989 |
| Solution 4 |
