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