使用 R 解析其中键和值是数值的类似字典的结构

Parse dictionary-like structure in which key and values are numeric values using R

我正在尝试解析我在旅行中遇到的一些奇怪的数据结构。本质上它们类似于 python 或 javascript 字典,但键和值都是数字:

weird <- "{47=4578.0005, 181=23456.7831, 216=7548.2367}"

为了将其转换为 table,我尝试将其转换为更典型的字典格式并使用 jsonlite 进行解析:

library(tidyverse)
library(jsonlite)
weird <- parse_json(str_replace(weird, "=", ":"))
#> Error: parse error: invalid object key (must be a string)

parse_json 函数正确地抱怨密钥不是字符串。我当然可以使用 str_split 剖析所有这些,但我希望善良的人可能对更多 elegant/compact 解决方案有一些洞察力(希望完全避免正则表达式),可以将其解析为 table 这样:

tibble::tribble(
  ~ key,    ~ value,
  47,  4578.0005,
  181, 23456.7831,
  216,  7548.2367 
)
#> # A tibble: 3 x 2
#>     key  value
#>   <dbl>  <dbl>
#> 1    47  4578.
#> 2   181 23457.
#> 3   216  7548.

谢谢!

JSON 要求引用其字典键。虽然此解决方案也引用了值,但将 as.numeric 应用于值应该不难(如果您确定数据语料库)。

library(dplyr)
library(tidyr)
gsub("=", ":", gsub("(\b|-?)([0-9.]+)\b", '"\1"', weird)) %>%
  jsonlite::fromJSON(.) %>%
  enframe() %>%
  unnest(value)
# # A tibble: 3 x 2
#   name  value     
#   <chr> <chr>     
# 1 47    4578.0005 
# 2 181   23456.7831
# 3 216   7548.2367 

这假设所有的键和值都是完全数字的(可能是负数,可能是小数),但在此模式中没有科学记数法。将其包括在内并非不可能,因此如果您需要更进一步:https://www.regular-expressions.info/floatingpoint.html, Parsing scientific notation sensibly?, and Regex for numbers on scientific notation? 可能会有所帮助。

下一步可能很简单

gsub("=", ":", gsub("(\b|-?)([0-9.]+)\b", '"\1"', weird)) %>%
  jsonlite::fromJSON(.) %>%
  enframe() %>%
  unnest(value) %>%
  mutate_all(as.numeric)
# # A tibble: 3 x 2
#    name  value
#   <dbl>  <dbl>
# 1    47  4578.
# 2   181 23457.
# 3   216  7548.

(明显丢失小数位只是表象,原始数据仍然有非整数值。)

虽然您可能想要一个紧凑的非正则表达式版本,但对我来说这似乎是最容易理解和易于理解的:

library(stringr)
library(dplyr)

weird <- "{47=4578.0005, 181=23456.7831, 216=7548.2367}"

str_remove_all(weird, "[{}]") %>% 
  str_split(", ", simplify = TRUE) %>% 
  str_split("=", simplify = TRUE) %>% 
  as_tibble() %>% 
  mutate_all(as.numeric)

#> # A tibble: 3 x 2
#>      V1     V2
#>   <dbl>  <dbl>
#> 1    47  4578.
#> 2   181 23457.
#> 3   216  7548.

reprex package (v0.3.0)

于 2020 年 4 月 20 日创建

我不介意对数据进行三遍,每一步只完成一件事。我发现它更容易阅读和推理。


更新:

虽然我仍然认为上面的版本是 R 惯用的方法,但它不是 R 惯用的数据结构,所以这是通过 {reticulate} 使用 Python 的解决方案。这比使用 {jsonlite} 的 JSON 解析器要复杂一些,但事实证明您可以使用数值作为 Python 字典中的键。

我们首先必须将 = 转换为 : 然后字符串成为有效的字典。然后我们可以启动一个 Python 上下文,运行 一个字符串,将其分配给一个变量,然后 return 将状态分配给 R。然后它只是将 R 中的列表转换为数据帧。

library(reticulate)
library(stringr)
library(glue)
library(tibble)

weird <- '{47=4578.0005, 181=23456.7831, 216=7548.2367}'

weird <- str_replace_all(weird, "=", ": ")

x <- py_run_string(
  glue("weird_dict = {weird}"), 
  convert = T
  )

x$weird_dict %>% 
  unlist() %>% 
  enframe()
#> # A tibble: 3 × 2
#>   name   value
#>   <chr>  <dbl>
#> 1 47     4578.
#> 2 181   23457.
#> 3 216    7548.

reprex package (v2.0.1)

于 2021-10-21 创建

我不认为你可以完全避免正则表达式,但通过一些小的替换它可以被 read.table() 轻松阅读。

wt <- c("{47=4578.0005, 181=23456.7831, 216=7548.2367}")

read.table(text = gsub("[{},]", "\n", wt), sep = "=")

   V1        V2
1  47  4578.001
2 181 23456.783
3 216  7548.237