2010-08-13 25 views
2

Je dois prendre un data.frame au format:données Reshape basées sur la colonne dans dataframe

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

et de générer des lignes en double en fonction de la différence de start - end. Par exemple, j'ai besoin de 3 lignes pour la première rangée, 1 pour la seconde et 2 pour la troisième. Les champs de début et de fin devraient être dans l'ordre séquentiel dans le data.frame final. Le résultat final de cette data.frame devrait être:

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 

J'ai écrit cette fonction qui fonctionne, mais il est pas écrit dans le code très R'esque:

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) 
} 

Quelqu'un at-il des suggestions sur comment optimiser ce code? Y a-t-il une fonction dans plyr qui peut être applicable?

#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) 
) 

Répondre

2

Il existe probablement un moyen plus général de faire cela, mais ci-dessous utilise 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 
+0

qui est là un travail assez de fantaisie, je l'apprécie. Il a fallu ~ 1,5 minutes de travail avec une trame de données de 100k lignes pour sortir les données dans le format approprié. Merci! – Chase

1

La fonction survSplit du paquet survival fait quelque chose dans ce sens, mais il a un peu plus d'options (par exemple, en spécifiant les temps de coupe). Vous pourriez être capable de l'utiliser, ou regarder son code pour voir si vous pouvez mieux implémenter votre version simplifiée.

1

Sans doute est-ce pas un de ces moments où mieux vaut tard que jamais, mais j'ai eu un problème similaire et est venu avec cette ...

library(plyr) 
ddply(df, c("id1", "id2", "mean", "start", "end"), summarise, 
        sq=seq(1:(end-start))) 
0

Deux alternatives, beaucoup ans plus tard, en offrant des alternatives en utilisant data.table populaires d'aujourd'hui et tidyverse paquets:

Option 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 

Option 2:

library(tidyverse) 
mydf %>% 
    group_by(id1, id2, mean) %>% 
    summarise(start = list(start:(end-1))) %>% 
    unnest(start) %>% 
    mutate(end = start+1)