tapply调用paste0时参数不同结果差异及转换方法问询
三元组转矩阵:tapply调用paste0的行为差异及解决方案
问题重现
给定含索引i、j和值v的三元组数据框,尝试构建矩阵并合并多值:
idx <- data.frame( i = c( 1, 2, 3, 4, 4, 1 ), j = c( 4, 3, 2, 1, 1, 4 ), v = c( 1, 1, 0, 0,"=", "=") )
调用带collapse参数的tapply得到合并后的矩阵:
a1 <- tapply(idx$v, idx[1:2], FUN = paste0, collapse="")
结果:
y x 1 2 3 4 1 NA NA NA "1=" 2 NA NA "1" NA 3 NA "0" NA NA 4 "0=" NA NA NA
省略collapse参数后,结果结构完全不同:
a2 <- tapply(idx$v, idx[1:2], FUN = paste0)
结果:
y x 1 2 3 4 1 NULL NULL NULL character,2 2 NULL NULL "1" NULL 3 NULL "0" NULL NULL 4 character,2 NULL NULL NULL
其中character,2是包含两个元素的列表:
# [[1]] # [1] "1" "="
行为差异的原因
paste0的参数逻辑:
- 未指定
collapse时,paste0对输入向量的每个元素独立执行字符转换,返回与输入长度相同的向量(比如含两个元素的组会返回长度为2的字符向量)。 - 指定
collapse时,paste0会将整个输入向量的所有元素拼接成单个字符串,返回长度为1的标量。
- 未指定
tapply的返回规则:
- 如果分组后
FUN返回长度为1的结果,tapply将其作为矩阵的标量元素存储; - 如果
FUN返回长度>1的向量,tapply会将该向量包裹成列表,作为矩阵对应位置的元素,这就是a2中出现character,2列表的原因。
- 如果分组后
将a2转换为a1的简便方法
可以用vapply遍历a2的每个元素,统一处理空值(NULL)和多值向量:
a1_from_a2 <- vapply(a2, function(x) { if (is.null(x)) NA_character_ else paste0(x, collapse = "") }, FUN.VALUE = character(1)) # 恢复矩阵的维度和名称 dim(a1_from_a2) <- dim(a2) dimnames(a1_from_a2) <- dimnames(a2)
执行后a1_from_a2的结果与a1完全一致。
关于NA的补充说明
paste0系列函数会自动将NA转换为字符串"NA",而非保留原NA值,即使指定collapse参数也是如此:
idx <- data.frame( i = c( 1, 2, 3, 4), j = c( 4, 3, 2, 1), v = c( 1, NA, 0, 0) ) a3 <- tapply(idx$v, idx[1:2], FUN = paste0, collapse="")
结果:
j i 1 2 3 4 1 NA NA NA "1" 2 NA NA "NA" NA 3 NA "0" NA NA 4 "0" NA NA NA
内容的提问来源于stack exchange,提问作者clp
相关产品推荐
相关产品推荐

