You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

含NA的子集参数下tapply调用length()为何返回不同结果?

理解R中tapplylength()处理NA分组的行为

我完全懂你这个困惑——在R里用tapply处理带NA的分组变量时,length()的表现确实容易让人摸不着头脑,但这背后是R的设计逻辑,咱们一步步理清楚:

先重现你遇到的场景

假设我们有一组测试数据和带NA的分组变量:

x <- 1:5
group <- c("A", "B", NA, "A", "B")

1. 符合“直观预期”的简短版本

我猜你说的“简短版本”是指先手动过滤掉分组中的NA,再调用tapply

# 过滤掉分组变量中的NA后调用tapply
tapply(x[!is.na(group)], group[!is.na(group)], length)
#> A B 
#> 2 2

这个结果符合直观:我们只统计了有效分组"A"和"B"的元素个数,NA分组被直接排除了。

2. 符合设计模式的直接调用

而如果直接传入带NA的分组变量调用tapply,结果就会包含NA分组:

# 直接使用带NA的分组变量
tapply(x, group, length)
#>  A  B NA 
#>  2  2  1

这里NA被当作了一个独立的分组,统计出该组有1个元素——这看起来反直觉,但确实是R的预期设计行为

为什么会有这种差异?

关于tapply的分组逻辑

tapply的默认行为是:分组变量中的每一个唯一值(包括NA)都会被当作一个独立分组。这和table()函数的逻辑一致——table(group)也会把NA算成一个单独的类别。

关于length()的行为

length()函数的作用是统计向量的元素个数,它不会忽略NA值(因为NA本身就是一个合法的元素)。所以当tapply把NA对应的元素单独分成一组后,length()自然会统计出该组的元素数量,哪怕这个组里只有一个NA对应的元素。

你换成其他函数比如sum测试,会发现逻辑完全一致:

tapply(x, group, sum)
#>  A  B NA 
#>  3  7  3

NA分组对应的元素是x[3] = 3,所以sum结果就是3,和length的处理逻辑完全匹配。

怎么让tapply自动忽略NA分组?

如果你想让tapply直接忽略NA分组,不用手动过滤,可以结合droplevels()na.omit()处理分组变量:

tapply(x, droplevels(na.omit(group)), length)
#> A B 
#> 2 2

或者用dplyr包的分组统计,它默认会忽略NA分组:

library(dplyr)
data.frame(x, group) %>%
  group_by(group) %>%
  summarise(count = n())
#> # A tibble: 2 × 2
#>   group count
#>   <chr> <int>
#> 1 A         2
#> 2 B         2

内容的提问来源于stack exchange,提问作者uhClem

火山引擎 最新活动