Преобразование XML-документа в R-кадр данных, где все подузлы имеют одинаковое имя, и для его идентификации используется дочерний узел. - PullRequest
0 голосов
/ 11 октября 2018

Я хочу преобразовать следующий XML-документ в фрейм данных, который я могу использовать для доступа к информации о координатах.

a3 <- xmlParse("test.xml")

Я подумал, что мне нужно будет выполнить какую-то функцию применения, но я 'Я до сих пор не уверен, как обрабатывать подузлы XML со многими подузлами, которые используются для идентификации этого конкретного узла.Узлы <Marker_Data> и <Marker> используют подузел и дочерний узел (соответственно) для их уникальной идентификации.Это простая проблема?Примеры, которые я видел при переполнении стека, имеют уникальные атрибуты xml на каждом уровне для идентификации узла: пример - Анализ XML с различным количеством подузлов с одинаковым именем в R или Извлечение второго атрибутаXML-узел в R (пакет XML)

Я хотел бы создать такой кадр данных:

| Filename | Type| Xaxis| Yaxis | Zaxis |

| File.tif |  1  | 7172 | 4332  |   1   |

| File.tif |  1  | 7170 | 4140  |   1   |

| File.tif |  2  | 6172 | 4332  |   1   |

| File.tif |  2  | 5173 | 4140  |   1   |

test.XML:

<?xml version="1.0" encoding="UTF-8"?>
<CellCounter_Marker_File>
 <Image_Properties>
     <Image_Filename>File.tif</Image_Filename>
 </Image_Properties>
 <Marker_Data>
     <Marker_Type>
         <Type>1</Type>
         <Marker>
             <MarkerX>7172</MarkerX>
             <MarkerY>4332</MarkerY>
             <MarkerZ>1</MarkerZ>
         </Marker>
         <Marker>
             <MarkerX>7170</MarkerX>
             <MarkerY>4140</MarkerY>
             <MarkerZ>1</MarkerZ>
         </Marker>
     </Marker_Type>
     <Marker_Type>
         <Type>2</Type>
         <Marker>
             <MarkerX>6172</MarkerX>
             <MarkerY>4332</MarkerY>
             <MarkerZ>1</MarkerZ>
         </Marker>
         <Marker>
             <MarkerX>5173</MarkerX>
             <MarkerY>4140</MarkerY>
             <MarkerZ>1</MarkerZ>
         </Marker>
     </Marker_Type>
 </Marker_Data>
</CellCounter_Marker_File>

Ответы [ 2 ]

0 голосов
/ 11 октября 2018

?? на хорошо продуманный второй вопрос SO!

Поскольку вы используете пакет XML, мы оставим вас в этой вселенной с этим ответом:

library(XML)

doc <- xmlParse("~/Data/sample.xml")

xpathApply(doc, "//Marker", function(.x) {

  tmp <- xmlToList(.x) # quick way to get the kids into a native R structure

  tmp$Type <- xmlValue(getNodeSet(.x, "../Type")[[1]]) # go back one and get the type

  # go to the root and get the filename (which we can and should do outside the apply)
  xmlValue(
    getNodeSet(.x, "//CellCounter_Marker_File/Image_Properties/Image_Filename")[[1]]
  ) -> tmp$Filename

  # change the names to protect the innocent  
  names(tmp) <- gsub("Marker([[:alpha:]])", "\\1axis", names(tmp))

  tmp

}) -> res

saf <- default.stringsAsFactors()
options("stringsAsFactors" = FALSE) # assuming you want strings vs factors

Вам повезло с этим XML-файлом, так как целевые узлы имеют постоянных детей (ну, скорее всего, это больше, чем удача, так как этот файл выглядит очень структурированным, но мы должны были бы сделать больше работы после вызова xpathApply(), если бы былВероятность изменения дочерних узлов <Marker>):

res <- do.call(rbind.data.frame, res) 
res <- type.convert(res, as.is = TRUE)
res <- res[,c("Filename", "Type", "Xaxis", "Yaxis", "Zaxis")]
rownames(res) <- NULL

