Matlab使用日期向量矢量化日期和时间序列的实现

时间:2016-10-23 02:56:04

标签: matlab loops datetime vectorization

我有'dd-MM-YYYY'形式的日期向量。从这些日期的每一个开始,我想以'dd-MM-YYYY hh:mm:ss'的形式生成一系列日期时间。这些日期时间将从某个开始时间开始,增加n分钟,并在给定的结束时间结束。

我可以在双循环中执行此操作,但是它需要一个很长的时间,一个足够小的时间增量,以及给定输入日期足够大的矢量。这可以通过某种方式进行矢量化以缩短处理时间吗?

此示例脚本仅使用1分钟增量值60天,但我的最终数据将有数千个。

我的脚本循环:

%% ===== StackOverFlow Question ================
tic
% Create an Arbritrary Vector of Dates
sDates = (datetime([1991,11,01]) + caldays(0:60))';

% Remove Weekends from Dates
sDates = sDates( weekday(sDates)>1 &weekday(sDates)<7);

% Outer Loop to go through LIST OF DATES
for theDate =1:size(sDates,1)  
    [y,m,d] = ymd(sDates(theDate)); % Get y,m,d of each date in list

    % START and END Time to create date time sequence each                  
    % t1 and t2 are synced to each day in the date vector
    t1 = datetime(y,m,d,08,30,00);  t2 = datetime(y,m,d,15,00,00);

    % Inner Loop to go through each increment of time
    NDX=0;
    for theMin =t1 : minutes(1) : t2 %Scroll through Minutes
        NDX=NDX+1;

        % Store each minute/increment into another vector
        % Final Desired Vector
        x_tDate(NDX,1)=theMin;
    end
end
toc

-------------------------------编辑---处理速度更快------ --------- 所以我想出了一种让它变得更快的方法。 OUTER-LOOP仍在那里,但我已经想出了如何对INNER-LOOP进行矢量化。因此,它的内部从大循环的每次迭代运行大约390次,到大循环的每次迭代运行1次。但我想知道它是否可以进一步简化?

%% ===== StackOverFlow Question ================
tic
% Create an Arbritrary Vector of Dates

sDates = (datetime([1991,11,01]) + caldays(0:60))';

% Remove Weekends from Dates
sDates = sDates( weekday(sDates)>1 &weekday(sDates)<7);

% Outer Loop to go through LIST OF DATES

newDates=datetime([],[],[],[],[],[]); %Initialize dateTime Place holder

for theDate =1:size(sDates,1)  
    [y,m,d] = ymd(sDates(theDate)); % Get y,m,d of each date in list

    % START and END Time to create date time sequence each day listed in Date     
    % t1 and t2 are synced to each day in the date vector
    t1 = datetime(y,m,d,08,30,00);  t2 = datetime(y,m,d,15,00,00);

    temp=t1 :minutes(1) :t2;
    newDates=[newDates temp];

end
newDates = newDates';

toc

2 个答案:

答案 0 :(得分:1)

与Matlab一样,加速代码的一种简单方法是对其进行矢量化(就像使用内部循环一样)并依赖已经优化的函数。在这里,您正在处理datetring,实际上更容易使用数字并最终转换为datetrings。

这是一个简单的代码,可带来8倍到10倍的加速。我不是Matlab代码优化方面的专家,也许它可以进一步推进。

tic
% Create an Arbritrary Vector of Dates

sDates = (datetime([1991,11,01]) + caldays(0:60))';

% Remove Weekends from Dates
sDates = sDates( weekday(sDates)>1 &weekday(sDates)<7);
[Y,M,D] = ymd(sDates);

% Get y,m,d of first (or any) date in list to build the the h,m,s time range
[y,m,d] = ymd(sDates(1));
t1 = datetime(y,m,d,08,30,00);  t2 = datetime(y,m,d,15,00,00);
temp = (t1:minutes(1):t2)';
[h,m,s]=hms(temp);

newDates = datetime([repelem([Y M D], length(temp), 1) repmat([h m s], [length(sDates) 1])]);
% repmat() to REPLICATE the TIME MATRIX, length(sDates) times along the rows
% repelem() to REPEAT EACH ELEMENT OF the DATE MATRIX, length(temp) times along the rows

toc

答案 1 :(得分:1)

我想要性能,如果您在日期/时间进行大量操作,我会邀请您考虑使用datenum。在这种格式中,每个时间/日期只是一个简单的数字,与必须处理复杂对象(如datetime)相比,它允许更快的计算。

在繁重的计算完成后,您始终可以转换为datetime对象。

这是一种(更快)生成相同日期向量的方法,而不是示例。您应该了解的两个主要功能是datenumbsxfun

这有点长,因为我开发了每一行并使用临时变量来表示清晰度并显示步骤。如果您愿意,可以在几行中减少所有这些:

