我刚开始从Matlab迁移到Python 2.7,我在阅读我的.mat文件时遇到了一些麻烦。时间信息以Matlab的datenum格式存储。对于那些不熟悉它的人:
序列日期编号表示日历日期,作为自固定基准日期以来经过的天数。在MATLAB中,序列号1是1月1日0000。
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):这已在上面发布的教程中修复。
答案 0 :(得分:16)
您链接到解决方案,它有一个小问题。就是这样:
python_datetime = datetime.fromordinal(int(matlab_datenum)) + timedelta(days=matlab_datenum%1) - timedelta(days = 366)
可以找到更长的解释here
答案 1 :(得分:10)
以防万一对其他人有用,这里有一个从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()
答案 2 :(得分:3)
以下是使用numpy.datetime64
而不是datetime
转换这些内容的方法。
origin = np.datetime64('0000-01-01', 'D') - np.timedelta64(1, 'D')
date = serdate * np.timedelta64(1, 'D') + origin
这适用于serdate
单个整数或整数数组。
答案 3 :(得分:3)
使用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),这是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
答案 4 :(得分:2)
只是建立并添加到以前的评论。关键是在日期计数中由方法toordinal
和类fromordinal
中的构造函数datetime
以及相关的子类执行。例如,从Python Library Reference for 2.7开始,可以读取fromordinal
返回对应于公历格里高利序数的日期,其中第1年1月1日有序数1 。除非1&lt; = ordinal&lt; = 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
希望这有助于并且乐于纠正。