Как извлечь только высказывания человека а в разговоре между двумя людьми а и Б


У меня есть запись разговоров между двумя произвольными лицами а и В.

c1 <- "Person A: blabla...something Person B: blabla something else Person A: OK blabla"
c2 <- "Person A: again blabla Person B: blabla something else Person A: thanks blabla"

Фрейм данных выглядит следующим образом:

df <- data.frame(id = rbind(123, 345), conversation = rbind(c1, c2))

df

    id                                                                     conversation
c1 123 Person A: blabla...something Person B: blabla something else Person A: OK blabla
c2 345   Person A: again blabla Person B: blabla something else Person A: thanks blabla
Теперь я хотел бы извлечь только часть человека А и поместить ее в фрейм данных. Результат должен быть:
   id                     person_A
1 123 blabla...something OK blabla
2 345   again blabla thanks blabla
5 8

5 ответов:

Я большой поклонник решения такого рода проблем таким способом, который дает вам доступ ко всем данным (включая дискурс человека Б). Я люблю tidyr S extract за такое разделение столбцов. Я использовал подход do.call(rbind, strsplit())), но мне нравится, насколько чист подход extract.

c1 <- "Person A: blabla...something Person B: blabla something else Person A: OK blabla"
c2 <- "Person A: again blabla Person B: blabla something else Person A: thanks blabla"
c3 <- "Person A: again blabla Person B: blabla something else"
df <- data.frame(id = rbind(123, 345, 567), conversation = rbind(c1, c2, c3))


if (!require("pacman")) install.packages("pacman")
pacman::p_load(dplyr, tidyr)

conv <- strsplit(as.character(df[["conversation"]]), "\\s+(?=Person\\s)", perl=TRUE)

df2 <- df[rep(1:nrow(df), sapply(conv, length)), ,drop=FALSE]
rownames(df2) <- NULL
df2[["conversation"]] <- unlist(conv)

df2 %>%
    extract(conversation, c("Person", "Conversation"), "([^:]+):\\s+(.+)")

##    id   Person          Conversation
## 1 123 Person A    blabla...something
## 2 123 Person B blabla something else
## 3 123 Person A             OK blabla
## 4 345 Person A          again blabla
## 5 345 Person B blabla something else
## 6 345 Person A         thanks blabla
## 7 567 Person A          again blabla
## 8 567 Person B blabla something else


df2 %>%
    extract(conversation, c("Person", "Conversation"), "([^:]+):\\s+(.+)") %>%
    filter(Person == "Person A")    

##    id   Person       Conversation
## 1 123 Person A blabla...something
## 2 123 Person A          OK blabla
## 3 345 Person A       again blabla
## 4 345 Person A      thanks blabla
## 5 567 Person A       again blabla

Или свернуть их, как показано в желаемом выводе:

df2 %>%
    extract(conversation, c("Person", "Conversation"), "([^:]+):\\s+(.+)") %>%
    filter(Person == "Person A") %>%
    group_by(id) %>%
    select(-Person) %>%
    summarise(Person_A =paste(Conversation, collapse=" "))

##    id                     Person_A
## 1 123 blabla...something OK blabla
## 2 345   again blabla thanks blabla
## 3 567                 again blabla

Edit: на самом деле я подозреваю, что ваши данные имеют реальные имена, такие как "Джон Смит" и "человек а". Если это так, то это начальное регулярное выражение split захватит имя и фамилию, которые используют заглавные буквы, а затем двоеточие:

c1 <- "Greg Smith: blabla...something Sue Williams: blabla something else Greg Smith: OK blabla"
c2 <- "Greg Smith: again blabla Sue Williams: blabla something else Greg Smith: thanks blabla"
c3 <- "Greg Smith: again blabla Sue Williams: blabla something else"
df <- data.frame(id = rbind(123, 345, 567), conversation = rbind(c1, c2, c3))r


conv <- strsplit(as.character(df[["conversation"]]), "\\s+(?=([A-Z][a-z]+\\s+[A-Z][a-z]+:))", perl=TRUE)

df2 <- df[rep(1:nrow(df), sapply(conv, length)), ,drop=FALSE]
rownames(df2) <- NULL
df2[["conversation"]] <- unlist(conv)

df2 %>%
    extract(conversation, c("Person", "Conversation"), "([^:]+):\\s+(.+)")

##    id       Person          Conversation
## 1 123   Greg Smith    blabla...something
## 2 123 Sue Williams blabla something else
## 3 123   Greg Smith             OK blabla
## 4 345   Greg Smith          again blabla
## 5 345 Sue Williams blabla something else
## 6 345   Greg Smith         thanks blabla
## 7 567   Greg Smith          again blabla
## 8 567 Sue Williams blabla something else

Использование пакета stringr

Сначала мы разделяем строку, используя "Person A:" в качестве разделителя

library(stringr)
conv.split <- str_split(df$conversation, "Person A: ")

Это даст нам все куски разговора, начатого A с прилагаемым (необязательным) ответом B

Теперь мы удаляем ответы B

conv.split <- lapply(conv.split, function(x){str_split(x, "Person B:.*")})

И, наконец, мы разблокируем каждый элемент и свернем его вместе в строку

sapply(conv.split, function(x){x <- unlist(x); paste(x, collapse = "")})

Результат:

[1] "blabla...something OK blabla" "again blabla thanks blabla" 

Работает также в том случае, когда B начинает разговор, если говорит только один из двух, а также для долгий разговор.

Используя data.table andgsub ' из базы R:

require(data.table)
setDT(df)[, Person_A := gsub(".*Person A:[ ]*(.*)[ ]*Person B.*:[ ]*(.*)$", 
                         "\\1\\2", conversation)][, conversation := NULL]
df
#     id                       Person_A
# 1: 123 blabla...something OK blabla
# 2: 345   again blabla thanks blabla

Это может не сработать для всех ваших случаев. Особенно те, с которых начинается разговор Person B. Дайте мне знать, если это так. Еще попробуйте

df$person_A <- gsub("Person B.*:|Person A:", "", df$conversation)
df <- data.frame(df$id, df$person_A)

Это моя попытка, я также добавил второй разговор, начатый человеком B, и разговор, также закончившийся человеком B, просто чтобы охватить также эти случаи:

c1 <- "Person A: blabla...something Person B: blabla something else Person A: OK blabla"
c2 <- "Person A: again blabla Person B: blabla something else Person A: thanks blabla"
c3 <- "Person A: again blabla Person B: blabla something else"
df <- data.frame(id = rbind(123, 345, 567), conversation = rbind(c1, c2, c3))


df$PersonA <- gsub("(Person A: |Person B: .+? (?<= Person A: )|Person B: .+?\\Z)", "", df$conversation, perl = TRUE)
df$PersonA

То, что я делаю с gsub, это удаление:

  1. Лицо А:
  2. предложения лица Б, за которыми следуют предложения а
  3. предложения Б В конце разговора \Z

Я использовал perl = TRUE, потому что жизнь слишком коротка, чтобы не использовать зеркало заднего вида... хм... оператор назад.