Skip to content

Readings¤

The Readings class may be used after requesting measurement data from equipment. It calculates the mean, standard deviation and the standard deviation of the mean and provides a user-friendly way to display the information.

Suppose we have a Connection to a digital multimeter, dmm, and we fetch some readings

>>> data = dmm.query("FETCH?")
>>> data
' 8.000167847E+00, 8.000164032E+00, 8.000163241E+00, 8.000165864E+00, 8.000164893E+00\r\n'

We can create a Readings instance from the fetched data and then get statistical information about the data

>>> from msl.equipment import Readings
>>> r = Readings(data)
>>> r.mean
8.0001651754
>>> r.std
1.784701179552952e-06
>>> r.std_mean
7.981426314008917e-07
>>> len(r)
5

We can treat the readings as a numpy ndarray and call ndarray attributes

>>> print(r.max())
8.000167847
>>> print(r.min())
8.000163241

or access the numpy ndarray directly

>>> r.data
array([8.00016785, 8.00016403, 8.00016324, 8.00016586, 8.00016489])

Unpacking the Readings instance returns the mean and the standard deviation of the mean

>>> mean, std_mean = r
>>> mean
8.0001651754
>>> std_mean
7.981426314008917e-07

When converting the Readings to a string, the custom format specification is used and it displays the value (mean) with the uncertainty (standard deviation of the mean)

>>> f"{r}" # use the default options
'8.00016518(80)'
>>> f"{r:.2B}" # equivalent to previous
'8.00016518(80)'
>>> f"{r:.3PU}"  # retain 3 digits, +/- mode, unicode style
'8.000165175±0.000000798'

Format Specification¤

The format specification is similar to the built-in Python specification, but with additional options (mode, style and si). The number of significant digits for the value (mean) and the uncertainty (standard deviation of the mean) can be controlled, based on the uncertainty.

The grammar for the format specification is defined as,

[[fill]align][sign][z][#][0][width][grouping][.digits][type][mode][style][si]

where we note the use of digits (not precision) and the additional mode, style and si options. digits refers to the number of significant digits to retain in the uncertainty. The mode option specifies how the value and the uncertainty are separated: B (bracket mode, default) or P (plus-minus sign). There are two style options: L (\(\LaTeX\)) or U (unicode). The si option can only be S and if it is specified the appropriate SI prefix symbol replaces the Base-10 component.

We can also create a Readings instance by specifying the mean, standard deviation and size keyword arguments

>>> r = Readings(mean=3.4562e-6, std=4.218e-8, size=10)
>>> r.mean
3.4562e-06
>>> r.std
4.218e-08
>>> r.std_mean  # 4.218e-8 / sqrt(10)
1.3338487170590222e-08

Here are some examples on how to use the custom format specification

>>> f"{r}"  # default is to retain 2 digits with bracket mode
'0.000003456(13)'
>>> f"{r:P}"  # +/- mode
'0.000003456+/-0.000000013'
>>> f"{r:PU}"  # +/- mode, unicode style
'0.000003456±0.000000013'
>>> f"{r:e}"  # exponent form
'3.456(13)e-06'
>>> f"{r:S}" # SI prefix
'3.456(13) u'
>>> f"{r:US}" # unicode style, SI prefix
'3.456(13) µ'
>>> f"{r:.1eU}" # 1 digit, exponent form, unicode style
'3.46(1)×10⁻⁶'
>>> f"{r:eL}" # exponent form, LaTeX style
'3.456\\left(13\\right)\\times10^{-6}'
>>> f"{r:=^+30.4e}"  # fill with '=', align center, include + sign, 30 characters in total, 4 digits, exponent form
'======+3.45620(1334)e-06======'

If the standard deviation of the mean is zero, the uncertainty component is not included in the output and the format applies only to the mean

>>> r = Readings(mean=3.4562e-6, std=0, size=10)
>>> f"{r}"  # default is 2 decimal places in floating-point, f, notation
'0.00'
>>> f"{r:e}"
'3.46e-06'
>>> f"{r:.4e}"  # 4 decimals (uses built-in specification since no custom options are used)
'3.4562e-06'
>>> f"{r:.4eUS}"  # 4 digits (uses custom specification since custom options are included)
'3.456 µ'

Readings ¤

Readings(
    data: str | Sequence[str | float] | NDArray[number] | None = None,
    *,
    mean: float | None = None,
    std: float | None = None,
    size: int | None = None,
    overload: float | None = 1e30,
    delimiter: str | None = ","
)

A formatting-friendly convenience class for measurement data.

Parameters:

Name Type Description Default
data str | Sequence[str | float] | NDArray[number] | None

The measurement data.

None
mean float | None

If specified, then the mean is not calculated from the data.

None
std float | None

If specified, then the standard deviation is not calculated from the data.

None
size int | None

If specified, then the number of items is not determined from the data.

None
overload float | None

For some devices, like a digital multimeter, if the input signal is greater than the range can measure, the device returns a large value (e.g., 9.9E+37) to indicate a measurement overload. If the absolute value of the mean is greater than overload then the mean and standard deviation become NaN. Setting overload to None disables this check.

1e+30
delimiter str | None

The character used to separate values (only used if data is of type str). The value None corresponds to whitespace.

','
Source code in src/msl/equipment/readings.py
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
def __init__(
    self,
    data: str | Sequence[str | float] | NDArray[np.number] | None = None,
    *,
    mean: float | None = None,
    std: float | None = None,
    size: int | None = None,
    overload: float | None = 1e30,
    delimiter: str | None = ",",
) -> None:
    """A formatting-friendly convenience class for measurement data.

    Args:
        data: The measurement data.
        mean: If specified, then the mean is not calculated from the `data`.
        std: If specified, then the standard deviation is not calculated from the `data`.
        size: If specified, then the number of items is not determined from the `data`.
        overload: For some devices, like a digital multimeter, if the input signal is
            greater than the range can measure, the device returns a large value
            (e.g., 9.9E+37) to indicate a measurement overload. If the absolute value of
            the mean is greater than `overload` then the mean and standard deviation become
            `NaN`. Setting `overload` to `None` disables this check.
        delimiter: The character used to separate values (only used if `data` is of type [str][]).
            The value `None` corresponds to whitespace.
    """
    if data is not None and any(a is not None for a in (mean, std, size)):
        msg = "Cannot specify data and the mean, std or size"
        raise ValueError(msg)

    self._data: NDArray[np.float64]
    if isinstance(data, str):
        stripped = data.strip()
        if stripped:
            self._data = np.loadtxt(StringIO(stripped), dtype=float, delimiter=delimiter)
        else:
            self._data = np.empty(0)
    elif isinstance(data, np.ndarray):
        self._data = data.astype(np.float64)
    elif data is None:
        self._data = np.empty(0)
    else:
        self._data = np.asarray(data, dtype=float)

    self._size: int = self._data.size if size is None else size
    self._overload: float | None = None if overload is None else float(overload)
    self._std: float | None = std

    self._mean: float | None
    if mean is not None:
        self._mean = self._check_overload(mean)
    else:
        self._mean = None

data property ¤

Returns the data.

mean property ¤

mean: float

Returns the mean.

overload property ¤

overload: float | None

Returns the overload value.

std property ¤

std: float

Returns the sample standard deviation (uses \(N-1\) in the denominator).

std_mean property ¤

std_mean: float

Returns the standard deviation of the mean.