【问题标题】:Placing Commas Between Names在名称之间放置逗号
【发布时间】:2022-07-04 13:06:05
【问题描述】:

我正在尝试找出某些模式是否出现在数据框中。

假设我有以下“模式字典”(注意“james”与“jamesj”):

patterns <- c("john", "jack", "james", "jamesj", "jason")

我拥有的实际数据框(“date_frame”)如下所示:

  id                                              names
1  1                                     johnjack jameS
2  2                             john/james, jasonjames
3  3                                    peter_jackjason
4  4                                   jamesjasonj jack
5  5 jamesjjason, johnjasonjohn , jason-jack sam _ peter

我试图产生的最终结果应该是这样的:

  id                                                         names
1  1                                             john, jack, james
2  2                                     john, james, jason, james
3  3                                            peter, jack, jason
4  4                                          jamesj, asonj,  jack
5  5 jamesj, jason, john, jason, john , jason, jack,  sam ,  peter

我尝试在此处查看此帖子 (R: insert comma after each element from the output) 并尝试了那里提供的答案:

> data_frame$parsed_names = dput(data_frame$names)



  id                                                         names                                                  parsed_names
1  1                                             john, jack, james                                             john, jack, james
2  2                                     john, james, jason, james                                     john, james, jason, james
3  3                                            peter, jack, jason                                            peter, jack, jason
4  4                                          jamesj, asonj,  jack                                          jamesj, asonj,  jack
5  5 jamesj, jason, john, jason, john , jason, jack,  sam ,  peter jamesj, jason, john, jason, john , jason, jack,  sam ,  peter

但这不符合我想要的。

然后我在这里(insert commas in text string after certain words in r)尝试了这篇文章并尝试了那里提供的答案:

library(gsubfn)

data_frame$parsed_names = gsubfn("\\w+", as.list(setNames(paste0(patterns, ","), patterns)), 
  format(data_frame$names))

 data_frame
  id                                                         names                                                         parsed_names
1  1                                             john, jack, james     john,, jack,, james,                                            
2  2                                     john, james, jason, james    john,, james,, jason,, james,                                    
3  3                                            peter, jack, jason      peter, jack,, jason,                                           
4  4                                          jamesj, asonj,  jack      jamesj,, asonj,  jack,                                         
5  5 jamesj, jason, john, jason, john , jason, jack,  sam ,  peter jamesj,, jason,, john,, jason,, john, , jason,, jack,,  sam ,  peter
  • 谁能告诉我如何解决这个问题?

谢谢!

【问题讨论】:

  • 有些元素,例如'sam' 不在模式中。你想保持原样吗?
  • 是的——没错!
  • 从起始列到您想要的输出的规则是非常不清楚的。对于将来对其他人有帮助的问题,澄清您想要的转换会很有用:例如:用 , 替换特殊字符,在模式中存在的单词之间添加 , 等。
  • 为什么不将jamesjasonj 拆分为james, jason, j,因为jamesjasonpatterns 中?
  • 在您想要的输出中,您是否真的打算在最后一行的john ,sam , 中有额外的空间,或者这是一个错字?

标签: r regex text


【解决方案1】:

这是一个有点临时的答案,但它符合您的要求(patterns 向量没有变化):

library(tidyverse)
patterns <- c("john", "jack", "james", "jamesj", "jason")

data_frame %>% 
  separate_rows(names) %>% 
  mutate(name = str_split(tolower(names), paste0("(?<=(", paste0(patterns, collapse = "|"), "))"))) %>% 
  unnest(name) %>% 
  filter(nzchar(name)) %>% 
  group_by(j = cumsum(!(name == "j"))) %>% 
  summarise(name = paste(name, collapse = ""),
            id = unique(id)) %>% 
  group_by(id) %>% 
  summarise(name = toString(name))

## A tibble: 5 × 2
#     id name                                                     
#  <dbl> <chr>                                                    
#1     1 john, jack, james                                        
#2     2 john, james, jason, james                                
#3     3 peter, jack, jason                                       
#4     4 jamesj, asonj, jack                                      
#5     5 jamesj, jason, john, jason, john, jason, jack, sam, peter

上一个答案:

在模式向量中添加其他可能的名称,并重新排序向量以便jamesj 优先于james,然后您可以使用str_extract_all

library(stringr)
library(dplyr)

patterns <- c("john", "jack", "jamesj", "james", "jason", "asonj", "peter", "sam")
patterns <- patterns[order(nchar(patterns), decreasing = T)]

data_frame %>% 
  mutate(names = lapply(str_extract_all(tolower(names), paste(patterns, collapse = "|")), toString))

