【问题标题】:how to combine two regex with if else using stringr如何使用 stringr 将两个正则表达式与 if else 结合起来
【发布时间】:2019-12-11 23:52:43
【问题描述】:

问题: 大家好,我有这个示例数据框,其中包含我需要提取的机构名称:

mydf<- data.frame(ID=c('1', '2', '3'), Institution=c('Univ of Space, TX, US', '[Bloggs, J., Smith, T.] Univ of Time, CA, US', '[Windz, P., Lol, D.] College of the World, CA, US' ))

我只需要提取机构名称,它会如下所示:

1 Univ of Space
2 Univ of Time
3 College of the World

我不关心机构字符串中的任何其他字符,只关心第一个逗号之前的所有字符。问题是我在某些情况下机构名称前面会带有一个括号,有时是单独的(如第一行的情况)。

我编写了以下代码来分别提取这两个实例:

ex_inst<- str_extract_all(mydf$Institution,"(?<=])(.+?)(?=,)", simplify = TRUE)
ex_inst2<- str_extract_all(mydf$Institution,"^(.+?)(?=,)", simplify = TRUE)

我正在努力将它们结合在一起。我已经研究了交替,并尝试了这个

ex_inst3<- str_extract_all(mydf$Institution,"^(.+?)(?=,)|(?<=])(.+?)(?=,)", simplify = TRUE)

但我对正则表达式没有经验,并且对它的输出感到困惑:

[1,] "Univ of Space" ""                     
[2,] "[Bloggs"       " Univ of Time"        
[3,] "[Windz"        " College of the World"

将它与 stringr 结合的最佳方法是什么,我可以使用某种 if else 语句吗? 谢谢。

