字符串分解
String decomposition
我需要使用 R 分解大约 7500 万个字符串。我需要做一些事情,比如创建一个术语文档矩阵,其中文档中出现的每个单词都成为矩阵中的一列,并且术语出现的任何地方,矩阵元素编码为 1.
我有:
约7500万个字符串,长度约0-100个字符;它们代表一个时间序列,提供有关该时期发生的事情的编码信息。每个代码都是一个字符,对应一个时间段。
我需要:
某种矩阵或传达信息的方式会带走时间序列,只告诉我某个代码在每个序列中被报告了多少次。
例如:
字符串 "ABCDEFG-123" 将成为矩阵中的一行,其中每个字符都被计为出现一次。如果这太难了,一个 0 和 1 的矩阵也会给我一些信息,但我更愿意保留尽可能多的信息。
有没有人知道如何快速完成此操作?有 20 个可能的代码。
示例:
my20chars = c(LETTERS[1:10], 0:9)
set.seed(1)
x = replicate(1e4, paste0(sample(c(my20chars,"-"),10, replace=TRUE), collapse=""))
一种方法:
library(data.table)
d = setDT(stack(strsplit(setNames(x,x),"")))
dcast(d[ values %in% my20chars ], ind ~ values, fun = length)
结果:
ind 0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J
1: ---8EEAD8I 0 0 0 0 0 0 0 0 2 0 1 0 0 1 2 0 0 0 1 0
2: --33B6E-32 0 0 1 3 0 0 1 0 0 0 0 1 0 0 1 0 0 0 0 0
3: --3IFBG8GI 0 0 0 1 0 0 0 0 1 0 0 1 0 0 0 1 2 0 2 0
4: --4210I8H5 1 1 1 0 1 1 0 0 1 0 0 0 0 0 0 0 0 1 1 0
5: --5H4DE9F- 0 0 0 0 1 1 0 0 0 1 0 0 0 1 1 1 0 1 0 0
---
9996: JJFJBJ24AJ 0 0 1 0 1 0 0 0 0 0 1 1 0 0 0 1 0 0 0 5
9997: JJI-J-0FGB 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 3
9998: JJJ1B54H63 0 1 0 1 1 1 1 0 0 0 0 1 0 0 0 0 0 1 0 3
9999: JJJED7A3FI 0 0 0 1 0 0 0 1 0 0 1 0 0 1 1 1 0 0 1 3
10000: JJJIF6GI13 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 1 1 0 2 3
基准:
library(microbenchmark)
nstrs = 1e5
nchars = 10
x = replicate(nstrs, paste0(sample(c(my20chars,"-"), nchars, replace=TRUE), collapse=""))
microbenchmark(
dcast = {
d = setDT(stack(strsplit(setNames(x,x),"")))
dcast(d[ values %in% my20chars ], ind ~ values, fun = length, value.var="ind")
},
times = 10)
# Unit: seconds
# expr min lq mean median uq max neval
# dcast 3.112633 3.423935 3.480692 3.494176 3.573967 3.741931 10
因此,这不足以处理 OP 的 7500 万个字符串,但可能是一个不错的起点。
我真的很喜欢@Frank 的解决方案,但这是另一种方法,它有两个优点:
它使用稀疏矩阵格式,因此您更有可能将所有内容放入内存;和
它(甚至)更简单。
它使用我们的 quanteda 包,您可以在其中对每个字符串中的字符进行标记,并从中形成一个 文档特征矩阵 命令:
my20chars = c(LETTERS[1:10], 0:9)
set.seed(1)
x = replicate(1e4, paste0(sample(c(my20chars,"-"),10, replace=TRUE), collapse=""))
require(quanteda)
myDfm <- dfm(x, what = "character", toLower = FALSE, verbose = FALSE)
# for equivalent printing, does not change content:
myDfm <- myDfm[, order(features(myDfm))]
rownames(myDfm) <- x
head(myDfm)
# Document-feature matrix of: 6 documents, 20 features.
# 6 x 20 sparse Matrix of class "dfmSparse"
# features
# docs 0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J
# FH29E8933B 0 0 1 2 0 0 0 0 1 2 0 1 0 0 1 1 0 1 0 0
# ED4I605-H6 1 0 0 0 1 1 2 0 0 0 0 0 0 1 1 0 0 1 1 0
# 9E3CFIAI8H 0 0 0 1 0 0 0 0 1 1 1 0 1 0 1 1 0 1 2 0
# 020D746C5I 2 0 1 0 1 1 1 1 0 0 0 0 1 1 0 0 0 0 1 0
# 736116A054 1 2 0 1 1 1 2 1 0 0 1 0 0 0 0 0 0 0 0 0
# 08JFBCG03I 2 0 0 1 0 0 0 0 1 0 0 1 1 0 0 1 1 0 1 1
缺点:
- 慢了(很多)。
基准:
microbenchmark(
dcast = {
d = setDT(stack(strsplit(setNames(x,x),"")))
dcast(d[ values %in% my20chars ], ind ~ values, fun = length, value.var="ind")
},
quanteda = dfm(x, what = "character", toLower = FALSE, removePunct = FALSE, verbose = FALSE),
times = 10)
# Unit: seconds
# expr min lq mean median uq max naval
# dcast 2.380971 2.423677 2.465338 2.429331 2.521256 2.636102 10
# quanteda 21.106883 21.168145 21.369443 21.345173 21.519018 21.883966 10
我需要使用 R 分解大约 7500 万个字符串。我需要做一些事情,比如创建一个术语文档矩阵,其中文档中出现的每个单词都成为矩阵中的一列,并且术语出现的任何地方,矩阵元素编码为 1.
我有: 约7500万个字符串,长度约0-100个字符;它们代表一个时间序列,提供有关该时期发生的事情的编码信息。每个代码都是一个字符,对应一个时间段。
我需要: 某种矩阵或传达信息的方式会带走时间序列,只告诉我某个代码在每个序列中被报告了多少次。
例如: 字符串 "ABCDEFG-123" 将成为矩阵中的一行,其中每个字符都被计为出现一次。如果这太难了,一个 0 和 1 的矩阵也会给我一些信息,但我更愿意保留尽可能多的信息。
有没有人知道如何快速完成此操作?有 20 个可能的代码。
示例:
my20chars = c(LETTERS[1:10], 0:9)
set.seed(1)
x = replicate(1e4, paste0(sample(c(my20chars,"-"),10, replace=TRUE), collapse=""))
一种方法:
library(data.table)
d = setDT(stack(strsplit(setNames(x,x),"")))
dcast(d[ values %in% my20chars ], ind ~ values, fun = length)
结果:
ind 0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J
1: ---8EEAD8I 0 0 0 0 0 0 0 0 2 0 1 0 0 1 2 0 0 0 1 0
2: --33B6E-32 0 0 1 3 0 0 1 0 0 0 0 1 0 0 1 0 0 0 0 0
3: --3IFBG8GI 0 0 0 1 0 0 0 0 1 0 0 1 0 0 0 1 2 0 2 0
4: --4210I8H5 1 1 1 0 1 1 0 0 1 0 0 0 0 0 0 0 0 1 1 0
5: --5H4DE9F- 0 0 0 0 1 1 0 0 0 1 0 0 0 1 1 1 0 1 0 0
---
9996: JJFJBJ24AJ 0 0 1 0 1 0 0 0 0 0 1 1 0 0 0 1 0 0 0 5
9997: JJI-J-0FGB 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 3
9998: JJJ1B54H63 0 1 0 1 1 1 1 0 0 0 0 1 0 0 0 0 0 1 0 3
9999: JJJED7A3FI 0 0 0 1 0 0 0 1 0 0 1 0 0 1 1 1 0 0 1 3
10000: JJJIF6GI13 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 1 1 0 2 3
基准:
library(microbenchmark)
nstrs = 1e5
nchars = 10
x = replicate(nstrs, paste0(sample(c(my20chars,"-"), nchars, replace=TRUE), collapse=""))
microbenchmark(
dcast = {
d = setDT(stack(strsplit(setNames(x,x),"")))
dcast(d[ values %in% my20chars ], ind ~ values, fun = length, value.var="ind")
},
times = 10)
# Unit: seconds
# expr min lq mean median uq max neval
# dcast 3.112633 3.423935 3.480692 3.494176 3.573967 3.741931 10
因此,这不足以处理 OP 的 7500 万个字符串,但可能是一个不错的起点。
我真的很喜欢@Frank 的解决方案,但这是另一种方法,它有两个优点:
它使用稀疏矩阵格式,因此您更有可能将所有内容放入内存;和
它(甚至)更简单。
它使用我们的 quanteda 包,您可以在其中对每个字符串中的字符进行标记,并从中形成一个 文档特征矩阵 命令:
my20chars = c(LETTERS[1:10], 0:9)
set.seed(1)
x = replicate(1e4, paste0(sample(c(my20chars,"-"),10, replace=TRUE), collapse=""))
require(quanteda)
myDfm <- dfm(x, what = "character", toLower = FALSE, verbose = FALSE)
# for equivalent printing, does not change content:
myDfm <- myDfm[, order(features(myDfm))]
rownames(myDfm) <- x
head(myDfm)
# Document-feature matrix of: 6 documents, 20 features.
# 6 x 20 sparse Matrix of class "dfmSparse"
# features
# docs 0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J
# FH29E8933B 0 0 1 2 0 0 0 0 1 2 0 1 0 0 1 1 0 1 0 0
# ED4I605-H6 1 0 0 0 1 1 2 0 0 0 0 0 0 1 1 0 0 1 1 0
# 9E3CFIAI8H 0 0 0 1 0 0 0 0 1 1 1 0 1 0 1 1 0 1 2 0
# 020D746C5I 2 0 1 0 1 1 1 1 0 0 0 0 1 1 0 0 0 0 1 0
# 736116A054 1 2 0 1 1 1 2 1 0 0 1 0 0 0 0 0 0 0 0 0
# 08JFBCG03I 2 0 0 1 0 0 0 0 1 0 0 1 1 0 0 1 1 0 1 1
缺点:
- 慢了(很多)。
基准:
microbenchmark(
dcast = {
d = setDT(stack(strsplit(setNames(x,x),"")))
dcast(d[ values %in% my20chars ], ind ~ values, fun = length, value.var="ind")
},
quanteda = dfm(x, what = "character", toLower = FALSE, removePunct = FALSE, verbose = FALSE),
times = 10)
# Unit: seconds
# expr min lq mean median uq max naval
# dcast 2.380971 2.423677 2.465338 2.429331 2.521256 2.636102 10
# quanteda 21.106883 21.168145 21.369443 21.345173 21.519018 21.883966 10