Convert quarter/year format to a date - r

I created a function that coerce a vector of quarters-years format to a vector of dates.
.quarter_to_date(c("Q1/13","Q2/14"))
[1] "2013-03-01" "2014-06-01"
This the code of my function.
.quarter_to_date <-
function(x){
ll <- strsplit(gsub('Q([0-9])[/]([0-9]+)','\\1,\\2',x),',')
res <- lapply(ll,function(x){
m <- as.numeric(x[1])*3
m <- ifelse(nchar(m)==1,paste0('0',m),as.character(m))
as.Date(paste(x[2],m,'01',sep='-'),format='%y-%m-%d')
})
do.call(c,res)
}
My function works fine but it looks long and a little bit complicated. I think that this should be already done in other packages( lubridate for example) But I can't find it. Can someone help me to simplify this code please?

1) The zoo package has a "yearqtr" class. Convert to that and then to "Date" class:
library(zoo)
x <- c("Q1/13","Q2/14")
as.Date(as.yearqtr(x, format = "Q%q/%y"))
## [1] "2013-01-01" "2014-04-01"
2) Alternately use this to get the last day of the quarter instead of the first:
as.Date(as.yearqtr(x, format = "Q%q/%y"), frac = 1)
## [1] "2013-03-31" "2014-06-30"
3) Also consider not converting to "Date" class at all and just using "yearqtr" class directly:
as.yearqtr(x, format = "Q%q/%y")
## [1] "2013 Q1" "2014 Q2"

Related

parse multiple date formats by index in R

I'm trying to parse multiple date formats based on their position in a vector of dates. At some the data switched the format it used from y/m/d to y/d/m. This is annoying for dates like 2010/07/03 where specifying the order in lubridate .
This is an example of dates
datevec <- c("2011/07/01", "2011/07/02", "2011/07/03", "2011/02/07" )
The dates are set up so before a certain row the dates are one format and after another row the dates are another format, so I'm trying to provide an index to the function
when I tried to parse them using this plus lubridate it only returned 3 dates.
lapply(datevec, function(x, i) ifelse( x[i] <4, parse_date_time(x, "%Y-%m-%d"), parse_date_time(x,"%Y-%d-%m" )) )
1) If we changed the ifelse in the question to a plain if then the basic idea in the question works with appropriate modifications. Note that it gives a list L so assuming we really want a vector we add the last line of code.
f <- function(x, i) if (i < 4)
parse_date_time(x, "ymd") else parse_date_time(x, "ydm")
L <- Map(f, datevec, seq_along(datevec), USE.NAMES = FALSE)
do.call("c", L)
## [1] "2011-07-01 UTC" "2011-07-02 UTC" "2011-07-03 UTC" "2011-02-07 UTC"
2) Use the ifelse on the format part rather than on the date part and use as.Date instead of parse_date_time:
ix <- seq_along(datevec)
as.Date(datevec, ifelse(ix < 4, "%Y/%m/%d", "%Y/%d/%m"))
## [1] "2011-07-01" "2011-07-02" "2011-07-03" "2011-07-02"
3) Convert the first 3 using ymd and the rest using ydm and then concatenate.
c(ymd(head(datevec, 3)), ydm(tail(datevec, -3)))
## [1] "2011-07-01" "2011-07-02" "2011-07-03" "2011-07-02"
4) or with only base R:
c(as.Date(head(datevec, 3)), as.Date(tail(datevec, -3), "%Y/%d/%m"))
## [1] "2011-07-01" "2011-07-02" "2011-07-03" "2011-07-02"
5) Another approach is to convert the later dates using string manipulation so that all the dates are in the same format and then use as.Date or ymd:
ix <- seq_along(datevec)
swap <- sub("(..)/(..)$", "\\2/\\1", datevec)
as.Date(ifelse(ix < 4, datevec, swap))
## [1] "2011-07-01" "2011-07-02" "2011-07-03" "2011-07-02"
6) The above codes return Date class, which is more appropriate for dates without times but if for some reason you really need POSIXct use as.POSIXct on the above or else use parse_date_time like this:
c(parse_date_time(head(datevec, 3), "ymd"), parse_date_time(tail(datevec, -3), "ydm"))
## [1] "2011-07-01 UTC" "2011-07-02 UTC" "2011-07-03 UTC" "2011-07-02 UTC"

Converting characters like "01APR2020" to date class R

