2

我有三个数据帧,我想在第一个数据帧中添加一些列,它计算第一个数据帧中的前两列出现在其他数据帧中的次数,例如

数据框 - x
ab
1 1
1 2
2 1
2 2

数据框 - y
ab
1 1
1 1
1 2
2 2
2 2

数据框 - z
ab
1 2
2 1
2 1
2 2

所以第一个数据帧会变成
abyz
1 1 2 0
1 2 1 1
2 1 0 2
2 2 2 1

我有办法做到这一点,例如我目前正在做

x$y<- sapply(1:nrow(x), function(i){
    sum(y$a == x$a[i] & y$b == x$b[i])
  }

x$z<- sapply(1:nrow(x), function(i){
    sum(z$a == x$a[i] & z$b == x$b[i])
  }

但是我的数据框非常大,我的方法需要一段时间才能完成,所以我想知道最快的方法来做到这一点。

请询问是否有任何不清楚的地方。

提前致谢

4

3 回答 3

3

为了避免双重循环,我将使用函数 match,该函数针对在另一个列表中查找元素进行了优化。要计算有多少元素,我建议先将变量制成表格,然后与表格进行匹配。

我的猜测是它会显着降低时间复杂度,因为您提出的方法是二次的(一个循环遍历 x 行,每个内部循环遍历 y 行)而函数 match 和 table 基于排序(我认为) 是 n*log(n)。

我们首先使用粘贴将数据帧转换为向量,取自 Josh 的回答:

# Recreate your data
x <- data.frame(a=c(1,1,2,2), b=c(1,2,1,2))
y <- data.frame(a=c(1,1,1,2,2), b=c(1,1,2,2,2))
z <- data.frame(a=c(1,2,2,2), b=c(2,1,1,2))

# Use paste to combine the two columns
X <- do.call(paste, c(x, sep="_"))
Y <- do.call(paste, c(y, sep="_"))
Z <- do.call(paste, c(z, sep="_"))

然后我们制表并与制表相匹配。

x$y <- table(Y)[match(X, names(table(Y)))]
x$y[is.na(x$y)] <- 0

x$z <- table(Z)[match(X, names(table(Z)))]
x$z[is.na(x$z)] <- 0

x  
a b y z
1 1 1 2 0
2 1 2 1 1
3 2 1 0 2
4 2 2 2 1

如果要避免两次制表,可以将 table(Y) 放在中间变量中。

于 2012-05-23T14:39:24.450 回答
2

你说你的数据框非常大,所以这是data.table这样的:

> require(data.table)
> x <- data.table(a=c(1,1,2,2), b=c(1,2,1,2))
> y <- data.table(a=c(1,1,1,2,2), b=c(1,1,2,2,2))
> z <- data.table(a=c(1,2,2,2), b=c(2,1,1,2)) 
> 
> setkey(x,a,b)    # sort and mark as sorted by a,b
> setkey(y,a,b)    # same for y
> setkey(z,a,b)    # same for z
> x[,y:=y[x,.N][[3]]]  
       # join to y from x, using the key.
       # .N = number of matching rows
       # := means assign by reference back to column y in x, no copy at all
       # [[3]] can be understood by running `y[x,.N]` on its own
     a b y
[1,] 1 1 2
[2,] 1 2 1
[3,] 2 1 0
[4,] 2 2 2
> x[,z:=z[x,.N][[3]]]   # same for z
     a b y z
[1,] 1 1 2 0     # bug in v1.8.0 gave z=1 on this row, fixed in v1.8.1
[2,] 1 2 1 1
[3,] 2 1 0 2
[4,] 2 2 2 1

这根本不会复制大对象,即使是一次。它们越大,可能就越重要。

于 2012-05-25T11:50:39.883 回答
2

这可能会更快:

# Recreate your data
x <- data.frame(a=c(1,1,2,2), b=c(1,2,1,2))
y <- data.frame(a=c(1,1,1,2,2), b=c(1,1,2,2,2))
z <- data.frame(a=c(1,2,2,2), b=c(2,1,1,2))

# Use paste to combine the two columns in each data.frame
X <- do.call(paste, c(x, sep="-"))
Y <- do.call(paste, c(y, sep="-"))
Z <- do.call(paste, c(z, sep="-"))

# Count number of times each element of X appears in Y and Z
x$y <- sapply(X, function(string) sum(string==Y))
x$z <- sapply(X, function(string) sum(string==Z))
x
#   a b y z
# 1 1 1 2 0
# 2 1 2 1 1
# 3 2 1 0 2
# 4 2 2 2 1
于 2012-05-23T14:11:15.033 回答