Разбор Excel XML в R со значением индекса строки - PullRequest
1 голос
/ 10 июля 2020

Я хочу разобрать формат Excel XML (пример файла ниже) в R. Ответ здесь ( Разбор Excel XML в R ) меня очень близко подошел, но есть еще один шаг я не могу понять. Мой код R (ниже) дает мне все XML данные в одном столбце без связанных значений индекса строки. В этих данных есть 23 «заголовка», которые в конечном итоге будут преобразованы в строки. Загвоздка в том, что не каждая запись содержит все 23 фрагмента данных, поэтому мне нужен индекс строки, чтобы правильно формировать мои данные после их анализа. Любая помощь будет принята с благодарностью.

Пример XML файл:

<?xml version="1.0" encoding="utf-8"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40">
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Bottom"/>
</Style>
<Style ss:ID="Header1">
<Borders>
 <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1" />
 <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1" />
 <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1" />
 <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1" />
</Borders>
<Font ss:Bold="1"/>
<Interior ss:Color="#D8F6D8" ss:Pattern="Solid" />
</Style>
</Styles>
<Worksheet ss:Name="OfficeAppointments">
<Table ss:ExpandedColumnCount="23">
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
<Column ss:AutoFitWidth="1"/>
    <Row>
      <Cell ss:Index="1" ss:StyleID="Header1">
         <Data ss:Type="String">Doctor</Data>
      </Cell>
      <Cell ss:Index="2" ss:StyleID="Header1">
         <Data ss:Type="String">Period</Data>
      </Cell>
      <Cell ss:Index="3" ss:StyleID="Header1">
         <Data ss:Type="String">Appt Date</Data>
      </Cell>
      <Cell ss:Index="4" ss:StyleID="Header1">
         <Data ss:Type="String">Patient</Data>
      </Cell>
      <Cell ss:Index="5" ss:StyleID="Header1">
         <Data ss:Type="String">Type</Data>
      </Cell>
      <Cell ss:Index="6" ss:StyleID="Header1">
         <Data ss:Type="String">Avail</Data>
      </Cell>
      <Cell ss:Index="7" ss:StyleID="Header1">
         <Data ss:Type="String">Init</Data>
      </Cell>
      <Cell ss:Index="8" ss:StyleID="Header1">
         <Data ss:Type="String">Acct</Data>
      </Cell>
      <Cell ss:Index="9" ss:StyleID="Header1">
         <Data ss:Type="String">Loc</Data>
      </Cell>
      <Cell ss:Index="10" ss:StyleID="Header1">
         <Data ss:Type="String">Status</Data>
      </Cell>
      <Cell ss:Index="11" ss:StyleID="Header1">
         <Data ss:Type="String">Note</Data>
      </Cell>
      <Cell ss:Index="12" ss:StyleID="Header1">
         <Data ss:Type="String">Memo 1</Data>
      </Cell>
      <Cell ss:Index="13" ss:StyleID="Header1">
         <Data ss:Type="String">Memo 2</Data>
      </Cell>
      <Cell ss:Index="14" ss:StyleID="Header1">
         <Data ss:Type="String">Referring Dr#</Data>
      </Cell>
      <Cell ss:Index="15" ss:StyleID="Header1">
         <Data ss:Type="String">Referring Dr Name</Data>
      </Cell>
      <Cell ss:Index="16" ss:StyleID="Header1">
         <Data ss:Type="String">Sex</Data>
      </Cell>
      <Cell ss:Index="17" ss:StyleID="Header1">
         <Data ss:Type="String">Chart</Data>
      </Cell>
      <Cell ss:Index="18" ss:StyleID="Header1">
         <Data ss:Type="String">Date Of Birth</Data>
      </Cell>
      <Cell ss:Index="19" ss:StyleID="Header1">
         <Data ss:Type="String">Age</Data>
      </Cell>
      <Cell ss:Index="20" ss:StyleID="Header1">
         <Data ss:Type="String">HIPAA Priv Alert</Data>
      </Cell>
      <Cell ss:Index="21" ss:StyleID="Header1">
         <Data ss:Type="String">Supervising Dr</Data>
      </Cell>
      <Cell ss:Index="22" ss:StyleID="Header1">
         <Data ss:Type="String">PCP #</Data>
      </Cell>
      <Cell ss:Index="23" ss:StyleID="Header1">
         <Data ss:Type="String">PCP Name</Data>
      </Cell>
    </Row>
    <Row>
      <Cell ss:Index="1" ss:StyleID="Default">
         <Data ss:Type="String">1</Data>
      </Cell>
      <Cell ss:Index="2" ss:StyleID="Default">
         <Data ss:Type="String">10:00a</Data>
      </Cell>
      <Cell ss:Index="3" ss:StyleID="Default">
         <Data ss:Type="String">04/23/20</Data>
      </Cell>
      <Cell ss:Index="5" ss:StyleID="Default">
         <Data ss:Type="String">OV</Data>
      </Cell>
      <Cell ss:Index="6" ss:StyleID="Default">
         <Data ss:Type="String">N</Data>
      </Cell>
      <Cell ss:Index="7" ss:StyleID="Default">
         <Data ss:Type="String">SB</Data>
      </Cell>
      <Cell ss:Index="9" ss:StyleID="Default">
         <Data ss:Type="String">O</Data>
      </Cell>
      <Cell ss:Index="10" ss:StyleID="Default">
         <Data ss:Type="String">D</Data>
      </Cell>
      <Cell ss:Index="11" ss:StyleID="Default">
         <Data ss:Type="String">Note gets typed here</Data>
      </Cell>
    </Row>
    <Row>
      <Cell ss:Index="1" ss:StyleID="Default">
         <Data ss:Type="String">1</Data>
      </Cell>
      <Cell ss:Index="2" ss:StyleID="Default">
         <Data ss:Type="String">10:30a</Data>
      </Cell>
      <Cell ss:Index="3" ss:StyleID="Default">
         <Data ss:Type="String">04/23</Data>
      </Cell>
      <Cell ss:Index="4" ss:StyleID="Default">
         <Data ss:Type="String"> NAME </Data>
      </Cell>
      <Cell ss:Index="5" ss:StyleID="Default">
         <Data ss:Type="String">OV</Data>
      </Cell>
      <Cell ss:Index="6" ss:StyleID="Default">
         <Data ss:Type="String">N</Data>
      </Cell>
      <Cell ss:Index="7" ss:StyleID="Default">
         <Data ss:Type="String">sb</Data>
      </Cell>
      <Cell ss:Index="8" ss:StyleID="Default">
         <Data ss:Type="String">73104</Data>
      </Cell>
      <Cell ss:Index="9" ss:StyleID="Default">
         <Data ss:Type="String">O</Data>
      </Cell>
      <Cell ss:Index="10" ss:StyleID="Default">
         <Data ss:Type="String">B</Data>
      </Cell>
      <Cell ss:Index="11" ss:StyleID="Default">
         <Data ss:Type="String">follow up</Data>
      </Cell>
      <Cell ss:Index="14" ss:StyleID="Default">
         <Data ss:Type="String">32</Data>
      </Cell>
      <Cell ss:Index="15" ss:StyleID="Default">
         <Data ss:Type="String">NAME, CREDENTIALS</Data>
      </Cell>
      <Cell ss:Index="16" ss:StyleID="Default">
         <Data ss:Type="String">SEX</Data>
      </Cell>
      <Cell ss:Index="18" ss:StyleID="Default">
         <Data ss:Type="String">DOB entered here</Data>
      </Cell>
      <Cell ss:Index="19" ss:StyleID="Default">
         <Data ss:Type="String">5</Data>
      </Cell>
      <Cell ss:Index="20" ss:StyleID="Default">
         <Data ss:Type="String">No</Data>
      </Cell>
      <Cell ss:Index="22" ss:StyleID="Default">
         <Data ss:Type="String">32</Data>
      </Cell>
      <Cell ss:Index="23" ss:StyleID="Default">
         <Data ss:Type="String">NAME, CREDENTIALS</Data>
      </Cell>
    </Row>
 </Table>
 <WorksheetOptions xmlns="urn:schemas-microsoft-com:office:excel">
  <Selected/>
  <FreezePanes/>
  <FrozenNoSplit/>
  <SplitHorizontal>1</SplitHorizontal>
  <TopRowBottomPane>1</TopRowBottomPane>
  <ActivePane>2</ActivePane>
  <Panes>
   <Pane>
    <Number>3</Number>
   </Pane>
   <Pane>
    <Number>2</Number>
   </Pane>
  </Panes>
  <ProtectObjects>False</ProtectObjects>
  <ProtectScenarios>False</ProtectScenarios>
 </WorksheetOptions>
