R выборка N строк кадра данных как можно более равномерно по M кластерам (но случайным образом внутри)

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

> testdata
   Cluster Name
1        1    A
2        1    B
3        1    C
4        2    D
5        3    E
6        3    F
7        3    G
8        3    H
9        4    I
10       5    J
11       5    K
12       5    L
13       5    M
14       5    N
15       6    O
16       7    P
17       7    Q

Например, если я запрашиваю выборку из 3 строк, я хотел бы извлечь случайную строку из случайных 3 кластеров (то есть не первые строки кластеров 1-3 каждый раз, хотя это один действительный результат).

Приемлемые примеры:

> testdata_subset
   Cluster Name
1        1    A
5        3    E
12       5    L 

> testdata_subset
   Cluster Name
6        3    F
14       5    N
15       6    O

Неверный пример:

> testdata_subset
   Cluster Name
6        3    F
8        3    H
13       5    M

Та же идея применима до размера выборки 7 в показанных примерных данных (1 на кластер). Для более высоких размеров выборки я хотел бы рисовать из каждого кластера как можно более равномерно, затем равномерно по оставшимся кластерам с несэмплированными строками и т. Д., Пока не будет выбрано указанное число строк.

Я знаю, как выбрать N строк без разбора:

testdata[sample(nrow(testdata), N),]

Но это не имеет никакого отношения к кластерам. Я также использовал plyr для случайной выборки N строк на кластер:

ddply(testdata,"Cluster", function(z) z[sample(nrow(z), N),])

Но это терпит неудачу, как только вы запрашиваете больше строк, чем в кластере (то есть, если N > 1). Затем я добавил оператор if/else, чтобы начать обрабатывать это:

numsamp_per_cluster <- 2

ddply(testdata,"Cluster", function(z) if (numsamp_per_cluster > nrow(z)){z[sample(nrow(z), nrow(z)),]} else {z[sample(nrow(z), numsamp_per_cluster),]})

Это эффективно ограничивает размер выборки, требуемый для размера каждого кластера. Но при этом он теряет контроль над общим размером выборки. Я надеюсь (но начинаю сомневаться), что есть элегантный метод, использующий dplyr или подобный пакет, который может сделать этот вид полурандомизированной выборки. В любом случае, я изо всех сил пытаюсь связать эти элементы вместе и решить проблему.

3 ответа

Решение

Стратегия: во-первых, вы случайным образом назначаете порядок внутри каждого cluster, Это значение хранится в inside переменная ниже. Далее вы случайным образом выбираете порядок первого выбора каждого кластера и так далее (outside переменная). Наконец, вы заказываете свой фрейм данных, выбирая первый выбор, затем второй и т. Д. Каждого кластера, разрывая связи с outside переменная. Что-то вроде того:

set.seed(1)
inside<-ave(seq_along(testdata$Cluster),testdata$Cluster,FUN=function(x) sample(length(x)))
outside<-ave(inside,inside,FUN=function(x) sample(seq_along(x)))
testdata[order(inside,outside),]   
#   Cluster Name
#10       5    J
#15       6    O
#4        2    D
#5        3    E
#9        4    I
#16       7    P
#1        1    A
#13       5    M
#3        1    C
#17       7    Q
#7        3    G
#6        3    F
#14       5    N
#2        1    B
#12       5    L
#8        3    H
#11       5    K

Теперь выбираем первый n строки результирующего data.frame вы получите образец, который вы ищете.

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

   sample_df=function(df,iter){
    l=unique(df$Cluster)
    cluster_pos=lapply(l, function(x) which(df$Cluster==x))
    random_cluster_pos=lapply(cluster_pos, function(x) if(length(x) > 1) { sample(x) } else x)
    ## index=random_cluster_pos[rev(order(sapply(random_cluster_pos, length)))]
    index=sample(random_cluster_pos)
    inde_pos=c(t(sapply(index, "[", 1:length(index))))
    inde_pos=inde_pos[!is.na(inde_pos)]
    return(df[inde_pos[1:iter],])
}
sample_df(testdata, 3)

Опция Base R: Вы можете случайно выбрать уникальные значения кластера, а затем использовать их для случайной выборки имен? Не очень элегантно, но может быть определено в функции. N - количество выборок, которые вы хотите извлечь из "кластера".

sampler <- function(df,n){
  s <- sample(unique(df[,1]),n)
  n <- sapply(s, function(x) sample(df[which(df[,1]==x),2],1,replace=F))
  data.frame(cluster = s, name = n)
}

> sampler(testdata,6)
  cluster name
1       4    I
2       2    D
3       6    O
4       1    A
5       7    Q
6       5    K
Другие вопросы по тегам