19

我刚开始从 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 的时间格式,我找到了这个教程。简而言之,作者指出

如果您使用 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 文件,因为它们很多,我的同事也需要使用它们。

如果有帮助,其他人要求反过来。可悲的是,我对 Python 太陌生,无法真正理解那里发生的事情。

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

4

5 回答 5

25

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

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

可以在这里找到更长的解释

于 2012-12-20T05:25:14.823 回答
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 值,这originpd.to_datetime().

我使用以下 Matlab 代码进行设置:

datenum('1970-01-01')  % gives 719529
datenums = datenum('06-Mar-2018') - linspace(0,1,6)  % test data
datestr(datenums)  % human readable format
于 2018-03-06T15:55:54.700 回答
12

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

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

在此处输入图像描述

于 2013-08-16T15:32:25.373 回答
4

这是一种使用numpy.datetime64, 而不是datetime.

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

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

于 2017-09-10T15:09:29.047 回答
2

只是建立并添加到以前的评论。关键是由类和相关子类中的方法toordinal和构造函数执行的日期计数。例如,从Python Library Reference for 2.7中可以看到fromordinaldatetimefromordinal

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

但是,公元 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 

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

于 2016-03-27T16:09:18.663 回答