【问题标题】:Selecting random rows by category from a data frame?从数据框中按类别选择随机行?
【发布时间】:2013-01-19 01:34:56
【问题描述】:

我有一个数据框如下:

Category Name Value

我将如何选择每个类别 5 个随机名称?使用 sample 返回随机行,使用所有行作为可能的候选。但是,我想指定每个类别的随机行数。有什么建议吗?

更新:我愿意使用ddply

【问题讨论】:

    标签: r dataframe


    【解决方案1】:

    没有测试用例的最佳猜测:

      do.call( rbind, lapply( split(dfrm, df$cat) ,
                             function(df) df[sample(nrow(df), 5) , ] )
              )
    

    用乔纳森的数据测试:

    > do.call( rbind, lapply( split(df, df$Category) ,
    +                          function(df) df[sample(nrow(df), 5) , ] )
    +           )
    
          Category Name      Value   
    1.8          1    8 -0.2496109   #  useful side-effect of labeling source group
    1.15         1   15 -0.4037368
    1.17         1   17 -0.4223724
    1.12         1   12 -0.9359026
    1.18         1   18  0.3741184
    2.37         2   37  0.3033610
    2.34         2   34 -0.4517738
    2.36         2   36 -0.7695923
    snipped remainder
    

    【讨论】:

    • 该代码假定每个类别至少有 5 行,如果不是这样,则会出错。根据您的规格,您可能希望保持原样,将replace = TRUE 添加到sample 调用中,或将5 替换为min(5, nrow(df))
    • 是的,它会的。只是向公众公开他们的要求。你的建议是一个明确的改进。
    • @DWin +1,但是by = lapply + split,不是吗?
    • by.data.frame的代码,有点不一样。使用tapply,但如果您查看tapply 的代码,就会发现lapply(split(.,.)) 被调用。您仍然需要rbind 来完成包装,所以我不确定使用by 是否有优势。我发现我说明的方法更容易理解。
    【解决方案2】:

    如果您希望每个类别的商品数量相同,这很简单:

    df[unlist(tapply(1:nrow(df),df$Category,function(x) sample(x,3))),]
    

    例如,我生成df如下:

    df <- data.frame(Category=rep(1:5,each=20),Name=1:100,Value=rnorm(100))
    

    然后我从我的代码中得到以下信息:

    > df[unlist(tapply(1:nrow(df),df$Category,function(x) sample(x,3))),]
        Category Name       Value
    5          1    5  0.25151044
    20         1   20  1.52486482
    18         1   18  0.69313462
    30         2   30  0.73444185
    27         2   27  0.24000427
    39         2   39 -0.10108203
    46         3   46 -0.37200574
    49         3   49 -1.84920469
    43         3   43  0.35976388
    68         4   68  0.57879516
    76         4   76 -0.11049302
    64         4   64 -0.13471303
    100        5  100  0.95979408
    95         5   95 -0.01928741
    99         5   99  0.85725242
    

    如果您希望每个类别的行数不同,那将更加复杂。

    【讨论】:

    • 我认为这将取决于具有默认行名的 df 对象。
    • @DWin 为什么要依赖行名?我的代码中的任何地方都没有引用行名。
    • 我错了。用df &lt;- data.frame(Category=rep(1:5,each=20),Name=1:100,Value=rnorm(100), row.names=101:200) 测试。正在考虑它需要与行名进行字符匹配。
    • 对于某个类别中只有一个实例的情况,我们应该像这样替换上面的代码! df[unlist(tapply(1:nrow(df),df$Category,function(x) {if (length(x)&gt;1) {sample(x,min(length(x),3))}else {x}})),]
    【解决方案3】:

    过去,我为"sampling" package 中的一些函数编写了一个小包装器。

    函数如下:

    strata.sampling <- function(data, group, size, method = NULL) {
      #  USE: 
      #   * Specify a data.frame and grouping variable.
      #   * Decide on your sample size. For a sample proportional to the 
      #     population, enter "size" as a decimal. For an equal number of 
      #     samples from each group, enter "size" as a whole number. For
      #     a specific number of samples from each group, enter the numbers
      #     required as a vector.
    
      require(sampling)
      if (is.null(method)) method <- "srswor"
      if (!method %in% c("srswor", "srswr")) 
        stop('method must be "srswor" or "srswr"')
      temp <- data[order(data[[group]]), ]
      ifelse(length(size) > 1,
             size <- size, 
             ifelse(size < 1,
                    size <- round(table(temp[group]) * size),
                    size <- rep(size, times=length(table(temp[group])))))
      strat = strata(temp, stratanames = names(temp[group]), 
                     size = size, method = method)
      getdata(temp, strat)
    }
    

    你可以这样使用它:

    # Sample data --- Note each category has a different number of observations
    df <- data.frame(Category = rep(1:5, times = c(40, 15, 7, 13, 25)), 
                     Name = 1:100, Value = rnorm(100))
    
    # Sample 5 from each "Category" group
    strata.sampling(df, "Category", 5)
    # Sample 2 from the first category, 3 from the next, and so on
    strata.sampling(df, "Category", c(2, 3, 4, 5, 2))
    # Sample 15% from each group
    strata.sampling(df, "Category", .15)
    

    还有一个我写的增强功能here。该函数可以优雅地处理一组观察值可能少于指定样本数的情况,并且还允许您按多个变量进行分层。几个例子见the docs

    【讨论】:

      猜你喜欢
      • 2020-02-19
      • 1970-01-01
      • 2013-04-02
      • 1970-01-01
      • 1970-01-01
      • 2022-01-09
      • 2015-05-21
      • 1970-01-01
      相关资源
      最近更新 更多