Ответ 1
Вы ссылаетесь на решение, у него небольшая проблема. Это:
python_datetime = datetime.fromordinal(int(matlab_datenum)) + timedelta(days=matlab_datenum%1) - timedelta(days = 366)
более длинное объяснение можно найти здесь
Я только начал переходить с Matlab на Python 2.7, и у меня есть некоторые проблемы с чтением моих .mat файлов. Информация о времени хранится в формате данных Matlab. Для тех, кто не знаком с ним:
Серийный номер даты представляет собой календарную дату как число дней, прошедших с фиксированной базовой даты. В MATLAB серийная дата № 1 - 1 января 0000 года.
MATLAB также использует серийное время для представления долей дней, начинающихся в полночь; например, 6 часов вечера. равно 0.75 серийным дням. Таким образом, строка '31 -Oct-2003, 6:00 PM 'в MATLAB - это дата 731885.75.
(взято из документации Matlab)
Я хотел бы преобразовать это в формат времени Pythons, и я нашел этот учебник. Короче говоря, автор утверждает, что
Если вы проанализируете это с помощью 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): Это было исправлено в учебнике, опубликованном выше.
Вы ссылаетесь на решение, у него небольшая проблема. Это:
python_datetime = datetime.fromordinal(int(matlab_datenum)) + timedelta(days=matlab_datenum%1) - timedelta(days = 366)
более длинное объяснение можно найти здесь
На всякий случай, это полезно для других, вот полный пример загрузки данных временных рядов из файла Matlab mat, преобразования вектора данных Matlab в список объектов datetime с использованием ответа carlosdc (определенного как функция) и затем в виде временных рядов с 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()
Используя 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 является значением datenum от начала эпохи Unix (1970-01-01), который является по умолчанию origin
для pd.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
Здесь можно преобразовать их с помощью numpy.datetime64
, а не datetime
.
origin = np.datetime64('0000-01-01', 'D') - np.timedelta64(1, 'D')
date = serdate * np.timedelta64(1, 'D') + origin
Это работает для serdate
как целого, так и целочисленного массива.
Просто строим и добавляем к предыдущим комментариям. Ключ находится в подсчете дня, как выполняется методом toordinal
и конструктором fromordinal
в классе datetime
и связанных подклассах. Например, из Python Library Reference для 2.7, читается, что fromordinal
Верните дату, соответствующую прорептическому григорианскому порядку, где 1 января года 1 имеет порядковый номер 1. ValueError возникает, если только 1 <= порядковый номер <= date.max.toordinal().
Однако, год 0 AD все еще один (скачок) год для подсчета, так что еще 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
Надеюсь, что это поможет и будет рад быть исправленным.