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

У меня есть фрейм данных, который состоит из первого столбца (experiment.id), а остальные столбцы — это значения, связанные с этим идентификатором эксперимента. Каждая строка представляет собой уникальный идентификатор эксперимента. В моем фрейме данных есть столбцы в порядке от 10⁴ до 10⁵.

data.frame(experiment.id=1:100, v1=rnorm(100,1,2),v2=rnorm(100,-1,2) )

Этот фрейм данных является источником моего демонстрационного пространства. Что я хотел бы сделать, так это для каждого уникального Experiment.id (строка) случайным образом выбрать (с заменой) одно из значений v1, v2, ...., v10000, связанных с этим идентификатором, и создать образец s1. В каждом образце s1 представлены все идентификаторы экспериментов.

В конце концов я хочу выполнить 10⁴ выборок, s1, s2, ...., s 10⁴ и вычислить некоторую статистику.

Что было бы наиболее эффективным способом (в вычислительном отношении) для выполнения этого процесса выборки. Я хотел бы максимально избежать циклов for.

Обновление: Мои вопросы касаются не только семплирования, но и хранения сэмплов. Я предполагаю, что мой реальный вопрос заключается в том, есть ли более быстрый способ выполнить вышеперечисленное, кроме

d<-data.frame(experiment.id=1:1000, replicate (10000,rnorm(1000,100,2)) )
results<-data.frame(d$experiment.id,replicate(n=10000,apply(d[,2:10001],1,function(x){sample(x,size=1,replace=T)})))

person ECII    schedule 28.12.2012    source источник
comment
Experiment.id — любая уникальная строка/число символов.   -  person ECII    schedule 28.12.2012
comment
replicate и apply по существу зацикливаются и медленны. Любой из опубликованных ответов будет быстрее, если у вас есть возможность хранить такую ​​матрицу. Если пространство становится проблемой, вам нужно будет перебирать строки и получать из них статистику по ходу дела. Так что разберитесь с этой частью заранее. До сих пор вы подразумевали, что вам нужны значения 1e9. Вот и попадаем в многогигабайтную матрицу.   -  person John    schedule 28.12.2012
comment
@Джон Спасибо за ваш вклад. Моя основная проблема, как вы упомянули, - это время вычислений и хранение, а не процедура выборки как таковая. Вычисление статистики на лету было бы вариантом, но было бы удобно заранее иметь полный набор данных и проводить анализ постфактум. Спасибо за ваш вклад.   -  person ECII    schedule 28.12.2012


Ответы (3)


Самое короткое и читаемое ИМХО по-прежнему заключается в использовании apply, но с хорошим использованием того факта, что sample векторизовано:

results <- data.frame(experiment.id = d$experiment.id,
                      t(apply(d[, -1], 1, sample, 10000, replace = TRUE)))

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

person flodel    schedule 28.12.2012
comment
Может ли это быть правильным? Вы выбираете каждую строку и возвращаете вектор. Как этот вектор вводится в результирующий фрейм данных? - person ECII; 28.12.2012
comment
Скорость этого будет сильно зависеть от количества строк - person John; 28.12.2012
comment
@ECII, t(apply(...)) вернет матрицу 1000 на 10000, которая будет привязана к идентификаторам эксперимента с помощью data.frame. Попробуйте. - person flodel; 28.12.2012

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

Для фрейма данных d:

d[matrix(c(seq(nrow(d)), sample(ncol(d)-1, nrow(d), replace=TRUE)+1), ncol=2)]

Это один образец. Чтобы получить N выборки, просто умножьте выборку (как в ответе Джона):

mm <- matrix(c(rep(seq(nrow(d)), N), sample(ncol(d)-1, nrow(d)*N, replace=TRUE)+1), ncol=2)

result <- matrix(d[mm], ncol=N)

Но у тебя будут проблемы с памятью.

person Matthew Lundberg    schedule 28.12.2012
comment
Спасибо. Ваша первая строка создает образец. Однако я хотел бы построить и сохранить 10⁴ этих образцов, а затем выполнить некоторую статистику. Что было бы эффективно для создания и хранения такого большого количества образцов? - person ECII; 28.12.2012
comment
@ECII Сохранить в переменную? В противном случае, я не уверен, что вы спрашиваете. - person Matthew Lundberg; 28.12.2012
comment
Сохраните все образцы во фрейме данных для дальнейшего статистического анализа. Смотрите мое обновление моего вопроса и комментарий Джона - person ECII; 28.12.2012

Можно вообще обойтись без циклов. Если вы преобразуете свои столбцы после первого в матрицу, это становится легко, потому что матрица может быть адресована либо как [строка, столбец], либо последовательно как ее базовый вектор.

mat <- as.matrix(datf[,-1])
nr <- nrow(mat); nc <- ncol(mat)
sel <- sample( 1:nc, nr, replace = TRUE )
sel <- sel + ((1:nr)-1) * nc
x <- t(mat)[sel]
seldatf <- data.frame( datf[,1], x = x )

Теперь, чтобы получить множество сэмплов, достаточно просто умножить одну и ту же логику.

ns <- 10 # number of samples / row
sel <- sample(1:nc, nr * ns, replace = TRUE )
sel <- sel + rep(((1:nr)-1) * nc, each = ns)
x <- t(mat)[sel]
seldatf <- cbind( datf[,1],  data.frame(matrix(x, ncol = ns, byrow = TRUE)) )

Вполне возможно, что это будет действительно большой фрейм данных, если вы собираетесь установить ns <- 1e5 и у вас много строк. Возможно, вам придется смотреть нехватку памяти. Я делаю немного ненужного копирования из соображений удобочитаемости. Вы можете устранить это для памяти и скорости, потому что, когда вы используете большие объемы памяти, вы будете подкачивать другие запущенные программы. Это медленно. Вам не нужно назначать и сохранять x, mat или даже sel. Если этого не сделать, вы получите максимально быстрый ответ.

person John    schedule 28.12.2012