1

R でデータ フレームから XML ファイルを書き込もうとしていますが、パフォーマンスの問題が発生しています。

XML ファイルを書き込むための次のコードがあり、私の例のような小さなデータ フレームでは問題なく動作します。しかし、実際のデータ フレームは 50,000 行以上 5 列で構成されています。この処理には 10 時間以上かかります

どうすればパフォーマンスを向上させることができますか?

require(XML)
products <- c('A','B','C')
location <- c(1,2,3) 
var1 <- c(1,2,3)
var2 <- c(1,2,3)
df <- data.frame(products, location, var1, var2)

data = newXMLNode("data",attrs=c(guid="snapshot_data"))
data2 = newXMLNode("data",parent=data)
for (j in 1:nrow(df)) {
for (i in 3:4) {
element = newXMLNode("element",attrs=c(guid=paste(colnames(df) [i],df[j,1],df[j,2],sep="_")),parent=data2)
name = newXMLNode("name", paste(colnames(df) [i],df[j,1],df[j,2],sep=" "), parent=element)
value = newXMLNode("value", attrs=c(period="year", unit="Pure"),parent=element)
orig = newXMLNode("orig", round(df[j,i]),parent=value)
processes = newXMLNode("processed",parent=value)
meta = newXMLNode("meta",parent=element)
ref = newXMLNode("ref", attrs=c('source-guid'="fs_items"),parent=meta)
value = newXMLNode("value", attrs=c(guid=colnames(df) [i]),parent=ref)
ref = newXMLNode("ref", attrs=c('source-guid'="products"),parent=meta)
value = newXMLNode("value", attrs=c(guid=as.character(df[j,1])),parent=ref)
ref = newXMLNode("ref", attrs=c('source-guid'="location"),parent=meta)
value = newXMLNode("value", attrs=c(guid=as.character(df[j,2])),parent=ref)
 }
}

saveXML(data,file="test.xml")
4

1 に答える 1

2

ここには、速度を低下させているいくつかの問題があります。まず、ネストされたループはおそらくあまり役​​に立ちません。データフレームを再形成することでそれを取り除くことができます:

require(XML)
require(reshape2)

products <- c('A','B','C')
location <- c(1,2,3) 
var1 <- c(1,2,3)
var2 <- c(1,2,3)
df <- data.frame(products, location, var1, var2)

df2 <- melt(df, id.vars = c("products", "location"))
df2[,sapply(df2, is.factor)] <- lapply(df2[,sapply(df2, is.factor)], as.character)

df2
  products location variable value
1        A        1     var1     1
2        B        2     var1     2
3        C        3     var1     3
4        A        1     var2     1
5        B        2     var2     2
6        C        3     var2     3

このように、XML に含める各メトリックは、個別の列に並べられます。

これにより、XML ツリーを構築するための次のメソッドが得られます (後でベンチマークするために関数にラップされます)。

xml2 <- function(...) {
  data = newXMLNode("data",attrs=c(guid="snapshot_data"))
  data2 = newXMLNode("data",parent=data)
  for (j in 1:nrow(df2)) {
    element = newXMLNode("element",attrs=c(guid=paste(df2$variable[j],df2$products[j],df2$location[j],sep="_")),parent=data2)
    name = newXMLNode("name", paste(df2$variable[j],df2$products[j],df2$location[j],sep=" "), parent=element)
    value = newXMLNode("value", attrs=c(period="year", unit="Pure"),parent=element)
    orig = newXMLNode("orig", round(df2$value[j]),parent=value)
    processes = newXMLNode("processed",parent=value)
    meta = newXMLNode("meta",parent=element)
    ref = newXMLNode("ref", attrs=c('source-guid'="fs_items"),parent=meta)
    value = newXMLNode("value", attrs=c(guid=df2$variable[j]),parent=ref)
    ref = newXMLNode("ref", attrs=c('source-guid'="products"),parent=meta)
    value = newXMLNode("value", attrs=c(guid=df2$products[j]),parent=ref)
    ref = newXMLNode("ref", attrs=c('source-guid'="location"),parent=meta)
    value = newXMLNode("value", attrs=c(guid=df2$location[j]),parent=ref)
  }
  data2
}

