2015-01-26 16 views
5

mam listę z następującym przykładem struktury:spłaszczania listę ze złożonej struktury zagnieżdżonej

> dput(test) 
structure(list(id = 1, var1 = 2, var3 = 4, section1 = structure(list(
    var1 = 1, var2 = 2, var3 = 3), .Names = c("var1", "var2", 
"var3")), section2 = structure(list(row = structure(list(var1 = 1, 
    var2 = 2, var3 = 3), .Names = c("var1", "var2", "var3")), 
    row = structure(list(var1 = 4, var2 = 5, var3 = 6), .Names = c("var1", 
    "var2", "var3")), row = structure(list(var1 = 7, var2 = 8, 
     var3 = 9), .Names = c("var1", "var2", "var3"))), .Names = c("row", 
"row", "row"))), .Names = c("id", "var1", "var3", "section1", 
"section2")) 


> str(test) 
List of 5 
$ id  : num 1 
$ var1 : num 2 
$ var3 : num 4 
$ section1:List of 3 
    ..$ var1: num 1 
    ..$ var2: num 2 
    ..$ var3: num 3 
$ section2:List of 3 
    ..$ row:List of 3 
    .. ..$ var1: num 1 
    .. ..$ var2: num 2 
    .. ..$ var3: num 3 
    ..$ row:List of 3 
    .. ..$ var1: num 4 
    .. ..$ var2: num 5 
    .. ..$ var3: num 6 
    ..$ row:List of 3 
    .. ..$ var1: num 7 
    .. ..$ var2: num 8 
    .. ..$ var3: num 9 

Zauważ, że lista section2 zawiera elementy nazwie rows. Reprezentują wiele rekordów. To, co mam, to lista zagnieżdżona, w której niektóre elementy znajdują się na poziomie katalogu głównego, a inne są rekordami zagnieżdżonymi dla tej samej obserwacji. Chciałbym następujące dane wyjściowe w formacie data.frame:

> desired 
    id var1 var3 section1.var1 section1.var2 section1.var3 section2.var1 section2.var2 section2.var3 
1 1 2 4    1    2    3    1    4    7 
2 NA NA NA   NA   NA    NA    2    5    8 
3 NA NA NA   NA   NA    NA    3    6    9 

elementy głównej szczebla powinny wypełnić pierwszy wiersz, a row elementy powinny mieć swoje własne wiersze. Jako dodatkową komplikację liczba zmiennych w pozycjach row może być różna.

+0

Po co to pożądane wyjście? To wydaje się niewygodnym formatem danych do pracy. – A5C1D2H2I1M1N2O1R2T1

+0

Wykonuję żądanie soap, które zwraca tabelę html z bardzo zagnieżdżoną strukturą na liście zagnieżdżonej. Nie jestem pewien, dlaczego uważasz, że pożądana wydajność jest niewygodna. Odtwarza tabelę html w formacie data.frame i wypełnia wartości NA, w których wpis obejmuje wiele wierszy. – Zelazny7

+0

Czy możesz podać jeden lub dwa dodatkowe przypadki testowe, ponieważ dodałeś do tego nagrodę.Wspomniałeś, że szukasz "ogólnego" rozwiązania, więc dobrze byłoby wiedzieć, jakie inne scenariusze należy uwzględnić. – A5C1D2H2I1M1N2O1R2T1

Odpowiedz

3

Oto ogólne podejście. Nie zakłada, że ​​będziesz miał tylko trzy rzędy; będzie działać z wieloma rzędami. A jeśli brakuje wartości w strukturze zagnieżdżonej (np. Var1 nie istnieje dla niektórych podlist w sekcji 2), kod poprawnie zwraca NA dla tej komórki.

E.g. jeśli używamy następujące dane:

test <- structure(list(id = 1, var1 = 2, var3 = 4, section1 = structure(list(var1 = 1, var2 = 2, var3 = 3), .Names = c("var1", "var2", "var3")), section2 = structure(list(row = structure(list(var1 = 1, var2 = 2), .Names = c("var1", "var2")), row = structure(list(var1 = 4, var2 = 5), .Names = c("var1", "var2")), row = structure(list(var2 = 8, var3 = 9), .Names = c("var2", "var3"))), .Names = c("row", "row", "row"))), .Names = c("id", "var1", "var3", "section1", "section2")) 

Ogólne podejście jest użycie stopu stworzyć dataframe, który zawiera informacje na temat zagnieżdżonych struktury, a następnie dcast kształtować go do formatu pragnienie.

library("reshape2") 

flat <- unlist(test, recursive=FALSE) 
names(flat)[grep("row", names(flat))] <- gsub("row", "var", paste0(names(flat)[grep("row", names(flat))], seq_len(length(names(flat)[grep("row", names(flat))])))) ## keeps track of rows by adding an ID 
ul <- melt(unlist(flat)) 
split <- strsplit(rownames(ul), split=".", fixed=TRUE) ## splits the names into component parts 
max <- max(unlist(lapply(split, FUN=length))) 
pad <- function(a) { 
    c(a, rep(NA, max-length(a))) 
} 
levels <- matrix(unlist(lapply(split, FUN=pad)), ncol=max, byrow=TRUE) 

## Get the nesting structure 
nested <- data.frame(levels, ul) 
nested$X3[is.na(nested$X3)] <- levels(as.factor(nested$X3))[[1]] 
desired <- dcast(nested, X3~X1 + X2) 
names(desired) <- gsub("_", "\\.", gsub("_NA", "", names(desired))) 
desired <- desired[,names(flat)] 

