将某些空格替换为制表符 - 分隔符

Replace certain spaces to tabs - delimiters

我有一列 data.frame,其中一些 space 应该是定界符,有些只是 space.

#input data
dat <- data.frame(x=c("A 2 2 textA1 textA2 Z1",
                      "B 4 1 textX1 textX2 textX3 Z2",
                      "C 3 5 textA1 Z3"))
#                               x
# 1        A 2 2 textA1 textA2 Z1
# 2 B 4 1 textX1 textX2 textX3 Z2
# 3               C 3 5 textA1 Z3

需要将其转换为 5 列 data.frame:

#expected output
output <- read.table(text="
A   2   2   textA1 textA2   Z1
B   4   1   textX1 textX2 textX3    Z2
C   3   5   textA1  Z3",sep="\t")
#   V1 V2 V3                   V4 V5
# 1  A  2  2        textA1 textA2 Z1
# 2  B  4  1 textX1 textX2 textX3 Z2
# 3  C  3  5               textA1 Z3

基本上,需要将第一个、第二个、第三个和最后一个 space 更改为制表符(或任何其他分隔符,如果这样更容易编码)。

regex 还没有提供任何有用的东西...

注意 1: 在实际数据中,我必须将第 1、2、3、...、19 和最后 space 替换为制表符。
注意2:V4没有模式,文本可以是任何东西。
注3:最后一列是可变长度的一个单词文本。

尝试

v1 <- gsub("^([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+", '\1,\2,\3,', dat$x)
read.table(text=sub(' +(?=[^ ]+$)', ',', v1, perl=TRUE), sep=",")
#  V1 V2 V3                   V4 V5
#1  A  2  2        textA1 textA2 Z1
#2  B  4  1 textX1 textX2 textX3 Z2
#3  C  3  5               textA1 Z3

或者受@Tensibai 启发的选项 post

n <- 3
fpat <- function(n){
   paste0('^((?:\w+ ){', n,'})([\w ]+)\s+(\w+)$')
}

read.table(text=gsub(fpat(n), "\1'\2' \3", dat$x, perl=TRUE))
#  V1 V2 V3                   V4 V5
#1  A  2  2        textA1 textA2 Z1
#2  B  4  1 textX1 textX2 textX3 Z2
#3  C  3  5               textA1 Z3

更多专栏,

 n <- 19
 v1 <- "A 24 34343 212 zea4 2323 12343 111 dsds 134d 153xd 153xe 153de 153dd dd dees eese tees3 zee2 2353 23335 23353 ddfe 3133"

 read.table(text=gsub(fpat(n), "\1'\2' \3", v1, perl=TRUE), sep='')
 # V1 V2    V3  V4   V5   V6    V7  V8   V9  V10   V11   V12   V13   V14 V15
 #1  A 24 34343 212 zea4 2323 12343 111 dsds 134d 153xd 153xe 153de 153dd  dd
 #  V16  V17   V18  V19                   V20  V21
 #1 dees eese tees3 zee2 2353 23335 23353 ddfe 3133

这可能是一种扭曲的方式,无论您有多少 "words" 都可以使用(并且可以处理您的数据);它基于您 "words" 中字母数字字符的数量与其他字段中字母数字字符数量的比较:

res <- gsub("\w{3,}\K\t(?=\w{3,})", " ", gsub(" ", "\t", dat$x), perl=T)
res
# [1] "A\t2\t2\ttextA1 textA2\tZ1"        "B\t4\t1\ttextX1 textX2 textX3\tZ2" "C\t3\t5\ttextA1\tZ3"

read.table(text=res, sep="\t")
#  V1 V2 V3                   V4 V5
#1  A  2  2        textA1 textA2 Z1
#2  B  4  1 textX1 textX2 textX3 Z2
#3  C  3  5               textA1 Z3

编辑:一种完全不同的方式,仅基于您需要在最后一个之前替换的空格数k

k <- 3 # in your example
res <- sapply(as.character(dat$x), 
              function(x, k){
                 pos_sp <- gregexpr(" ", x)[[1]]
                 x <- strsplit(x, "")[[1]]
                 if (length(pos_sp) > k+1) pos_sp <- pos_sp[c(1:k, length(pos_sp))]
                 x[pos_sp] <- "\t"
                 x <- paste(x, collapse="")
               }, k=k)

read.table(text=res, sep="\t")
#   V1 V2 V3                   V4 V5
# 1  A  2  2        textA1 textA2 Z1
# 2  B  4  1 textX1 textX2 textX3 Z2
# 3  C  3  5               textA1 Z3

列数可变:

library(stringr)
cols <- 3
m <- str_match(dat$x, paste0("((?:\w+ ){" , cols , "})([\w ]+) (\w+)"))
t <- paste0(gsub(" ", "\t", m[,2]), m[,3], "\t", m[,4])

> read.table(text=t,sep="\t")
  V1 V2 V3                   V4 V5
1  A  2  2        textA1 textA2 Z1
2  B  4  1 textX1 textX2 textX3 Z2
3  C  3  5               textA1 Z3

更改列数以告知您之前希望的列数。 对于正则表达式:

  • ((?:\w+ ){3}) 捕获非捕获组 (?:\w+ ) 的 3 次重复 {3} 匹配至少一个字母数字字符 w+ 后跟 space
  • ([\w ]+) (\w+) 从字母数字字符或 space [\w ]+ 后跟 space 捕获自由文本,并用 \w+[= 捕获最后一个词40=]

完成后,粘贴 str_match 返回的 3 个部分,注意用制表符替换第一组 m[,2] 中的 space。

m[,1]是整个匹配所以这里没有用到。


旧答案:

基于固定字段数的基本匹配:

> read.table(text=gsub("(\w+) (\w+) (\w+) ([\w ]+) (\w+)$","\1\t\2\t\3\t\4\t\5",dat$x,perl=TRUE),sep="\t")
  V1 V2 V3                   V4 V5
1  A  2  2        textA1 textA2 Z1
2  B  4  1 textX1 textX2 textX3 Z2
3  C  3  5               textA1 Z3

前面想加多少(\w+)就加多少,\1(向后引用)加多少就加多少