Векторизация цикла - PullRequest
       14

Векторизация цикла

2 голосов
/ 25 февраля 2011

Я создаю некоторые искусственные данные.Мне нужно создать идентификатор домохозяйства (H_ID) и персональный идентификатор (P_ID, в каждом домохозяйстве).

Я нашел способ создания H_ID в векторизации.

N <- 50

### Household ID
# loop-for
set.seed(20110224)
H_ID <- vector("integer", N)
H_ID[1] <- 1
for (i in 2:N) if (runif(1) < .5) H_ID[i] <- H_ID[i-1]+1 else H_ID[i] <- H_ID[i-1]
print(H_ID)

# vectorised form
set.seed(20110224)
r <- c(0, runif(N-1))
H_ID <- cumsum(r < .5)
print(H_ID)

Но я могуне понять, как создать P_ID в векторизации.

### Person ID
# loop-for
P_ID <- vector("integer", N)
P_ID[1] <- 1
for (i in 2:N) if (H_ID[i] > H_ID[i-1]) P_ID[i] <- 1 else P_ID[i] <- P_ID[i-1]+1
print(cbind(H_ID, P_ID))

# vectorised form
# ???

Ответы [ 5 ]

4 голосов
/ 25 февраля 2011

Другой пример:

P_ID <- ave(rep(1, N), H_ID, FUN=cumsum)

Я узнал о функции ave несколько дней назад (здесь) и нашел ее действительно полезным и эффективным сочетанием клавиш во многих ситуациях.

2 голосов
/ 25 февраля 2011
P_ID <- unname(unlist(tapply(H_ID, H_ID, function(x)c(1:length(x)))))
1 голос
/ 25 февраля 2011

Вдохновленный решением Мартина Моргана к тесно связанному вопросу, вот действительно векторизованный способ генерации P_ID с использованием функции cummax. Когда вы заметите, что P_ID тесно связано с cumsum из !(r < 0.5):

, становится ясно
set.seed(1)
N <- 10
r <- c(0, runif(N-1))
H_ID <- cumsum(r < .5)
r_ <- r >= .5 # flip the coins that generated H_ID.
z <- cumsum(r_)  # this is almost P_ID; just need to subtract the right amount...
# ... and the right amount to subtract is obtained via cummax
P_ID <- 1 + z - cummax( z * (!r_) )
> cbind(H_ID, P_ID)
      H_ID P_ID
 [1,]    1    1
 [2,]    1    2
 [3,]    2    1
 [4,]    3    1
 [5,]    3    2
 [6,]    3    3
 [7,]    3    4
 [8,]    4    1
 [9,]    5    1
[10,]    5    2

Я не проводил подробных временных тестов, но, вероятно, это быстро, потому что это все внутренние, векторизованные функции

0 голосов
/ 25 февраля 2011

Вот довольно компактное и выразительное решение. Несколько похож на Симпсона с точки зрения его промежуточных значений:

cbind(H_ID,   unlist( sapply(table(H_ID), seq) ) )

Суть его стратегии состоит в том, чтобы использовать значения table () в качестве аргумента для seq (), который по умолчанию примет одно числовое значение и вернет последовательность из 1.

0 голосов
/ 25 февраля 2011

seq_along() является полезным инструментом здесь.Этот пример разбивает H_ID сам по себе на список, содержащий домохозяйства:

> head(split(H_ID, H_ID))
$`1`
[1] 1 1

$`2`
[1] 2

$`3`
[1] 3 3 3 3
....

Тогда решение Q состоит в том, чтобы lapply() функция seq_along() для каждого элемента списка;seq_along() создает вектор 1:length(foo).На последних двух шагах по ведению домашнего хозяйства удалите список результатов и удалите names:

> unname(unlist(lapply(split(H_ID, H_ID), seq_along)))
 [1] 1 2 1 1 2 3 4 1 1 2 3 1 1 1 1 1 2 3 4 5 1 2 3 4 1 1 2 1 2 1
[31] 1 2 1 2 3 4 1 2 1 2 1 2 1 1 2 1 2 1 2 3
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...