разбор вложенных структур в R

У меня есть json-подобная строка, представляющая вложенную структуру. это не настоящий json, поскольку имена и значения не указаны. Я хочу разобрать его на вложенную структуру, например. список списков.

#example:
x_string = "{a=1, b=2, c=[1,2,3], d={e=something}}"

и результат должен быть таким:

x_list = list(a=1,b=2,c=c(1,2,3),d=list(e="something"))

есть ли какая-нибудь удобная функция, о которой я не знаю, которая делает такой разбор?

Спасибо.


person amit    schedule 21.12.2017    source источник
comment
Без кавычек это неприятно. Каковы ограничения относительно того, какие возможны нечисловые значения? Возможно, это можно изменить на действительный код R, используя регулярное выражение, а затем eval(parse()) результат.   -  person Roland    schedule 21.12.2017


Ответы (2)


Если все ваши данные согласуются, есть простое решение, включающее пакет regex и jsonlite. Код:

if(!require(jsonlite, quiet=TRUE)){ 
    #if library is not installed: installs it and loads it into the R session for use.

    install.packages("jsonlite",repos="https://ftp.heanet.ie/mirrors/cran.r-project.org")
    library(jsonlite)
}

x_string = "{a=1, b=2, c=[1,2,3], d={e=something}}"

json_x_string = "{\"a\":1, \"b\":2, \"c\":[1,2,3], \"d\":{\"e\":\"something\"}}"
fromJSON(json_x_string)

s <- gsub( "([A-Za-z]+)", "\"\\1\"",  gsub( "([A-Za-z]*)=", "\\1:", x_string ) )

fromJSON( s )

Первый раздел проверяет, установлен ли пакет. Если это так, он загружает его, в противном случае он устанавливает его, а затем загружает. Обычно я включаю это в любой код R, который пишу, чтобы упростить передачу между компьютерами/людьми.

Ваша строка — это x_string, мы хотим, чтобы она выглядела как json_x_string, которая дает желаемый результат, когда мы вызываем fromJSON().

Регулярное выражение разделено на две части, потому что это было давно - я почти уверен, что это можно было бы сделать более элегантным. Опять же, это зависит от того, согласуются ли ваши данные, поэтому я пока оставлю это так. Сначала он заменяет "=" на ":", затем добавляет кавычки вокруг всех групп букв. Вызов fromJSON(s) дает результат:

изJSON(s)

$a

[1] 1

$b

[1] 2

$c

[1] 1 2 3

$d

$d$e

[1] «что-то»

person AodhanOL    schedule 21.12.2017

Я бы предпочел избегать использования синтаксического анализа JSON из-за отсутствия расширяемости и гибкости и придерживаться решения регулярного выражения + рекурсии.

А вот расширяемый базовый код, который анализирует вашу входную строку по желанию.

Основная функция рекурсии:

# Parse string
parse.string = function(.string){
  regex = "^((.*)=)??\\{(.*)\\}"

  # Recursion termination: element parsing
  if(iselement(.string)){
    return(parse.element(.string))
  }

  # Extract components 
  elements.str = gsub(regex, "\\3", .string)
  elements.vector = get.subelements(elements.str)

  # Recursively parse each element
  parsed.elements = list(sapply(elements.vector, parse.string, USE.NAMES = F))

  # Extract list's name and return 
  name = gsub(regex, "\\2", .string)
  names(parsed.elements) = name
  return(parsed.elements)
}

.

Помогающие функции:

library(stringr)

# Test if the string is a base element
iselement = function(.string){
  grepl("^[^[:punct:]]+=[^\\{\\}]+$", .string)
}

# Parse element
parse.element = function(element.string){
  splits = strsplit(element.string, "=")[[1]]
  element = splits[2]

  # Parse numeric elements
  if(!is.na(as.numeric(element))){
    element = as.numeric(element)
  }

  # TODO: Extend here to include vectors

  # Reformat and return 
  element = list(element)
  names(element) = splits[1]
  return(element)
}

# Get subelements from a string
get.subelements = function(.string){
  # Regex of allowed elements - Extend here to include more types 
  elements.regex = c("[^, ]+?=\\{.+?\\}", #Sublist
                     "[^, ]+?=\\[.+?\\]", #Vector
                     "[^, ]+?=[^=,]+")    #Base element
  str_extract_all(.string, pattern = paste(elements.regex, collapse = "|"))[[1]]
}

.

Результаты разбора:

string = "{a=1, b=2, c=[1,2,3], d={e=something}}"
string_2 = "{a=1, b=2, c=[1,2,3], d=somthing}"
named_string = "xyz={a=1, b=2, c=[1,2,3], d={e=something, f=22}}"
named_string_2 = "xyz={d={e=something, f=22}}"

parse.string(string)
# [[1]]
# [[1]]$a
# [1] 1
# 
# [[1]]$b
# [1] 2
# 
# [[1]]$c
# [1] "[1,2,3]"
# 
# [[1]]$d
# [[1]]$d$e
# [1] "something"
person Deena    schedule 26.12.2017