1

I have a data frame:

x <- data.frame(a = letters[1:7], b = letters[2:8], 
   c = c("bla bla    [ text1 ]", "bla bla  [text2]", "how how [text3  ]",
   "wow wow   [ text4a ] [ text4b  ]", "ba ba [ text5a  ][  text5b]", 
    "my text A", "my text B"), stringsAsFactors = FALSE)
x

I want to split column c based on what's between two square brackets [...] in it. If column c contains only one set of square brackets, I want the string to go to the next column. If column c contains two sets of strings surrounded by [ and ], I want only the string between the last [ ] to go to the new column.

Here is how I've done it. It seems complicated and I am using a loop. Is it possible to do it in a more parsimonious way?

library(stringr)

# Counting number of square brackets "[" in column c:
sqrbrack_count <- str_count(x$c, pattern = '\\[')

# Creating a new column:
x$newcolumn <- NA

for(i in 1:nrow(x)){                 # looping through rows of x
  if(sqrbrack_count[i] == 0) next    # do nothing of 0 square brackets
  minilist <- str_split_fixed(x[i, "c"], pattern = '\\[', n = Inf)  # split string
  if(sqrbrack_count[i] == 1) {       # if there is only one square bracket "["
    x[i, "c"] <- minilist[1]
    x[i, "newcolumn"] <- minilist[2]
  } else {                           # if there are >1 square bracket "["
    x[i, "c"] <- paste(minilist[1:2], collapse = "+")
    x[i, "newcolumn"] <- minilist[3]
  }
}
# Replacing renmaning square brackets we don't need anymore:
x$c <- str_replace(x$c, pattern = " \\]", replacement =  "")
x$c <- str_replace(x$c, pattern = "\\]", replacement =  "")
x$newcolumn <- str_replace(x$newcolumn, pattern = " \\]", replacement =  "")
x$newcolumn <- str_replace(x$newcolumn, pattern = "\\]", replacement =  "")
x
Florian
  • 24,425
  • 4
  • 49
  • 80
user3245256
  • 1,842
  • 4
  • 24
  • 51

1 Answers1

2

The following code is a bit shorter and maybe more easily understood since most of the complex logic happens in two lines. I have added comments above these two lines, I think the rest is quite self-explanatory.

library(plyr)
# find all strings between characters '[' and ']'
strmatches = lapply(1:nrow(x), function(y) {regmatches(x$c[y], gregexpr("(?<=\\[).*?(?=\\])", x$c[y], perl=T))[[1]]})
# parse these to a dataframe called 'new_cols'
new_cols = rbind.fill(lapply(strmatches, function(x) {as.data.frame(t(x),stringsAsFactors = F)}))
df = cbind(x,new_cols)
df$c = gsub("\\[.*$", "", x$c) # only keep everything before '['
df$c[!is.na(df$V2)] = paste0(df$c[!is.na(df$V2)], '+',df$V1[!is.na(df$V2)])
df$V1[!is.na(df$V2)] = df$V2[!is.na(df$V2)]
df$V2=NULL
colnames(df)[colnames(df)=="V1"]="newcolumn"

Output:

  a b                   c        V1
1 a b         bla bla        text1 
2 b c           bla bla       text2
3 c d            how how    text3  
4 d e wow wow   + text4a   text4b  
5 e f    ba ba + text5a      text5b
6 f g           my text A      <NA>
7 g h           my text B      <NA>

Hope this helps!

PS: This matches your expected output, but you might want to add some str_trim's in there.

Florian
  • 24,425
  • 4
  • 49
  • 80