R strptime: yyyyww conversion using %V behaves strange - r

I was trying to create a mapping from a week key to yyyyww. For example, week-1 is 200113, week-2 is 200114, and so on.
In my system, first week starts on 2001-03-26, week starts on Monday, and weeks of the year range from 01-53.
I was doing a test with %V striptime, but found strange behavior:
day1 <- as.Date("20010326", "%Y%m%d")
format(day1, "%Y%V") # output "200113", which is correct
However, when I do it inversely:
as.Date("200113", format = "%Y%V") # output "2001-11-01"
as.Date("200113 1", format = "%Y%V %u") # output "2001-11-01"
as.Date with %Y%V failed to return me the correct date. Did I miss something?
Additional testing:
format(as.Date("2001-03-26")+7*39, "%Y%V") # returns "200152"
format(as.Date("2001-03-26")+7*40, "%Y%V") # returns "200101"
format(as.Date("2001-12-31"), "%Y%V") # returns "200101"
The last two should return "200201", it seems that R did not automatically increment the year.
Additional testing: what I'm doing now is kind of nasty but working:
date <- as.Date("2001-03-26") + 7*(week_key-1),
year <- year(date),
month <- month(date),
week <- as.numeric(format(date, "%V")),
yyyyww <- ifelse(week==1 & month==12, 100*(year+1)+week, 100*year+week)

Related

How to parse an invalid date with lubridate?

I need to parse dates and have a cases like "31/02/2018":
library(lubridate)
> dmy("31/02/2018", quiet = T)
[1] NA
This makes sense as the 31st of Feb does not exist. Is there a way to parse the string "31/02/2018" to e.g. 2018-02-28 ? So not to get an NA, but an actual date?
Thanks.
We can write a function assuming you would only have dates which could be higher than the actual date and would have the same format always.
library(lubridate)
get_correct_date <- function(example_date) {
#Split vector on "/" and get 3 components (date, month, year)
vecs <- as.numeric(strsplit(example_date, "\\/")[[1]])
#Check number of days in that month
last_day_of_month <- days_in_month(vecs[2])
#If the input date is higher than actual number of days in that month
#replace it with last day of that month
if (vecs[1] > last_day_of_month)
vecs[1] <- last_day_of_month
#Paste the date components together to get new modified date
dmy(paste0(vecs, collapse = "/"))
}
get_correct_date("31/02/2018")
#[1] "2018-02-28"
get_correct_date("31/04/2018")
#[1] "2018-04-30"
get_correct_date("31/05/2018")
#[1] "2018-05-31"
With small modification you can adjust the dates if they have different format or even if some dates are smaller than the first date.

R change quarter to timestamp

Hey I have some data aggregated at quarter level and there is a column contains data like this:
> unique(data$fiscalyearquarter)
[1] "2012Q3" "2010Q3" "2012Q1" "2011Q4" "2012Q4" "2008Q1" "2008Q2" "2010Q4" "2010Q1"
[10] "2009Q2" "2012Q2" "2011Q3" "2013Q2" "2013Q1" "2011Q2" "2013Q4" "2009Q4" "2009Q3"
[19] "2011Q1" "2010Q2" "2013Q3" "2008Q4" "2009Q1" "2014Q1" "2008Q3" "2014Q2"
I am thinking about writing a function that turn a string into a timestamp.
Something like this, split the the string to be year and quarter and then force the quarter to be converted to be month(the middle of the quarter).
convert <- function(myinput = "2008Q2"){
year <- substr(myinput, 1, 4)
quarter <- substr(myinput, 6, 6)
month <- 3 * as.numeric(quarter) - 1
date <- as.Date(paste0(year, sprintf("%02d", month), '01'), '%Y%m%d')
return(date)
}
I have to convert those strings to date format and then analyze it from there.
> convert("2010Q3")
[1] "2010-08-01"
Is there any way beyond my hard coding solution to analyze time series problem at quarterly level?
If x is your vector and you're okay having the date be the first day of the quarter:
library(zoo)
as.Date(as.yearqtr(x))
If you want the date to be the first day of the second month of the quarter like your example, you could hack together something like this:
as.Date(format(as.Date(as.yearqtr(x))+40, "%Y-%m-01"))

as.Date function doesn't work. The output is either "NA" or the year is 2020

