Использование jsonlite для анализа строки json с числовыми метками

#r #json #jsonlite

#r #json #jsonlite

Вопрос:

Я извлекаю строку json из API (над которым у меня нет контроля), которая выглядит следующим образом:

 {
     "data": [
     {
         "6": {
             "value": "Jamie Stein"
         },
         "7": {
             "value": 10
         }
     },
     {
         "6": {
             "value": "Bill Smith"
         },
         "7": {
             "value": 23
         }
     }
     ],
     "fields": [
     {
         "id": 6,
         "label": "Full Name"
     },
     {
         "id": 7,
         "label": "Amount"
     }
     ]
 }
  

Я использую fromJSON пакет jsonlite (версия 1.7.0) для анализа строки:
res <- from JSON(jsonstr) .

Результирующий фрейм данных имеет поврежденные имена. Результат выглядит следующим образом:

 > res
$data
        value value
1 Jamie Stein    10
2  Bill Smith    23

$fields
  id     label
1  6 Full Name
2  7    Amount
  

Обратите внимание на имена столбцов «value» во фрейме данных. Я могу обновить имена столбцов для фрейма данных, но это, похоже, делает вещи еще более странными:

 > get_label <- function(x) { res$fields$label[res$fields$id == x]}
> names(res$data) <- sapply(as.integer(names(res$data)), get_label)
> names(res$data)
[1] "Full Name" "Amount"   
> res$data
        value value
1 Jamie Stein    10
2  Bill Smith    23
  

Функция names указывает, что имена столбцов были обновлены, но простая печать фрейма данных указывает, что имена столбцов все еще повреждены.

Может кто-нибудь помочь мне понять, что происходит, и что я могу с этим поделать? На данный момент я сбит с толку. Поврежденный фрейм данных проблематичен — когда я использую write.csv, результирующий файл является мусором.

Кроме того, если это полезно, версия R, которую я использую, — 4.0.2.

Комментарии:

1. str(res) показывает, что data это не является чистым data.frame . Когда вы запустите это, увидите, что fields это выглядит так, как должен выглядеть фрейм , тогда data как это своего рода вложенный фрейм. Вам нужно сохранить идентификаторы 6 и 7 или просто отбросить их и получить «обычный» data фрейм?

2. @r2evans — 6 и 7 используются для сопоставления с меткой поля. Данные, возвращаемые API, имеют связанный с ними идентификатор (6 или 7), и с каждым идентификатором имеет соответствующую метку (метки «Полное имя» и «Количество»). Метки могут меняться со временем, в то время как идентификатор является постоянным — я полагаю, именно поэтому API предназначен для возврата данных в этом конкретном формате.

Ответ №1:

Хм, странно. Вы можете сделать:

 library(jsonlite)
library(purrr) # to use 'transpose'
obj <- fromJSON(json, simplifyDataFrame = FALSE)
lapply(obj, function(x){
  as.data.frame(lapply(transpose(x), unlist))
})
# $data
#            X6 X7
# 1 Jamie Stein 10
# 2  Bill Smith 23
# 
# $fields
#   id     label
# 1  6 Full Name
# 2  7    Amount
  

Чтобы получить имена столбцов "6" и "7" :

 lapply(obj, function(x){
  as.data.frame(lapply(transpose(x), unlist), check.names = FALSE)
})
# $data
#             6  7
# 1 Jamie Stein 10
# 2  Bill Smith 23
# 
# $fields
#   id     label
# 1  6 Full Name
# 2  7    Amount
  

РЕДАКТИРОВАТЬ: более простой способ

На самом деле не странно…

 obj <- fromJSON(json)
  

obj$data является фреймом данных, но его столбцы также являются фреймами данных:

 > str(obj$data)
'data.frame':   2 obs. of  2 variables:
 $ 6:'data.frame':  2 obs. of  1 variable:
  ..$ value: chr  "Jamie Stein" "Bill Smith"
 $ 7:'data.frame':  2 obs. of  1 variable:
  ..$ value: int  10 23
  

Затем вы хотите:

 obj$data <- as.data.frame(lapply(obj$data, "[[", "value"))
  

Комментарии:

1. Спасибо. Это позволяет мне преодолеть мое узкое место, но я все еще не понимаю, что происходит. Например, я думал, что имена столбцов, отображаемые при отображении фрейма данных, всегда будут совпадать с именами столбцов, отображаемыми при вызове функции names() в фрейме данных. Тот факт, что в этом случае они разные, является для меня новым.

2. Хорошо, я думаю, я понял. Каждый столбец фрейма данных данных, вместо того, чтобы быть вектором, сам по себе был одностолбцовым фреймом данных. Таким образом, было два уровня фреймов данных. Когда я вызывал имена на верхнем уровне, он показывал мне имена, связанные со столбцами верхнего уровня. Но когда я отображал фрейм данных верхнего уровня в целом, он отображал имена, связанные с фреймом данных нижнего уровня. Это запутано, но это, безусловно, объясняет то, что я наблюдал. Спасибо за вашу помощь.