【问题标题】:Append a digit at the beginning of character data using stringr or gsub使用 stringr 或 gsub 在字符数据的开头附加一个数字
【发布时间】:2019-08-08 11:04:45
【问题描述】:

我有一个名为DATA_TEST的数据集。这个数据框包含6个字符格式的观察值。您可以看到下表。

dput(DATA_TEST)
structure(list(Ten_digits = c("NA", "207", "0101", "0208 90", 
"0206 90 99 00", "103")), .Names = "Ten_digits", row.names = c(NA, 
-6L), class = "data.frame")
# -------------------------------------------------------------------------
# > DATA_TEST
#       Ten_digits
# 1            NA
# 2           207
# 3          0101
# 4       0208 90
# 5 0206 90 99 00
# 6           103

所以我的意图是使用 stringr 或其他包转换此数据帧,如下图所示。实际上代码需要做一件事或更精确 首先必须找到只有三位数的变量,如207103,并将此变量转换为02070103。 在下表中,您最终可以看到该表应该是什么样子。

# > Desired Output
#       Ten_digits
# 1            NA
# 2          0207
# 3          0101
# 4       0208 90
# 5 0206 90 99 00
# 6          0103

那么有人可以帮我处理这段代码吗?

【问题讨论】:

  • 有像234 45 56这样的值需要转换成0234 45 56吗?
  • 好吧,好像DATA_TEST$Ten_digits <- sub("^(\\d{3})$", "0\\1", DATA_TEST$Ten_digits, perl=TRUE) 也可以。
  • 不,只有三位数的值,比如 207。你可以在第二张表上看到

标签: r string gsub


【解决方案1】:

您可以使用带有sub 的简单正则表达式:

DATA_TEST<-data.frame(Ten_digits=c("NA","207","0101","0208 90","0206 90 99 00","103"),stringsAsFactors = FALSE)
DATA_TEST$Ten_digits <- sub("^(\\d{3})$", "0\\1", DATA_TEST$Ten_digits)
DATA_TEST
## => Ten_digits
1            NA
2          0207
3          0101
4       0208 90
5 0206 90 99 00
6          0103

这里,^(\\d{3})$ 匹配一个三位数的字符串并将数字捕获到第 1 组(因为模式在括号内),0\1 替换模式插入 0 并将整个匹配值添加回第 1 组.

模式详情

  • ^ - 字符串开头
  • (\d{3}) - 第 1 组:三位数字
  • $ - 字符串结束。

【讨论】:

    【解决方案2】:

    你可以从stringr使用str_length

    library(tidyverse) # in order to load all required packages at once
    
    DATA_TEST %>% 
      mutate(Ten_digits = case_when(
        str_length(Ten_digits) == 3 ~ paste0("0", Ten_digits),
        TRUE ~ Ten_digits
        ))
    
    # Ten_digits
    #1            NA
    #2          0207
    #3          0101
    #4       0208 90
    #5 0206 90 99 00
    #6          0103
    

    str_length 允许您向量化字符向量的长度:来自function's documentation

    Technically this returns the number of "code points", in a string. One code point usually corresponds to one character(...).

    case_when 允许向量化多个if_else 语句。

    如cmets中所说,可以使用ifelseif_else,比case_when更直接。请参阅下面的微基准测试示例:

    microbenchmark::microbenchmark(
      DATA_TEST %>% 
        mutate(Ten_digits = case_when(
          str_length(Ten_digits) == 3 ~ paste0("0", Ten_digits),
          TRUE ~ Ten_digits
        )),
      DATA_TEST %>% 
        mutate(Ten_digits = ifelse(
          str_length(Ten_digits) == 3, paste0("0", Ten_digits),
          Ten_digits
        )),
      DATA_TEST %>% 
        mutate(Ten_digits = if_else(
          str_length(Ten_digits) == 3, paste0("0", Ten_digits),
          Ten_digits
        ))
    )
    
    #     min       lq      mean  median       uq      max neval
    # 785.809 806.9130 1051.9314 858.217 1193.865 2445.434   100  # case_when
    # 613.398 623.3985  862.6720 636.858  822.027 8610.763   100  # ifelse
    # 625.485 641.1370  822.3502 664.135  894.812 1995.932   100  # if_else
    

    【讨论】:

    • 为什么投反对票?它使用它要求的包完全满足 OP 的要求。
    • 也许我没有完全理解这个问题。您是否期望有更多的条件进行测试?如果没有,ifelse/if_else 会更短更直接
    • 我回答了 OP 的问题。你可以用不同的方式做同样的事情,但这不是拒绝投票的理由。如果您将鼠标悬停在downvoting 上,您将看到this answer is not useful,它显然是这样。无论如何,我编辑了我的帖子。
    • 我实际上赞成它。我认为这是一个很好的答案;如果我现在可以在您添加基准后再次投票,我会的。我真的只是指我问的问题,即您是否要留出空间以适应更多条件
    【解决方案3】:

    我们可以简单地通过在 3 个字符的字符串前面粘贴 0 来做到这一点,即

    DATA_TEST$Ten_digits[nchar(DATA_TEST$Ten_digits) == 3] <- paste0("0", DATA_TEST$Ten_digits[nchar(DATA_TEST$Ten_digits) == 3])
    DATA_TEST
    
    #     Ten_digits
    #1            NA
    #2          0207
    #3          0101
    #4       0208 90
    #5 0206 90 99 00
    #6          0103
    

    【讨论】:

      【解决方案4】:

      您可以从stingr 使用str_pad。请注意,它将填充长度小于 4 个字符的任何字符串,因此如果您特别想关注长度为 3 的字符串,则需要修改代码。此外,如果您有文字 NA,则不需要 ifelse的“NA”。 -

      DATA_TEST %>% 
        mutate(
          Ten_digits = ifelse(Ten_digits == "NA", "NA", str_pad(Ten_digits, width = 4, pad = 0))
        )
      
           Ten_digits
      1            NA
      2          0207
      3          0101
      4       0208 90
      5 0206 90 99 00
      6          0103
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-01-10
        • 2018-10-30
        • 2019-10-23
        • 1970-01-01
        • 2022-07-21
        • 1970-01-01
        • 2019-06-16
        • 1970-01-01
        相关资源
        最近更新 更多