The ErrorNum class (advanced)

A Japanese version is here.

In the last post, I explained the basic usage of the ErrorNum class. In this post, I will explain a little more advanced usage of the class, including the following topics.

  • How to use covariance for error calculation
  • Significance test of the value
  • Conversion of a value using a function and its derivative

How to use covariance for error calculation

Sometimes, two variables have errors that do not independently vary. An example case is like this.

>> x = 4 + randn(100, 1);
>> y = x + 0.5 * randn(100, 1);
>> figure;plot(x, y, '.');
>> axis([0, 8, 0, 8]);

The resulting plot is,

Fig. 1: Two variables with correlated errors (covariance)

In this case, the error of x and y covary positively. Namely, when x is high, y also tends to be high and vice versa. In this case, x + y will have a larger error than x – y as illustrated in the next image.

Fig. 2: The variance of x + y should be larger than that of x – y

If two values have finite covariance, a naive addition or subtraction in the ErrorNum class will give an incorrect error estimation.

>> x_en = ErrorNum.create(x, 1);
>> y_en = ErrorNum.create(y, 1);

>> x_en + y_en % error is simple square root addition of individual error
ans = 
ErrorNum:
8.3 ± 1.5

>> x_en - y_en % this error should be smaller
ans = 
ErrorNum:
0.0 ± 1.5

In the ErrorNum class, you can aid this by giving covariance of these two variables. There are methods to invoke covariance-enable calculation.

% calculate covariance and feed it into addition.
>> cov_xy = cov(x, y); % note, cov gives a covariance 'matrix', so pick up (1, 2) or (2, 1) element.

>> x_en.covadd(y_en, cov_xy(1, 2))
ans = 
ErrorNum:
8 ± 2

>> x_en.covsub(y_en, cov_xy(1, 2))
ans = 
ErrorNum:
0.0 ± 0.4

Now the covariance for the subtraction is reduced to 0.4 from 1.5. By construction, y is based on x plus additional noise with 0.5 * randn, so getting a value close to 0.5 makes sense.

Significance test for the value

Once you calculate a value, you often want to do a significance test. The ErrorNum class can give you a simple estimate of the error based on ANOVA (analysis of variance).

%% ANOVA significance test
>> a = ErrorNum(3, 1);
>> b = ErrorNum(2, 1);

>> a.sig % this works
ans =
    0.0027

>> sig(a) % this also works
ans =
    0.0027

>> sig(a - b) % this also works
ans =
    0.4795

>> (a - b).sig % this doesn't work.
Error: File: ErrorNum_demo_advanced.m Line: 34 Column: 8
Invalid use of operator.

Note that there are two ways to invoke a class method. One with dot-notation, another with function-notation. Beware with the dot notation as it does not work unless an ErrorNum instance is already created before invoking the command.

Conversion of a value using a function and its derivative

Another case where the error calculation is complicated is when you convert a number with a certain function. Let’s see the following example.

>> x = ErrorNum([1, 2, 3], 0.1 * ones(1, 3))
ErrorNum:
1.00 ± 0.10,  2.00 ± 0.10,  3.00 ± 0.10

>> y = x.convert(@log, @(t) 1./t)
ErrorNum:
0.00 ± 0.10,  0.69 ± 0.05,  1.10 ± 0.03

In general, normal built-in functions do not work for ErrorNum (e.g. log(x) gives you an error), but by using this convert function and giving its derivative directly, you can let it apply function with appropriate (linear) errors calculation. Note that the same error in x is correctly shrunk in y, depending on the value of x.

The ErrorNum class (basics)

A Japanese version is here.

Please follow this instruction to get my software on your computer.

Proper treatment of the errors is essential in data analysis. Sometimes it is troublesome to deal with error for every value you compute in your code. This Matlab class helps your data and error analysis a little easy. Take a look at the following example. (Empty lines are omitted.)

% you know two values with an error
>> en1 = ErrorNum(5, 0.3)
en1 = 
ErrorNum:
5.0 ± 0.3
>> en2 = ErrorNum(2, 0.2)
en2 = 
ErrorNum:
2.0 ± 0.2
% You can do simple arithmetics on these values.
>> en1 + en2
ans = 
ErrorNum:
7.0 ± 0.4
>> en1 * en2
ans = 
ErrorNum:
10.0 ± 1.2
>> en1 / en2
ans = 
ErrorNum:
2.5 ± 0.3

These calculations obey basic error propagation rules. The results are shown using proper significant figures that are inferred from the error, which is the leading order of the error except when the leading order is 1.

% You should not worry about values much smaller than the error.
>> ErrorNum(1.2531, 0.2)
ans = 
ErrorNum:
1.3 ± 0.2
>> ErrorNum(1.2531, 0.02)
ans = 
ErrorNum:
1.25 ± 0.02
>> ErrorNum(1.2531, 0.002)
ans = 
ErrorNum:
1.253 ± 0.002

This will avoid a typical mistake of a science beginner who unnecessarily reports a 10-digit number for every result.

The ErrorNum can also be a vector or matrix. Currently, it only does the element-wise calculation.

>> m1 = ErrorNum([5, 4; 3, 2], 0.3 * ones(2, 2))
ans = 
ErrorNum:
5.0 ± 0.3,  4.0 ± 0.3
3.0 ± 0.3,  2.0 ± 0.3
>> m1 * en1
ans = 
ErrorNum:
25 ± 2,  20.0 ± 1.9
15.0 ± 1.7,  10.0 ± 1.6

You can also run basic functions like sum or mean on the ErrorNum matrix.

>> mean(m1)
ans = 
ErrorNum:
4.0 ± 0.2,  3.0 ± 0.2
>> sum(m1, 2) % summing direction also works!
ans = 
ErrorNum:
9.0 ± 0.4
5.0 ± 0.4

If you have some data, you can create ErrorNum out of it. It calculates the mean and the standard error of the mean (SEM) automatically.

% Note that you have to specify the direction to calculate.
>> data1 = [5.3, 5.2, 5.1, 5.8, 5.5]; ErrorNum.create(data1, 2)
ans = 
ErrorNum:
5.38 ± 0.12
>> data2 = ErrorNum.create(randn(30, 5) + 5, 1)
data2 = 
ErrorNum:
4.76 ± 0.15,  4.60 ± 0.18,  4.9 ± 0.2,  4.99 ± 0.17,  5.14 ± 0.19

In the next post, I will explain the advanced usage of the ErrorNum class.