#  id                                                     names
#1  1                                         john, jack, james
#2  2                                 john, james, jason, james
#3  3                                        peter, jack, jason
#4  4                                       jamesj, asonj, jack
#5  5 jamesj, jason, john, jason, john, jason, jack, sam, peter

数据

data_frame <- tribble(
  ~id, ~names,
  1, "johnjack jameS",
  2, "john/james, jasonjames",
  3, "peter_jackjason",
  4, "jamesjasonj jack",
  5, "jamesjjason, johnjasonjohn , jason-jack sam _ peter"
)

【讨论】:

  • 感谢您的回答!我在我的真实框架上试过这个,它似乎没有工作。我想知道是否有其他方法可以编写可能有效的代码?但是非常感谢您的帮助!
  • 什么不起作用?你能详细说明一下为什么这个答案还不够吗?
  • 我编辑了答案以完美匹配您想要的数据框。
  • 我相信如果你将你的模式从长到短排序,你就不必为 jamesj / james 问题手动调整
  • 说得好。
【解决方案2】:

已更新以保留不在模式中的全名:

library(tidyverse)

data_frame <- tribble(
  ~id, ~names,
  1, "johnjack jameS",
  2, "john/james, jasonjames",
  3, "peter_jackjason",
  4, "jamesjasonj jack",
  5, "jamesjjason, johnjasonjohn , jason-jack sam _ peter"
)

patterns <- c("john", "jack", "jamesj", "james", "jason")

data_frame |> 
  mutate(names = map_chr(names, ~ str_to_lower(.) |> 
                           str_extract_all(str_c(c(patterns, "[a-z]{3,10}"), collapse = "|")) |> 
                           unlist() |> 
                           stringi::stri_remove_empty() |> 
                           str_c(collapse = ", "))
         )
#> # A tibble: 5 × 2
#>      id names                                                    
#>   <dbl> <chr>                                                    
#> 1     1 john, jack, james                                        
#> 2     2 john, james, jason, james                                
#> 3     3 peter, jack, jason                                       
#> 4     4 jamesj, asonj, jack                                      
#> 5     5 jamesj, jason, john, jason, john, jason, jack, sam, peter

reprex package (v2.0.1) 于 2022-05-14 创建

【讨论】:

  • 非常感谢您的回答!我的问题的重点 - 假设“彼得”这个名字不在“模式”数据框中。当“data_frame”中的名称不存在于“patterns”中时,您提供的这段代码是否仍然能够运行?谢谢!
  • 已更新以保持名称不在模式中
【解决方案3】:

目前尚不清楚您应该遵循什么规则来生成最终输出,因为似乎发生了很多事情。

