如何基于数据框中的分组变量创建连续索引

时间:2011-05-24 15:21:13

标签: r r-faq

我有一个数据框(all_data),其中我有一个网站列表(1 ...到n)及其分数,例如。

  site  score
     1    10
     1    11  
     1    12
     4    10 
     4    11
     4    11
     8    9
     8    8
     8    7

我想创建一个按行数对每个级别的网站进行编号的列,就像一个计数器。在该示例中,站点(1,4和8)将在“数字”列中具有1到3的相应计数器:

site  score number
     1    10    1
     1    11    1 
     1    12    1 
     4    10    2
     4    11    2
     4    11    2
     8    9     3
     8    8     3 
     8    7     3

我相信这一定很容易解决,但我还没有找到办法。

9 个答案:

答案 0 :(得分:16)

尝试Data$number <- as.numeric(as.factor(Data$site))

旁注:我和@Chase的解决方案与另一方面@DWin的解决方案之间的区别在于数字的排序。 as.factorfactor都会自动对级别进行排序,而在@DWin的解决方案中则不会发生这种情况:

Dat <- data.frame(site = rep(c(1,8,4), each = 3), score = runif(9))

Dat$number <- as.numeric(factor(Dat$site))
Dat$sitenum <- match(Dat$site, unique(Dat$site) ) 

给出

> Dat
  site     score number sitenum
1    1 0.7377561      1       1
2    1 0.3131139      1       1
3    1 0.7862290      1       1
4    8 0.4480387      3       2
5    8 0.3873210      3       2
6    8 0.8778102      3       2
7    4 0.6916340      2       3
8    4 0.3033787      2       3
9    4 0.6552808      2       3

答案 1 :(得分:11)

这应该是相当有效和可理解的:

Dat$sitenum <- match(Dat$site, unique(Dat$site))  

答案 2 :(得分:11)

另外两个选择:

1)使用.GRP包中的data.table功能:

library(data.table)
setDT(dat)[, num := .GRP, by = site]

使用下面的示例数据集,结果为:

> dat
    site      score num
 1:    1 0.14945795   1
 2:    1 0.60035697   1
 3:    1 0.94643075   1
 4:    8 0.68835336   2
 5:    8 0.50553372   2
 6:    8 0.37293624   2
 7:    4 0.33580504   3
 8:    4 0.04825135   3
 9:    4 0.61894754   3
10:    8 0.96144729   2
11:    8 0.65496051   2
12:    8 0.51029199   2

2)使用group_indices中的dplyr功能:

dat$num <- group_indices(dat, site)

或当您想要解决非标准评估时:

library(dplyr)
dat %>% 
  mutate(num = group_indices_(dat, .dots = c('site')))

导致:

   site      score num
1     1 0.42480366   1
2     1 0.98736177   1
3     1 0.35766187   1
4     8 0.06243182   3
5     8 0.55617002   3
6     8 0.20304632   3
7     4 0.90855921   2
8     4 0.25215078   2
9     4 0.44981251   2
10    8 0.60288270   3
11    8 0.46946587   3
12    8 0.44941782   3

可以看出,dplyr给出了不同的组号顺序。

如果您希望每次更改组时都有其他号码,还有其他几个选项:

1),基数为R:

# option 1:
dat$num <- cumsum(c(TRUE, head(dat$site, -1) != tail(dat$site, -1)))

# option 2:
x <- rle(dat$site)$lengths
dat$num <- rep(seq_along(x), times=x)
使用data.table

2)

library(data.table)
setDT(dat)[, num := rleid(site)]

所有结果都是:

> dat
   site      score num
1     1 0.80817855   1
2     1 0.07881334   1
3     1 0.60092828   1
4     8 0.71477988   2
5     8 0.51384565   2
6     8 0.72011650   2
7     4 0.74994627   3
8     4 0.09564052   3
9     4 0.39782587   3
10    8 0.29446540   4
11    8 0.61725367   4
12    8 0.97427413   4

使用过的数据:

dat <- data.frame(site = rep(c(1,8,4,8), each = 3), score = runif(12))

答案 3 :(得分:3)

您可以将网站转换为系数,然后返回该系数的数值或整数值:

