tapply 为因子索引的每个级别返回 NA 或坚持对象和索引的长度不同

Posted

技术标签:

【中文标题】tapply 为因子索引的每个级别返回 NA 或坚持对象和索引的长度不同【英文标题】:tapply returns NA for every level of the factor index or insists the object and index are different lengths 【发布时间】:2019-11-15 14:31:36 【问题描述】:

我正在尝试使用 tapply 来获取每天捕获的海龟的平均重量。对于我尝试过的每种方法,tapply 为每个日期值(类:POSIXct)返回 NA

我试过了: 在重量列和日期列上调用 tapply -> 参数长度不同错误

在我的数据框的权重列中删除具有 NA 值的记录,然后在权重列和日期列上调用 tapply。 -> 参数长度不同错误

在权重列的 na.omit 调用和权重列的 na.omit 调用索引的日期列上调用 tapply -> 参数长度不同错误

在权重列的 na.omit 调用和权重列的 na.omit 调用索引的因子强制日期列上调用 tapply -> 为因子强制日期列的每个级别返回 NA

原始数据帧的头部

> head(stinkpotData)
       Date     DateCt  Species Turtle.ID ID.Code             Location Recapture Weight.g C.Length.mm
1  6/1/2001 2001-06-01 Stinkpot         1       1   keck lab dock site         0      190          95
2  6/1/2001 2001-06-01 Stinkpot         2      10        Right of dock         0      200         100
3  8/9/2001 2001-08-09 Stinkpot         2      10 #4 Deep Right of lab         1      175         104
4 8/27/2001 2001-08-27 Stinkpot         2      10 #4 Deep Right of lab         1      175         105
5  6/1/2001 2001-06-01 Stinkpot         3      11        Right of dock         0      200         109
6 10/3/2001 2001-10-03 Stinkpot         3      11 #4 Deep Right of lab         1      205         109
  C.Width.mm Female.1.Male.2 Rotation                                  Marks
1         70            <NA>     <NA>                                   <NA>
2         72            <NA>     <NA>                                   <NA>
3         72               2     <NA>                                   Male
4         71               2     <NA>    male, 1 small leech Right front leg
5         74            <NA>     <NA>                          algae covered
6         76               2     <NA> male, 1 lg & 1 sm leech right rear leg

原始数据帧的头部,其中省略了 NA 权重的记录(检查了 NA 是否实际上被省略了)

> head(noNAWeightsDf)
       Date     DateCt  Species Turtle.ID ID.Code             Location Recapture Weight.g C.Length.mm
1  6/1/2001 2001-06-01 Stinkpot         1       1   keck lab dock site         0      190          95
2  6/1/2001 2001-06-01 Stinkpot         2      10        Right of dock         0      200         100
3  8/9/2001 2001-08-09 Stinkpot         2      10 #4 Deep Right of lab         1      175         104
4 8/27/2001 2001-08-27 Stinkpot         2      10 #4 Deep Right of lab         1      175         105
5  6/1/2001 2001-06-01 Stinkpot         3      11        Right of dock         0      200         109
6 10/3/2001 2001-10-03 Stinkpot         3      11 #4 Deep Right of lab         1      205         109
  C.Width.mm Female.1.Male.2 Rotation                                  Marks
1         70            <NA>     <NA>                                   <NA>
2         72            <NA>     <NA>                                   <NA>
3         72               2     <NA>                                   Male
4         71               2     <NA>    male, 1 small leech Right front leg
5         74            <NA>     <NA>                          algae covered
6         76               2     <NA> male, 1 lg & 1 sm leech right rear leg

在原始数据框中的列上调用 tapply

> tapply(stinkpotData$Weight.g, stinkpotData$DateCt, FUN = mean)
Error in tapply(stinkpotData$Weight.g, stinkpotData$DateCt, FUN = mean) : 
  arguments must have same length

在 noNA 数据框中的列上调用 tapply

>tapply(noNAWeightsDf$Weight.g, noNAWeightsDf$DateCt, FUN = mean)
Error in tapply(noNAWeightsDf$Weight.g, noNAWeightsDf$DateCt, FUN = mean) : 
  arguments must have same length