I have the following column in my dataframe
> df$dates
[1] "01APR2020" "01JUN2020" "01MAR2020" "01MAY2020" "02APR2020" "02JUN2020"
[7] "02MAR2020"
I would like to format this to an object of Date class, so I want my output to look like this
> df$dates
[1] "01-04" "01-06" "01-03" "01-05" "02-04" "02-06"
[7] "02-03"
And I would like to order them from the oldest to the newest.
Edit:
For example I tried this but it doesn't work:
> format(as.Date("01APR2020", "%d%b%Y"), "%d-%m")
[1] NA
Thanks!
Just use the anydate() function from the anytime package
R> anydate(c("01APR2020", "01JUN2020", "01MAR2020"))
[1] "2020-04-01" "2020-06-01" "2020-03-01"
R>
It's idea is to not require a format for a variety of common and sensible date (or datetime) inputs. Once they are parsed, putting out day and months is easy too:
R> format(anydate(c("01APR2020", "01JUN2020", "01MAR2020")), "%d-%m")
[1] "01-04" "01-06" "01-03"
R>
We can use as.Date with format
df$dates <- format(as.Date(df$dates, "%d%b%Y"), "%d-%m")
df$dates
#[1] "01-04" "01-06" "01-03" "01-05" "02-04" "02-06" "02-03"
Or using lubridate
library(lubridate)
df$dates <- format(dmy(df$dates), "%d-%m")
NOTE: Both the solutions work on R 4.0
data
df <- data.frame(dates = c("01APR2020" ,"01JUN2020", "01MAR2020",
"01MAY2020", "02APR2020" ,"02JUN2020" , "02MAR2020"))

Convert dates to text in R

I have the following dataset with dates (YYYY-MM-DD):
> dates
[1] "20180412" "20180424" "20180506" "20180518" "20180530" "20180611" "20180623" "20180705" "20180717" "20180729"
I want to convert them in:
DD-MMM-YYYY but with the month being text. For example 20180412 should become 12Apr2018
Any suggestion on how to proceed?
M
You can try something like this :
# print today's date
today <- Sys.Date()
format(today, format="%B %d %Y") "June 20 2007"
where The following symbols can be used with the format( ) function to print dates 1
You need to first parse the text strings as Date objects, and then format these Date objects to your liking to have the different text output:
R> library(anytime) ## one easy way to parse dates and times
R> dates <- anydate(c("20180412", "20180424", "20180506", "20180518", "20180530",
+ "20180611", "20180623", "20180705", "20180717", "20180729"))
R> dates
[1] "2018-04-12" "2018-04-24" "2018-05-06" "2018-05-18" "2018-05-30"
[6] "2018-06-11" "2018-06-23" "2018-07-05" "2018-07-17" "2018-07-29"
R>
R> txtdates <- format(dates, "%d%b%Y")
R> txtdates
[1] "12Apr2018" "24Apr2018" "06May2018" "18May2018" "30May2018"
[6] "11Jun2018" "23Jun2018" "05Jul2018" "17Jul2018" "29Jul2018"
R>
You could use the as.Date() and format() functions:
dts <- c("20180412", "20180424", "20180506", "20180518", "20180530",
"20180611", "20180623")
format(as.Date(dts, format = "%Y%m%d"), "%d%b%Y")
More information here
Simply use as.POSIXct and as.format:
dates <- c("20180412", "20180424", "20180506")
format(as.POSIXct(dates, format="%Y%m%d"),format="%d%b%y")
Output:
[1] "12Apr18" "24Apr18" "06May18"

Fixing date format in R