</Worksheet>
</Workbook>

Мой код R на данный момент:

library(XML)
nmsp <- c(doc="urn:schemas-microsoft-com:office:spreadsheet")
fileurl <- "example.xml"
doc <- xmlParse(fileurl) 
df <- xmlToDataFrame(doc, nodes=getNodeSet(doc, "//doc:Cell", nmsp), collectNames = T, homogeneous = F)

Что дает мне мой код:

   Data                
   <chr>               
 1 Doctor              
 2 Period              
 3 Appt Date           
 4 Patient             
 5 Type                
 6 Avail               
 7 Init                
 8 Acct                
 9 Loc                 
10 Status              
11 Note                
12 Memo 1              
13 Memo 2              
14 Referring Dr#       
15 Referring Dr Name   
16 Sex                 
17 Chart               
18 Date Of Birth       
19 Age                 
20 HIPAA Priv Alert    
21 Supervising Dr      
22 PCP #               
23 PCP Name            
24 1                   
25 10:00a              
26 04/23/20            
27 OV                  
28 N                   
29 SB                  
30 O                   
31 D                   
32 Note gets typed here
33 1                   
34 10:30a              
35 04/23               
36 NAME                
37 OV                  
38 N                   
39 sb                  
40 73104               
41 O                   
42 B                   
43 follow up           
44 32                  
45 NAME, CREDENTIALS   
46 SEX                 
47 DOB entered here    
48 5                   
49 No                  
50 32                  
51 NAME, CREDENTIALS 

