Извлечь текст с помощью регулярных выражений в R - PullRequest
0 голосов
/ 29 мая 2018

Я читаю текстовый файл с данными ниже и пытаюсь преобразовать его в фрейм данных

Id:   1
ASIN: 0827229534
  title: Patterns of Preaching: A Sermon Sampler
  group: Book
  salesrank: 396585
  similar: 5  0804215715  156101074X  0687023955  0687074231  082721619X
  reviews: total: 2  downloaded: 2  avg rating: 5

Образец фрейма данных со столбцами и данными

Id | ASIN      | title                                   |group | similar   | avg rating
1  | 0827229534 | Patterns of Preaching: A Sermon Sampler | Book | 0804215715 | 5

Код:

text <- readLines("https://raw.githubusercontent.com/pranavn91/PhD/master/Expt/sample.txt")
ids <- gsub('Id:\\s+', '', text)
ASIN <- gsub('ASIN:\\s+', '', text)
title <- gsub('title:\\s+', '', text)
group <- gsub('group:\\s+', '', text)
similar <- gsub('similar:\\s+', '', text)
rating <- gsub('avg rating:\\s+', '', text)

Это не работает, и я получаю полный текстовый файл в качестве вывода.

Ответы [ 5 ]

0 голосов
/ 04 июня 2018

Я в основном использую здесь baseR (кроме zoo и tiydr), может быть немного длинным кодом, но он может получить желаемые результаты.

options(stringsAsFactors = F)
text <- readLines("https://raw.githubusercontent.com/pranavn91/PhD/master/Expt/sample.txt") #Input file

textdf <- data.frame(text, stringsAsFactors = F) #Reading it
search_words <- c("Id","ASIN","title","group","salesrank","similar","avg rating") #search words as per OP
textdf <- data.frame(text = textdf[grepl(paste0(search_words,collapse = "|"), textdf$text),]) #finding the words and filtering it
textdf$key <- as.numeric(gsub("Id:\\s+(\\d+)","\\1",textdf$text))
View(textdf) # Making a key for each Id

textdf$key <- zoo::na.locf(textdf$key) #Propagating the key for same set of Ids
textdf$text <- gsub( "(.*)(?=avg rating:\\s*\\d+)","", textdf$text, perl=T) #Removing text from before "avg rating" 
textdf$text <- gsub("(similar:\\s*\\d+)(.*)","\\1", textdf$text, perl=T) #Removing text after "similar"
textdf$text <- trimws(textdf$text) ##removing leading and trailing blanks
textdf$text <- sub(":","+",textdf$text) #Replacing the first instance of : so that we can split with plus sign, since plus sign is very uncommon hence took it
splits <- strsplit(textdf$text, "\\+")  #Splitting 
max_len <- max(lengths(splits)) #checking for max length of items in the list
all_lyst_eq_len <- lapply(splits, `length<-`, max_len) #equaling the list
df_final <- data.frame(cbind(do.call('rbind', all_lyst_eq_len), textdf$key))# binding the data frame

df_final <- df_final[!duplicated(df_final),] #Removing the duplicates, there is some dups in data
df_f <- tidyr::spread(df_final, X1,X2) # Reshaping it(transposing)

df_f[,c("Id","ASIN", "title", "group","similar",
            "avg rating")] #Final dataset 

Вывод:

Текстовый файл очень обёрнут, поэтому я добавляю скриншот, мои извинения перед сообществом.

Вывод такой же, как в OP.

enter image description here

0 голосов
/ 29 мая 2018

Здесь используется другой подход, использующий separate_rows и spread для переформатирования текстового файла в кадр данных:

text = readLines(path_to_textfile)

library(dplyr)
library(tidyr)

data.frame(text = text) %>%
  separate_rows(text, sep = "(?<=\\d)\\s+(?=[a-z])") %>%
  extract(text, c("title", "value"), regex = "(?i)([a-z]+):(.+)") %>%
  filter(!title %in% c("reviews", "downloaded")) %>%
  group_by(title) %>%
  mutate(id = 1:n()) %>%
  spread(title, value) %>%
  select(-id)

Результат:

         ASIN group   Id rating salesrank
1  0827229534  Book    1      5    396585
2    12412441  Book    2     10   4225352
                                                         similar
1  5  0804215715  156101074X  0687023955  0687074231  082721619X
2                                         1241242 1412414 124124
                                     title
1  Patterns of Preaching: A Sermon Sampler
2                                Patterns2

Данные:

Id:   1
ASIN: 0827229534
  title: Patterns of Preaching: A Sermon Sampler
  group: Book
  salesrank: 396585
  similar: 5  0804215715  156101074X  0687023955  0687074231  082721619X
  reviews: total: 2  downloaded: 2  avg rating: 5
Id:   2
ASIN: 12412441
  title: Patterns2
  group: Book
  salesrank: 4225352
  similar: 1241242 1412414 124124
  reviews: total: 2  downloaded: 2  avg rating: 10

Примечание:

Оставьте дополнительную пустую строку в конце текстового файла.В противном случае readLines вернет ошибку при попытке прочитать файл.

0 голосов
/ 29 мая 2018

Это только начало.Так как я не профессионал в regExp, я позволю другим творить чудеса.:)

Либо вы определяете правила для каждого объекта и делаете что-то подобное.

