Domanda

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,....);
È stato utile?

Soluzione 2

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

Altri suggerimenti

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);

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)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top