我发布了一个使用dplyr
和tidyr
的问题的答案。基于这个评论,我使用Map
来构建答案。
接下来,我尝试使用base R
工具来回答同样的问题,但这并没有像预期的那样工作:
transform(
df,
Begin_New = Map(seq, Begin, End - 6000, list(by = 1000)) # or mapply(...)
)
导致错误:
函数(…)出错, row.names = NULL,检查。参数表示不同的行数: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()
。我错过了什么?
我读到这个问题错误在(函数(…), row.names = NULL,检查。rows = FALSE, check.names = TRUE,:参数意味着不同的行数: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")
我认为这是有关创建一个data.frame的列是一个列表。所以使用I
来抑制对象的解释/转换。
transform(
df,
Begin_New = I(Map(seq, Begin, End - 6000, list(by = 1000)))
)
另一种方法是使用list2DF
,如。
transform(
df,
unusedName = list2DF(list(Begin_New = Map(seq, Begin, End - 6000,
list(by = 1000))))
)
正如@r2evans已经指出的那样。在第一种情况下,您将创建一个新列,在第二种情况下,您将覆盖现有的列。
错误出现在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
中的一个bug,仅仅由于(丢弃的)列的预先存在而产生不一致的行为似乎很奇怪。如果我们把new-variable的赋值改成这样:
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
应该处理的其他一切,但也许这应该是一个bug报告/补丁请求R-devel。)