Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to delete everything after nth delimiter in R?

Tags:

regex

r

I have this vector myvec. I want to remove everything after second ':' and get the result. How do I remove the string after nth ':'?

myvec<- c("chr2:213403244:213403244:G:T:snp","chr7:55240586:55240586:T:G:snp" ,"chr7:55241607:55241607:C:G:snp")

result
chr2:213403244   
chr7:55240586
chr7:55241607
like image 974
MAPK Avatar asked Oct 11 '15 05:10

MAPK


People also ask

How do you delete everything after a string?

To remove everything after a specific character in a string:Use the String. split() method to split the string on the character. Access the array at index 0 .

How do I delete everything before a character in R?

Using gsub() Function and \\ It is also possible to remove all characters in front of a point using the gsub function.

How do you delete part of a character in R?

Use gsub() function to remove a character from a string or text in R. This is an R base function that takes 3 arguments, first, the character to look for, second, the value to replace with, in our case we use blank string, and the third input string were to replace.

How do I remove unwanted characters from R?

To remove a character in an R data frame column, we can use gsub function which will replace the character with blank. For example, if we have a data frame called df that contains a character column say x which has a character ID in each value then it can be removed by using the command gsub("ID","",as.


2 Answers

We can use sub. We match one or more characters that are not : from the start of the string (^([^:]+) followed by a :, followed by one more characters not a : ([^:]+), place it in a capture group i.e. within the parentheses. We replace by the capture group (\\1) in the replacement.

sub('^([^:]+:[^:]+).*', '\\1', myvec)
#[1] "chr2:213403244" "chr7:55240586"  "chr7:55241607" 

The above works for the example posted. For general cases to remove after the nth delimiter,

n <- 2
pat <- paste0('^([^:]+(?::[^:]+){',n-1,'}).*')
sub(pat, '\\1', myvec)
#[1] "chr2:213403244" "chr7:55240586"  "chr7:55241607" 

Checking with a different 'n'

n <- 3

and repeating the same steps

sub(pat, '\\1', myvec)
#[1] "chr2:213403244:213403244" "chr7:55240586:55240586"  
#[3] "chr7:55241607:55241607"  

Or another option would be to split by : and then paste the n number of components together.

n <- 2
vapply(strsplit(myvec, ':'), function(x)
            paste(x[seq.int(n)], collapse=':'), character(1L))
#[1] "chr2:213403244" "chr7:55240586"  "chr7:55241607" 
like image 157
akrun Avatar answered Nov 10 '22 03:11

akrun


Here are a few alternatives. We delete the kth colon and everything after it. The example in the question would correspond to k = 2. In the examples below we use k = 3.

1) read.table Read the data into a data.frame, pick out the columns desired and paste it back together again:

k <- 3 # keep first 3 fields only
do.call(paste, c(read.table(text = myvec, sep = ":")[1:k], sep = ":"))

giving:

[1] "chr2:213403244:213403244" "chr7:55240586:55240586"  
[3] "chr7:55241607:55241607"  

2) sprintf/sub Construct the appropriate regular expression (in the case below of k equal to 3 it would be ^((.*?:){2}.*?):.* ) and use it with sub:

k <- 3
sub(sprintf("^((.*?:){%d}.*?):.*", k-1), "\\1", myvec)

giving:

[1] "chr2:213403244:213403244" "chr7:55240586:55240586"  
[3] "chr7:55241607:55241607"  

Note 1: For k=1 this can be further simplified to sub(":.*", "", myvec) and for k=n-1 it can be further simplified to sub(":[^:]*$", "", myvec)

Note 2: Here is a visualization of the regular regular expression for k equal to 3:

^((.*?:){2}.*?):.*

Regular expression visualization

Debuggex Demo

3) iteratively delete last field We could remove the last field n-k times using the last regular expression in Note 1 above like this:

n <- 6 # number of fields
k < - 3 # number of fields to retain
out <- myvec
for(i in seq_len(n-k)) out <- sub(":[^:]*$", "", out)

If we wanted to set n automatically we could optionally replace the hard coded line setting n above with this:

n <- count.fields(textConnection(myvec[1]), sep = ":")

4) locate position of kth colon Locate the positions of the colons using gregexpr and then extract the location of the kth subtracting one from it since we don't want the trailing colon. Use substr to extract that many characters from the respective strings.

k <- 3
substr(myvec, 1, sapply(gregexpr(":", myvec), "[", k) - 1)

giving:

[1] "chr2:213403244:213403244" "chr7:55240586:55240586"  
[3] "chr7:55241607:55241607"  

Note 3: Suppose there are n fields. The question asked to delete everything after the kth delimiter so the solution should work for k = 1, 2, ..., n-1. It need not work for k = n since there are not n delimiters; however, if instead we define k as the number of fields to return then k = n makes sense and, in fact, (1) and (3) work in that case too. (2) and (4) do not work for this extension but we can easily get them to work by using paste0(myvec, ":") as the input instead of myvec.

Note 4: We compare performance:

library(rbenchmark)
benchmark(
 .read.table = do.call(paste, c(read.table(text = myvec, sep = ":")[1:k], sep = ":")),
 .sprintf.sub = sub(sprintf("^((.*?:){%d}.*?):.*", k-1), "\\1", myvec),
 .for = { out <- myvec; for(i in seq_len(n-k)) out <- sub(":[^:]*$", "", out)},
 .gregexpr = substr(myvec, 1, sapply(gregexpr(":", myvec), "[", k) - 1),
  order = "elapsed", replications = 1000)[1:4]

giving:

          test replications elapsed relative
2 .sprintf.sub         1000    0.11    1.000
4    .gregexpr         1000    0.14    1.273
3         .for         1000    0.15    1.364
1  .read.table         1000    2.16   19.636

The solution using sprintf and sub is the fastest although it does use a complex regular expression whereas the others use simpler or no regular expressions and might be preferred on grounds of simplicity.

ADDED Added additional solutions and additional notes.

like image 8
G. Grothendieck Avatar answered Nov 10 '22 01:11

G. Grothendieck