1
votes

Dans R, convertissez «29 octobre - 1 novembre» en «20201029» et «20201101»

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.

Modifier 1

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


0 commentaires

3 Réponses :


3
votes

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


0 commentaires

3
votes

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


0 commentaires

1
votes

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)


0 commentaires