> desired 
    ## id var1 var3 section1.var1 section1.var2 section1.var3 section2.var1 section2.var2 section2.var3 
## 1 1 2 4    1    2    3    1    4    7 
## 2 NA NA NA   NA   NA   NA    2    5    8 
## 3 NA NA NA   NA   NA   NA    3    6    9 
1

Główną ideą tego rozwiązania jest spłaszczenie wszystkich podlist, z wyjątkiem podlist, o nazwie "wiersz". Można to zrobić, tworząc unikatowy identyfikator dla każdego elementu listy (przechowywany w z), a następnie żądając, aby wszystkie elementy w pojedynczym "wierszu" miały ten sam identyfikator (przechowywany w z2; musiał napisać funkcję rekursywną, aby przemieścić zagnieżdżone lista). Następnie można użyć z2 do grupowania elementów należących do tego samego wiersza. Uzyskaną listę można przekształcić w formularz macierzy przy użyciu stri_list2matrix z pakietu stringi, a następnie przekształcić w ramkę danych.

utest <- unlist(test) 
z <- relist(seq_along(utest),test) 

recurse <- function(L) { 
    if (class(L)!='list') return(L) 
    b <- names(L)=='row' 
    L.b <- lapply(L[b],function(k) relist(rep(k[[1]],length(k)),k)) 
    L.nb <- lapply(L[!b],recurse) 
    c(L.b,L.nb) 
} 

z2 <- unlist(recurse(z)) 

library(stringi) 
desired <- as.data.frame(stri_list2matrix(split(utest,z2))) 
names(desired) <- names(z2)[unique(z2)] 

desired 
#  id var1 var3 section1.var1 section1.var2 section1.var3 section2.row.var1 
# 1 1 2 4    1    2    3     1 
# 2 <NA> <NA> <NA>   <NA>   <NA>   <NA>     2 
# 3 <NA> <NA> <NA>   <NA>   <NA>   <NA>     3 
# section2.row.var1 section2.row.var1 
# 1     4     7 
# 2     5     8 
# 3     6     9 
0

Ponieważ problem nie jest dobrze zdefiniowana, gdy wiersze mają złożone struktur (czyli jeśli każdy wiersz w test zawierał listę test`, w jaki sposób powinny być ze sobą powiązane wiersze. Również co jeśli wierszy w tej samej tabeli mają różne struktury?), następujące rozwiązanie zależy od wierszy będących listą wartości.

Powiedział, zgaduję, że w ogólnym przypadku, Twoja lista test będzie zawierać zarówno wartości, listy wartości lub listy wierszy (gdzie wiersze listy wartości). Ponadto, jeśli wiersze nie zawsze są nazywane "wierszami", to rozwiązanie nadal działa.

temp <- lapply(test, 
       function(x){ 
        if(!is.list(x)) 
         # x is a value 
         return(x) 
        # x is a lis of rows or values 
        out <- do.call(cbind,x) 
        if(nrow(out)>1){ 
         # x is a list of rows 
         colnames(out)<-paste0(colnames(out),'.',rownames(out)) 
         rownames(out)<-rep_len(NA,nrow(out)) 
        } 
        return(out) 
       }) 

# a function that extends a matrix to a fixt number of rows (n) 
# by appending rows of NA's 
rowExtend <- function(x,N){ 
       if((!is.matrix(x))){ 
        out<-do.call(rbind,c(list(x),as.list(rep_len(NA,N - 1)))) 
        colnames(out) <- "" 
        out 
       }else if(nrow(x) < N) 
        do.call(rbind,c(list(x),as.list(rep_len(NA,N - nrow(x))))) 
       else 
        x 
      } 

# calculate the maximum number of rows 
.nrows <- sapply(temp,nrow) 
.nrows <- max(unlist(.nrows[!sapply(.nrows,is.null)])) 

# extend the shorter rows 
(temp2<-lapply(temp, rowExtend,.nrows)) 

# calculate new column namames 
newColNames <- mapply(function(x,y) { 
         if(nzchar(y)[1L]) 
          paste0(x,'.',y) 
         else x 
         }, 
         names(temp2), 
         lapply(temp2,colnames)) 


do.call(cbind,mapply(`colnames<-`,temp2,newColNames)) 

#> id var1 var3 section1.var1 section1.var2 section1.var3 section2.row.var1 section2.row.var2 section2.row.var3 
#> 1 2 4 1    2    3    1     4     7     
#> NA NA NA NA   NA   NA   2     5     8     
#> NA NA NA NA   NA   NA   3     6     9     
0

To zaczyna się podobnie do odpowiedzi Tiffany, ale potem nieco się rozbiega.

library(data.table) 

# flatten the first level 
flat = unlist(test, recursive = FALSE) 

# compute max length 
N = max(sapply(flat, length)) 

# pad NA's and convert to data.table (at this point it will *look* like the right answer) 
dt = as.data.table(lapply(flat, function(l) c(l, rep(NA, N - length(l))))) 

# but in reality some of the columns are lists - check by running sapply(dt, class) 
# so unlist them 
dt = dt[, lapply(.SD, unlist)] 
# id var1 var3 section1.var1 section1.var2 section1.var3 section2.row section2.row section2.row 
#1: 1 2 4    1    2    3   1   4   7 
#2: NA NA NA   NA   NA   NA   2   5   8 
#3: NA NA NA   NA   NA   NA   3   6   9 
Powiązane problemy