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