Excel Date Conversion into MATLAB

4 views (last 30 days)
Nicholas Qassis
Nicholas Qassis on 17 Apr 2017
Commented: dpb on 18 Apr 2017
I have an excel file with 20,000 dates listed in one column in the form dd/mm/yyyy. I need to import these dates into MATLAB and convert them into a 20,000x2 matrix with month in the first column and year in the second column. I currently have code written to index an excel file I created with this form, but I need to use an excel file in which the dates are in the form dd/mm/yyyy and convert it. Example: the date 1/30/2017 (in excel) should be in the form (in MATLAB) so I can index it with the code I have written. Month (in the first column) Year (in the second column) 1 17
  1 Comment
dpb
dpb on 17 Apr 2017
"...so I can index it with the code I have written."
I'd suggest just convert to Matlab datetime and use its features will be so much simpler to deal with that trying to reuse Excel code is a waste. isbetween, ismember and the logical operators are all datetime -aware.

Sign in to comment.

Answers (2)

Star Strider
Star Strider on 17 Apr 2017
The xlsread function will return your dates as a cell array of strings in a column. Assuming that you have already isolated that column (as ‘ExcelDate’ here), see if this does what you want:
The datenum function should parse the dates correctly on its own:
dv = datevec(datenum(ExcelDate)); % Date Vectors
MATLAB_Date = [dv(:,2) rem(dv(:,1), 2000)]; % Desired Output
If for some reason it doesn’t this will work as an alternative:
ExcelDate = {'1/30/2017'; '1/31/2017'; '2/1/2017'};
date_parse = cellfun(@(x)regexp(x, '/', 'split'), ExcelDate, 'Uni',0); % Get Numbers (Cell)
MATLAB_Date = cellfun(@(x)str2double(x([1 3])), date_parse, 'Uni',0); % Convert To Numeric
MATLAB_Date = reshape([MATLAB_Date{:}], 2, [])'; % Create Matrix
MATLAB_Date(:,2) = rem(MATLAB_Date(:,2), 2000); % Get Only Last 2 Year Numbers
MATLAB_Date =
1 17
1 17
2 17
Both produce the same output.
  8 Comments
Star Strider
Star Strider on 18 Apr 2017
For some reason, datenum doesn’t want to vectorize the dates in column 2, so I went with a loop.
The Code
[d,s] = xlsread('Building_Permits.csv');
for k1 = 2:20
dv = datevec(datenum(s(k1,2)));
Permit_Issued_Date(k1,:) = [dv(:,2) rem(dv(:,1), 2000)];
end
You can do the same with the other dates. You will likely have to do them column-by-column, either in the same loop or a different loop. I would save all the results you extract and convert to a ‘.mat’ file so you can import them much more effeciently later. See the documentation on save, matfile, and load to understand ‘.mat’ files if you’ve not used them before.
dpb
dpb on 18 Apr 2017
"..., datenum doesn’t want to vectorize the dates in column 2,"
Because there are a number of missing values...
>> >> sum(cellfun(@(x) length(x),t(2:end,2))==0)
ans =
703
>> length(t)
ans =
45589
>>
What does OP propose to do for these? The loop won't convert them, either...

Sign in to comment.


