5

分析を行うために、R と XML パッケージを使用して XML をデータフレームにインポートしたいと考えています。XML ファイルの例:

<watchers shop_name="TEST" created_at="September 14, 2012 05:44">
<watcher channel="Site Name">
    <code>123456</code>
    <search_key>TestKey</search_key>
    <date>September 14, 2012 04:15</date>
    <result>Found</result>
    <link>http://www.test.com/fakeurl</link>
    <price>100.0</price>
    <shipping>0.0</shipping>
    <origposition>0</origposition>
    <name>Name Test</name>
    <results>
        <result position="1">
            <c_name>CTest1</c_name>
            <c_price>599.49</c_price>
            <c_shipping>0.0</c_shipping>
            <c_total_price>599.49</c_total_price>
            <c_rating>8.3</c_rating>
            <c_delivery/>
        </result><result position="2">
            <c_name>CTest2</c_name>
            <c_price>654.0</c_price>
            <c_shipping>0.0</c_shipping>
            <c_total_price>654.0</c_total_price>
            <c_rating>9.8</c_rating>
            <c_delivery/>
        </result>
        <result position="3">
            <c_name>CTest3</c_name>
            <c_price>654.0</c_price>
            <c_shipping>0.0</c_shipping>
            <c_total_price>654.0</c_total_price>
            <c_rating>8.8</c_rating>
            <c_delivery/>
        </result>
    </results>
</watcher>
</watchers>

次のフィールドを含むデータフレームの行が必要です。

shop_name   created_at  code    search_key  date    result
link    price   shipping    origposition    name    
position    c_name  c_price c_shipping  c_total_price   
c_rating    c_delivery

これは、子ノードも考慮する必要があることを意味し、この例では 3 行のデータフレームになります (結果が 3 つの位置を示しているため)。フィールド

shop_name   created_at  code    search_key
date    result  link    price   shipping    
origposition    name

これらの各行で同じです。

XML ファイルを調べることはできますが、必要なフィールドを含むデータフレームを取得できません。データフレームをデータフレームに変換すると、次のフィールドが取得されます。

"code"       "search_key"      "date"     "result"  
"link" "price"      "shipping"   "origposition"  
"name"    "results"     

ここでフィールド

shop_name   created_at

最初に欠落しており、「結果」は列「結果」の下の文字列にまとめられます。

必要なデータフレームを取得できるはずですが、これを正確に行う方法がわかりません。

アップデート

@MvG が提供するソリューションは、上記のテスト XML ファイルで見事に機能します。ただし、「結果」列の値が「見つかりません」になる場合もあります。この値を持つエントリは、特定のフィールド (常に同じフィールド) を見逃すため、ソリューションの実行時に「引数の列数が一致しません」というエラーが発生します。これらのエントリをデータフレームにも配置し、存在しないフィールドを空のままにしたいと思います。このシナリオを組み込む方法がわかりません。

test.xml

<watchers shop_name="TEST" created_at="September 14, 2012 05:44">
<watcher channel="Site Name">
    <code>123456</code>
    <search_key>TestKey</search_key>
    <date>September 14, 2012 04:15</date>
    <result>Found</result>
    <link>http://www.test.com/fakeurl</link>
    <price>100.0</price>
    <shipping>0.0</shipping>
    <origposition>0</origposition>
    <name>Name Test</name>
    <results>
        <result position="1">
            <c_name>CTest1</c_name>
            <c_price>599.49</c_price>
            <c_shipping>0.0</c_shipping>
            <c_total_price>599.49</c_total_price>
            <c_rating>8.3</c_rating>
            <c_delivery/>
        </result><result position="2">
            <c_name>CTest2</c_name>
            <c_price>654.0</c_price>
            <c_shipping>0.0</c_shipping>
        <c_total_price>654.0</c_total_price>
        <c_rating>9.8</c_rating>
        <c_delivery/>
    </result>
    <result position="3">
        <c_name>CTest3</c_name>
        <c_price>654.0</c_price>
        <c_shipping>0.0</c_shipping>
        <c_total_price>654.0</c_total_price>
        <c_rating>8.8</c_rating>
        <c_delivery/>
    </result>
</results>
</watcher>
<watcher channel="Shopping">
    <code>12804</code>
    <search_key></search_key>
    <date></date>
    <result>Not found</result>
    <link>https://www.test.com/testing1323p</link>
    <price>0.0</price>
    <shipping>0.0</shipping>
    <origposition>0</origposition>
    <name>MOOVM6002020</name>
    <results>
    </results>
