首页 文章

将Matlab的datenum格式转换为Python

提问于
浏览
13

我刚开始从Matlab迁移到Python 2.7,我在阅读我的.mat文件时遇到了一些麻烦 . 时间信息以Matlab的datenum格式存储 . 对于那些不熟悉它的人:

序列日期编号表示日历日期,作为自固定基准日期以来经过的天数 . 在MATLAB中,序列号1是0000年1月1日.MATLAB还使用序列时间来表示从午夜开始的天数;例如,下午6点等于0.75连续日 . 所以MATLAB中的字符串'31 -Oct-2003,6:00 PM'是日期号731885.75 . (取自Matlab文档)

我想将其转换为Pythons时间格式,我找到this tutorial . 简而言之,作者说明了这一点

如果你使用python的datetime.fromordinal(731965.04835648148)解析它,那么结果可能看起来合理[...]

(在任何进一步的转换之前),这对我不起作用,因为datetime.fromordinal需要一个整数:

>>> datetime.fromordinal(731965.04835648148) 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: integer argument expected, got float

虽然我可以将它们整理成日常数据,但实际上我需要输入精确的时间序列 . 有没有人有这个问题的解决方案?我想避免重新格式化我的.mat文件,因为它们中有很多,我的同事也需要使用它们 .

如果有帮助,someone else asked for the other way round . 可悲的是,我对Python太新了,无法真正理解那里发生的事情 .

/ edit(2012-11-01):这已在上面发布的教程中修复 .

5 回答

  • 15

    使用pandas,可以在尊重小数部分的同时转换整个datenum值数组:

    import numpy as np
    import pandas as pd
    datenums = np.array([737125, 737124.8, 737124.6, 737124.4, 737124.2, 737124])
    timestamps = pd.to_datetime(datenums-719529, unit='D')
    

    值719529是Unix纪元启动(1970-01-01)的datenum值,这是pd.to_datetime()的默认值 origin .

    我使用以下Matlab代码来设置它:

    datenum('1970-01-01')  % gives 719529
    datenums = datenum('06-Mar-2018') - linspace(0,1,6)  % test data
    datestr(datenums)  % human readable format
    
  • 9

    只是 Build 并添加到以前的评论 . 关键是当天计数由 datetime 和类 datetime 中的构造函数 fromordinal 以及相关的子类执行 . 例如,从Python Library Reference for 2.7,一个读取 fromordinal

    返回对应于公历格里高利序数的日期,其中第1年1月1日有序数1.除非1 <= ordinal <= date.max.toordinal(),否则引发ValueError .

    但是,公元0年仍然是一个(跳跃)年份,因此仍有366天需要考虑 . (闰年,就像2016年,恰好是四年前的四十四年 . )

    这些是我用于类似目的的两个功能:

    import datetime 
    
    def datetime_pytom(d,t):
    '''
    Input
        d   Date as an instance of type datetime.date
        t   Time as an instance of type datetime.time
    Output
        The fractional day count since 0-Jan-0000 (proleptic ISO calendar)
        This is the 'datenum' datatype in matlab
    Notes on day counting
        matlab: day one is 1 Jan 0000 
        python: day one is 1 Jan 0001
        hence an increase of 366 days, for year 0 AD was a leap year
    '''
    dd = d.toordinal() + 366
    tt = datetime.timedelta(hours=t.hour,minutes=t.minute,
                           seconds=t.second)
    tt = datetime.timedelta.total_seconds(tt) / 86400
    return dd + tt
    
    def datetime_mtopy(datenum):
    '''
    Input
        The fractional day count according to datenum datatype in matlab
    Output
        The date and time as a instance of type datetime in python
    Notes on day counting
        matlab: day one is 1 Jan 0000 
        python: day one is 1 Jan 0001
        hence a reduction of 366 days, for year 0 AD was a leap year
    '''
    ii = datetime.datetime.fromordinal(int(datenum) - 366)
    ff = datetime.timedelta(days=datenum%1)
    return ii + ff
    

    希望这有助于并且乐于纠正 .

  • 3

    您链接到解决方案,它有一个小问题 . 就是这个:

    python_datetime = datetime.fromordinal(int(matlab_datenum)) + timedelta(days=matlab_datenum%1) - timedelta(days = 366)
    

    更长的解释可以找到here

  • 2

    为了防止对其他人有用,这里有一个从Matlab mat文件加载时间序列数据的完整示例,使用carlosdc的答案(定义为函数)将Matlab datenums的向量转换为日期时间对象列表,然后绘制为与熊猫的时间序列:

    from scipy.io import loadmat
    import pandas as pd
    import datetime as dt
    import urllib
    
    # In Matlab, I created this sample 20-day time series:
    # t = datenum(2013,8,15,17,11,31) + [0:0.1:20];
    # x = sin(t)
    # y = cos(t)
    # plot(t,x)
    # datetick
    # save sine.mat
    
    urllib.urlretrieve('http://geoport.whoi.edu/data/sine.mat','sine.mat');
    
    # If you don't use squeeze_me = True, then Pandas doesn't like 
    # the arrays in the dictionary, because they look like an arrays
    # of 1-element arrays.  squeeze_me=True fixes that.
    
    mat_dict = loadmat('sine.mat',squeeze_me=True)
    
    # make a new dictionary with just dependent variables we want
    # (we handle the time variable separately, below)
    my_dict = { k: mat_dict[k] for k in ['x','y']}
    
    def matlab2datetime(matlab_datenum):
        day = dt.datetime.fromordinal(int(matlab_datenum))
        dayfrac = dt.timedelta(days=matlab_datenum%1) - dt.timedelta(days = 366)
        return day + dayfrac
    
    # convert Matlab variable "t" into list of python datetime objects
    my_dict['date_time'] = [matlab2datetime(tval) for tval in mat_dict['t']]
    
    # print df
    <class 'pandas.core.frame.DataFrame'>
    DatetimeIndex: 201 entries, 2013-08-15 17:11:30.999997 to 2013-09-04 17:11:30.999997
    Data columns (total 2 columns):
    x    201  non-null values
    y    201  non-null values
    dtypes: float64(2)
    
    # plot with Pandas
    df = pd.DataFrame(my_dict)
    df = df.set_index('date_time')
    df.plot()
    

    enter image description here

  • 2

    这是使用 numpy.datetime64 而不是 datetime 转换它们的方法 .

    origin = np.datetime64('0000-01-01', 'D') - np.timedelta64(1, 'D')
    date = serdate * np.timedelta64(1, 'D') + origin
    

    这适用于 serdate 单个整数或整数数组 .

相关问题