在R中计算两个字符串的差异

4

我有一些R语言的数据:

string_1 = c("newyork 123", "california 123", "washington 123")
string_2 = c("123 red", "123 blue", "123 green")
my_data = data.frame(string_1, string_2)

我希望从字符串1中“减去”字符串2。结果应该类似于这样:
"newyork", "california", "washington"

我试图做到这一点:

library(tidyverse)

# did not work as planned
> str_remove(string_1, "string_2")

[1] "newyork 123"    "california 123" "washington 123"

但这并不是执行“完整”的减法。

  • 有人知道如何做吗?
  • 我应该尝试在SQL中使用ANTI JOIN来完成吗?

谢谢!


3
你需要定义在这里“减去”字符串的实际含义。如果给定输入为 newyork 123 并输出为 newyork,那么 123 red 中的子字符串 red 会发生什么? - Tim Biegeleisen
5个回答

5
你可以将两个字符串分割后找到它们的差集。
mapply(setdiff, strsplit(string_1, "\\s+"), strsplit(string_2, "\\s+"))

# [1] "newyork"    "california" "washington"

2
library(dplyr)
library(purrr)
library(stringr)

string_1 <- c("newyork 123", "california 123", "washington 123")
string_2 <- c("123 red", "123 blue", "123 green")

my_data <- data.frame(string_1, string_2)

my_data %>%
    mutate(
        subtracted = map2(
            str_split(string_1, "\\s+"),
            str_split(string_2, "\\s+"),
            ~ setdiff(.x, .y)
        ) %>% map_chr(~ paste0(.x, collapse = " "))
    )

#>         string_1  string_2 subtracted
#> 1    newyork 123   123 red    newyork
#> 2 california 123  123 blue california
#> 3 washington 123 123 green washington


如果我像@DarrenTsai建议的那样更改string_2,我们也会得到我们想要的结果。
string_1 <- c("newyork 123", "california 123", "washington 123")
string_2_test <- c("123 red", "456 blue", "789 green")

my_data <- data.frame(string_1, string_2_test)

my_data %>%
    mutate(
        subtracted = map2(
            str_split(string_1, "\\s+"),
            str_split(string_2_test, "\\s+"),
            ~ setdiff(.x, .y)
        ) %>% map_chr(~ paste0(.x, collapse = " "))
    )

#>         string_1 string_2_test     subtracted
#> 1    newyork 123       123 red        newyork
#> 2 california 123      456 blue california 123
#> 3 washington 123     789 green washington 123

reprex包 (v2.0.1) 于2022-07-07 创建


1
string_2 <- c("123 red", "456 blue", "789 green") 替换 string_2,然后再次运行您的代码。第二个和第三个 string_1 中的 "123" 不应该被删除,但是您删除了它们,对吗?这是因为 unlist() 将所有子字符串融合在一起,但是 setdiff() 应该成对地进行。 - Darren Tsai
哦,我明白了,这个解决方案是不完整的。那我需要做得更好,谢谢@DarrenTsai。 - shafee
@DarrenTsai,非常感谢您指出这一点。 - shafee

2
library(purrr)

list1 <- str_split(string_1, pattern = " ")
list2 <- str_split(string_2, pattern = " ")

a <- map2(list1, list2, function(x, y){
    
    output <- setdiff(x, y)
    return(output)
  }) %>% unlist()

map2() 函数中,unlist(x) %>% as.vector() 是多余的。只需使用 output <- setdiff(x, y) 即可。 - Darren Tsai
所以你的代码可以简化为 map2_chr(list1, list2, function(x, y) setdiff(x, y)),甚至可以简化为 map2_chr(list1, list2, ~ setdiff(.x, .y)),甚至可以简化为 map2_chr(list1, list2, setdiff)。请注意,map2_chr() 可以帮助你省去最后一个 unlist() 的使用。 - Darren Tsai

2
我们可以使用 gsub + mapply + trimws 来实现它。
> trimws(mapply(function(x, y) gsub(x, "", y), gsub("\\s+", "|", string_2), string_1))
     123|red     123|blue    123|green
   "newyork" "california" "washington"

1

使用tidyverse的另一个选择是,将string_2拆分为每一行,然后折叠成一个字符串,我们可以用它来搜索任何单词(即使用|作为“或”的符号;例如,“123”或“red”等),然后使用str_remove_all删除这些单词。接下来,我们可以使用pull命令提取带有删除内容的string_1列。

library(tidyverse)

my_data %>%
  rowwise() %>%
  mutate(string_1 = trimws(str_remove_all(string_1, str_c(
    unlist(str_split(string_2, " ")), collapse = "|")))) %>%
  pull(string_1)

输出

[1] "newyork"    "california" "washington"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接