%% Create the vector of days
% Initial parameters
StartDate   = datenum(1991,11,01) ;         % first day
nDaysFromStart = 60 ;                       % number of days
EndDate     = StartDate + nDaysFromStart ;  % last day to consider
dowStart = weekday(StartDate) ;             % "day of week" number
dowEnd   = weekday(EndDate) ;
% now create an array containing an integer multiple of 7 (days)
daysarray = (0-dowStart+1):(nDaysFromStart+7-dowEnd) ;
% reshape it [7xN] to trim the days of the week we don't want
daysarray = reshape(daysarray,7,[]) ; 
dowToRemove = [1 7] ;               % remove Sunday (1) and Saturday (7)
daysarray(dowToRemove,:) = [] ;
% Now remove the extra days created at start and end. Note that this
% operation will also reshape the 2d array into a 1d vector.
daysarray( daysarray<0 | daysarray>nDaysFromStart ) = [] ;
% Now add the real start date to all elements of the vector
daysarray = daysarray + StartDate ;

%% Create the vector of minutes for any given day
step = 1/24/60 ;                % portion of a day that represent a minute
start = datenum(0,0,0,8,30,0) ; % start time for each day
stop = datenum(0,0,0,15,00,00) ;% end time for each day
dailyvec = (start:step:stop).' ;

%% Use bsxfun to create the complete arrays 
alldates = bsxfun(@plus,daysarray,dailyvec) ;

%% Eventually, convert to "datetime" objects if you prefer to work with them
allNewDates = datetime( alldates(:) , 'ConvertFrom','datenum' ) ;

到目前为止,对不同方法的快速基准:

benchmark

基准测试的代码(使用timeit代替tic/toc),我的代码也有点压缩:

function t = bench_datevec_generation()

%     nDaysToGenerate = [10:10:100 200:100:900] ;
    nDaysToGenerate = [10:10:60] ;

    t = zeros(numel(nDaysToGenerate),3) ;
    for k=1:numel(nDaysToGenerate)
        nDays = nDaysToGenerate(k) ;
        f1 = @() genDateTime_loop(nDays);
        f2 = @() genDateTime_vectorized(nDays);
        f3 = @() genDatenum(nDays);

        t(k,1) = timeit( f1 ) ;
        t(k,2) = timeit( f2 ) ;
        t(k,3) = timeit( f3 ) ;
        fprintf('Measuring time for nDays=%d\n',nDays)
    end

end

function newDates = genDateTime_loop(nDays)  
    sDates = (datetime([1991,11,01]) + caldays(0:nDays)).';
    % Remove Weekends from Dates
    sDates = sDates( weekday(sDates)>1 &weekday(sDates)<7);
    % Outer Loop to go through LIST OF DATES
    newDates=datetime([],[],[],[],[],[]); %Initialize dateTime Place holder

    for theDate =1:size(sDates,1)  
        [y,m,d] = ymd(sDates(theDate)); % Get y,m,d of each date in list

        % START and END Time to create date time sequence each day listed in Date     
        % t1 and t2 are synced to each day in the date vector
        t1 = datetime(y,m,d,08,30,00);  t2 = datetime(y,m,d,15,00,00);

        temp=t1 :minutes(1) :t2;
        newDates=[newDates temp];
    end
    newDates = newDates.' ;
end

function newDates = genDateTime_vectorized(nDays)  
    sDates = (datetime([1991,11,01]) + caldays(0:nDays))';

    % Remove Weekends from Dates
    sDates = sDates( weekday(sDates)>1 &weekday(sDates)<7);
    [Y,M,D] = ymd(sDates);

    % Get y,m,d of first (or any) date in list to build the the h,m,s time range
    [y,m,d] = ymd(sDates(1));
    t1 = datetime(y,m,d,08,30,00);  t2 = datetime(y,m,d,15,00,00);
    temp = (t1:minutes(1):t2)';
    [h,m,s]=hms(temp);

    newDates = datetime([repelem([Y M D], length(temp), 1) repmat([h m s], [length(sDates) 1])]);
    % repmat() to REPLICATE the TIME MATRIX, length(sDates) times along the rows
    % repelem() to REPEAT EACH ELEMENT OF the DATE MATRIX, length(temp) times along the rows
end

function newDates = genDatenum(nDays)  
    % Initial parameters
    StartDate   = datenum(1991,11,01) ;         % first day
    % now create an array containing an integer multiple of 7 (days)
    daysarray = reshape((0-weekday(StartDate)+1):(nDays+7-weekday(StartDate+nDays)),7,[]) ; 
    dowToRemove = [1 7] ;
    daysarray(dowToRemove,:) = [] ;
    daysarray( daysarray<0 | daysarray>nDays ) = [] ;
    daysarray = daysarray + StartDate ;

    % Create the vector of minutes for any given day
    step = 1/24/60 ;                % portion of a day that represent a minute
    start = datenum(0,0,0,8,30,0) ; % start time for each day
    stop = datenum(0,0,0,15,00,00) ;% end time for each day
    dailyvec = (start:step:stop).' ;

    % Use bsxfun to create the complete arrays 
    alldates = bsxfun(@plus,daysarray,dailyvec) ;

    % Eventually, convert to "datetime" objects if you prefer to work with them
    newDates = datetime( alldates(:) , 'ConvertFrom','datenum' ) ;
end