根据 vectorized/unvectorized 调用,stringr 的字符串操作不起作用

String operations with stringr not working depending on vectorized/unvectorized call

我很难理解为什么我的下面的代码只有在 rowwiseifelse 结合使用时才有效。或者更准确地说,我想我明白为什么它在那种情况下有效,但不明白为什么它不能简单地与 if_else.

一起使用

我正在做的是检查某些行是否包含单词“infile”或“outfile”,以及它是否具有相对路径 ("..")。如果它有单词“infile/outfile”而不是相对路径,那么它有一个绝对路径“C:”。在这种情况下,我想用其他名称替换用户名(此处:“test”)。

有什么想法吗?

数据:

df <- structure(list(value = c("infile 'C:\Users\USER\folder\Data.sav'", 
"infile '..\folder\Data.sav'", "outfile '..\folder\Data.sav'", 
"test", "")), class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, 
-5L))

user_name <- "test"

有效代码:

df |> 
  rowwise() |> 
  mutate(value = ifelse(str_detect(value, "infile|outfile") & !str_detect(value, "\'\.\.\\"),
                        str_replace(value,
                                    str_sub(value,
                                            str_locate_all(value, "\\")[[1]][2] + 1,
                                            str_locate_all(value, "\\")[[1]][3] - 1),
                                    user_name),
                        value)) |> 
  ungroup()

输出:

# A tibble: 5 × 1
  value                                       
  <chr>                                       
1 "infile 'C:\Users\test\folder\Data.sav'"
2 "infile '..\folder\Data.sav'"             
3 "outfile '..\folder\Data.sav'"            
4 "test"                                      
5 ""   

无效的代码:

df |> 
  mutate(value = if_else(str_detect(value, "infile|outfile") & !str_detect(value, "\'\.\.\\"),
                        str_replace(value,
                                    str_sub(value,
                                            str_locate_all(value, "\\")[[1]][2] + 1,
                                            str_locate_all(value, "\\")[[1]][3] - 1),
                                    user_name),
                        value))

我认为这可行,但给出了警告消息:

Warning messages:
1: Problem while computing `value = if_else(...)`.
ℹ empty search patterns are not supported 
2: Problem while computing `value = if_else(...)`.
ℹ empty search patterns are not supported 

无效的代码:

df |> 
  rowwise() |>
  mutate(value = if_else(str_detect(value, "infile|outfile") & !str_detect(value, "\'\.\.\\"),
                        str_replace(value,
                                    str_sub(value,
                                            str_locate_all(value, "\\")[[1]][2] + 1,
                                            str_locate_all(value, "\\")[[1]][3] - 1),
                                    user_name),
                        value)) |> 
  ungroup()

Error in `mutate()`:
! Problem while computing `value = if_else(...)`.
ℹ The error occurred in row 2.
Caused by error:
! Empty `pattern` not supported

这是一种方法(我对 USER 的替换非常简单;不确定是否应该更通用):

df %>% 
    tidyr::separate(value, into = c('Type', 'Path'), sep = ' ') %>% 
    dplyr::mutate(
        Value = dplyr::if_else(
            (Type %in% c('infile', 'outfile')) & !startsWith(Path, "'.."),
            stringr::str_replace(Path, 'USER', user_name),
            Path
        )
    )

我拆分了 value 列以便于检查。

如果您需要用变量替换用户名,您可以这样做(这里使用反向引用正则表达式):

df %>% 
    tidyr::separate(value, into = c('Type', 'Path'), sep = ' ') %>% 
    dplyr::mutate(
        Value = dplyr::if_else(
            (Type %in% c('infile', 'outfile')) & !startsWith(Path, "'.."),
            sub('^(C:\\Users\\)([[:alnum:]]+)\\', paste0('\1', user_name, '\\'), Path),
            Path
        )
    )

基本上,问题是 if_else() 计算每一行中的 true 和 false 输出,而 ifelse() 只计算使用它们的 true 和 false 表达式。

此外,如果您不使用 rowwise(),则 mutate 会在每次迭代时传递 df$value 中的整个字符串集,然后 returns 开始时传递相同的索引和每一行的字符串结尾。

为了调试,我建议稍微分解一下计算:

df %>% rowwise() %>%
       mutate(n=length(value), slen=str_length(value),
              l1=str_locate_all(value,"\\")[[1]][2]+1,
              l2=str_locate_all(value,"\\")[[1]][3]-1, 
              ssub=str_sub(value, l1, l2), 
              detect=str_detect(value, "infile|outfile")& !str_detect(value,"\'\.\.\\"), 
              vout=if_else(detect, ssub, user_name))
# A tibble: 5 × 8
# Rowwise: 
  value                                            n  slen    l1    l2 ssub   detect vout 
  <chr>                                        <int> <int> <dbl> <dbl> <chr>  <lgl>  <chr>
1 "infile 'C:\Users\USER\folder\Data.sav'"     1    38    18    21 "USER" TRUE   USER 
2 "infile '..\folder\Data.sav'"                  1    27    19    10 ""     FALSE  test 
3 "outfile '..\folder\Data.sav'"                 1    28    20    11 ""     FALSE  test 
4 "test"                                           1     4    NA    NA  NA    FALSE  test 
5 ""                                               1     0    NA    NA  NA    FALSE  test 

虽然没有 rowwise(),但 mutate 会一次性获取值列中的所有字符串,并且它会在 每一行 上找到相同的切割位置:

df %>% 
       mutate(n=length(value), slen=str_length(value),
              l1=str_locate_all(value,"\\")[[1]][2]+1,
              l2=str_locate_all(value,"\\")[[1]][3]-1, 
              ssub=str_sub(value, l1, l2), 
              detect=str_detect(value, "infile|outfile")& !str_detect(value,"\'\.\.\\"), 
              vout=if_else(detect, ssub, user_name))
# A tibble: 5 × 8
  value                                            n  slen    l1    l2 ssub    detect vout 
  <chr>                                        <int> <int> <dbl> <dbl> <chr>   <lgl>  <chr>
1 "infile 'C:\Users\USER\folder\Data.sav'"     5    38    18    21 "USER"  TRUE   USER 
2 "infile '..\folder\Data.sav'"                  5    27    18    21 "\Dat" FALSE  test 
3 "outfile '..\folder\Data.sav'"                 5    28    18    21 "r\Da" FALSE  test 
4 "test"                                           5     4    18    21 ""      FALSE  test 
5 ""                                               5     0    18    21 ""      FALSE  test 

一旦您错误地计算了字符串子集的位置,我认为您很幸运 if_else 引发了不同的错误。