Изменение формы данных на основе столбца в кадре данных - PullRequest
2 голосов
/ 13 августа 2010

Мне нужно взять data.frame в формате:

  id1 id2 mean start end
1   A   D    4    12  15
2   B   E    5    14  15
3   C   F    6     8  10

и генерировать повторяющиеся строки на основе разницы в start - end. Например, мне нужно 3 строки для первого ряда, 1 для второго и 2 для третьего. Начальное и конечное поля должны быть в последовательном порядке в конечном data.frame. Конечный результат для этого data.frame должен быть:

   id1 id2 mean start end
1    A   D    4    12  13
2    A   D    4    13  14
3    A   D    4    14  15
21   B   E    5    14  15
31   C   F    6     8   9
32   C   F    6     9  10

Я написал эту функцию, которая работает, но написана не очень в простом коде:

dupData <- function(df){
    diff <- abs(df$start - df$end)
    ret <- {}

    #Expand our dataframe into the appropriate number of rows.
    for (i in 1:nrow(df)){
        for (j in 1:diff[i]){
            ret <- rbind(ret, df[i,])
        } 
    }

    #If matching ID1 and ID2, generate a sequential ordering of start & end dates
    for (k in 2:nrow(ret) - 1) {
        if ( ret[k,1] == ret[k + 1, 1] & ret[k, 2] == ret[k, 2]  ){ 
            ret[k, 5] <- ret[k, 4] + 1
            ret[k + 1, 4] <- ret[k, 5]  
        }
    }
    return(ret)
}

У кого-нибудь есть предложения по оптимизации этого кода? Есть ли в plyr функция, которая может быть применима?

#sample daters
df <- data.frame(id1 = c("A", "B", "C")
        , id2 = c("D", "E", "F")
        , mean = c(4,5,6)  
        , start = c(12,14,8)
        , end = c(15, 15, 10)
)

Ответы [ 4 ]

2 голосов
/ 13 августа 2010

Вероятно, есть более общий способ сделать это, но ниже используется rbind.fill.

cbind(df[rep(1:nrow(df), times = apply(df[,4:5], 1, diff)), 1:3],
      rbind.fill(apply(df[,4:5], 1, function(x)
                       data.frame(start = x[1]:(x[2]-1), end = (x[1]+1):x[2]))))


##     id1 id2 mean start end
## 1     A   D    4    12  13
## 1.1   A   D    4    13  14
## 1.2   A   D    4    14  15
## 2     B   E    5    14  15
## 3     C   F    6     8   9
## 3.1   C   F    6     9  10
1 голос
/ 04 ноября 2010

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

library(plyr)
ddply(df, c("id1", "id2", "mean", "start", "end"), summarise,
                    sq=seq(1:(end-start)))
1 голос
/ 13 августа 2010

Функция survSplit пакета survival делает что-то в этом направлении, хотя у нее немного больше опций (например, указание времени резки).Возможно, вы сможете использовать его или посмотреть его код, чтобы увидеть, сможете ли вы лучше реализовать свою упрощенную версию.

0 голосов
/ 14 декабря 2017

Две альтернативы, много лет спустя, предлагая альтернативы с использованием популярных сегодня пакетов data.table и tidyverse:

Вариант 1:

library(data.table)
setDT(mydf)[, list(mean, start = start:(end-1)), .(id1, id2)][, end := start + 1][]
   id1 id2 mean start end
1:   A   D    4    12  13
2:   A   D    4    13  14
3:   A   D    4    14  15
4:   B   E    5    14  15
5:   C   F    6     8   9
6:   C   F    6     9  10

Вариант 2:

library(tidyverse)
mydf %>% 
  group_by(id1, id2, mean) %>% 
  summarise(start = list(start:(end-1))) %>% 
  unnest(start) %>% 
  mutate(end = start+1)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...