Что мне нужно:

   Data                 Index
   <chr>                <dbl>
 1 Doctor                   1
 2 Period                   2
 3 Appt Date                3
 4 Patient                  4
 5 Type                     5
 6 Avail                    6
 7 Init                     7
 8 Acct                     8
 9 Loc                      9
10 Status                  10
11 Note                    11
12 Memo 1                  12
13 Memo 2                  13
14 Referring Dr#           14
15 Referring Dr Name       15
16 Sex                     16
17 Chart                   17
18 Date Of Birth           18
19 Age                     19
20 HIPAA Priv Alert        20
21 Supervising Dr          21
22 PCP #                   22
23 PCP Name                23
24 1                        1
25 10:00a                   2
26 04/23/20                 3
27 OV                       5
28 N                        6
29 SB                       7
30 O                        9
31 D                       10
32 Note gets typed here    11
33 1                        1
34 10:30a                   2
35 04/23                    3
36 NAME                     4
37 OV                       5
38 N                        6
39 sb                       7
40 73104                    8
41 O                        9
42 B                       10
43 follow up               11
44 32                      14
45 NAME, CREDENTIALS       15
46 SEX                     16
47 DOB entered here        18
48 5                       19
49 No                      20
50 32                      22
51 NAME, CREDENTIALS       23

Ответы [ 2 ]

1 голос
/ 10 июля 2020

В дополнение к xmlToDataFrame в значениях элементов Cell , рассмотрите внутренний (недокументированный) метод в XML для синтаксического анализа атрибутов Cell фрейма данных: xmlAttrsToDataframe доступно через оператор тройного двоеточия. Оттуда вызовите cbind для двух серий данных.

elem_df <- XML::xmlToDataFrame(doc, nodes=getNodeSet(doc, path="//doc:Cell", namespace=nmsp))

attrib_df <- XML:::xmlAttrsToDataFrame(getNodeSet(doc, path="//doc:Cell", namespace=nmsp))$Index
    
final_df <- cbind.data.frame(Data=elem_df, Index=attrib_df)

