Повышение производительности визуализации перекрывающихся сегментов - PullRequest
4 голосов
/ 26 марта 2012

У меня есть набор пар точек x для рисования сегментов вдоль оси x для создания пользовательской карты чтения в R:

example read map

Половина задачи построения этих сегментовопределяет их позиции y так, чтобы не было двух перекрывающихся сегментов на одном уровне y.Для каждого сегмента я перебираю уровни y из первой позиции, пока не доберусь до позиции, которая еще не содержит сегмент, который будет перекрывать текущий.Затем я записываю конечную позицию текущего сегмента и перехожу к следующему.

Фактический код представляет собой следующую функцию:

# Dummy data
# A list of start and end positions for each segment along the X axis. Sorted by start.
# Passing the function few.reads draws a map in half a second. Passing it many.reads takes about half an hour to complete.
few.reads <- data.frame( start=c(rep(10,150), rep(16,100), rep(43,50)), end=c(rep(30,150), rep(34,100), rep(57,50)) );
many.reads <- data.frame( start=c(rep(10,15000), rep(16,10000), rep(43,5000)), end=c(rep(30,15000), rep(34,10000), rep(57,5000)) );

#---
# A function to draw a series of overlapping segments (or "reads" in my along
# The x-axis. Where reads overlap, they are "stacked" down the y axis
#---
drawReads <- function(reads){

    # sort the reads by their start positions
    reads <- reads[order(reads$start),];

    # minimum and maximum for x axis
    minstart <- min(reads$start);
    maxend <- max(reads$end);

    # initialise yread: a list to keep track of used y levels
    yread <- c(minstart - 1);
    ypos <- c(); #holds the y position of the ith segment

    #---
    # This iteration step is the bottleneck. Worst case, when all reads are stacked on top
    # of each other, it has to iterate over many y levels to find the correct position for
    # the later reads
    #---
    # iterate over segments
    for (r in 1:nrow(reads)){
        read <- reads[r,];
        start <- read$start;
        placed <- FALSE;

        # iterate through yread to find the next availible
        # y pos at this x pos (start)
        y <- 1;
        while(!placed){

            if(yread[y] < start){
                ypos[r] <- y;
                yread[y] <- read$end;
                placed <- TRUE;
            } 

            # current y pos is used by another segment, increment
            y <- y + 1;
            # initialize another y pos if we're at the end of the list
            if(y > length(yread)){
                yread[y] <- minstart-1;
            }
        }
    }

    #---
    # This is the plotting step
    # Once we are here the rest of the process is very quick
    #---
    # find the maximum y pos that is used to size up the plot
    maxy <- length(yread);
    miny = 1;


    reads$ypos <- ypos + miny;

    print("New Plot...")
    # Now we have all the information, start the plot
    plot.new();
    plot.window(xlim=c(minstart, maxend+((maxend-minstart)/10)), ylim=c(1,maxy));

    axis(3,xaxp=c(minstart,maxend,(maxend-minstart)/10));
    axis(2, yaxp=c(miny,maxy,3),tick=FALSE,labels=FALSE);

    print("Draw the reads...");
    maxy <- max(reads$ypos);
    segments(reads$start, maxy-reads$ypos, reads$end, maxy-reads$ypos, col="blue");   
}

Мой фактический набор данных очень большой и содержит области, которые, насколько я могу судить, могут иметь до 600000 операций чтения.Чтения будут естественным образом накладываться друг на друга, поэтому очень легко реализовать сценарий наихудшего случая, когда все чтения накладываются друг на друга.Время, необходимое для построения большого количества операций чтения, для меня неприемлемо, поэтому я ищу способ сделать этот процесс более эффективным.Могу ли я заменить свои петли чем-то более быстрым?Есть ли алгоритм, который может организовать чтение быстрее?Я действительно не могу придумать лучшего способа сделать это в данный момент.

Спасибо за вашу помощь.

Ответы [ 2 ]

2 голосов
/ 26 марта 2012

Заполните каждый у-уровень жадным способом.После того, как уровень заполнен, идите на один уровень вниз и никогда не возвращайтесь вверх.

Псевдокод:

 y <- 1
 while segment-list.not-empty
   i <- 1
   current <- segment-list[i]
   current.plot(y)
   segment-list.remove(i)
   i <- segment-list.find_first_greater(current.end)
   while (i > 0)
     current <- segment-list[i]
     current.plot(y)
     segment-list.remove(i)
   y <- y + 1

Это не обязательно дает «оптимальный» график в любом смысле, но по крайней мереэто O (n log n).

1 голос
/ 26 марта 2012

Не можете ли вы отсортировать по начальному значению? Затем вы идете по списку спереди назад. Для каждого элемента нанесите его на график, а затем выполните бинарный поиск в оставшейся части списка для первого элемента, превышающего конечную координату только что нанесенного элемента. Если ничего не найдено, увеличьте Y. Удалите каждый элемент по мере его зарисовки.

Сортировка - O (N lg N), а двоичный поиск - O (lg N) для каждого элемента, поэтому общее количество - O (N lg N).

...