【问题标题】:Access values within S3 list-like objects with mutate() (or extract values from within S3 objects)使用 mutate() 访问 S3 类列表对象中的值(或从 S3 对象中提取值)
【发布时间】:2017-05-20 21:41:03
【问题描述】:

我希望能够对包含类似 S3 列表的对象的数据框(tibble)列应用操作,以作用于列中每个对象的命名项之一。根据问题的底部,我在mutate() 中使用sapply() 进行这项工作,但这似乎是不必要的。

信息存储在包含原子数据的列中,像 mutate() 这样的 dplyr 函数可以按预期工作。这有效,例如:

library(dplyr)
people_cols <- tibble(name = c("Fiona Foo", "Barry Bar", "Basil Baz"),
                  height_mm = c(1750, 1700, 1800),
                  weight_kg = c(75, 73, 74)) %>%
  mutate(height_inch = height_mm / 25.4)
people_cols
# # A tibble: 3 × 4
#   name          height_mm   weight_kg   height_inch
#   <chr>         <dbl>       <dbl>       <dbl>
# 1 Fiona Foo     1750        75          68.89764
# 2 Barry Bar     1700        73          66.92913
# 3 Basil Baz     1800        74          70.86614

但我想处理 S3 列表对象中的数据。这是一个玩具示例:

person_stats <- function(name, height_mm, weight_kg) {
  this_person <- structure(list(name = name,
                                height_mm = height_mm,
                                weight_kg = weight_kg),
                           class = "person_stats")
}

fiona <- person_stats("Fiona Foo", 1750, 75)
barry <- person_stats("Barry Bar", 1700, 73)
basil <- person_stats("Basil Baz", 1800, 74)

fiona$height_mm
# [1] 1750

我可以像这样将这些对象放入一个 tibble 列中:

people <- tibble(personstat = list(fiona, barry, basil))

people
# # A tibble: 3 × 1
# personstat
#     <list>
#   1 <S3: person_stats>
#   2 <S3: person_stats>
#   3 <S3: person_stats>

但是,如果我尝试在包含这些对象的列上使用 mutate(),我会得到错误:

people <- tibble(personstat = list(fiona, barry, basil)) %>%
  mutate(height_inch = personstat$height_mm / 25.4)
# Error in mutate_impl(.data, dots) : object 'personstat' not found

尽量保持简单 - 如果我什至可以自己引用已命名的项目,那么我至少可以将它们放入一个新列,然后对它们执行任何操作:

people <- tibble(personstat = list(fiona, barry, basil)) %>%
  mutate(height_mm = personstat$height_mm)
# Error in mutate_impl(.data, dots) : 
#  Unsupported type NILSXP for column "height_mm"

注意不同的错误,这很有趣 - 它不再抱怨找到列,只是在与命名项目斗争。

我可以使用基本函数 cbind()sapply() 以及 [[ 作为函数来让它工作:

people <- tibble(personstat = list(fiona, barry, basil)) %>%
  cbind(height_mm = sapply(.$personstat, '[[', name="height_mm"))

people
#            personstat height_mm
# 1 Fiona Foo, 1750, 75      1750
# 2 Barry Bar, 1700, 73      1700
# 3 Basil Baz, 1800, 74      1800

虽然这失去了轻率。

class(people)
# [1] "data.frame"

最后,这让我明白了这一点,它有效,但感觉就像使用 sapply() 有点错过了 dplyr mutate() 的要点,我认为它应该在不需要的情况下一直向下工作:

people <- tibble(personstat = list(fiona, barry, basil)) %>%
   mutate(height_mm = sapply(.$personstat, '[[', name="height_mm"))
people
# A tibble: 3 x 2
#           personstat height_mm
#               <list>     <dbl>
# 1 <S3: person_stats>      1750
# 2 <S3: person_stats>      1700
# 3 <S3: person_stats>      1800

有没有什么方法可以使用mutate() 来获得上述输出,而不必依赖sapply() 之类的东西?或者,实际上,从存储在 tibble 列中的类似列表的 S3 对象中提取命名值的任何其他明智方法?

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    rowwise可以处理这种情况:

    people <- tibble(personstat = list(fiona, barry, basil))
    
    people %>%
        rowwise() %>%
        mutate(height_mm = personstat$height_mm)
    # # A tibble: 3 × 2
    #           personstat height_mm
    # <list>     <dbl>
    # 1 <S3: person_stats>      1750
    # 2 <S3: person_stats>      1700
    # 3 <S3: person_stats>      1800
    
    people %>%
        rowwise() %>%
        mutate(height_inch = personstat$height_mm / 25.4)
    
    # # A tibble: 3 × 2
    #           personstat height_inch
    # <list>       <dbl>
    # 1 <S3: person_stats>    68.89764
    # 2 <S3: person_stats>    66.92913
    # 3 <S3: person_stats>    70.86614
    

    【讨论】:

    • 谢谢。我不知道rowwise(),但它看起来就像是这份工作。但是,按照您的建议,我遇到了一些错误。顶级版本有效,但带有height_inch = 计算的版本会引发错误。我正在使用 dplyr v0.5.0(CRAN 上的最新版本)。不幸的是,目前没有为我安装 GitHub 版本。也许您正在使用更新的/开发版本?
    • 错误信息是Error in mutate_impl(.data, dots) : object 'personstat' not found
    • @jamse,我在上面的代码中使用了 dplyr 0.6.0 预发行版(可从 github 获得)。我看到与 v0.5.0 相同的错误。这可能是 v0.5.0 的错误。我不确定。
    • 结束这个循环:使用新发布的版本(我认为,当它前往 CRAN 时,v0.6.0 变成了 v0.7.0),现在这是一种享受。非常感谢!
    • 此解决方案不再适用于 dplyr 1.0.0
    【解决方案2】:

    如果您想将其保存在 tidyverse 中,您可以在此处使用 purrr::map_dbl

    library(tidyverse)    
    people %>% mutate(height = map_dbl(personstat, "height_mm"))
    

    【讨论】:

    • 谢谢。我已经接受了另一个答案,因为它只是 dplyr 并且对我来说更清晰一些,但这也确实有效。对于那些想要使用map_dbl 版本的人,如果您想进行计算以及提取变量,您可以将其包含在 map_dbl 调用之外,如下所示:people %&gt;% mutate(height_inch = map_dbl(personstat, "height_mm") / 25.4)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-10
    • 2015-08-23
    相关资源
    最近更新 更多