I am having difficulty converting a vector of integers into dates.
I've imported a dataset from Stata using:
> dataire <- read.dta13("~/lcapm_ireland.dta", convert.factors = TRUE,
generate.factors = FALSE, encoding = "UTF-8", fromEncoding = NULL,
convert.underscore = FALSE, missing.type = FALSE, convert.dates = TRUE,
replace.strl = TRUE, add.rownames = FALSE)
My date variable is a monthly time series starting on January 2000 and formatted as "2000-Jan".
Similarly to R, Stata handles dates as integers but in the latter January 1960 is origin zero for monthly dates. Thus, when importing the dataset into R, I end up with a vector of dates of the form:
> c(478, 479, 480, ...)
In addition, my date variable is:
> class(datem)
[1] "Date"
How can I use as.Date or other functions to transform the time-series of integers in a monthly date variable formatted as "%Y-%b"?
The short answer is that you can't get exactly what you want. This is because
in R, dates with numeric form must include a day.
For successfully importing a Stata date in R, you first can convert the respective
variable in Stata from a monthly to a date-time one:
clear
set obs 1
generate date = monthly("2000-Jan", "YM")
display %tmCCYY-Mon date
2000-Jan
display date
480
replace date = dofm(date)
display %tdCCYY-Mon date
2000-Jan
display date
14610
replace date = cofd(date) + tc(00:00:35)
display %tc date
01jan2000 00:01:40
display %15.0f date
1262304100352
Then in R you can do the following:
statadatetime <- 1262304100352
rdatetime <- as.POSIXct(statadatetime/1000, origin = "1960-01-01")
rdatetime
[1] "2000-01-01 02:01:40 EET"
typeof(rdatetime)
[1] "double"
rdate <- as.Date(rdatetime)
rdate
[1] "2000-01-01"
typeof(rdate)
[1] "double"
You can get the Year-(abbreviated) Month form you want with the following:
rdate = format(rdate,"%Y-%b")
[1] "2000-Jan"
typeof(rdate)
[1] "character"
However, as you can see, this will change the type of rdate holding
the date.
Trying to change it back you get:
rdate <- as.Date(rdate)
Error in charToDate(x) :
character string is not in a standard unambiguous format
This is simpler but you will get a date with day, 1990-03-01.
You have a column vector of integers, DATE_IN_MONTHS, that are months since the origin of time in Stata which is 1960-01-01. In R the origin of time is is 1970-01-01.
With package lubridate one simple changes the origin of time and then adds months:
db <- haven::read_dta('StataDatabase.dta') %>%
dplyr::mutate(., DATE_IN_MONTHS = ymd("1960-01-01") + months(DATE_IN_MONTHS))
Now db$DATE_IN_MONTHS contains c(1990-03-01, 1990-04-01, 1990-05-01,...) where each element is a date in R.
Related
I am trying to convert numeric values into times and dates. I am working with a data set so it would be appreciated if you should show an example using a dataset.
Here are some examples, converting 93537 into 09:35:57 (HH:MM:SS). Additionally, I need to convert 220703 into 22-07-03 (YY:MM:DD).
I will add an example of my code below:
CPLF_data$HMS <- substr(as.POSIXct(sprintf("%04.0f", CPLF_data$StartTime), format='%H%M%S'), 12, 16)
CPLF_data$YMD <- as.POSIXct(CPLF_data$Date, tz="UTC", origin ="1970-01-01", format ="%Y-%M-%D")
The first line is correct however, it does not show seconds.
The second line is incorrect.
Thank you.
I want my final product to be a new column with the times and dates in the correct format with their own columns.
Use chron times class to get the times or if a character string is wanted use as.character on that. Use as.Date to get a Date class object. The sub puts colons between the parts of the time after which we can convert it to times class. The sprintf pads the date with 0 on the left if it is only 5 characters and otherwise leaves it as 6 characters and then we convert that to Date class.
library(chron)
time <- 93537
date <- 220703
tt <- times(sub("(..)(..)$", ":\\1:\\2", time))
tt
## [1] "09:35:37"
as.character(tt)
## [1] "09:35:37"
dd <- as.Date(sprintf("%06d", date), "%y%m%d")
dd
## [1] "2022-07-03"
as.character(dd)
## [1] "2022-07-03"
Try the ymd_hms function in the lubridate package.
output$datetime <- ymd_hms(paste(input$year, input$month, input$day,
input$HH, input$MM, input$SS, sep="-"))
You can enter 00 if you don't have seconds, for example ....
Base R does not have a class for just "time" (of day), as.POSIXct doesn't deal with "times", it deals with "date-times". The lubridate:: package does give number-like HMS values, which may be relevant, but since each row has both date and time, it seems relevant to combine them instead of putting them into separate columns.
CPLF_data |>
transform(
StartTime = as.numeric(StartTime),
Date = as.numeric(Date)
) |>
transform(
DateTime = ISOdate(
2000 + Date %/% 10000, (Date %% 10000) %/% 100, Date %% 100,
StartTime %/% 10000, (StartTime %% 10000) %/% 100, StartTime %% 100)
)
# StartTime Date DateTime
# 1 93537 220703 2022-07-03 09:35:37
Note: I'm assuming that all years are 2-digits and at/after 2000. If this is not true, it's not difficult to work around it with some custom code. Also, over to you if you want to set the timezone of this timestamp by adding tz="US/Mountain" or whichever is more appropriate for the data.
Data
CPLF_data <- data.frame(StartTime = "93537", Date = "220703")
I imported Excel data into R and I have a problem to convert dates.
In R, my data are character and look like :
date<-c('1971-02-00 00:00:00', '1979-06-00 00:00:00')
I would like to convert character into date (MM/YYYY) but the '00' value used for days poses a problem and 'NA' are returned systematically.
It works when I manually replace '00' with '01' and then use as.yearmon, ymd and format. But I have lots of dates to change and I don't know how to change all my '00' into '01' in R.
# data exemple
date1<-c('1971-02-00 00:00:00', '1979-06-00 00:00:00')
# removing time -> doesn't work because of the '00' day
date1c<-format(strptime(date1, format = "%Y-%m-%d"), "%Y/%m/%d")
date1c<-format(strptime(date1, format = '%Y-%m'), '%Y/%m')
# trying to convert character into date -> doesn't work either
date1c<-ymd(date1)
date1c<-strptime(date1, format = "%Y-%m-%d %H:%M:%S")
date1c<-as.Date(date1, format="%Y-%m-%d %H:%M:%S")
date1c<as.yearmon(date1, format='%Y%m')
# everything works if days are '01'
date2<-c('1971-02-01 00:00:00', '1979-06-01 00:00:00')
date2c<-as.yearmon(ymd(format(strptime(date2, format = "%Y-%m-%d"), "%Y/%m/%d")))
date2c
If you have an idea to do it or an another idea to solve my problem, I would be thankful!
Use gsub to replace -00 with -01.
date1<-c('1971-02-01 00:00:00', '1979-06-01 00:00:00')
date1 <- gsub("-00", "-01", date1)
date1c <-format(strptime(date1, format = "%Y-%m-%d"), "%Y/%m/%d")
> date1c
[1] "1971/02/01" "1979/06/01"
Another possibility could be:
as.Date(paste0(substr(date1, 1, 9), "1"), format = "%Y-%m-%d")
[1] "1971-02-01" "1979-06-01"
Here it extracts the first nine characters, pastes it together with 1 and then converts it into a date object.
These alternatives each accept a vector input and produce a vector as output.
Date output
These all will accept a vector as input and produce a Date vector as the output.
# 1. replace first occurrence of '00 ' with '01 ' and then convert to Date
as.Date(sub("00 ", "01 ", date1))
## [1] "1971-02-01" "1979-06-01"
# 2. convert to yearmon class and then to Date
library(zoo)
as.Date(as.yearmon(date1, "%Y-%m"))
## [1] "1971-02-01" "1979-06-01"
# 3. insert a 1 and then convert to Date
as.Date(paste(1, date1), "%d %Y-%m")
## [1] "1971-02-01" "1979-06-01"
yearmon output
Note that if you really are trying to represent just months and years then yearmon class directly represents such objects without the kludge of using an unused day of the month. Such objects are internally represented as a year plus a fraction of a year, i.e. year + 0 for January, year + 1/12 for February, etc. They display in a meaningful way, they sort in the expected manner and can be manipulated, e.g. take the difference between two such objects or add 1/12 to get the next month, etc. As with the others it takes a vector in and produces a vector out.
library(zoo)
as.yearmon(date1, "%Y-%m")
## [1] "Feb 1971" "Jun 1979"
character output
If you want character output rather than Date or yearmon output then these variations work and again accept a vector as input and produce a vector as output:
# 1. replace -00 and everything after that with a string having 0 characters
sub("-00.*", "", date1)
## [1] "1971-02" "1979-06"
# 2. convert to yearmon and then format that
library(zoo)
format(as.yearmon(date1, "%Y-%m"), "%Y-%m")
## [1] "1971-02" "1979-06"
# 3. convert to Date class and then format that
format(as.Date(paste(1, date1), "%d %Y-%m"), "%Y-%m")
## [1] "1971-02" "1979-06"
# 4. pick off the first 7 characters
substring(date1, 1, 7)
## [1] "1971-02" "1979-06"
library(xts)
data <- data.frame(year_week = c("2016-46", "2016-47", "2016-48"),
satisfaction = c(0.25, 0.45, 0.58))
data = xts(data[-1], order.by = as.POSIXct(data$year_week, format = "%Y-%W"))
I want to create an xts object from the data.frame data where the dates keep the format year-week. When I am running the code the columns take the form 2016-12-05 which is incorrect and far from what I am trying to achieve.
This is a variant on a quasi-FAQ on 'by can one not parse year and month as a date': because a date is day and month and year.
Or year, a year and week and day. Otherwise you are indeterminate:
> as.Date(format(Sys.Date(), "%Y-%W-%d"), "%Y-%W-%d")
[1] "2017-12-04"
>
using
> Sys.Date()
[1] "2017-12-04"
> format(Sys.Date(), "%Y-%W-%d")
[1] "2017-49-04"
>
so %W works on input and output provided you also supply a day.
For input data that does not have a day, you could just add a given weekday, say 1:
> as.Date(paste0(c("2016-46", "2016-47", "2016-48"), "-1"), "%Y-%W-%w")
[1] "2016-11-14" "2016-11-21" "2016-11-28"
>
So I currently read in a .cvs file (as a dataframe ndata) that contains timestamp strings that are in a format like the following:
year-month-day hour:minute:second,millisecond
Example: "2016-08-26 19:06:11,380"
I contain the values of the timestamp in ndata$time.
I tried using
ndata$modifiedTime = as.numeric(ndata$time)
But this didn't work because R didn't acknowledge the timestamp as a timestamp but rather as any other string. Is there a way that I can alter/redefine the timestamp string so that I can store these values as a set amount of seconds after time 0 (0000-00-00 00:00:00,000) and actually use these dates in calculations?
Below we input a string s and then convert it to a POSIXct object p and then a number num representing the number of seconds since the Epoch (January 1, 1970).
We then reverse the process, creating a POSIXct object p2 and then formatting that back into a string s2.
We see that the original string s and the string obtained from num are identical.
No packages are used.
s <- "2016-08-26 19:06:11,380" # test input
# convert string to numeric
p <- as.POSIXct(chartr(",", ".", s))
num <- as.numeric(p)
# convert numeric to string
p2 <- as.POSIXct(num, origin = "1970-01-01")
s2 <- chartr(".", ",", format(p, "%Y-%m-%d %H:%M:%OS3"))
identical(s, s2)
## [1] TRUE
Convert to R date-time using as.POSIXct or other methods and then use as.numeric. For manipulating time you can use lubridate which is quite useful:
tstamp <- "2016-08-26 19:06:11,380"
z <- (as.POSIXct(tstamp, format = "%Y-%m-%d %H:%M:%S", tz = "UTC"))
> as.numeric(z)
[1] 1472238371
library(xts)
data <- data.frame(year_week = c("2016-46", "2016-47", "2016-48"),
satisfaction = c(0.25, 0.45, 0.58))
data = xts(data[-1], order.by = as.POSIXct(data$year_week, format = "%Y-%W"))
I want to create an xts object from the data.frame data where the dates keep the format year-week. When I am running the code the columns take the form 2016-12-05 which is incorrect and far from what I am trying to achieve.
This is a variant on a quasi-FAQ on 'by can one not parse year and month as a date': because a date is day and month and year.
Or year, a year and week and day. Otherwise you are indeterminate:
> as.Date(format(Sys.Date(), "%Y-%W-%d"), "%Y-%W-%d")
[1] "2017-12-04"
>
using
> Sys.Date()
[1] "2017-12-04"
> format(Sys.Date(), "%Y-%W-%d")
[1] "2017-49-04"
>
so %W works on input and output provided you also supply a day.
For input data that does not have a day, you could just add a given weekday, say 1:
> as.Date(paste0(c("2016-46", "2016-47", "2016-48"), "-1"), "%Y-%W-%w")
[1] "2016-11-14" "2016-11-21" "2016-11-28"
>