1
4

2 回答 2

2

In Iris, unit strings for time coordinates must be specified in the format <time-period> since <epoch>, where <time-period> is a unit of measure of time, such as 'days', or 'years'. This format is specified by udunits2, the library Iris uses to supply valid units and perform unit conversions.

The time coordinate in this case does not have a unit that follows this format, meaning the time coordinate will not have full time coordinate functionality (this partly explains the Attribute Error in the question). To fix this we will need to construct a new time coordinate based on the values and metadata of the existing time coordinate and then replace the cube's existing time coordinate with the new one.

To do this we'll need to:

  1. construct a new time unit based on the metadata contained in the existing time unit
  2. take the existing time coordinate's point values and format them as datetime objects, using the format string specified in the existing time unit
  3. convert the datetime objects from (2.) to an array of floating-point numbers using the new time unit constructed in (1.)
  4. create a new time coordinate from the array constructed in (3.) and the new time unit produced in (1.)
  5. remove the old time coordinate from the cube and add the new one.

Here's the code to do this...

import datetime
import cf_units
import iris
import numpy as np

t_coord = EARTH3.coord('time')

t_unit = t_coord.attributes['invalid_units']
timestep, _, t_fmt_str = t_unit.split(' ')
new_t_unit_str = '{} since 1850-01-01 00:00:00'.format(timestep)
new_t_unit = cf_units.Unit(new_t_unit_str, calendar=cf_units.CALENDAR_STANDARD)

new_datetimes = [datetime.datetime.strptime(str(dt), t_fmt_str) for dt in t_coord.points]
new_dt_points = [new_t_unit.date2num(new_dt) for new_dt in new_datetimes]
new_t_coord = iris.coords.DimCoord(new_dt_points, standard_name='time', units=new_t_unit)

t_coord_dim = cube.coord_dims('time')
cube.remove_coord('time')
cube.add_dim_coord(new_t_coord, t_coord_dim)

I've made an assumption about the best epoch for your time data. I've also made an assumption about the calendar that best describes your data, but you should be able to replace (when constructing new_t_unit) the standard calendar I've chosen with any other valid cf_units calendar without difficulty.

As a final note, it is effectively impossible to change calendar types. This is because different calendar types include and exclude different days. For example, a 360day calendar has a Feb 30 but no May 31 (as it assumes 12 idealised 30 day long months). If you try and convert from a 360day calendar to a standard calendar, problems you hit include what you do with the data from 29 and 30 Feb, and how you fill the five missing days that don't exist in a 360day calendar. For such reasons it's generally impossible to convert calendars (and Iris doesn't allow such operations).

Hope this helps!

于 2017-11-30T11:10:48.147 回答
0

Maybe the answer is not more useful however I write here the function that I made in order to convert the data from %Y%m%d.%f in datetime array.

The function create a perfect datetime array, without missing values, it can be modified to take into account if there are missing times, however a climate model should not have missing data.

def fromEARTHtime2Datetime(dt,timeVecEARTH):
    """
    This function returns the perfect array from the EARTH %Y%m%d.%f time 
    format and convert it to a more useful time, such as the time array
    from the datetime of pyhton, this is WHTOUT any missing data!

    Parameters
    ----------
    dt : string
        This is the time discretization, it can be 1h or 6h, but always it 
        needs to be hours, example dt = '6h'.
        
    timeVecEARTH : array of float
        Vector of the time to be converted. For example the time of the
        EARTH is day as %Y%m%d.%f.
        And only this format can be converted to datetime, for example:
            20490128.0,20490128.25,20490128.5,20490128.75 this will be converted
            in datetime: '2049-01-28 00:00:00', '2049-01-28 60:00:00',
                         '2049-01-28 12:00:00','2049-01-28 18:00:00'

    Returns
    -------
    timeArrNew : datetime
        This is the perfect and WITHOUT any missing data datatime array, 
        for example: DatetimeIndex(['2049-01-28 00:00:00', '2049-01-28 06:00:00',
                                        ...
                                   '2049-02-28 18:00:00', '2049-03-01 00:00:00'],
                                   dtype='datetime64[ns]', length=129, freq='6H')

    """

    dtDay = 24/np.float(dt[:-1])
    partOfDay = np.arange(0,1,1/dtDay)
    hDay = []
    for ip in partOfDay:
        hDay.append('%02.f:00:00' %(24*ip))
    dictHours = dict(zip(partOfDay,hDay))
    
    t0Str = str(timeVecEARTH[0])
    timeAux0 = t0Str.split('.')
    timeAux0 = timeAux0[0][0:4] +'-' + timeAux0[0][4:6] +'-' + timeAux0[0][6:] + ' ' + dictHours[float(timeAux0[1])]
    
    tendStr = str(timeVecEARTH[-1])
    timeAuxEnd = tendStr.split('.')
    timeAuxEnd = timeAuxEnd[0][0:4] +'-' + timeAuxEnd[0][4:6] +'-' + timeAuxEnd[0][6:] + ' ' + dictHours[float(timeAuxEnd[1])]
    
    timeArrNew = pd.date_range(timeAux0,timeAuxEnd, freq=dt)
    
    return timeArrNew

于 2020-12-02T11:29:17.680 回答