Reputation: 33
I have 3-dimensional data matrix for ten years (2001-2010). In each file data matrix is 180 x 360 x 365/366 (latitude x longitude x daily rainfall). for example: 2001: 180x360x365, 2002: 180x360x365, 2003: 180x360x365, 2004: 180x360x366........................... 2010: 180x360x365
Now I want to convert this daily rainfall into monthly rainfall (by summing) and combine all the years in one file.
So my final output will be 180x360x120 (latitude x longitude x monthly rainfall over ten the years).
Upvotes: 1
Views: 223
Reputation: 1439
I am sure you can vectorise this to work faster, but it should do the job. Haven't tested properly
% range of years
years = 2000:2016;
leap_years = [2000 2004 2008 2012 2016];
% Generating random data
nr_of_years = numel(years);
rainfall_data = cell(nr_of_years, 1);
for i=1:nr_of_years
nr_of_days = 365;
if ismember(years(i), leap_years);
nr_of_days = 366;
end
rainfall_data{i} = rand(180, 360, nr_of_days);
end
The actual code you need is below
% fixed stuff
months = 12;
nr_of_days = [31 28 31 30 31 30 31 31 30 31 30 31];
nr_of_days_leap = [31 29 31 30 31 30 31 31 30 31 30 31];
% building vectors of month indices for days
month_indices = [];
month_indices_leap = [];
for i=1:months
month_indices_temp = repmat(i, nr_of_days(i), 1);
month_indices_leap_temp = repmat(i, nr_of_days_leap(i), 1);
month_indices = [month_indices; month_indices_temp];
month_indices_leap = [month_indices_leap; month_indices_leap_temp];
end
% the result will be stored here
result = zeros(size(rainfall_data{i}, 1), size(rainfall_data{i}, 2), months*nr_of_years);
for i=1:nr_of_years
% determining which indices to use depending if it is a leap year
month_indices_temp = month_indices;
if size(rainfall_data{i}, 3)==366
month_indices_temp = month_indices_leap;
end
% data for the current year
current_data = rainfall_data{i};
% this holds the data for current year
monthy_sums = zeros(size(rainfall_data{i}, 1), size(rainfall_data{i}, 2), months);
for j=1:months
monthy_sums(:,:,j) = sum(current_data(:,:,j==month_indices_temp), 3);
end
% putting it into the combined matrix
result(:,:,((i-1)*months+1):(i*months)) = monthy_sums;
end
You can probably achieve a more elegant solution using build in datetime
, datestr
and datenum
, but I am not sure those would be a lot faster or shorter.
EDIT: An alternative using built in date functions
months = 12;
% where the result will be stored
result = zeros(size(rainfall_data{i}, 1), size(rainfall_data{i}, 2), months*nr_of_years);
for i=1:nr_of_years
current_data = rainfall_data{i};
% first day of the year
year_start_timestamp = datenum(datetime(years(i), 1, 1));
% holding current sums
monthy_sums = zeros(size(current_data, 1), size(current_data, 2), months);
% finding the month indices vector
datetime_obj = datetime(datestr(year_start_timestamp:(year_start_timestamp+size(current_data, 3)-1)));
month_indices = datetime_obj.Month;
% summing
for j=1:months
monthy_sums(:,:,j) = sum(current_data(:,:,j==month_indices), 3);
end
% result
result(:,:,((i-1)*months+1):(i*months)) = monthy_sums;
end
This 2nd solution took 1.45 seconds for me, compared to the 1.2 seconds for the first solution. The results were the same for both cases. Hope this helps.
Upvotes: 0
Reputation: 56
It might be time consuming, but I suppose you could use some form of loop to iterate over the data in each year on a monthly basis, pick out the appropriate number of data points for each month, and then add that to a final data set. Something to the effect of the (very rough) code below might work:
years = ['2001','2002,'2003',...,'2010'];
months = ['Jan','Feb','Mar',...,'Dec'];
finalDataset=[];
for i=1:length(years)
year = years(i);
yearData=%% load in dataset for that year %%
for j=1:length(months)
month = months(j);
switch month
case {'Jan','Mar'}
days=30;
case 'Feb'
days=28'
if(year=='2004' || year=='2008')
days=29;
end
% then continue with cases to include each month
end
monthData=yearData(:,:,1:days) % extract the data for those months
yearData(:,:,1:days)=[]; % delete data already extracted
summedRain = % take mean of rainfall data
monthSummed = % replace daily rainfall data with monthly rainfall, but keep latitude and longitude data
finalDataset=[finalDataset; monthSummed];
end
end
Apologies it's very shabby and I haven't included some of the indexing details, but I hope that helps in at least illustrating an idea? I'm also not entirely sure whether 'if' statements work within 'switch' statements, but the days amendment can be added elsewhere if not.
Upvotes: 0