需要使用 R 从字符串列中提取单个字符

Met*_*fan 3 regex string r dplyr

背景

下面是我的dput形式的游戏数据数据集——它包含一些 MLB 游戏的得分。

structure(list(team = c("NYM", "NYM", "BOS", "NYM", "BOS"), linescore = c("010000000", 
"(10)1140006x", "002200010", "00000(11)01x", "311200"), ondate = structure(c(18475, 
18476, 18487, 18489, 18494), class = "Date")), class = "data.frame", row.names = c(NA, 
-5L))
Run Code Online (Sandbox Code Playgroud)

例如,这是一行得分:“002200010”。

有些行分数以“x”结尾,有些在括号中包含两位数值,如“00000(11)01x”。不在括号中的每个数字表示球队在局中得分的次数。如果一支球队在一局中得分超过 9 分,则数字放在括号中,因此在得分“00000(11)01x”行中,该球队在第六局中得分为 11 分,但没有在最后一局击球。第九个(用“x”表示)。

并非每条线得分都有九局。有些有更多,有些只有六个。

我需要做什么

首先,我需要做的是获取一个团队在每一局中得分的次数,例如第一、第二、第三等,并将每个得分放在一个新列中。我更喜欢使用 dplyr 的解决方案。

我已经查看了 stackoverflow 的建议解决方案,但没有发现符合我需要的解决方案。如果有的话,如果你能分享它的网址,我将不胜感激。

我尝试使用以下代码来做到这一点:

gamedata %>%
  select(ondate, team, linescore) %>%
  mutate(inng1 = str_extract(linescore, "\\d|\\(\\d{2}\\)"))
Run Code Online (Sandbox Code Playgroud)

这是输出:

ondate      team linescore    inng1
2020-08-01  NYM 010000000       0   
2020-08-02  NYM (10)1140006x  (10)  
2020-08-13  BOS 002200010       0   
2020-08-15  NYM 00000(11)01x    0   
2020-08-20  BOS 311200          3
Run Code Online (Sandbox Code Playgroud)

其次,如何删除'10'的inng1列中的括号?

下面的代码在它下面产生了错误

gamedata %>%
  select(ondate, team, linescore) %>%
  mutate(inng1 = str_extract(linescore, "\\d|\\(\\d{2}\\)"))
 str_remove_all(inng1,"[()]")
Run Code Online (Sandbox Code Playgroud)

这是我收到的错误消息:

“stri_replace_all_regex(string, pattern, fix_replacement(replacement), : 找不到对象‘inng1’时出错”

第三,我需要知道如何提取每个附加局的得分,从第二局开始,将每个值放在自己的列中,例如 inng2、inng3 等等。

最后,我应该有上面显示的输出(每个两位数的局没有括号),每个局都有一列,所以会有一个标题为“inng1”、“inng2”、“inng3”、“inng4”的列,等等。局列中的数据需要是数字,因为稍后我将对它们求和。

Ano*_*n R 5

解决方案02

这是您可以用于此问题的另一种解决方案,它比第一个更有效,并且主要基于purrr函数系列:

library(dplyr)
library(purrr)

df %>%
  bind_cols(
    map(df %>% select(linescore), ~ strsplit(.x, "\\(|\\)")) %>%
      flatten() %>%
      map_dfr(~ map(.x, ~ if(nchar(.x) > 2) strsplit(.x, "")[[1]] else .x) %>%
                reduce(~ c(.x, .y)) %>%
                keep(~ nchar(.x) != 0) %>% t() %>%
                as_tibble() %>% 
                set_names(~ paste0("inng", 1:length(.x)))) %>%
      mutate(across(everything(), ~ replace(.x, .x == "x", NA_character_)), 
             count_inng = pmap_dbl(cur_data(), ~ sum(!is.na(c(...)))), 
             sums_inng = pmap_dbl(select(cur_data(), starts_with("inng")), 
                                  ~ sum(as.numeric(c(...)), na.rm = TRUE)))
  )

  team    linescore     ondate inng1 inng2 inng3 inng4 inng5 inng6 inng7 inng8 inng9 count_inng
1  NYM    010000000 2020-08-01     0     1     0     0     0     0     0     0     0          9
2  NYM (10)1140006x 2020-08-02    10     1     1     4     0     0     0     6  <NA>          8
3  BOS    002200010 2020-08-13     0     0     2     2     0     0     0     1     0          9
4  NYM 00000(11)01x 2020-08-15     0     0     0     0     0    11     0     1  <NA>          8
5  BOS       311200 2020-08-20     3     1     1     2     0     0  <NA>  <NA>  <NA>          6
  sums_inng
1         1
2        22
3         5
4        12
5         7
Run Code Online (Sandbox Code Playgroud)

解决方案01

我对我的解决方案进行了一些修改,因为它错误地替换了输出向量中的两位数,我认为它已得到修复。我认为这个解决方案可以帮助你。为此,我决定编写一个自定义函数来检测两位数并修剪分数的输出:

library(dplyr)
library(stringr)
library(tidyr)
library(purrr)

fn <- function(x) {
  out <- c()
  if(str_detect(x, "\\((\\d){2}\\)")) {
    double <- str_replace_all(str_extract(x, "\\((\\d){2}\\)"), "[)()]", "")
    ind <- str_locate(x, "\\(")
    x <- str_remove(x, "\\((\\d){2}\\)")
    out <- c(out, str_split(x, "")[[1]])
    out[(ind[1, 1]+1):(length(out)+1)] <- out[(ind[1, 1]):length(out)]
    out[ind] <- double
  } else {
    out <- c(out, str_split(x, "")[[1]])
  }
  if(any(grepl(")", out))) {
    out <- out[-which(out == ")")]
  }
  out
}

# Test
fn("(10)1140006x)")
[1] "10" "1"  "1"  "4"  "0"  "0"  "0"  "6"  "x" 
Run Code Online (Sandbox Code Playgroud)

然后我们以行操作将其应用于我们的数据集:

df %>%
  mutate(linescore = map(linescore, fn)) %>% 
  unnest_wider(linescore) %>%
  rename_with(~ gsub("(\\.\\.\\.)(\\d)", paste0("inng", "\\2"), .), starts_with("...")) %>%
  mutate(across(starts_with("inng"), ~ {replace(.x, .x == "x", NA)
    as.numeric(.x)}), 
    inns_count = pmap_dbl(select(cur_data(), starts_with("inng")), 
                          ~ sum(!is.na(c(...)))), 
    inns_sums = pmap_dbl(select(cur_data(), starts_with("inng")), 
                         ~ sum(c(...), na.rm = TRUE)))

# A tibble: 5 x 13
  team  inng1 inng2 inng3 inng4 inng5 inng6 inng7 inng8 inng9 ondate     inns_count inns_sums
  <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <date>          <dbl>     <dbl>
1 NYM       0     1     0     0     0     0     0     0     0 2020-08-01          9         1
2 NYM      10     1     1     4     0     0     0     6    NA 2020-08-02          8        22
3 BOS       0     0     2     2     0     0     0     1     0 2020-08-13          9         5
4 NYM       0     0     0     0     0    11     0     1    NA 2020-08-15          8        12
5 BOS       3     1     1     2     0     0    NA    NA    NA 2020-08-20          6         7
Run Code Online (Sandbox Code Playgroud)

  • 厉害了伙计们。我正在进一步尝试,我怎么能正则表达式 `(, 1, 0, )` 到 `10` (2认同)