将一行中的项目与所有其他行进行比较,并使用 data.table - R 遍历所有行

Posted

技术标签:

【中文标题】将一行中的项目与所有其他行进行比较,并使用 data.table - R 遍历所有行【英文标题】:Compare item in one row against all other rows and loop through all rows using data.table - R 【发布时间】:2016-04-29 20:23:34 【问题描述】:

我正在使用 stringdist() 组合相似的名称,并使用 lapply 使其工作,但运行 500k 行需要 11 个小时,我想看看 data.table 解决方案是否能更快地工作。这是一个示例,到目前为止,我尝试的解决方案是根据读数 here、here、here、here 和 here 构建的,但我并没有完全成功:

library(stringdist)
library(data.table)
data("mtcars")
mtcars$cartype <- rownames(mtcars)
mtcars$id <- seq_len(nrow(mtcars))

我目前正在使用 lapply() 循环遍历 cartype 列中的字符串,并将 cartype 名称比指定值 (.08) 更接近的行汇集在一起​​。

output <- lapply(1:length(mtcars$cartype), function(x) mtcars[which(stringdist(mtcars$cartype[x], mtcars$cartype, method ="jw", p=0.08)<.08), ])

> output[1:3]
[[1]]
              mpg cyl disp  hp drat    wt  qsec vs am gear carb       cartype id
Mazda RX4      21   6  160 110  3.9 2.620 16.46  0  1    4    4     Mazda RX4  1
Mazda RX4 Wag  21   6  160 110  3.9 2.875 17.02  0  1    4    4 Mazda RX4 Wag  2

[[2]]
              mpg cyl disp  hp drat    wt  qsec vs am gear carb       cartype id
Mazda RX4      21   6  160 110  3.9 2.620 16.46  0  1    4    4     Mazda RX4  1
Mazda RX4 Wag  21   6  160 110  3.9 2.875 17.02  0  1    4    4 Mazda RX4 Wag  2

[[3]]
            mpg cyl disp hp drat   wt  qsec vs am gear carb    cartype id
Datsun 710 22.8   4  108 93 3.85 2.32 18.61  1  1    4    1 Datsun 710  3

数据表尝试:

mtcarsdt <- as.data.table(mtcars)    
myfun <- function(x) mtcars[which(stringdist(mtcars$cartype[x], mtcars$cartype, method ="jw", p=0.08)<.08), ]

中间步骤:此代码根据我手动插入 myfun() 的行的值提取相似的名称,但它对所有行重复该值。

res <- mtcarsdt[,.(vlist = list(myfun(1))),by=id]
res$vlist[[1]] #correctly combines the 2 mazda names
res$vlist[[6]] #but it's repeated down the line

我现在正尝试使用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

现在,这很 hacky,但我发现如果我创建 cartype 列的副本并将其放在第一列中,它几乎可以工作,但必须有一种更简洁的方法来做到这一点。此外,最好将输出保留为列表形式,如上面的 lapply() 输出,因为我为该格式设置了其他后处理步骤。

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\")"         

【问题讨论】:

【参考方案1】:

您是否尝试过使用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

现在,您可能不能只针对 500k X 500k 矩阵运行上述方法,而是可以将其拆分为更小的部分(选择适合您的数据/内存大小的大小):

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

【讨论】:

我想避免使用距离矩阵方法(内存限制)和拆分数据集。拆分它可以在每个矩阵中工作,但随后在多个矩阵中识别匹配会带来额外的挑战。例如,假设 2 个名称在一个矩阵中匹配,2 个非常相似的名称在另一个矩阵中匹配。在最终数据集中将这 4 个相似的名称放在一起将是一项挑战。此外,有时一个名称会匹配另外 3 个名称,但其他名称中的一个不会与原始名称匹配,这是我可以用原始方法处理的问题,但使用多个矩阵会更难。

以上是关于将一行中的项目与所有其他行进行比较,并使用 data.table - R 遍历所有行的主要内容,如果未能解决你的问题,请参考以下文章

如何将表中的每一行与所有行进行比较?

java按行读取txt文件并与数据库表中的内容进行匹配处理(问题好像比较复杂哈)

熊猫应用功能 - 将每一行与整列进行比较

Pandas 循环数据框并将所有行与其他 DF 行进行比较并分配一个值

游标中的前一行 oracle

Kusto:将结果集中的每一行与另一个表进行比较