I have a string variable. I want to convert it to a Date variable.
I used as.Date but the output is not correct.
I tried format = "%d%b%Y". However it didn't work. The new dates don't make sense to me. for "06MAR2007" I have 2020-03-06. It seems all the days and months are correct. However, I have 2020 for all the years.
I also tried format = %b%d%Y. However, the output is "NA".
The output of (x <- 0; class(x) <- "Date"; x) is 1970-01-01. I even tried to change the origin but still the code didn't work.
a$arrivaldate <- as.Date(a$arrivaldate, format="%d%b%y", origin = "1900-01-01")
I am not exactly sure what is the root cause, but this error will be resolved by changing the date format of the year input.
Example in your case:
date <- '06MAR2007'
day <- substr(date, 1, 2) # Returns 06
month <- substr(date, 3, 5) # Returns MAR
year <- substr(date, 8, 9) #Returns 07
as.Date(paste(day, month, year, sep=''), format = "%d%b%y") # Returns 2007-03-06
format(as.Date(paste(day, month, year, sep=''), format = "%d%b%y"), "%d%m%Y") # Returns 06032007
The downside is, this method will not work for earlier periods e.g. Year 1968 and before. If your input date is 1968, it will return 2068.

Separate Date into week and year

Currently my dataframe has dates displayed in the 'Date' column as 01/01/2007 etc I would like to convert these into a week/year value i.e. 01/2007. Any ideas?
I have been trying things like this and getting no where...
enviro$Week <- strptime(enviro$Date, format= "%W/%Y")
You have to first convert to date, then you can convert back to the week of the year using format, for example:
### Converts character to date
test.date <- as.Date("10/10/2014", format="%m/%d/%Y")
### Extracts only Week of the year and year
format(test.date, format="Week number %W of %Y")
[1] "Week number 40 of 2014"
### Or if you prefer
format(date, format="%W/%Y")
[1] "40/2014"
So, in your case, you would do something like this:
enviro$Week <- format(as.Date(enviro$Date, format="%m/%d/%Y"), format= "%W/%Y")
But remember that the part as.Date(enviro$Date, format="%m/%d/%Y") is only necessary if your data is not in Date format, and you also should put the right format parameter to convert your character to Date, if that is the case.
What is the class of enviro$Date? If it is of class Date there is probably a better way of doing this, otherwise you can try
v <- strsplit(as.character(enviro$Date), split = "/")
weeks <- sapply(v, "[", 2)
years <- sapply(v, "[", 3)
enviro$Week <- paste(weeks, years, sep = "/")

Add correct century to dates with year provided as "Year without century", %y

I have an file with birthdays in %d%b%y format. Some eg.
# "01DEC71" "01AUG54" "01APR81" "01MAY81" "01SEP83" "01FEB59"
I tried to reformat the date as
o108$fmtbirth <- format(as.Date(o108$birth, "%d%b%y"), "%Y/%m/%d")
and this is the result
# "1971/12/01" "2054/08/01" "1981/04/01" "1981/05/01" "1983/09/01" "2059/02/01"
These are birthdays and I see 2054. From this page I see that year values between 00 and 68 are coded as 20 for century. Is there a way to toggle this, in my case I want only 00 to 12 to be coded as 20.
1) chron. chron uses 30 by default so this will convert them converting first to Date (since chron can't read those sorts of dates) reformatting to character with two digit years into a format that chron can understand and finally back to Date.
library(chron)
xx <- c("01AUG11", "01AUG12", "01AUG13") # sample data
as.Date(chron(format(as.Date(xx, "%d%b%y"), "%m/%d/%y")))
That gives a cutoff of 30 but we can get a cutoff of 13 using chron's chron.year.expand option:
library(chron)
options(chron.year.expand =
function (y, cut.off = 12, century = c(1900, 2000), ...) {
chron:::year.expand(y, cut.off = cut.off, century = century, ...)
}
)
and then repeating the original conversion. For example assuming we had run this options statement already we would get the following with our xx :
> as.Date(chron(format(as.Date(xx, "%d%b%y"), "%m/%d/%y")))
[1] "2011-08-01" "2012-08-01" "1913-08-01"
2) Date only. Here is an alternative that does not use chron. You might want to replace "2012-12-31" with Sys.Date() if the idea is that otherwise future dates are really to be set 100 years back:
d <- as.Date(xx, "%d%b%y")
as.Date(ifelse(d > "2012-12-31", format(d, "19%y-%m-%d"), format(d)))
EDIT: added Date only solution.
See response from related thread:
format(as.Date("65-05-14", "%y-%m-%d"), "19%y-%m-%d")
o108$fmtbirth <- format(as.Date(o108$birth, "%d%b%y"), "%Y/%m/%d")
o108$fmtbirth <- as.Date(ifelse(o108$fmtbirth > Sys.Date(),
format(o108$fmtbirth, "19%y-%m-%d"),
format(o108$fmtbirth)))

Resources