2014-01-21 9 views
18

Если я хочу, чтобы случайным образом выбрать несколько образцов из разных групп я использую пакет plyr и код нижевыборочные ряды подгрупп из dataframe с dplyr

require(plyr) 
sampleGroup<-function(df,size) { 
    df[sample(nrow(df),size=size),] 
} 

iris.sample<-ddply(iris,.(Species),function(df) sampleGroup(df,10)) 

Здесь 10 образцов выбраны из каждого вида.

Некоторые из моих dataframes очень большие, и мой вопрос: могу ли я использовать ту же функцию sampleGroup с пакетом dplyr? Или есть другой способ сделать то же самое в dplyr?

EDIT

Версия 0.2 пакета dplyr представила две новые функции для выбора случайных строк из таблицы sample_n и sample_frac

+0

вот ссылка на введение dplyr. http://rpubs.com/hadley/dplyr-intro – marbel

+0

Спасибо, но я думаю, что решение этой проблемы еще не содержится в документации. Хорошее решение с data.table! – Robert

+1

Почему бы просто не использовать 'iris%.% group_by (Species)%.% sampleGroup (size = 10) ' – dickoa

ответ

8

Это легко сделать с помощью data.table, и полезно для большой Таблица.

Примечание: Как уже отмечалось в коментарии Трой, есть более effiecient способ сделать это с помощью data.table, но я хотел уважать функцию ОП образца и формат в ответ.

require(data.table) 
DT <- data.table(x = rnorm(10e6, 100, 50), y = letters) 

sampleGroup<-function(df,size) { 
    df[sample(nrow(df),size=size),] 
} 

result <- DT[, sampleGroup(.SD, 10), by=y] 
print(result) 

# y   x y 
# 1: a 30.11659 m 
# 2: a 57.99974 h 
# 3: a 58.13634 o 
# 4: a 87.28466 x 
# 5: a 85.54986 j 
# ---    
# 256: z 149.85817 d 
# 257: z 160.24293 e 
# 258: z 26.63071 j 
# 259: z 17.00083 t 
# 260: z 130.27796 f 

system.time(DT[, sampleGroup(.SD, 10), by=y]) 
# user system elapsed 
# 0.66 0.02 0.69 

Using the iris dataset: 
iris <- data.table(iris) 
iris[,sampleGroup(.SD, 10), by=Species] 
+2

+1 для данных.table. Использование '.I' удваивает скорость работы:' iris [iris [, list (idx = sample (.I, 10)), by = "Species"] $ idx] ' – Troy

+1

Я думаю, что вы хотите' sampleGroup (.SD, 10) '(обратите внимание на' .SD' вместо 'DT') – eddi

+0

Спасибо @eddi. Я отредактировал ответ, основанный на вашем комментарии. – marbel

7

Это хороший вопрос! Невозможно увидеть простой способ сделать это с документированным синтаксисом для dplyr, но как насчет этого для обходного пути?

sampleGroup<-function(df,x=1){ 

    df[ 
    unlist(lapply(attr((df),"indices"),function(r)sample(r,min(length(r),x)))) 
    ,] 

} 

sampleGroup(iris %.% group_by(Species),3) 

#Source: local data frame [9 x 5] 
#Groups: Species 
# 
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species 
#39   4.4   3.0   1.3   0.2  setosa 
#16   5.7   4.4   1.5   0.4  setosa 
#25   4.8   3.4   1.9   0.2  setosa 
#51   7.0   3.2   4.7   1.4 versicolor 
#62   5.9   3.0   4.2   1.5 versicolor 
#59   6.6   2.9   4.6   1.3 versicolor 
#148   6.5   3.0   5.2   2.0 virginica 
#103   7.1   3.0   5.9   2.1 virginica 
#120   6.0   2.2   5.0   1.5 virginica 

РЕДАКТИРОВАТЬ - Сравнение производительности

Вот тест против использования data.table (как родные и с вызовом функции, как в приведенном примере) для 1х строк, 26 групп.

Собственная таблица данных примерно в 2 раза быстрее, чем обходной путь dplyr, а также data.table с выносом. Поэтому, вероятно, dplyr/data.table имеют одинаковую производительность.

Надеюсь, ребята dplyr дадут нам некоторый собственный синтаксис для отбора проб в ближайшее время! (Или даже лучше, может быть, это уже есть)

sampleGroup.dt<-function(df,size) { 
    df[sample(nrow(df),size=size),] 
} 

testdata<-data.frame(group=sample(letters,10e5,T),runif(10e5)) 

dti<-data.table(testdata) 

# using the dplyr workaround with external function call 
system.time(sampleGroup(testdata %.% group_by(group),10)) 
#user system elapsed 
#0.07 0.00 0.06 

#using native data.table 
system.time(dti[dti[,list(val=sample(.I,10)),by="group"]$val]) 
#user system elapsed 
#0.04 0.00 0.03 

#using data.table with external function call 
system.time(dti[, sampleGroup.dt(dti, 10), by=group]) 
#user system elapsed 
#0.06 0.02 0.08 
+0

+1 для андроида Трои с использованием data.table в строгом соответствии. Мой ответ, вероятно, медленнее, потому что он копирует два раза таблицу. – marbel

+1

+1 очень приятные сравнения. Но я не понимаю вашу причину последнего теста? Вы образец всей информации для 10 элементов для каждой группы. В то время как вы делаете что-то с 'attributes' для случая' dplyr'. Почему бы не сравнить то же самое для 'dplyr' с функцией, аналогичной 3-му случаю для' DT'? – Arun

+3

Также важным аспектом бенчмаркинга является то, насколько хорошо он масштабируется **. С 26 группами для объединения не будет никакой реальной разницы, которую можно обнаружить. Измените свою строку на 'testdata <-data.frame (group = sample (paste (" id ", 1: 1e5, sep =" "), 10e5, T), runif (10e5))' и снова запустите ваши тесты – Arun

39

Да, вы можете использовать dplyr элегантно с помощью функции делать(). Вот пример:

mtcars %>% 
    group_by(cyl) %>% 
    do(sample_n(.,2)) 

и результаты не являются, как этот

Source: local data frame [6 x 11] 
Groups: cyl 

    mpg cyl disp hp drat wt qsec vs am gear carb 
1 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2 
2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2 
3 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4 
4 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4 
5 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4 
6 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8 

Update:

do функция больше не требуется для sample_n в новых версиях dplyr. Текущий код для взятия случайной выборки из двух рядов на группу:

mtcars %>% 
    group_by(cyl) %>% 
    sample_n(2) 
+0

@Arun, Да, но вы должны обновить dplyr до последней версии 0.1.3.0.99. – PhilChang

+0

@ Arun, извините, вы должны использовать sample_n() – PhilChang

+0

Есть ли способ сделать это без использования 'do'? – Brani