sparklyr: как взять сбалансированный образец по группам - PullRequest
1 голос
/ 14 января 2020

Я хочу выбрать n строк из каждого из классов моего Spark DataFrame в sparklyr.

Я понимаю, что для этого нельзя использовать функцию dplyr::sample_n ( Is sample_n действительно случайная выборка при использовании с sparklyr? ), поэтому я использовал функцию sparklyr::sdf_sample(). Проблема в том, что я не могу выполнить выборку по группе, то есть получить 10 наблюдений от каждого класса, я могу указать только долю всего набора данных для выборки.

У меня есть обходной путь для использования sdf_sample() в каждая группа индивидуально в al oop, но так как функция не возвращает точный размер выборки, это все еще не идеально.

R код для обхода проблемы:

library(sparklyr)
library(dplyr)

sc <- spark_connect(master = "local", version = "2.3")

# copy iris to our spark cluster
iris_tbl <- copy_to(sc, iris, overwrite = TRUE)


# get class counts
class_counts <- iris_tbl %>% count(Species) %>%
  collect()
#  Species        n
#  <chr>      <dbl>
#1 versicolor    50
#2 virginica     50
#3 setosa        50

# we want to sample n = 10 points from each class
n <- 10 

sampled_iris <- data.frame(stringsAsFactors = F)
for( i in seq_along(class_counts$Species)){

  my_frac <- n / class_counts[[i, 'n']]
  my_class <- class_counts[[i, 'Species']]

  tmp <- iris_tbl %>%
    filter(Species == my_class) %>%
    sdf_sample(fraction = my_frac) %>%
    collect()

  sampled_iris <- bind_rows(sampled_iris, tmp)
}

Мы не я получаю ровно 10 образцов из каждого класса:

# new counts
sampled_iris %>% count(Species)


#Species        n
#  <chr>      <int>
#1 setosa         7
#2 versicolor     9
#3 virginica      6

Мне интересно, есть ли лучший способ получить сбалансированную выборку по группам, используя sparklyr? Или даже используя sql запрос, который я могу передать непосредственно в кластер, используя DBI::dbGetQuery()?

1 Ответ

2 голосов
/ 14 января 2020

Я не могу выполнить выборку по группе

Пока столбец группировки является строкой (это ограничение отображения типа sparklyr), эту часть можно легко обработать с помощью DataFrameStatFunctions.sampleBy:

spark_dataframe(iris_tbl) %>%
  sparklyr::invoke("stat") %>%
  sparklyr::invoke(
    "sampleBy",
    "Species",
    fractions=as.environment(list(
      "setosa"=0.2,
      "versicolor"=0.2,
      "virginica"=0.2
    )),
    seed=1L
  ) %>% sparklyr::sdf_register()

Однако ни один распределенный и масштабируемый метод не даст вам «точный размер выборки». Можно использовать такие хаки, как:

iris_tbl %>% 
  group_by(Species) %>% 
  mutate(rand = rand()) %>%
  arrange(rand, .by_group=TRUE) %>%
  filter(row_number() <= 10) %>%
  select(-rand)

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

Если выборки малы, вы можете сделать это немного дальше, но сначала с передискретизацией (с помощью первого метода), а затем с точными выборками (с помощью второго метода), но если ваши данные достаточно велики, чтобы обрабатывать с помощью Spark, небольшие колебания не должны иметь большого значения.

...