dpb
dpb on 17 Apr 2017
Edited: dpb on 18 Apr 2017
Are the Excel data stored as dates or as strings? If they're date types they'll be read by xlsread as Excel serial numbers. In that case, use
datm=datetime(ExcelDate,'ConvertFrom','excel'); % keeping S Strider's nomenclature
If they're strings, to make it clear to datenum and also speed up processing, tell it the format...
dn=datetime(ExcelDate,'InputFormat','MM/dd/yyyy')');
See
doc datetime % and friends for creating and using...
Would be interested to see the functions/code you've written to compare to what I'm thinking would work simpler...
ADDENDUM
Based on comment, I'm not yet a believer... :)
>> [n,t,r]=xlsread('BPermit.csv'); % load your file
>> ix=(cellfun(@(x) length(x),t(:,2))~=0); % mark missing dates to skip
>> ix(1)=false; % skip over header row
>> da(ix)=datetime(char(t(ix,2)),'inputformat','MM/dd/yyyy'); % datetime array
>>
Now your function could be something like...
function annualPermits=YearIndex_er3309(dates,Year)
% Return number of permits found in given calendar year
% Usage:
% annualPermits=YearIndex_er3309(Dates,Year)
% returns permits in a given year given
% Dates -- datetime vector of dates in database
% Year -- desired year as yyyy or yy (assume 2000 epoch)
if Year<100, Year=Year+2000; end % fixup if yy form
% any other error checking desired here (or before)...
annualPermits=sum(year(dates)==Year);
As Star says, read the data and do the conversion only once, then use the database. Anyway, the above would let you do things like--
>> arrayfun(@(yr) disp([yr sum(year(da)==yr)]), [2010:2017].')
2010 3887
2011 4772
2012 5771
2013 4557
2014 6849
2015 8541
2016 9218
2017 1288
>>
I've not read the rest of the code, but I can't believe it can't be simpler to make the conversion now...convenience going forward, particularly the newer datetime class as it has the builtin properties/methods like year above that avoids the explicit year/month hassles of datevec with datenum will quickly override a little transition effort now.
Again, however, seems as though a first order of business would be to clean up the database for the missing values...the above works around it to make code run but isn't particularly clean in doing so.
ADDENDUM 2:
OK, I did read the script--can't emphasize enough to read the data first, then ask user what to do with it. While may be a very short initial startup, it'll be much more responsive later when don't have to retrieve data after the menu choice. Anyways, presuming have done as shown above, then
...
x=input('Enter the numerical months ...');
y=input('Of what year? ');
%call month index function
[permits,PermitsPerMonth]=MonthlyPermits_ex1419(x,y)
can become something like
mn=input('Enter the numerical months ...'); % use names that at least
yr=input('Of what year? '); % have some bearing on value
% call month index function
[permits,monthlyPermits]=MonthlyPermits_ex1419(da,perms,yr,mn)
and the function something on the order of--
[permits,monthlyPermits]=MonthlyPermits_ex1419(Dates,Permits,Year,Months)
% Return number of permits found in given months for a calendar year
% Usage:
% permits=MonthlyPermits_ex1419(Dates,Permits,Year,Months)
% returns permit numbers in a given year and set of months
%
% [permits,monthlyPermits=MonthlyPermits_ex1419(Dates,Permits,Year,Months)
% also returns total numbers permits in respective months
% Inputs:
% Dates -- datetime vector of dates in database
% Permits -- cellstr vector of permits in database
% Year -- desired year as yyyy or yy (assume 2000 epoch)
% Months -- desired months in year as vector of numeric, 1-12
if Year<100, Year=Year+2000; end % fixup if yy form
% any other error checking desired here (or before)...
isYr=(year(Dates)==Year);
isOK=isYr.*(ismember(month(Dates),Months));
permits=Permits(isOK);
if nargout==1, return,end
monthlyPermits=zeros(length(Months),1);
for i=1:length(Months)
monthlyPermits(i)=sum(isYr.*(month(Dates)==Months(i)));
end
  2 Comments
Nicholas Qassis
Nicholas Qassis on 18 Apr 2017
see my comments above. I know it's not the most efficient way to do this, but I have other functions in my code that would make changing things more difficult.
dpb
dpb on 18 Apr 2017
OK, after the second function the datetime class didn't help directly as much as I thunk it would...seems like the simplest coding would be to keep the year/month for each date column. I didn't mess with it any further but possibly as a categorical in a table would let you use grouping variables effectively albeit they're still rather klunky in their implementation in Matlab vis a vis "real" statistics packages.

Sign in to comment.

Categories

Find more on Dates and Time in Help Center and File Exchange

Tags

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!