r - Collapse multiple rows of strings into one row based on a condition.



Say I had this data:

df <- data.frame(
   text = c("Treatment1: This text is","on two lines","","Treatment2:This text","has","three lines","","Treatment3: This has one")
1 Treatment1: This text is
2             on two lines
4     Treatment2:This text
5                      has
6              three lines
8 Treatment3: This has one

How would I parse this text so that all the "Treatments" are on their own row with all the text below on the same row?

For example this is the desired output:

1 Treatment1: This text is on two lines
2 Treatment2: This text has three lines                
3 Treatment3: This has one

Can anyone recommend a way to do this?

2 Answers: 

Maybe something like the following.
First, the data in dput format, the best format to share datasets in posts.

df <-
structure(list(text = c("Treatment1: This text is", "on two lines", 
"", "Treatment2:This text", "has", "three lines", "", "Treatment3: This has one"
)), .Names = "text", class = "data.frame", row.names = c("1", 
"2", "3", "4", "5", "6", "7", "8"))

Now the base R code.

fact <- cumsum(grepl("treatment", df$text, , ignore.case = TRUE))
result <- do.call(rbind, lapply(split(df, fact), function(x)
                     trimws(paste(x$text, collapse = " "))))
result <- as.data.frame(result)
names(result) <- "text"
#                                   text
#1 Treatment1: This text is on two lines
#2  Treatment2:This text has three lines
#3              Treatment3: This has one

As Rich Scriven points out in his comment, tapply can greatly simplify the code above. (I didn't see that, sometimes I complicate too much.)

result2 <- data.frame(
    text = tapply(df$text, fact, function(x) trimws(paste(x, collapse = " ")))

all.equal(result, result2)
#[1] "Component “text”: 'current' is not a factor"
x <- gsub("\\s+Treatment", "*BREAK*Treatment",
          as.character(paste(df[[1]], collapse = " ")))
data.frame(text = unlist(strsplit(x, "\\*BREAK\\*")))