在权重列和日期列的na.omit调用上调用tapply

> tapply(na.omit(stinkpotData$Weight.g), stinkpotData$DateCt[!is.na(stinkpotData$Weight.g)], FUN = mean)
Error in tapply(na.omit(stinkpotData$Weight.g), stinkpotData$DateCt[!is.na(stinkpotData$Weight.g)],  : 
  arguments must have same length

在权重列和因子的na.omit调用上调用tapply-

coerced date column indexed by the na.omit call of the weight column 
tapply(na.omit(stinkpotData$Weight.g), as.factor(stinkpotData$DateCt[!is.na(stinkpotData$Weight.g)]), FUN = mean)
2001-01-07 2001-06-01 2001-06-04 2001-06-06 2001-06-07 2001-06-11 2001-06-12 2001-06-15 2001-06-19 
        NA         NA         NA         NA         NA         NA         NA         NA         NA 
2001-06-20 2001-06-25 2001-06-27 2001-06-29 2001-07-03 2001-07-09 2001-07-11 2001-07-13 2001-07-16 
        NA         NA         NA         NA         NA         NA         NA         NA         NA ................etc

There were 50 or more warnings (use warnings() to see the first 50)

在出现上述错误后调用 warnings():

> warnings()
Warning messages:
1: In mean.default(X[[i]], ...) :
  argument is not numeric or logical: returning NA
2: In mean.default(X[[i]], ...) :
  argument is not numeric or logical: returning NA
3: In mean.default(X[[i]], ...) :
  argument is not numeric or logical: returning NA
.......................etc

编辑:

split(na.omit(stinkpotData$Weight.g), as.factor(stinkpotData$DateCt[!is.na(stinkpotData$Weight.g)])) 列出每个日期海龟的个体重量。验证它是模式列表。它的元素是模式数字,类因子。 lapply 在拆分列表上使用 FUN=mean 仍然为每个日期级别返回 NA。可以获得强制转换为向量的拆分列表的各个元素的方法,但不是我需要的。

编辑 2: 终于得到了我想要的结果,但是到达那里的步骤似乎过于复杂,我仍然不明白为什么使用 tapply 不起作用。我必须像第一次编辑一样调用 split,然后使用 lapply 将结果列表的每个元素强制为数字类(最初作为类因子返回),然后使用 lapply 对每个元素调用 mean:

weightsDateList = split(na.omit(stinkpotData$Weight.g), as.factor(stinkpotData$DateCt[!is.na(stinkpotData$Weight.g)]))
weightsDateList = lapply(weightsDateList, FUN = as.numeric)
weightsDateList = lapply(weightsDateList, FUN = mean)

编辑 3: 我现在意识到我从 EDIT 2 中的解决方案中得到的结果并调用 tapply( 严重低估了手段,所以仍然丢失。

编辑 4: 意识到将权重转换为类数值会返回权重作为因子时的级别数,这解释了均值的严重低估。

我希望 tapply 调用返回每个日期以及海龟重量及其在这些日期捕获的海龟的平均重量。谢谢,如果我遗漏了一些简单的东西,我深表歉意。

【问题讨论】:

你试过aggregate(Weight.g ~ DateCt, data = stinkpotData, mean) 除非你有理由,否则我建议不要使用tapply。 data.tabledplyr 都提供了更简单的分组功能。我非常偏爱 data.table,但我建议同时查看并查看适合您的内容 我不认为tapply 有问题,但我很偏爱base R。它的许多方法都提供分组功能:tapplybysplitaveaggregate 等等。我建议检查一下,看看什么适合你。 dput 复制此错误的实际原始数据框的几行。你检查了DateCt 的 NA 吗? @heds1 aggregate(Weight.g ~ DateCt, data = stinkpotData, mean) 给出了无效类型错误,因为 DateCt 似乎是一个列表。我将它强制为一个因子,然后称为聚合,但它只是再次为每个级别返回 NA 【参考方案1】:

通常,要使用tapply,您必须注意以下关于其参数的规则:

第一个参数必须是或可转换为逻辑、整数或数字。此处不能使用因子、字符或其他类型。

Second 参数必须是或可转换为一个因子,该因子可以是任何基本数据类型,但更复杂的类型除外。如果使用 list() 则包括多个分组,其中 tapply 然后返回一个矩阵。

因为这个参数只考虑因素,所以使用 as.factor() 进行转换是多余的,tapply 可能已经在幕后做了。 第三个参数必须是一个函数,它为按组(即第二个参数)切片的每个输入(即第一个参数)返回一个原子数值。 长度:第一个和第二个参数必须是相同的长度,如果两者都从数据帧派生,因为数据帧定义为 list 类型的 class 对象,包含相等长度的原子向量。 由于这条规则,请避免对第一个或第二个参数运行 不同 操作,因为它可能导致 不同 长度。相反,在调用 tapply 之前,要么对两个向量运行相同的操作,要么对整个数据帧运行更好的操作: 由于每个NA 的长度都保持为1(与NULL 不同),因此它在tapply 中的存在并不重要。但是,子函数可能会遇到 tapply 在上游引发的 NA 问题。

具体来说,您的问题与原始类型有关:Weight.g 的因子类型和 DateCtPOSIXlt 类型。考虑将这些类型转换为遵守tapply

但不要直接将这些原始类型转换为factor,因为它的底层数字或因子级别数会导致不良结果。对于数字转换,首先转换为character。对于 POSIXlt 转换为 Datecharacter。下面以OP的前十行dput与其他分组方式进行演示。

数据 (只有两个相关的列)

stinkpotDataDeparsed <- structure(list(Weight.g = structure(c(15L, 13L, 20L, 16L, 15L, 
12L, NA, 12L, 15L, 20L, 26L), .Label = c("100", "105", "106", 
"107", "110", "115", "1150", "120", "125", "126", "128", "130", 
"135", "138", "140", "145", "150", "155", "159", "160", "165", 
"168", "170", "175", "180", "185", "187", "190", "195", "20", 
"200", "205", "210", "215", "220", "225", "230", "235", "245", 
"250", "40", "45", "50", "55", "60", "65", "70", "75", "80", 
"85", "90", "95", "oops!"), class = "factor"), DateCt = structure(list(
    sec = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), min = c(0L, 0L, 
    0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L), hour = c(0L, 0L, 0L, 
    0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L), mday = c(20L, 30L, 8L, 29L, 
    23L, 26L, 12L, 17L, 29L, 13L, 4L), mon = c(8L, 8L, 10L, 10L, 
    5L, 5L, 6L, 6L, 6L, 5L, 5L), year = c(101L, 101L, 101L, 101L, 
    102L, 102L, 102L, 102L, 102L, 103L, 101L), wday = c(4L, 0L, 
    4L, 4L, 0L, 3L, 5L, 3L, 1L, 5L, 1L), yday = c(262L, 272L, 
    311L, 332L, 173L, 176L, 192L, 197L, 209L, 163L, 154L), isdst = c(0L, 
    0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L), zone = c("EST", 
    "EST", "EST", "EST", "EST", "EST", "EST", "EST", "EST", "EST", 
    "EST"), gmtoff = c(NA_integer_, NA_integer_, NA_integer_, 
    NA_integer_, NA_integer_, NA_integer_, NA_integer_, NA_integer_, 
    NA_integer_, NA_integer_, NA_integer_)), .Names = c("sec", 
"min", "hour", "mday", "mon", "year", "wday", "yday", "isdst", 
"zone", "gmtoff"), class = c("POSIXlt", "POSIXt"), tzone = c("EST", 
"EST", "   "))), .Names = c("Weight.g", "DateCt"), row.names = 60:70, class = "data.frame")

清洁

# REMOVE NAs FROM DATA FRAME TO RUN ON ALL COLUMNS BUT DOES NOT MATTER W/ tapply
stinkpotDataDeparsed <- stinkpotDataDeparsed[!is.na(stinkpotDataDeparsed$Weight.g),]

# CAST FACTOR TYPE TO NUMERIC    
stinkpotDataDeparsed$Weight.g <- as.numeric(as.character(stinkpotDataDeparsed$Weight.g))

# CAST POISXlt TO DATE OR CHARACTER FOR FACTOR-ABILITY
stinkpotDataDeparsed$DateCt <- as.Date(stinkpotDataDeparsed$DateCt)
# stinkpotDataDeparsed$DateCt <- as.character(stinkpotDataDeparsed$DateCt)

点击 (返回一个向量)

with(stinkpotDataDeparsed, tapply(Weight.g, DateCt, mean))     

# 2001-06-04 2001-09-20 2001-09-30 2001-11-08 2001-11-29 2002-06-23 2002-06-26 2002-07-17 2002-07-29 2003-06-13 
#        185        140        135        160        145        140        130        130        140        160 

聚合 (返回一个数据框)

aggregate(Weight.g ~ DateCt, data = stinkpotDataDeparsed, mean)

#        DateCt Weight.g
# 1  2001-06-04      185
# 2  2001-09-20      140
# 3  2001-09-30      135
# 4  2001-11-08      160
# 5  2001-11-29      145
# 6  2002-06-23      140
# 7  2002-06-26      130
# 8  2002-07-17      130
# 9  2002-07-29      140
# 10 2003-06-13      160

Ave (返回与输入长度相同的向量,因此可以分配一个数据框列)

stinkpotDataDeparsed$Wgt.Mean <- with(stinkpotDataDeparsed, ave(Weight.g, DateCt, FUN=mean))
stinkpotDataDeparsed

#    Weight.g     DateCt Wgt.Mean
# 60      140 2001-09-20      140
# 61      135 2001-09-30      135
# 62      160 2001-11-08      160
# 63      145 2001-11-29      145
# 64      140 2002-06-23      140
# 65      130 2002-06-26      130
# 67      130 2002-07-17      130
# 68      140 2002-07-29      140
# 69      160 2003-06-13      160
# 70      185 2001-06-04      185

(面向对象的包装器到tapply,返回一个列表)

by(stinkpotDataDeparsed, stinkpotDataDeparsed$DateCt, FUN=function(sub) mean(sub$Weight.g))

# stinkpotDataDeparsed$DateCt: 2001-06-04
# [1] 185
# ------------------------------------------------------------ 
# stinkpotDataDeparsed$DateCt: 2001-09-20
# [1] 140
# ------------------------------------------------------------ 
# stinkpotDataDeparsed$DateCt: 2001-09-30
# [1] 135
# ------------------------------------------------------------ 
# stinkpotDataDeparsed$DateCt: 2001-11-08
# [1] 160
# ------------------------------------------------------------ 
# stinkpotDataDeparsed$DateCt: 2001-11-29
# [1] 145
# ------------------------------------------------------------ 
# stinkpotDataDeparsed$DateCt: 2002-06-23
# [1] 140
# ------------------------------------------------------------ 
# stinkpotDataDeparsed$DateCt: 2002-06-26
# [1] 130
# ------------------------------------------------------------ 
# stinkpotDataDeparsed$DateCt: 2002-07-17
# [1] 130
# ------------------------------------------------------------ 
# stinkpotDataDeparsed$DateCt: 2002-07-29
# [1] 140
# ------------------------------------------------------------ 
# stinkpotDataDeparsed$DateCt: 2003-06-13
# [1] 160

Rextester Demo

【讨论】:

以上是关于tapply 为因子索引的每个级别返回 NA 或坚持对象和索引的长度不同的主要内容,如果未能解决你的问题,请参考以下文章

通过 sapply 维护 tapply 索引

r语言apply函数与tapply函数有啥区别

将具有看不见的字符串值的新记录附加到数据框时,看不见的因子级别会导致警告并导致 NA

一个因子的 addNA 之后的子集数据帧

<NA> 和 NA 有啥区别?

R语言怎么输入一个自定义矩阵?求R大神交流