将一行中的项与所有其他行进行比较,并使用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