2

I have a file called data, within which there is a part Data.ensemble of which the first few lines look as following:

                Year Month Day Hour Min Temp
0   1635    1   2009    12  10  22  36  16.28
0   1635    2   2009    12  10  22  37  17.25
0   1635    3   2009    12  10  22  38  16.97
0   1635    4   2009    12  10  22  39  16.69
0   1635    5   2009    12  10  22  40  17.42

I want to extract the temperature in December on minute 0, 20, 30 and 40 every hour. I have trouble coding this. This is what I am trying:

Month = 12;
Minute = [0 20 30 40];

if Data.ensemble(:, 5) == Month & (Data.ensemble(:, 8) == (Minute(1) | Minute(2) | Minute(3) | Minute(4)))
    Temperature = Data.ensemble(:, 10)
end

This doesn't seem to create Temperature, and I expect it's just gonna copy the entire colomn, not just the temperature for the correct minutes. Moreover, I am not quite sure the brackets really use the right hierarchy between and / or. It has to always be december (12) and on of the minutes (0 or 20 or 30 or 40).

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
Jellyse
  • 839
  • 7
  • 22

4 Answers4

3

You can use logical indexing:

%dummy data

x = datevec(now+[1:30]+170)

% x =
%
%   2019.0000     11.0000     30.0000     12.0000     56.0000     43.4885
%   2019.0000     12.0000      1.0000     12.0000     56.0000     43.4885
%   2019.0000     12.0000      2.0000     12.0000     56.0000     43.4885
%   2019.0000     12.0000      3.0000     12.0000     56.0000     43.4885
%   2019.0000     12.0000      4.0000     12.0000     56.0000     43.4885
%    ... 

%create a logical index to get each 10th,20th and 30th day of the month in december.
%we can use ismember to check several day at once.
index = ismember(x(:,3),[10,20,30]) & x(:,2) == 12
y     = x(index,:)

% y =
%
%   2019.000     12.000     10.000     12.000     59.000     13.826
%   2019.000     12.000     20.000     12.000     59.000     13.826

If you want to use an if else statement you need a for loop to check each row individually.

obchardon
  • 10,614
  • 1
  • 17
  • 33
1

You're close, but if you try to evaluate your logical conditions, you'll see it doesn't work:

>> Minute(1)|Minute(2)|Minute(3)|Minute(4)
ans = 1

And so (assuming I store your data in the variable A):

>> (A(:,8) == (Minute(1)|Minute(2)|Minute(3)|Minute(4)))
ans =

  0
  0
  0
  0
  0

Given that:

>> A(:,5) == Month
ans =

  1
  1
  1
  1
  1

We get:

>> A(:,5) == Month & (A(:,8)==(Minute(1)|Minute(2)|Minute(3)|Minute(4)))
ans =

  0
  0
  0
  0
  0

which means the condition is never true and Temperature never gets defined. The correct way to do this is (or I should say one of the ways to do this) is by using logical indexing:

>> idx = ( A(:,8)==Minute(1)|A(:,8)==Minute(2)|A(:,8)==Minute(3)|A(:,8)==Minute(4)) & A(:,5) == Month

idx =

  0
  0
  0
  0
  1

You can then define your temperature as:

>> Temperature = A(idx,9)
Temperature =  17.420
am304
  • 13,758
  • 2
  • 22
  • 40
1

You can use logical indexing:

tf = Data.ensemble(:,5) == Month & any(Data.ensemble(:,8) == Minute, 2);
Temperature = Data.ensemble(tf,9)

Or find:

ind = find(Data.ensemble(:,5) == Month & any(Data.ensemble(:,8) == Minute, 2));
Temperature = Data.ensemble(ind,9)

Note: find is a slower than logical indexing.

Explanation:

Data.ensemble(:,5) == Month returns a logical column vector with 1 where the condition is true and 0 for false. For your example the result will be:

5×1 logical array
   1
   1
   1
   1
   1

Data.ensemble(:,8) == Minute returns a logical matrix where each column is for each element of Minute. For your example the result will be:

  5×4 logical array
   0   0   0   0
   0   0   0   0
   0   0   0   0
   0   0   0   0
   0   0   0   1

any(Data.ensemble(:,8) == Minute, 2): To get a single logical vector that is 1 for each row that has a 1 (= a minute condition was matched), we call any on the rows (the second dimension). For your example the result will be:

5×1 logical array
   0
   0
   0
   0
   1

Calling:

any(Data.ensemble(:,8) == Minute, 2)

is shorter than:

Data.ensemble(:,8) == Minute(1) | Data.ensemble(:,8) == Minute(2) | Data.ensemble(:,8) == Minute(3) | Data.ensemble(:,8) == Minute(4)
Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
1

You have a misunderstanding of how logical indexing works in MATLAB, but aside from that, you got the right idea.

The expression (Minute(1) | Minute(2) | Minute(3) | Minute(4)) will do logical or on the scalar elements. Since they are all non-zero (and scalars), the result is always 1. Since your minute is rarely going to be 1, this is probably going to select nothing.

The expression Data.ensemble(:, 5) == Month creates a logical array with the same number of elements as there are rows in Data.ensemble. Some of the elements may be 0, if there is more data than what you show in the example. This expression likely gave you a warning, since the logical value of the resulting array is ambiguous:

  1. You can set it to 1 if any element is 1
  2. You can set it to 0 if any element is 0
  3. You can set it to 1 if the array is simply non-empty
  4. Any other criterion that makes sense in context ...

Even if final condition were to come out 1, the expression Temperature = Data.ensemble(:, 10) would just set Temperature to the entire 10th column.

You are looking instead to apply a corrected version of that if statement to each individual row of the data. The trick is to use a logical array to encode your condition, and then use that logical array as an index:

 month_is_12 = (Data.ensemble(:, 5) == Month);
 minute_is_good = ismember(Data.ensemble(:, 8), Minute);

ismember will check each element of the 8th column against the Minute array to determine if it matches any of the elements. It's equivalent to doing

minute_is_good = ((Data.ensemble(:, 8) == Minute(1)) | (Data.ensemble(:, 8) == Minute(2)) | (Data.ensemble(:, 8) == Minute(3)) | (Data.ensemble(:, 8) == Minute(4)));

Not only does ismember look nicer, but it lets you modify the size of Minute without having to modify your actual code.

Now you don't need an if to get the data you want:

Temperature = Data.endemble(month_is_12 & minute_is_good);
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264