从矢量中删除相似但更长的重复项

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从矢量中删除相似但更长的重复项相关的知识,希望对你有一定的参考价值。

对于数据库清理,我有一个矢量,比方说,菜肴,我想删除“基础”菜的所有变种,只保留基础菜。例如,如果我有......

dishes <- c("DAL BHAT", "DAL BHAT-(SPICY)", "DAL BHAT WITH EXTRA RICE", 
            "HAMBURGER", "HAMBURGER-BIG", "HAMBURGER2", "PIZZA", 
            "PIZZA (PROSCIUTO)", "PIZZA_BOLOGNESE")

...我想删除已经在向量中具有较短匹配版本的所有条目。因此,所得到的载体仅包括:“DAL BHAT”,“HAMBURGER”,“PIZZA”。

使用嵌套的for循环并针对所有其他循环检查所有内容将适用于此示例,但是对于手头的大型数据集将花费很长时间,而且我说的是更难编码。

可以假设所有条目都是大写的,并且向量已经被排序。不能假设下一个基础菜肴的第一项总是比前一项更短。

有关如何以有效的方式解决这个问题的任何建议?

奖金问题:理想情况下,我只想删除初始向量中的项目,如果它们比较短的对应项长至少3个字符。在上述情况下,这意味着“HAMBURGER2”也将保留在结果向量中。

答案

这是我采用的方法。我创建了一个函数,其中包含一些我需要考虑的条件,并在输入中使用它。我添加了注释来解释函数中发生的事情。

该函数有4个参数:

  • invec:输入字符向量。
  • thresh:我们可以使用多少个字符来确定“基础”菜肴。默认= 5。
  • minlen:你的“奖励”问题。默认= 3。
  • strict:逻辑。如果有nchar的基础菜肴比你的thresh短,你想降低门槛还是要严格关注你所看到的基地?默认= FALSE。有关strict如何工作的信息,请参阅最后一个示例。

myfun <- function(invec, thresh = 5, minlen = 3, strict = FALSE) {
  # Bookkeeping -- sort, unique, all upper case
  invec <- sort(unique(toupper(invec)))
  # More bookkeeping -- min should not be longer 
  # than min base dish unless strict = TRUE
  thresh <- if (isTRUE(strict)) thresh else min(min(nchar(invec)), thresh)
  # Use `thresh` to get the `stubs``
  stubs <- invec[!duplicated(substr(invec, 1, thresh))]
  # loop through the stubs and do two things:
  #   - Match the dish with the stub
  #   - Return the base dish and any dishes within the minlen
  unlist(
    lapply(stubs, function(x) {
      temp <- grep(x, invec, value = TRUE, fixed = TRUE)
      temp[temp == x | nchar(temp) <= nchar(x) + minlen]
      }), 
    use.names = FALSE)
}

您的样本数据:

dishes <- c("DAL BHAT", "DAL BHAT-(SPICY)", "DAL BHAT WITH EXTRA RICE", 
            "HAMBURGER", "HAMBURGER-BIG", "HAMBURGER2", "PIZZA", 
            "PIZZA (PROSCIUTO)", "PIZZA_BOLOGNESE")    

这是结果:

myfun(dishes, minlen = 0)
# [1] "DAL BHAT"  "HAMBURGER" "PIZZA" 

myfun(dishes)
# [1] "DAL BHAT"   "HAMBURGER"  "HAMBURGER2" "PIZZA" 

这是一些更多的样本数据。请注意,在“dishes2”中,数据不再排序,并且有一个新项目“DAL”,而在“dishes3”中,您还有小写菜肴。

dishes2 <- c("DAL BHAT", "DAL BHAT-(SPICY)", "DAL BHAT WITH EXTRA RICE", 
             "HAMBURGER", "HAMBURGER-BIG", "HAMBURGER2", "PIZZA", 
             "PIZZA (PROSCIUTO)", "PIZZA_BOLOGNESE", "DAL")

dishes3 <- c("DAL BHAT", "DAL BHAT-(SPICY)", "DAL BHAT WITH EXTRA RICE", 
             "HAMBURGER", "HAMBURGER-BIG", "HAMBURGER2", "PIZZA", 
             "PIZZA (PROSCIUTO)", "PIZZA_BOLOGNESE", "DAL", "pizza!!")

这是这些向量的函数:

myfun(dishes2, 4)
# [1] "DAL"        "HAMBURGER"  "HAMBURGER2" "PIZZA"   

myfun(dishes3)
# [1] "DAL"        "HAMBURGER"  "HAMBURGER2" "PIZZA"      "PIZZA!!"  

myfun(dishes3, strict = TRUE)
# [1] "DAL"        "DAL BHAT"   "HAMBURGER"  "HAMBURGER2" "PIZZA"      "PIZZA!!"  
另一答案

OP已请求删除已在向量中具有较短匹配版本的所有条目。此外,OP希望从初始向量中删除项目,如果它们比其较短的对应项长至少3个字符。

强力方法会尝试将所有条目相互比较,以查找一个字符串是否是另一个字符串的一部分。这将需要n×(n-1)次比较。

下面的方法试图通过事先检查字符数来减少字符串比较的次数。这至少会减少对grepl()的调用次数。

