【问题标题】:Conditional Data Frame String Split Based on Second White Space基于第二个空格的条件数据帧字符串拆分
【发布时间】:2019-06-09 12:51:44
【问题描述】:

我有一个数据框,我想将第一列的文本字符串分成两列,但只在序列中的第二个空格之后。这是一个示例:

                           test22 Ticker
1        Current SharePrice $6.57    MFM
2               Current NAV $7.11    MFM
3 Current Premium/Discount -7.59%    MFM
4        52WkAvg SharePrice $6.55    MFM
5               52WkAvg NAV $7.21    MFM
6 52WkAvg Premium/Discount -9.19%    MFM

本质上,如果最终结果是一个总共包含三列的数据框,并且价格/百分比字段是它自己的单独列。谢谢!

【问题讨论】:

    标签: r string tidyr data-cleaning


    【解决方案1】:

    基础 中的一个选项是使用sub 创建分隔符,,然后使用read.csv

    out <- cbind(read.csv(text = sub(" (\\S+)$", ",\\1", df1$test22), 
           header = FALSE, stringsAsFactors = FALSE), df1[2])
    out
    #.                       V1     V2 Ticker
    #1       Current SharePrice  $6.57    MFM
    #2              Current NAV  $7.11    MFM
    #3 Current Premium/Discount -7.59%    MFM
    #4       52WkAvg SharePrice  $6.55    MFM
    #5              52WkAvg NAV  $7.21    MFM
    #6 52WkAvg Premium/Discount -9.19%    MFM
    

    或者使用来自tidyrextract

    library(tidyverse)
    df1 %>% 
         extract(test22, into = c("V1", "V2"), "^(\\S+\\s+\\S+)\\s+(.*)")
    #                        V1     V2 Ticker
    #1       Current SharePrice  $6.57    MFM
    #2              Current NAV  $7.11    MFM
    #3 Current Premium/Discount -7.59%    MFM
    #4       52WkAvg SharePrice  $6.55    MFM
    #5              52WkAvg NAV  $7.21    MFM
    #6 52WkAvg Premium/Discount -9.19%    MFM
    

    数据

    df1 <- structure(list(test22 = c("Current SharePrice $6.57", "Current NAV $7.11", 
      "Current Premium/Discount -7.59%", "52WkAvg SharePrice $6.55", 
     "52WkAvg NAV $7.21", "52WkAvg Premium/Discount -9.19%"), Ticker = c("MFM", 
     "MFM", "MFM", "MFM", "MFM", "MFM")), class = "data.frame", row.names = c("1", 
      "2", "3", "4", "5", "6"))
    

    【讨论】:

      【解决方案2】:

      这是一个使用strsplit的选项

      data.frame(do.call(rbind, strsplit(df$test22, '\\s(?!.*\\s)', perl = TRUE)), 
                 Ticker=df$Ticker)
      #                         X1     X2 Ticker
      # 1       Current SharePrice  $6.57    MFM
      # 2              Current NAV  $7.11    MFM
      # 3 Current Premium/Discount -7.59%    MFM
      # 4       52WkAvg SharePrice  $6.55    MFM
      # 5              52WkAvg NAV  $7.21    MFM
      # 6 52WkAvg Premium/Discount -9.19%    MFM
      

      或使用gsub

      gsub('.*\\s.*?\\s(.*)','\\1', df$test22, perl = TRUE)
      # [1] "$6.57"  "$7.11"  "-7.59%" "$6.55"  "$7.21"  "-9.19%"
      # or if factors
      # gsub('.*\\s.*?\\s(.*)','\\1', as.character(df$test22), perl = TRUE)
      

      第二个的优点是它真正考虑了第二个空格字符(而不是最后一个空格)。

      【讨论】:

      • 我收到以下错误,但我将其应用于大型数据框:strsplit(df6$test22, "\\s(?!.*\\s)" 中的错误, perl = TRUE) : 非字符参数
      • @js80 也许是因为你有因素。尝试:as.character(df$test22) 中的 strsplit 而不是 df$test22。或者可以试试第二个选项。
      【解决方案3】:

      这是一个使用dplyrstringr 的选项:

      library(dplyr)
      library(stringr)
      
      data <-
        tibble(test22 = c("Current SharePrice $6.57",
                          "Current NAV $7.11",
                          "Current Premium/Discount -7.59%",
                          "52WkAvg SharePrice $6.55",
                          "52WkAvg NAV $7.21",
                          "52WkAvg Premium/Discount -9.19%"),
               Ticker = "MFM")
      
      data %>% 
        mutate(category = str_replace(test22, "^(.+ .+) (.+)$", "\\1"),
               price_pc = str_replace(test22, "^(.+ .+) (.+)$", "\\2"))
      
      
      # A tibble: 6 x 4
      test22                          Ticker category                 price_pc
      <chr>                           <chr>  <chr>                    <chr>   
      1 Current SharePrice $6.57        MFM    Current SharePrice       $6.57   
      2 Current NAV $7.11               MFM    Current NAV              $7.11   
      3 Current Premium/Discount -7.59% MFM    Current Premium/Discount -7.59%  
      4 52WkAvg SharePrice $6.55        MFM    52WkAvg SharePrice       $6.55   
      5 52WkAvg NAV $7.21               MFM    52WkAvg NAV              $7.21   
      6 52WkAvg Premium/Discount -9.19% MFM    52WkAvg Premium/Discount -9.19% 
      

      编辑:所用正则表达式的解释

      暂时忽略括号:

      ^ = 字符串的开始

      . = 除新行以外的任何字符

      + = 前一个字符中的至少一个(在这种情况下,除换行符外的任何字符)

      $ = 字符串结尾

      所以"^(.+ .+) (.+)$" 会查找开头的字符串,有一些字符,然后是空格,然后是一些字符,然后是空格,然后是更多字符,然后结束。

      括号作为“捕获组”添加,意味着查询“记住”了由这些括号表示的字符串部分,并且可以通过参考括号的顺序来提取。因此"\\1" 返回第一个括号捕获的内容,"\\2" 返回第二个括号捕获的内容。

      学习正则表达式的好资源是Regexr

      【讨论】:

      • 不用担心@js80。一些赞成/选择的答案将不胜感激。
      • 您能解释一下语法吗:^(.+ .+) (.+)$", "\\1"?
      • @js80 在上面添加了解释,因为它太罗嗦了,无法发表评论。简而言之:它是正则表达式。
      猜你喜欢
      • 1970-01-01
      • 2021-02-05
      • 2021-09-26
      • 2015-06-08
      • 1970-01-01
      • 2015-05-26
      • 1970-01-01
      • 2012-01-08
      相关资源
      最近更新 更多