2012-01-25 3 views
4

Предположим, что мы имеем следующую базу данных:нумерации групп

ID Shoot hit 
1  10 2 
1  9 3 
1  8 1 
2  10 8 
2  8 8 
2  11 10 
2  7 2 
3  9 2 
4  6 6 
4  6 5 
. 
. 

И я хотел бы иметь его с номерами, присвоенными в каждой группе, в данном случае на ID, такие как:

ID Shoot hit number.in.group 
1 10  2 1 
1 9  3 2 
1 8  1 3 
2 10  8 1 
2 8  8 2 
2 11 10 3 
2 7  2 4 
3 9  2 1 
4 6  6 1 
4 6  5 2 
    . 
    . 

I может сделать это легко, используя петлю. Что-то вроде это будет работать:

df$number.in.group = rep(1,nrow(df)) 

for(i in 2:nrow(df)) 
    if(df$ID[i]==df$ID[i-1]){ 
    df$number.in.group[i] = df$number.in.group[i-1] + 1 } 

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

+0

Мы вообще не беспокоиться о датах, когда маркировка вопросы как дубликаты. На другой вопрос есть более качественные ответы. – zwol

ответ

2

Использование dplyr

dat <- data.frame(ID = rep(1:3, c(2, 3, 5)), val = rnorm(10)) 

library(dplyr) 
dat %>% group_by(ID) %>% 
    mutate(number.in.group = 1:n()) 
8

Если вы хотите один-лайнер, что-то вроде

df$number.in.group = unlist(lapply(table(df$ID),seq.int)) 
+0

Это очень близко к коду для 'sequence', no? – joran

+0

Ну, 'sequence (X)' определяется как 'unlist (lapply (X, seq_len))' поэтому, да, вы можете записать его как 'sequence (table (df $ ID))' - я просто предпочитаю использовать direct функции, а не обертки - экономит время;) [и меньше функций для запоминания: P]. –

+0

Ты как Нео; вы думаете с точки зрения исходного кода! – joran

2

Есть, вероятно, более эффективные способы, но можно было бы использовать tapply на идентификаторы и бросить в функцию, которая возвращает последовательность.

# Example data 
dat <- data.frame(ID = rep(1:3, c(2, 3, 5)), val = rnorm(10)) 

# Using tapply with a function that returns a sequence 
dat$number.in.group <- unlist(tapply(dat$ID, dat$ID, function(x){seq(length(x))})) 
dat 

что приводит к

> dat 
    ID   val number.in.group 
1 1 -0.454652118    1 
2 1 -2.391824247    2 
3 2 0.530832021    1 
4 2 -1.671043812    2 
5 2 -0.045261549    3 
6 3 2.311162484    1 
7 3 -0.525635803    2 
8 3 0.008588811    3 
9 3 0.078942033    4 
10 3 0.324156111    5 
8

Вы могли бы просто использовать rle и sequence:

dat <- read.table(text = "ID Shoot hit 
+ 1  10 2 
+ 1  9 3 
+ 1  8 1 
+ 2  10 8 
+ 2  8 8 
+ 2  11 10 
+ 2  7 2 
+ 3  9 2 
+ 4  6 6 
+ 4  6 5",sep = "",header = TRUE) 

> sequence(rle(dat$ID)$lengths) 
[1] 1 2 3 1 2 3 4 1 1 2 

В самом деле, я думаю, что sequence предназначен именно для этой цели.

2
df$number.in.group <- unlist(lapply(as.vector(unlist(rle(df$ID)[1])), function(x) 1:x)) 
+0

Крысы Я вижу, что Йоран избил меня слишком редким решением и более эффективно –

6
> dat$number.in.group <- ave(dat$ID,dat$ID, FUN=seq_along) 
> dat 
    ID Shoot hit number.in.group 
1 1 10 2    1 
2 1  9 3    2 
3 1  8 1    3 
4 2 10 8    1 
5 2  8 8    2 
6 2 11 10    3 
7 2  7 2    4 
8 3  9 2    1 
9 4  6 6    1 
10 4  6 5    2 
1

Вот еще одно решение

require(plyr) 
ddply(dat, .(ID), transform, num_in_grp = seq_along(hit)) 
+0

Что соответствует val? – aatrujillob

+0

'val' соответствует' hit'. просмотреть отредактированный ответ – Ramnath

0

Я сравнил свои anwsers и IShouldBuyABoat является наиболее перспективно. Я обнаружил, что функция ave может применяться, даже если набор данных не сортируется в соответствии с переменной группировки.

Пусть рассмотрим набор данных:

dane<-data.frame(g1=c(-1,-2,-2,-2,-3,-3,-3,-3,-3), 
      g2=c('reg','pl','reg','woj','woj','reg','woj','woj','woj')) 

Joran anwser и применяется к моему примеру:

> sequence(rle(as.character(dane$g2))$lengths) 
[1] 1 1 1 1 2 1 1 2 3 

Саймон Urbanek пропозиции и результаты:

> unlist(lapply(table(dane$g2),seq.int)) 
    pl reg1 reg2 reg3 woj1 woj2 woj3 woj4 woj5 
    1 1 2 3 1 2 3 4 5 

IShouldBuyABoat код дает правильный anwser:

> as.numeric(ave(as.character(dane$g1),as.character(dane$g1),FUN=seq_along)) 
[1] 1 1 2 3 1 2 3 4 5 
Смежные вопросы