Функция быстрее, чем datenum в MATLAB

#matlab #date #performance

#matlab #Дата #Производительность

Вопрос:

Кто-нибудь знает более быстрый способ преобразовать строку даты (2010-12-12 12: 21: 12.123) в число?

Комментарии:

1. Под числом вы подразумеваете временную метку, такую как часто используемые «секунды с начала эпохи»?

Ответ №1:

Часто в качестве инструкции используется профилирование встроенных функций Matlab и извлечение только интересующей внутренней функциональности.

В вашем конкретном случае,

 dtstr2dtnummx({'2010-12-12 12:21:12.123'},'yyyy-MM-dd HH:mm:ss')
  

в 3 раза быстрее (занимает 30% времени), чем:

 datenum({'2010-12-12 12:21:12.123'},'yyyy-mm-dd HH:MM:SS')
  

где dtstr2dtnummx — это внутренняя функция (C:Program Files Matlab R2011a toolbox matlab timefun private dtstr2dtnummx.mexw32 на моем компьютере с Windows).

Чтобы получить доступ к этой внутренней функции, просто добавьте ее папку в путь Matlab с помощью addpath функции или скопируйте файл dtstr2dtnummx.mexw32 в другую папку, которая уже находится в вашем пути Matlab.

Обратите внимание, что формат строки отличается между dtstr2dtnummx и datenum, поэтому будьте осторожны!

Для тех, кому интересно, в папке выше содержатся другие интересные функции преобразования дат, так что изучайте и наслаждайтесь!

Примечание 5/5/2011: теперь я опубликовал статью, которая расширяет этот ответ на http://undocumentedmatlab.com/blog/datenum-performance /

Комментарии:

1. К сожалению, вы не можете добавить частные каталоги Matlab в path, поэтому для пользователей, у которых нет возможности скопировать файл в свой текущий каталог, это решение не будет работать.

2. Для пояснения, пользователи могут не иметь возможности добавить файл, содержащий dtstr2dtnummx , в свой текущий каталог из-за проблем с авторскими правами.

3. @Ricardo — Я не думаю, что здесь есть какая-либо проблема с авторским правом. Я считаю, что у вас есть право использовать любую внутреннюю функцию при покупке лицензии Matlab.

4. Когда я посещал учебную сессию несколько месяцев назад, сотрудники Mathworks убеждали меня не делать этого при создании автономных приложений Matlab. Отсюда мои колебания.

5. Я бы отнесся к их призывам с такой же серьезностью, как к призывам Apple не рутировать iPhone или к призывам Ford не настраивать свои автомобили вручную. У вас есть выбор, и вам решать, использовать его или нет.

Ответ №2:

Часто вам нужно использовать системный подход. У меня была очень похожая проблема, когда я извлекал тысячи дат из базы данных. Оказывается, что многие современные базы данных (я пробовал Postgres, Sql server и Oracle) могут выполнять преобразование из своих представлений даты в представления даты Matlab на несколько порядков быстрее, чем текст в datenum на стороне matlab. Если эти данные поступают из базы данных, подумайте о преобразовании на стороне базы данных!!

Ответ №3:

Предположительно, если вас волнует время, затрачиваемое на преобразование дат, вы конвертируете многие из них. Даже если не учитывать оптимизацию JIT в последних версиях matlab, вы получите гораздо более быстрые результаты при вызове

 datenum(cellarrayofdates, 'yyyy-mm-dd HH:MM:SS');
  

чем

 for i=1:length(cellarrayofdates); datenum(cellarrayofdates{i}, 'yyyy-mm-dd HH:MM:SS'); end
  

Если вы еще этого не делаете, начните с этого, поскольку это позволяет matlab сократить накладные расходы на определение вашего формата даты для каждого вызова функции.

Ответ №4:

Я понимаю, что этот вопрос устарел. Однако мне удалось создать функцию, которая примерно в 30-40 раз быстрее datenum. Примечание: Существуют незначительные недостатки, зависящие от использования. Если кто-нибудь хочет, чтобы я это исправил, просто дайте мне знать.

Выполняется с 1 792 379 строками:

  • дата — 11,463186 секунды
  • datenumjck — 0,300503 секунды

Просто прочитайте ваш файл с помощью textscan и интерпретируйте дату и время как двойные и введите вместе с форматом даты в мою функцию.

Пример:

Предположим, что данные формируются следующим образом:

 Data,2016-03-03,16:15:50;686,0.000000,-0.009500
Data,2016-03-03,16:15:50;696,0.000000,0.006500
Data,2016-03-03,16:15:50;706,0.000000,0.004500
Data,2016-03-03,16:15:50;716,0.000000,-0.006000
  

Считывание данных:

 fileID = fopen('myFile.csv','r');
formatSpec = '%*s %f %f %f %f %f %f %f %*[^n]'; % Ignore first string, save
                                                 % date and time as doubles
                                                 % ignore all other data
data = textscan(fileID,formatSpec,'delimiter',',t/:;-.\ ');
fclose(fileID);
  

Укажите формат даты и используйте datenumjck():

 dateFormat = 'yyyy-mm-dd,HH:MM:SS;FFF';
numDate = datenumjck(data,dateFormat);
  

Код:

 function num = datenumjck(data, dateFormat)

n = size(data{1});
dateFormat = textscan(dateFormat,'%s','delimiter',',/:;-.\');
dateFormat = dateFormat{1};

k = find(strcmp('yyyy', dateFormat),1);
if ~isempty(k)
    y = data{k};
elseif ~isempty(find(strcmp('yy', dateFormat),1))
    y = data{find(strcmp('yy', dateFormat),1)};
else
    y = zeros(n);
end

k = find(strcmp('mm', dateFormat),1);
if ~isempty(k)
    m = data{k};
elseif ~isempty(find(strcmp('mmm', dateFormat),1))
    month = cellfun(@strfind,...
        repmat({'janfebmaraprmayjunjulaugsepoctnovdec'},...
        size(data),lower(data(find(strcmp('mmm', dateFormat),1)))));
    m = (month 2)/3;
else
    m = zeros(n);
end

k = find(strcmp('dd', dateFormat),1);
if ~isempty(k)
    d = data{k};
else
    d = zeros(n);
end

k = find(strcmp('HH', dateFormat),1);
if ~isempty(k)
    H = data{k};
else
    H = zeros(n);
end

k = find(strcmp('MM', dateFormat),1);
if ~isempty(k)
    M = data{k};
else
    M = zeros(n);
end


k = find(strcmp('SS', dateFormat),1);
if ~isempty(k)
    S = data{k};
else
    S = zeros(n);
end

k = find(strcmp('FFF', dateFormat),1);
if ~isempty(k)
    F = data{k};
else
    F = zeros(n);
end

ms = [0,31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];

num = zeros(n);
for k = 1:n
    num(k) = y(k)*365   ms(m(k))   d(k)   floor(y(k)/4)...
        - floor(y(k)/100)   floor(y(k)/400)   (mod(y(k),4)~=0)...
        - (mod(y(k),100)~=0)   (mod(y(k),400)~=0)...
          (H(k)*3600   M(k)*60   S(k)   F(k)/1000)/86400   1;
end