SONY
SONY

Reputation: 33

How to convert 3-dimensional daily data into monthly?

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

Answers (2)

Vahe Tshitoyan
Vahe Tshitoyan

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

Jonathan Page
Jonathan Page

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

Related Questions