【问题标题】:Manipulate values within nested lists操作嵌套列表中的值
【发布时间】:2019-12-25 22:07:52
【问题描述】:

我正在尝试从 API 读取 JSON 元数据,在 R 中对其进行操作,然后 POST 回来。

但是,当我通过 jsonlite 获取元数据时,TRUE/FALSE 值被读取为逻辑值并变为大写。 API 将不接受大写的 TRUE/FALSE 返回。所以我需要将所有 TRUE/FALSE 替换为“true”和“false”作为字符,但保留与输入相同的嵌套列表结构。

问题在于这些是 JSON 键/值对和嵌套数组,因此 true/false 值嵌套在不同级别。我也只想更改“TRUE”或“FALSE”的值,而不是包含“true”或“false”的字符串。

我尝试过应用函数系列、purrr 映射和递归 for 循环。

#read data via API
dashitem_api<-paste0("api/metadata.json?filter=id:like:",dashitem_old_idprefix)
url<-paste0(baseurl,dashitem_api)

dash_items<-jsonlite::fromJSON(content(GET(url),"text")[])

...然后替换ID ....

编辑: ID 的替换将逻辑 TRUE/FALSE 值强制转换为字符串“true”/“false”。

为冗长的插图道歉......


    dashitem_old_idprefix<-"Ane0008"
    dashitem_new_idprefix<-"Ane0028"

    dash_items<-jsonlite::fromJSON(content(GET(url),"text")[])

    class(dash_items$charts$showData)
###output = "logical"

    #put all the replacement items into a list
    x1<-list(dashitem_old_idprefix,
             dashitem_new_idprefix)

    x2<-sapply(x1, function(x) as.character(x))

    replacements<-function(y){
      return(
        y %>% 
          gsub(x2[1], x2[2], .)
      )
    }

    new_dash <- rapply(dash_items, f = replacements, 
                       how = "replace")

    class(new_dash$charts$showData)
###output = "character"

R 对象是一个列表,包含字符向量、命名列表、未命名列表、字符向量列表和数据帧,类似这样

new_dash<-list(charts=list(list(id="abcd123",shared="FALSE",translations=list(),
                 dimensions=data.frame(thisyear="FALSE",last6Months="TRUE"),
params=list(reportingPeriod="FALSE",reportingUnit="FALSE"),
dimensionItems=list(type="DATA_ELEMENT",dataElement=list(id="ZYXW987"))),

               list(id="abcd4567",shared="FALSE",translations=list(),
                dimensions=data.frame(thisyear="FALSE",last6Months="TRUE"),
                params=list(reportingPeriod="FALSE",reportingUnit="TRUE"),
dimensionItems=list(type="DATA_ELEMENT",dataElement=list(id="ZYXW988")))),

reportTables=list(id="abcd124",title="false positives", shared="FALSE",translations=list(),
 dimensions=data.frame(thisyear="FALSE",last6Months="TRUE"),
params=list(reportingPeriod="FALSE",reportingUnit="FALSE"),
dimensionItems=list(type="DATA_ELEMENT",dataElement=list(id="ZYXW989"))))

我在网上找到了这些解决方案,但我发现我需要用真/假数据值指定列表的名称或位置,否则会出错,或者它以不需要的方式更改 new_dash 文件(添加一个新的嵌套例如列表)。

#solution 1
change_list <- function(x) {
  for (i in seq_along(x)) {
    value <- x[[i]]
    if (is.list(value)) {
      x[[i]] <- change_list(value)
    } else {
      if (as.character(value)=="FALSE") {
        x[[i]] <- tolower(value)
      }
    }
  }
  x
}
test1<-change_list(new_dash)

#solution 2
test2<-lapply(new_dash, function(x) {
  id <- x == "FALSE"
  x[id] <- "false"
  return(x)
})


#solution 3
test3<- c(map(new_dash$charts, 
~modify_if(~x=="TRUE", tolower)), 
recursive= TRUE)

我可能需要一些结合 modify_if 和 modify_at 的 purrr 函数。或者,另一种读取默认情况下不会转换为逻辑 TRUE/FALSE 的数据的方法。

FWIW 我是一名 R 新手,无论多么复杂或简单,我都将不胜感激。

【问题讨论】:

    标签: r json purrr


    【解决方案1】:

    列表对象中的值实际上是"TRUE"(R 字符串)还是TRUE(R 逻辑)?如果它们是有效的 R 逻辑(与您共享的示例数据不同),则 jsonlite::toJSON 将更正它们。

    x <- list(
      partA = list(numbers = 1:3, boolean = T),
      partB = list(
        nested = list(
          numbers = 4:6, 
          nestB = list(boolean = c(FALSE, FALSE))
          )
        )
    )
    
    jsonlite::toJSON(x, pretty = T)
    
    {
      "partA": {
        "numbers": [1, 2, 3],
        "boolean": [true]
      },
      "partB": {
        "nested": {
          "numbers": [4, 5, 6],
          "nestB": [
            {
              "boolean": false
            },
            {
              "boolean": false
            }
          ]
        }
      }
    }
    

    您似乎不太可能在数据处理步骤中生成 "TRUE""FALSE" 字符串(更新: 确实是问题!),所以希望这行得通。 jsonlite::fromJSON[true, false] 转换为c(TRUE, FALSE),而toJSON 则相反。

    确保数据帧被强制转换为相同的格式可能需要进行一些检查。 toJSON有一些选择:dataframe =可以

    • ”(默认),
    • "nestB": {"boolean": [false, false]},或
    • "nestB": [[false], [false]]

    但如果您使用默认值来读取 API 响应,则不太可能需要更改默认值以将其发回。


    更新:

    这个用例调用rapply 来搜索整个列表对象并替换某些元素。因为那是调用gsub,所以每个元素都被强制转换为一个字符,包括任何数字或逻辑值。为了防止这种情况,您可以使用some_output_object &lt;- rapply(some_input_object, f = some_replacing_function, how = "replace", classes = "character")。这使数字和逻辑值保持不变,以便toJSON 可以正确地包装它们。

    【讨论】:

    • 嗨,布赖恩,感谢您的回复!但是,这些值在数据处理之前确实是逻辑的,但是在数据处理过程中转换为字符串。请参阅我上面的编辑。也许我应该直接尝试替换 JSON 对象?我不知道,但让我感到奇怪的是,我不能简单地将所有“TRUE”值替换为“true”。
    • @bodo 你能发布实际的 JSON 响应对象(如果需要,手动匿名)吗?将"TRUE" 替换为"true" 无论如何都不会解决您的问题,因为当您重新JSONify 列表时会遇到问题:{"x": true} vs {"x": "true"}
    • 您可能只需要更改您的rapply 调用:new_dash &lt;- rapply(dash_items, f = replacements, how = "replace", classes = "character"),这样它就不会更改非字符字段,因此不会将它们强制转换为字符。
    • 谢谢,这行得通!!!诀窍是 classes = "character" 选项。连想都没想!但现在 API 可以正确读取 POST 请求的 JSON 输出。
    猜你喜欢
    • 2019-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-20
    • 1970-01-01
    • 1970-01-01
    • 2011-07-10
    • 2020-12-09
    相关资源
    最近更新 更多