Reputation: 790
I am subtracting two matrices from each other. dataClim is the average data of each month (12 months) over a 30 year period. dataAll is the daily data over 1257 days. I need to subtract the average monthly data from the daily data of each month from 20100101 to 20130611 (t = 1:31 is January, t = 32-57 is February, all the way to December, then 363:393 is January again).
This code works, but I was wondering if there was any way to make it more efficient and less tedious. I don't know how I would write a loop because the months vary in number of days from 28 to 31.
% Create new array in which data_Anom is the anomaly
% dataAnom = dataAll - dataClim
% January
dataAnom_1 = bsxfun(@minus, dataAll(:,:,[1:31, 363:393, 728:758, 1094:1124]), dataClim(:,:,1));
% February
dataAnom_2 = bsxfun(@minus, dataAll(:,:,[32:57, 394:421, 759:787, 1125:1152]), dataClim(:,:,2));
% March
dataAnom_3 = bsxfun(@minus, dataAll(:,:,[58:88, 422:452, 788:818, 1153:1183]), dataClim(:,:,3));
% April
dataAnom_4 = bsxfun(@minus, dataAll(:,:,[89:118, 453:482, 819:848, 1184:1213]), dataClim(:,:,4));
% May
dataAnom_5 = bsxfun(@minus, dataAll(:,:,[119:148, 483:513, 849:879, 1214:1244]), dataClim(:,:,5));
% June
dataAnom_6 = bsxfun(@minus, dataAll(:,:,[149:178, 514:543, 880:909, 1245:1255]), dataClim(:,:,6));
% July
dataAnom_7 = bsxfun(@minus, dataAll(:,:,[179:209, 544:574, 910:940]), dataClim(:,:,7));
% August
dataAnom_8 = bsxfun(@minus, dataAll(:,:,[210:240, 575:605, 941:971]), dataClim(:,:,8));
% September
dataAnom_9 = bsxfun(@minus, dataAll(:,:,[241:270, 606:635, 972:1001]), dataClim(:,:,9));
% October
dataAnom_10 = bsxfun(@minus, dataAll(:,:,[271:301, 636:666, 1002:1032]), dataClim(:,:,10));
% November
dataAnom_11 = bsxfun(@minus, dataAll(:,:,[302:331, 667:696, 1033:1062]), dataClim(:,:,11));
% December
dataAnom_12 = bsxfun(@minus, dataAll(:,:,[332:362, 697:727, 1063:1093]), dataClim(:,:,12));
% Concatenate the seperate Anomalies
dataAnom = cat(3, dataAnom_1, dataAnom_2, dataAnom_3, dataAnom_4, dataAnom_5, dataAnom_6, dataAnom_7, dataAnom_8, dataAnom_9, dataAnom_10, dataAnom_11, dataAnom_12);
clear dataAnom_*
One idea I had was to concatenate the days for each month together first and then create dataAnom for each month. It's probably even slower.
% Concatenation days below to each month in dataAll into dataMon so that each month is placed together. This
% makes it easier to do the anomaly subtraction later.
dataMon = cat(3, dataAll(:,:,1:31), dataAll(:,:,363:393), dataAll(:,:,728:758) , dataAll(:,:,1094:1124),... % January
dataAll(:,:,32:57), dataAll(:,:,394:421), dataAll(:,:,759:787), dataAll(:,:,1125:1152),... % February
dataAll(:,:,58:88), dataAll(:,:,422:452), dataAll(:,:,788:818), dataAll(:,:,1153:1183),... % March
dataAll(:,:,89:118), dataAll(:,:,453:482), dataAll(:,:,819:848), dataAll(:,:,1184:1213),... % April
dataAll(:,:,119:148), dataAll(:,:,483:513), dataAll(:,:,849:879), dataAll(:,:,1214:1244),... % May
dataAll(:,:,149:178), dataAll(:,:,514:543), dataAll(:,:,880:909), dataAll(:,:,1245:1255),... % June. Last entry goes up to 20130611, not the full month
dataAll(:,:,179:209), dataAll(:,:,544:574), dataAll(:,:,910:940),... % July
dataAll(:,:,210:240), dataAll(:,:,575:605), dataAll(:,:,941:971),... % August
dataAll(:,:,241:270), dataAll(:,:,606:635), dataAll(:,:,972:1001),... % Sept
dataAll(:,:,271:301), dataAll(:,:,636:666), dataAll(:,:,1002:1032),... % Oct
dataAll(:,:,302:331), dataAll(:,:,667:696), dataAll(:,:,1033:1062),... % Nov
dataAll(:,:,332:362), dataAll(:,:,697:727), dataAll(:,:,1063:1093)); % Dec
% Create dataAnom
dataAnom1 = bsxfun(@minus, dataAll(:,:,1:124), dataClim(:,:,1);
dataAnom2 = bsxfun(@minus, dataAll(:,:,125:238:), dataClim(:,:,1);
.
.
.
dataAnom12 = ...
% Combine
dataAnom = cat(3, dataAnom1, dataAnom2, dataAnom3,....);
Upvotes: 0
Views: 235
Reputation: 8759
While I still hold onto the fact that you should refactor your data, you can also use the power of datenum
to help you. The following will make your life a lot easier, than trying to manually enter the days:
clear all; clc;
% Init Data
dataAll = rand(1437,159,1258);
% Starting date of sampling. Note that this assumes each day there was a sample, and only one sample
startDate = datenum('01-01-2010');
dateList = [0:1257] + startDate;
[yr, mn, ~, ~, ~, ~] = datevec(dateList);
% extract data depending on month that sample was taken
jan = dataAll(:,:,mn == 1);
feb = dataAll(:,:,mn == 2);
mar = dataAll(:,:,mn == 3);
... % and so forth
This should get the desired results you're looking for. From here you can do your resulting calculations:
dataAnom_1 = bsxfun(@minus, jan, dataClim(:,:,1));
With your updated information from the comments you can do the following to separate your data according to month and year:
jan2010 = dataAll(:,:,(mn == 1 & yr == 2010);
feb2010 = dataAll(:,:,(mn == 2 & yr == 2010);
... % and so forth
Upvotes: 1
Reputation: 8759
I think you need to rethink how you approaching the way your structuring your data here. Instead of creating massive single row arrays (dataAnom1
and dataAll
) I would use a better structured matrix.
At it's simplest you could used a scheme like this: 31x12xNumYears
which would produce something like this:
Data = NaN(31x12xNumYears); % Blank Init
Data(1:31,1,1) = Rand(31,1); % January's populated values
Data(1:28,2,1) = Rand(28,1); % February's populated values
... % and so forth
The advantage here is that matrix operations is far easier to do, and you have a better understanding of what the data actually represents. For example, given that dataClim is the monthy average over 30 years (matrix should be 12x30
) and dataAll is the daily reading (matrix should be 31x12x30
) you can do the following:
subValues = NaN(31,12,30);
for yr = 1:30
for mn = 1:12
subValues(1:31,mn,yr) = dataAll(1:31,mn,yr) - dataClim(mn,yr);
end
end
With the added information that you've given me, I think this is the type of structure that you might be looking for: days x months x years x 3
where three represents the lat, long, and dataValue of your data. So for example:
test = rand(31,12,30,3);
lat = test(1:end,1:end,1:end,1);
long = test(1:end,1:end,1:end,2);
data = test(1:end,1:end,1:end,3);
Upvotes: 3
Reputation: 2519
Couldn't you do
% January
dataAnom1 = bsxfun(@minus, dataAll(:,:,[1:31 363:393 728:758 1094:1124]), dataClim(:,:,1));
? I think this is the same.
And if so, then you can do
dataAnom1=zeros(size(dataAll,1),size(dataAll,2), 128*12);
for v=1:12
dataAnom1(:,:,1+((v-1)*128:v*128)) = bsxfun(@minus, dataAll(:,:,[1:31 363:393 728:758 1094:1124]+(v-1*32)), dataClim(:,:,v));
end
(Indexing might be a little off)
Upvotes: 1