【问题标题】:using purr:: map across a data_frame with a list column使用 purrr:: 在具有列表列的数据框中映射
【发布时间】:2019-03-16 22:49:32
【问题描述】:

我正在尝试使用 purr map 函数或其变体来跨多个函数映射数据(在我的例子中是 R-Shiny 函数)。我正在读取来自example.json 的参数。

{
  "Section_1": {
    "MainHeader": [{
      "School": "Montessori"
    }],
    "boxitems": [{
        "tabName": "id1",
        "box": [{
            "title": "Students graph",
            "custofun": ["Bob", "Dan", "Sean"]
          },
          {
            "title": "Teacher graph",
            "custofun": ["Robinson"]
          }
        ]
      },
      {
        "tabName": "id2",
        "box": [{
            "title": "Students graph",
            "custofun": ["Felix", "Helix", "Alex"]
          },
          {
            "title": "Teacher graph",
            "custofun": ["Phelix"]
          }
        ]
      }
    ]
  }
}

我把它读成了df 格式

json <- fromJSON("example.json")

我正在尝试通过将 tabItem 及其 box'es 包装在 map 函数中来动态生成它们,例如仅映射 tabItem 值我可以使用 map。

 map(unique(df$id), ~ tabItem(tabName = .x)) 

这将为tabItem 生成html 代码,其中包含我在json 文件中拥有的所有ID 列表。对于我拥有的 json 结构,我需要遍历 ID -&gt; list -&gt; list。并且,将各自的输入参数传递给tabItembox 尝试了pmap 等其他变体,但无法解决。如何在该数据结构的数据框中递归使用 purr map 函数?

这是我的尝试

json$Section_1$boxitems %>% as.tibble() # to check the strucutre
df <- json$Section_1$boxitems %>% select(tabName,box)
df$box <- setNames(df$box,df$tabName)

BoxCustomFunc <- function(tabName,box) {
   map(tabName , ~ tabItem(tabName = .x),
   map2(x = box, y = box[tabName],
         box(title = .x$title, 
      column(width = 2, get(.y$custofun)(tabName)))
    ))
}

下面的当前输出。我得到的是tabItem,缺少的是框和列的html 输出。似乎map2 甚至没有渲染。

[[1]]
<div role="tabpanel" class="tab-pane" id="shiny-tab-id1"></div>

[[2]]
<div role="tabpanel" class="tab-pane" id="shiny-tab-id2"></div>

【问题讨论】:

  • 制作这个reproducible & minimal的几件事:第一,我们需要查看具有代表性的数据样本,而不是文本打印输出,因为您正在处理有关数据类型的问题;第二,这个问题实际上与 Shiny 无关,所以你不需要在这里引用 Shiny
  • 感谢您的指出,我分享了我有这个结构的 json 文件。希望这会有所帮助。

标签: r tidyverse purrr


【解决方案1】:

我建议将所有内容放在一个单一的平面数据框中:

df <- fromJSON( "example.json" )$Section_1$boxitems %>% as.tibble() %>% 
  unnest() %>% unnest() %>% mutate( Width = rep(c(2, 12, 4, 12), 2) )
# # A tibble: 8 x 4
#   tabName title         custofun Width
#   <chr>   <chr>         <chr>    <dbl>
# 1 id1     Student graph Bob          2
# 2 id1     Student graph Dan         12
# 3 id1     Student graph Sean         4
# 4 id1     Teacher graph Robinson    12
# 5 id2     Student graph Felix        2
# 6 id2     Student graph Helix       12
# 7 id2     Student graph Alex         4
# 8 id2     Teacher graph Phelix      12

第一步是将函数的字符名称映射到实际函数:

## Assuming that Felix, Helix, Alex and Phelix are defined
X <- df %>% mutate_at( "custofun", map, rlang::parse_expr ) %>%
  mutate_at( "custofun", map, rlang::eval_tidy )
# # A tibble: 8 x 4
#   tabName title         custofun Width
#   <chr>   <chr>         <list>   <dbl>
# 1 id1     Student graph <fn>         2
# 2 id1     Student graph <fn>        12
# ...

从内向外工作,您现在可以系统地应用map2 来生成闪亮的元素(函数应用于 ID -> 列 -> 框 -> 选项卡):

