1
votes

Trouver l'année dans des données aléatoires dans R

J'ai 71 colonnes dans un dataframe, dont 10 incluent des données pouvant inclure une année entre 1990 et 2019 au format AAAA (par exemple 2019). Par exemple:

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)

J'essaie de trouver un moyen d'extraire les années des cellules pertinentes et de les insérer dans une nouvelle colonne.

Jusqu'à présent, je suis seulement conscient comment filtrer les données de manière très chronophage. J'ai produit le code suivant, qui commence comme ceci:

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

Cependant, cela prend beaucoup de temps pour l'écrire pour les dix colonnes et les 30 ans. Je suis sûr qu'il existe un moyen plus rapide. Je ne sais pas non plus comment extraire les dates de chacune des cellules correspondantes dans une nouvelle cellule.

Le résultat que je veux ressemble à ceci:

daté_data $ year

Quelqu'un sait-il comment je fais cela? Merci d'avance pour votre aide!


2 commentaires

sont toutes les colonnes avec une année potentielle appelée id_ * , y aura-t-il seulement une année par ligne, et chaque ligne aura-t-elle une année?


vous pouvez utiliser sub () ou regexec () à partir de la base en recherchant des nombres avec 4 chiffres voir plus lien


6 Réponses :


4
votes

Il existe de nombreuses façons. Voici l'un d'entre eux:

Étape 1 : définissez un modèle que vous souhaitez faire correspondre avec l'expression régulière:

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 

Étape 2 : définissez une fonction pour extraire les correspondances brutes:

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

Étape 3 : appliquez la fonction à vos données, par exemple, id_1 :

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

Voici une autre façon, en fait plus simple;)

Il utilise la fonction str_extract du stringr package. Vous installez donc le package et l'activez:

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

et utilisez str_extract pour extraire vos correspondances:

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


2 commentaires

C'est parfait. Merci beaucoup!


Hé, juste un problème. Certaines colonnes ont moins de correspondances que de lignes, donc lorsque j'essaye d'ajouter les valeurs à la nouvelle colonne 'year', j'obtiens cette erreur: Error in $ <-. Data.frame (< code> * tmp * , years, value = c ("2014", "2009", "2019",: le remplacement a 410 lignes, les données en ont 1350`. Savez-vous comment résoudre ce problème? Merci encore pour votre aide



2
votes

En vous basant sur l'exemple de votre question, vous essayez de filtrer toutes les lignes sans années, puis d'extraire l'année de la chaîne. Il semble que chaque ligne ne contient qu'un an. Voici du code pour que vous n'ayez pas à écrire de longues instructions de filtre pour 10 colonnes et 30 ans. Gardez à l'esprit que je n'ai pas vos données donc je n'ai pas pu les tester.

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"))

MODIFIER: d'après votre commentaire, il semble que certaines colonnes ont un an et d'autres pas. Ce que nous faisons à la place est d'extraire l'année de n'importe quelle colonne avec id_ * , puis nous fusionnons les colonnes ensemble. Encore une fois, sans vos données, il est difficile de tester cela.

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 = "|")))


4 commentaires

C'est une excellente solution. Merci!


Hé, merci beaucoup pour ça. Un seul problème - j'obtiens l'erreur suivante car toutes les cellules ne contiennent pas une année: L'année de la colonne doit avoir une longueur de 1350 (le nombre de lignes) ou une, pas 940 . Une idée de la façon dont je peux résoudre cela?


y a-t-il des situations où une variable de id a un an mais pas les autres?


@Oliver J'ai mis à jour avec un petit ajustement. si vous voulez plus d'aide, j'aurai besoin des données ou d'un court extrait des données. Vous pouvez utiliser dput (head (undated_data)) pour donner les 10 premières lignes.



1
votes

En utilisant les méthodes tidyverse:

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

(Notez que le modèle d'expression régulière correspondra à des nombres qui peuvent ne pas être des années, comme 2999; si vos données ont beaucoup de "faux positifs" comme ça, vous mieux vaut peut-être écrire une fonction personnalisée.)


0 commentaires

1
votes

Voici une solution similaire à celle fournie, mais en utilisant dplyr et stringr sur un 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}")))


0 commentaires

0
votes

Voici peut-être une autre solution.

Nous utilisons simplement la fonction gsub () et définissons le motif comme ". (199 [0-9] | 20 [01] [0- 9]). ".

Le modèle capture le texte d'une année entre 1990 et 2019 sous la forme d'un résultat du groupe, en particulier un seul groupe, nous remplaçons donc le texte original par une première chaîne de groupe :)

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"


0 commentaires

1
votes

Solution de base 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 = ", ")


0 commentaires