【问题标题】:R - Regex to separate string based on first dot?R - 正则表达式根据第一个点分隔字符串?
【发布时间】:2017-05-07 09:47:51
【问题描述】:

我有一列充满了包含多个点的字符串。我想将此列分成两列,其中包含第一个点之前和之后的两个子字符串。

comb          num
UWEA.n.49.sp   3
KYFZ.n.89.kr   5
     ...

进入

 a         b       num
UWEA    n.49.sp     3
KYFZ    n.89.kr     5
     ...

我正在使用来自tidyrseparate 函数,但无法正确获取正则表达式。我正在尝试使用来自this answer 的正则表达式样式:

foo %>%
    separate(comb, into=c('a', 'b'),
             sep="([^.]+)\\.(.*)")

因此,a 列应由包含至少一个非点字符的第一个捕获组 ([^.]+) 确定,然后是第一个点,然后是第二个捕获组 (.*) 只匹配后面的任何内容。

然而这似乎不匹配任何东西:

a   b   num
         3
         5

这是我的虚拟数据集:

library(dplyr)
library(tidyr)
foo <- data.frame(comb=replicate(10, 
                                 paste(paste(sample(LETTERS, 4), collapse=''),
                                       sample(c('p', 'n'), 1), 
                                       sample(1:100, 1), 
                                       paste(sample(letters, 2), collapse=''), 
                                       sep='.')
                                 ),
                  num = sample(1:10, 10, replace=T))

【问题讨论】:

  • 当有一个内置函数来分割字符串时,为什么要使用正则表达式? stat.ethz.ch/R-manual/R-devel/library/base/html/strsplit.html
  • 除非我遗漏了什么,否则该函数仍然需要 split 参数的正则表达式,然后我必须手动将结果设置为两个不同的列。使用 tidyr 的 separate 函数更简单。
  • foo %&gt;% separate(comb, into = c("a","b"), sep = "(?&lt;=[A-Z])\\.(?=[a-z]+)").
  • 这不起作用@RichScriven,a 列是完整的字符串,b 是 。可能是因为该示例需要像 separate 不允许的正则表达式之类的 Perl。 @Abdou 有效!如果您将其写成答案并解释?&lt;=?= 的作用,我会接受。

标签: r regex dplyr tidyr


【解决方案1】:

在这种情况下,您可以利用separate 中的extra = "merge" 选项。因为separate 默认在符号上分隔,所以您不必定义分隔符。如果你愿意,你可以使用"\\."

foo %>%
    separate(comb, into=c('a', 'b'), extra = "merge")

      a       b num
1  NPTE p.10.ku   4
2  YAIU p.54.lw   4
3  CHUR n.51.kx   6
4  EPGX n.14.lg   3
5  POBJ n.11.ja   5
6  LEWI n.72.un   7
7  WLAP n.20.ve  10
8  XZUY p.75.cf   6
9  ZSNJ  p.4.aj   3
10 ABKR n.69.ua   3

extra = "merge" 将您定义的列之外的所有额外部分合并到最后一列中。

【讨论】:

  • 对不起,我想在问题中说我想要一个完整的正则表达式答案,因为我想提高我的技能。我已经让它以这种方式工作,但对我无法使用在我看来是基本的正则表达式格式感到沮丧。
【解决方案2】:

我认为@aosmith 的答案很棒,而且肯定比涉及环视的regex 解决方案更简洁。但既然你打算使用regex,这里是:

foo %>% 
    separate(comb, 
             into = c("a","b"), 
             sep = "(?<=[A-Z])\\.(?=[a-z]+)")

这里的诀窍是正则表达式本身。它使用所谓的lookaround。基本上,您正在为sep 参数寻找一个位于大写字母和小写字母(即UWEA.n)之间的点(.)。这意味着:match a dot preceded by a capital letter and followed by a lowercase letter

这允许separate 函数在An 之间或Zn 之间的点上拆分comb 列,在您的情况下。

我希望这会有所帮助。

【讨论】:

    【解决方案3】:

    这是一个base R 选项。将'comb'列中的第一个.替换为,,使用read.csv读取以基于分隔符,cbind创建两列与'foo'的其他列

    cbind(read.csv(text=sub("\\.", ",", foo$comb), 
              col.names = c('a', 'b'), header=FALSE), foo[-1])
    #      a       b num
    #1  GJMU n.83.cu   3
    #2  IVMD p.85.ny   9
    #3  HLQB p.94.rd   8
    #4  WIJY n.92.sz   4
    #5  QXCM n.38.lf   8
    #6  UBNC n.82.js   5
    #7  EPLZ n.56.kl   3
    #8  YRBA  n.6.ny   8
    #9  HQMR p.54.pn  10
    #10 LBPO p.98.tv   7
    

    或者另一种选择是使用extract from tidyr 匹配一个或多个不是. 的字符,将其放入捕获组(([^.]+)),后跟一个点(\\. ) 后跟第二个捕获组 ((.*)) 中的其他字符。捕获的组字符以两列的形式返回,替换原来的“梳子”列。

    library(tidyr)
    extract(foo, comb, into = c("a", "b"), "([^.]+)\\.(.*)")
    #      a       b num
    #1  GJMU n.83.cu   3
    #2  IVMD p.85.ny   9
    #3  HLQB p.94.rd   8
    #4  WIJY n.92.sz   4
    #5  QXCM n.38.lf   8
    #6  UBNC n.82.js   5
    #7  EPLZ n.56.kl   3
    #8  YRBA  n.6.ny   8
    #9  HQMR p.54.pn  10
    #10 LBPO p.98.tv   7
    

    注意:OP 的帖子中没有 set.seed

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-14
      • 2014-08-17
      • 1970-01-01
      • 2019-04-02
      • 1970-01-01
      • 1970-01-01
      • 2015-07-19
      相关资源
      最近更新 更多