2
votes

I have a dataframe with speech data in column Turn:

test <- data.frame(
  Turn = c("Hi. I'm you an' you are me cos",
          "she'd've been so happy cos with all this stuff goin' on",
          "but we're in this together, because y' know things happens",
          "so you can't, cos well, ah because you know why!",
          "not now because it's too late!"), stringsAsFactors = F)

I want to subset the dataframe on those rows in which there are at least four words prior to cos and/or because. To this end I compute the indices of cosand because in their Turn:

test$Index <- sapply(strsplit(test$Turn, " "), function(x) which(x == 'cos'|x == 'because'))
test
                                                        Turn Index
1                             Hi. I'm you an' you are me cos     8
2    she'd've been so happy cos with all this stuff goin' on     5
3 but we're in this together, because y' know things happens     6
4           so you can't, cos well, ah because you know why!  4, 7
5                             not now because it's too late!     3

In one row there is more than one index. That's why my attempt at subsetting like this fails:

test[test$Index >= 5,]
Error in `[.data.frame`(test, test$Index >= 5, ) : 
  (list) object cannot be coerced to type 'double'

How can I subset test by disregarding the second listed Index value?

Expected result:

test
                                                        Turn Index
1                             Hi. I'm you an' you are me cos     8
2    she'd've been so happy cos with all this stuff goin' on     5
3 but we're in this together, because y' know things happens     6

I'd be grateful for any answer, including one that that does not use the detour via the indices but uses a regex pattern for the subsetting procedure.

EDIT:

The solution, within the sapply paradigm, is really quite simple by selecting just the first value of the listed objects:

sapply(test$Index, function(x) x[1])
[1] 4 5 6 4 3
2
It is not clear what you consider a word. Why is the expected output just those three strings? - Wiktor Stribiżew
Why does line 4 not match? There are 6 words before because? - Ian Campbell
Line 4 does not match because there is cos already in position 4. - Chris Ruehlemann
@WiktorStribiżew I consider a word anything separated by whitespace, i.e., Hi. counts as one word just as she'd'vecounts as one word. - Chris Ruehlemann
Do I understand it right that if cos or because appear as the first, second, third or fourth word in the string you would like to skip/omit/filter out the string? - Wiktor Stribiżew

2 Answers

1
votes

A solution based in the tidyverse could look as follows.

library(dplyr)
library(purrr)
library(stringr)

test %>%
  mutate(index = map(str_split(Turn, ' '), 
                     ~ str_which(., 'cos|because')[1])) %>%
  filter(index >= 5)

#                                                         Turn index
# 1                             Hi. I'm you an' you are me cos     8
# 2    she'd've been so happy cos with all this stuff goin' on     5
# 3 but we're in this together, because y' know things happens     6
1
votes

I hope this will give you an idea:

test <- data.frame(
  Turn = c("Hi. I'm you an' you are me cos",
          "she'd've been so happy cos with all this stuff goin' on",
          "but we're in this together, because y' know things happens",
          "so you can't, cos well, ah because you know why!",
          "not now because it's too late!"), stringsAsFactors = F)
rx <- "^\\s*(?:\\S+\\s+){0,3}(?:cos|because)\\b.*(*SKIP)(*F)|(?:\\S+[\\s,]+){4}\\b(cos|because)\\b"
Turn <- test[grepl(rx, test$Turn, perl=TRUE),]
split <- strsplit(Turn, "\\b(cos|because)\\b")
Index <- sapply(split, function(x) lengths(strsplit(trimws(x[[1]]), "\\s+"))+1)
test <- data.frame(Turn, Index, stringsAsFactors = F)
test

Output:

                                                       Turn Index
1                             Hi. I'm you an' you are me cos     8
2    she'd've been so happy cos with all this stuff goin' on     5
3 but we're in this together, because y' know things happens     6

See the R demo and the main regex demo.

Regex details:

  • ^\s*(?:\S+\s+){0,3}(?:cos|because)\b.*(*SKIP)(*F) - match start of stirng, then zero to three words and then cos or because as a whole word and the rest of the string and then skip the match
  • | - or
  • (?:\S+[\s,]+){4}\b(cos|because)\b - match cos or because prepended with four words.