Skip to content

heuristic_min_samples

timecave.utils.heuristic_min_samples(fs, freq_limit)

Compute the minimum number of samples the series should have according to the 10 / 20 sampling heuristic.

This function computes the minimum and maximum lengths for capturing a given frequency, assuming the time series was sampled at a frequency of fs Hertz and the largest frequency of interest for modelling purposes is freq_limit Hertz. The interval in which the sampling frequency should lie for freq_limit to be effectively captured is also derived. The 10 / 20 sampling heuristic is used to derive both results.

Parameters:

Name Type Description Default
fs float | int

The time series' sampling frequency (Hz).

required
freq_limit float | int

Largest frequency of interest (Hz).

required

Returns:

Type Description
dict

Minimum and maximum number of samples (Min_samples and Max_samples, respectively) required to capture freq_limit with a sampling frequency of fs, according to the 10 / 20 heuristic rule.

Raises:

Type Description
TypeError

If either fs or freq_limit is neither a float nor an integer.

ValueError

If either fs or freq_limit are non-positive.

Warning

If the choice of fs and freq_limit does not abide by the 10 / 20 heuristic.

See also

Nyquist_min_samples: Performs the same computations using the Nyquist theorem.

Notes

Under certain circumstances, the conditions of the Nyquist theorem might not be enough to guarantee that the reconstruction of the signal is possible. To address this isssue, a heuristic has been developed in the field of control engineering, according to which the sampling frequency should be 10 to 20 times higher than the largest frequency of interest:

\[ 10 \cdot f <= f_s <= 20 \cdot f \]

Theoretically, the higher the sampling frequency, the better (i.e. the easier it can be to reconstruct the original signal), though hardware limitations naturally come into play here.

Examples:

>>> from timecave.utils import heuristic_min_samples
>>> n_samples = heuristic_min_samples(150, 10);
10 / 20 sampling heuristic results
----------------------------------
Minimum sampling rate required to capture a frequency of 10 Hz : 100 Hz
Maximum sampling rate required to capture a frequency of 10 Hz : 200 Hz
----------------------------------------------------------------------------------------------
Capturing a frequency of 10 Hz with a sampling frequency of 150 Hz would require:
150 to 300 samples
>>> n_samples
{'Min_samples': 150, 'Max_samples': 300}

If the frequency of interest cannot be captured using the sampling frequency provided by the user according to the heuristic, an exception is thrown:

>>> samples = heuristic_min_samples(80, 10);
Traceback (most recent call last):
...
Warning: This choice of sampling frequency and frequency of interest is not compliant with the 10 / 20 sampling heuristic.

If negative frequencies are passed, or if their values are neither integers nor floats, exceptions are thrown as well:

>>> samples = heuristic_min_samples(-2, 1);
Traceback (most recent call last):
...
ValueError: Frequencies should be non-negative.
>>> samples = heuristic_min_samples(1, "a");
Traceback (most recent call last):
...
TypeError: Both 'fs' and 'freq_limit' should be either integers or floats.
Source code in timecave/utils.py
def heuristic_min_samples(fs: float | int, freq_limit: float | int) -> dict:

    """
    Compute the minimum number of samples the series should have
    according to the 10 / 20 sampling heuristic.

    This function computes the minimum and maximum lengths for capturing a given frequency, 
    assuming the time series was 
    sampled at a frequency of `fs` Hertz and the largest frequency 
    of interest for modelling purposes is `freq_limit` Hertz. The
    interval in which the sampling frequency should lie for `freq_limit` 
    to be effectively captured is also derived. The 10 / 20 sampling 
    heuristic is used to derive both results.

    Parameters
    ----------
    fs : float | int
        The time series' sampling frequency (Hz).

    freq_limit : float | int
        Largest frequency of interest (Hz).

    Returns
    -------
    dict
        Minimum and maximum number of samples (Min_samples and Max_samples, respectively)
        required to capture freq_limit with a sampling frequency of fs, 
        according to the 10 / 20 heuristic rule.

    Raises
    ------
    TypeError
        If either `fs` or `freq_limit` is neither a float nor an integer.

    ValueError
        If either `fs` or `freq_limit` are non-positive.

    Warning
        If the choice of `fs` and `freq_limit` does not abide by the 10 / 20 heuristic.

    See also
    --------
    [Nyquist_min_samples](nyquist.md):
        Performs the same computations using the Nyquist theorem.

    Notes
    -----
    Under certain circumstances, the conditions of the Nyquist theorem might not be enough to guarantee that the reconstruction of the signal is possible.
    To address this isssue, a heuristic has been developed in the field of control engineering, according to which the sampling frequency should be 10 to 20 times higher than the largest \
    frequency of interest:

    $$
    10 \cdot f <= f_s <= 20 \cdot f
    $$

    Theoretically, the higher the sampling frequency, the better (i.e. the easier it can be to reconstruct the original signal), \
    though hardware limitations naturally come into play here.

    Examples
    --------
    >>> from timecave.utils import heuristic_min_samples
    >>> n_samples = heuristic_min_samples(150, 10);
    10 / 20 sampling heuristic results
    ----------------------------------
    Minimum sampling rate required to capture a frequency of 10 Hz : 100 Hz
    Maximum sampling rate required to capture a frequency of 10 Hz : 200 Hz
    ----------------------------------------------------------------------------------------------
    Capturing a frequency of 10 Hz with a sampling frequency of 150 Hz would require:
    150 to 300 samples
    >>> n_samples
    {'Min_samples': 150, 'Max_samples': 300}

    If the frequency of interest cannot be captured using the sampling frequency provided \
    by the user according to the heuristic, an exception is thrown:

    >>> samples = heuristic_min_samples(80, 10);
    Traceback (most recent call last):
    ...
    Warning: This choice of sampling frequency and frequency of interest is not compliant with the 10 / 20 sampling heuristic.

    If negative frequencies are passed, or if their values are neither integers nor floats, exceptions are thrown as well:

    >>> samples = heuristic_min_samples(-2, 1);
    Traceback (most recent call last):
    ...
    ValueError: Frequencies should be non-negative.
    >>> samples = heuristic_min_samples(1, "a");
    Traceback (most recent call last):
    ...
    TypeError: Both 'fs' and 'freq_limit' should be either integers or floats.
    """

    _check_frequencies(fs, freq_limit);
    _check_heuristic(fs, freq_limit);

    ts = 1 / fs;
    T_limit = 1 / freq_limit;
    t_lower = 10 * T_limit;
    t_upper = 20 * T_limit;
    n_lower = int(np.ceil(t_lower / ts));
    n_upper = int(np.ceil(t_upper / ts));

    print("10 / 20 sampling heuristic results");
    print("----------------------------------");

    print(f"Minimum sampling rate required to capture a frequency of {freq_limit} Hz : {10*freq_limit} Hz");
    print(f"Maximum sampling rate required to capture a frequency of {freq_limit} Hz : {20*freq_limit} Hz");
    print("----------------------------------------------------------------------------------------------");
    print(f"Capturing a frequency of {freq_limit} Hz with a sampling frequency of {fs} Hz would require:");
    print(f"{n_lower} to {n_upper} samples");

    return {"Min_samples": n_lower, "Max_samples": n_upper};