2013-11-03 163 views
4

我想前两个冒号分割字符串的列,但不能对任何后续冒号:分割字符串

my.data <- read.table(text=' 

my.string some.data 
123:34:56:78 -100 
87:65:43:21 -200 
a4:b6:c8888 -300 
11:bbbb:ccccc -400 
uu:vv:ww:xx -500', header = TRUE) 

desired.result <- read.table(text=' 

my.string1 my.string2 my.string3 some.data 
123   34   56:78   -100 
87   65   43:21   -200 
a4   b6   c8888   -300 
11   bbbb  ccccc   -400 
uu   vv   ww:xx   -500', header = TRUE) 

我已经广泛搜查,以下问题是最接近我目前的窘境:

Split on first comma in string

谢谢你的任何建议。我更喜欢使用基R.

编辑:

字符数之前第一结肠并不总是2和第一两个冒号之间的字符数并不总是2。所以,我编辑了这个例子来反映这一点。

回答

3

在基础R:

> my.data <- read.table(text=' 
+ 
+ my.string some.data 
+ 123:34:56:78 -100 
+ 87:65:43:21 -200 
+ a4:b6:c8888 -300 
+ 11:bbbb:ccccc -400 
+ uu:vv:ww:xx -500', header = TRUE,stringsAsFactors=FALSE) 
> m <- regexec ("^([^:]+):([^:]+):(.*)$",my.data$my.string) 
> my.data$my.string1 <- unlist(lapply(regmatches(my.data$my.string,m),'[',c(2))) 
> my.data$my.string2 <- unlist(lapply(regmatches(my.data$my.string,m),'[',c(3))) 
> my.data$my.string3 <- unlist(lapply(regmatches(my.data$my.string,m),'[',c(4))) 
> my.data 
     my.string some.data my.string1 my.string2 my.string3 
1 123:34:56:78  -100  123   34  56:78 
2 87:65:43:21  -200   87   65  43:21 
3 a4:b6:c8888  -300   a4   b6  c8888 
4 11:bbbb:ccccc  -400   11  bbbb  ccccc 
5 uu:vv:ww:xx  -500   uu   vv  ww:xx 

你会请参阅我已使用stringsAsFactors=FALSE确保my.string可以作为字符串的向量处理。

+0

这是一个很好的答案,但我想知道'm'中的数字是什么意思? –

+1

'regexec()'返回一个匹配对象,其中每个匹配的第一个元素是每个组开始匹配位置的向量(首先整个匹配,所以第一个显式组是#2,第二个是#3,等等),第二个是矢量匹配长度。 'regmatches()'然后使用该匹配数据从字符串向量中提取匹配的文本。 – Simon

0

难道你不能只在第一个冒号的strsplit(sub(“:\ s *”,XX,x),XX)(如你在其他问题的链接中列出的例子)再次分裂第一个冒号?

+0

我不这么认为。当你尝试它时它有效吗? –

3

使用包stringr

str_match(my.data$my.string, "(.+?):(.+?):(.*)") 

    [,1]   [,2] [,3] [,4] 
[1,] "123:34:56:78" "123" "34" "56:78" 
[2,] "87:65:43:21" "87" "65" "43:21" 
[3,] "a4:b6:c8888" "a4" "b6" "c8888" 
[4,] "11:bbbb:ccccc" "11" "bbbb" "ccccc" 
[5,] "uu:vv:ww:xx" "uu" "vv" "ww:xx" 

UPDATE: 与最新的例子(上图)和哈德利的评论解决方案:

str_split_fixed(my.data$my.string, ":", 3) 
    [,1] [,2] [,3] 
[1,] "123" "34" "56:78" 
[2,] "87" "65" "43:21" 
[3,] "a4" "b6" "c8888" 
[4,] "11" "bbbb" "ccccc" 
[5,] "uu" "vv" "ww:xx" 
+0

还有'str_split_fixed' – hadley

1

用“,”替换前两个“:”,然后拆分“,”。

x <- gsub("([[:alnum:]]*):([[:alnum:]]*):(.)","\\1,\\2,\\3","12:34:56:78") 

strsplit(X “”)

应用到数据帧

a.list <- sapply(my.data$my.string, function(x) strsplit(gsub("([[:alnum:]]*):([[:alnum:]]*):(.)","\\1,\\2,\\3",x),",")) 
a.vect <- unlist(a.list) 
a.df <- as.data.frame(matrix(a.vect,ncol=3,byrow=T), stringsAsFactors = F) 
names(a.df) <- c("my.string1", "my.string2", "my.string3") 
a.df$some.data <- my.data$some.data 
a.df 
+0

这很简洁,但它需要使用字符来代替可能永远不会出现在字符串其余部分的逗号。 – topchef

+0

@topchef真的,像“ZZZZZZZZZZ8888888888”可能会这样:) – ndr

1

我有点太迟了。我的解决方案与早期的答案有很大的重叠。然而,它可能是有用的人:

# Replace first two colons with commas. 
new.string = gsub(pattern="(^[^:]+):([^:]+):(.+$)", 
        replacement="\\1,\\2,\\3", 
        x=my.data$my.string) 

# Split on commas, producing a list. 
split.data = strsplit(new.string, ",") 

# Change list into matrix, then data.frame. 
new.data = data.frame(do.call(rbind, split.data)) 
names(new.data) = paste("my.string", seq(ncol(new.data)), sep="") 

my.data$my.string = NULL 
my.data = cbind(new.data, my.data) 
my.data 

# my.string1 my.string2 my.string3 some.data 
# 1  123   34  56:78  -100 
# 2   87   65  43:21  -200 
# 3   a4   b6  c8888  -300 
# 4   11  bbbb  ccccc  -400 
# 5   uu   vv  ww:xx  -500 

正如@topchef指出,逗号(或其它字符)必须保证是从数据中缺席。

另外,每个字符串中必须至少有两个冒号,否则该模式不匹配任何内容,因此不会发生分裂。