Подтвердить что ты не робот

Для каждой строки возвращаем имя столбца наибольшего значения

У меня есть список сотрудников, и мне нужно знать, в каком отделе они работают чаще всего. Тривиально табулировать идентификатор сотрудника по имени отдела, но сложнее вернуть имя отдела, а не количество подсчетов реестров, из таблицы частот. Простой пример ниже (имена столбцов = отделы, имена строк = идентификаторы сотрудников).

DF <- matrix(sample(1:9,9),ncol=3,nrow=3)
DF <- as.data.frame.matrix(DF)
> DF
  V1 V2 V3
1  2  7  9
2  8  3  6
3  1  5  4

Теперь как мне получить

> DF2
  RE
1 V3
2 V1
3 V2
4b9b3361

Ответ 1

Один вариант использования ваших данных (для справки в будущем используйте set.seed(), чтобы примеры с использованием sample воспроизводимых):

DF <- data.frame(V1=c(2,8,1),V2=c(7,3,5),V3=c(9,6,4))

colnames(DF)[apply(DF,1,which.max)]
[1] "V3" "V1" "V2"

Более быстрое решение, чем использование apply, может быть max.col:

colnames(DF)[max.col(DF,ties.method="first")]
#[1] "V3" "V1" "V2"

... где ties.method может быть любым из "random" "first" или "last"

Это, конечно, вызывает проблемы, если у вас есть два столбца, которые равны максимальному. Я не уверен, что вы хотите сделать в этом экземпляре, поскольку у вас будет несколько результатов для некоторых строк. Например:.

DF <- data.frame(V1=c(2,8,1),V2=c(7,3,5),V3=c(7,6,4))
apply(DF,1,function(x) which(x==max(x)))

[[1]]
V2 V3 
 2  3 

[[2]]
V1 
 1 

[[3]]
V2 
 2 

Ответ 2

Если вы заинтересованы в решении data.table, здесь один. Это немного сложно, так как вы предпочитаете получать идентификатор для первого максимума. Это намного проще, если вы предпочтете последний максимум. Тем не менее, это не так сложно и быстро!

Здесь я создал данные ваших измерений (26746 * 18).

Данные

set.seed(45)
DF <- data.frame(matrix(sample(10, 26746*18, TRUE), ncol=18))

data.table ответ:

require(data.table)
DT <- data.table(value=unlist(DF, use.names=FALSE), 
            colid = 1:nrow(DF), rowid = rep(names(DF), each=nrow(DF)))
setkey(DT, colid, value)
t1 <- DT[J(unique(colid), DT[J(unique(colid)), value, mult="last"]), rowid, mult="first"]

Бенчмаркинг:

# data.table solution
system.time({
DT <- data.table(value=unlist(DF, use.names=FALSE), 
            colid = 1:nrow(DF), rowid = rep(names(DF), each=nrow(DF)))
setkey(DT, colid, value)
t1 <- DT[J(unique(colid), DT[J(unique(colid)), value, mult="last"]), rowid, mult="first"]
})
#   user  system elapsed 
#  0.174   0.029   0.227 

# apply solution from @thelatemail
system.time(t2 <- colnames(DF)[apply(DF,1,which.max)])
#   user  system elapsed 
#  2.322   0.036   2.602 

identical(t1, t2)
# [1] TRUE

Это примерно в 11 раз быстрее по данным этих измерений, а data.table тоже очень хорошо масштабируется.


Изменить: если любой из максимальных значений в порядке, то:

DT <- data.table(value=unlist(DF, use.names=FALSE), 
            colid = 1:nrow(DF), rowid = rep(names(DF), each=nrow(DF)))
setkey(DT, colid, value)
t1 <- DT[J(unique(colid)), rowid, mult="last"]

Ответ 3

Основываясь на приведенных выше предложениях, следующее решение data.table работало очень быстро для меня:

set.seed(45)
DT <- data.table(matrix(sample(10, 10^7, TRUE), ncol=10))

system.time(  DT[, MAX := colnames(.SD)[max.col(.SD, ties.method="first")]]  )
   user  system elapsed 
   0.10    0.02    0.21

DT
         V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 MAX
      1:  7  4  1  2  3  7  6  6  6   1  V1
      2:  4  6  9 10  6  2  7  7  1   3  V4
      3:  3  4  9  8  9  9  8  8  6   7  V3
      4:  4  8  8  9  7  5  9  2  7   1  V4
      5:  4  3  9 10  2  7  9  6  6   9  V4
     ---                                   
 999996:  4  6 10  5  4  7  3  8  2   8  V3
 999997:  8  7  6  6  3 10  2  3 10   1  V6
 999998:  2  3  2  7  4  7  5  2  7   3  V4
 999999:  8 10  3  2  3  4  5  1  1   4  V2
1000000: 10  4  2  6  6  2  8  4  7   4  V1

И также имеет преимущество, которое всегда может указать, какие столбцы .SD следует учитывать, указав их в .SDcols:

DT[, MAX2 := colnames(.SD)[max.col(.SD, ties.method="first")], .SDcols = c("V9", "V10")]