</watcher>
</watchers>
4

2 に答える 2

4

これは、より一般的なアプローチです。すべてのノードは、次の 3 つのケースのいずれかに分類されます。

  • ノード名が kind の場合、rows子ノードからのデータ フレームは結果の異なる行になります。
  • ノード名が kind の場合、cols子ノードからのデータ フレームは結果の異なる列になります。
  • ノード名が kind の場合、ノード名valueを列名として、ノード値を列値として使用して、単一の値を持つデータ フレームが構築されます。
  • 3 つのケースすべてで、ノードの属性がデータ フレームに追加されます。

アプリケーションの呼び出しは、下部に向かって表示されます。

library(XML)

zeroColSingleRow <- function() {
  res <- data.frame(dummy=NA)
  res$dummy <- NULL
  stopifnot(nrow(res) == 1, ncol(res) == 0)
  return (res)
}

xml2df <- function(node, classifier) {
  if (! inherits(node, c("XMLInternalElementNode", "XMLElementNode"))) {
    return (zeroColSingleRow())
  }
  kind <- classifier(node)
  if (kind == "rows") {
    cdf <- lapply(xmlChildren(node), xml2df, classifier)
    if (length(cdf) == 0) {
      res <- zeroColSingleRow()
    }
    else {
      names <- unique(unlist(lapply(cdf, colnames)))
      cdf <- lapply(cdf, function(i) {
        missing <- setdiff(names, colnames(i))
        if (length(missing) > 0) {
          i[missing] <- NA
        }
        return (i)
      })
      res <- do.call(rbind, cdf)
    }
  }
  else if (kind == "cols") {
    cdf <- lapply(xmlChildren(node), xml2df, classifier)
    if (length(cdf) == 0) {
      res <- zeroColSingleRow()
    }
    else {
      res <- cdf[[1]]
      if (length(cdf) > 1) {
        for (i in 2:length(cdf)) {
          res <- merge(res, cdf[[i]], by=NULL)
        }
      }
    }
  }
  else {
    stopifnot(kind == "value")
    res <- data.frame(xmlValue(node))
    names(res) <- xmlName(node)
  }
  if (ncol(res) == 0) {
    res <- zeroColSingleRow()
  }
  attr <- xmlAttrs(node)
  if (length(attr) > 0) {
    attr <- do.call(data.frame, as.list(attr))
    res <- merge(attr, res, by=NULL)
  }
  rownames(res) <- NULL
  return(res)
}

doc<-xmlParse("test.xml")

xml2df(xmlRoot(doc), function(node) {
  name <- xmlName(node)
  if (name %in% c("watchers", "results"))
    return("rows")
  # make sure to treat results/result different from watcher/result
  if (name %in% c("watcher", "result") &&
      xmlName(xmlParent(node)) == paste0(name, "s"))
    return("cols")
  return("value")
})
于 2012-09-16T08:43:12.047 に答える
-1

1 つの可能性を次に示します。

attr2df <- function(n) do.call(data.frame, as.list(xmlAttrs(n)))
cbind(attr2df(xmlRoot(doc)), 
  do.call(rbind, xpathApply(doc, "//watcher", function(w) {
    x <- xmlToDataFrame(nodes = list(w))
    x$results<-NULL
    cbind(attr2df(w), x,
          xmlToDataFrame(nodes = getNodeSet(w, "results/result")))
  } ))
)

すべてのウォッチャーを反復処理します。ウォッチャーごとに、そのサブツリーをデータ フレームxに読み取り、その結果ノードを別のデータ フレームに読み取ります。最初のデータ フレームから結果を削除し、両方の列をバインドして、ウォッチャーからの属性もスローします。このアプリケーションは、ウォッチャーごとに 1 つの data.frame を生成し、外側の rbind cal はそれらを 1 つのデータ フレームに結合します。最も外側の cbind は、ルート ノードの属性を追加します。

結果には次の名前が付けられます。

 [1] "shop_name"     "created_at"    "channel"       "code"         
 [5] "search_key"    "date"          "result"        "link"         
 [9] "price"         "shipping"      "position"      "name"         
[13] "c_name"        "c_price"       "c_shipping"    "c_total_price"
[17] "c_rating"      "c_delivery"   
于 2012-09-14T10:19:33.210 に答える