如何高效地为大型数据框添加订单变量

3

我有一个类似于以下数据框的数据,但是有55,000个观测值和大约50,000个组:

d <- structure(list(a = structure(c(1L, 1L, 1L, 2L, 2L, 3L, 3L), .Label = c("A", 
"B", "C"), class = "factor"), b = c(1, 1, 2, 1, 2, 1, 2)), .Names = c("a", 
"b"), row.names = c(1L, 3L, 2L, 4L, 5L, 6L, 7L), class = "data.frame")

由于在这个数据框中,每个组又根据变量"b"排序。我现在想根据分组变量"a"拆分数据框,并添加一个向量,指示每个子数据框中每个元素的排序号码。因此,结果应该如下所示:

structure(list(a = structure(c(1L, 1L, 1L, 2L, 2L, 3L, 3L), .Label = c("A", 
"B", "C"), class = "factor"), b = c(1, 2, 1, 1, 2, 1, 2), order = c(1, 
2, 3, 1, 2, 1, 2)), .Names = c("a", "b", "order"), row.names = c("1", 
"2", "3", "4", "5", "6", "7"), class = "data.frame")

使用split()函数和我的自定义gmark()函数,我能够在测试数据框上得到这个结果(gmark()假设输入已经排序):

gmark <- function(input){
  x = 0
  result = vector()
  for(i in input){
    x <- x+1
    result <- append(result, x)
  }
  result
}

x <- split(d, d$a)
x <- lapply(x, function(x){cbind(x, order = gmark(x$b))})
x <- unsplit(x, a)

然而,一旦我将此应用于更大的数据框时,split()的速度会变得非常缓慢,并且无法返回结果。有没有更高效的方法在更大的数据框上获得这个结果?


如果您知道result将是一个长度为50k的向量,那么将其初始化为确切大小的数字向量将使for循环更快。例如,使用result <- numeric(5e4)而不是result <- vector()进行初始化。 - Rich Scriven
谢谢。然而,我的问题不在于gmark()函数,因为每个组大约只有1到6个观测值。不幸的是,split()正在阻碍我的工作流程。 - fr3d-5
这样怎么样:ta <- as.character(d$a); unlist(sapply(rle(ta)$lengths,seq.int)) - Bhas
2个回答

2
这里有一个使用data.table包的解决方案。这将会更快速。
require(data.table)
DT <- as.data.table(DF)
DT[, order := 1:.N, by=a]
> DT
   a b order
1: A 1     1
2: A 2     2
3: A 1     3
4: B 1     1
5: B 2     2
6: C 1     1
7: C 2     2

:= 是一种 data.table 操作符,它通过引用添加列(意味着不会复制数据)。而 .N 是一个特殊变量,它包含每个组的长度(这里将分别是 A,B,C 组的长度为 3,2,2)。


我假设在执行此操作之前,原始数据必须进行排序,对吗? - fr3d-5
如果您想按 a,b 排序后得到顺序,则在最后一行之前执行 setkey(DT, a,b) - Arun
是的,对于已排序的输入,它可以正常工作。我还将尝试setkey()。看起来data.table包具有相当多的不错功能,这可能会让我的生活更轻松。谢谢! - fr3d-5

1

你的代码无法完全运行。尝试这样做(倒数第二行会有所不同):

d <- data.frame(
    a = sample(LETTERS[1:5],5e4,replace=TRUE),
    b = sample(letters[1:10],5e4,replace=TRUE)) 
x <- split(d,d$a)
y <- lapply(x, function(x){cbind(x, order = 1:nrow(x))})
z <- unsplit(y,d$a)

谢谢查看!我的代码出了什么错误?这些示例以及函数定义和函数调用都是在测试示例设置后从R中复制的。我将它们从问题文本中复制回来,它们在我的环境中可以工作。此外,我描述的拆分问题仍然存在。 - fr3d-5
尝试在新的R环境中运行代码,或者在rm(list = ls())之后运行。您在未定义变量a上进行了split()操作 - 定义的只有d $ a。实际上,也没有定义d。您在您的环境中拥有这两个,但我们没有,因此您的代码无法完全再现。(尽管它比这里的许多其他问题要好得多;-) split()问题可能仍然存在,因为您有很多因子级别,这是可能的吗? - Stephan Kolassa
我明白了。我根据您的评论更新了问题。是的,正如提到的那样,因子水平的数量约为50,000个。 - fr3d-5

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接