Для каждой строки возвращаем имя столбца наибольшего значения
У меня есть список сотрудников, и мне нужно знать, в каком отделе они работают чаще всего. Тривиально табулировать идентификатор сотрудника по имени отдела, но сложнее вернуть имя отдела, а не количество подсчетов реестров, из таблицы частот. Простой пример ниже (имена столбцов = отделы, имена строк = идентификаторы сотрудников).
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
Ответы
Ответ 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")]