我有'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
答案 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
对象。
这是一种(更快)生成相同日期向量的方法,而不是示例。您应该了解的两个主要功能是datenum
和bsxfun
。
这有点长,因为我开发了每一行并使用临时变量来表示清晰度并显示步骤。如果您愿意,可以在几行中减少所有这些:
%% 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' ) ;
到目前为止,对不同方法的快速基准:
基准测试的代码(使用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