将一行中的项与所有其他行进行比较,并使用data.table-R循环遍历所有行
我正在使用将一行中的项与所有其他行进行比较,并使用data.table-R循环遍历所有行,r,performance,data.table,stringdist,R,Performance,Data.table,Stringdist,我正在使用stringdist()组合类似的名称,并使用lappy使其工作,但运行500k行需要11个小时,我想看看data.table解决方案是否工作得更快。下面是一个示例和我迄今为止尝试的解决方案,它是根据阅读资料、和构建的,但我还没有完全实现: library(stringdist) library(data.table) data("mtcars") mtcars$cartype <- rownames(mtcars) mtcars$id <- seq_len(nrow(mt
stringdist()
组合类似的名称,并使用lappy
使其工作,但运行500k行需要11个小时,我想看看data.table解决方案是否工作得更快。下面是一个示例和我迄今为止尝试的解决方案,它是根据阅读资料、和构建的,但我还没有完全实现:
library(stringdist)
library(data.table)
data("mtcars")
mtcars$cartype <- rownames(mtcars)
mtcars$id <- seq_len(nrow(mtcars))
数据表尝试:
mtcarsdt <- as.data.table(mtcars)
myfun <- function(x) mtcars[which(stringdist(mtcars$cartype[x], mtcars$cartype, method ="jw", p=0.08)<.08), ]
我现在正尝试使用set()
循环遍历所有行。很接近,但尽管代码似乎与第12列(cartype
)中的文本正确匹配,但它返回了第一列的值,mpg
:
for (i in 1:32) set(mtcarsdt,i ,12L, myfun(i))
> mtcarsdt
mpg cyl disp hp drat wt qsec vs am gear carb cartype id
1: 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4 c(21, 21) 1
2: 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4 c(21, 21) 2
3: 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1 22.8 3
现在,这是一个相当粗糙的方法,但是我发现如果我创建一个cartype
列的副本并将其放在第一列中,它几乎可以正常工作,但是必须有一种更干净的方法来实现这一点。另外,最好将输出保持在列表形式,如上面的lappy()
输出,因为我已经为该格式设置了其他后处理步骤
mtcars$cartypeorig <- mtcars$cartype
mtcars <- mtcars[,c(14,1:13)]
mtcarsdt <- as.data.table(mtcars)
for (i in 1:32) set(mtcarsdt,i ,13L, myfun(i))
> mtcarsdt[1:14,cartype]
[1] "c(\"Mazda RX4\", \"Mazda RX4 Wag\")"
[2] "c(\"Mazda RX4\", \"Mazda RX4 Wag\")"
[3] "Datsun 710"
[4] "Hornet 4 Drive"
[5] "Hornet Sportabout"
[6] "Valiant"
[7] "Duster 360"
[8] "c(\"Merc 240D\", \"Merc 230\", \"Merc 280\")"
[9] "c(\"Merc 240D\", \"Merc 230\", \"Merc 280\", \"Merc 280C\")"
[10] "c(\"Merc 240D\", \"Merc 230\", \"Merc 280\", \"Merc 280C\")"
[11] "c(\"Merc 230\", \"Merc 280\", \"Merc 280C\")"
[12] "c(\"Merc 450SE\", \"Merc 450SL\", \"Merc 450SLC\")"
[13] "c(\"Merc 450SE\", \"Merc 450SL\", \"Merc 450SLC\")"
[14] "c(\"Merc 450SE\", \"Merc 450SL\", \"Merc 450SLC\")"
mtcars$cartypeorig您是否尝试过使用矩阵版的stringdist
res = stringdistmatrix(mtcars$cartype, mtcars$cartype, method = 'jw', p = 0.08)
out = as.data.table(which(res < 0.08, arr.ind = T))[, .(list(mtcars[row,])), by = col]$V1
identical(out, output)
#[1] TRUE
res=stringdistmatrix(mtcars$cartype,mtcars$cartype,method='jw',p=0.08)
out=as.data.table(其中(res<0.08,arr.ind=T))[,(列表(mtcars[row,]),by=col]$V1
相同(输出、输出)
#[1] 真的
现在,您可能无法仅针对500k X 500k矩阵运行上述操作,但可以将其拆分为更小的部分(选择适合您的数据/内存大小的大小):
size=4#分成大小为4x4的碎片
#我选择了一个可整除的数字,需要做更多的工作
#如果有残留物(nrow(mtcars)=32)
setDT(mtcars)
网格=CJ(序号(mtcars)/4),序号(mtcars)/4)
指数=网格[,{
res=stringdistmatrix(mtcars[顺序((V1-1)*大小+1,(V1-1)*大小+大小),cartype],
mtcars[序号((V2-1)*尺寸+1,(V2-1)*尺寸+尺寸),cartype],
方法='jw',p=0.08)
out=as.data.table(其中(res<0.08,arr.ind=T))
如果(nrow(out)>0)
输出[,(行=(V1-1)*大小+行,列=(V2-1)*大小+列]
},by=(V1,V2)]
相同(索引[,(列表(mtcars[行])),按=列]$V1,lapply(输出,setDT))
#[1] 真的
我希望避免使用距离矩阵方法(内存限制)和分割数据集。在每个矩阵中拆分它会起作用,但是在多个矩阵中识别匹配会带来额外的挑战。例如,想象两个名称在一个矩阵中匹配,两个非常相似的名称在另一个矩阵中匹配。将这4个相似的名字放在最后的数据集中将是一个挑战。有时一个名字会和另外三个名字匹配,但其中一个不会和原来的名字匹配,这是我在最初的方法中可以处理的,但在多个矩阵中会更难处理。
mtcars$cartypeorig <- mtcars$cartype
mtcars <- mtcars[,c(14,1:13)]
mtcarsdt <- as.data.table(mtcars)
for (i in 1:32) set(mtcarsdt,i ,13L, myfun(i))
> mtcarsdt[1:14,cartype]
[1] "c(\"Mazda RX4\", \"Mazda RX4 Wag\")"
[2] "c(\"Mazda RX4\", \"Mazda RX4 Wag\")"
[3] "Datsun 710"
[4] "Hornet 4 Drive"
[5] "Hornet Sportabout"
[6] "Valiant"
[7] "Duster 360"
[8] "c(\"Merc 240D\", \"Merc 230\", \"Merc 280\")"
[9] "c(\"Merc 240D\", \"Merc 230\", \"Merc 280\", \"Merc 280C\")"
[10] "c(\"Merc 240D\", \"Merc 230\", \"Merc 280\", \"Merc 280C\")"
[11] "c(\"Merc 230\", \"Merc 280\", \"Merc 280C\")"
[12] "c(\"Merc 450SE\", \"Merc 450SL\", \"Merc 450SLC\")"
[13] "c(\"Merc 450SE\", \"Merc 450SL\", \"Merc 450SLC\")"
[14] "c(\"Merc 450SE\", \"Merc 450SL\", \"Merc 450SLC\")"
res = stringdistmatrix(mtcars$cartype, mtcars$cartype, method = 'jw', p = 0.08)
out = as.data.table(which(res < 0.08, arr.ind = T))[, .(list(mtcars[row,])), by = col]$V1
identical(out, output)
#[1] TRUE
size = 4 # dividing into pieces of size 4x4
# I picked a divisible number, a little more work will be needed
# if you have a residue (nrow(mtcars) = 32)
setDT(mtcars)
grid = CJ(seq_len(nrow(mtcars)/4), seq_len(nrow(mtcars)/4))
indices = grid[, {
res = stringdistmatrix(mtcars[seq((V1-1)*size+1, (V1-1)*size + size), cartype],
mtcars[seq((V2-1)*size+1, (V2-1)*size + size), cartype],
method = 'jw', p = 0.08)
out = as.data.table(which(res < 0.08, arr.ind = T))
if (nrow(out) > 0)
out[, .(row = (V1-1)*size+row, col = (V2-1)*size +col)]
}, by = .(V1, V2)]
identical(indices[, .(list(mtcars[row])), by = col]$V1, lapply(output, setDT))
#[1] TRUE