Вывод

final_df
#                    Data Index
# 1                Doctor     1
# 2                Period     2
# 3             Appt Date     3
# 4               Patient     4
# 5                  Type     5
# 6                 Avail     6
# 7                  Init     7
# 8                  Acct     8
# 9                   Loc     9
# 10               Status    10
# 11                 Note    11
# 12               Memo 1    12
# 13               Memo 2    13
# 14        Referring Dr#    14
# 15    Referring Dr Name    15
# 16                  Sex    16
# 17                Chart    17
# 18        Date Of Birth    18
# 19                  Age    19
# 20     HIPAA Priv Alert    20
# 21       Supervising Dr    21
# 22                PCP #    22
# 23             PCP Name    23
# 24                    1     1
# 25               10:00a     2
# 26             04/23/20     3
# 27                   OV     5
# 28                    N     6
# 29                   SB     7
# 30                    O     9
# 31                    D    10
# 32 Note gets typed here    11
# 33                    1     1
# 34               10:30a     2
# 35                04/23     3
# 36                NAME      4
# 37                   OV     5
# 38                    N     6
# 39                   sb     7
# 40                73104     8
# 41                    O     9
# 42                    B    10
# 43            follow up    11
# 44                   32    14
# 45    NAME, CREDENTIALS    15
# 46                  SEX    16
# 47     DOB entered here    18
# 48                    5    19
# 49                   No    20
# 50                   32    22
# 51    NAME, CREDENTIALS    23
0 голосов
/ 10 июля 2020

Это будет проще, если мы будем использовать пакет xml2:

library(xml2)

myxml <- read_xml("example.xml")

Rows     <- myxml %>% xml_ns_strip() %>% xml_find_all("//Row")
cells    <- Rows %>% lapply(xml_children)
indices  <- lapply(cells, function(x) as.numeric(xml_attr(x, "Index")))
n_row    <- lapply(seq_along(indices), function(i) rep(i, length(indices[[i]])))
contents <- lapply(cells, xml_text)

df <- do.call(rbind, lapply(seq_along(indices), function(i){
  data.frame(row = n_row[[i]], col = indices[[i]], value = contents[[i]])
}))

Итак, теперь наш фрейм данных содержит строку, столбец и содержимое для облегчения индексации:

df
#>    row col                value
#> 1    1   1               Doctor
#> 2    1   2               Period
#> 3    1   3            Appt Date
#> 4    1   4              Patient
#> 5    1   5                 Type
#> 6    1   6                Avail
#> 7    1   7                 Init
#> 8    1   8                 Acct
#> 9    1   9                  Loc
#> 10   1  10               Status
#> 11   1  11                 Note
#> 12   1  12               Memo 1
#> 13   1  13               Memo 2
#> 14   1  14        Referring Dr#
#> 15   1  15    Referring Dr Name
#> 16   1  16                  Sex
#> 17   1  17                Chart
#> 18   1  18        Date Of Birth
#> 19   1  19                  Age
#> 20   1  20     HIPAA Priv Alert
#> 21   1  21       Supervising Dr
#> 22   1  22                PCP #
#> 23   1  23             PCP Name
#> 24   2   1                    1
#> 25   2   2               10:00a
#> 26   2   3             04/23/20
#> 27   2   5                   OV
#> 28   2   6                    N
#> 29   2   7                   SB
#> 30   2   9                    O
#> 31   2  10                    D
#> 32   2  11 Note gets typed here
#> 33   3   1                    1
#> 34   3   2               10:30a
#> 35   3   3                04/23
#> 36   3   4                NAME 
#> 37   3   5                   OV
#> 38   3   6                    N
#> 39   3   7                   sb
#> 40   3   8                73104
#> 41   3   9                    O
#> 42   3  10                    B
#> 43   3  11            follow up
#> 44   3  14                   32
#> 45   3  15    NAME, CREDENTIALS
#> 46   3  16                  SEX
#> 47   3  18     DOB entered here
#> 48   3  19                    5
#> 49   3  20                   No
#> 50   3  22                   32
#> 51   3  23    NAME, CREDENTIALS
...