Выборка одного и того же количества несбалансированных классов из двух фреймов данных

#r #dplyr #tidyverse

Вопрос:

Я пытаюсь отобрать одинаковое количество классов из двух фреймов данных разных размеров. Я могу сделать это вручную, но количество классов в некоторых моих фреймах данных довольно велико.

Я смог использовать эту dplyr::count функцию, чтобы получить список интересующих классов из меньшего фрейма данных, а также их количество. Затем я извлекаю эти классы и их количество в виде векторов. Затем я попытался создать функцию с использованием этих векторов и вызвать ее с помощью mapply , чтобы я мог создавать отфильтрованные фрагменты для каждого класса , а затем повторно присоединяться к спискам с помощью do.call , но при попытке запуска я получаю ошибки mapply .

Ниже приведены примеры наборов данных. df-это меньший фрейм данных , содержащий ControlVarA == "Group_1" 6 строк и 10 строк ControlVarA == "Group_2" , и я хочу извлечь такое же количество строк/классов из большего фрейма данных df2 (который содержит 6 строк ControlVarA == "Group_1" и 20 строк ControlVarA == "Group_2" ).

 df <- data.frame("ID" = 1:16)
df$VarA <- c(1,1,1,1,1,1,1,1,1,1,1,14,NA_real_,NA_real_,NA_real_,16)
df$VarB <- c(10,0,0,0,12,12,12,12,0,14,NA_real_,14,16,16,16,16)
df$VarC <- c(10,12,14,16,10,12,14,16,10,12,14,16,10,12,14,16)
df$VarD <- c(10,12,14,16,10,12,14,16,10,12,14,16,10,12,14,16)
df$ControlVarA <- factor(c("Group_1","Group_1","Group_1","Group_1","Group_1", "Group_1",
                           "Group_2","Group_2","Group_2","Group_2","Group_2","Group_2",
                           "Group_2","Group_2","Group_2","Group_2")) 
df


df2 <- data.frame("ID" = 1:26)
df2$VarA <- c(1,1,1,1,1,1,1,1,1,1,1,14,NA_real_,NA_real_,NA_real_,16,16,16,16,16,16,16,16,16,16,16)
df2$VarB <- c(10,0,0,0,12,12,12,12,0,14,NA_real_,14,16,16,16,16,16,16,16,16,16,16,16,16,16,16)
df2$VarC <- c(10,12,14,16,10,12,14,16,10,12,14,16,10,12,14,16,16,16,16,16,16,16,16,16,16,16)
df2$VarD <- c(10,12,14,16,10,12,14,16,10,12,14,16,10,12,14,16,16,16,16,16,16,16,16,16,16,16)
df2$ControlVarA <- factor(c("Group_1","Group_1","Group_1","Group_1","Group_1", "Group_1",
                           "Group_2","Group_2","Group_2","Group_2","Group_2","Group_2",
                           "Group_2","Group_2","Group_2","Group_2","Group_2","Group_2",
                           "Group_2","Group_2","Group_2","Group_2","Group_2","Group_2","Group_2","Group_2")) 
df2
 

Чтобы извлечь имена классов и количество классов, я использую приведенный ниже код.

 slice_vars <- df %>% 
  count(ControlVarA) %>% 
  filter(!is.na(.)) %>% 
  t() %>% 
  janitor::row_to_names(1) %>% 
  colnames()

slice_nums <- df %>% 
  count(ControlVarA) %>% 
  filter(!is.na(.)) %>% 
  t() %>% 
  janitor::row_to_names(2) %>% 
  as.data.frame() %>% 
  rename_with(~ gsub(" ", "", .x)) %>% 
  colnames() %>% 
  as.numeric()
 

function Я создал и mapply инструкция ниже

 func_group <- function(dataset, x, y) {
  dataset %>% 
    group_by(ControlVarA) %>% 
    slice_sample(n = all_of(x)) %>% 
    ungroup() %>% 
  filter(ControlVarA == data[[y]])
}

combine_lists <- mapply(func_group, slice_nums, slice_vars, MoreArgs = list(dataset = df2)) 

do.call(rbind, combine_lists)
 

Ответ №1:

count чтобы получить количество строк для каждого значения ControlVarA , объединитесь df2 и выберите n случайные строки из каждой группы с помощью sample_n . (К сожалению, slice_sample(n = first(n)) возвращает ошибку)

 library(dplyr)

df %>% 
  count(ControlVarA) %>%
  left_join(df2, by = 'ControlVarA') %>%
  group_by(ControlVarA) %>%
  sample_n(first(n)) %>%
  ungroup %>%
  select(-n)