dat <- data.frame(site = rep(c(1,4,8), each = 3), score = runif(9))
dat$number <- as.integer(factor(dat$site))
dat

  site     score number
1    1 0.5305773      1
2    1 0.9367732      1
3    1 0.1831554      1
4    4 0.4068128      2
5    4 0.3438962      2
6    4 0.8123883      2
7    8 0.9122846      3
8    8 0.2949260      3
9    8 0.6771526      3

答案 4 :(得分:2)

在新的dplyr 1.0.0中,我们可以使用cur_group_id(),它为组提供唯一的数字标识符。

library(dplyr)
df %>% group_by(site) %>% mutate(number = cur_group_id())

#  site score number
#  <int> <int>  <int>
#1     1    10      1
#2     1    11      1
#3     1    12      1
#4     4    10      2
#5     4    11      2
#6     4    11      2
#7     8     9      3
#8     8     8      3
#9     8     7      3

数据

df <- structure(list(site = c(1L, 1L, 1L, 4L, 4L, 4L, 8L, 8L, 8L), 
score = c(10L, 11L, 12L, 10L, 11L, 11L, 9L, 8L, 7L)), 
class = "data.frame", row.names = c(NA, -9L))

答案 5 :(得分:1)

使用data.table软件包的另一种解决方案。

具有Jaap提供的更完整数据集的示例:

setDT(dat)[, number := frank(site, ties.method = "dense")]
dat
    site     score number
 1:    1 0.3107920      1
 2:    1 0.3640102      1
 3:    1 0.1715318      1
 4:    8 0.7247535      3
 5:    8 0.1263025      3
 6:    8 0.4657868      3
 7:    4 0.6915818      2
 8:    4 0.3558270      2
 9:    4 0.3376173      2
10:    8 0.7934963      3
11:    8 0.9641918      3
12:    8 0.9832120      3

答案 6 :(得分:1)

使用来自@Jaap的数据,使用dplyr的另一种dense_rank()可能性是:

dat %>%
 mutate(ID = dense_rank(site))

   site     score ID
1     1 0.1884490  1
2     1 0.1087422  1
3     1 0.7438149  1
4     8 0.1150771  3
5     8 0.9978203  3
6     8 0.7781222  3
7     4 0.4081830  2
8     4 0.2782333  2
9     4 0.9566959  2
10    8 0.2545320  3
11    8 0.1201062  3
12    8 0.5449901  3

或者采用类似rleid()的{​​{1}}方法,首先排列数据:

dplyr

或者以另一种类似dat %>% arrange(site) %>% mutate(ID = {ID_rleid = rle(site); rep(seq_along(ID_rleid$lengths), ID_rleid$lengths)}) site score ID 1 1 0.1884490 1 2 1 0.1087422 1 3 1 0.7438149 1 4 4 0.4081830 2 5 4 0.2782333 2 6 4 0.9566959 2 7 8 0.1150771 3 8 8 0.9978203 3 9 8 0.7781222 3 10 8 0.2545320 3 11 8 0.1201062 3 12 8 0.5449901 3 的方式rleid(),首先排列数据:

dplyr

dat %>% arrange(site) %>% mutate(ID = with(rle(site), rep(seq_along(lengths), lengths))) 相同:

base R

或使用df$ID <- with(rle(df$site), rep(seq_along(lengths), lengths)) duplicated()

cumsum()

与基数R相同:

df %>%
 mutate(ID = cumsum(!duplicated(site)))

答案 7 :(得分:0)

另一种方法。即使您对R知之甚少,我也很容易得到:

library(dplyr)
df <- data.frame('site' = c(1, 1, 1, 4, 4, 4, 8, 8, 8))
df <- mutate(df, 'number' = cumsum(site != lag(site, default=-1)))

答案 8 :(得分:0)

如果您想保留现有列并将其分配回相同的数据框...

my_df <- my_df %>%
    select(everything()) %>% 
    group_by(geo) %>% 
    mutate(geo_id = cur_group_id())

你可以像这样做多列...

my_df <- my_df %>%
    select(everything()) %>% 
    group_by(geo) %>% 
    mutate(geo_id = cur_group_id()) %>% 
    group_by(state) %>% 
    mutate(state_id = cur_group_id()) %>% 
    group_by(name) %>% 
    mutate(name_id = cur_group_id())