使用 data.table 创建组

Creating groups using data.table

工作数据集如下所示:

library('data.table')
df <- data.table(Name = c("a","a","b","b","c","c","d","d","e","e","f","f"),
                 Y = sample(1:30,12),
                 X = sample(1:30,12))

df
    Name  Y  X
 1:    a 14 23
 2:    a 19 18
 3:    b 10 16
 4:    b 23 11
 5:    c  2 12
 6:    c 12 24
 7:    d  8 14
 8:    d 26  2
 9:    e 16 26
10:    e  6  4
11:    f 29 28
12:    f 28 30

我最终想要的是按组制作图表(基于Name)进行比较:

library(ggplot2)
ggplot(df, aes(X, Y)) + geom_point() + facet_grid(. ~ Name)

由于实际数据集包含更多的观察结果和 grp。我正在创建的 ggplot 需要太多时间来处理,最终图表不可读 (grp > 300)。我计划用有限数量的观察结果对数据进行重新分组,并分别绘制图表(例如,每次绘制 10 组图表)。

所以最终的数据集应该是这样的:

    Name  Y  X grp level
 1:    a 14 23   1     1
 2:    a 19 18   1     1
 3:    b 10 16   2     1
 4:    b 23 11   2     1
 5:    c  2 12   3     1
 6:    c 12 24   3     1
 7:    d  8 14   4     2
 8:    d 26  2   4     2
 9:    e 16 26   5     2
10:    e  6  4   5     2
11:    f 29 28   6     2
12:    f 28 30   6     2

然后我可以根据新组进行绘图 level:

ggplot(df, aes(X, Y)) + geom_point() + facet_grid(. ~ level)

在上图中,我简单地创建了 grp

df[, grp := .GRP, by = Name]

现在的问题是如何根据grp自动创建level组(我必须创建grp而不是直接使用Name作为基础,因为,在原始数据集中,Name) 中没有模式?

我试过类似的方法:

setkey(df, grp)
i <- 1
j <- 1
while(i < 4 ) {
  df[levels(factor(grp)) == (i:i+2), level := j]
  i <- i + 2
  j <- j + 1
}

它不能很好地满足我的需要。谁能给我一些建议如何解决这个问题?我真的被困在这里了。我想可能有一种简单的方法可以做到这一点,也许我什至不需要创建 level 组并且可以通过其他方式直接创建单独的图形?

如果只有几个组,可以使用forcats包中的fct_collapse()函数。它允许轻松地将因子级别折叠到手动定义的组中。

这样就可以直接创建新变量level,不用绕组号和cut()了。并且,可以为级别分配有意义的标签。

library('data.table')
df <- data.table(Name = rep(letters[1:6], each = 2),
                 Y = sample(1:30,12),
                 X = sample(1:30,12))
df[, level := forcats::fct_collapse(Name, "a-c" = letters[1:3], "d-e" = letters[4:6])]
df
#    Name  Y  X level
# 1:    a 11 13   a-c
# 2:    a 29 12   a-c
# 3:    b 16  5   a-c
# 4:    b 12  6   a-c
# 5:    c 25 28   a-c
# 6:    c 27 11   a-c
# 7:    d  5  9   d-e
# 8:    d 23 20   d-e
# 9:    e 13 26   d-e
#10:    e 17 19   d-e
#11:    f 19  8   d-e
#12:    f 22  3   d-e

但是,OP提到有很多组 (df[, uniqueN(Name)] > 300) 并且他希望 重新分组具有有限数量观察的数据 。按照中建议的方式使用cut()可能会导致不理想的结果。

为了证明这一点,我们需要创建一个更大的 100 行样本数据集:

N <- 100
set.seed(1234)
df <- data.table(Name = sample(letters, N, replace = TRUE),
                 Y = sample(seq.int(3*N), N),
                 X = sample(seq.int(3*N), N))
df

请注意,set.seed() 用于使数据可重现。

现在,Name(对应于 OP 的 grp)的唯一值的数量被分成 6 个级别并绘制在方面(在 之后):

n_lvls <- 6
df[, level := as.numeric(cut(as.integer(factor(Name)), breaks = n_lvls))] 
ggplot(df, aes(X, Y)) + geom_point() + facet_grid(. ~ level)

在这里,方面 3 仅包含几个数据点,而其他方面显得非常拥挤。


为了克服这个问题,可以安排水平包含大约 相同数量的数据点 而不是相同数量的因子水平:

lvls <- df[, .N, by = Name][order(-N), level := cut(cumsum(N), n_lvls, labels = FALSE)]
df <- lvls[df, on = "Name"]

ggplot(df, aes(X, Y)) + geom_point() + facet_grid(. ~ level)

现在,观察值在各个方面的分布更加均匀。

代码计算每个 Name 的观察次数,按 N 的降序排序,使用 cut() 对观察的累积总和创建 data.table lvls 个新关卡。最后,将新的级别与原始数据集 df.

右连接