为数据框中的组内的行编号


163

使用类似于以下内容的数据框:

set.seed(100)  
df <- data.frame(cat = c(rep("aaa", 5), rep("bbb", 5), rep("ccc", 5)), val = runif(15))             
df <- df[order(df$cat, df$val), ]  
df  

   cat        val  
1  aaa 0.05638315  
2  aaa 0.25767250  
3  aaa 0.30776611  
4  aaa 0.46854928  
5  aaa 0.55232243  
6  bbb 0.17026205  
7  bbb 0.37032054  
8  bbb 0.48377074  
9  bbb 0.54655860  
10 bbb 0.81240262  
11 ccc 0.28035384  
12 ccc 0.39848790  
13 ccc 0.62499648  
14 ccc 0.76255108  
15 ccc 0.88216552 

我正在尝试在每个组中添加一列编号。这样做显然不使用R的能力:

 df$num <- 1  
 for (i in 2:(length(df[,1]))) {  
   if (df[i,"cat"]==df[(i-1),"cat"]) {  
     df[i,"num"]<-df[i-1,"num"]+1  
     }  
 }  
 df  

   cat        val num  
1  aaa 0.05638315   1  
2  aaa 0.25767250   2  
3  aaa 0.30776611   3  
4  aaa 0.46854928   4  
5  aaa 0.55232243   5  
6  bbb 0.17026205   1  
7  bbb 0.37032054   2  
8  bbb 0.48377074   3  
9  bbb 0.54655860   4  
10 bbb 0.81240262   5  
11 ccc 0.28035384   1  
12 ccc 0.39848790   2  
13 ccc 0.62499648   3  
14 ccc 0.76255108   4  
15 ccc 0.88216552   5  

什么是做到这一点的好方法?


1
我建议在问题标题中添加“沿水平
顺序

