ここには、速度を低下させているいくつかの問題があります。まず、ネストされたループはおそらくあまり役に立ちません。データフレームを再形成することでそれを取り除くことができます:
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
を繰り返し呼び出しています。round
paste
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 以外のものを使用します。