R:使用tapply

时间:2015-11-21 00:30:06

标签: r split sample lapply

我有一个大数据集我试图从中采样行。每行都有一个系列ID,每个系列ID可能有一行或多行。我想通过为每个系列ID随机抽样一行来解析数据集。我试图通过同时使用tapply()split() + lapply()函数来实现这一目标,但无济于事。下面是重现我的问题的代码 - 因子级别和数据条目的大小和范围反映了我正在使用的数据集。

set.seed(63)
f1 <- factor(c(rep(30000:32000, times=1), 
               rep(30500:31700, times = 2), 
               rep(30900:31900, times = 3)))
f2 <- factor(rep(sample(1:7, replace = TRUE), times = length(f1)/7))
x1 <- round(matrix(rnorm(length(f1)*300), nrow = length(f1), ncol = 300),3)
df <- data.frame(f1, f2, x1)

接下来,我使用tapplyf1为每个因子抽取一行,然后检查重复。 (f2是对观察的另一个方面进行索引的次要因素,但是[希望]在这里无关紧要;我只包括它以完全披露我的数据集的结构。)

s1 <- tapply(1:nrow(df), df$f1, sample, size=1)
any(duplicated(s1))

使用duplicated的第二行代码的输出是TRUE,这意味着有重复。难过,我试过split看看是不是问题。

df.split <- split(1:nrow(df), df$f1)
any(duplicated(df.split))

此处duplicated的输出为FALSE,因此问题不是split。然后我将输出df.splitlapplysample一起用于查看问题是否与tapply一致。

df.unique <- unlist(lapply(df.split, sample, size = 1, replace = FALSE, 
                           prob = NULL))
any(duplicated(df.unique))

在第一行中,我从df.split的每个元素中抽取一个值,该元素输出一个列表,然后我使用unlist转换为一个向量。此处duplicated的输出也是TRUE

samplelapply中的某个地方有一些时髦的东西(因为tapply只是调用lapply)。我不确定如何解决这个问题(我搜索了SO和谷歌并发现与我的问题无关),所以任何帮助都将不胜感激!

编辑:我希望有人能告诉我为什么使用tapplylapply的上述代码无法正常运行。 Arthur提供了一个很好的答案,我也为sample编了一个循环。我想知道为什么上面的代码行为不端。

1 个答案:

答案 0 :(得分:2)

我会这样做:

library(data.table)
data.table(df)[,.SD[sample(.N,1)],by='f1']

...但实际上,如果您只想要一个索引而不是实际的子集表,那么使用tapply的原始方法会更快;但是,您必须注意sample(n) 1:n实际上length(n)==1 ?sample。见 s1 <- tapply(1:nrow(df), list(df$f1), function(v) v[sample(1:length(v), 1)])` is error prooff 。这个版本是防错的:

Sub Cmemo()
    With Worksheets("Sheet4")    '<~~ SET THIS WORKSHEET REFERENCE PROPERLY!!
        If .AutoFilterMode Then .AutoFilterMode = False
        With .Cells(1, 1).CurrentRegion
            .AutoFilter Field:=1, Criteria1:="*RR*"
            .AutoFilter Field:=3, Criteria1:="<>memo"
            .AutoFilter Field:=5, Criteria1:="<>Air", _
                        Operator:=xlAnd, Criteria2:="<>Printed"
            If CBool(Application.Subtotal(103, .Offset(1, 0).Cells)) Then
                With .Resize(.Rows.Count - 1, 1).Offset(1, 11)
                    .SpecialCells(xlCellTypeVisible) = 0
                End With
            End If
            .AutoFilter Field:=5, Criteria1:="Air", _
                        Operator:=xlOr, Criteria2:="Printed"
            If CBool(Application.Subtotal(103, .Offset(1, 0).Cells)) Then
                With .Resize(.Rows.Count - 1, 1).Offset(1, 11)
                    .SpecialCells(xlCellTypeVisible).FormulaR1C1 = "=rc[-4]/10"
                End With
            End If
        End With
        If .AutoFilterMode Then .AutoFilterMode = False
    End With
End Sub