Выборка R с оператором if и аналогичным количеством выборок

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

 name <- sample(c("Adam","John","Henry","Mike"),100,rep = TRUE)
 area <- sample(c("run","develop","test"),100,rep = TRUE)
 id <- sample(100:200,100,rep = FALSE)

 mydata <- as.data.frame(cbind(id,area,name))


qcsample <- mydata %>%
  group_by(area) %>% 
  nest() %>%            
  mutate(n = c(20, 15, 15)) %>% 
  mutate(samp = map2(data, n, sample_n)) %>% 
  select(area, samp) %>%
  unnest()

Теперь я получаю эти результаты.

table(qcsample$area) 

develop     run    test 
     15      15      20 

-

table(qcsample$name)

Adam Henry  John  Mike 

    9     9    16    16 

Я хотел бы создать образец, который имел бы более или менее одинаковое количество образцов для каждого имени, например. Адам - ​​12, Генри - 12, Джон - 13, Майк - 13. Как я могу этого добиться? Могу я как-нибудь попросить, чтобы образец был распределен поровну?

Также в этом примере я использовал функцию

sample_n

и указанное количество образцов.

Я ожидаю, что иногда не будет необходимого номера из данной группы. В моем примере я беру 20 образцов из области, называемой «тест», но иногда бывает только, скажем, 10 строк, содержащих «тест». Общее количество - 50, поэтому мне нужно убедиться, что если есть только 10 «test», код должен автоматически увеличивать остальные, поэтому образец будет «test» - 10, «run» - 20 и «develop» - 20. Это может произойти с любой областью, поэтому мне нужно проверить, достаточно ли строк для создания образца и увеличения других областей. Если есть только 1, его можно добавить к любой из оставшихся областей, или если разница составляет 3, мы добавляем 1 к одной области и 2 к другой.

Как это проверить, учитывая все возможности? Я считаю, что в этом случае есть восемь перестановок.

Заранее спасибо А.

Чтобы обеспечить почти равные пропорции (т.е. намеренно уменьшить случайность), вам нужно либо (а) выполнить повторную выборку (неизвестное количество раз), пока не получите приемлемые пропорции; или (б) заранее зафиксируйте свои пропорции и просто выберите порядок этих имен. Вы не можете действительно "запросить", чтобы случайный выбор был менее случайным :-)

r2evans 02.09.2018 22:05

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

Art 20.09.2018 11:38
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
147
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если вы используете выдуманные данные, вы можете создать минимальное количество каждой строки, а затем создать заполнитель, чтобы получить общее количество:

set.seed(42)

names <- c("Adam", "John", "Henry", "Mike")
areas <- c("run", "develop", "test")

totalrows <- 100
minname   <-  22 # No less than 20 of each name (set to near threshold to test)
minarea   <-  30 # No less than 30 of each area (less randomness the higher these are)

qcsample <- data.frame(
  name=sample(c(rep(names, minname), sample(names, totalrows-length(names)*minname, replace=T))),
  area=sample(c(rep(areas, minarea), sample(areas, totalrows-length(areas)*minarea, replace=T))),
  id=sample(99+(1:totalrows))
)

Это приводит к:

R> table(qcsample$name)

 Adam Henry  John  Mike 
   23    28    24    25 
R> table(qcsample$area)

develop     run    test 
     37      31      32

Обратите внимание, что количество от name до area не ограничено:

R> table(qcsample[,-3])
       area
name    develop run test
  Adam        5  11    7
  Henry      11   8    9
  John       10   7    7
  Mike       11   5    9
R> 

Используя цикл, предложенный @ r2evans:

library(dplyr)
set.seed(42)

mydata <- data.frame(
  name = sample(c("Adam","John","Henry","Mike"), 100, rep = TRUE),
  area = sample(c("run","develop","test"), 100, rep = TRUE),
  id   = sample(100:200, 100, rep = FALSE)
)

Nsamples <- 50
mysample <- data.frame(sample_n(mydata, Nsamples))

minname <- 11  # max is 50/4 -> 12 
minarea <- 15  # max is 50/3 -> 16

# the test you were asking about
while( (min(table(mysample$name)) < minname) || (min(table(mysample$area)) < minarea) ) {
  mysample <- data.frame(sample_n(mydata, Nsamples))
}

Это приводит к:

R> table(mysample$name)

 Adam Henry  John  Mike 
   13    15    11    11 

R> table(mysample$area)

develop     run    test 
     15      17      18 

И, как и раньше, у области нет минимума имени.

R> table(mysample[-3])
       area
name    develop run test
  Adam        4   3    6
  Henry       2   6    7
  John        4   4    3
  Mike        5   4    2

Если вам нужно установить минимальное количество для каждой перестановки, добавьте это в тест:

