convert a netcdf time variable to an R date object

前端 未结 3 1186
孤街浪徒
孤街浪徒 2021-01-01 00:55

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

    double time(time) ;
            time:standard_name = \"t         


        
3条回答
  •  傲寒
    傲寒 (楼主)
    2021-01-01 01:24

    There is not, that I know of. I have this handy function 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: One might also want to take a look at ncdf4.helpers::nc.get.time.series

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

    EDIT3: another way is to use the units package directly, which is what stars uses. One could do something like this: (still not handling the calendar correctly, I'm not sure 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)
    }
    

提交回复
热议问题