Skip to content

Commit 89d8664

Browse files
jeandetclaude
andcommitted
Rewrite NumPy and SciPy compatibility docs for clarity
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3da8fd2 commit 89d8664

File tree

2 files changed

+92
-18
lines changed

2 files changed

+92
-18
lines changed

docs/user/numpy.rst

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
Numpy compatibility
1+
NumPy compatibility
22
===================
33

44
.. toctree::
55
:maxdepth: 1
66

7-
8-
Speasy is compatible with `numpy <https://numpy.org/>`_ and uses it internally to perform various operations. You can also pass Speasy variables to most numpy functions.
7+
``SpeasyVariable`` objects behave like NumPy arrays: you can use arithmetic operators, pass them to NumPy functions,
8+
and index them with boolean masks or integer arrays. The result is always a ``SpeasyVariable`` when the shape
9+
allows it (i.e. when the time axis is preserved), and a scalar or plain array otherwise.
910

1011
Arithmetic operations
1112
---------------------
1213

13-
For example, you can perform arithmetic operations on Speasy variables:
14+
Standard arithmetic operators (``+``, ``-``, ``*``, ``/``, ``**``) work directly on Speasy variables
15+
and return a new ``SpeasyVariable`` with the same time axis:
1416

1517
>>> import speasy as spz
1618
>>> ace_mag = spz.get_data('amda/imf', "2016-6-2", "2016-6-5")
@@ -19,12 +21,12 @@ For example, you can perform arithmetic operations on Speasy variables:
1921
<class 'speasy.products.variable.SpeasyVariable'>
2022

2123

22-
Numpy functions
24+
NumPy functions
2325
---------------
2426

25-
You can also use numpy functions on Speasy variables, depending on the function, the result will be a Speasy variable or a scalar value:
27+
Most NumPy functions accept Speasy variables directly.
2628

27-
In the following example, np.mean and np.std return scalar values:
29+
**Reduction functions** (like ``np.mean``, ``np.std``) collapse the data and return scalar values:
2830

2931
>>> import speasy as spz
3032
>>> import numpy as np
@@ -35,7 +37,7 @@ In the following example, np.mean and np.std return scalar values:
3537
>>> np.std(ace_mag) / np.std(mag_divided_offset)
3638
np.float32(3.0)
3739

38-
In the following example, np.linalg.norm returns a Speasy variable with the same number of rows as the input variable:
40+
**Per-row functions** (like ``np.linalg.norm`` with ``axis=1``) preserve the time axis and return a ``SpeasyVariable``:
3941

4042
>>> import speasy as spz
4143
>>> import numpy as np
@@ -49,10 +51,10 @@ In the following example, np.linalg.norm returns a Speasy variable with the same
4951
(16200, 1)
5052

5153

52-
Indexing
53-
--------
54+
Indexing and slicing
55+
--------------------
5456

55-
Speasy variables support several indexing methods, including boolean indexing:
57+
**Boolean indexing** — select rows where a condition is true across all columns:
5658

5759
>>> import speasy as spz
5860
>>> ace_mag = spz.get_data('amda/imf', "2016-6-2", "2016-6-5")
@@ -61,7 +63,7 @@ Speasy variables support several indexing methods, including boolean indexing:
6163
>>> ace_mag[ace_mag > 0].shape, ace_mag.shape
6264
((1157, 3), (16200, 3))
6365

64-
You can also use integer indexing as with :meth:`numpy.where(...) <numpy.where>`:
66+
**Integer indexing** with ``np.where``:
6567

6668
>>> import numpy as np
6769
>>> import speasy as spz
@@ -70,3 +72,36 @@ You can also use integer indexing as with :meth:`numpy.where(...) <numpy.where>`
7072
<speasy.products.variable.SpeasyVariable object at ...>
7173
>>> ace_mag[np.where(ace_mag>0)].shape, ace_mag.shape
7274
((1157, 3), (16200, 3))
75+
76+
**Column selection** — select one or more columns by name:
77+
78+
>>> import speasy as spz
79+
>>> ace_mag = spz.get_data('amda/imf', "2016-6-2", "2016-6-5")
80+
>>> ace_mag.columns
81+
['bx', 'by', 'bz']
82+
>>> bx = ace_mag["bx"]
83+
>>> bx.shape
84+
(16200, 1)
85+
86+
**Time slicing** — slice by ``np.datetime64`` or ``datetime`` objects:
87+
88+
>>> import speasy as spz
89+
>>> import numpy as np
90+
>>> ace_mag = spz.get_data('amda/imf', "2016-6-2", "2016-6-5")
91+
>>> one_hour = ace_mag[np.datetime64("2016-06-02"):np.datetime64("2016-06-02T01:00:00")]
92+
>>> one_hour.shape[0] < ace_mag.shape[0]
93+
True
94+
95+
96+
Pandas conversion
97+
-----------------
98+
99+
You can convert a ``SpeasyVariable`` to a Pandas ``DataFrame`` and back:
100+
101+
>>> import speasy as spz
102+
>>> ace_mag = spz.get_data('amda/imf', "2016-6-2", "2016-6-5")
103+
>>> df = ace_mag.to_dataframe()
104+
>>> type(df)
105+
<class 'pandas.core.frame.DataFrame'>
106+
>>> df.shape
107+
(16200, 3)

