In Matlab 2016a, I have a vector of week numbers:
weekNum = [20 21 22 23];
Is there a way to convert the week numbers to their respective dates in 2018 starting on Sunday?
You can do this using the functions days and weekday. Starting with the first day of the year, you can subtract days to find the previous Sunday (if it isn't Sunday already), then multiply your vector weekNum by 7 to add offsets to that date for each desired week:
weekNum = [20 21 22 23];
t = datetime(2018, 1, 1, 'Format', 'dd-MMMM-yyyy'); % First day of the year
t = t - days(weekday(t)-1) + days((weekNum-1).*7);
And this gives you the following array of Sunday datetimes:
t =
1×4 datetime array
13-May-2018 20-May-2018 27-May-2018 03-June-2018
And you can confirm it works using week:
>> week(t)
ans =
20 21 22 23 % Same as weekNum
NOTE: The first week is usually defined as the week that includes January 1st, and can include days from the previous year. This means you could end up with a date in 2017 for a Sunday in week 1.
% 1st day of year
D = datetime(2018,1,1);
% datetime objects representing the Sunday of the specified weeks
Dt = D + (20:23)*7-7-(weekday(D)-1);
This assumes that week 1 is defined to be the first week for which at least one day is in 2018. If you use a different definition, adjust your indexes accordingly.
What I love about Matlab is that, sometimes, you can delegate computational tasks to the underlying Java framework, if you know it a little bit:
year = 2018;
week = [20 21 22 23];
dow = 1;
sep = repmat('-',4,1);
chain = [repmat(num2str(year),4,1) sep num2str(week.','%d') sep repmat(num2str(dow),4,1)];
df = java.text.SimpleDateFormat('yyyy-w-u');
for i = 1:4
date = 719529 + (df.parse(chain(i,:)).getTime() / 1000 / 3600 / 24);
disp(datestr(date,'dd-mm-yyyy'));
end
Output:
13-05-2018
20-05-2018
27-05-2018
03-06-2018
Related
How to I compute the mean/average time of a datetime array containing hours and minutes?
rowMin =
20×1 datetime array
09:00
09:10
09:00
09:14
09:59
09:05
09:00
09:35
13:15
09:00
09:00
15:00
09:35
10:15
14:10
16:35
09:00
11:15
09:00
10:20
How to I compute the mean/average time?
rowMin.Format='HH:mm'
mean(rowMin)
Does not work. Neither converting to table and subsequently timetable does:
rowMinTable=table(rowMin)
rowMinTimetable=table2timetable(rowMinTable)
dt = minutes(30);
output = retime(table2timetable(rowMinTable),'regular','linear','TimeStep',dt)
Also, converting to datenum to compute mean and then revert to datetime is not the solution, since it uses the dates as well (I am ONLY interested in the mean of the hours and minutes HH:MM):
rowMin.Format='HH:mm';
rowMinDatenum=datenum(rowMin)
rowMinDatenumMean=mean(rowMinDatenum)
rowMinMean=datetime(rowMinDatenumMean,'ConvertFrom','datenum')
Also, converting to datenum to compute mean and then revert to
datetime is not the solution, since it uses the dates as well (I am
ONLY interested in the mean of the hours and minutes HH:MM)
So, first remove dates and then compute the average:
% generating some random datetimes
dtn = datenum('2000-01-01') + rand(100, 1) * ...
(datenum('2020-12-31')-datenum('2000-01-01'));
dt = datetime(dtn, 'convertfrom', 'datenum');
% finding mean of times
t = timeofday(dt); % elapsed time since midnight
m = mean(t)
datestr(m, 'hh:MM')
Suppose DT is a vector of type datetime. How can I get their days of the year?
This is an approximation, but I can't find the right function:
month(DT) * 30 + day(DT)
You can use the Matlab's day function with a vector of datetime elements (see the doc)
t = [datetime('yesterday');datetime('today');datetime('tomorrow')]; % vector of datetime
day( t, 'dayofyear')
that will give the Day-of-year number, from 1 to 365 or 366, depending on the year.
ans =
334
335
336
You can use datetime and between as follows:
d = datetime; %Read current time (just for the example).
jan_1st = datetime(d.Year, 1, 1); %Create datetime object of January fist in year of d.
day_in_year = between(jan_1st, d, 'Days'); %Get day in year.
The result is a calendarDuration object: 334d.
For converting to scalar, use split:
day_in_year = split(day_in_year, 'Days') + 1; % Add one in case the first day of the year is day 1 and not 0
Applying the solution on a vector of datetime may require a for loop due to the usage of datetime(d.Year, 1, 1).
I am running into the following problem in Matlab 2019a.
>> datestr(datetime('20190927','InputFormat','yyyyMMdd')-datetime('20190923','InputFormat','yyyyMMdd'),'yyyymmdd')
ans =
'00000104'
Why is the answer 1 month 4 days? Shouldn't it be zero month 4 days?
EDIT:
I realize that arithmetic on datetime arrays create duration arrays. But I don't know how to set format of duration arrays after doing arithmetic on datetime arrays or how to retrieve the duration data in a format free way.
Datetime arithmetic is what I need. So I am asking the question so I can do datetime arithmetic without issue.
d = datetime('20190927','InputFormat','yyyyMMdd')-datetime('20190923','InputFormat','yyyyMMdd')
returns:
d =
duration
96:00:00
That is, 96 hours. Converting this to a date string results in January 4th. If you start counting hours from midnight on the year 0, 96 hours later you end up on January 4th. There's no 0th month.
What you can do is this:
d = duration(d,'Format','dd:hh:mm:ss')
which returns:
d =
duration
04:00:00:00
You can also do:
d = days(d)
which returns:
d =
4
Whenever I need to plot data vs time I generate the corresponding array of datenums so I can visualize a timeline on the plot by calling datetick.
Let's suppose I need all the datenums with a 1 minute interval between hours h_1 and h_2. This is how would I generate my array of datenums vd:
h_1 = [01 00]; % 01:00 AM
h_2 = [17 00]; % 17:00 PM
HH_1 = h_1(1); % Hour digits of h_1
MM_1 = h_1(2); % Minute digits of h_1
HH_2 = h_2(1); % Hour digits of h_2
MM_2 = h_2(2); % Minute digits of h_2
% Vector of 01:00 - 17:30 with 1 minute increments (24 hours a day, 1440 minutes a day)
vd = HH_1/24+MM_1/1440:1/1440:HH_2/24+MM_2/1440;
I learnt this technique reading this answer.
On the other hand, whenever I wish to generate a single datenum, I use the datenum function like this:
d = datenum('14:20','HH:MM');
Since 14:20 is comprised between the interval 01:00 - 17:30, its datenum should be too! Unfortunately this is not working as expected, since the number assigned to d is radically different to those values contained in vd. I think I might be missing something, probably something to do with setting up a reference date or similar.
So what would be the appropriate way to generate datenums consistently?
The reason is that the datenum function gives you a number of days from January 0, 0000.
So when calling
d = datenum('14:20','HH:MM');
you get a number about 735965, while the numbers in your vd array are between 0 and 1. In order to substruct the days between January 0, 0000 and today you can write
d = datenum('14:20','HH:MM') - datenum('00:00','HH:MM');
Then your code will look like
h_1 = [01 00]; % 01:00 AM
h_2 = [17 00]; % 17:00 PM
HH_1 = h_1(1); % Hour digits of h_1
MM_1 = h_1(2); % Minute digits of h_1
HH_2 = h_2(1); % Hour digits of h_2
MM_2 = h_2(2); % Minute digits of h_2
vd = HH_1/24 + MM_1/1440 : 1/1440 : HH_2/24+MM_2/1440;
d = datenum('14:20','HH:MM') - datenum('00:00','HH:MM');
display(d);
display(vd(801));
And the result:
d = 0.5972
ans = 0.5972
Say that I have a dataset:
Jday = datenum('2009-01-01 00:00','yyyy-mm-dd HH:MM'):1/24:...
datenum('2009-01-05 23:00','yyyy-mm-dd HH:MM');
DateV = datevec(Jday);
DateV(4,:) = [];
DateV(15,:) = [];
DateV(95,:) = [];
Dat = rand(length(Jday),1)
How is it possible to remove all of the days that have less than 24 measurements. For example, in the first day there is only 23 measurements thus I would need to remove that entire day, how could I repeat this for all of the array?
A quick solution is to group by year, month, day with unique(), then count observation per day with accumarray() and exclude those with less than 24 obs with two steps of logical indexing:
% Count observations per day
[unDate,~,subs] = unique(DateV(:,1:3),'rows');
counts = [unDate accumarray(subs,1)]
counts =
2009 1 1 22
2009 1 2 24
2009 1 3 24
2009 1 4 24
2009 1 5 23
Then, apply criteria to the counts and retrieve logical index
% index only those that meet criteria
idxC = counts(:,end) == 24
idxC =
0
1
1
1
0
% keep those which meet criteria (optional, for visual inspection)
counts(idxC,:)
ans =
2009 1 2 24
2009 1 3 24
2009 1 4 24
Finally, find the members of Dat that fall into the selected counts with a second round of logical indexinf through ismember():
idxDat = ismember(subs,find(idxC))
Dat(idxDat,:)
Rather long answer, but I think it should be useful. I would do this using containers.Map. Possibly there is a faster way, but maybe for now this one will be good.
Jday = datenum('2009-01-01 00:00','yyyy-mm-dd HH:MM'):1/24:...
datenum('2009-01-05 23:00','yyyy-mm-dd HH:MM');
DateV = datevec(Jday);
DateV(4,:) = [];
DateV(15,:) = [];
DateV(95,:) = [];
% create a map
dateMap = containers.Map();
% count measurements in each date (i.e. first three columns of DateV)
for rowi = 1:1:size(DateV,1)
dateRow = DateV(rowi, :);
dateStr = num2str(dateRow(1:3));
if ~isKey(dateMap, dateStr)
% initialize Map for a given date with 1 measurement (i.e. our
% counter of measuremnts
dateMap(dateStr) = 1;
continue;
end
% increment measurement counter for given date
dateMap(dateStr) = dateMap(dateStr) + 1;
end
% get the dates
dateStrSet = keys(dateMap);
for keyi = 1:numel(dateStrSet)
dateStrCell = dateStrSet(keyi);
dateStr = dateStrCell{1};
% get number of measurements in a given date
numOfmeasurements = dateMap(dateStr);
% if less then 24 do something about it, e.g. save the date
% for later removal from DateV
if numOfmeasurements < 24
fprintf(1, 'This date has less than 24 measurement: %s\n', dateStr);
end
end
The results is:
This date has less than 24 measurement: 2009 1 1
This date has less than 24 measurement: 2009 1 5