【问题标题】:New column with random boolean values while controlling the ratio of TRUE/FALSE per category具有随机布尔值的新列,同时控制每个类别的 TRUE/FALSE 比率
【发布时间】:2021-11-10 10:21:45
【问题描述】:

在 R 中,我有一个像这样的数据集:

df <- data.frame(
  ID = c(1:30),
  x1 = seq(0, 1, length.out = 30),
  x2 = seq(100, 3000, length.out = 30),
  category = gl(3, 10, labels = c("NEGATIVE", "NEUTRAL", "POSITIVE"))
)

现在我想添加一个带有随机布尔值的新列,但是在每个类别中,TRUE 和 FALSE 值的比例应该相同(即随机化过程应该生成相同计数的真假值,在上面数据框 3 个类别中的每个类别中的 5 个 TRUE 和 5 个 FALSE)。如何做到这一点?

【问题讨论】:

  • 您可以按照所需的比例非随机分配布尔值,然后遍历类别并使用 sample() 置换值。

标签: r random


【解决方案1】:

您可以在不替换的情况下对“TRUE”和“FALSE”值的向量进行采样,这样您的数据框中就有一个随机且平衡的列。

sample(rep(c("TRUE","FALSE"),each=5),10,replace=FALSE)

【讨论】:

    【解决方案2】:

    基于 Yacine Hajji 的回答:

    addRandomBool <- function(df, p){
      
      n <- ceiling(nrow(df) * p)
      df$bool <- sample(rep(c("TRUE","FALSE"), times = c(n, nrow(df) - n)))
      
      df
    }
    
    Reduce(rbind, lapply(split(df, df$category), addRandomBool, p = 0.5))
    

    其中参数p决定TRUE的比例。

    【讨论】:

      【解决方案3】:

      这将在每个组中从 5 TRUE 和 5 FALSE 的向量中进行采样,无需替换。它将假定每组始终有 10 条记录。

      library(dplyr)
      library(tidyr)
      
      df <- data.frame(
        ID = c(1:30),
        x1 = seq(0, 1, length.out = 30),
        x2 = seq(100, 3000, length.out = 30),
        category = gl(3, 10, labels = c("NEGATIVE", "NEUTRAL", "POSITIVE"))
      )
      
      set.seed(pi)
      
      df %>% 
        group_by(category) %>% 
        nest() %>% 
        mutate(data = lapply(data, 
                             function(df){ # Function to saple and assign the new_col
                               df$new_col <- sample(rep(c(FALSE, TRUE), 
                                                        each = 5), 
                                                    size = 10, 
                                                    replace = FALSE)
                               df
                             })) %>% 
        unnest(cols = "data")
      

      下一个示例更笼统一些,但仍假设(大约)TRUE 和 FALSE 在组内均匀分布。但它可以容纳可变的组大小,甚至是奇数记录的组(但对于奇数记录会偏爱 FALSE)

      library(dplyr)
      library(tidyr)
      
      df <- data.frame(
        ID = c(1:30),
        x1 = seq(0, 1, length.out = 30),
        x2 = seq(100, 3000, length.out = 30),
        category = gl(3, 10, labels = c("NEGATIVE", "NEUTRAL", "POSITIVE"))
      )
      
      set.seed(pi)
      
      df %>% 
        group_by(category) %>% 
        nest() %>% 
        mutate(data = lapply(data, 
                             function(df){
                               df$new_col <- sample(rep(c(FALSE, TRUE), 
                                                        length.out = nrow(df)), 
                                                    size = nrow(df), 
                                                    replace = FALSE)
                               df
                             })) %>% 
        unnest(cols = "data")
      

      维护列顺序

      保持列顺序的几个选项:

      首先,您可以在执行group_by - nest 之前保存列顺序,然后在完成后使用select 设置顺序。

      set.seed(pi)
      
      orig_col <- names(df)  # original column order
      
      df %>% 
        group_by(category) %>% 
        nest() %>% 
        mutate(data = lapply(data, 
                             function(df){
                               df$new_col <- sample(rep(c(FALSE, TRUE), 
                                                        length.out = nrow(df)), 
                                                    size = nrow(df), 
                                                    replace = FALSE)
                               df
                             })) %>% 
        unnest(cols = "data") %>% 
        select_at(c(orig_col, "new_col"))   # Restore the column order
      

      或者您可以使用base R 解决方案,该解决方案首先不会更改列顺序

      df <- split(df, df["category"])
      df <- lapply(df, 
                   function(df){
                     df$new_col <- sample(rep(c(FALSE, TRUE), 
                                              length.out = nrow(df)), 
                                          size = nrow(df), 
                                          replace = FALSE)
                     df
                   })
      do.call("rbind", c(df, list(make.row.names = FALSE)))
      

      可能还有十几种其他方法可以做到这一点,而且可能是我没有想到的更有效的方法。

      【讨论】:

      • 我非常喜欢广义的方法。但是您的第二个代码示例更改了“类别”列的位置:它变成了第一个。如何保持列顺序?
      • 更新了答案,为您提供了几个列顺序选项。
      猜你喜欢
      • 2019-07-19
      • 2017-10-30
      • 2015-05-18
      • 1970-01-01
      • 1970-01-01
      • 2015-03-26
      • 2013-12-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多