【问题标题】:Why isn't mapply working with transform as expected?为什么 mapply 不能按预期使用转换?
【发布时间】:2021-10-30 22:39:19
【问题描述】:

我使用dplyrtidyr 向问题发布了answer。基于this comment 我使用Map 来构建答案。

接下来我尝试使用base R 工具仅回答相同的问题,但这并没有按预期工作:

transform(
  df,
  Begin_New = Map(seq, Begin, End - 6000, list(by = 1000)) # or mapply(...)
)

导致错误:

错误(函数(...,row.names = NULL,check.rows = FALSE,check.names = TRUE,:参数暗示不同的行数:25、33、84、36、85、165

好吧,好吧。这似乎不起作用,但是为什么这个起作用?

df2 <- data.frame(id = 1:4, nested = c("a, b, f", "c, d", "e", "e, f"))
transform(df2, nested = strsplit(nested, ", "))

据我了解,Map(seq, Begin, End - 6000, list(by = 1000))strsplit(nested, ", ") 都返回包含向量的 list()。我错过了什么?

我阅读了这个问题Error in (function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE, : Arguments imply different number of rows: 1, 4, 5, 2,但我仍然不知道,为什么这两个示例的行为不同。

数据

df <- structure(list(ID = c("A01", "A01", "A01", "A01", "A01", "A01"
), Period = c("Baseline", "Run", "Recovery", "Baseline", "Run", 
"Recovery"), Begin = c(0, 30500, 68500, 2000, 45000, 135000), 
    End = c(30500, 68500, 158000, 43000, 135000, 305000)), row.names = c(NA, 
-6L), class = "data.frame")

【问题讨论】:

  • 仅供参考,within(df, { Begin_New = Map(...); }) 有效。不知道为什么。

标签: r list dataframe dictionary


【解决方案1】:

我认为它与Create a data.frame where a column is a list 有关。所以使用I禁止对象的解释/转换

transform(
  df,
  Begin_New = I(Map(seq, Begin, End - 6000, list(by = 1000)))
)

另一种方法是使用list2DF like。

transform(
  df,
  unusedName = list2DF(list(Begin_New = Map(seq, Begin, End - 6000,
                 list(by = 1000))))
)

正如@r2evans 已经指出的那样。在第一种情况下,您创建一个新列,在第二种情况下,您覆盖现有的列。

【讨论】:

  • 这很有趣!有和没有I(.)之间的唯一区别(在transform.data.frame内)是Begin_New上的class = "AsIs"list
  • 但有道理,比较 data.frame(a=1:3, b = list(1, 2, 3))data.frame(a=1:3, b = I(list(1 ,2, 3)))
  • ?data.frame: 如果将列表或数据框或矩阵传递给“data.frame”,就好像每个组件或列都作为单独的参数传递(受 ' 保护的矩阵除外我')。
  • 轻松解决我仍然不完全理解的问题。谢谢。
【解决方案2】:

错误似乎在transform.data.frame 以及它是如何(重新)分配列的。

transform.data.frame
# function (`_data`, ...) 
# {
#     e <- eval(substitute(list(...)), `_data`, parent.frame())
#     tags <- names(e)
#     inx <- match(tags, names(`_data`))
#     matched <- !is.na(inx)
#     if (any(matched)) {
#         `_data`[inx[matched]] <- e[matched]
#         `_data` <- data.frame(`_data`)
#     }
#     if (!all(matched)) 
#         do.call("data.frame", c(list(`_data`), e[!matched]))
#     else `_data`
# }
# <bytecode: 0x000000000a34e4b0>
# <environment: namespace:base>

具体来说,如果any(matched) 则使用

`_data`[inx[matched]] <- e[matched]

有效。在您的df2 示例中就是这种情况,因为您重新分配了现有变量nested。但是,如果您选择分配给不存在的变量,它也会失败:

transform(df2, nested2 = strsplit(nested, ", "))
# Error in (function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE,  : 
#   arguments imply differing number of rows: 3, 2, 1

如果该列不存在(如原始df 中的情况),则

do.call("data.frame", c(list(`_data`), e[!matched]))

失败。

如果我们预先分配df$Begin_New,它会起作用。

df$Begin_New <- NA
str(transform(
  df,
  Begin_New = Map(seq, Begin, End - 6000, by = 1000) # or mapply(...)
))
# 'data.frame': 6 obs. of  5 variables:
#  $ ID       : chr  "A01" "A01" "A01" "A01" ...
#  $ Period   : chr  "Baseline" "Run" "Recovery" "Baseline" ...
#  $ Begin    : num  0 30500 68500 2000 45000 135000
#  $ End      : num  30500 68500 158000 43000 135000 305000
#  $ Begin_New:List of 6
#   ..$ : num  0 1000 2000 3000 4000 5000 6000 7000 8000 9000 ...
#   ..$ : num  30500 31500 32500 33500 34500 35500 36500 37500 38500 39500 ...
#   ..$ : num  68500 69500 70500 71500 72500 73500 74500 75500 76500 77500 ...
#   ..$ : num  2000 3000 4000 5000 6000 7000 8000 9000 10000 11000 ...
#   ..$ : num  45000 46000 47000 48000 49000 50000 51000 52000 53000 54000 ...
#   ..$ : num  135000 136000 137000 138000 139000 140000 141000 142000 143000 144000 ...

也许这是transform.data.frame 中的一个错误,仅由于列的(丢弃)预先存在而出现不一致的行为似乎很奇怪。如果我们将新变量赋值更改为这样的:

transform2 <- function (`_data`, ...) {
    e <- eval(substitute(list(...)), `_data`, parent.frame())
    tags <- names(e)
    inx <- match(tags, names(`_data`))
    matched <- !is.na(inx)
    if (any(matched)) {
        `_data`[inx[matched]] <- e[matched]
        `_data` <- data.frame(`_data`)
    }
    if (!all(matched))  {
        `_data`[ncol(`_data`) + seq_len(sum(!matched))] <- e[!matched]
        `_data` <- data.frame(`_data`)
    }
    `_data`
}

然后就可以了。 (我还没有测试transform.data.frame 应该处理的所有其他内容,但也许这应该是对 R-devel 的错误报告/补丁请求。)

【讨论】:

  • 来自文档:“如果某些值不是适当长度的向量,那么你应该得到任何东西!”
  • 那是那个文档中的一个有趣的注释,是的......它似乎是从列表列被接受之前的时间开始的。 (它们以前在基础 R 中是“可接受的”吗?我知道它们通常工作,但是 ...
  • 感谢@r2evans 的启发性回答。今天学了点儿新东西。 :-)
  • 这就是我们经常自我提升的方式:学习将一种方法转换为其他方法,并观察事物如何崩溃/崩溃。
猜你喜欢
  • 2021-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-03
  • 2021-05-30
  • 2020-03-05
  • 1970-01-01
相关资源
最近更新 更多