Mini classes
Because pysmo is built around its types rather than one all-encompassing class,
it has no single built-in class that all its functions and modules expect. As
shown in the tutorial, the intended approach is a
tailor-made class for each use case. When a class with exactly the attributes of
a given type is needed, pysmo provides its "Mini" classes. These are minimal
implementations of their respective types, named accordingly (for example,
Seismogram has MiniSeismogram).
They also serve as a lightweight scratch copy for processing, covered in the
example workflow below.
Forgiving on input, strict on values
Mini classes are deliberately forgiving on input and strict on values. They use converters to accept a range of input types, and validators to ensure those data make sense for seismological processing.
For example, when setting a sampling interval
(delta), the input can be a float (seconds), a
str (e.g. "10ms"), or a standard Python timedelta object. The Mini class
converts these into a canonical pandas.Timedelta. It does,
however, reject a negative value, since a negative sampling interval is
physically impossible.
Similarly, the data attribute accepts lists or
tuples and converts them to a numpy.ndarray automatically.
This "forgiving on input, strict on value" approach also applies when modifying attributes after the object has been created.
MiniSeismogram
The MiniSeismogram class shows what a Mini class looks
like:
@define(kw_only=True)
class MiniSeismogram(SeismogramEndtimeMixin):
"""Minimal implementation of the `Seismogram` type.
See [`Seismogram`][pysmo.Seismogram].
Examples:
```python
>>> from pysmo import MiniSeismogram
>>> import pandas as pd
>>> from datetime import timezone
>>> import numpy as np
>>> now = pd.Timestamp.now(timezone.utc)
>>> delta = pd.Timedelta(seconds=0.1)
>>> seismogram = MiniSeismogram(
... begin_time=now, delta=delta, data=np.random.rand(100)
... )
>>>
```
"""
begin_time: UtcTimestamp = field(
default=SeismogramDefaults.begin_time,
converter=convert_to_utc_timestamp,
on_setattr=setters.convert,
)
"""Seismogram begin time."""
delta: PositiveTimedelta = field(
default=SeismogramDefaults.delta,
converter=convert_to_timedelta,
validator=[
validators.instance_of(pd.Timedelta),
validators.gt(pd.Timedelta(0)),
],
on_setattr=setters.pipe(setters.convert, setters.validate),
)
"""Seismogram sampling interval."""
data: npt.NDArray[np.floating] = field(
factory=lambda: np.array([]),
converter=convert_to_ndarray,
validator=validators.instance_of(np.ndarray),
on_setattr=setters.pipe(setters.convert, setters.validate),
eq=cmp_using(eq=np.array_equal),
)
"""Seismogram data."""
At first glance it looks similar to the examples in the tutorial. A closer look shows some differences:
- Instead of the built-in
dataclasses.dataclassit uses attrs.define. The two look and work similarly, but attrs allows the validation and conversion mentioned above. - The
begin_timeis automatically converted to apandas.Timestamp. Timezone-aware values are converted to UTC; timezone-naive values are assumed to be UTC. - Some attributes have default values, usually replaced in real use but convenient for quick tests.
Example workflow
Mini classes can be instantiated directly, but it is often convenient to create one already populated with data from another object. Pysmo provides two functions for this:
clone_to_minicreates a new Mini class instance by copying matching attributes from an existing object. Attributes present in the source but not in the Mini class are ignored, resulting in a lightweight copy of the original data.copy_from_minidoes the reverse: it copies attributes from a Mini class instance back to a compatible target object.
Together these enable a workflow where data are cloned into a Mini class for
processing, then copied back to the original object. The example below loads
data from a SAC file into a MiniSeismogram, processes it, and copies it back:
from pysmo import MiniSeismogram
from pysmo.classes import SAC
from pysmo.functions import clone_to_mini, copy_from_mini, resample
# Read SAC file and clone it to a MiniSeismogram
sac = SAC.from_file("testfile.sac")
mini = clone_to_mini(MiniSeismogram, sac.seismogram)
# Process seismogram
resample(mini, mini.delta * 2) # (1)!
...
# Copy processed seismogram back to the SAC file
copy_from_mini(mini, sac.seismogram)
sac.write("testfile_out.sac")
sac.seismogramcould be processed directly; this example assumes processing is faster on aMiniSeismogram.