对于数据库清理,我有一个矢量,比方说,菜肴,我想删除" base"的所有变体。菜,只保留基础菜。例如,如果我有......
dishes <- c("DAL BHAT", "DAL BHAT-(SPICY)", "DAL BHAT WITH EXTRA RICE",
"HAMBURGER", "HAMBURGER-BIG", "HAMBURGER2", "PIZZA",
"PIZZA (PROSCIUTO)", "PIZZA_BOLOGNESE")
...我想删除已经在向量中具有较短匹配版本的所有条目。因此,生成的矢量只包括:&#34; DAL BHAT&#34;,&#34; HAMBURGER,&#34; PIZZA&#34;。
使用嵌套的for
循环并针对所有其他循环检查所有内容将适用于此示例,但是对于手头的大型数据集将花费很长时间,而且我还要说是丑陋的编码。
可以假设所有条目都是大写的,并且向量已经排序。不能假设下一个基础菜的第一项总是比前一项更短。
有关如何以有效方式解决此问题的任何建议?
奖金问题:理想情况下,我只想删除初始向量中的项目,如果它们的长度至少比短对象长3个字符。在上面的例子中,这意味着&#34; HAMBURGER2&#34;也将保留在结果向量中。
答案 0 :(得分:5)
以下是我采用的方法。我创建了一个具有一些我需要考虑的条件的函数,并在输入中使用它。我已添加评论来解释函数中发生了什么。
该函数有4个参数:
invec
:输入字符向量。thresh
:我们可以使用多少个字符来确定&#34; base&#34;碟。默认= 5。minlen
:您的&#34; BONUS&#34;题。默认= 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"
这里还有一些示例数据。注意在&#34; dish2&#34;数据不再排序,并且有一个新项目&#34; DAL&#34;和&#34; dishes3&#34;你也有小写的菜肴。
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!!"
答案 1 :(得分:2)
OP已请求删除已在矢量中具有较短匹配版本的所有条目。此外,OP 希望从初始向量中删除项目,如果它们比其较短的对象至少长3个字符。
强力方法会尝试将所有条目相互比较,以查找一个字符串是否是另一个字符串的一部分。这将需要 n x(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"
由于非equi join ,这种方法也可以无预先重新排序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
的原始顺序(删除了“重复”)。
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")
请注意,已添加两个项目以涵盖其他测试用例。
答案 2 :(得分:2)
使用sapply
与grepl
和colSums
:
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
的列总和,您可以获得每个菜的名称包含的其他菜的数字向量:< / p>
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
只有较短的菜名的计数大于零。因此,将数字向量与零进行比较会返回一个逻辑向量,其中 dish 将保留。
> 0
的替代方法,您还可以在!!
前面使用双重否定符号(colSums
)。这也会选择计数不等于零的元素:dishes[!!colSums(sapply(dishes, function(x) grepl(x, setdiff(dishes, x))))]
。如果您想考虑字符长度的最大差异,则可以使用agrepl
代替grepl
,您可以使用max.distance
指定字符的最大编辑差异-parameter:
dishes[colSums(sapply(dishes, function(x) agrepl(x, setdiff(dishes, x), max.distance = 3))) > 0]
给出:
[1] "DAL BHAT" "HAMBURGER" "HAMBURGER2" "PIZZA"
答案 3 :(得分:1)
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"