提问人:msl 提问时间:10/20/2023 最后编辑:msl 更新时间:10/21/2023 访问量:80
当子节点发生变化时,将 XML 节点提取到数据帧中
Extracting XML nodes into a dataframe when child nodes vary
问:
我正在 R 中构建一个代码,以从 XML 格式的IRS990PF文件中提取授权数据。我构建了以下代码,该代码可以很好地为仅包含美国受助者的文件构建数据帧。但是,当有外国受助者时,代码不起作用,因为子节点的名称不同 - 导致列表大小不同。
我的示例XML文件是这个,用于亿滋国际基金会:https://projects.propublica.org/nonprofits/download-xml?object_id=202043219349103104
这是我的 R 代码:
grant_test <- XML::xmlParse(paste0(getwd(),"/xml_all/Mondelez-2019.xml")) %>%
XML::xmlToList()
grants_df <- grant_test[["ReturnData"]][["IRS990PF"]][["SupplementaryInformationGrp"]] %>%
lapply(function(x) unlist(x)) %>%
as.data.frame() %>%
## Choose the values to keep
dplyr::filter(row.names(.) %in% c("RecipientBusinessName.BusinessNameLine1Txt","RecipientFoundationStatusTxt", "GrantOrContributionPurposeTxt","Amt")) %>%
dplyr::select(-OnlyContriToPreselectedInd, -TotalGrantOrContriPdDurYrAmt,- TotalGrantOrContriApprvFutAmt) %>%
## Transpose dataframe
tibble::rownames_to_column(var="rowname") %>%
data.table::transpose(make.names="rowname")
它运行良好,并给了我一个数据框,其中每行都是赠款,并且有 4 列,分别是受赠人姓名、受赠人状态、项目描述和金额。
问题是,当我将相同的代码应用于不同的 XML 时,包括美国以外的受助者(例如这个:https://projects.propublica.org/nonprofits/download-xml?object_id=202033179349101943),我在第三行(“as.data.frame”)出现此错误:
错误 (函数 (..., row.names = NULL, check.rows = FALSE, check.names = TRUE, : 参数表示不同的行数: 9, 8, 7, 10, 1
我知道这个错误是由于国际受赠方的赠款与国内赠款没有相同的子节点造成的。美国受助者将始终如下所示:
<GrantOrContributionPdDurYrGrp>
<RecipientBusinessName>
<BusinessNameLine1Txt>IMMUNARTES LLC</BusinessNameLine1Txt>
</RecipientBusinessName>
<RecipientUSAddress>
<AddressLine1Txt>1463 E 53RD STREET FL 2</AddressLine1Txt>
<CityNm>CHICAGO</CityNm>
<StateAbbreviationCd>IL</StateAbbreviationCd>
<ZIPCd>60615</ZIPCd>
</RecipientUSAddress>
<RecipientFoundationStatusTxt>NC: US NON-EXEMPT</RecipientFoundationStatusTxt>
<GrantOrContributionPurposeTxt>PNEUMONIA</GrantOrContributionPurposeTxt>
<Amt>198000</Amt>
<GrantOrContributionPdDurYrGrp>
而国际被授权者的条目通常如下例所示(地址节点的名称不同,国家/地区代码的附加子节点),但在“RecipientForeignAddress”下也可以有不同的子节点(例如,有些缺少省/州编号)。
<GrantOrContributionPdDurYrGrp>
<RecipientBusinessName>
<BusinessNameLine1Txt>IMMUNIMED INC</BusinessNameLine1Txt>
</RecipientBusinessName>
<RecipientForeignAddress>
<AddressLine1Txt>62 SCURFIELD BLVD</AddressLine1Txt>
<CityNm>WINNIPEG</CityNm>
<ProvinceOrStateNm>MB</ProvinceOrStateNm>
<CountryCd>CA</CountryCd>
<ForeignPostalCd>R3Y1M5</ForeignPostalCd>
</RecipientForeignAddress>
<RecipientFoundationStatusTxt>NC: FOREIGN NON-EXEM</RecipientFoundationStatusTxt>
<GrantOrContributionPurposeTxt>DISCOVERY AND TRANSLATIONAL SCIENCES</GrantOrContributionPurposeTxt>
<Amt>50000</Amt>
<GrantOrContributionPdDurYrGrp>
所以,我的问题是:我怎样才能重新设计我的代码来解释“GrantOrContributionPdDurYrGrp”的不同实例之间列表长度的差异?我不需要数据框中的地址,但我想保留国际受助人的国家/地区代码,并将美国添加到国内受助人的该变量中。
答:
下面是 .
我们首先创建一个 ,每个授权一个节点,并将其转换为常规嵌套列表;当迭代列表中的授权时,我们将提取特定项目,对于不存在的项目最终具有值,生成的命名列表可以转换为 Data.frame / Tibble。xml2
xml_nodeset
NA
这里的一个假设是,缺少节点自动意味着存在且有效,因此一旦列表变成 ,列中的所有值都被视为非外部记录指标,并且这些值设置为.//RecipientForeignAddress/CountryCd
RecipientUSAddress
bind_rows()
NA
CountryCd
CountryCd
US
library(xml2)
grant_test_int <- read_xml("https://projects.propublica.org/nonprofits/download-xml?object_id=202033179349101943")
xml_ns_strip(grant_test_int)
grants_df <- xml_find_all(grant_test_int, "/Return/ReturnData/IRS990PF/SupplementaryInformationGrp/GrantOrContributionPdDurYrGrp") |>
as_list() |>
lapply(\(grant_node) list(
BusinessName = grant_node$RecipientBusinessName$BusinessNameLine1Txt[[1]],
FoundationStatus = grant_node$RecipientFoundationStatusTxt[[1]],
Purpose = grant_node$GrantOrContributionPurposeTxt[[1]],
Amt = as.numeric(grant_node$Amt[[1]]),
CountryCd = grant_node$RecipientForeignAddress$CountryCd[[1]]
)) |>
dplyr::bind_rows() |>
tidyr::replace_na(list(CountryCd = "US"))
grants_df
#> # A tibble: 3,712 × 5
#> BusinessName FoundationStatus Purpose Amt CountryCd
#> <chr> <chr> <chr> <dbl> <chr>
#> 1 1 FOR THE PLANET INC PC: 509(A)(1) GLOBAL… 5.07e4 US
#> 2 1000 DAYS PC: 509(A)(1) NUTRIT… 1 e6 US
#> 3 100 BLACK MEN OF AMERICA INC PC: 509(A)(2) GLOBAL… 4 e5 US
#> 4 2164 INC PC: 509(A)(2) GLOBAL… 5 e4 US
#> 5 4POINT0 SCHOOLS PC: 509(A)(1) K-12 E… 1 e6 US
#> 6 501 COMMONS PC: 509(A)(1) PACIFI… 3 e5 US
#> 7 A-ALPHA BIO INC NC: US NON-EXEM… SUPPOR… 6.87e4 US
#> 8 AARON DIAMOND AIDS RESEARCH CENTER… PC: 509(A)(1) HIV 7.55e5 US
#> 9 ABT ASSOCIATES INC NC: US NON-EXEM… FAMILY… 1.41e6 US
#> 10 ACADEMISCH MEDISCH CENTRUM GOV: FOREIGN GO… HIV 1.53e6 NL
#> # ℹ 3,702 more rows
第一次尝试 - https://stackoverflow.com/revisions/77332323/1 - 试图通过反复传递授权节点来避免并仅从 XML 树中提取特定节点;它慢了大约 20 倍。xml2::to_list()
xml2::xml_find_first()
评论