I have three data tables in R. Each one has a date column. The tables are vix_data,gold_ohlc_data,btc_ohlc_data. They are formatted as follows:
head(vix_data$Date)
[1] 1/2/04 1/5/04 1/6/04 1/7/04 1/8/04 1/9/04
3435 Levels: 1/10/05 1/10/06 1/10/07 1/10/08 1/10/11 ... 9/9/16
head(gold_ohlc_data$date)
[1] 8/23/17 8/22/17 8/21/17 8/18/17 8/17/17 8/16/17
2519 Levels: 1/10/08 1/10/11 1/10/12 1/10/13 1/10/14 ... 9/9/16
head(btc_ohlc_data$Date)
[1] "2017-08-23" "2017-08-22" "2017-08-21" "2017-08-20" "2017-08-19"
[6] "2017-08-18"
How can I change the date column in the vix_data and gold_ohlc_data tables to match the btc_ohlc_data format? I have tried several methods, for example using as.Date to transform each column- but this usually messes up the values and inserts a lot of N/A's
An option is to use functions from the package lubridate. The users need to know which one is day and which one is month to select the right function to use, such as dmy or mdy
# Load package
library(lubridate)
# Create example string
date1 <- c("1/2/04", "1/5/04", "1/6/04", "1/7/04", "1/8/04", "1/9/04")
date2 <- c("8/23/17", "8/22/17", "8/21/17", "8/18/17", "8/17/17", "8/16/17")
# Convert to date class
dmy(date1)
# [1] "2004-02-01" "2004-05-01" "2004-06-01" "2004-07-01" "2004-08-01" "2004-09-01"
mdy(date1)
# [1] "2004-01-02" "2004-01-05" "2004-01-06" "2004-01-07" "2004-01-08" "2004-01-09"
mdy(date2)
# [1] "2017-08-23" "2017-08-22" "2017-08-21" "2017-08-18" "2017-08-17" "2017-08-16"
Look into the package lubridate. lubridate::dmy() and ymd() should handle this just fine.
It looks like your data are read in as factors, so first you'll have to change them to characters. Then after that you can convert it to a date and specify the input format where %m represents the numerical month, %d represents the day, and %y represents the 2-digit year.
x <- c('1/2/04', '1/5/04', '1/6/04', '1/7/04', '1/8/04', '1/9/04')
y <- as.Date(x, format = "%m/%d/%y")
y
[1] "2004-01-02" "2004-01-05" "2004-01-06" "2004-01-07" "2004-01-08"
[6] "2004-01-09"
Are you sure you're specifying as.Date correctly? For example, do you have %y, instead of %Y?
I did the following and it worked:
> vix <- c("1/2/04", "1/5/04", "1/6/04", "1/7/04", "1/8/04", "1/9/04")
> vix<- as.factor(vix)
> vix
[1] 1/2/04 1/5/04 1/6/04 1/7/04 1/8/04 1/9/04
Levels: 1/2/04 1/5/04 1/6/04 1/7/04 1/8/04 1/9/04
> as.Date(vix, "%m/%d/%y")
[1] "2004-01-02" "2004-01-05" "2004-01-06" "2004-01-07" "2004-01-08" "2004-01-09"

as.Date returning NA while converting from 'ddmmmyyyy'

I am trying to convert the string "2013-JAN-14" into a Date as follow :
sdate1 <- "2013-JAN-14"
ddate1 <- as.Date(sdate1,format="%Y-%b-%d")
ddate1
but I get :
[1] NA
What am I doing wrong ? should I install a package for this purpose (I tried installing chron) .
Works for me. The reasons it doesn't for you probably has to do with your system locale.
?as.Date has the following to say:
## This will give NA(s) in some locales; setting the C locale
## as in the commented lines will overcome this on most systems.
## lct <- Sys.getlocale("LC_TIME"); Sys.setlocale("LC_TIME", "C")
x <- c("1jan1960", "2jan1960", "31mar1960", "30jul1960")
z <- as.Date(x, "%d%b%Y")
## Sys.setlocale("LC_TIME", lct)
Worth a try.
This can also happen if you try to convert your date of class factor into a date of class Date. You need to first convert into POSIXt otherwise as.Date doesn't know what part of your string corresponds to what.
Wrong way: direct conversion from factor to date:
a<-as.factor("24/06/2018")
b<-as.Date(a,format="%Y-%m-%d")
You will get as an output:
a
[1] 24/06/2018
Levels: 24/06/2018
class(a)
[1] "factor"
b
[1] NA
Right way, converting factor into POSIXt and then into date
a<-as.factor("24/06/2018")
abis<-strptime(a,format="%d/%m/%Y") #defining what is the original format of your date
b<-as.Date(abis,format="%Y-%m-%d") #defining what is the desired format of your date
You will get as an output:
abis
[1] "2018-06-24 AEST"
class(abis)
[1] "POSIXlt" "POSIXt"
b
[1] "2018-06-24"
class(b)
[1] "Date"
My solution below might not work for every problem that results in as.Date() returning NA's, but it does work for some, namely, when the Date variable is read in in factor format.
Simply read in the .csv with stringsAsFactors=FALSE
data <- read.csv("data.csv", stringsAsFactors = FALSE)
data$date <- as.Date(data$date)
After trying (and failing) to solve the NA problem with my system locale, this solution worked for me.

Resources