2015-11-10 4 views
3

У меня есть таблица данных R. с столбцом id и несколькими столбцами, определяющими упорядоченный пороговый уровень и соответствующее значение. То, что я хотел бы сделать, это искать для каждой строки первый уровень, который больше или равен параметру для этого id и возвращает соответствующее значение.R data.table упорядоченный поиск столбца

Вот пример набора данных.

DT<-data.table(id=c("Obs1","Obs2"), 
    level.1=c(1,1),level.2=c(2,4),level.3=c(3,8), 
    val.1=c(10,10),val.2=c(20,30),val.3=c(30,50)) 

DT 
    id level.1 level.2 level.3 val.1 val.2 val.3 
1: Obs1  1  2  3 10 20 30 
2: Obs2  1  4  8 10 30 50 

Таким образом, если параметры подстановки:

params<-list("Obs1"=2.5,"Obs2"=1) 

значения должны быть возвращены:

c(30,10). 

Я хотел бы также, как количество уровней и значений, несколько произвольно, хотя они будет удовлетворять соглашению об именах, аналогично примеру

Я могу решить это, используя несколько шаги, но это очень некрасиво и, вероятно, не очень вычислительно эффективным:

level.names<-colnames(DT)[grep("level",colnames(DT))] 
val.names<-colnames(DT)[grep("val",colnames(DT))] 
setkey(DT,id) 

idx<-DT[,grep(TRUE,lapply(.SD,function(y)((params[[id]] <= y))))[1], 
     .SDcols=level.names,by=id] 

values<-ifelse(is.na(idx$V1),as.numeric(NA),DT[,get(val.names[idx[id,V1]]),by=id]$V1) 

ранее я решил эту проблему, используя data.frames гораздо более аккуратно, используя plyr :: ddply и тот факт, что я мог бы использовать имена переменных для столбцы в data.frame. (Для краткости я не включаю это решение здесь.)

Любые предложения по улучшению приветствуются.

ответ

5

Я хотел бы сделать это с помощью качению присоединяется следующим образом:

DT_m = melt(DT, measure=patterns("^level", "^val"), value.name=c("level", "val")) 
query = list(id=c("Obs1", "Obs2"), level=c(2.5, 1)) 
DT_m[query, val, on=c("id", "level"), roll=-Inf] 

roll=-Inf выполняет соединение NOCB (следующее наблюдение переносится назад). Когда значение для соединения (здесь query) попадает в промежуток, следующее наблюдение переносится назад как совпадающая строка. Например, 2.5 находится между 2 и 4. Соответственно, строка соответствует 4 (следующее наблюдение). Соответствующий val равен 30.

+2

Спасибо! Оба решения - это значительные улучшения. Знать о расплавах и прокатных соединениях будет очень полезно в будущем. – mjreed

2

Вот один из способов:

mDT = melt(DT, measure.var = patterns("level","val"), value.name = c("level","val")) 
setkey(mDT, id) 

#  id variable level val 
# 1: Obs1  1  1 10 
# 2: Obs1  2  2 20 
# 3: Obs1  3  3 30 
# 4: Obs2  1  1 10 
# 5: Obs2  2  4 30 
# 6: Obs2  3  8 50 

params2 <- list(id = c("Obs1","Obs2"), v=c(2.5,1)) 
mDT[params2,{ 
    i = findInterval(v, level, rightmost.closed=TRUE) 
    val[ i + (v != level[i]) ] 
}, by=.EACHI] 

#  id V1 
# 1: Obs1 30 
# 2: Obs2 10 

Если вы установили params$v над верхней level, NA будут возвращены:

params3 <- list(id = c("Obs1","Obs2"), v=c(5, 1)) 
mDT[params3, {i = findInterval(v, level, rightmost.closed=TRUE); val[ i + (v != level[i])]}, by=.EACHI] 

#  id V1 
# 1: Obs1 NA 
# 2: Obs2 10 

Комментарий. Я думаю, что лучше хранить данные в длинной/расплавленной форме, чем играть в игры с именами столбцов.

Если вы хотите, чтобы ввести параметры в виде пар ключ-значение, stack и setNames являются полезными:

p0  = list(Obs1 = 1, Obs2 = 2.5) 
params0 = setNames(stack(p0), c("v","id")) 
Смежные вопросы