Y <- X %>% mutate( fres = invoke_map(custofun, tabName) ) %>%
  mutate( Col = map2(Width, fres, column) ) %>%
  group_by( tabName, title ) %>%
  summarize_at( "Col", list ) %>%
  mutate( Box = map2(title, Col, ~box(title=.x, .y)) ) %>%
  summarize_at( "Box", list ) %>%
  transmute( Tab = map2(tabName, Box, ~tabItem(tabName = .x, .y)) )
# # A tibble: 2 x 1
#   Tab            
#   <list>         
# 1 <S3: shiny.tag>
# 2 <S3: shiny.tag>

Y$Tab[[1]] 现在应该与您“手动”生成的 HTML 相匹配。 (减去 JSON 中的“学生图”和代码中的“学生图”之间的差异。)

【讨论】:

  • 这真是太棒了。非常感谢。我认为,对于我的真实数据,我想控制 JSON 本身的宽度。它因地块而异
  • 有没有办法可以使用 lappy 或 map 来提取 Y$Tab 元素?简单的打印,抛出一个错误,期望有 shiny.tag 对象。 map(lst,[[),需要一个索引,如果我将seq_along(lst) 作为索引传递,它会在 2 处引发错误递归失败。如何递归打印 shiny.tags ?
  • 只需 Y$Tab 为我打印两个标签。
  • 它在源代码中,但是当我在 Rshiny 上渲染时,它会抛出 Error in FUN(X[[i]], ...) : Expected an object with class 'shiny.tag'.
  • 听起来像是一个单独的问题。也许shiny 不能很好地处理存储在列表中的元素?我建议发布一个带有最小示例的单独问题,您可以手动创建tabItems 列表并显示shiny 给您的错误。如果您无法通过“手动”列表重现问题,请告诉我。这可能意味着shinytibble 之间存在一些奇怪的相互作用。
【解决方案2】:

有点难以准确理解你需要什么,但希望你能从中得到你需要的东西。

我所做的就是将非结构化数据转换成一个整齐的高数据框,然后循环生成 r 闪亮的代码。

1- 我遍历并抓取每个 boxitem 数据框并添加一个新列,将 boxitem 的 tabName 添加到其中。

2- boxitem 数据框的每一行都有一个 custofuns 列表,unnest 函数将它们分成多行。

3- 我将 boxitem 数据框组合成一个大数据框,您可以根据需要对其进行操作。

library(jsonlite)
library(tidyverse)

json <- fromJSON("example.json")

listOfGraphs <- apply(json$Section_1$boxitems, 1, function(x) x$box %>% mutate(tabName = x$tabName) ) 

listOfTabNames <- lapply(listOfGraphs, function(y) unnest(y))

listOfColumns <- bind_rows(listOfTabNames)

4- 这会生成字符串格式的 r 闪亮代码。如果有很多列,我们面临的问题是列的宽度。它遍历每个学生并为他们创建一个列。

listOfTabItems <- lapply(listOfTabNames, 
       function(x) paste(
         "tabItem(
  tabName = '",x$tabName[1],"',
  box(        
    title = 'Students graph',",
         apply(subset(x, title=="Student graph"), 1, function(y) paste0("column(width = 4, ",y[3],"('",y[2],"'))")), collapse = ", ",
         "),
  box(        
    title = 'Teacher graph',
   column(width = 12, ",subset(x, title=="Teacher graph")$custofun[1],"('",x$tabName[1],"'))
  )
)"
         )
       )

【讨论】:

  • Boxitems 列表是什么?解决方案引发错误。我正在尝试读取输入到 Rshiny 函数的参数。 Rshiny 函数生成 html 标签。因此,所需的输出是 html 标签。
  • 抱歉,我重命名了我的变量。在这里修复它。好的,那么第 4 步应该很有用
  • 我认为这不是我正在寻找的解决方案。如果您在上面看到我的尝试,我在其中创建了一个 BoxCustomFunc ,我将 JSON 文件内容传递给此函数。此外,您使用粘贴将带有\n 和其他特殊字符的html 输出弄乱了。我在上面有一个示例输出。我将在上面粘贴预期的 html。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-26
相关资源
最近更新 更多