R if else语句,它取决于字符串中元素的数量

a1o7rhls  于 2023-05-26  发布在  其他
关注(0)|答案(4)|浏览(234)

我试图创建一个if else语句,它取决于字符串中元素的数量,下面是我的模拟数据:

df1 <- data.frame(name = c("Matt Smith", "Matt L. Smith", "Sara Smith", "Sara Rose Smith"))

我试着写这条语句,这样任何包含名字、中间名和姓氏(即3个独立的单词)的名字都会被分成三列(第一列、中间名和姓氏),而任何只有名字和姓氏的名字都会被排序到名字和姓氏列。以下是我尝试过的:

if (any(lengths(strsplit(df1$name, "\\W+")) > 2)) {
  df1 <- df1 %>%
    separate(name, c('Collector.First.Name1', 'Collector.Middle1', "Collector.Last.Name1"))
} else {
  df1 <- df1 %>%
    separate(name, c('Collector.First.Name1', "Collector.Last.Name1"))
}

这将输出以下 Dataframe :

Collector.First.Name1 Collector.Middle1 Collector.Last.Name1
1                  Matt             Smith                 <NA>
2                  Matt                 L                Smith
3                  Sara             Smith                 <NA>
4                  Sara              Rose                Smith

然而,我正在寻找的输出将只有2个元素的名称排序为只有名字和姓氏的名称,如下所示:

Collector.First.Name1 Collector.Middle1 Collector.Last.Name1
1                  Matt              <NA>                Smith
2                  Matt                 L                Smith
3                  Sara              <NA>                Smith
4                  Sara              Rose                Smith

使用R 4.2.2

bbuxkriu

bbuxkriu1#

当名称只有一个时,我们可以添加一个额外的空格,这将使separate()创建一个空的中间组。比空格更好的是,我们可以使用像'='这样的临时字符来避免问题。

如果使用case_when()进行其他处理:

df1 %>%
  mutate(name = case_when(str_count(name, " ") == 1 ~ str_replace(name, " ", "=="),
                          str_count(name, " ") > 1 ~ str_replace_all(name, " ", "=")) %>%
                  str_remove_all("\\.")) %>%
  separate(name, c("first", "middle", "last"), "=")

如果空格数(str_count(name, " "))为1,则添加两个临时字符。否则,每次出现一个空格时,我们就添加一个空格。我们稍后删除点,因为您似乎不想要它们。最后,我们使用临时字符来分隔列。

使用gsub()的Regex方法:

我们可以定义一个正则表达式来匹配一个中间组,即使它是一个空的,并在它们之间添加临时字符。

df1 %>%
  mutate(name = gsub('([A-Za-z]+) ([A-Za-z\\.]* )?([A-Za-z]+)', '\\1=\\2=\\3', name) %>%
                  str_remove_all("\\.| ")) %>%
  separate(name, c("first", "middle", "last"), "=")

解释模式:

  • ([A-Za-z]+)一个任意大小的单词,名字;
  • 任何大小的单词,可能在某处有一个点,结尾有一个空格。整个群(...)可能存在也可能不存在?;
  • ([A-Za-z]+)任意大小的单词,姓氏。

稍后我们删除多余的空格和点,并将列分开

两者产生相同的结果:

first middle  last
1  Matt        Smith
2  Matt      L Smith
3  Sara        Smith
4  Sara   Rose Smith
gz5pxeao

gz5pxeao2#

Witch正则表达式匹配组和stringr可能是这样的:

library(dplyr)
library(stringr)
df1 <- data.frame(name = c("Matt Smith", "Matt L. Smith", "Sara Smith", "Sara Rose Smith"))

# 3 regex match groups will end up in 3 columns
str_match(df1$name, "^(\\S+)(.*\\s+)(.*)$") %>% 
  `colnames<-`(c("name", "first", "mid", "last")) %>% 
  as_tibble() %>% 
  mutate(mid = trimws(mid) %>% na_if("")) 
#> # A tibble: 4 × 4
#>   name            first mid   last 
#>   <chr>           <chr> <chr> <chr>
#> 1 Matt Smith      Matt  <NA>  Smith
#> 2 Matt L. Smith   Matt  L.    Smith
#> 3 Sara Smith      Sara  <NA>  Smith
#> 4 Sara Rose Smith Sara  Rose  Smith

创建于2023-05-23带有reprex v2.0.2

bnl4lu3b

bnl4lu3b3#

使用tidyr,我们可以使用separate_wider_*两次,指定不同的too_many/too_few-参数:

library(tidyr)

df1 |>
  separate_wider_delim(name, delim = " ", names = c("first", "name"), too_many = "merge") |>
  separate_wider_delim(name, delim = " ", names = c("mid", "last"), too_few = "align_end")

输出:

# A tibble: 4 × 3
  first mid   last 
  <chr> <chr> <chr>
1 Matt  NA    Smith
2 Matt  L.    Smith
3 Sara  NA    Smith
4 Sara  Rose  Smith

请注意,separate_wider_delim是实验性的,但类似的功能存在于(被取代的)separate中。

gxwragnw

gxwragnw4#

你不需要if else语句。只要确保你有正确的正则表达式:

library(tidyverse)
df1 %>%
  extract(name, c("first", "middle", "last"), "(\\w+) (\\S*) ?(\\b\\w+$)")

 first middle  last
1  Matt        Smith
2  Matt     L. Smith
3  Sara        Smith
4  Sara   Rose Smith

在Base R中,你可以:

read.csv(text = gsub(" (\\S*) ?\\b(?!$)", ",\\1,", df1$name, perl = TRUE),
    header =FALSE, col.names = c("first", "middle", "last"), na.strings = '')
  first middle  last
1  Matt   <NA> Smith
2  Matt     L. Smith
3  Sara   <NA> Smith
4  Sara   Rose Smith

相关问题