Вопрос:

Преобразование формата datenum в Matlab в Python

python matlab

15018 просмотра

5 ответа

648 Репутация автора

Я только начал переходить с Matlab на Python 2.7, и у меня возникли проблемы с чтением моих .mat-файлов. Информация о времени хранится в формате datenum в Matlab. Для тех, кто не знаком с этим:

Серийный номер представляет календарную дату как количество дней, прошедших с фиксированной базовой даты. В MATLAB серийная дата № 1 - 1 января 0000.

MATLAB также использует последовательное время для представления долей дней, начинающихся в полночь; например, 6 часов вечера равны 0,75 серийным дням. Таким образом, строка «31 октября 2003 года, 18:00» в 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): это было исправлено в руководстве, размещенном выше.

Автор: Fred S Источник Размещён: 20.12.2012 05:14

Ответы (5)


16 плюса

10455 Репутация автора

Решение

Вы ссылаетесь на решение, оно имеет небольшую проблему. Вот это:

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

более длинное объяснение можно найти здесь

Автор: carlosdc Размещён: 20.12.2012 05:25

10 плюса

8614 Репутация автора

На всякий случай это полезно для других, вот полный пример загрузки данных временных рядов из файла mat Matb, преобразования вектора датных чисел Matlab в список объектов datetime, используя ответ carlosdc (определенный как функция), и затем построение графика как временные ряды с пандами:

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

введите описание изображения здесь

Автор: Rich Signell Размещён: 16.08.2013 03:32

2 плюса

634 Репутация автора

Просто опираясь на и добавляя к предыдущим комментариям. Ключ в подсчете дня выполняется методом toordinalи конструктором fromordinalв классе datetimeи связанных подклассах. Например, из Библиотеки Python для 2.7 читается, чтоfromordinal

Верните дату, соответствующую пропускающему григорианскому порядковому номеру, где 1 января года 1 имеет порядковый номер 1 . Ошибка ValueError возникает, если только 1 <= ordinal <= date.max.toordinal ().

Тем не менее, год 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 

Надеюсь, что это помогает и счастлив, чтобы быть исправленным.

Автор: XavierStuvw Размещён: 27.03.2016 04:09

3 плюса

23973 Репутация автора

Вот способ конвертировать их, используя numpy.datetime64вместо datetime.

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

Это работает для serdateодного целого или целочисленного массива.

Автор: Dougal Размещён: 10.09.2017 03:09

4 плюса

356 Репутация автора

Используя 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
Автор: jonas Размещён: 06.03.2018 03:55
Вопросы из категории :
32x32