Импорт данных, сгенерированных JavaScript, в R или Stata - PullRequest
0 голосов
/ 06 июля 2019

Мне дали CSV-файл, который выглядит следующим образом:

id,v2,class
1,1,"{2=22.7, 0=4.8, 3=321}"
2,1,"{2=929.5, 1=11.2, 3=1229.8}"
3,2,"{2=50.9, 1=28.8}"

Мне нужно сделать эту таблицу - точнее, импортировать ее в R фрейм данных или Stata. В конце дня я хочу иметь таблицу, которая выглядит следующим образом:

id  v2  class_0 class_1 class_2 class_3
1   1   4.8     0       22.7    321
2   1   0       11.2    929.5   1229.8
3   2   0       28.8    50.9    0

Проблемный «столбец» ( класс ) содержит данные о том, что должно стать 4 столбцами в таблице. Обратите внимание, что значения 0 не отображаются в исходном файле.

Фон

Этот файл был создан в Google Earth Engine, который использует JavaScript. За исключением того факта, что это действительно файл значений, разделенных запятыми, проблемный «столбец» ( class ) для меня выглядит как объект JSON (что имеет смысл, поскольку файл был сгенерирован JavaScript). Однако ясно, что это не файл JSON.

Учитывая, что в R я пытался интерпретировать столбец преступника как JSON и сделал следующее:

library("rjson")
df = read.csv(csv_file)
json_data = fromJSON(paste(df$waterClass, collapse=""))
> Error in fromJSON(paste(df$waterClass, collapse = "")) :
  unexpected character "1"; expecting opening string quote (") for key value

Не повезло.

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

Ответы [ 4 ]

3 голосов
/ 06 июля 2019

Мы можем преобразовать каждую строку в запись формата DCF и затем прочитать ее, используя read.dcf. Сначала прочитайте его в DF0, а затем преобразуйте каждую строку в запись в формате DCF, получив v. Затем прочитайте его, используя read.dcf, измените порядок столбцов и преобразуйте их в числовые, получая числовую матрицу. Наконец, замените NA на 0. Пакеты не используются.

DF0 <- read.csv(text = Lines, as.is = TRUE)
v <- with(DF0, paste0("\nid:", id, "\nv2:", v2, chartr("{}=,", "\n :\n", class)))
v <- gsub(" ", "", v)
m <- read.dcf(textConnection(v))
nms <- c("id", "v2", sort(setdiff(colnames(m), c("id", "v2"))))
mm <- apply(m[, nms], 2, as.numeric)
mm[is.na(mm)] <- 0
mm

дает эту числовую матрицу:

     id v2   0    1     2      3
[1,]  1  1 4.8  0.0  22.7  321.0
[2,]  2  1 0.0 11.2 929.5 1229.8
[3,]  3  2 0.0 28.8  50.9    0.0

Примечание

Ввод в воспроизводимом виде:

Lines <- 'id,v2,class
1,1,"{2=22.7, 0=4.8, 3=321}"
2,1,"{2=929.5, 1=11.2, 3=1229.8}"
3,2,"{2=50.9, 1=28.8}"'
2 голосов
/ 06 июля 2019

Вот решение Stata с примерами данных как data.csv:

import delimited using data.csv, clear
split class, parse(,) 
drop class 
reshape long class, i(id) j(which) 
replace class = subinstr(class, "{", "", .) 
replace class = subinstr(class, "}", "", .) 
split class, parse("=") destring 
drop class which 
rename (class?) (which class_) 
replace class_ = 0 if missing(class_) 
drop if missing(which) 
reshape wide class_, i(id) j(which) 
mvencode class_*, mv(0) 

list

     +-------------------------------------------------+
     | id   class_0   class_1   class_2   class_3   v2 |
     |-------------------------------------------------|
  1. |  1       4.8         0      22.7       321    1 |
  2. |  2         0      11.2     929.5    1229.8    1 |
  3. |  3         0      28.8      50.9         0    2 |
     +-------------------------------------------------+
2 голосов
/ 06 июля 2019

Тидиверс подход:

library(tidyverse)

df <- read_csv('id,v2,class
1,1,"{2=22.7, 0=4.8, 3=321}"
2,1,"{2=929.5, 1=11.2, 3=1229.8}"
3,2,"{2=50.9, 1=28.8}"')

df_clean <- df %>% 
    separate_rows(class, sep = ',') %>%    # separate dict elements
    separate(class, c('key', 'value'), sep = '=') %>%    # split key-value pairs
    mutate(key = paste0('class_', parse_number(key)),    # clean up
           value = parse_number(value)) %>% 
    spread(key, value, fill = 0)    # spread back to wide form

df_clean
#> # A tibble: 3 x 6
#>      id    v2 class_0 class_1 class_2 class_3
#>   <dbl> <dbl>   <dbl>   <dbl>   <dbl>   <dbl>
#> 1     1     1     4.8     0      22.7    321 
#> 2     2     1     0      11.2   930.    1230.
#> 3     3     2     0      28.8    50.9      0
1 голос
/ 06 июля 2019

Вы можете немного выдумать, чтобы добавить кавычки, и тогда jsonlite сможет позаботиться об этом:

library(jsonlite)
cbind(
  dat[c("id","v2")],
  stream_in(textConnection(
    gsub("(\\s+|\\{)(.+?)=(.+?)(,|})", '\\1"\\2":\\3\\4', dat$class))
  )
)
# Imported 3 records. Simplifying...
#  id v2     2    0      3    1
#1  1  1  22.7  4.8    321 <NA>
#2  2  1 929.5 <NA> 1229.8 11.2
#3  3  2  50.9 <NA>   <NA> 28.8
...