作为另一个函数(摘要)的新数据框列对我不起作用
New dataframe column as function (digest) of another one is not working for me
我想创建一个新的计算列(另一列文本的摘要)。为了让您重现,我创建了一个 df 作为可重现的示例:
df <- data.frame(name = replicate(1000, paste(sample(LETTERS, 20, replace=TRUE), collapse="")),stringsAsFactors=FALSE)
> head(df,3)
name
1 ZKBOZVFKNJBRSDWTUEYR
2 RQPHUECABPQZLKZPTFLG
3 FTBVBEQTRLLUGUVHDKAY
现在我想要第二列,其中每一行的 'name' 列摘要
这个很好用但是很慢(每个md5都不一样,都是name列对应的摘要):
> df$md5 <- sapply(df$name, digest)
> head(df, 3)
name md5
1 ZKBOZVFKNJBRSDWTUEYR b8d93a9fe6cefb7a856e79f54bac01f2
2 RQPHUECABPQZLKZPTFLG 52f6acbd939df27e92232904ce094053
3 FTBVBEQTRLLUGUVHDKAY a401a8bc18f0cb367435b77afd353078
但这(使用 dplyr)不起作用,我不明白为什么:每一行的 md5 都相同!事实上,它是完整 df$name 的摘要,包括所有行。请问有人可以给我解释一下吗?
> df <- mutate(df, md5=digest(name))
> head(df, 3)
name md5
1 ZKBOZVFKNJBRSDWTUEYR 10aa31791d0b9288e819763d9a41efd8
2 RQPHUECABPQZLKZPTFLG 10aa31791d0b9288e819763d9a41efd8
3 FTBVBEQTRLLUGUVHDKAY 10aa31791d0b9288e819763d9a41efd8
同样,如果我采用数据 table 方式,使用新变量的标准方式似乎不起作用:
> dt <- data.table(df)
> dt[, md5:=digest(name)]
> head(dt,3)
name md5
1: ZKBOZVFKNJBRSDWTUEYR 10aa31791d0b9288e819763d9a41efd8
2: RQPHUECABPQZLKZPTFLG 10aa31791d0b9288e819763d9a41efd8
3: FTBVBEQTRLLUGUVHDKAY 10aa31791d0b9288e819763d9a41efd8
如果我强制分组然后它再次工作(但很慢):
> dt[,md5:=digest(name), by=name]
> head(dt, 3)
name md5
1: ZKBOZVFKNJBRSDWTUEYR b8d93a9fe6cefb7a856e79f54bac01f2
2: RQPHUECABPQZLKZPTFLG 52f6acbd939df27e92232904ce094053
3: FTBVBEQTRLLUGUVHDKAY a401a8bc18f0cb367435b77afd353078
我也测试了 tapply 和工作(创建一个因素,但我的真实数据有数百万行,而且速度非常慢)。
然后,首先,有人可以向我解释为什么 dplyr mutate 没有采用每一行的值来计算摘要,以及为什么数据 table 符号会发生同样的想法(除非我分组)?
其次,是否有更快的方法计算所有行的摘要?
您获得相同 md5 值的原因是 digest
函数不是矢量化函数。要解决此问题,请将 rowwise
放在 mutate 之前,如:
df <- data.frame(name = replicate(1000, paste(sample(LETTERS, 20, replace=TRUE), collapse="")),stringsAsFactors=FALSE)
ptm <- proc.time()
df %>% rowwise() %>% mutate(md5=digest(name)) %>% print(n=3)
1 SSYNAIPPMBNICTXCTZMH cf06eaeab2a4b1b3f0fb964e91867702
2 XAFNBFYOXSDIFSSCGKKX 28cb7f90ac14f4a2ee5743a1dce91ac7
3 TMWBHOHWVDSRUPBGKYGS a248a7eb31657555b2bf8b463b7e3ce3
.. ... ...
proc.time() - ptm
user system elapsed
0.09 0.00 0.09
至于速度,你看我的桌面只用了1/10秒
考虑到您有一个非常大的数据集,最好在更大的数据集上测试不同的方法(对于这个例子,我使用 100000 行,更大的数据集在我的系统上需要很长时间):
df <- data.frame(name = replicate(1e5, paste(sample(LETTERS, 20, replace=TRUE), collapse="")), stringsAsFactors=FALSE)
首先,让我们考虑几种可用的方法:
# base R
df$md5 <- sapply(df$name, digest)
# data.table (grouping by name, based on the assumption that all names are unique)
dt[, md5:=digest(name), name]
# data.table with a unique identifier for each row
dt[,indx:=.I][, md5:=digest(name), indx]
# dplyr (grouping by name, based on the assumption that all names are unique)
df %>% group_by(name) %>% mutate(md5=digest(name))
# dplyr with rowwise (from the other answer)
df %>% rowwise() %>% mutate(md5=digest(name))
其次,测试哪种方法最快:
library(rbenchmark)
benchmark(replications = 10, order = "elapsed", columns = c("test", "elapsed", "relative"),
baseR = df$md5 <- sapply(df$name, digest),
dtbl1 = dt[, md5:=digest(name), name],
dtbl2 = dt[,indx:=.I][, md5:=digest(name), indx],
dplyr = df %>% group_by(name) %>% mutate(md5=digest(name)),
rowwi = df %>% rowwise() %>% mutate(md5=digest(name)))
给出:
test elapsed relative
2 dtbl1 77.878 1.000
3 dtbl2 78.343 1.006
1 baseR 81.399 1.045
5 rowwi 118.799 1.525
4 dplyr 129.748 1.666
因此,坚持使用基本的 R 解决方案并不是一个糟糕的选择。我怀疑它在您的真实数据集上运行缓慢的原因可能是 digest
函数,而不是某个 package/function.
的某些不当行为
我想创建一个新的计算列(另一列文本的摘要)。为了让您重现,我创建了一个 df 作为可重现的示例:
df <- data.frame(name = replicate(1000, paste(sample(LETTERS, 20, replace=TRUE), collapse="")),stringsAsFactors=FALSE)
> head(df,3)
name
1 ZKBOZVFKNJBRSDWTUEYR
2 RQPHUECABPQZLKZPTFLG
3 FTBVBEQTRLLUGUVHDKAY
现在我想要第二列,其中每一行的 'name' 列摘要 这个很好用但是很慢(每个md5都不一样,都是name列对应的摘要):
> df$md5 <- sapply(df$name, digest)
> head(df, 3)
name md5
1 ZKBOZVFKNJBRSDWTUEYR b8d93a9fe6cefb7a856e79f54bac01f2
2 RQPHUECABPQZLKZPTFLG 52f6acbd939df27e92232904ce094053
3 FTBVBEQTRLLUGUVHDKAY a401a8bc18f0cb367435b77afd353078
但这(使用 dplyr)不起作用,我不明白为什么:每一行的 md5 都相同!事实上,它是完整 df$name 的摘要,包括所有行。请问有人可以给我解释一下吗?
> df <- mutate(df, md5=digest(name))
> head(df, 3)
name md5
1 ZKBOZVFKNJBRSDWTUEYR 10aa31791d0b9288e819763d9a41efd8
2 RQPHUECABPQZLKZPTFLG 10aa31791d0b9288e819763d9a41efd8
3 FTBVBEQTRLLUGUVHDKAY 10aa31791d0b9288e819763d9a41efd8
同样,如果我采用数据 table 方式,使用新变量的标准方式似乎不起作用:
> dt <- data.table(df)
> dt[, md5:=digest(name)]
> head(dt,3)
name md5
1: ZKBOZVFKNJBRSDWTUEYR 10aa31791d0b9288e819763d9a41efd8
2: RQPHUECABPQZLKZPTFLG 10aa31791d0b9288e819763d9a41efd8
3: FTBVBEQTRLLUGUVHDKAY 10aa31791d0b9288e819763d9a41efd8
如果我强制分组然后它再次工作(但很慢):
> dt[,md5:=digest(name), by=name]
> head(dt, 3)
name md5
1: ZKBOZVFKNJBRSDWTUEYR b8d93a9fe6cefb7a856e79f54bac01f2
2: RQPHUECABPQZLKZPTFLG 52f6acbd939df27e92232904ce094053
3: FTBVBEQTRLLUGUVHDKAY a401a8bc18f0cb367435b77afd353078
我也测试了 tapply 和工作(创建一个因素,但我的真实数据有数百万行,而且速度非常慢)。
然后,首先,有人可以向我解释为什么 dplyr mutate 没有采用每一行的值来计算摘要,以及为什么数据 table 符号会发生同样的想法(除非我分组)?
其次,是否有更快的方法计算所有行的摘要?
您获得相同 md5 值的原因是 digest
函数不是矢量化函数。要解决此问题,请将 rowwise
放在 mutate 之前,如:
df <- data.frame(name = replicate(1000, paste(sample(LETTERS, 20, replace=TRUE), collapse="")),stringsAsFactors=FALSE)
ptm <- proc.time()
df %>% rowwise() %>% mutate(md5=digest(name)) %>% print(n=3)
1 SSYNAIPPMBNICTXCTZMH cf06eaeab2a4b1b3f0fb964e91867702
2 XAFNBFYOXSDIFSSCGKKX 28cb7f90ac14f4a2ee5743a1dce91ac7
3 TMWBHOHWVDSRUPBGKYGS a248a7eb31657555b2bf8b463b7e3ce3
.. ... ...
proc.time() - ptm
user system elapsed
0.09 0.00 0.09
至于速度,你看我的桌面只用了1/10秒
考虑到您有一个非常大的数据集,最好在更大的数据集上测试不同的方法(对于这个例子,我使用 100000 行,更大的数据集在我的系统上需要很长时间):
df <- data.frame(name = replicate(1e5, paste(sample(LETTERS, 20, replace=TRUE), collapse="")), stringsAsFactors=FALSE)
首先,让我们考虑几种可用的方法:
# base R
df$md5 <- sapply(df$name, digest)
# data.table (grouping by name, based on the assumption that all names are unique)
dt[, md5:=digest(name), name]
# data.table with a unique identifier for each row
dt[,indx:=.I][, md5:=digest(name), indx]
# dplyr (grouping by name, based on the assumption that all names are unique)
df %>% group_by(name) %>% mutate(md5=digest(name))
# dplyr with rowwise (from the other answer)
df %>% rowwise() %>% mutate(md5=digest(name))
其次,测试哪种方法最快:
library(rbenchmark)
benchmark(replications = 10, order = "elapsed", columns = c("test", "elapsed", "relative"),
baseR = df$md5 <- sapply(df$name, digest),
dtbl1 = dt[, md5:=digest(name), name],
dtbl2 = dt[,indx:=.I][, md5:=digest(name), indx],
dplyr = df %>% group_by(name) %>% mutate(md5=digest(name)),
rowwi = df %>% rowwise() %>% mutate(md5=digest(name)))
给出:
test elapsed relative
2 dtbl1 77.878 1.000
3 dtbl2 78.343 1.006
1 baseR 81.399 1.045
5 rowwi 118.799 1.525
4 dplyr 129.748 1.666
因此,坚持使用基本的 R 解决方案并不是一个糟糕的选择。我怀疑它在您的真实数据集上运行缓慢的原因可能是 digest
函数,而不是某个 package/function.