【问题标题】:Converting Matlab's datenum format to Python将 Matlab 的 datenum 格式转换为 Python
【发布时间】:2012-12-20 05:14:24
【问题描述】:

我刚开始从 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 文档)

我想把它转换成 Python 的时间格式,我找到了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):这已在上面发布的教程中修复。

【问题讨论】:

    标签: python matlab


    【解决方案1】:

    使用 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
    

    【讨论】:

    • 我不确定有什么区别,但这个答案给出的结果比公认的答案更接近。对于719529 + 1/24/12 的输入,应该在 UNIX 纪元之后 5 分钟,接受的答案是 2 秒。这个答案延迟了 20 微秒。
    【解决方案2】:

    您链接到解决方案,它有一个小问题。是这样的:

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

    更详细的解释可以找到here

    【讨论】:

    • 我会将matlab_datenum 转换为int,然后再将其输入fromordinal
    • 或更简单的:python_datetime = datetime.fromordinal(int(matlab_datenum) - 366) + timedelta(days=matlab_datenum%1) :)
    • 我必须将 timedelta 的输入也转换为 int:timedelta(days=int(matlab_datenum%1))。否则我会得到:TypeError: unsupported type for timedelta days component: numpy.int32
    • 我不知道这是浮点精度问题还是实际上是不同的算法,但这个答案不如下面@jonas 的熊猫答案准确。请参阅我在该答案下的评论。
    【解决方案3】:

    以防万一它对其他人有用,这里是从 Matlab mat 文件加载时间序列数据的完整示例,使用 carlosdc 的答案(定义为函数)将 Matlab datenums 向量转换为 datetime 对象列表,以及然后用 Pandas 绘制时间序列:

    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()
    

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

      • 谢谢!正在寻找这个。为了获得更高的分辨率,可以将 timedelta64 乘数更改为另一个单位,并在一天内乘以该单位的数量。这是我能够在不溢出的情况下获得的最高分辨率示例(使用与您相同的 origin):delta = np.timedelta64(1,'us') * 86400e6 t = datenum_array * delta + origin 可选:t = t.astype(dtype = 'datetime64[us]') 其中datenum_array 是从 .mat 文件导入的浮点数组
      【解决方案5】:

      只是建立并添加到以前的 cmets。关键是在类datetime 和相关子类中的方法toordinal 和构造函数fromordinal 执行的日期计数。例如,从Python Library Reference for 2.7 中读取fromordinal

      返回对应于预测公历序数的日期,其中第 1 年的 1 月 1 日有序数 1。除非 1

      但是,公元 0 年仍然是需要计算的一年(闰)年,因此仍然需要考虑 366 天。 (闰年,就像 2016 年一样,正好是四年前的 504 个周期。)

      这是我一直用于类似目的的两个函数:

      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 
      

      希望这有助于并很高兴得到纠正。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多