それを超えて、関数を不必要に呼び出し、事前にデータフレームでそれらを呼び出すことができたときに関数と関数as.characterを繰り返し呼び出しています。roundpaste

df3 <- df2
df3$element <- paste(df3$variable,df3$products,df3$location, sep="_")
df3$name <- paste(df3$variable,df3$products,df3$location, sep=" ")
df3$value <- round(df3$value)

これにより、次の結果が得られます。

xml3 <- function(...) {
  data = newXMLNode("data",attrs=c(guid="snapshot_data"))
  data2 = newXMLNode("data",parent=data)
  for (j in 1:nrow(df3)) {
    element = newXMLNode("element",attrs=c(guid=df3$element[j]), parent=data2)
    name = newXMLNode("name", df3$name[j], parent=element)
    value = newXMLNode("value", attrs=c(period="year", unit="Pure"),parent=element)
    orig = newXMLNode("orig", df3$value[j],parent=value)
    processes = newXMLNode("processed",parent=value)
    meta = newXMLNode("meta",parent=element)
    ref = newXMLNode("ref", attrs=c('source-guid'="fs_items"),parent=meta)
    value = newXMLNode("value", attrs=c(guid=df3$variable[j]),parent=ref)
    ref = newXMLNode("ref", attrs=c('source-guid'="products"),parent=meta)
    value = newXMLNode("value", attrs=c(guid=df3$products[j]),parent=ref)
    ref = newXMLNode("ref", attrs=c('source-guid'="location"),parent=meta)
    value = newXMLNode("value", attrs=c(guid=df3$location[j]),parent=ref)
  }
  data2
}

最後に、 への呼び出し内で子ノードを作成できますnewXMLNode

xml4 <- function(...) {
  data = newXMLNode("data",attrs=c(guid="snapshot_data"))
  data2 = newXMLNode("data",parent=data)
  for (j in 1:nrow(df3)) {
    element = newXMLNode("element",attrs=c(guid=df3$element[j]), parent=data2,
      .children = 
        list(newXMLNode("name", df3$name[j]),
          newXMLNode("value", attrs=c(period="year", unit="Pure"),
            .children = list(newXMLNode("orig", df3$value[j]),
              newXMLNode("processed")))))
    meta = newXMLNode("meta",parent=element,
      .children = list(
        newXMLNode("ref", attrs=c('source-guid'="fs_items"),
          .children = newXMLNode("value", attrs=c(guid=df3$variable[j]))),
        newXMLNode("ref", attrs=c('source-guid'="products"),
          .children = newXMLNode("value", attrs=c(guid=df3$products[j]))),
        newXMLNode("ref", attrs=c('source-guid'="location"),
          .children = newXMLNode("value", attrs=c(guid=df3$location[j])))))
  }
  data2
}

したがって、元のプロセスを使用すると、次のようになります。

xml1 <- function(...) {
data = newXMLNode("data",attrs=c(guid="snapshot_data"))
data2 = newXMLNode("data",parent=data)
for (j in 1:nrow(df)) {
  for (i in 3:4) {
    element = newXMLNode("element",attrs=c(guid=paste(colnames(df) [i],df[j,1],df[j,2],sep="_")),parent=data2)
    name = newXMLNode("name", paste(colnames(df) [i],df[j,1],df[j,2],sep=" "), parent=element)
    value = newXMLNode("value", attrs=c(period="year", unit="Pure"),parent=element)
    orig = newXMLNode("orig", round(df[j,i]),parent=value)
    processes = newXMLNode("processed",parent=value)
    meta = newXMLNode("meta",parent=element)
    ref = newXMLNode("ref", attrs=c('source-guid'="fs_items"),parent=meta)
    value = newXMLNode("value", attrs=c(guid=colnames(df) [i]),parent=ref)
    ref = newXMLNode("ref", attrs=c('source-guid'="products"),parent=meta)
    value = newXMLNode("value", attrs=c(guid=as.character(df[j,1])),parent=ref)
    ref = newXMLNode("ref", attrs=c('source-guid'="location"),parent=meta)
    value = newXMLNode("value", attrs=c(guid=as.character(df[j,2])),parent=ref)
  }
}
  data2
}

そしてそれをベンチマークします:

