Convert netcdf time variable to date object R

I have a netcdf file with time periods, and the time variable has the following typical metadata:

double time(time) ; time:standard_name = "time" ; time:bounds = "time_bnds" ; time:units = "days since 1979-1-1 00:00:00" ; time:calendar = "standard" ; time:axis = "T" ; 

Inside R, I want to convert time to a date object R. I currently achieve this in hard mode by reading the units attribute and breaking the string and using the third record as my origin (assuming the interval is โ€œdaysโ€ and time is 00:00 and etc.):

 require("ncdf4") f1<-nc_open("file.nc") time<-ncvar_get(f1,"time") tunits<-ncatt_get(f1,"time",attname="units") tustr<-strsplit(tunits$value, " ") dates<-as.Date(time,origin=unlist(tustr)[3]) 

This hardware solution works for my specific example, but I was hoping there might be a package in R that handles UNIDATA netcdf conventions for temporary units perfectly and safely converts them to an R date object?

+5
source share
3 answers

No, I know. I have this handy feature using lubridate , which is basically identical to yours.

 getNcTime <- function(nc) { require(lubridate) ncdims <- names(nc$dim) #get netcdf dimensions timevar <- ncdims[which(ncdims %in% c("time", "Time", "datetime", "Datetime", "date", "Date"))[1]] #find time variable times <- ncvar_get(nc, timevar) if (length(timevar)==0) stop("ERROR! Could not identify the correct time variable") timeatt <- ncatt_get(nc, timevar) #get attributes timedef <- strsplit(timeatt$units, " ")[[1]] timeunit <- timedef[1] tz <- timedef[5] timestart <- strsplit(timedef[4], ":")[[1]] if (length(timestart) != 3 || timestart[1] > 24 || timestart[2] > 60 || timestart[3] > 60 || any(timestart < 0)) { cat("Warning:", timestart, "not a valid start time. Assuming 00:00:00\n") warning(paste("Warning:", timestart, "not a valid start time. Assuming 00:00:00\n")) timedef[4] <- "00:00:00" } if (! tz %in% OlsonNames()) { cat("Warning:", tz, "not a valid timezone. Assuming UTC\n") warning(paste("Warning:", timestart, "not a valid start time. Assuming 00:00:00\n")) tz <- "UTC" } timestart <- ymd_hms(paste(timedef[3], timedef[4]), tz=tz) f <- switch(tolower(timeunit), #Find the correct lubridate time function based on the unit seconds=seconds, second=seconds, sec=seconds, minutes=minutes, minute=minutes, min=minutes, hours=hours, hour=hours, h=hours, days=days, day=days, d=days, months=months, month=months, m=months, years=years, year=years, yr=years, NA ) suppressWarnings(if (is.na(f)) stop("Could not understand the time unit format")) timestart + f(times) } 

EDIT: You can also take a look at ncdf4.helpers::nc.get.time.series

EDIT2: note that the recently proposed and currently in development awesome stars package will handle dates automatically, see the first blog post for an example.

EDIT3: Another way is to use the units package that stars uses. One could do something like this: (anyway, not processing the calendar correctly, I'm not sure what units can)

 getNcTime <- function(nc) { ##NEW VERSION, with the units package require(units) require(ncdf4) options(warn=1) #show warnings by default if (is.character(nc)) nc <- nc_open(nc) ncdims <- names(nc$dim) #get netcdf dimensions timevar <- ncdims[which(ncdims %in% c("time", "Time", "datetime", "Datetime", "date", "Date"))] #find (first) time variable if (length(timevar) > 1) { warning(paste("Found more than one time var. Using the first:", timevar[1])) timevar <- timevar[1] } if (length(timevar)!=1) stop("ERROR! Could not identify the correct time variable") times <- ncvar_get(nc, timevar) #get time data timeatt <- ncatt_get(nc, timevar) #get attributes timeunit <- timeatt$units units(times) <- make_unit(timeunit) as.POSIXct(time) } 
+2
source

I could not get the @ AF7 function to work with my files, so I wrote my own. In the function below, a vector POSIXct of dates is created for which the start date, time interval, unit and length are read from the nc file. It works with nc files of many (but probably not all ...) forms or forms.

  ncdate <- function(nc) { ncdims <- names(nc$dim) #Extract dimension names timevar <- ncdims[which(ncdims %in% c("time", "Time", "datetime", "Datetime", "date", "Date"))[1]] # Pick the time dimension ntstep <-nc$dim[[timevar]]$len t <- ncvar_get(nc, timevar) # Extract the timestep count tunits <- ncatt_get(nc, timevar, "units") # Extract the long name of units tspace <- t[2] - t[1] # Calculate time period between two timesteps, for the "by" argument tstr <- strsplit(tunits$value, " ") # Extract string components of the time unit a<-unlist(tstr[1]) # Isolate the unit .ie seconds, hours, days etc. uname <- a[which(a %in% c("seconds","hours","days"))[1]] # Check unit startd <- as.POSIXct(gsub(paste(uname,'since '),'',tunits$value),format="%Y-%m-%d %H:%M:%S") ## Extract the start / origin date tmulti <- 3600 # Declare hourly multiplier for date if (uname == "days") tmulti =86400 # Declare daily multiplier for date ## Rename "seconds" to "secs" for "by" argument and change the multiplier. if (uname == "seconds") { uname <- "secs" tmulti <- 1 } byt <- paste(tspace,uname) # Define the "by" argument if (byt == "0.0416666679084301 days") { ## If the unit is "days" but the "by" interval is in hours byt= "1 hour" ## R won't understand "by < 1" so change by and unit to hour. uname = "hours"} datev <- seq(from=as.POSIXct(startd+t[1]*tmulti),by= byt, units=uname,length=ntstep) } 
+2
source

Thanks for the last feature. Do you know how I can get an information variable (for example, chlo-a or mdl "from a certain latitude, longitude and date. To explain, I have data in netcdf from mercaptor and another table with 3 columns (latitude, longitude, date )) and I want to get mlp (thickness of the mixed layer of ocean density) for my table to create another column (mlp).

Hope this is clear: ยง

0
source

Source: https://habr.com/ru/post/1271429/


All Articles