#   ControlVarA    ID  VarA  VarB  VarC  VarD
#   <fct>       <int> <dbl> <dbl> <dbl> <dbl>
# 1 Group_1         1     1    10    10    10
# 2 Group_1         4     1     0    16    16
# 3 Group_1         3     1     0    14    14
# 4 Group_1         2     1     0    12    12
# 5 Group_1         5     1    12    10    10
# 6 Group_1         6     1    12    12    12
# 7 Group_2        12    14    14    16    16
# 8 Group_2        25    16    16    16    16
# 9 Group_2        15    NA    16    14    14
#10 Group_2        22    16    16    16    16
#11 Group_2         9     1     0    10    10
#12 Group_2         8     1    12    16    16
#13 Group_2        24    16    16    16    16
#14 Group_2        21    16    16    16    16
#15 Group_2         7     1    12    14    14
#16 Group_2        14    NA    16    12    12
 

Ответ №2:

 library(tidyverse)

df <- data.frame("ID" = 1:16)
df$VarA <- c(1,1,1,1,1,1,1,1,1,1,1,14,NA_real_,NA_real_,NA_real_,16)
df$VarB <- c(10,0,0,0,12,12,12,12,0,14,NA_real_,14,16,16,16,16)
df$VarC <- c(10,12,14,16,10,12,14,16,10,12,14,16,10,12,14,16)
df$VarD <- c(10,12,14,16,10,12,14,16,10,12,14,16,10,12,14,16)
df$ControlVarA <- factor(c("Group_1","Group_1","Group_1","Group_1","Group_1", "Group_1",
                           "Group_2","Group_2","Group_2","Group_2","Group_2","Group_2",
                           "Group_2","Group_2","Group_2","Group_2")) 
df2 <- data.frame("ID" = 1:26)
df2$VarA <- c(1,1,1,1,1,1,1,1,1,1,1,14,NA_real_,NA_real_,NA_real_,16,16,16,16,16,16,16,16,16,16,16)
df2$VarB <- c(10,0,0,0,12,12,12,12,0,14,NA_real_,14,16,16,16,16,16,16,16,16,16,16,16,16,16,16)
df2$VarC <- c(10,12,14,16,10,12,14,16,10,12,14,16,10,12,14,16,16,16,16,16,16,16,16,16,16,16)
df2$VarD <- c(10,12,14,16,10,12,14,16,10,12,14,16,10,12,14,16,16,16,16,16,16,16,16,16,16,16)
df2$ControlVarA <- factor(c("Group_1","Group_1","Group_1","Group_1","Group_1", "Group_1",
                            "Group_2","Group_2","Group_2","Group_2","Group_2","Group_2",
                            "Group_2","Group_2","Group_2","Group_2","Group_2","Group_2",
                            "Group_2","Group_2","Group_2","Group_2","Group_2","Group_2","Group_2","Group_2")) 
df <- as_tibble(df) %>% 
  mutate(table = "df")
df2 <- as_tibble(df2) %>% 
  mutate(table = "df2")

final_df <- df %>% 
  bind_rows(df2)

set.seed(2021)  
final_df %>% 
  filter(!if_any(.cols = VarA:VarD, is.na)) %>% 
  group_by(table, ControlVarA) %>% 
  slice_sample(n = 5)
#> # A tibble: 20 x 7
#> # Groups:   table, ControlVarA [4]
#>       ID  VarA  VarB  VarC  VarD ControlVarA table
#>    <int> <dbl> <dbl> <dbl> <dbl> <fct>       <chr>
#>  1     6     1    12    12    12 Group_1     df   
#>  2     2     1     0    12    12 Group_1     df   
#>  3     3     1     0    14    14 Group_1     df   
#>  4     5     1    12    10    10 Group_1     df   
#>  5     4     1     0    16    16 Group_1     df   
#>  6    16    16    16    16    16 Group_2     df   
#>  7     9     1     0    10    10 Group_2     df   
#>  8     8     1    12    16    16 Group_2     df   
#>  9    10     1    14    12    12 Group_2     df   
#> 10     7     1    12    14    14 Group_2     df   
#> 11     1     1    10    10    10 Group_1     df2  
#> 12     4     1     0    16    16 Group_1     df2  
#> 13     3     1     0    14    14 Group_1     df2  
#> 14     2     1     0    12    12 Group_1     df2  
#> 15     6     1    12    12    12 Group_1     df2  
#> 16    22    16    16    16    16 Group_2     df2  
#> 17    23    16    16    16    16 Group_2     df2  
#> 18     9     1     0    10    10 Group_2     df2  
#> 19    18    16    16    16    16 Group_2     df2  
#> 20    20    16    16    16    16 Group_2     df2
 

Создано 2021-07-13 пакетом reprex (v2.0.0)

Комментарии:

1. Спасибо Десмонд, но я пытался найти решение, которое дало бы мне такое же количество отсчетов в исходном фрейме данных df, как и в более крупном df2. Т. Е. Мне нужно отфильтровать 6 строк с Group_1 и 10 строк с Group_2 из df 2. Исходный df2 содержит 6 строк с Group_1 и 20 строк с Group_2