2017-10-25 12 views
0

大きなXMLファイルを解析してRデータフレームにしようとしています。 XMLの構造は不均一であり、必ずしもすべての要素を含むとは限らず、ノードごとに2つ以上の複製要素が含まれることもあります。Rデータフレームへの不均一なXMLの解析

XMLは次のとおりです。

<root> 
<members> 
<member> 
    <id>1</id> 
    <educations> 
    <education> 
     <institution>Sydney University</institution> 
     <program>Masters of Science</program> 
     <start-date>2010</start-date> 
     <end-date>2015</end-date> 
     <description></description> 
    </education> 
    <education> 
     <institution>UTS</institution> 
     <program>Bachelor of Science</program> 
     <start-date>2004</start-date> 
     <end-date>2008</end-date> 
    </education> 
    </educations> 
</member> 

<member> 
    <id>2</id> 
</member> 

<member> 
    <id>3</id> 
    <educations> 
    <education> 
     <is-current>true</is-current> 
     <institution>Monash Univeristy</institution> 
     <start-date>2010</start-date> 
    </education> 
    </educations> 
</member> 
</members> 
</root> 

所望の出力テーブルには、各メンバーと彼らの教育・ブロックの重複のIDを持っているでしょう。したがって、ID1は教育期間ごとに2行を持ち、ID3はちょうど1になります。

xmlToList()を使用すると、過剰な列が作成され、各子ノードのIDを複製する方法が見つかりません。

答えて

0

これは確かに不器用な解決策であり、おそらくより洗練された宇宙空間のような解決策があります。 しかし、これは仕事をするようです。

library(XML) 
library(plyr) 

names_use <- c("institution", "program", "start-date", "end-date","description") 
list_xml <- xmlToList(test) 
df_use <- ldply(list_xml$member, function(x){ 
    if(is.null(x$educations)){ 
     df_edu <- data.frame(x$id,t(rep(NA,5))) 
     names(df_edu) <- c("id",names_use) 
     return(df_edu) 
    } 
    df_res <- ldply(x$educations, function(edu_tmp){ 
     df_edu <- as.data.frame(t(unlist(edu_tmp)), 
      stringsAsFactors = F) 
     for(i_names in names_use){ 
      if(!i_names %in% names(df_edu)){ 
       df_edu[,i_names] <- NA 
      } 
     } 
     return(df_edu) 
    }) 
    df_res$id <- x$id 
    return(df_res[,c("id",names_use)]) 
}) 
df_use <- df_use[,c("id",names_use)] 

df_use 
    id  institution    program start-date end-date description 
1 1 Sydney University Masters of Science  2010  2015   NA 
2 1    UTS Bachelor of Science  2004  2008   NA 
3 2    <NA>    <NA>  <NA>  <NA>   NA 
4 3 Monash Univeristy    <NA>  2010  <NA>   NA 
+0

ありがとうございました。これはうまくいきます。使用する名前を手動で指定する必要がありますが、速度に関してはうまく機能します。再度、感謝します。 – EwenM

0

代替アプローチ:

library(xml2) 
library(tidyverse) 

我々はヘルパー関数に追加しますので、私はきちんと列名を好き:

mgca <- function(tbl) { 

    x <- colnames(tbl) 
    x <- tolower(x) 
    x <- gsub("[[:punct:][:space:]]+", "_", x) 
    x <- gsub("_+", "_", x) 
    x <- gsub("(^_|_$)", "", x) 
    x <- make.unique(x, sep = "_") 

    colnames(tbl) <- x 

    tbl 

} 

doc <- read_xml("so.xml") 

ここでの考え方は、各​​上の第1反復するためにありますそれに対応する<id>を抽出します。

​​には一度お子様がいるかどうかを確認してください。そうでない場合は、<id>をデータフレームに戻してください。そうであれば、<education>ノードをさらに繰り返し、存在する子を識別し、それらを取り出して、それぞれのデータフレームを作成します(<id>を含む)。最後に列名を整理して最終データフレームにまとめます、心を読むことができません

xml_find_all(doc, ".//member") %>% 
    map_df(~{ 

    id <- (xml_find_first(.x, ".//id") %>% xml_text()) %||% NA_character_ 

    edus <- xml_find_all(.x, ".//educations/education") 

    if (length(edus) > 0) { 

     map_df(edus, ~{ 
     kid <- .x 
     nodes <- xml_children(kid) %>% xml_name() 
     map(nodes, ~xml_find_first(kid, sprintf(".//%s", .x)) %>% 
       xml_text()) %>% 
      set_names(nodes) %>% 
      append(list(id = id)) %>% 
      flatten_df() 
     }) 

    } else { 
     data_frame(id = id) 
    } 

    }) %>% 
    mgca() %>% 
    type_convert() 
## # A tibble: 4 x 7 
##   institution    program start_date end_date description id is_current 
##    <chr>    <chr>  <int> <int>  <chr> <int>  <chr> 
## 1 Sydney University Masters of Science  2010  2015  <NA>  1  <NA> 
## 2    UTS Bachelor of Science  2004  2008  <NA>  1  <NA> 
## 3    <NA>    <NA>   NA  NA  <NA>  2  <NA> 
## 4 Monash Univeristy    <NA>  2010  NA  <NA>  3  true 

type_convert()ので、あなたはおそらく自分で論理的なベクターにis_currentを有効にする必要があります:より良いの列タイプを取得。

関連する問題