У меня есть таблица данных с цифрами c Значения X и Y. Мне нужно пометить записи значениями Y из top-5 и bottom-5 из 3 групп: 1) все записи, 2) записи с X = 0, 3) записи с X = 0.5. Мое текущее решение вычисляет rank()
отдельно, добавляя соответствующие столбцы в таблицу:
set.seed(1)
dt1 <- data.table(valueX=round(rnorm(1e6),2),
valueY=round(rnorm(1e6),2)) # I also have many other columns that I'll want to keep
dt1[, all.top:=rank( valueY)]
dt1[, all.btm:=rank(-valueY)]
dt1[valueX==0, X0.top:=rank( valueY)]
dt1[valueX==0, X0.btm:=rank(-valueY)]
dt1[valueX==.5, X5.top:=rank( valueY)]
dt1[valueX==.5, X5.btm:=rank(-valueY)]
dt1[, selected:=(all.top<6 | all.btm<6 |
X0.top <6 | X0.btm <6 |
X5.top <6 | X5.btm <6 )]
Есть ли какой-нибудь однострочник, который пропустил бы создание промежуточных столбцов?
UPD. Добавление некоторого теста производительности: решение с head()
/ tail()
, предложенное @ chinsoon12, намного быстрее, чем использование rank()
:
solution_ranks <- function(inpDT){
inpDT[, all.top:=rank( valueY)]
inpDT[, all.btm:=rank(-valueY)]
inpDT[valueX==0, X0.top:=rank( valueY)]
inpDT[valueX==0, X0.btm:=rank(-valueY)]
inpDT[valueX==.5, X5.top:=rank( valueY)]
inpDT[valueX==.5, X5.btm:=rank(-valueY)]
inpDT[, selected:=(all.top<6 | all.btm<6 |
X0.top <6 | X0.btm <6 |
X5.top <6 | X5.btm <6 )]
inpDT[,c('all.top','all.btm','X0.top','X0.btm','X5.top','X5.btm'):=NULL]
return(inpDT)
}
solution_ht <- function(inpDT){
ht <- function(x, n=5L) unique(c(head(x, n), tail(x, n)))
inpDT[, rn := .I]
inpDT[rn %in% inpDT[order(valueY),c(ht(rn),ht(rn[valueX==0]),ht(rn[valueX==0.5]))],
selected:=TRUE]
return(inpDT)
}
require(microbenchmark)
print(microbenchmark(
solution_ranks(dt1),
solution_ht(dt1),
times=3L
), signif=3)
#
# Unit: milliseconds
# expr min lq mean median uq max neval
# solution_ranks(dt1) 1050 1070 1080 1080 1090 1090 3
# solution_ht(dt1) 113 114 143 116 159 202 3