while(... || (min(table(mysample[-3])) < some_min)) {

Кстати, количество перестановок, как вы можете видеть из таблицы, - это количество имен, умноженное на количество областей.

Спасибо. На самом деле я использую реальные данные, и приведенный пример просто объясняет, чего я пытаюсь достичь. Главное, чтобы образец имел необходимый размер, всего 50. Затем поддерживайте процентное значение 50, тестируйте 40%, запускайте 30% и развивайте 30%, что дает соответственно 20,15,15. Если для любого из них недостаточно данных, я должен увеличить (более или менее равномерно) другие области, чтобы их общее количество составляло 50. Все образцы должны быть уникальными, поэтому я не могу их заменить. Thnaks

Art 03.09.2018 11:29

Я этого боялся. Я сделал одно решение, в котором отбиралось минимальное количество для каждой категории, а затем выполнялось отборочное заполнение остальных или удаление строк по мере необходимости, но это было слишком некрасиво для публикации. Вероятно, ЛЮБОЕ другое решение, которое кто-то придумал, было более элегантным, чем мое. :) @ r2evans "Зацикливание, пока не получите нужные пропорции", вероятно, лучший вариант.

keithpjolley 03.09.2018 19:31

Спасибо. Знаете ли вы, как создать оператор if для установки счетчика? скажем, я сначала проверю, равен ли размер первой области 50. Если это 40, я бы установил свой первый счетчик на 40 и протестировал другие. Если второй и третий больше 50, я бы сказал, установите 50 + 5 для каждого из них. От первого осталось 10, поэтому я разделил его на 2. Если число нечетное, скажем 9, мне нужно будет разделить его на 4 и 5. Я хочу сделать общее число 150. Мне нужно будет проверить все возможности по трем направлениям. Тогда я мог бы получить от них процентную выборку. Большое спасибо !

Art 03.09.2018 22:36

Отредактируйте исходный ответ, чтобы включить такой тест.

keithpjolley 03.09.2018 23:44

Ну, я думаю, короткий ответ - «нет, я не знаю, как проводить такой тест», но я не думаю, что это выход.

keithpjolley 04.09.2018 00:09

Кроме того, возможно, что с другим семенем вы не сможете достичь минимума. Вероятно, вам следует убедиться, что это возможно, прежде чем запускать цикл while!

keithpjolley 04.09.2018 00:20

Большое спасибо! Как только у меня появится больше времени на работе, я протестирую его на реальных данных и расскажу, как у меня дела. Очень признателен! :)

Art 04.09.2018 21:44

Привет, наконец-то у меня появилось время реализовать код для моих данных и запустить несколько тестов ... единственное условие для цикла while, которое я установил для (min (table (mysample [-3])) <1) ... цикл никогда не останавливается :( он работает вечно ... мой набор данных больше, чем в примере, но даже в установленном примере цикл while никогда не останавливается ...

Art 20.09.2018 11:21

Вот еще одна мысль.

В зависимости от желаемого конечного размера, он может создать избыточное количество выборок, чтобы уменьшить некоторые пары имя / область, чтобы уменьшить общее количество.

Допустим, вы хотите получить в итоге 50 строк:

final_size <- 50

Для полноты картины, вот наборы, из которых мы выберем:

avail_names <- c("Adam", "John", "Henry", "Mike")
avail_areas <- c("run", "develop", "test")

и минимум, который нам нужно создать для Adam,run (и т. д.), чтобы в конечно осталось не менее final_size строк:

size_per_namearea <- ceiling(final_size / (length(avail_names) * length(avail_areas)))

Хорошо, сгенерируйте как минимум столько (вероятно, больше, чем) количество строк, которые нам нужны:

set.seed(20180920)
qcsample <- crossing(data_frame(rownum = seq_len(size_per_namearea)),
                     data_frame(name   = avail_names),
                     data_frame(area   = avail_areas)) %>%
  group_by(name, area) %>%
  mutate(id = sample(100, size = n(), replace = FALSE))
qcsample
# # A tibble: 60 x 4
# # Groups:   name, area [12]
#    rownum name  area       id
#     <int> <chr> <chr>   <int>
#  1      1 Adam  run        59
#  2      1 Adam  develop    51
#  3      1 Adam  test       23
#  4      1 John  run        71
#  5      1 John  develop     5
#  6      1 John  test       24
#  7      1 Henry run         4
#  8      1 Henry develop    29
#  9      1 Henry test       79
# 10      1 Mike  run        77
# # ... with 50 more rows

Убедитесь, что у нас одинаковые размеры выборки для каждого имени / области:

xtabs(~ name + area, data = qcsample) %>%
  stats::addmargins()
#        area
# name    develop run test Sum
#   Adam        5   5    5  15
#   Henry       5   5    5  15
#   John        5   5    5  15
#   Mike        5   5    5  15
#   Sum        20  20   20  60

Если мы просто сделаем head(final_size), то мы будем знать, имена которых мы будем сокращать, что немного подрывает случайность вашей выборки. Причина, по которой я добавил rownum заранее, заключалась в том, чтобы я мог организовать с помощью него плюс джиттер, гарантируя, что я получу весь max(rownum)-1, а затем некоторую выборку max(rownum), гарантия, чтобы каждая пара имя / область имела строки max(rownum)-1 или max(rownum); ваши результаты никогда не отличаются более чем на 1.

reducedsample <- arrange(qcsample, rownum + runif (n()))  %>%
  head(final_size) %>%
  select(-rownum)
reducedsample %>%
  xtabs(~ name + area, data = .) %>%
  stats::addmargins()
#        area
# name    develop run test Sum
#   Adam        4   4    5  13
#   Henry       5   4    4  13
#   John        4   4    4  12
#   Mike        4   4    4  12
#   Sum        17  16   17  50

Другие вопросы по теме