Мисля, че има две предизвикателни части на този въпрос. Първият е намирането на припокриванията. Бих използвал пакета IRanges
от Bioconductor (?findInterval
в основния пакет може също да е полезно)
library(IRanges)
създаване на диапазони с ширина 1, представляващи координатния вектор, и набор от диапазони, представящи резултатите; Сортирам координатните вектори за удобство, като приемам, че дублиращите се координати могат да бъдат третирани еднакво
coord <- sort(sample(.Machine$integer.max, 5000000))
starts <- sample(.Machine$integer.max, 1200000)
scores <- runif(length(starts))
q <- IRanges(coord, width=1)
s <- IRanges(starts, starts + 100L)
Тук намираме кое query
припокрива кое subject
system.time({
olaps <- findOverlaps(q, s)
})
Това отнема около 7s на моя лаптоп. Има различни видове припокривания (вижте ?findOverlaps
), така че може би тази стъпка изисква малко усъвършенстване. Резултатът е двойка вектори, индексиращи заявката и припокриващия се обект.
> olaps
Hits of length 281909
queryLength: 5000000
subjectLength: 1200000
queryHits subjectHits
<integer> <integer>
1 19 685913
2 35 929424
3 46 1130191
4 52 37417
Мисля, че това е краят на първата сложна част, намирането на 281909 припокривания. (Не мисля, че отговорът data.table, предложен другаде, се отнася за това, въпреки че може да греша...)
Следващата предизвикателна част е изчисляването на голям брой средни стойности. Вграденият начин би бил нещо подобно
olaps0 <- head(olaps, 10000)
system.time({
res0 <- tapply(scores[subjectHits(olaps0)], queryHits(olaps0), mean)
})
което отнема около 3,25 секунди на моя компютър и изглежда се мащабира линейно, така че може би 90 секунди за 280k се припокриват. Но мисля, че можем да изпълним тази таблица ефективно с data.table
. Оригиналните координати са start(v)[queryHits(olaps)]
, така че
require(data.table)
dt <- data.table(coord=start(q)[queryHits(olaps)],
score=scores[subjectHits(olaps)])
res1 <- dt[,mean(score), by=coord]$V1
което отнема около 2,5 s за всички 280k припокривания.
Може да се постигне малко повече скорост, като се разпознае, че попаденията на заявката са подредени. Искаме да изчислим средна стойност за всяко изпълнение на попадения на заявка. Започваме със създаване на променлива, за да посочим края на всяко изпълнение на заявка
idx <- c(queryHits(olaps)[-1] != queryHits(olaps)[-length(olaps)], TRUE)
и след това изчислете кумулативните резултати в края на всяко бягане, дължината на всяко бягане и разликата между кумулативния резултат в края и в началото на бягането
scoreHits <- cumsum(scores[subjectHits(olaps)])[idx]
n <- diff(c(0L, seq_along(idx)[idx]))
xt <- diff(c(0L, scoreHits))
И накрая, средната стойност е
res2 <- xt / n
Това отнема около 0,6s за всички данни и е идентично с (макар и по-загадъчно от?) резултата data.table
> identical(res1, res2)
[1] TRUE
Оригиналните координати, съответстващи на средствата, са
start(q)[ queryHits(olaps)[idx] ]
person
Martin Morgan
schedule
20.01.2013