ids <- do.call(rbind, regmatches(regexec(pattern = 'Id:\\s+', text = text), x = text))
ASIN <- do.call(rbind, regmatches(regexec(pattern = 'ASIN:\\s+', text = text), x = text))
title <- do.call(rbind, regmatches(regexec(pattern = 'title:\\s+', text = text), x = text))

Или вы определяете общее правило, которое должно работать для каждой строки.Как то так:

sapply(text,  FUN = function(x) {
  regmatches(x, regexec(text = x, pattern = "([^:]+)"))
  })

sapply(text,  FUN = function(x) {
  regmatches(x, regexec(text = x, pattern = "(:.*)"))
})
0 голосов
/ 29 мая 2018

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

library(tidyverse)

text <- list(readLines("https://raw.githubusercontent.com/pranavn91/PhD/master/Expt/sample.txt"))

out <- tibble(text = text)

out <- out %>%
  rowwise() %>%
  mutate(ids = str_extract(text,"Id: .+") %>% na.omit() %>% str_remove("Id: ") %>% str_c(collapse = ", "),
         ASIN = str_extract(text,"ASIN: .+") %>% na.omit() %>% str_remove("ASIN: ") %>% str_c(collapse = ", "),
         title = str_extract(text,"title: .+") %>% na.omit() %>% str_remove("title: ") %>% str_c(collapse = ", "),
         group = str_extract(text,"group: .+") %>% na.omit() %>% str_remove("group: ") %>% str_c(collapse = ", "),
         similar = str_extract(text,"similar: .+") %>% na.omit() %>% str_remove("similar: ") %>% str_c(collapse = ", "),
         rating = str_extract(text,"avg rating: .+") %>% na.omit() %>% str_remove("avg rating: ") %>% str_c(collapse = ", ")
         ) %>%
  ungroup()

Я поместил текст в список, потому что я предполагаю, что вы захотите создать фрейм данных с поиском нескольких элементов.Если вы просто добавляете новый элемент списка для каждой строки readLines, которую вы делаете.

Обратите внимание, что mutate рассматривает каждый элемент в списке как объект, который эквивалентен использованию текста [[1]] ...

Если у вас есть, и элемент встречается несколько раз, вы 'Вам нужно будет добавить %>% str_c(collapse = ", "), как я сделал, в противном случае вы можете удалить его.

ОБНОВЛЕНИЕ на основе новых образцов данных:

Новый образец набора данных создает некоторые различные проблемы, которые не были рассмотрены в моем первоначальном ответе.

Во-первых, все данные находятся в одном файле, и я предполагал, что они будут в нескольких файлах.Можно либо разделить все в список списков, либо разделить все на вектор символов.Я выбрал второй вариант.

Поскольку я выбрал второй вариант, теперь мне нужно обновить свой код для извлечения данных, пока не будет достигнута \ r (необходимо \\ r в R из-за того, как R обрабатывает экранирование).

Далее некоторые поля пусты!Нужно добавить проверку, чтобы увидеть, если результат пуст, и исправить вывод, если он есть.Для этого я использую %>% ifelse(length(.)==0,NA,.).

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

library(tidyverse)

# Read text into a single long file.
text <- read_file("https://raw.githubusercontent.com/pranavn91/PhD/master/Expt/sample.txt")

# Separate each Id: into a character string in a vector
# Use negative lookahead to capture groups that don't have Id: in them.
# Use an or to also capture any non-words that don't have Id: in them.
text <- str_extract_all(text,"Id: (((?!Id:).)|[^(Id:)])+") %>% 
  flatten()

out <- tibble(text = text)

out <- out %>%
  rowwise() %>%
  mutate(ids = str_extract(text,"Id: ((?!\\\\r).)+") %>% na.omit() %>% str_remove("Id: ") %>% str_c(collapse = ", ") %>% ifelse(length(.)==0,NA,.),
         ASIN = str_extract(text,"ASIN: ((?!\\\\r).)+") %>% na.omit() %>% str_remove("ASIN: ") %>% str_c(collapse = ", ") %>% ifelse(length(.)==0,NA,.),
         title = str_extract(text,"title: ((?!\\\\r).)+") %>% na.omit() %>% str_remove("title: ") %>% str_c(collapse = ", ") %>% ifelse(length(.)==0,NA,.),
         group = str_extract(text,"group: ((?!\\\\r).)+") %>% na.omit() %>% str_remove("group: ") %>% str_c(collapse = ", ") %>% ifelse(length(.)==0,NA,.),
         similar = str_extract(text,"similar: ((?!\\\\r).)+") %>% na.omit() %>% str_remove("similar: \\d") %>% str_c(collapse = ", ") %>% ifelse(length(.)==0,NA,.),
         rating = str_extract(text,"avg rating: ((?!\\\\r).)+") %>% na.omit() %>% str_remove("avg rating: ") %>% str_c(collapse = ", ") %>% ifelse(length(.)==0,NA,.)
  ) %>%
  ungroup()
0 голосов
/ 29 мая 2018

РЕДАКТИРОВАТЬ: Исправление моего ответа.

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

library(stringr)

ids <- str_extract(text, 'Id:[ ]*\\S+')
ASIN <- str_extract(text, 'ASIN:[ ]*\\S+')
title <- str_extract(text, 'title:[ ]*\\S+')
group <- str_extract(text, 'group:[ ]*\\S+')
similar <- str_extract(text, 'similar:[ ]*\\S+')
rating <- str_extract(text, 'avg rating:[ ]*\\S+')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...