【问题标题】:R: (SQL style) %LIKE% statementR:(SQL 风格)%LIKE% 语句
【发布时间】:2021-03-27 15:52:06
【问题描述】:

我试图弄清楚如何在 R 编程语言中使用类似于 %LIKE% 的语句。使用以下 stackoverflow 帖子:How to join (merge) data frames (inner, outer, left, right),我能够弄清楚如何在 SQL 中运行基本合并。但由于某种原因,这不适用于 %LIKE% 条件。

例如,如果我创建以下数据:

table_a <- data.frame (

"name" = c("John", "ALex", "ToM", "Kev", "Peter"),
"color" = c("red", "blue", "green", "yellow", "pink")

)

name$table_a = as.factor(name$table_a)
    
table_b <- data.frame (

"name" = c("Johnathan", "Alexander", "Tomas", "Kevin", "Luke", "Ryan"),
"food" = c("pizza", tacos", "sushi", "cake", "brownies", "burgers")

)

name$table_b = as.factor(name$table_b)

table_c <- data.frame (

"name" = c("Johnatha", "Alexande1", "Toma1", "Kevi1", "Luk1"),
"food" = c("pizza", tacos", "sushi", "cake", "brownies")

)

name$table_c = as.factor(name$table_c)

现在我想做的是,如果 table_a 中的名称包含在 table_b 中的某个名称中,则运行“左连接”。 (使用相同的逻辑,应该也可以使用单面的 %LIKE 吗?)

#Left joins

join_1 =  merge(x = table_a, y = table_b, by = "%name%", all.x = TRUE)

join_2 =  merge(x = table_b, y = table_c, by = "%name", all.x = TRUE)

在常规 SQL 语句中,如果数据行满足 %LIKE% 指定的条件,通常可以直接选择数据行。在 R 中是否有同样的可能?

# select using %LIKE% (is there a way to override "case sensitivity" ? e.g. %like% "jOn"?)

selected_1 = table_a[name %like% "Jon"|| "Ale" || "Pet"]
selected_2 = table_a[name %like% "Jon"|| "Ale" || "Pet" || color %like% "ye"]

谢谢

【问题讨论】:

  • 您这里似乎有两个问题,一个是关于合并数据,一个是关于选择数据。对于后者,“data.table”具有%like%,但要区分大小写,您必须使用like() 版本。比如你可以library(data.table); as.data.table(table_b)[like(name, "joh|al|tom", ignore.case = TRUE) &amp; like(food, "pizza")]
  • 有没有办法做到完全匹配?例如新 = (table_b) [ (name = "john|tom|alex") $ (food = "pizza)]
  • 您能花一些时间确保您提供给我们的代码确实有效吗?示例:name$table_a 应该是 table_a$nametacos" 应该是 "tacos"。 (我相信。)它可能会分散解析问题中的拼写错误的注意力,并想知道您的问题是由于您的真实(不可见)代码中的其他拼写错误造成的,以及与其他内容相关的程度。

标签: sql r select merge data-manipulation


【解决方案1】:

我认为您可能需要使用其他 R 函数来实现 merge 无法实现的功能。 grepl 是检查是否在另一个字符串中找到一个字符串的主要函数。如果您想要其他模式,您可以使用startsWith (LIKE%) 或endsWith (%LIKE),而不是grepl

#sapply iterates over the names of table_a
table_a$name_b <- sapply(table_a$name, function(x)  {
                          #check to see if the names of table_a 
                          #are included within table_b
                          #which(...)[1] selects the first instance
                          check <- which(grepl(x, table_b$name, ignore.case = TRUE))[1]
                          #filter table_b and return what matched.
                          table_b$name[check]
                        })

输出:

# table_a
#   name  color    name_b
#1  John    red Johnathan
#2  Alex   blue Alexander
#3   Tom  green     Tomas
#4   Kev yellow     Kevin
#5 Peter   pink     <NA>
                  

【讨论】:

  • 感谢您的回复!我想没有直接的方法可以做到这一点....有没有办法将 table_b 中的“食物”列添加到您的答案中?
  • 当然!而不是table_b$name[check],您只需添加列食物,即`table_b$food[check]。
【解决方案2】:

你可以和fuzzyjoin相当接近:

library(fuzzyjoin)
regex_right_join(table_b, table_a, by = "name", ignore_case = TRUE)
# Warning: Coercing `pattern` to a plain character vector.
#      name.x  food name.y  color
# 1 Johnathan pizza   John    red
# 2 Alexander tacos   ALex   blue
# 3     Tomas sushi    ToM  green
# 4     Kevin  cake    Kev yellow
# 5      <NA>  <NA>  Peter   pink

为了明确起见,fuzzyjoin 包将在此处重命名 by= 变量。虽然这看起来不方便,但我个人认为它没有歧义,使用起来非常安全。尤其是在这里,两者通常不会相同(因为根据我们的意图,一个是用于测试另一个的“模式”)。

顺便说一句:[name %like% "Jon"|| "Ale" || "Pet"] 不会像你希望的那样做:它首先基于name %like% "Jon" 生成一个logical 向量,然后将该向量作为一个完整元素与"Ale" 进行或运算(即错误)等。您可能正在寻找的是基于正则表达式的%in%,但并非如此。此外,|| 是单元素 OR,| 是矢量化 OR,我怀疑您想要后者。

对于类似正则表达式的%in%,您需要类似:

sapply(c("Jon", "Ale", "Pet"), grepl, x = table_a$name)
#        Jon   Ale   Pet
# [1,] FALSE FALSE FALSE
# [2,] FALSE FALSE FALSE
# [3,] FALSE FALSE FALSE
# [4,] FALSE FALSE FALSE
# [5,] FALSE FALSE  TRUE

table_a[rowSums(sapply(c("Jon", "Ale", "Pet"), grepl, x = table_a$name)) > 0,]
#    name color
# 5 Peter  pink
table_a[rowSums(sapply(c("Jon", "Ale", "Pet"), grepl, x = table_a$name)) > 0 |
          grepl("ye", table_a$color),]
#    name  color
# 4   Kev yellow
# 5 Peter   pink

可以像这样变成一个小的内联运算符:

`%regexin%` <- function(lhs, rhs) rowSums(sapply(rhs, grepl, x = as.character(lhs))) > 0
`%iregexin%` <- function(lhs, rhs) rowSums(sapply(rhs, grepl, x = as.character(lhs), ignore.case = TRUE)) > 0

table_a$name %regexin% c("Jon", "Ale", "Pet")
# [1] FALSE FALSE FALSE FALSE  TRUE
subset(table_a, name %regexin% c("Jon", "Ale", "Pet"))
#    name color
# 5 Peter  pink

请注意,这对您的加入没有帮助,尽管它可能是某些相关逻辑的一部分。


数据:

table_a <- structure(list(name = structure(c(2L, 1L, 5L, 3L, 4L), .Label = c("ALex", "John", "Kev", "Peter", "ToM"), class = "factor"), color = c("red", "blue", "green", "yellow", "pink")), row.names = c(NA, -5L), class = "data.frame")
table_b <- structure(list(name = structure(c(2L, 1L, 6L, 3L, 4L, 5L), .Label = c("Alexander", "Johnathan", "Kevin", "Luke", "Ryan", "Tomas"), class = "factor"), food = c("pizza", "tacos", "sushi", "cake", "brownies", "burgers")), row.names = c(NA, -6L), class = "data.frame")
table_c <- structure(list(name = structure(c(2L, 1L, 5L, 3L, 4L), .Label = c("Alexande1", "Johnatha", "Kevi1", "Luk1", "Toma1"), class = "factor"), food = c("pizza", "tacos", "sushi", "cake", "brownies")), row.names = c(NA, -5L ), class = "data.frame")

【讨论】:

    猜你喜欢
    • 2018-01-22
    • 1970-01-01
    • 2020-12-23
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 2010-10-11
    • 1970-01-01
    • 2012-08-19
    相关资源
    最近更新 更多