Переупорядочить фрейм данных в соответствии со столбцом индекса

#r

#r

Вопрос:

У меня есть фрейм данных, как показано ниже. Я хочу переупорядочить фрейм данных на основе столбца «Точка останова».

введите описание изображения здесь

Ожидаемый результат должен быть таким, как показано ниже

введите описание изображения здесь

Комментарии:

1. Можете ли вы объяснить это немного лучше? Получается ли так, что каждый раз, когда вы получаете Y в «точке останова», вы хотите поменять местами все строки между этой строкой и предыдущей точкой останова? Что происходит в конце? Почему есть две строки с данными 21,28

2. @Spacedman — Все строки будут перевернуты всякий раз, когда есть точка останова. Кроме того, я хочу добавить все строки (если таковые имеются), присутствующие ниже последней точки останова, как показано на выходе.

3. Вы хотите, чтобы строки ниже последней точки останова включались после повторения последней точки останова ? О, это печально, потому что у меня есть действительно аккуратное решение, если бы этой неправильности не было.

4. Можете ли вы помочь мне с решением, если не было нарушений? Я могу внешне добавить все строки, присутствующие ниже последней точки останова. Меня это не должно беспокоить.

Ответ №1:

С помощью этого примера данных:

 df <- data.frame(
    Range1 = c(1, 2, 3, 5, 10, 12, 16, 20, 21, 28, 33),
    Range2 = c(2, 3, 5, 10, 12, 16, 20, 21, 28, 33, 40),
    Breakpoint = c("", "", "", "Y", "", "Y", "", "", "Y", "", ""))
  

Решение с вырезанием конечных битов является:

Сначала отрежьте висячие биты:

  df2 = df[1:max(which(df$Breakpoint=="Y")),]
  

Затем определите длину каждой группы:

 > rgroup=rle(rev(cumsum(rev(df2$Break=="Y"))))$lengths
  

Получить, где находятся Y:

 > Ypos = which(df2$Breakpoint=="Y")
  

Создайте вектор индекса, который представляет собой позиции Y минус обратную последовательность от 1 до длины фрагмента. Подмножество:

 > df2[rep(Ypos, rgroup) - unlist(lapply(rgroup,function(x){1:x}))  1,]
  Range1 Range2 Breakpoint
4      5     10          Y
3      3      5           
2      2      3           
1      1      2           
6     12     16          Y
5     10     12           
9     21     28          Y
8     20     21           
7     16     20       
  

При необходимости добавьте оборванные биты обратно.

[редактировать — добавлена новая версия выше. Код ниже для исторических целей]

Моя старая версия была такой и имела дело с оборванными битами:

 > group=rev(cumsum(rev(df$Break=="Y")))
> rbind(do.call(rbind,lapply(split(df[group>0,],-group[group>0]),function(x){x[nrow(x):1,,drop=FALSE]}))[,c("Range1","Range2")],df[max(which(df$Break=="Y")),1:2,drop=FALSE],df[group==0,1:2])
  

и получаем:

      Range1 Range2
-3.4      5     10
-3.3      3      5
-3.2      2      3
-3.1      1      2
-2.6     12     16
-2.5     10     12
-1.9     21     28
-1.8     20     21
-1.7     16     20
9        21     28
10       28     33
11       33     40
  

Если вам не нравятся имена строк, удалите их. Использует только базовые функции R.

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

Бонусная аннотированная версия:

 > group=rev(cumsum(rev(df$Break=="Y")))
  

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

Этот бит не будет работать, если cutpaste из-за комментариев, которые я собираюсь сделать:

 > rbind(

# we need to bind three things. The reversed chunks, the last break point and   
# the trailing stuff:

      do.call(

# the trailing stuff is the rbind of the reversed chunks:

          rbind,

#           split the data into a list of chunks 

             lapply(
               split(df[group>0,],-group[group>0]),

     # reverse them

                  function(x){x[nrow(x):1,,drop=FALSE]}
     # and only take the columns we need:
        ))[,c("Range1","Range2")],
  # this is the last Y
      df[max(which(df$Break=="Y")),1:2,drop=FALSE],

  # this is the trailing rows, get them in order they appear:

      df[group==0,1:2])
  

Подобное аннотирование позволило мне увидеть некоторые оптимизации, которые можно было бы внести, но на данный момент это все.

Ответ №2:

В зависимости от размера вашего data.frame это может быть достигнуто вручную с помощью цикла for .

 BreakPoints <- which(!is.na(DF$`break point`))
if(length(breakPoints) > 0){
    startIndex <- 1 #Startindex tells me where i should point the breakPoint
    for(i in breakPoints){ #Iterate over breakpoints
        #Put the break point at the startIndex row 
        DF[startIndex:i,] <- DF[c(i, startIndex:(i-1), ] 
        #Update the placement as the next block 
        startIndex <- i   1
    }
}
  

если ваши данные большие, вероятно, существует более эффективный метод. В общем случае подмножество via [<-.dataframe выполняется медленно по сравнению с другими методами. Начальный оптимизатор мог бы просто преобразовать приведенный выше код в data.table формат, где подмножество выполняется намного быстрее.