我想定期拆分一个字符串。我的问题几乎与这个问题相同: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的解决方案。
答案 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
更改为sapply
或vapply
,并更改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))