【问题标题】:R convert dataframe to a nested json file/object grouped by column namesR将数据框转换为按列名分组的嵌套json文件/对象
【发布时间】:2021-09-18 21:22:15
【问题描述】:

我想将数据框转换为嵌套的 json 对象,并根据列名确定在哪里创建嵌套的 json 对象。

我做了一个玩具例子来解释这个问题。鉴于此数据框:

df <- read.csv(textConnection(
"id,name,allergies.pollen,allergies.pet,attributes.height,attributes.gender
x,alice,no,yes,175,female
y,bob,yes,yes,180,male"))

或者以更易读的格式:

    id  name allergies.pollen allergies.pet attributes.height attributes.gender
  1  x alice               no           yes               175            female
  2  y   bob              yes           yes               180              male

那我想要下面的json对象:

'[
  {
    "id": "x",
    "name": "alice",
    "allergies":
    {
      "pollen": "no",
      "pet": "yes"
    },
    "attributes": 
    {
      "height": "175",
      "gender": "female"
    }
  },
  {
    "id": "y",
    "name": "bob",
    "allergies":
    {
      "pollen": "yes",
      "pet": "yes"
    },
    "attributes":
    {
      "height": "180",
      "gender": "male"
    }
  }
]'

所以它应该自动在固定分隔符“.”处对列进行分组。

理想情况下,它也应该能够处理嵌套嵌套的对象,例如allergies.pet.catallergies.pet.dog

解决这个问题我最好的想法是创建一个函数,递归调用jsonlite::toJSON 并使用stringr::str_extract("^[^.]*") 提取类别,但我无法完成这项工作。

【问题讨论】:

  • 请说出您期望发生碰撞时会发生什么,例如同时具有allergies.petallergies.pet.cat。一般来说,如果您的样本数据有第二个嵌套,这将有助于答案的完整性。
  • 我希望子类别也可以嵌套,即{"allergies" : {"pet": {"dog": "yes", "cat": "yes"}}}
  • 对于碰撞我没有考虑过,所以经销商选择。但我认为你的回答处理它的方式是明智的。

标签: r dplyr stringr jsonlite


【解决方案1】:

这是一个似乎有效的功能。唯一的故障是是否存在可能的冲突,例如allergies.petallergies.pet.car;虽然它没有错误,但它可能是非标准的。

新数据:

df <- read.csv(textConnection(
"id,name,allergies.pollen,allergies.pet,attributes.height,attributes.gender,allergies.pet.cat
x,alice,no,yes,175,female,quux
y,bob,yes,yes,180,male,unk"))

功能:

func <- function(x) {
  grps <- split(names(x), gsub("[.].*", "", names(x)))
  for (nm in names(grps)) {
    if (length(grps[[nm]]) > 1 || !nm %in% grps[[nm]]) {
      x[[nm]] <- setNames(subset(x, select = grps[[nm]]),
                          gsub("^[^.]+[.]", "", grps[[nm]]))
      x[,setdiff(grps[[nm]], nm)] <- NULL
    }
  }
  for (nm in names(x)) {
    if (is.data.frame(x[[nm]])) {
      x[[nm]] <- func(x[[nm]])
    }
  }
  if (any(grepl("[.]", names(x)))) func(x) else x
}

看看这是如何将所有.分隔的列嵌套到框架中的:

str(df)
# 'data.frame': 2 obs. of  7 variables:
#  $ id               : chr  "x" "y"
#  $ name             : chr  "alice" "bob"
#  $ allergies.pollen : chr  "no" "yes"
#  $ allergies.pet    : chr  "yes" "yes"
#  $ attributes.height: int  175 180
#  $ attributes.gender: chr  "female" "male"
#  $ allergies.pet.cat: chr  "quux" "unk"
newdf <- func(df)
str(newdf)
# 'data.frame': 2 obs. of  4 variables:
#  $ id        : chr  "x" "y"
#  $ name      : chr  "alice" "bob"
#  $ allergies :'data.frame':   2 obs. of  2 variables:
#   ..$ pollen: chr  "no" "yes"
#   ..$ pet   :'data.frame':    2 obs. of  2 variables:
#   .. ..$ pet: chr  "yes" "yes"
#   .. ..$ cat: chr  "quux" "unk"
#  $ attributes:'data.frame':   2 obs. of  2 variables:
#   ..$ height: int  175 180
#   ..$ gender: chr  "female" "male"

从这里开始,直接jsonify:

jsonlite::toJSON(newdf, pretty = TRUE)
# [
#   {
#     "id": "x",
#     "name": "alice",
#     "allergies": {
#       "pollen": "no",
#       "pet": {
#         "pet": "yes",
#         "cat": "quux"
#       }
#     },
#     "attributes": {
#       "height": 175,
#       "gender": "female"
#     }
#   },
#   {
#     "id": "y",
#     "name": "bob",
#     "allergies": {
#       "pollen": "yes",
#       "pet": {
#         "pet": "yes",
#         "cat": "unk"
#       }
#     },
#     "attributes": {
#       "height": 180,
#       "gender": "male"
#     }
#   }
# ] 

【讨论】:

  • 它完全按照预期工作 - 非常感谢!关于冲突,那么我的数据集没有任何情况下标签应该既嵌套又非嵌套(例如{"pet": "yes", "pet": {"dog": "yes"}}),但如果确实如此,那么我认为处理它的方式与@ 987654329@ 有道理。
猜你喜欢
  • 2023-03-20
  • 1970-01-01
  • 2021-06-12
  • 2021-12-21
  • 1970-01-01
  • 1970-01-01
  • 2021-12-21
  • 2019-03-28
  • 2019-07-08
相关资源
最近更新 更多