2
@crazysantaclaus如果那是标题,那么我不会找到我想要的东西:-(我的确是在寻找“如何对数据框中的组内的行进行编号”
Zimano

Answers:


280

使用aveddplydplyrdata.table

df$num <- ave(df$val, df$cat, FUN = seq_along)

要么:

library(plyr)
ddply(df, .(cat), mutate, id = seq_along(val))

要么:

library(dplyr)
df %>% group_by(cat) %>% mutate(id = row_number())

或(最有效的内存,由内的引用分配DT):

library(data.table)
DT <- data.table(df)

DT[, id := seq_len(.N), by = cat]
DT[, id := rowid(cat)]

2
可能值得一提的是,ave此处给出的是浮点数而不是整数。或者,可以更改df$valseq_len(nrow(df))。我在这里遇到了这个问题:stackoverflow.com/questions/42796857/…–
弗兰克(Frank)

1
有趣的是,这种data.table解决方案似乎比使用frank以下方法更快: library(microbenchmark); microbenchmark(a = DT[, .(val ,num = frank(val)), by = list(cat)] ,b =DT[, .(val , id = seq_len(.N)), by = list(cat)] , times = 1000L)
hannes101 '17

4
谢谢!该dplyr解决方案是好的。但是,如果像我一样,在尝试这种方法时不断出现奇怪的错误,请确保您之间不会发生冲突plyrdplyr正如本文中所解释的可以通过明确地致电dplyr::mutate(...)
EcologyTom

2
另一种data.table方法是setDT(df)[, id:=rleid(val), by=.(cat)]
chinsoon12 '18

如何修改library(plyr)library(dplyr)回答以使val列按降序排列?
Przemyslaw Remin,

26

为了做到这一点 问题更完整,使用sequence和的基本R替代方案rle

df$num <- sequence(rle(df$cat)$lengths)

得到预期的结果:

> df
   cat        val num
4  aaa 0.05638315   1
2  aaa 0.25767250   2
1  aaa 0.30776611   3
5  aaa 0.46854928   4
3  aaa 0.55232243   5
10 bbb 0.17026205   1
8  bbb 0.37032054   2
6  bbb 0.48377074   3
9  bbb 0.54655860   4
7  bbb 0.81240262   5
13 ccc 0.28035384   1
14 ccc 0.39848790   2
11 ccc 0.62499648   3
15 ccc 0.76255108   4
12 ccc 0.88216552   5

如果df$cat是因素变量,则需要as.character先将其包装:

df$num <- sequence(rle(as.character(df$cat))$lengths)

刚注意到,此解决方案需要对cat列进行排序?
zx8754

@ zx8754是的,除非您要按连续出现的次数进行计数cat
Jaap

9

这是一个使用for按组而不是按行循环的选项(就像OP一样)

for (i in unique(df$cat)) df$num[df$cat == i] <- seq_len(sum(df$cat == i))

9

这是一个小的改进技巧,允许在组内排序“ val”:

# 1. Data set
set.seed(100)
df <- data.frame(
  cat = c(rep("aaa", 5), rep("ccc", 5), rep("bbb", 5)), 
  val = runif(15))             

# 2. 'dplyr' approach
df %>% 
  arrange(cat, val) %>% 
  group_by(cat) %>% 
  mutate(id = row_number())

您不能按group_by排序吗?
zcoleman

6

我想data.table使用该rank()函数添加一个变体,该变体提供了更改顺序的其他可能性,因此使其比seq_len()解决方案更灵活,并且与RDBMS中的row_number函数非常相似。

# Variant with ascending ordering
library(data.table)
dt <- data.table(df)
dt[, .( val
   , num = rank(val))
    , by = list(cat)][order(cat, num),]

    cat        val num
 1: aaa 0.05638315   1
 2: aaa 0.25767250   2
 3: aaa 0.30776611   3
 4: aaa 0.46854928   4
 5: aaa 0.55232243   5
 6: bbb 0.17026205   1
 7: bbb 0.37032054   2
 8: bbb 0.48377074   3
 9: bbb 0.54655860   4
10: bbb 0.81240262   5
11: ccc 0.28035384   1
12: ccc 0.39848790   2
13: ccc 0.62499648   3
14: ccc 0.76255108   4

# Variant with descending ordering
dt[, .( val
   , num = rank(-val))
    , by = list(cat)][order(cat, num),]

5

dplyr一种可能是:

df %>%
 group_by(cat) %>%
 mutate(num = 1:n())

   cat      val   num
   <fct>  <dbl> <int>
 1 aaa   0.0564     1
 2 aaa   0.258      2
 3 aaa   0.308      3
 4 aaa   0.469      4
 5 aaa   0.552      5
 6 bbb   0.170      1
 7 bbb   0.370      2
 8 bbb   0.484      3
 9 bbb   0.547      4
10 bbb   0.812      5
11 ccc   0.280      1
12 ccc   0.398      2
13 ccc   0.625      3
14 ccc   0.763      4
15 ccc   0.882      5

3
在某些情况下,如果您在操作顺序中遇到以下情况,则1:n()使用seq_len(n())它会更安全:n()可能会返回0,因为1:0给您一个长度为2的向量,而seq_len(0)长度为0的向量,从而避免了的长度不匹配错误mutate()
Brian Stamper

0

在中使用rowid()功能data.table

> set.seed(100)  
> df <- data.frame(cat = c(rep("aaa", 5), rep("bbb", 5), rep("ccc", 5)), val = runif(15))
> df <- df[order(df$cat, df$val), ]  
> df$num <- data.table::rowid(df$cat)
> df
   cat        val num
4  aaa 0.05638315   1
2  aaa 0.25767250   2
1  aaa 0.30776611   3
5  aaa 0.46854928   4
3  aaa 0.55232243   5
10 bbb 0.17026205   1
8  bbb 0.37032054   2
6  bbb 0.48377074   3
9  bbb 0.54655860   4
7  bbb 0.81240262   5
13 ccc 0.28035384   1
14 ccc 0.39848790   2
11 ccc 0.62499648   3
15 ccc 0.76255108   4
12 ccc 0.88216552   5

1
感谢您的回答,但@mnel回答中的最后一条建议似乎已经涵盖了该问题
eli-k
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.