library(data.table)
# prepare data
DT <- data.table(dish = dishes)[, len := nchar(dish)][order(len)]
DT
                        dish len
 1:                      NAN   3
 2:                    PIZZA   5
 3:                 DAL BHAT   8
 4:                HAMBURGER   9
 5:               HAMBURGER2  10
 6:            HAMBURGER-BIG  13
 7:           SLICE OF PIZZA  14
 8:          PIZZA_BOLOGNESE  15
 9:         DAL BHAT-(SPICY)  16
10:        PIZZA (PROSCIUTO)  17
11: DAL BHAT WITH EXTRA RICE  24
# use non-equi join to find row numbers of "duplicate" entries
tmp <- DT[.(len + 3L, dish), on = .(len > V1), nomatch = 0L, allow = TRUE,
          by = .EACHI, .I[grepl(V2, dish)]]
tmp
   len V1
1:   8  7
2:   8  8
3:   8 10
4:  11  9
5:  11 11
6:  12  6
# anti-join to remove "duplicates"
DT[!tmp$V1, dish]
[1] "NAN"        "PIZZA"      "DAL BHAT"   "HAMBURGER"  "HAMBURGER2"

Edit

由于非equi连接,这种方法也可以预先重新排序DT

delta_len <- 3L
DT <- data.table(dish = dishes)[, len := nchar(dish)]
DT[!DT[.(len + delta_len, dish), on = .(len > V1), nomatch = 0L, allow = TRUE,
       by = .EACHI, .I[grepl(V2, dish)]]$V1, dish]
[1] "DAL BHAT"   "HAMBURGER"  "HAMBURGER2" "PIZZA"      "NAN"

这样做的好处是保持了dishes的原始顺序(删除了“重复”)。

Data

dishes <- c("DAL BHAT", "DAL BHAT-(SPICY)", "DAL BHAT WITH EXTRA RICE", 
            "HAMBURGER", "HAMBURGER-BIG", "HAMBURGER2", "PIZZA", 
            "PIZZA (PROSCIUTO)", "PIZZA_BOLOGNESE", "NAN", "SLICE OF PIZZA")

请注意,已添加两个项目以涵盖其他测试用例。

另一答案

使用sapplygreplcolSums的可能解决方案:

dishes[colSums(sapply(dishes, function(x) grepl(x, setdiff(dishes, x)))) > 0]

这使:

[1] "DAL BHAT"  "HAMBURGER" "PIZZA"

这是做什么的:

  • sapply(dishes, function(x) grepl(x, setdiff(dishes, x)))dishes的每个元素与其他元素进行比较,并与grepl查看特定元素是否是其他元素的一部分。
  • 这将返回一个逻辑矩阵,其中TRUE值指示菜名是否是另一个菜名的一部分: DAL BHAT DAL BHAT-(SPICY) DAL BHAT WITH EXTRA RICE HAMBURGER HAMBURGER-BIG HAMBURGER2 PIZZA PIZZA (PROSCIUTO) PIZZA_BOLOGNESE [1,] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE [2,] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE [3,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE [4,] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE [5,] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE [6,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE [7,] FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE [8,] FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE
  • 通过使用colSums的列总和,您可以获得每个菜肴名称包含的其他菜肴的数字向量: DAL BHAT DAL BHAT-(SPICY) DAL BHAT WITH EXTRA RICE HAMBURGER HAMBURGER-BIG HAMBURGER2 PIZZA PIZZA (PROSCIUTO) PIZZA_BOLOGNESE 2 0 0 2 0 0 2 0 0
  • 只有较短的菜肴名称的计数大于零。因此,将数字向量与零进行比较将返回要保留的菜肴的逻辑向量。
  • 作为使用> 0的替代方法,您还可以在!!前使用双重否定符号(colSums)。这也选择了计数不等于零的元素:dishes[!!colSums(sapply(dishes, function(x) grepl(x, setdiff(dishes, x))))]

如果要考虑字符长度的最大差异,则可以使用agrepl而不是grepl,您可以使用max.distance参数指定字符的最大编辑差异:

dishes[colSums(sapply(dishes, function(x) agrepl(x, setdiff(dishes, x), max.distance = 3))) > 0]

这使:

[1] "DAL BHAT"   "HAMBURGER"  "HAMBURGER2" "PIZZA"
另一答案
unlist(sapply(split(dishes, substr(dishes, 1, 5)), function(x){
    N = nchar(x)
    x[(N - N[1]) < 3]
}))
#       DAL B       HAMBU1       HAMBU2        PIZZA 
#  "DAL BHAT"  "HAMBURGER" "HAMBURGER2"      "PIZZA"

以上是关于从矢量中删除相似但更长的重复项的主要内容,如果未能解决你的问题,请参考以下文章

为什么g ++需要更长的时间来编译 用-std = c ++ 11?

将文本片段扩展为更长的文本块

openmp 在 C 中为 Black Scholes 算法花费更长的时间

使用python正则表达式在字符串中搜索长度为6或更长的特定重复(mnr)

MySQL:如何获得视图的模块化,但优化更长的代码?

从 LEFT OUTER JOIN 中删除重复项