这是我假设的哪些是我编码到 regular expressions 中的规则以及它们被替换为的模式(..如果我错了,请告诉我

  1. 使用正则表达式"jack(?=[:alpha:])",如果匹配的单词紧接在任何字母之前出现,则在匹配的单词之后添加, (这是针对文本中间的单词,然后是其他单词)
  2. 使用正则表达式'(?!,)[:punct:]', 以外的特殊字符替换为, 一个特殊的负前瞻,如从source 中提取的正则表达式
  3. 在单词之间的空格前添加,,因此 使用正则表达式'(?&lt;=[:alpha:]) (?=[:alpha:])' 夹在字母之间
  4. 使用正则表达式 'james(?!j)(?=[:alpha:])' 忽略 james 后跟 j 来计算 jamesj

这些正则表达式与命名向量中的替换文本配对,并传递给str_replace_all 以进行替换。

我喜欢使用命名向量方法进行文本替换,因为您可以打印向量并一眼看出将要替换的内容。

这里是完整的代码作为可重现的示例-

library(tidyverse)

# Load the data frame
# Thanks to @Mael for the code
.df <- tribble( 
  ~id, ~names,
  1, "johnjack jameS",
  2, "john/james, jasonjames",
  3, "peter_jackjason",
  4, "jamesjasonj jack",
  5, "jamesjjason, johnjasonjohn , jason-jack sam _ peter"
)

# Load the pattern to place commas after; 
# Note jamesj comes before james, which is a sub-pattern of (james)j 
patterns <- c("john", "jack", "jamesj", "james", "jason")

# Create a named vector for the string substitutions, format :  c('regex pattern' = 'replacement', ..)
sub_pattern <- setNames(object = paste0(patterns, ', '), # append comma and space
                        nm = paste0(patterns, '(?=[:alpha:])')) # for words occurring immediately before any alphabet

# Address james and jamesj double matching
names(sub_pattern) <- 
  str_replace(names(sub_pattern),
              'james(?!j).*', # replace the james matcher with
              'james(?!j)(?=[:alpha:])') # ensures james is not followed by a j 

# additional substitutions                        
sub_pattern <- append(sub_pattern,
                      c('(?!,)[:punct:]' = ', ', # replace non comma punctuations with a comma and space
                        '(?<=[:alpha:]) (?=[:alpha:])' = ', ')) # insert comma for spaces between words
                        # '[:space:],' = ',' # remove spaces before comma if needed

# Perform the string substitutions to the names column
newdf <- mutate(.df, names_with_comma = str_replace_all(tolower(names), sub_pattern))
# converting all the text to lower case (for the S in first column, if that's not a typo..)

newdf$names_with_comma
#> [1] "john, jack, james"                                           
#> [2] "john, james, jason, james"                                   
#> [3] "peter, jack, jason"                                          
#> [4] "jamesj, asonj, jack"                                         
#> [5] "jamesj, jason, john, jason, john , jason, jack, sam ,  peter"

reprex package (v2.0.1) 于 2022-05-14 创建

感谢StringR cheatsheet 对我永恒的正则表达式的支持,感谢@Maël 提供数据框的代码

【讨论】:

    【解决方案4】:

    paste|patterns 之间,并将其与tolower data_frame$names 进行比较,然后在匹配时使用gsub 在匹配的两侧添加
    使用gsub/_, - 替换为, trimws 其中空格是,

    data_frame$names <-
      trimws( gsub("[/_, -]+", ", ",
                   gsub( paste0("(", paste(patterns, collapse="|"), ")"), " \\1 ",
                        tolower(data_frame$names) )
                   )
           , whitespace = ", ")
    data_frame
    #  id                                                     names
    #1  1                                         john, jack, james
    #2  2                                 john, james, jason, james
    #3  3                                        peter, jack, jason
    #4  4                                       jamesj, asonj, jack
    #5  5 jamesj, jason, john, jason, john, jason, jack, sam, peter
    

    数据:

    patterns <- c("john", "jack", "james", "jamesj", "jason")
    
    data_frame <- data.frame(id=1:5, names = c("johnjack jameS",
      "john/james, jasonjames", "peter_jackjason", "jamesjasonj jack",
      "jamesjjason, johnjasonjohn , jason-jack sam _ peter"))
    

    【讨论】:

      【解决方案5】:

      您可以将tidyr 中的crossing()stringr 中的str_detect() 结合使用,以按ID 查找每个模式。

      names_from_pattern <- data_frame|> tidyr::crossing(patterns)  %>%
        dplyr::rowwise() %>%
        dplyr::filter(stringr::str_detect(names, patterns))|>
        dplyr::select(id, "names" = patterns)
      

      然后在模式中找到所有其他的名字

      find.string <- paste(patterns, collapse = "|") #e.g. 'or' separated 
      other_names <- tibble(id = data_frame$id, other_names = gsub(find.string, replacement = " ", x = data_frame$names)) %>%
      tidytext::unnest_tokens(., input = other_names, output = names)
      # removes remaining non-letters (e.g "peter", not "peter_")
      other_names$names<- gsub("[^a-z]","",other_names$names) 
      

      将模式中的名称与其他所有名称绑定

      df<- rbind(names_from_pattern, other_names)
      

      然后要将输出格式化为您的规范,请将 dplyr 的 pivot_wider() 与来自 tidyrunite() 结合使用。

      df <-   df |>  
      pivot_wider(id_cols = id, names_from = names, values_from = names) %>%
      unite(.,col='names', 2:length(.), sep=', ', na.rm = TRUE)
      

      输出:

      # A tibble: 5 x 2
           id names                                       
        <int> <chr>                                       
      1     1 jack, james, john                           
      2     2 james, john, jason                          
      3     3 jack, jason, peter                          
      4     4 jack, james, jason, jamesj, asonj           
      5     5 jack, james, john, jason, jamesj, peter, sam
      

      图书馆:

      library(dplyr)
      library(stringr)
      library(tidyr)
      library(tidytext)
      

      【讨论】:

        【解决方案6】:

        以最优惠的价格为英国各地的学生提供优质的assignment help 在线和一流的作业写作服务是我们的使命。当学生向他们寻求我的作业写作帮助时,我们的团队随时准备好立即提供解决方案。我们经验丰富的专业作业作家教师包括来自一流大学的前教授,并在网络上为在英国、美国、加拿大、澳大利亚、马来西亚和新的大学攻读学位的学生提供 100% 独特且精心编写的在线作业帮助新西兰。如果您也希望在学术生涯中取得成功并在成功之路上取得巨大飞跃,那么请继续以最优惠的价格利用我们的在线作业帮助英国。我们的支持团队一年 365 天、每天 24 小时在线为您提供最佳帮助,帮助您完成作业写作服务任务和我们网站上的查询。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-04-18
          • 2020-09-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多