Note
Go to the end to download the full example code.
RMEMeas Objects¶
This is a basic example demonstrating how to create and use RMEMeas objects. In this example we go over different ways to initialize one from scratch, how to create linear uncertainty mechanisms, and how to create probability distributions for Monte Carlo analysis. Indexing and interpolating are also briefly discussed.
Initializing from a Nominal Data Set¶
The easiest - and most versatile - way to make a RMEMeas object from scratch is to
use the rmellipse.uobjects.RMEMeas.from_nom() function, then
add the uncertainty mechanisms and Monte Carlo data.
With this function you just provide a nominal data set and a name for
your object. The name is used when saving to formats like xml and hdf5.
from rmellipse.uobjects import RMEMeas
import xarray as xr
import numpy as np
nom = xr.DataArray(
[[1, 2, 3], [4, 5, 6]],
dims=('d1', 'd2'),
coords={'d1': [0.1, 0.2], 'd2': ['a', 'b', 'c']},
)
meas = RMEMeas.from_nom(name='myval', nom=nom)
print(meas.nom)
<xarray.DataArray (d1: 2, d2: 3)> Size: 48B
array([[1, 2, 3],
[4, 5, 6]])
Coordinates:
* d1 (d1) float64 16B 0.1 0.2
* d2 (d2) <U1 12B 'a' 'b' 'c'
Then we can manually add linear uncertainty mechanisms, defining the degrees
of freedom and categorizing the mechanisms as we go. The
rmellipse.uobjects.RMEMeas.add_umech() function
is expecting the value to be the same shape, dimensions, and coordinates of the
nominal. It is easiest to use the nom attribute of your object, then perturb
it 1 standard uncertainty.
This example adds a linear uncertainty mechanism with infinite degrees of
freedom, because
it comes from an external source we trust, and perturbs each element in the
nominal data set by 0.001.
We also add a unique ID to the mechanism name, because we want to be sure
that this mechanism will be fully independent in the event someone
uses it with other uncertainty objects they might create in their own
scripts. We also categorize it as Type B and categorize it’s origin from
a data sheet so we can group this mechanism down the road with mechanisms
from similar sources.
meas.add_umech(
name='My Uncertainty Mechanism',
value=meas.nom + np.ones(meas.nom.shape) * 0.01,
dof=np.inf,
category={'Type': 'B', 'Origin': 'Data Sheet'},
add_uid=True,
)
print(meas.stdunc(k=1).cov)
C:\Users\dcg2\AppData\Local\Temp\1\tmps8r3znms\cf560783ee5174087b691ad0277df8fc9b35e110\docs\examples\grp1_RME_gettingstarted\plot_e00_creating_RMEMeas_objects.py:56: DeprecationWarning: add_uid is deprecated and will be removed in 0.5.0, all umech_ids will be assigned as a uid moving. This is included to avoid breaking existing code.
meas.add_umech(
<xarray.DataArray (d1: 2, d2: 3)> Size: 48B
array([[0.01, 0.01, 0.01],
[0.01, 0.01, 0.01]])
Coordinates:
* d1 (d1) float64 16B 0.1 0.2
* d2 (d2) <U1 12B 'a' 'b' 'c'
For linear uncertainties can calculate degrees of freedom associated with each measurand.
print(meas.dof())
# Calculate uncertainty bounds for a given expansion factor
# (nominal + k * standard uncertainty).
print(meas.uncbounds(k=1).cov)
# And estimate confidence intervals.
print(meas.confint(0.95))
<xarray.DataArray (d1: 2, d2: 3)> Size: 48B
array([[inf, inf, inf],
[inf, inf, inf]])
Coordinates:
* d1 (d1) float64 16B 0.1 0.2
* d2 (d2) <U1 12B 'a' 'b' 'c'
<xarray.DataArray (d1: 2, d2: 3)> Size: 48B
array([[1.01, 2.01, 3.01],
[4.01, 5.01, 6.01]])
Coordinates:
* d1 (d1) float64 16B 0.1 0.2
* d2 (d2) <U1 12B 'a' 'b' 'c'
(<xarray.DataArray (d1: 2, d2: 3)> Size: 48B
array([[0.98040036, 1.98040036, 2.98040036],
[3.98040036, 4.98040036, 5.98040036]])
Coordinates:
* d1 (d1) float64 16B 0.1 0.2
* d2 (d2) <U1 12B 'a' 'b' 'c', <xarray.DataArray (d1: 2, d2: 3)> Size: 48B
array([[1.01959964, 2.01959964, 3.01959964],
[4.01959964, 5.01959964, 6.01959964]])
Coordinates:
* d1 (d1) float64 16B 0.1 0.2
* d2 (d2) <U1 12B 'a' 'b' 'c')
Monte Carlo Distributions¶
We also sample from a random distribution to add samples for use
in Monte Carlo uncertainty analysis using
rmellipse.uobjects.RMEMeas.add_mc_sample(). It is expecting a DataArray
with the same dimensions and coordinates as the nominal.
for i in range(100):
meas.add_mc_sample(meas.nom + np.random.normal(*meas.nom.shape) * 0.01)
print(meas.stdunc(k=1).mc)
<xarray.DataArray (d1: 2, d2: 3)> Size: 48B
array([[0.02906974, 0.02906974, 0.02906974],
[0.02906974, 0.02906974, 0.02906974]])
Coordinates:
* d1 (d1) float64 16B 0.1 0.2
* d2 (d2) <U1 12B 'a' 'b' 'c'
We can also calculate uncertainty bounds for a given expansion factor (nominal + k * standard uncertainty).
print(meas.uncbounds(k=1).cov)
<xarray.DataArray (d1: 2, d2: 3)> Size: 48B
array([[1.01, 2.01, 3.01],
[4.01, 5.01, 6.01]])
Coordinates:
* d1 (d1) float64 16B 0.1 0.2
* d2 (d2) <U1 12B 'a' 'b' 'c'
Indexing¶
RMEMeas objects support xarray style indexing, which allows for label based indexing into arrays (like pandas) and integer based indexing into arrays (like numpy).The cov/mc attributes of the newly created RMEMeas object will be views on the original. Call RMEMeas.copy() to turn the view into it’s own measurement.
Importantly, when indexing into a RMEMeas array with xarray like functions ( __getitem__ , loc, sel, and isel) you index into the RMEMeas object as if you were indexing into the nominal value. These functions, when called on the RMEMeas object, will always keep all the linear uncertainty mechanisms or Monte Carlo samples.
Please refer to the xarray documentation for more details on how to use these indexing functions.
print('Inspect the nominal to see the dimensions and coordinates to index')
print(meas.nom.shape)
print(meas.nom.dims)
print(meas.nom.coords, '\n')
print('positional lookup by integer')
print(meas[0, 0], '\n')
print('positional lookup by label')
print(meas.loc[0.1, 'a'], '\n')
print('named lookup by integer')
print(meas.isel(d1=0, d2=0), '\n')
print('named lookup by label')
print(meas.sel(d1=0.1, d2='a'), '\n')
Inspect the nominal to see the dimensions and coordinates to index
(2, 3)
('d1', 'd2')
Coordinates:
* d1 (d1) float64 16B 0.1 0.2
* d2 (d2) <U1 12B 'a' 'b' 'c'
positional lookup by integer
myval with nominal:
<xarray.DataArray ()> Size: 8B
array(1.)
Coordinates:
d1 float64 8B 0.1
d2 <U1 4B 'a'
positional lookup by label
myval with nominal:
<xarray.DataArray ()> Size: 8B
array(1.)
Coordinates:
d1 float64 8B 0.1
d2 <U1 4B 'a'
named lookup by integer
myval with nominal:
<xarray.DataArray ()> Size: 8B
array(1.)
Coordinates:
d1 float64 8B 0.1
d2 <U1 4B 'a'
named lookup by label
myval with nominal:
<xarray.DataArray ()> Size: 8B
array(1.)
Coordinates:
d1 float64 8B 0.1
d2 <U1 4B 'a'
Indexing Covariance and Monte Carlo Data¶
If you wish to get a view into specific uncertainty mechanisms, or specific samples in the Monte Carlo distribution, then use the function usel.
# This example throws away the montecarlo samples and looks only at a single
# linear uncertainty mechanism.
mech = meas.usel(umech_id=meas.umech_id[0], mcsamples=[])
linunc, mcunc = mech.stdunc(k=1)
print(linunc)
<xarray.DataArray (d1: 2, d2: 3)> Size: 48B
array([[0.01, 0.01, 0.01],
[0.01, 0.01, 0.01]])
Coordinates:
* d1 (d1) float64 16B 0.1 0.2
* d2 (d2) <U1 12B 'a' 'b' 'c'
We can also look a one or more of the Monte Carlo samples by throwing away the covariance data and just keeping one of the Monte Carlo samples.
sample = meas.usel(umech_id=[], mcsamples=[1])
print(sample.mc[1, ...])
<xarray.DataArray (d1: 2, d2: 3)> Size: 48B
array([[1.02065929, 2.02065929, 3.02065929],
[4.02065929, 5.02065929, 6.02065929]])
Coordinates:
* d1 (d1) float64 16B 0.1 0.2
* d2 (d2) <U1 12B 'a' 'b' 'c'
umech_id int64 8B 1
While the xarray indexing methods index across uncertainty mechanisms and Monte Carlo samples, the usel method indexes into uncertainty mechanisms and Monte Carlo samples. In either case, the nominal values (the ‘nominal’ parameter location and 0th Monte Carlo sample) are always protected and always kept regardless of method.
For example, using usel and giving empty lists for umech_id and mcsamples throws away all the uncertainty information, and just keeps the nominal. This effectively means it no longer has any associated uncertainties.
nominal_only = meas.usel(umech_id=[], mcsamples=[])
print(nominal_only.nom)
print(nominal_only.stdunc())
<xarray.DataArray (d1: 2, d2: 3)> Size: 48B
array([[1., 2., 3.],
[4., 5., 6.]])
Coordinates:
* d1 (d1) float64 16B 0.1 0.2
* d2 (d2) <U1 12B 'a' 'b' 'c'
RMEUncTuple(cov=<xarray.DataArray (d1: 2, d2: 3)> Size: 48B
array([[0., 0., 0.],
[0., 0., 0.]])
Coordinates:
* d1 (d1) float64 16B 0.1 0.2
* d2 (d2) <U1 12B 'a' 'b' 'c', mc=<xarray.DataArray (d1: 2, d2: 3)> Size: 48B
array([[0., 0., 0.],
[0., 0., 0.]])
Coordinates:
* d1 (d1) float64 16B 0.1 0.2
* d2 (d2) <U1 12B 'a' 'b' 'c')
Assigning RMEMeas¶
Currently, RMEMeas objects indexing functions do not support item assignment. Values can be reassigned through propagation.
try:
meas[0] = 1
except TypeError as e:
print(e)
'RMEMeas' object does not support item assignment
Interpolating¶
RMEMeas supports interpolation by wrapping xarray’s built in interp function.
print(meas.interp(d1=[0.125, 0.15, 0.175]))
myval with nominal:
<xarray.DataArray (d1: 3, d2: 3)> Size: 72B
array([[1.75, 2.75, 3.75],
[2.5 , 3.5 , 4.5 ],
[3.25, 4.25, 5.25]])
Coordinates:
* d2 (d2) <U1 12B 'a' 'b' 'c'
* d1 (d1) float64 24B 0.125 0.15 0.175
Total running time of the script: (0 minutes 3.383 seconds)