【问题标题】:R JSON: Quickly create a json field as a new variableR JSON:快速创建一个json字段作为新变量
【发布时间】:2022-01-18 23:41:42
【问题描述】:

我想将 JSON 列添加到现有的 data.frame 中,其中包含大多数每一行的信息。下面的代码显示了我当前的方法并达到了我想要的结果。

# Establish test data
testData <- data.table::data.table(id = 1:10000,
                                   var1 = rep(letters[1:10], times = 1000),
                                   var2 = rep(letters[11:20], times = 1000))

# Establish which variables will be JSON-ed
jsonVars <- c("var1","var2")

# Initialize JSON column
testData[["json"]] <- as.character(NA)

# Loop through and populate JSON column
for(i in 1:nrow(testData)) {
  cat(paste0("Running ", i, " of ", nrow(testData), "\n"))
  testData[i,][["json"]] <- gsub('^.|.$','',jsonlite::toJSON(testData[i,jsonVars,with=F]))
}

# Keep only the identifier and JSON fields
testData <- testData[,c("id","json"),with=F]

如您所见,我想要的结果只是一个“id”字段和“json”字段,其中包含所有行的信息。

虽然上述方法有效,但速度太慢,因为我的“真实”数据集有数百万条记录和约 300 列。

我尝试使用apply()(见下文),但性能没有提高

# Establish test data
testData <- data.table::data.table(id = 1:10000,
                                   var1 = rep(letters[1:10], times = 1000),
                                   var2 = rep(letters[11:20], times = 1000))

# Establish which variables will be JSON-ed
jsonVars <- c("var1","var2")

# Initialize JSON column
testData[["json"]] <- as.character(NA)

# Use apply to populate JSON column
jsonFUN <- function(x) { gsub('^.|.$','',jsonlite::toJSON(testData[,jsonVars,with=F])) }
testData$json <- apply(X = testData, MARGIN = 1, FUN = jsonFUN)

# Keep only the identifier and JSON fields
testData <- testData[,c("id","json"),with=F]

有人知道更快完成这项任务的有效方法吗?

也许答案是更好地使用data.table 工具。 jsonlite::toJSON 的文档还提到了对向量、data.frames 和数组的处理,但我一直没能找到解决方案。

感谢您的帮助!

【问题讨论】:

    标签: r json jsonlite


    【解决方案1】:

    jsonlite

    这不是很快,但它可以工作并且是一个规范的 json 方法:

    testData[, .(json = jsonlite::toJSON(.SD)), by = id
      ][, json := gsub("^\\[|\\]$", "", json)][]
    #           id                    json
    #        <int>                  <json>
    #     1:     1 {"var1":"a","var2":"k"}
    #     2:     2 {"var1":"b","var2":"l"}
    #     3:     3 {"var1":"c","var2":"m"}
    #     4:     4 {"var1":"d","var2":"n"}
    #     5:     5 {"var1":"e","var2":"o"}
    #     6:     6 {"var1":"f","var2":"p"}
    #     7:     7 {"var1":"g","var2":"q"}
    #     8:     8 {"var1":"h","var2":"r"}
    #     9:     9 {"var1":"i","var2":"s"}
    #    10:    10 {"var1":"j","var2":"t"}
    #    ---                              
    #  9991:  9991 {"var1":"a","var2":"k"}
    #  9992:  9992 {"var1":"b","var2":"l"}
    #  9993:  9993 {"var1":"c","var2":"m"}
    #  9994:  9994 {"var1":"d","var2":"n"}
    #  9995:  9995 {"var1":"e","var2":"o"}
    #  9996:  9996 {"var1":"f","var2":"p"}
    #  9997:  9997 {"var1":"g","var2":"q"}
    #  9998:  9998 {"var1":"h","var2":"r"}
    #  9999:  9999 {"var1":"i","var2":"s"}
    # 10000: 10000 {"var1":"j","var2":"t"}
    

    每行的默认输出实际上是 "[{"var1":"a","var2":"k"}]"(长度为 1 的列表),我使用 gsub 模式删除了它。如果您不介意长度为 1 的列表,则可以简化 gsub

    此外,这假设行是唯一的 id;如果id 有多行,那么这将产生不同的结果(对于这些行)。

    sprintf

    这是一个 hack,几乎没有那么普遍,但它可能要快得多:

    testData[, .(id, json = sprintf('{"var1":%s,"var2":%s}', dQuote(var1, FALSE), dQuote(var2, FALSE)))]
    

    我告诫不要将其用于更复杂的事情,因为使用 jsonlite::toJSON 是处理任何不重要的事情的更谨慎/正确的方法。

    【讨论】:

    • 这个解决方案效果很好。在我的“真实”数据集中,规范方法每 1000 条记录需要约 17 秒,这对于我的目的来说是可以接受的。 hacky 方法大约快 10 倍,但实施起来会有些乏味。感谢您的帮助!
    猜你喜欢
    • 2015-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-20
    • 1970-01-01
    • 2021-05-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多