从 R 中的整数拆分字符串

Splitting strings from integers in R

我最近在尝试时遇到了一个有趣的问题 创建自定义数据库。

我的行的格式是:

 183746IGH
 105928759UBS

等等(所以基本上是一个整数与一个字符串连接,两者的大小都是相对随机的。)。我想做的是以某种方式将第 1 列中的整数与第 2 列中的所有其他内容(字母)分开。如何做到这一点?我一直在尝试使用 strsplit,但它似乎没有提供这种功能。

感谢您的帮助。

你可以这样做:

df <- data.frame(V1 = c("adad131341", "adadar45365", "cavsbsb425", "daadvsv46567567"))

library(dplyr)
library(stringr)
df %>% mutate(V2 = str_extract(V1, "[0-9]+"),
              V3 = str_extract(V1, "[aA-zZ]+"))

给出:

#               V1       V2      V3
#1      adad131341   131341    adad
#2     adadar45365    45365  adadar
#3      cavsbsb425      425 cavsbsb
#4 daadvsv46567567 46567567 daadvsv

另一种使用 base-R 和正则表达式的方法:

all <- c(' 183746IGH','105928759UBS')

numeric <- sapply(a, function(x) sub('[[:alpha:]]+','', x))

alphabetic <- sapply(a, function(x) sub('[[:digit:]]+','', x))

    > data.frame(all,alphabetic,numeric)
                      all alphabetic   numeric
 183746IGH      183746IGH        IGH    183746
105928759UBS 105928759UBS        UBS 105928759

或根据@rawr 的下方评论:

> read.table(text = gsub('(\d)(\D)', '\1 \2', all))
         V1  V2
1    183746 IGH
2 105928759 UBS

或以上函数的矢量化版本:

get_alphanum <- function(x, type) {
  type <- switch(type,
                 alpha = '[[:digit:]]+',
                 digit = '[[:alpha:]]+')
  sub(type,'', x)
}

get_alphanum <- Vectorize(get_alphanum)

它给出了直接应用于向量的结果!

> get_alphanum(all, type='alpha')
   183746IGH 105928759UBS 
      " IGH"        "UBS" 
> get_alphanum(all, type='digit')
   183746IGH 105928759UBS 
   " 183746"  "105928759" 

也可用于创建 data.frame:

> data.frame(all, 
             alpha=get_alphanum(all, type='alpha') ,
             numeric=get_alphanum(all, type='digit'))
                      all alpha   numeric
 183746IGH      183746IGH   IGH    183746
105928759UBS 105928759UBS   UBS 105928759
gsubfn 包中的

read.pattern 可以做到这一点。 pattern 参数中给出的正则表达式的每个括号部分将被读入单独的列:

x <- c("183746IGH", "105928759UBS")

library(gsubfn)
read.pattern(text = x, pattern = "(\d+)(\D+)")

给予:

         V1  V2
1    183746 IGH
2 105928759 UBS

其他选项包括 tstrsplit 来自 data.table

的开发版本
library(data.table)#v1.9.5+
setDT(df)[,tstrsplit(V1,'(?<=\d)(?=\D)', perl=TRUE, type.convert=TRUE)]
#        V1      V2
#1:   131341    adad
#2:    45365  adadar
#3:      425 cavsbsb
#4: 46567567 daadvsv

如果有元素 'non-numeric' 部分首先出现,'numeric' 最后出现,那么,我们可以使用更通用的选项作为正则表达式模式,

 setDT(df)[,tstrsplit(V1, "(?<=\d)(?=\D)|(?<=\D)(?=\d)",
                  perl = TRUE)]

或使用 extract 来自 tidyr

library(tidyr)
extract(df, V1, into=c('V1', 'V2'), '(\d+)(\D+)', convert=TRUE)
#        V1      V2
#1   131341    adad
#2    45365  adadar
#3      425 cavsbsb
#4 46567567 daadvsv

如果您也需要原始列,

 extract(df, V1, into=c('V2', 'V3'), '(\d+)(\D+)',
                               convert=TRUE, remove=FALSE)
 #               V1       V2      V3
 #1      131341adad   131341    adad
 #2     45365adadar    45365  adadar
 #3      425cavsbsb      425 cavsbsb
 #4 46567567daadvsv 46567567 daadvsv

对于 data.table,我们可以使用 := 创建新列,以便现有列保留在输出中,即

setDT(df)[,paste0('V',2:3):=tstrsplit(V1,'(?<=\d)(?=\D)',
                     perl=TRUE, type.convert=TRUE)]
#               V1       V2      V3
#1:      131341adad   131341    adad
#2:     45365adadar    45365  adadar
#3:      425cavsbsb      425 cavsbsb
#4: 46567567daadvsv 46567567 daadvsv

注意:两种解决方案都可以选择转换拆分列 (type.convert/convert) 的 class。

数据

df <- data.frame(V1 = c("131341adad", "45365adadar", "425cavsbsb", 
               "46567567daadvsv"))

strsplit 如果您提供正确的正则表达式作为拆分依据,则确实有效。

在这种情况下,你会想要这样的东西:

strsplit(String, split = "(?<=[a-zA-Z])(?=[0-9])", perl = TRUE)

此处应用于@Steven 的示例数据:

strsplit(as.character(df$V1), split = "(?<=[a-zA-Z])(?=[0-9])", perl = TRUE)
# [[1]]
# [1] "adad"   "131341"
# 
# [[2]]
# [1] "adadar" "45365" 
# 
# [[3]]
# [1] "cavsbsb" "425"    
# 
# [[4]]
# [1] "daadvsv"  "46567567"

过去有一段时间我编写了一个函数来执行此操作,因为老实说,我的大脑并不经常用正则表达式思考。该函数如下所示:

SplitMe <- function(string, alphaFirst = TRUE, bind = FALSE) {
  if (!is.character(string)) string <- as.character(string)
  Pattern <- ifelse(isTRUE(alphaFirst),
                    "(?<=[a-zA-Z])(?=[0-9])", 
                    "(?<=[0-9])(?=[a-zA-Z])")
  out <- strsplit(string, split = Pattern, perl = TRUE)
  if (isTRUE(bind)) {
    require(data.table)
    as.data.table(do.call(rbind, out))
  } else {
    out
  } 
}

预期用途类似于:

library(data.table)
as.data.table(df)[, c("char", "num") := SplitMe(V1, bind = TRUE)][]
#                 V1    char      num
# 1:      adad131341    adad   131341
# 2:     adadar45365  adadar    45365
# 3:      cavsbsb425 cavsbsb      425
# 4: daadvsv46567567 daadvsv 46567567

一旦您知道该模式,您就可以在其他使用 strsplit 的地方使用它,例如 "tidyr" 中的 separate,它可以方便地将值分隔到列中:

library(dplyr)
library(tidyr)
df %>%
  separate(V1, into = c("char", "num"), 
           sep = "(?<=[a-zA-Z])(?=[0-9])", perl = TRUE)
#      char      num
# 1    adad   131341
# 2  adadar    45365
# 3 cavsbsb      425
# 4 daadvsv 46567567