【问题讨论】:

    标签: r regex stringr


    【解决方案1】:

    我们可以使用str_replace 来捕获不是, 的字符,同时删除包括方括号在内的任何字符

    library(stringr)
    str_replace(mydf$Institution,"^(\\[[^]]*\\]\\s*)?([^,]+),.*", "\\2")
    #[1] "Univ of Space"        "Univ of Time"         "College of the World"
    

    或者使用与sub相同的模式

    sub("^(\\[[^]]*\\]\\s*)?([^,]+),.*", "\\2", mydf$Institution)
    #[1] "Univ of Space"        "Univ of Time"         "College of the World"
    

    更新

    如果我们有多个条目,一个选项是拆分成组件,然后使用与上面相同的代码

    v1 <- unlist(strsplit(as.character(mydf$Institution), ";\\s(?=\\[)", perl = TRUE))
    sub("^(\\[[^]]*\\]\\s*)?([^,]+),.*", "\\2", v1)
    #[1] "Univ of Space"        "Univ of Time"         
    #[3] "College of the World" "College of the World" "Space Institute"  
    

    如果我们需要追加为新列

    lst1 <- setNames(strsplit(as.character(mydf$Institution), 
          ";\\s(?=\\[)", perl = TRUE), mydf$ID)
    mydf2 <- stack(lst1)
    mydf2$values <- sub("^(\\[[^]]*\\]\\s*)?([^,]+),.*", "\\2", stack(lst1)$values)
    out1 <- aggregate(values ~ ., merge(mydf, mydf2, by.x = "ID", by.y = "ind"),
          FUN = paste, collapse = '; ')
    out1[order(as.numeric(out1$ID)),]
    #  ID                                                                                      Institution                                values
    #4  1                                                                            Univ of Space, TX, US                         Univ of Space
    #1  2                                                     [Bloggs, J., Smith, T.] Univ of Time, CA, US                          Univ of Time
    #2  3                                                [Windz, P., Lol, D.] College of the World, CA, US                  College of the World
    #3  4 [Windz, P., Lol, D.] College of the World, CA, US; [Bon, D.; Wilson, M.] Space Institute, TX, US College of the World; Space Institute
    

    数据

    mydf <- structure(list(ID = c("1", "2", "3", "4"), Institution = c("Univ of Space, TX, US", 
    "[Bloggs, J., Smith, T.] Univ of Time, CA, US", "[Windz, P., Lol, D.] College of the World, CA, US", 
    "[Windz, P., Lol, D.] College of the World, CA, US; [Bon, D.; Wilson, M.] Space Institute, TX, US"
    )), class = "data.frame", row.names = c(NA, -4L))
    

    注意:来自 cmets 的更新数据

    【讨论】:

    • 谢谢。如果同一字符串中还有其他机构,有没有办法做到这一点,例如[Windz, P., Lol, D.] College of the World, CA, US; [Bon, D.; Wilson, M.] Space Institute, TX, US 有没有办法同时提取“世界学院”和“太空学院”
    • @vuvuzelling 试试v1 &lt;- unlist(strsplit(as.character(mydf$Institution), ";\\s(?=\\[)", perl = TRUE));sub("^(\\[[^]]*\\]\\s*)?([^,]+),.*", "\\2", v1)# [1] "Univ of Space" "Univ of Time" "College of the World" "College of the World" "Space Institute"
    • 这工作得很好,但我很困惑如何将它返回到一个数据框中,其中的组件与它们各自的 ID 匹配?
    • @vuvuzelling 更新帖子
    • 感谢@akrun,效果很好。我会把你的标记为答案,因为我喜欢在 sub() 中有一个更复杂的正则表达式供我学习
    【解决方案2】:

    这是一个使用str_remove 和separate 的选项

    library(dplyr)
    library(tidyr)
    mydf %>%
        mutate(Institution = str_remove(Institution, "\\[.+\\]")) %>%
        separate(Institution, c("Institution", "State", "Country"), sep = ", ")
    #  ID           Institution State Country
    #1  1         Univ of Space    TX      US
    #2  2          Univ of Time    CA      US
    #3  3  College of the World    CA      US
    

    您可以使用select(-State, -Country) 删除State 和Country 列。

    或者在separate期间删除额外的列

    mydf %>%
        mutate(Institution = str_remove(Institution, "\\[.+\\]")) %>%
        separate(Institution, "Institution", sep = ", ", extra = "drop")
    #  ID           Institution
    #1  1         Univ of Space
    #2  2          Univ of Time
    #3  3  College of the World
    

    更新

    根据您的评论,这似乎变得有点复杂。

    对于您修改后的样本数据

    mydf<- data.frame(
        ID=c('1', '2', '3', '4'),
        Institution=c('Univ of Space, TX, US', '[Bloggs, J., Smith, T.] Univ of Time, CA, US', '[Windz, P., Lol, D.] College of the World, CA, US', '[Windz, P., Lol, D.] College of the World, CA, US; [Bon, D., Wilson, M.] Space Institute, TX, US'))
    

    我们可以的

    mydf %>%
        mutate(Institution = map(
            str_split(Institution, "; "),
            ~trimws(str_remove(.x, "\\[.+\\]")))) %>%
        unnest(Institution) %>%
        separate(Institution, "Institution", sep = ", ", extra = "drop") %>%
        group_by(ID) %>%
        mutate(col = sprintf("Insitution%i", 1:n())) %>%
        pivot_wider(names_from = col, values_from = Institution)
    ## A tibble: 4 x 3
    ## Groups:   ID [4]
    #  ID    Insitution1          Insitution2
    #  <fct> <chr>                <chr>
    #1 1     Univ of Space        NA
    #2 2     Univ of Time         NA
    #3 3     College of the World NA
    #4 4     College of the World Space Institute
    

    【讨论】:

    • 我真的很喜欢这种替代方法,它适用于示例!我使用 str_extract 是因为我可能会在同一个字符串中遇到多个机构,所以它会提取它找到的所有内容。使用 mutate 虽然它只会提取第一个实例,然后用那个实例创建机构列,但是我怎么能解决一个问题,我可能有一个像 [Windz, P., Lol, D.] College of the World, CA, US; [Bon, D.; Wilson, M.] Space Institute, TX, US 这样的字符串,我需要留下两个学院世界与空间研究所在两个单独的专栏中?
    • @vuvuzelling 这有点复杂,我进行了编辑以显示一个选项。请看一看。
    【解决方案3】:

    应该有一种方法可以只用一个sub 来做到这一点,但是这里有一个在基础R 中有两个sub 的方法

    sub('(.*?),.*', '\\1', sub('\\[.*\\]', "", mydf$Institution))
    #[1] "Univ of Space"         " Univ of Time"         " College of the World"
    

    其中内部的用于删除方括号内的所有内容,外部的用于捕获直到第一个逗号。


    str_match 的替代方法

    stringr::str_match(mydf$Institution, "(\\[.*\\]\\s)?(.*?),")[, 3]
    #[1] "Univ of Space"        "Univ of Time"         "College of the World"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-26
      • 2015-01-14
      • 2011-08-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多