microbenchmark(xml1(), xml2(), xml3(), xml4())
Unit: milliseconds
   expr       min        lq    median        uq      max neval
 xml1() 100.43712 100.97356 101.52694 102.28243 367.6518   100
 xml2()  99.38772 100.02676 100.63210 101.19588 373.8043   100
 xml3()  98.91923  99.67163 100.22482 100.92313 394.2360   100
 xml4()  82.09688  82.60983  83.02559  83.64807 367.6711   100

forループ内で繰り返し行うのではなく、データフレームを再形成し、そのデータフレームで関数を1回呼び出すと、どちらも(非常に)少し役立ちますが、実際の時間の節約は、への呼び出し内で子を親に割り当てることになりますnewXMLNode。まだ速くはなりませんが、以前よりは速くなるはずです。

編集

さらに速度が必要な場合は、ノードの作成をもう少し折りたたむことができます (newXMLnode への最初の呼び出し内で「要素」の子として「メタ」を割り当てます)。

xml5 <- function(...) {
  data = newXMLNode("data",attrs=c(guid="snapshot_data"))
  data2 = newXMLNode("data",parent=data)
  for (j in 1:nrow(df3)) {
    element = newXMLNode("element",attrs=c(guid=df3$element[j]), parent=data2,
      .children = 
        list(newXMLNode("name", df3$name[j]),
          newXMLNode("value", attrs=c(period="year", unit="Pure"),
            .children = list(newXMLNode("orig", df3$value[j]),
              newXMLNode("processed"))),
          newXMLNode("meta",
            .children = list(
              newXMLNode("ref", attrs=c('source-guid'="fs_items"),
                .children = newXMLNode("value", attrs=c(guid=df3$variable[j]))),
              newXMLNode("ref", attrs=c('source-guid'="products"),
                .children = newXMLNode("value", attrs=c(guid=df3$products[j]))),
              newXMLNode("ref", attrs=c('source-guid'="location"),
                .children = newXMLNode("value", attrs=c(guid=df3$location[j])))))))
  }
  data2
}

ただし、それ以上に、XML 文書自体を構造化するために選択した方法を再評価する必要があるでしょう。たとえば、「値」ノードを実際の値として「参照」ノード内に含めた場合 (現在、「参照」ノードにはノード値のない属性しかありません)、ループの反復ごとに newXMLNode への 3 つの呼び出しをなくすことができます。 :

xml6 <- function(...) {
  data = newXMLNode("data",attrs=c(guid="snapshot_data"))
  data2 = newXMLNode("data",parent=data)
  for (j in 1:nrow(df3)) {
    element = newXMLNode("element",attrs=c(guid=df3$element[j]), parent=data2,
      .children = 
        list(newXMLNode("name", df3$name[j]),
          newXMLNode("value", attrs=c(period="year", unit="Pure"),
            .children = list(newXMLNode("orig", df3$value[j]),
              newXMLNode("processed"))),
          newXMLNode("meta",
            .children = list(
              newXMLNode("ref", df3$variable[j], attrs=c('source-guid'="fs_items")),
              newXMLNode("ref", df3$products[j], attrs=c('source-guid'="products")),
              newXMLNode("ref", df3$location[j], attrs=c('source-guid'="location"))
            ))))
  }
  data2
}

XML ドキュメントの構造を単純化すると、速度が向上します。

microbenchmark(xml1(), xml2(), xml3(), xml4(), xml5(), xml6())

Unit: milliseconds
   expr      min        lq    median        uq      max neval
 xml1() 99.66528 100.79417 101.09906 101.56140 393.4303   100
 xml2() 98.58393  99.68279  99.90569 100.64327 392.6561   100
 xml3() 98.26595  99.41217  99.65450 100.37495 363.4646   100
 xml4() 81.32157  82.33324  82.62350  82.96958 363.4569   100
 xml5() 78.89286  79.96670  80.14763  80.74278 346.1388   100
 xml6() 71.17018  72.05212  72.36548  72.81261 334.9638   100

それでも実行時間が数時間から数分に短縮されるわけではありません。それほど速く実行する必要がある場合は、ループをより迅速に処理できる R 以外のものを使用します。

于 2013-06-11T18:16:21.440 に答える