Найти год в случайных данных в R - PullRequest
1 голос
/ 10 января 2020

У меня есть 71 столбец в кадре данных, 10 из которых включают данные, которые могут включать год между 1990 и 2019 в формате YYYY (например, 2019). Например:

id_1 <- c("regkfg_2013", "fsgdf-2014", "f2016sghsg", "gjdg1990_3759")
id_2 <- c("dghdgl2013jg", "2fgdg_2014_hf", "ghdg_2016*89", "gc-hs1990")

Я пытаюсь найти способ извлечь годы из соответствующих ячеек и вставить их в новый столбец.

Пока что я знаю только о том, как фильтровать данные очень трудоемким способом. Я создал следующий код, который начинается следующим образом:

dated_data <- select(undated_data, 1:71) %>% 
                filter(grepl("1990", id_1) | filter(grepl("1990", id_2) | filter(grepl("1991", id_1) | filter(grepl("1991", id_2)

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

Вывод, который я хочу, выглядит следующим образом:

dated_data$year <- c("2013", "2014", "2016", "1990")

Кто-нибудь знает, как я это делаю? Заранее благодарю за помощь!

Ответы [ 6 ]

4 голосов
/ 10 января 2020

Есть много способов. Это один из них:

Шаг 1 : определить шаблон, который вы хотите сопоставить с регулярным выражением:

pattern <- "(1|2)\\d{3}"

Шаг 2 : определить функция для извлечения сырых совпадений:

extract <- function(x) unlist(regmatches(x, gregexpr(pattern, x, perl = T)))

Шаг 3 : применить функцию к вашим данным, например, id_1:

extract(id_1)
[1] "2013" "2014" "2016" "1990"

Вот еще один способ , на самом деле проще;)

Используется функция str_extract из пакета stringr. Таким образом, вы устанавливаете пакет и активируете его:

install.packages("stringr")
library(stringr)

и используете str_extract, чтобы вытащить свои спички:

years <- str_extract(id_1,"(1|2)\\d{3}") 
years
[1] "2013" "2014" "2016" "1990"

РЕДАКТИРОВАТЬ:

Если не каждая строка содержит совпадение и вы хотите сохранить длину векторов / столбцов, вы можете использовать ifelse, чтобы проверить, находит ли регулярное выражение совпадение и, где его нет, поставить NA.

Например, если ваши данные похожи на это (обратите внимание на две добавленные строки, которые не содержат лет):

id_3 <- c("regkfg_2013", "fsgdf-2014", "f2016sghsg", "gjdg1990_3759", "gbgbgbgb", "hnhna25") 

, вы можете настроить тест ifelse следующим образом:

years <- ifelse(grepl("(1|2)\\d{3}", id_3), str_extract(id_3,"(1|2)\\d{3}"), NA)
years
[1] "2013" "2014" "2016" "1990" NA     NA 
2 голосов
/ 10 января 2020

Исходя из примера в вашем вопросе, вы пытаетесь отфильтровать строки без лет, а затем извлечь год из строки. Похоже, что каждая строка содержит только 1 год. Вот некоторый код, чтобы вам не приходилось писать длинные операторы фильтра для 10 столбцов и 30 лет. Имейте в виду, что у меня нет ваших данных, поэтому я не смог их протестировать.

library(tidyverse)

undated_data %>%
  select(1:71) %>%
  filter_at(vars(starts_with("id_"), any_vars(grepl(paste0(1990:2019, collapse = "|"), .)))) %>%
  mutate(year = str_extract(id_1, pattern = paste0(1990:2019, collapse = "|")))

РЕДАКТИРОВАТЬ: на основании вашего комментария похоже, что у некоторых столбцов есть год, а у других - нет. Вместо этого мы извлекаем год из любого столбца с id_*, а затем мы coalesce вместе. Опять же, без ваших данных это сложно проверить.

undated_data %>%
  select(1:71) %>%
  filter_at(vars(starts_with("id_"), any_vars(grepl(paste0(1990:2019, collapse = "|"), .)))) %>%
  mutate_at(vars(starts_with("id_")), list(year = ~str_extract(., pattern = paste0(1990:2019, collapse = "|")))) %>%
  mutate(year = coalesce(ends_with("_year"))) %>%
  select(-ends_with("_year"))
1 голос
/ 04 мая 2020

Базовый раствор R:

# Sample data: id_1; id_2 => character vectors
id_1 <- c("regkfg_2013", "fsgdf-2014", "f2016sghsg", "gjdg1990_3759")
id_2 <- c("dghdgl2013jg", "2fgdg_2014_hf", "ghdg_2016*89", "gc-hs1990")

# Thanks @Chris Ruehlemann: store the date pattern: date_pattern => character scalar
date_pattern <- "(1|2)\\d{3}"

# Convert to data.frame: df => data.frame 
df <- data.frame(id_1, id_2, stringsAsFactors = FALSE)

# Subset the data to only contain date information vectors: dates_subset => data.frame 
dates_subset <- df[,sapply(df, function(x){any(grepl(date_pattern, x))}), drop = FALSE]

# Initialse the year vector: year => character vector: 
df$years <- NA_character_

# Remove punctuation and letters, return valid dates, combine into a, comma-separated string:
# Store the dates found in the string: years => character vector 
df$years[which(rowSums(Vectorize(grepl)(date_pattern, dates_subset)) > 0)] <- 
  apply(sapply(dates_subset, function(x){
    grep(date_pattern,  unlist(strsplit(x, "[[:punct:]]|[a-zA-Z]")), value = TRUE)}), 
    1, paste, collapse = ", ")
1 голос
/ 10 января 2020

Вот решение, аналогичное предоставленному, но с использованием dplyr и stringr на data.frame.

library(stringr)
library(dplyr)

df<-data.frame("X1" = id_1,"X2" = id_2)
#Set in cols the column names from which years are going to be extracted
df %>%
  pivot_longer(cols = c("X1","X2"), names_to = "id") %>%
  arrange(id) %>%
  mutate(new = unlist(str_extract_all(value, pattern = "(1|2)\\d{3}")))
1 голос
/ 10 января 2020

Используя методы tidyverse:

undated_data %>% 
  mutate_at(vars(1:71), 
            funs(str_extract(., "(1|2)[0-9]{3}")))

(Обратите внимание, что шаблон регулярного выражения будет соответствовать числам, которые не могут быть годами, например 2999; если в ваших данных много подобных «ложных срабатываний», то вы можете лучше написать собственную функцию.)

0 голосов
/ 11 января 2020

Здесь может быть другое решение.

Мы просто используем функцию gsub() и устанавливаем шаблон как ". (199 [0-9] | 20 [01] [0-9]).".

Шаблон захватывает текст года между 1990 и 2019 гг. Как результат группы, особенно только одну группу, поэтому мы заменяем исходный текст первой строкой группы:)

library(magrittr)
id_1 <- c("regkfg_2013", "fsgdf-2014", "f2016sghsg", "gjdg1990_3759")
id_2 <- c("dghdgl2013jg", "2fgdg_2014_hf", "ghdg_2016*89", "gc-hs1990")

gsub(".*(199[0-9]|20[01][0-9]).*","\\1",id_1)
# [1] "2013" "2014" "2016" "1990"

gsub(".*(199[0-9]|20[01][0-9]).*","\\1",id_2)
#[1] "2013" "2014" "2016" "1990"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...