在固定间隔处分割字符串

5
我希望能够在固定间隔下分割字符串。我的问题几乎与如何将字符串拆分成给定长度的子字符串?相同,只是我有一个数据集中的字符串列,而不仅仅是一个字符串。
以下是一个示例数据集:
df = read.table(text = "
my.id   X1    
010101   1
010102   1
010103   1
010104   1
020101   1
020112   1
021701   0
021802   0
133301   0
133302   0  
241114   0
241215   0
", header = TRUE, colClasses=c('character', 'numeric'), stringsAsFactors = FALSE)

这是期望的结果。我希望去掉前导零,如下所示:

desired.result = read.table(text = "
A1 A2 A3   X1
 1  1  1   1
 1  1  2   1
 1  1  3   1
 1  1  4   1
 2  1  1   1
 2  1 12   1
 2 17  1   0
 2 18  2   0
13 33  1   0
13 33  2   0
24 11 14   0
24 12 15   0
", header = TRUE, colClasses=c('numeric', 'numeric', 'numeric', 'numeric'), stringsAsFactors = FALSE)

这里有一个循环,看起来接近我可能可以使用。然而,我认为可能有一种更高效的方法。

for(i in 1:nrow(df)) {
     print(substring(df$my.id[i], seq(1, 5, 2), seq(2, 6, 2)))
}

这个apply语句无法工作:

apply(df$my.id, 1,  function(x) substring(df$my.id[x], seq(1, 5, 2), seq(2, 6, 2))   )

谢谢您的建议。我更喜欢使用基本 R 的解决方案。

4个回答

10

我发现将read.fwf应用于textConnection是处理此类问题的最高效和易于理解的方法。它具有内置于read.*函数中的自动类检测的优点。

cbind( read.fwf(file=textConnection(df$my.id), 
              widths=c(2,2,2), col.names=paste0("A", 1:3)), 
     X1=df$X1)
#-----------
   A1 A2 A3 X1
1   1  1  1  1
2   1  1  2  1
3   1  1  3  1
4   1  1  4  1
5   2  1  1  1
6   2  1 12  1
7   2 17  1  0
8   2 18  2  0
9  13 33  1  0
10 13 33  2  0
11 24 11 14  0
12 24 12 15  0

我相信我大约6年前在Rhelp上从Gabor Grothendieck那里学到了这个。

如果您更喜欢正则表达式策略,请查看以下内容,它会在每两个位置插入一个制表符并通过read.table运行。非常紧凑:

read.table(text=gsub('(.{2})','\\1\t',df$my.id) )
#---------
   V1 V2 V3
1   1  1  1
2   1  1  2
3   1  1  3
4   1  1  4
5   2  1  1
6   2  1 12
7   2 17  1
8   2 18  2
9  13 33  1
10 13 33  2
11 24 11 14
12 24 12 15

3

您已经快完成了。请将您的apply更改为sapplyvapply,并更改substring作用的内容:

splt <- sapply(df$my.id, function(x) substring(x, seq(1, 5, 2), seq(2, 6, 2))   )
#this will produce the same thing
splt <- vapply(df$my.id, function(x) substring(x, seq(1, 5, 2), seq(2, 6, 2)),c("","","")   )
#     010101 010102 010103 010104 020101 020112 021701 021802 133301 133302 241114 241215
#[1,] "01"   "01"   "01"   "01"   "02"   "02"   "02"   "02"   "13"   "13"   "24"   "24"  
#[2,] "01"   "01"   "01"   "01"   "01"   "01"   "17"   "18"   "33"   "33"   "11"   "12"  
#[3,] "01"   "02"   "03"   "04"   "01"   "12"   "01"   "02"   "01"   "02"   "14"   "15"

您想将这些数字化。为了与数据框一起使用,矩阵也应该被转置。我们可以使用以下代码同时完成两个步骤:

splt <- apply(splt,1,as.numeric)
    # [,1] [,2] [,3]
 # [1,]    1    1    1
 # [2,]    1    1    2
 # [3,]    1    1    3
 # [4,]    1    1    4
 # [5,]    2    1    1
 # [6,]    2    1   12
 # [7,]    2   17    1
 # [8,]    2   18    2
 # [9,]   13   33    1
# [10,]   13   33    2
# [11,]   24   11   14
# [12,]   24   12   15

现在您需要将这个与旧数据框重新组合。可能是以下内容。
df <- cbind(splt,df)
#    1  2  3  my.id X1
#1   1  1  1 010101  1
#2   1  1  2 010102  1
#3   1  1  3 010103  1
#4   1  1  4 010104  1
#5   2  1  1 020101  1
#6   2  1 12 020112  1
#7   2 17  1 021701  0
#8   2 18  2 021802  0
#9  13 33  1 133301  0
#10 13 33  2 133302  0
#11 24 11 14 241114  0
#12 24 12 15 241215  0

你可以使用类似于 names(df)[1:3] <- c("A1","A2","A3") 的方法根据需要更改列名。

2
使用gsub和一些正则表达式,我会像这样做(不是很优雅,但它能完成工作)
cbind(
as.numeric(gsub('([0-9]{2})([0-9]{2})([0-9]{2})','\\1',df$my.id)),
as.numeric(gsub('([0-9]{2})([0-9]{2})([0-9]{2})','\\2',df$my.id)),
as.numeric(gsub('([0-9]{2})([0-9]{2})([0-9]{2})','\\3',df$my.id)),
df$X1)

    [,1] [,2] [,3] [,4]
 [1,]    1    1    1    1
 [2,]    1    1    2    1
 [3,]    1    1    3    1
 [4,]    1    1    4    1
 [5,]    2    1    1    1
 [6,]    2    1   12    1
 [7,]    2   17    1    0
 [8,]    2   18    2    0
 [9,]   13   33    1    0
[10,]   13   33    2    0
[11,]   24   11   14    0
[12,]   24   12   15    0

编辑

我说这并不是非常优雅,因此我加入了 @mnel 的建议:

x <- gsub('([0-9]{2})([0-9]{2})([0-9]{2})','\\1-\\2-\\3',df$my.id)
do.call(rbind, lapply(strsplit(x,'-'), as.numeric)) 

2
我建议使用 x <- gsub('([0-9]{2})([0-9]{2})([0-9]{2})','\\1-\\2-\\3',df$my.id); do.call(rbind, lapply(strsplit(x,'-), as.numeric)),以避免多次编写和执行 regex - mnel
非常好!我增加了一个按键:strsplit(x,'-')。 - Mark Miller

2

您也可以使用正则表达式来提取每个两位数字的部分。

我通常会结合stringr中的str_extract_all方法一起使用。

do.call(rbind,lapply(str_extract_all(as.character(df[['my.id']]), pattern = '[[:digit:]]{2}'), as.numeric))

如果您想要一个基本解决方案,您可以尝试使用regmatches(gregexpr(pattern, x))替换str_extract_all - sebastian-c

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接