docs/user/scipy.rst

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,21 @@ SciPy compatibility
44
.. toctree::
55
:maxdepth: 1
66

7-
Speasy wraps a few `scipy <https://www.scipy.org/>`_ functions to provide interpolation, filtering, and resampling capabilities for Speasy variables.
7+
When working with space physics data, you often need to combine measurements from different instruments
8+
that sample at different rates, or remove low-frequency trends from a signal. Speasy provides
9+
ready-to-use functions for these common tasks, built on top of `SciPy <https://www.scipy.org/>`_.
10+
11+
All functions in ``speasy.signal`` accept one or more ``SpeasyVariable`` objects and return
12+
``SpeasyVariable`` objects with metadata preserved.
13+
814

915
Interpolation
1016
-------------
1117

12-
You can use the :func:`speasy.signal.resampling.interpolate` function to interpolate one or more Speasy variables onto a new time base or another variable's time base.
18+
Instruments on the same spacecraft often sample at different cadences (e.g. magnetic field at 16 Hz,
19+
particle moments at 4.5 s). To compare them, you need to bring them onto a common time base.
1320

14-
For example, you can interpolate a variable onto a new time base:
21+
**Interpolate onto a regular time grid:**
1522

1623
>>> import speasy as spz
1724
>>> from speasy.signal import resampling
@@ -26,7 +33,7 @@ For example, you can interpolate a variable onto a new time base:
2633
>>> bool(np.all(ace_mag_resampled.time == ref_time))
2734
True
2835

29-
Even more complex interpolations are supported, such as interpolating multiple variables onto a reference variable.
36+
**Interpolate onto another variable's time base** — useful for aligning two instruments:
3037

3138
>>> import speasy as spz
3239
>>> from speasy.signal import resampling
@@ -45,12 +52,34 @@ Even more complex interpolations are supported, such as interpolating multiple v
4552
>>> Tperp_interp.shape, Tpara_interp.shape, b.shape
4653
((240, 1), (240, 1), (240, 4))
4754

55+
Here ``Tperp`` and ``Tpara`` (sampled at ~4.5 s) are interpolated onto the magnetic field time base (~16 Hz).
56+
You can pass a list of variables to interpolate them all onto the same reference in one call.
57+
58+
59+
Resampling
60+
----------
61+
62+
If you simply want to change the sampling rate of a variable without providing an explicit time vector,
63+
use :func:`~speasy.signal.resampling.resample`:
64+
65+
>>> import speasy as spz
66+
>>> from speasy.signal import resampling
67+
>>> import numpy as np
68+
>>> ace_mag = spz.get_data('amda/imf', "2016-6-2", "2016-6-5")
69+
>>> ace_mag.shape
70+
(16200, 3)
71+
>>> ace_mag_5s = resampling.resample(ace_mag, np.timedelta64(5, 's'))
72+
>>> ace_mag_5s.shape
73+
(51837, 3)
74+
75+
4876
Filtering
4977
---------
5078

51-
You can use the :func:`speasy.signal.filtering.apply_sos_filter` function to apply a second-order section (SOS) filter to one or more Speasy variables.
79+
You can apply any SciPy IIR filter (designed as second-order sections) to a Speasy variable.
80+
This is useful for removing trends, isolating frequency bands, or smoothing data.
5281

53-
For example, you can apply a high-pass filter to a variable:
82+
**Example: high-pass filter to remove the DC component**
5483

5584
>>> import speasy as spz
5685
>>> from speasy.signal import filtering
@@ -63,3 +92,13 @@ For example, you can apply a high-pass filter to a variable:
6392
(16200, 3)
6493
>>> round(np.mean(ace_mag), 3), round(np.mean(ace_mag_filtered), 3)
6594
(np.float32(0.355), np.float32(0.0))
95+
96+
The ``apply_sos_filter`` function takes three arguments:
97+
98+
1. The filter coefficients (from ``scipy.signal.butter``, ``scipy.signal.cheby1``, etc. with ``output='sos'``)
99+
2. The filter function to apply (typically ``scipy.signal.sosfilt`` or ``scipy.signal.sosfiltfilt`` for zero-phase filtering)
100+
3. The variable(s) to filter (a single ``SpeasyVariable`` or a list of them)
101+
102+
.. note::
103+
Filtering assumes a regular time axis. If your data has gaps or irregular sampling,
104+
resample it first using :func:`~speasy.signal.resampling.resample` or :func:`~speasy.signal.resampling.interpolate`.

0 commit comments

Comments
 (0)