Мои данные выглядят примерно так:
set.seed(1)
dt <- data.table(rank=c(3,4,2,1),`1`=rnorm(4),`2`=rnorm(4),`3`=rnorm(4),`4`=rnorm(4),`5`=rnorm(4),`6`=rnorm(4))
rank 1 2 3 4 5 6
1: 3 -0.6264538 0.3295078 0.5757814 -0.62124058 -0.01619026 0.91897737
2: 4 0.1836433 -0.8204684 -0.3053884 -2.21469989 0.94383621 0.78213630
3: 2 -0.8356286 0.4874291 1.5117812 1.12493092 0.82122120 0.07456498
4: 1 1.5952808 0.7383247 0.3898432 -0.04493361 0.59390132 -1.98935170
Я хотел бы добавить новый столбец rank_match
, который находит n-е (взятое из столбца rank
) наибольшее значение в строке от столбцов с именами 1
до 6
. Например, первая строка будет искать 3-е по величине значение в строке от столбцов с именем 1
до 6
, которое составляет 0,3295078.
Что-то вроде этого (но они, конечно, не работают):
dt[,rank_match := (sort(`1`:`6`, decreasing = TRUE)[rank])]
dt[,rank_match := (sort(.SD, decreasing = TRUE)[rank]), .SDcols=`1`:`6`]
Результат должен выглядеть примерно так:
rank 1 2 3 4 5 6 rank_match
1: 3 -0.6264538 0.3295078 0.5757814 -0.62124058 -0.01619026 0.91897737 0.3295078
2: 4 0.1836433 -0.8204684 -0.3053884 -2.21469989 0.94383621 0.78213630 -0.3053884
3: 2 -0.8356286 0.4874291 1.5117812 1.12493092 0.82122120 0.07456498 1.1249309
4: 1 1.5952808 0.7383247 0.3898432 -0.04493361 0.59390132 -1.98935170 1.5952808
Большое спасибо.
Один из вариантов - сгруппировать по последовательности строк, указать интересующие столбцы, начиная со столбца 2 и далее, unlist
, Subset of Data.table, sort
в порядке убывания, установить подмножество значения на основе столбца 'rank' и присвоить ему 'rank_match '
dt[, rank_match := sort(unlist(.SD), decreasing = TRUE)[rank],
1:nrow(dt), .SDcols = 2:ncol(dt) ]
dt
# rank 1 2 3 4 5 6 rank_match
#1: 3 -0.6264538 0.3295078 0.5757814 -0.62124058 -0.01619026 0.91897737 0.3295078
#2: 4 0.1836433 -0.8204684 -0.3053884 -2.21469989 0.94383621 0.78213630 -0.3053884
#3: 2 -0.8356286 0.4874291 1.5117812 1.12493092 0.82122120 0.07456498 1.1249309
#4: 1 1.5952808 0.7383247 0.3898432 -0.04493361 0.59390132 -1.98935170 1.5952808
Другой вариант - использовать melt
, а затем получить соответствующее значение столбца "значение".
out <- melt(dt, id.var = c('rn', 'rank'))[order(-value),
value[rank[1]] , .(rn)][order(rn)]$V1
dt[, rank_match := out][, rn := NULL][]
Или компактный подход, предложенный @IceCreamToucan
dt[, rank_match := melt(.SD, 'rank')[, value[order(-value)[rank]], rank]$V1]
Или используйте pmap
(от purrr
) для прокрутки строк
library(purrr)
dt[, rank_match := pmap_dbl(.SD, ~ c(...) %>%
{sort(-.[-1])[.[1]]})]
apply
указанная функция в каждой строке .SD
:
dt[, rank_match := apply(.SD, 1, function(x) -sort(-x[-1])[x[1]])]
давая:
rank 1 2 3 4 5 6 rank_match
1: 3 -0.6264538 0.3295078 0.5757814 -0.62124058 -0.01619026 0.91897737 0.3295078
2: 4 0.1836433 -0.8204684 -0.3053884 -2.21469989 0.94383621 0.78213630 -0.3053884
3: 2 -0.8356286 0.4874291 1.5117812 1.12493092 0.82122120 0.07456498 1.1249309
4: 1 1.5952808 0.7383247 0.3898432 -0.04493361 0.59390132 -1.98935170 1.5952808
dt[, rank_match := apply(.SD, 1, function(x) x[order(-x)][rank]), by = rank, .SDcols = `1`:`6`]
dt
rank 1 2 3 4 5 6 rank_match
1: 3 -0.6264538 0.3295078 0.5757814 -0.62124058 -0.01619026 0.91897737 0.3295078
2: 4 0.1836433 -0.8204684 -0.3053884 -2.21469989 0.94383621 0.78213630 -0.3053884
3: 2 -0.8356286 0.4874291 1.5117812 1.12493092 0.82122120 0.07456498 1.1249309
4: 1 1.5952808 0.7383247 0.3898432 -0.04493361 0.59390132 -1.98935170 1.5952808
Спасибо @snoram, этот ответ кажется мне наиболее читаемым, хотя все остальные тоже работают.
DescTools::Large
возвращает n
-е самые большие элементы из вектора без сортировки всего этого. Не уверен, как это сравнивается с dt[order(-value)[rank], ...]
.
library(DescTools)
library(data.table)
dt[, rank_match := melt(dt, 'rank')[, Large(value, rank)[1], rank]$V1]
# rank 1 2 3 4 5 6 rank_match
# 1: 3 -0.6264538 0.3295078 0.5757814 -0.62124058 -0.01619026 0.91897737 0.3295078
# 2: 4 0.1836433 -0.8204684 -0.3053884 -2.21469989 0.94383621 0.78213630 -0.3053884
# 3: 2 -0.8356286 0.4874291 1.5117812 1.12493092 0.82122120 0.07456498 1.1249309
# 4: 1 1.5952808 0.7383247 0.3898432 -0.04493361 0.59390132 -1.98935170 1.5952808
Примечание: если некоторые строки имеют одинаковый ранг, вы должны использовать логику rn
/ номера строки, как в ответе akrun.
Альтернативная реализация Таблица данных (с двумя вариантами):
# option 1
dt[melt(dt, id = 1)[, value[frank(-value) == .BY], by = rank]
, on = .(rank)
, rank_match := V1 ]
# option 2
dt[, rank_match := melt(dt, id = 1)[, value[frank(-value) == .BY], by = rank]$V1 ]
которые оба дают желаемый результат:
> dt rank 1 2 3 4 5 6 rank_match 1: 3 -0.6264538 0.3295078 0.5757814 -0.62124058 -0.01619026 0.91897737 0.3295078 2: 4 0.1836433 -0.8204684 -0.3053884 -2.21469989 0.94383621 0.78213630 -0.3053884 3: 2 -0.8356286 0.4874291 1.5117812 1.12493092 0.82122120 0.07456498 1.1249309 4: 1 1.5952808 0.7383247 0.3898432 -0.04493361 0.59390132 -1.98935170 1.5952808
Еще одно отличное решение, которое хорошо работает @G. Гротендик Спасибо.