定期拆分字符串

时间:2013-02-19 00:51:04

标签: string r

我想定期拆分一个字符串。我的问题几乎与这个问题相同:How to split a string into substrings of a given length?,除了我在数据集中有一列字符串而不是一个字符串。

以下是一个示例数据集:

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 个答案:

答案 0 :(得分:10)

我发现应用于read.fwf的{​​{1}}是最有效且易于理解的方法。它具有内置于read。*函数中的自动类检测的优点。

textConnection

(我相信大约6年前我从Rarep的Gabor Grothendieck那里学到了这一点。)

如果您更喜欢正则表达式策略,那么请查看每两个位置插入一个选项卡并通过read.table运行它。非常紧凑:

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

答案 1 :(得分: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 :(得分: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)) 

答案 3 :(得分:2)

您还可以使用regex提取每个两位数的部分。

我将此与str_extract_all

中的stringr结合使用
do.call(rbind,lapply(str_extract_all(as.character(df[['my.id']]), pattern = '[[:digit:]]{2}'), as.numeric))