Je travaille avec une table en désordre grattée sur un site Web, et pour rendre la colonne de date plus utile, je dois nettoyer ce qui a été gratté. Nos données ressemblent à ceci:
cleaning_dates_df <- do.call('rbind', strsplit(mydata$Dates, '-')) %>% as.data.frame() colnames(cleaning_dates_df) <- c('start', 'end') cleaning_dates_df$start <- as.character(cleaning_dates_df$start) cleaning_dates_df$end <- as.character(cleaning_dates_df$end) cleaning_dates_df <- cleaning_dates_df %>% dplyr::mutate(end = ifelse(nchar(end) > 4, end, paste0(trimws(sub("\r\n.*", "", start)), end))) %>% dplyr::mutate(start = ifelse(nchar(start) < 8, start, paste0(trimws(sub("\r\n.*", "", start)), sub(".*\\s", "", start)))) %>% dplyr::mutate(end = trimws(end)) %>% dplyr::mutate(start = trimws(start)) head(cleaning_dates_df, 8)
Chaque jour dans des Dates
est une plage de dates qui devrait vraiment être un startDate
et endDate
. Ce que nous essayons de créer alors, c'est:
> newdata StartDate EndDate points 1 20200910 20200913 500 1 20201008 20201011 500 1 20201029 20201101 500 1 20201119 20201122 500 1 20210121 20210124 500 1 20210304 20210307 500 1 20210429 20210502 500
Nous pouvons supposer que toutes les dates des mois de septembre à décembre sont pour 2020 et toutes les dates des mois de janvier à août sont de 2021.
Ce n'est peut-être pas le code le plus propre, mais j'ai réussi à diviser la colonne Dates en 2 colonnes
mydata <- structure(list(Dates = c("Sep\r\n \r\n 10 - 13", "Oct\r\n \r\n 8 - 11", "Oct 29 - Nov 1", "Nov\r\n \r\n 19 - 22", "Jan\r\n \r\n 21 - 24", "Mar\r\n \r\n 4 - 7", "Apr 29 - May 2"), points = c("500", "500", "500", "500", "500", "550", "500" )), row.names = c(1L, 5L, 8L, 11L, 16L, 23L, 32L), class = "data.frame") > mydata Dates points 1 Sep\r\n \r\n 10 - 13 500 5 Oct\r\n \r\n 8 - 11 500 8 Oct 29 - Nov 1 500 11 Nov\r\n \r\n 19 - 22 500 16 Jan\r\n \r\n 21 - 24 500 23 Mar\r\n \r\n 4 - 7 550 32 Apr 29 - May 2 500
... encore besoin de convertir en YYYYMMDD
3 Réponses :
Je n'appellerais pas ça joli, mais vous pouvez utiliser regex pour récupérer toutes les parties en premier:
mydata # Dates points startdate enddate #1 Sep\r\n \r\n 10 - 13 500 2020-09-10 2020-09-13 #5 Oct\r\n \r\n 8 - 11 500 2020-10-08 2020-10-11 #8 Oct 29 - Nov 1 500 2020-10-29 2020-11-01 #11 Nov\r\n \r\n 19 - 22 500 2020-11-19 2020-11-22 #16 Jan\r\n \r\n 21 - 24 500 2021-01-21 2021-01-24 #23 Mar\r\n \r\n 4 - 7 550 2021-03-04 2021-03-07 #32 Apr 29 - May 2 500 2021-04-29 2021-05-02
Dupliquez le mois lorsqu'un seul mois est mentionné:
mydata$startdate <- as.Date(paste(td$yr1, td$mth1, td$day1, sep="/")) mydata$enddate <- as.Date(paste(td$yr2, td$mth2, td$day2, sep="/"))
Convertissez le mois en numérique, puis décidez si 2020 ou 2021:
td[c("mth1","mth2")] <- lapply(td[c("mth1","mth2")], function(x) match(x, tolower(month.abb))) td[c("yr1","yr2")] <- lapply(td[c("mth1","mth2")], function(x) ifelse(x >= 9, 2020, 2021) )
Construisez les dates à partir de pièces séparées:
td$mth2[td$mth2 == ''] <- td$mth1[td$mth2 == '']
Terminer!:
rgx <- "^([a-z]+)(\\r|\\n|\\s)+(\\d+)\\s\\-\\s([a-z]+)*\\s*(\\d+)$" td <- strcapture(rgx, tolower(mydata$Dates), proto=list(mth1="",x="",day1="",mth2="",day2=""))
Tu peux essayer:
format(start_date, "%Y%m%d")
À moins que vous n'ayez raison de ne pas le faire, il est préférable de conserver les données dans un format de date. Mais si vous en avez besoin comme chaîne de caractères présentée, vous pouvez utiliser format()
, par exemple:
library(lubridate) library(dplyr) d <- do.call(rbind, lapply(str_split(gsub("[\v-]", " ", mydata$Dates), "\\s+"), function(x) if (length(x) == 3) append(x, x[1], after = 2) else x) ) start_date <- as.Date(paste(d[,1], d[,2], "2020", sep = "-"), format = "%b-%d-%Y") end_date <- as.Date(paste(d[,3], d[,4], "2020", sep = "-"), format = "%b-%d-%Y") start_date <- if_else(month(start_date) < 9, start_date + years(1), start_date) end_date <- if_else(month(end_date) < 9, end_date + years(1), end_date) data.frame(start_date, end_date,mydata$points) start_date end_date mydata.points 1 2020-09-10 2020-09-13 500 2 2020-10-08 2020-10-11 500 3 2020-10-29 2020-11-01 500 4 2020-11-19 2020-11-22 500 5 2021-01-21 2021-01-24 500 6 2021-03-04 2021-03-07 550 7 2021-04-29 2021-05-02 500
Voici une solution Base R désordonnée:
mydata <- structure(list(Dates = c("Sep\r\n \r\n 10 - 13", "Oct\r\n \r\n 8 - 11", "Oct 29 - Nov 1", "Nov\r\n \r\n 19 - 22", "Jan\r\n \r\n 21 - 24", "Mar\r\n \r\n 4 - 7", "Apr 29 - May 2"), points = c("500", "500", "500", "500", "500", "550", "500" )), row.names = c(1L, 5L, 8L, 11L, 16L, 23L, 32L), class = "data.frame")
Les données:
# Function to pad dateparts: pad_dateparts => function() pad_dateparts <- function(date_vec){ return(ifelse(nchar(date_vec) == 1, paste0("0", date_vec), date_vec)) } # Store the months for each obersvation: months_ => list of characters months_ <- lapply(regmatches(mydata$Dates, gregexpr( paste0(month.abb, collapse = "|"), mydata$Dates)), function(x) { if (length(x) == 1) { pad_dateparts(match(rep(x, 2), month.abb)) } else{ pad_dateparts(match(x, month.abb)) } } ) # Store the day numbers for each obersvation: days_ => list of characters days_ <- lapply(sapply(trimws(gsub('\\D+',' ', mydata$Dates), "both"), strsplit, "\\s+"), pad_dateparts) # Function to increment years from ordered vector of month parts: # increment_years => function() increment_years <- function(start_year, ordered_month_vec){ return(start_year + cumsum(c(FALSE, diff(as.integer(ordered_month_vec)) < 0))) } # Store the year parts: years_ => list of data.frames years_ <- split(apply(data.frame(do.call(rbind, months_)), 2, function(x){increment_years(2020, x)}), seq_along(months_)) # Create the required data.frame: clean_df => data.frame clean_df <- cbind(setNames( data.frame( do.call(rbind, Map(function(x, y, z) { as.integer(paste0(x, y, z)) }, years_, months_, days_)), row.names = NULL, stringsAsFactors = FALSE ), c("StartDate", "EndDate") ), Points = mydata$points)