options("stringsAsFactors" = saf)

res
##   Filename Type Xaxis Yaxis Zaxis
## 1 File.tif    1  7172  4332     1
## 2 File.tif    1  7170  4140     1
## 3 File.tif    2  6172  4332     1
## 4 File.tif    2  5173  4140     1

str(res)
## 'data.frame': 4 obs. of  5 variables:
##  $ Filename: chr  "File.tif" "File.tif" "File.tif" "File.tif"
##  $ Type    : int  1 1 2 2
##  $ Xaxis   : int  7172 7170 6172 5173
##  $ Yaxis   : int  4332 4140 4332 4140
##  $ Zaxis   : int  1 1 1 1
0 голосов
/ 11 октября 2018

Поскольку узлы Marker являются теми, которые определяют вашу строку, вы должны использовать их в качестве основы для цикла.Получите узлы Marker, используя

xml_find_all(doc, ".//Marker")

Затем вы перебираете узлы-маркеры и используете xpath, чтобы найти соответствующие дочерние элементы (MarkerX, MarkerY и MarkerZ), но также найти его родственные (Type) и предковые (Image_Filename) узлы ... Найдя, xml_text() используется для извлечения их значений.

code

library( xml2 )
library( tidyverse )

df <-
  #get all markers, since thay are defining the rows
  xml_find_all(doc, ".//Marker") %>%
  #iterate over the markers, and get relevant data from child-, ancestor- and sibling-nodes
  map_df( function(x) {
    set_names( c( #get the filename, which is in the preceeding sibling 'Type' of the marker 
                  xml_find_all( x, "./ancestor::Marker_Data/preceding-sibling::Image_Properties/Image_Filename") %>% xml_text(),
                  #get the types, whoich are in the preceeding sibling 'Type' of the marker 
                  xml_find_all( x, "./preceding-sibling::Type") %>% xml_text(),
                  #get the MarkersX, Y and Z of the marker 
                  xml_find_all( x, ".//MarkerX") %>% xml_text(),
                  xml_find_all( x, ".//MarkerY") %>% xml_text(),
                  xml_find_all( x, ".//MarkerZ") %>% xml_text() ), 
               #set the column names
               c( "File", "Type", "Xaxis", "Yaxis", "z") ) %>% 
      as.list() %>% #make list
      flatten_df() #flatten to a data.frame
  }) %>%
  type_convert() #let R convert the values for you

выход

# # A tibble: 4 x 5
#   File      Type Xaxis Yaxis     z
#   <chr>    <int> <int> <int> <int>
# 1 File.tif     1  7172  4332     1
# 2 File.tif     1  7170  4140     1
# 3 File.tif     2  6172  4332     1
# 4 File.tif     2  5173  4140     1

образец данных

doc <- read_xml( '<?xml version="1.0" encoding="UTF-8"?>
<CellCounter_Marker_File>
<Image_Properties>
<Image_Filename>File.tif</Image_Filename>
</Image_Properties>
<Marker_Data>
<Marker_Type>
<Type>1</Type>
<Marker>
<MarkerX>7172</MarkerX>
<MarkerY>4332</MarkerY>
<MarkerZ>1</MarkerZ>
</Marker>
<Marker>
<MarkerX>7170</MarkerX>
<MarkerY>4140</MarkerY>
<MarkerZ>1</MarkerZ>
</Marker>
</Marker_Type>
<Marker_Type>
<Type>2</Type>
<Marker>
<MarkerX>6172</MarkerX>
<MarkerY>4332</MarkerY>
<MarkerZ>1</MarkerZ>
</Marker>
<Marker>
<MarkerX>5173</MarkerX>
<MarkerY>4140</MarkerY>
<MarkerZ>1</MarkerZ>
</Marker>
</Marker_Type>
</Marker_Data>
</CellCounter_Marker_File>' )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...