From 98fb669b341af857e42d52dedfbb40498acf8540 Mon Sep 17 00:00:00 2001 From: Derrick Chambers Date: Mon, 1 Jan 2024 22:16:47 -0700 Subject: [PATCH 1/6] improve table generation --- dascore/io/core.py | 5 +++-- dascore/transform/fft.py | 5 ++--- dascore/transform/fourier.py | 2 +- docs/filters/fill_links.py | 3 --- docs/index.qmd | 23 ++++++----------------- docs/styles.css | 28 ++++++++++++++-------------- pyproject.toml | 1 + scripts/_render_api.py | 20 +++++++++++++------- scripts/_templates/table.html | 24 ------------------------ 9 files changed, 40 insertions(+), 71 deletions(-) delete mode 100644 scripts/_templates/table.html diff --git a/dascore/io/core.py b/dascore/io/core.py index dbaa44f3..85f2b8ed 100644 --- a/dascore/io/core.py +++ b/dascore/io/core.py @@ -697,11 +697,12 @@ def write( The string indicating the format to write. file_version Optionally specify the version of the file, else use the latest - version. + version for the format. Raises ------ - dascore.exceptions.UnknownFiberFormat - Could not determine the fiber format. + [`UnkownFiberFormatError`](`dascore.exceptions.UnknownFiberFormatError`) + - Could not determine the fiber format. """ formatter = FiberIO.manager.get_fiberio(file_format, file_version) if not isinstance(patch_or_spool, dc.BaseSpool): diff --git a/dascore/transform/fft.py b/dascore/transform/fft.py index d3b5a6f1..428d1deb 100644 --- a/dascore/transform/fft.py +++ b/dascore/transform/fft.py @@ -1,7 +1,6 @@ """ -Module for Fourier transforms. - -See the [FFT note](dascore.org/notes/fft_notes.html) details. +Deprecated module for Fourier transforms. Use +[fourier](`dascore.transform.fourier`) instead. """ from __future__ import annotations diff --git a/dascore/transform/fourier.py b/dascore/transform/fourier.py index 5a1a6b2e..7d78df04 100644 --- a/dascore/transform/fourier.py +++ b/dascore/transform/fourier.py @@ -1,7 +1,7 @@ """ Module for Fourier transforms. -See the [FFT note](dascore.org/notes/fft_notes.html) for discussion on the +See the [FFT note](/notes/dft_notes.qmd) for discussion on the implementation. """ from __future__ import annotations diff --git a/docs/filters/fill_links.py b/docs/filters/fill_links.py index 719e8e72..960bcdad 100644 --- a/docs/filters/fill_links.py +++ b/docs/filters/fill_links.py @@ -99,6 +99,3 @@ def main(raw_data=None): if __name__ == "__main__": main() # uncomment before running quarto so IO works. - - -# this is helpful for debugging while quarto is running. diff --git a/docs/index.qmd b/docs/index.qmd index cd857ca0..83b7ba88 100644 --- a/docs/index.qmd +++ b/docs/index.qmd @@ -1,24 +1,9 @@ --- -title: DASCore execute: warning: false --- -A python library for distributed fiber optic sensing. - -[![coverage](https://codecov.io/gh/dasdae/dascore/branch/master/graph/badge.svg)](https://codecov.io/gh/dasdae/dascore) -[![PyPI Version](https://img.shields.io/pypi/v/dascore.svg)](https://pypi.python.org/pypi/dascore) -[![supported versions](https://img.shields.io/pypi/pyversions/dascore.svg?label=python_versions)](https://pypi.python.org/pypi/dascore) -[![PyPI Downloads](https://img.shields.io/pypi/dm/dascore.svg?label=pypi)](https://pypi.org/project/dascore/) -[![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/dascore.svg?label=conda)](https://github.com/conda-forge/dascore-feedstock) -[![DOI](https://zenodo.org/badge/422627477.svg)](https://zenodo.org/badge/latestdoi/422627477) -[![Licence](https://www.gnu.org/graphics/lgplv3-88x31.png)](https://www.gnu.org/licenses/lgpl.html) - - -[Code](https://github.com/DASDAE/dascore) - -Documentation [[stable](https://dascore.org), [development](https://dascore.netlify.app/)] - +{{< include ../readme.md >}} :::{.callout minimal="true"} Version-specific documentation builds are attached to the [release pages](https://github.com/DASDAE/dascore/releases). @@ -39,9 +24,13 @@ DASCore is a foundational package of the [DAS Data Analysis Ecosystem (DASDAE)]( # Supported file formats ```{python} #| echo: false +#| output: asis import pandas as pd from dascore.io.core import FiberIO -FiberIO.get_supported_io_table().replace(True, value='✅').replace(False, value='❌') +out_str = FiberIO.get_supported_io_table().replace(True, value='✅').replace(False, value='❌').to_markdown(index=False, stralign=True) + +out_str += '\n: {.striped}' +print(out_str) ``` # Introductory usage diff --git a/docs/styles.css b/docs/styles.css index 1289c45b..862fd001 100644 --- a/docs/styles.css +++ b/docs/styles.css @@ -26,7 +26,7 @@ td, th { } th { - background-color: rgb(235, 235, 235); + /*background-color: rgb(235, 235, 235);*/ text-align: center; } @@ -39,9 +39,9 @@ caption { padding: 10px; } -tbody tr:nth-child(even) { - background-color: rgba(210, 209, 209, 0.4); -} +/*tbody tr:nth-child(even) {*/ +/* background-color: rgba(210, 209, 209, 0.4);*/ +/*}*/ /* Custom classes */ @@ -61,13 +61,13 @@ tbody tr:nth-child(even) { padding-top: 2px; } -.origin_table { - border: 0em; - width: 100%; - margin: 0; - font-size: 1em; - border-style: hidden !important; - font-family: 'Bree Serif', serif; - display: block; - margin-bottom: 10px; -} +/*.origin_table {*/ +/* border: 0em;*/ +/* width: 100%;*/ +/* margin: 0;*/ +/* font-size: 1em;*/ +/* border-style: hidden !important;*/ +/* font-family: 'Bree Serif', serif;*/ +/* display: block;*/ +/* margin-bottom: 10px;*/ +/*}*/ diff --git a/pyproject.toml b/pyproject.toml index 634bdeb0..50de5f74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,6 +68,7 @@ extras = [ docs = [ "jinja2", "ipywidgets", + "tabulate", ] test = [ diff --git a/scripts/_render_api.py b/scripts/_render_api.py index 1f9224d8..4c07fe38 100644 --- a/scripts/_render_api.py +++ b/scripts/_render_api.py @@ -55,14 +55,20 @@ def sha_256(path_or_str: Path | str) -> str: return hashlib.sha256(str(path_or_str).encode("utf-8")).hexdigest() -def build_table(df: pd.DataFrame, caption=None): +def build_table(df: pd.DataFrame, caption=None, attrs=".striped .float"): """An opinionated function to make a dataframe into html table.""" df = df.drop_duplicates() - template = get_template("table.html") - columns = [x.capitalize() for x in df.columns] - rows = df.to_records(index=False).tolist() - out = template.render(columns=columns, rows=rows, caption=caption) - return out + df.columns = [x.capitalize() for x in df.columns] + out_str = df.to_markdown(tablefmt="github", index=False) + # add metadata, such as label or styling. + if caption is not None or attrs is not None: + meta = "\n: " + if caption: + meta += str(caption) + if attrs: + meta += "{" + attrs + "}" + out_str += meta + return out_str def is_it_subclass(obj, cls): @@ -246,7 +252,7 @@ def style_parameters(self, param_str): for ind_num, ind in enumerate(param_start[:-1]): key = lines[ind].strip() vals = [x.strip() for x in lines[ind + 1 : param_start[ind_num + 1]]] - param_desc.append((key, "\n".join(vals))) + param_desc.append((key, " ".join(vals))) table = pd.DataFrame(param_desc, columns=["Parameter", "Description"]) return build_table(table) diff --git a/scripts/_templates/table.html b/scripts/_templates/table.html deleted file mode 100644 index 4501b3c0..00000000 --- a/scripts/_templates/table.html +++ /dev/null @@ -1,24 +0,0 @@ -{# A template for making tables. #} - - {%- if caption %} - - {%- endif %} - - - {%- for col in columns %} - - {%- endfor %} - - - - {%- for row in rows %} - - {%- for val in row %} - - {%- endfor %} - - {%- endfor %} - -
- {{ caption }} -
{{ col }}
{{ val }}
From a98e851fa4a1b519cc3a2723d07d116ba531b18d Mon Sep 17 00:00:00 2001 From: Derrick Chambers Date: Tue, 2 Jan 2024 21:11:42 -0700 Subject: [PATCH 2/6] Improve docstrings, rework styling --- dascore/core/attrs.py | 19 ++- dascore/core/patch.py | 2 + dascore/core/spool.py | 4 +- dascore/examples.py | 211 ++++++++++++++++++----------- dascore/io/core.py | 53 ++++++++ dascore/units.py | 23 +++- dascore/utils/docs.py | 25 ++++ docs/index.qmd | 2 +- docs/styles.css | 58 ++++---- docs/tutorial/patch.qmd | 25 ++-- readme.md | 2 +- scripts/_render_api.py | 34 +++-- scripts/_templates/table.html | 24 ++++ tests/conftest.py | 2 +- tests/test_core/test_patch.py | 2 +- tests/test_utils/test_doc_utils.py | 21 ++- tests/test_viz/test_wiggle.py | 2 +- 17 files changed, 368 insertions(+), 141 deletions(-) create mode 100644 scripts/_templates/table.html diff --git a/dascore/core/attrs.py b/dascore/core/attrs.py index a8a5f677..43dd2f57 100644 --- a/dascore/core/attrs.py +++ b/dascore/core/attrs.py @@ -77,7 +77,24 @@ def _get_dims(data_dict): class PatchAttrs(DascoreBaseModel): - """The expected attributes for a Patch.""" + """ + The expected attributes for a Patch. + + The default attributes are: + ```{python} + #| echo: false + + import dascore as dc + from IPython.display import Markdown + + df_str = ( + dc.PatchAttrs.get_summary_df() + .reset_index() + .to_markdown(index=False, stralign="center") + ) + Markdown(df_str) + ``` + """ model_config = ConfigDict( title="Patch Summary", diff --git a/dascore/core/patch.py b/dascore/core/patch.py index 272deec0..7ca91ba7 100644 --- a/dascore/core/patch.py +++ b/dascore/core/patch.py @@ -25,6 +25,8 @@ class Patch: """ A Class for managing data and metadata. + See the [patch tutorial](/tutorial/patch.qmd) for examples. + Parameters ---------- data diff --git a/dascore/core/spool.py b/dascore/core/spool.py index cb3fe783..7c87d049 100644 --- a/dascore/core/spool.py +++ b/dascore/core/spool.py @@ -587,9 +587,9 @@ def _load_patch(self, kwargs) -> Self: @singledispatch def spool(obj: Path | str | BaseSpool | Sequence[PatchType], **kwargs) -> BaseSpool: """ - Create a spool from some data source. + Create a spool from a data source. - This function is used to load data from many different sources. + This is the main function for loading in DASCore. Parameters ---------- diff --git a/dascore/examples.py b/dascore/examples.py index a66dca24..c699cb06 100644 --- a/dascore/examples.py +++ b/dascore/examples.py @@ -22,26 +22,53 @@ @register_func(EXAMPLE_PATCHES, key="random_das") -def _random_patch( +def random_patch( *, - starttime="2017-09-18", - start_distance=0, + time_min="2017-09-18", + time_step=to_timedelta64(1 / 250), + time_array=None, + distance_min=0, + distance_step=1, + dist_array=None, network="", station="", tag="random", shape=(300, 2_000), - time_step=to_timedelta64(1 / 250), - distance_step=1, - time_array=None, - dist_array=None, ): - """Generate a random DAS Patch.""" + """ + Generate a random DAS Patch. + + Parameters + ---------- + time_min + The time the patch starts. + time_step + The step between time samples. + time_array + If not None, an array for time coordinate and`time_min` and + `time_step` will not be used. + distance_min + The start of the distance coordinate. + distance_step + The spacing between distance samples. + dist_array + If not None, an array of distance values and `distance_min` and + `distance_step` will not be used. + network + The network code. + station + The station designation. + tag + The patch tag + shape + The shape pf data array. + """ # get input data rand = np.random.RandomState(13) array = rand.random(shape) # create attrs - t1 = np.atleast_1d(np.datetime64(starttime))[0] - d1 = np.atleast_1d(start_distance) + t1 = np.atleast_1d(np.datetime64(time_min))[0] + d1 = np.atleast_1d(distance_min) attrs = dict( distance_step=distance_step, time_step=to_timedelta64(time_step), @@ -81,9 +108,16 @@ def _random_patch( @register_func(EXAMPLE_PATCHES, key="patch_with_null") -def _patch_with_null(): - """Create a patch which has nullish values.""" - patch = _random_patch() +def patch_with_null(**kwargs): + """ + A patch which has nullish values. + + Parameters + ---------- + **kwargs + Parameters passed to [`random_patch`](`dascore.examples.random_patch`). + """ + patch = random_patch(**kwargs) data = np.array(patch.data) data[data > 0.9] = np.NaN # also set the first row and column to NaN @@ -92,15 +126,36 @@ def _patch_with_null(): return patch.new(data=data) +@register_func(EXAMPLE_PATCHES, key="random_patch_with_lat_lon") +def random_patch_lat_lon(**kwargs): + """ + Create a patch with latitude/longitude coords on distance dim. + + Parameters + ---------- + **kwargs + Parameters passed to [`random_patch`](`dascore.examples.random_patch`). + """ + patch = random_patch(**kwargs) + dist = patch.coords.get_array("distance") + lat = np.arange(0, len(dist)) * 0.001 - 109.857952 + lon = np.arange(0, len(dist)) * 0.001 + 41.544654 + # add a single coord + out = patch.update_coords(latitude=("distance", lat), longitude=("distance", lon)) + return out + + @register_func(EXAMPLE_PATCHES, key="wacky_dim_coords_patch") -def _wacky_dim_coord_patch(): - """Creates a patch with one Monotonic and one Array coord.""" +def wacky_dim_coord_patch(): + """ + A patch with one Monotonic and one Array coord. + """ shape = (100, 1_000) # distance is neither monotonic nor evenly sampled. dist_ar = np.random.random(100) + np.arange(100) * 0.3 # time is monotonic, not evenly sampled. time_ar = dc.to_datetime64(np.cumsum(np.random.random(1_000))) - patch = _random_patch(shape=shape, dist_array=dist_ar, time_array=time_ar) + patch = random_patch(shape=shape, dist_array=dist_ar, time_array=time_ar) # check attrs attrs = patch.attrs assert pd.isnull(attrs.coords["time"].step) @@ -109,7 +164,7 @@ def _wacky_dim_coord_patch(): @register_func(EXAMPLE_PATCHES, key="sin_wav") -def _sin_wave_patch( +def sin_wave_patch( sample_rate=44100, frequency: Sequence[float] | float = 100.0, time_min="2020-01-01", @@ -118,16 +173,15 @@ def _sin_wave_patch( amplitude=10, ): """ - Return a Patch composed of simple 1 second sin waves. - - This is useful for debugging output to audio formats. + A Patch composed of sine waves. Parameters ---------- sample_rate The sample rate in Hz. frequency - The frequency of the sin wave. + The frequency of the sin wave. If a sequence is provided multiple + sine waves will be generated at each frequency. time_min The start time in the metadata. channel_count @@ -155,37 +209,19 @@ def _sin_wave_patch( return patch -@register_func(EXAMPLE_PATCHES, key="random_patch_with_lat_lon") -def _random_patch_lat_lon(): - """Create a patch with latitude/longitude coords on distance dim.""" - random_patch = get_example_patch("random_das") - dist = random_patch.coords.get_array("distance") - lat = np.arange(0, len(dist)) * 0.001 - 109.857952 - lon = np.arange(0, len(dist)) * 0.001 + 41.544654 - # add a single coord - out = random_patch.update_coords( - latitude=("distance", lat), longitude=("distance", lon) - ) - return out - - @register_func(EXAMPLE_PATCHES, key="example_event_1") -def _example_event_1(): +def example_event_1(): """ - Returns an example of a passive event recorded by DAS. - - This event is from @stanvek2022fracture. + An induced event recorded on a borehole fiber from @stanvek2022fracture. """ path = fetch("example_dasdae_event_1.h5") return dc.spool(path)[0] @register_func(EXAMPLE_PATCHES, key="example_event_2") -def _example_event_2(): +def example_event_2(): """ - The same as example event 1, but units are set and filtered. - - This event is from @stanvek2022fracture. + [`example_event_1`](`dascore.examples.example_event_1`) with pre-processing. """ path = fetch("example_dasdae_event_1.h5") patch = dc.spool(path)[0].update_attrs(data_type="strain_rate") @@ -202,7 +238,7 @@ def _example_event_2(): @register_func(EXAMPLE_PATCHES, key="ricker_moveout") -def _ricker_moveout( +def ricker_moveout( frequency=15, peak_time=0.25, duration=1.5, @@ -213,7 +249,7 @@ def _ricker_moveout( velocity=100, ): """ - Return a patch which has a ricker wavelet. with moveout. + A patch of a ricker wavelet with some apparent velocity. Parameters ---------- @@ -264,12 +300,19 @@ def _ricker(time, delay): return dc.Patch(data=data, coords=coords, dims=dims) +@register_func(EXAMPLE_PATCHES, key="dispersion_event") +def dispersion_event(): + """ + A synthetic shot record that exhibits dispersion. + """ + path = fetch("dispersion_event.h5") + return dc.spool(path)[0] + + @register_func(EXAMPLE_SPOOLS, key="random_das") -def _random_spool( - time_gap=0, length=3, starttime=np.datetime64("2020-01-03"), **kwargs -): +def random_spool(time_gap=0, length=3, time_min=np.datetime64("2020-01-03"), **kwargs): """ - Generate several random patches in the spool. + Several random patches in the spool. Parameters ---------- @@ -278,43 +321,43 @@ def _random_spool( number to create overlap. length The number of patches to generate. - starttime - The starttime of the first patch. Subsequent patches have startimes - after the endtime of the previous patch, plus the time_gap. + time_min + The start time of the first patch. Subsequent patches have start times + after the end time of the previous patch, plus the time_gap. **kwargs - Passed to the [_random_patch](`dasocre.examples._random_patch`) function. + Passed to the [_random_patch](`dascore.examples.random_patch`) function. """ out = [] for _ in range(length): - patch = _random_patch(starttime=starttime, **kwargs) + patch = random_patch(time_min=time_min, **kwargs) out.append(patch) diff = to_timedelta64(time_gap) + patch.attrs.coords["time"].step - starttime = patch.attrs["time_max"] + diff + time_min = patch.attrs["time_max"] + diff return dc.spool(out) @register_func(EXAMPLE_SPOOLS, key="diverse_das") -def _diverse_spool(): +def diverse_spool(): """ - Create a spool with a diverse set of patches for testing. + A spool with a diverse set of patch metadata for testing. There are various gaps, tags, station names, etc. """ - spool_no_gaps = _random_spool() - spool_no_gaps_different_network = _random_spool(network="das2") - spool_big_gaps = _random_spool(time_gap=np.timedelta64(1, "s"), station="big_gaps") - spool_overlaps = _random_spool( + spool_no_gaps = random_spool() + spool_no_gaps_different_network = random_spool(network="das2") + spool_big_gaps = random_spool(time_gap=np.timedelta64(1, "s"), station="big_gaps") + spool_overlaps = random_spool( time_gap=-np.timedelta64(10, "ms"), station="overlaps" ) time_step = spool_big_gaps[0].attrs.coords["time"].step dt = to_timedelta64(time_step / np.timedelta64(1, "s")) - spool_small_gaps = _random_spool(time_gap=dt, station="smallg") - spool_way_late = _random_spool( - length=1, starttime=np.datetime64("2030-01-01"), station="wayout" + spool_small_gaps = random_spool(time_gap=dt, station="smallg") + spool_way_late = random_spool( + length=1, time_min=np.datetime64("2030-01-01"), station="wayout" ) - spool_new_tag = _random_spool(tag="some_tag", length=1) - spool_way_early = _random_spool( - length=1, starttime=np.datetime64("1989-05-04"), station="wayout" + spool_new_tag = random_spool(tag="some_tag", length=1) + spool_way_early = random_spool( + length=1, time_min=np.datetime64("1989-05-04"), station="wayout" ) all_spools = [ @@ -331,13 +374,6 @@ def _diverse_spool(): return dc.spool([y for x in all_spools for y in x]) -@register_func(EXAMPLE_PATCHES, key="dispersion_event") -def _dispersion_event(): - """Returns an example of a synthetic shot record that exhibits dispersion.""" - path = fetch("dispersion_event.h5") - return dc.spool(path)[0] - - def spool_to_directory(spool, path=None, file_format="DASDAE", extention="hdf5"): """ Write out each patch in a spool to a directory. @@ -366,7 +402,16 @@ def get_example_patch(example_name="random_das", **kwargs) -> dc.Patch: Load an example Patch. Options are: - {examples} + ```{python} + #| echo: false + #| output: asis + from dascore.examples import EXAMPLE_PATCHES + + from dascore.utils.docs import objs_to_doc_df + + df = objs_to_doc_df(EXAMPLE_PATCHES) + print(df.to_markdown(index=False, stralign="center")) + ``` Parameters ---------- @@ -377,7 +422,8 @@ def get_example_patch(example_name="random_das", **kwargs) -> dc.Patch: Raises ------ - UnknownExample if unregistered patch is requested. + (`UnknownExampleError`)['dascore.examples.UnknownExampleError`] if + unregistered patch is requested. """ if example_name not in EXAMPLE_PATCHES: msg = ( @@ -393,8 +439,17 @@ def get_example_spool(example_name="random_das", **kwargs) -> dc.BaseSpool: """ Load an example Spool. - Options are: - {examples} + Supported example spools are: + ```{python} + #| echo: false + #| output: asis + from dascore.examples import EXAMPLE_SPOOLS + + from dascore.utils.docs import objs_to_doc_df + + df = objs_to_doc_df(EXAMPLE_SPOOLS) + print(df.to_markdown(index=False, stralign="center")) + ``` Parameters ---------- diff --git a/dascore/io/core.py b/dascore/io/core.py index 85f2b8ed..11acfd9c 100644 --- a/dascore/io/core.py +++ b/dascore/io/core.py @@ -477,6 +477,9 @@ def read( """ Read a fiber file. + For most cases, [`dascore.spool`](`dascore.spool`) is preferable to + this function. + Parameters ---------- path @@ -492,6 +495,20 @@ def read( An optional tuple of distances. *kwargs All kwargs are passed to the format-specific read functions. + + Notes + ----- + Unlike [`spool`](`dascore.spool`) this function reads the entire file + into memory. + + Examples + -------- + >>> import dascore as dc + >>> from dascore.utils.downloader import fetch + >>> + >>> file_path = fetch("prodml_2.1.h5") + >>> + >>> patch = dc.read(file_path) """ with IOResourceManager(path) as man: if not file_format or not file_version: @@ -539,6 +556,14 @@ def scan_to_df( exclude A sequence of strings to exclude from the analysis. + Examples + -------- + >>> import dascore as dc + >>> from dascore.utils.downloader import fetch + >>> + >>> file_path = fetch("prodml_2.1.h5") + >>> + >>> df = dc.scan_to_df(file_path) """ info = scan( path=path, @@ -587,6 +612,15 @@ def scan( ------- A list of [`PatchAttrs`](`dascore.core.attrs.PatchAttrs`) or subclasses which may have extra fields. + + Examples + -------- + >>> import dascore as dc + >>> from dascore.utils.downloader import fetch + >>> + >>> file_path = fetch("prodml_2.1.h5") + >>> + >>> attr_list = dc.scan(file_path) """ out = [] for patch_source in _iterate_scan_inputs(path): @@ -644,6 +678,14 @@ def get_format( ------ dascore.exceptions.UnknownFiberFormat - Could not determine the fiber format. + Examples + -------- + >>> import dascore as dc + >>> from dascore.utils.downloader import fetch + >>> + >>> file_path = fetch("prodml_2.1.h5") + >>> + >>> file_format, file_version = dc.get_format(file_path) """ with IOResourceManager(path) as man: path = man.source @@ -703,6 +745,17 @@ def write( ------ [`UnkownFiberFormatError`](`dascore.exceptions.UnknownFiberFormatError`) - Could not determine the fiber format. + + Examples + -------- + >>> from pathlib import Path + >>> import dascore as dc + >>> + >>> patch = dc.get_example_patch() + >>> path = Path("output.h5") + >>> dc.write(patch, path, "dasdae") + >>> + >>> path.unlink() """ formatter = FiberIO.manager.get_fiberio(file_format, file_version) if not isinstance(patch_or_spool, dc.BaseSpool): diff --git a/dascore/units.py b/dascore/units.py index fd946bbe..dd34b096 100644 --- a/dascore/units.py +++ b/dascore/units.py @@ -38,7 +38,13 @@ def get_registry(): @cache def get_unit(value) -> Unit: - """Convert a value to a pint unit.""" + """ + Convert a value to a pint unit. + + Usually quantities, generated with + [`get_quantity`](`dascore.units.get_quantity`), are easy to work + with. + """ if isinstance(value, Quantity): assert value.magnitude == 1.0 value = value.units @@ -55,7 +61,20 @@ def _str_to_quant(qunat_str): def get_quantity(value: str_or_none) -> Quantity | None: - """Convert a value to a pint quantity.""" + """ + Convert a value to a pint quantity. + + Parameters + ---------- + value + The value to convert to a quantity. + + Examples + -------- + >>> import dascore as dc + >>> meters = dc.get_quantity("m") + >>> accel = dc.get_quantity("m/s^2") + """ value = unbyte(value) if value is None or value is ... or value == "": return None diff --git a/dascore/utils/docs.py b/dascore/utils/docs.py index 3d900295..74ca2d03 100644 --- a/dascore/utils/docs.py +++ b/dascore/utils/docs.py @@ -1,10 +1,16 @@ """Utilities for documentation.""" from __future__ import annotations +import inspect import textwrap from collections.abc import Sequence +from pathlib import Path from typing import Any +import pandas as pd + +import dascore as dc + def format_dtypes(dtype_dict: dict[str, Any]) -> str: """ @@ -87,3 +93,22 @@ def _wrap(func): return func return _wrap + + +def objs_to_doc_df(doc_dict, cross_reference=True): + """ + Convert a dictionary of documentable entities to a pandas dataframe. + """ + out = {} + base = Path(dc.__file__).parent.parent + for key, obj in doc_dict.items(): + if cross_reference: + path = Path(inspect.getfile(obj)).relative_to(base) + name = obj.__name__ + address = str(path).replace(".py", "").replace("/", ".") + key = f"[`{key}`](`{address + '.' + name}`)" + doc = str(getattr(obj, "__func__", obj).__doc__).strip() + out[key] = doc.splitlines()[0] + df = pd.Series(out).to_frame().reset_index() + df.columns = ["Name", "Description"] + return df diff --git a/docs/index.qmd b/docs/index.qmd index 83b7ba88..ea003ce5 100644 --- a/docs/index.qmd +++ b/docs/index.qmd @@ -27,7 +27,7 @@ DASCore is a foundational package of the [DAS Data Analysis Ecosystem (DASDAE)]( #| output: asis import pandas as pd from dascore.io.core import FiberIO -out_str = FiberIO.get_supported_io_table().replace(True, value='✅').replace(False, value='❌').to_markdown(index=False, stralign=True) +out_str = FiberIO.get_supported_io_table().replace(True, value='✅').replace(False, value='❌').to_markdown(index=False, stralign="center") out_str += '\n: {.striped}' print(out_str) diff --git a/docs/styles.css b/docs/styles.css index 862fd001..f80bd3f1 100644 --- a/docs/styles.css +++ b/docs/styles.css @@ -1,6 +1,9 @@ /* css styles */ -/* Table stuff */ +body { + font-size: 18px; +} + table { border-collapse: collapse; border: 2px solid rgb(200, 200, 200); @@ -12,7 +15,7 @@ table { } caption { - padding: 1px; + padding: 10px; caption-side: top; color: #666; text-align: center; @@ -20,28 +23,33 @@ caption { font-size: small; } -td, th { - border: 1px solid rgb(190, 190, 190); +td { + border: 0.5px solid rgb(190, 190, 190); padding: 10px 20px; + text-align: left; + vertical-align: middle; + /*white-space: pre;*/ } -th { - /*background-color: rgb(235, 235, 235);*/ - text-align: center; +/*Try not to let first column wrap.*/ +td:first-child { + white-space: nowrap; } -td { +th { + border: 0.5px solid rgb(190, 190, 190); + padding: 10px 20px; text-align: center; } -caption { - padding: 10px; -} +/*This makes every other row striped. */ + -/*tbody tr:nth-child(even) {*/ -/* background-color: rgba(210, 209, 209, 0.4);*/ -/*}*/ + +tbody tr:nth-child(odd) { + background-color: rgba(210, 209, 209, 0.2); +} /* Custom classes */ @@ -51,7 +59,7 @@ caption { } .def_block { - background-color: rgba(210, 209, 209, 0.4); + background-color: rgba(210, 209, 209, 0.3); margin: 1em; box-sizing: content-box; padding-left: 10px; @@ -61,13 +69,13 @@ caption { padding-top: 2px; } -/*.origin_table {*/ -/* border: 0em;*/ -/* width: 100%;*/ -/* margin: 0;*/ -/* font-size: 1em;*/ -/* border-style: hidden !important;*/ -/* font-family: 'Bree Serif', serif;*/ -/* display: block;*/ -/* margin-bottom: 10px;*/ -/*}*/ +.origin_table { + border: 0em; + width: 100%; + margin: 0; + font-size: 1em; + border-style: hidden !important; + font-family: 'Bree Serif', serif; + display: block; + margin-bottom: 10px; +} diff --git a/docs/tutorial/patch.qmd b/docs/tutorial/patch.qmd index 3b8dd760..6e502abd 100644 --- a/docs/tutorial/patch.qmd +++ b/docs/tutorial/patch.qmd @@ -26,15 +26,7 @@ pa1 = dc.get_example_patch("random_das") pa2 = dc.get_example_patch("example_event_1") ``` -Other supported example patches are: -```{python} -#| code-fold: true -from pprint import pprint - -import dascore as dc - -pprint(sorted(dc.examples.EXAMPLE_PATCHES)) -``` +See [`get_example_patch`](`dascore.examples.get_example_patch`) for supported patches. ## Load a file @@ -166,15 +158,24 @@ Most of the other `CoordManager` features are primarily used internally by DASCo ## Attrs -The metadata stored in [`Patch.attrs`](`dascore.core.attrs.PatchAttrs`) is a [pydantic model](https://docs.pydantic.dev/usage/models/) which enforces a schema and provides validation. `PatchAttrs.get_summary_df` generates a table of the attribute descriptions: +The metadata stored in [`Patch.attrs`](`dascore.core.attrs.PatchAttrs`) is a [pydantic model](https://docs.pydantic.dev/usage/models/) which enforces a schema and provides validation. [`PatchAttrs.get_summary_df`](`dascore.utils.models.DascoreBaseModel.get_summary_df`) generates a table of the attribute descriptions: + ```{python} -#| code-fold: true +#| echo: false + import dascore as dc +from IPython.display import Markdown -dc.PatchAttrs.get_summary_df() +df_str = ( + dc.PatchAttrs.get_summary_df() + .reset_index() + .to_markdown(index=False, stralign="center") +) +Markdown(df_str) ``` + Specific data formats may also add attributes (e.g. "gauge_length", "pulse_width"), but this depends on the parser. ## String representation diff --git a/readme.md b/readme.md index 83184a1f..e5097806 100644 --- a/readme.md +++ b/readme.md @@ -2,8 +2,8 @@ A python library for distributed fiber optic sensing. -[![PyPI Version](https://img.shields.io/pypi/v/dascore.svg)](https://pypi.python.org/pypi/dascore) [![coverage](https://codecov.io/gh/dasdae/dascore/branch/master/graph/badge.svg)](https://codecov.io/gh/dasdae/dascore) +[![PyPI Version](https://img.shields.io/pypi/v/dascore.svg)](https://pypi.python.org/pypi/dascore) [![supported versions](https://img.shields.io/pypi/pyversions/dascore.svg?label=python_versions)](https://pypi.python.org/pypi/dascore) [![PyPI Downloads](https://img.shields.io/pypi/dm/dascore.svg?label=pypi)](https://pypi.org/project/dascore/) [![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/dascore.svg?label=conda)](https://github.com/conda-forge/dascore-feedstock) diff --git a/scripts/_render_api.py b/scripts/_render_api.py index 4c07fe38..80559c23 100644 --- a/scripts/_render_api.py +++ b/scripts/_render_api.py @@ -55,20 +55,14 @@ def sha_256(path_or_str: Path | str) -> str: return hashlib.sha256(str(path_or_str).encode("utf-8")).hexdigest() -def build_table(df: pd.DataFrame, caption=None, attrs=".striped .float"): +def build_table(df: pd.DataFrame, caption=None): """An opinionated function to make a dataframe into html table.""" df = df.drop_duplicates() - df.columns = [x.capitalize() for x in df.columns] - out_str = df.to_markdown(tablefmt="github", index=False) - # add metadata, such as label or styling. - if caption is not None or attrs is not None: - meta = "\n: " - if caption: - meta += str(caption) - if attrs: - meta += "{" + attrs + "}" - out_str += meta - return out_str + template = get_template("table.html") + columns = [x.capitalize() for x in df.columns] + rows = df.to_records(index=False).tolist() + out = template.render(columns=columns, rows=rows, caption=caption) + return out def is_it_subclass(obj, cls): @@ -242,7 +236,9 @@ def parse_sections(self, docstr): return out def style_parameters(self, param_str): - """Style the parameters block.""" + """ + Style the parameters block. + """ lines = param_str.split("\n") # parameters dont have spaces at the start param_start = [num for num, x in enumerate(lines) if not x.startswith(" ")] @@ -251,8 +247,16 @@ def style_parameters(self, param_str): param_desc = [] for ind_num, ind in enumerate(param_start[:-1]): key = lines[ind].strip() - vals = [x.strip() for x in lines[ind + 1 : param_start[ind_num + 1]]] - param_desc.append((key, " ".join(vals))) + # get the number of indents (usually 4) + in_char = (len(lines[1]) - len(lines[1].lstrip())) * " " + desc_lines = lines[ind + 1 : param_start[ind_num + 1]] + vals = [ + # strip out the first indentation line + (x[len(in_char) :] if x.startswith(in_char) and in_char else x) + for x in desc_lines + ] + # breakpoint() + param_desc.append((key, "
".join(vals))) table = pd.DataFrame(param_desc, columns=["Parameter", "Description"]) return build_table(table) diff --git a/scripts/_templates/table.html b/scripts/_templates/table.html new file mode 100644 index 00000000..555a3a3e --- /dev/null +++ b/scripts/_templates/table.html @@ -0,0 +1,24 @@ +{# A template for making tables. #} + + {%- if caption %} + + {%- endif %} + + + {%- for col in columns %} + + {%- endfor %} + + + + {%- for row in rows %} + + {%- for val in row %} + + {%- endfor %} + + {%- endfor %} + +
+ {{ caption }} +
{{ col }}
{{ val }}
diff --git a/tests/conftest.py b/tests/conftest.py index 83a715c8..f2300fa2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -368,7 +368,7 @@ def one_file_directory_spool(one_file_dir): @register_func(SPOOL_FIXTURES) def diverse_spool(): """Create a spool with a diverse set of patches for testing.""" - return ex._diverse_spool() + return ex.diverse_spool() @pytest.fixture(scope="class") diff --git a/tests/test_core/test_patch.py b/tests/test_core/test_patch.py index ba5a079b..0f5f2259 100644 --- a/tests/test_core/test_patch.py +++ b/tests/test_core/test_patch.py @@ -174,7 +174,7 @@ def test_coords_from_1_element_array(self): def test_sin_wave_patch(self): """Ensure the sin wave patch is consistent with its coord dims.""" # For some reason this combination can make coords with wrong shape. - patch = dc.examples._sin_wave_patch( + patch = dc.examples.sin_wave_patch( sample_rate=1000, frequency=[200, 10], channel_count=2, diff --git a/tests/test_utils/test_doc_utils.py b/tests/test_utils/test_doc_utils.py index 51609764..9295795a 100644 --- a/tests/test_utils/test_doc_utils.py +++ b/tests/test_utils/test_doc_utils.py @@ -3,8 +3,11 @@ import textwrap +import pandas as pd + from dascore.core.attrs import PatchAttrs -from dascore.utils.docs import compose_docstring, format_dtypes +from dascore.examples import EXAMPLE_PATCHES +from dascore.utils.docs import compose_docstring, format_dtypes, objs_to_doc_df class TestFormatDtypes: @@ -66,3 +69,19 @@ def dummy_func(): white_space_counts = [self.count_white_space(x) for x in list_lines] # all whitespace counts should be the same for the list lines. assert len(set(white_space_counts)) == 1 + + +class TestObjToDocDF: + """Tests for generating documentation dataframes.""" + + def test_examples_cross_ref(self): + """Tests for documenting examples with cross references.""" + df = objs_to_doc_df(EXAMPLE_PATCHES, cross_reference=True) + assert "(`dascore.examples" in df["Name"].iloc[0] + assert isinstance(df, pd.DataFrame) + + def test_example_no_cross_ref(self): + """Tests for documenting examples without cross references.""" + df = objs_to_doc_df(EXAMPLE_PATCHES, cross_reference=False) + assert "(`dascore.examples" not in df["Name"].iloc[0] + assert isinstance(df, pd.DataFrame) diff --git a/tests/test_viz/test_wiggle.py b/tests/test_viz/test_wiggle.py index 97b76f15..0b61f2a4 100644 --- a/tests/test_viz/test_wiggle.py +++ b/tests/test_viz/test_wiggle.py @@ -19,7 +19,7 @@ def small_patch(self, random_patch): def test_example(self): """Test the example from the docs.""" - patch = dc.examples._sin_wave_patch( + patch = dc.examples.sin_wave_patch( sample_rate=1000, frequency=[200, 10], channel_count=2, From b12e67d9d254d9aee55ad94defd42a62aae3e1af Mon Sep 17 00:00:00 2001 From: Derrick Chambers Date: Tue, 2 Jan 2024 21:18:24 -0700 Subject: [PATCH 3/6] fix failing test --- tests/conftest.py | 2 +- tests/test_io/test_dasdae/test_dasdae.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index f2300fa2..6e5f68f3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -406,7 +406,7 @@ def memory_spool_dim_1_patches(): time_step=0.999767552, shape=(100, 1), length=10, - starttime="2023-06-13T15:38:00.49953408", + time_min="2023-06-13T15:38:00.49953408", ) return spool diff --git a/tests/test_io/test_dasdae/test_dasdae.py b/tests/test_io/test_dasdae/test_dasdae.py index 48211006..3f6d82da 100644 --- a/tests/test_io/test_dasdae/test_dasdae.py +++ b/tests/test_io/test_dasdae/test_dasdae.py @@ -214,7 +214,7 @@ def test_roundtrip_dim_1_patch(self, tmp_path_factory, random_patch): "random_das", time_step=0.999767552, shape=(100, 1), - starttime="2023-06-13T15:38:00.49953408", + time_min="2023-06-13T15:38:00.49953408", ) patch.io.write(path, "dasdae") formatter = DASDAEV1() From e8a69d9297167c17a52d1497de0bf6cdf5ff3227 Mon Sep 17 00:00:00 2001 From: Derrick Chambers Date: Thu, 4 Jan 2024 12:40:58 -0700 Subject: [PATCH 4/6] fix write example --- dascore/io/core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dascore/io/core.py b/dascore/io/core.py index 11acfd9c..30e9468b 100644 --- a/dascore/io/core.py +++ b/dascore/io/core.py @@ -753,8 +753,9 @@ def write( >>> >>> patch = dc.get_example_patch() >>> path = Path("output.h5") - >>> dc.write(patch, path, "dasdae") + >>> _ = dc.write(patch, path, "dasdae") >>> + >>> assert path.exists() >>> path.unlink() """ formatter = FiberIO.manager.get_fiberio(file_format, file_version) From 75f652896b1e7114a5bacbfb81c2ea695060b369 Mon Sep 17 00:00:00 2001 From: derrick chambers Date: Thu, 4 Jan 2024 13:30:19 -0700 Subject: [PATCH 5/6] fix windows sep char --- dascore/utils/docs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dascore/utils/docs.py b/dascore/utils/docs.py index 74ca2d03..f1f05d10 100644 --- a/dascore/utils/docs.py +++ b/dascore/utils/docs.py @@ -2,6 +2,7 @@ from __future__ import annotations import inspect +import os import textwrap from collections.abc import Sequence from pathlib import Path @@ -105,7 +106,7 @@ def objs_to_doc_df(doc_dict, cross_reference=True): if cross_reference: path = Path(inspect.getfile(obj)).relative_to(base) name = obj.__name__ - address = str(path).replace(".py", "").replace("/", ".") + address = str(path).replace(".py", "").replace(os.sep, ".") key = f"[`{key}`](`{address + '.' + name}`)" doc = str(getattr(obj, "__func__", obj).__doc__).strip() out[key] = doc.splitlines()[0] From 51a411e4b6f33743c5e393bbe729840fe01c9ff5 Mon Sep 17 00:00:00 2001 From: derrick chambers Date: Thu, 4 Jan 2024 15:26:00 -0700 Subject: [PATCH 6/6] rename patch.new to patch.update --- dascore/core/attrs.py | 10 +++-- dascore/core/patch.py | 4 +- dascore/proc/basic.py | 2 +- dascore/proc/rolling.py | 4 +- dascore/transform/strain.py | 2 +- dascore/utils/time.py | 45 ++++++++++++++++++---- docs/styles.css | 5 +-- docs/tutorial/patch.qmd | 13 +++---- tests/test_core/test_patch.py | 6 +-- tests/test_core/test_spool.py | 2 +- tests/test_proc/test_basic.py | 2 +- tests/test_proc/test_correlate.py | 2 +- tests/test_proc/test_proc_coords.py | 2 +- tests/test_proc/test_taper.py | 2 +- tests/test_transform/test_differentiate.py | 2 +- tests/test_viz/test_waterfall.py | 2 +- 16 files changed, 67 insertions(+), 38 deletions(-) diff --git a/dascore/core/attrs.py b/dascore/core/attrs.py index 43dd2f57..0625e0f2 100644 --- a/dascore/core/attrs.py +++ b/dascore/core/attrs.py @@ -456,10 +456,12 @@ def merge_compatible_coords_attrs( Merge the coordinates and attributes of patches or raise if incompatible. The rules for compatibility are: - - All attrs must be equal other than history. - - Patches must share the same dimensions, in the same order - - All dimensional coordinates must be strictly equal - - If patches share a non-dimensional coordinate they must be equal. + + - All attrs must be equal other than history. + - Patches must share the same dimensions, in the same order + - All dimensional coordinates must be strictly equal + - If patches share a non-dimensional coordinate they must be equal. + Any coordinates or attributes contained by a single patch will be included in the output. diff --git a/dascore/core/patch.py b/dascore/core/patch.py index 7ca91ba7..54b9a3ef 100644 --- a/dascore/core/patch.py +++ b/dascore/core/patch.py @@ -175,7 +175,9 @@ def size(self) -> tuple[int, ...]: # --- basic patch functionality. - new = dascore.proc.new + update = dascore.proc.update + # Before 0.1.0 update was called new, this is for backwards compatibility. + new = dascore.proc.update equals = dascore.proc.equals update_attrs = dascore.proc.update_attrs assert_has_coords = dascore.proc.assert_has_coords diff --git a/dascore/proc/basic.py b/dascore/proc/basic.py index ace782ef..2c1c68cd 100644 --- a/dascore/proc/basic.py +++ b/dascore/proc/basic.py @@ -139,7 +139,7 @@ def equals(self: PatchType, other: Any, only_required_attrs=True) -> bool: return np.equal(self.data, other.data).all() -def new( +def update( self: PatchType, data: ArrayLike | np.ndarray | None = None, coords: None | dict[str | Sequence[str], ArrayLike] | CoordManager = None, diff --git a/dascore/proc/rolling.py b/dascore/proc/rolling.py index 39eaed39..2063935f 100644 --- a/dascore/proc/rolling.py +++ b/dascore/proc/rolling.py @@ -112,7 +112,7 @@ def apply(self, function): out = self._pad_roll_array(raw) new_coords = self.get_coords() attrs = self._get_attrs_with_apply_history(function) - return self.patch.new(data=out, coords=new_coords, attrs=attrs) + return self.patch.update(data=out, coords=new_coords, attrs=attrs) def mean(self): """Apply mean to moving window.""" @@ -168,7 +168,7 @@ def _repack_patch(self, df, attrs=None): if len(data.shape) != len(self.patch.data.shape): data = np.squeeze(data) coords = self.get_coords() - return self.patch.new(data=data, coords=coords, attrs=attrs) + return self.patch.update(data=data, coords=coords, attrs=attrs) def _call_rolling_func(self, name, *args, **kwargs): """Helper function for calling a rolling function.""" diff --git a/dascore/transform/strain.py b/dascore/transform/strain.py index a9afeb57..92c84e6d 100644 --- a/dascore/transform/strain.py +++ b/dascore/transform/strain.py @@ -39,4 +39,4 @@ def velocity_to_strain_rate( new_attrs = patch.attrs.update( data_type="strain_rate", gauge_length=step * gauge_multiple ) - return patch.new(attrs=new_attrs) + return patch.update(attrs=new_attrs) diff --git a/dascore/utils/time.py b/dascore/utils/time.py index b2196c4b..48754c22 100644 --- a/dascore/utils/time.py +++ b/dascore/utils/time.py @@ -24,13 +24,28 @@ def to_datetime64(obj: timeable_types | np.ndarray): This function accepts a wide range of inputs and returns something of the same shape, but converted to numpy's datetime64 representation. + Parameters + ---------- + obj + An object to convert to a datetime64. If a string is passed, it + should conform to [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601). + Floats and integers are interpreted as seconds from Jan 1st, 1970. + Arrays and Series of floats or strings are also supported. + Examples -------- - >>> # Convert an [iso 8601](https://en.wikipedia.org/wiki/ISO_8601) string + >>> import numpy as np >>> import dascore as dc - >>> time = dc.to_datetime64('2017-09-17T12:11:01.23212') - >>> # Convert a timestamp (float) - >>> dt = dc.to_datetime64(631152000.0) + >>> + >>> # Convert an iso 8601 string to datetime64 + >>> dt_1 = dc.to_datetime64('2017-09-17T12:11:01.23212') + >>> + >>> # Convert a time stamp (seconds from 1970) to datetime64 + >>> dt_2 = dc.to_datetime64(631152000.0) + >>> + >>> # Convert an array of time stamps to datetime64 + >>> timestamp_array = np.random.uniform(1704400000, 1704900000) + >>> dt_array = dc.to_datetime64(timestamp_array) """ if pd.isnull(obj): return np.datetime64("NaT") @@ -124,13 +139,27 @@ def to_timedelta64(obj: float | np.ndarray | str | timedelta): This function accepts a wide range of inputs and returns something of the same shape, but converted to numpy's timedelta64 representation. + Parameters + ---------- + obj + An object to convert to timedelta64. Can be a float, str or arary of + such. Floats are interpreted as seconds and strings must conform to + the output style of timedeltas (e.g. str(time_delta)). + Examples -------- - >>> # Convert a float to seconds >>> import dascore as dc - >>> d_time_1 = dc.to_timedelta64(10.1232) - >>> # also works on negative numbers - >>> d_time_2 = dc.to_datetime64(-10.5) + >>> + >>> # Convert a float to timedelta64 representing seconds. + >>> td_1 = dc.to_timedelta64(10.1232) + >>> + >>> # This also works on negative numbers. + >>> td_2 = dc.to_datetime64(-10.5) + >>> + >>> # Convert a string to timedelta64 + >>> td_str = "1000000000 nanoseconds" + >>> td_3 = dc.to_timedelta64(td_str) + """ if pd.isnull(obj): return np.timedelta64("NaT") diff --git a/docs/styles.css b/docs/styles.css index f80bd3f1..a26f8cde 100644 --- a/docs/styles.css +++ b/docs/styles.css @@ -44,11 +44,8 @@ th { /*This makes every other row striped. */ - - - tbody tr:nth-child(odd) { - background-color: rgba(210, 209, 209, 0.2); + background-color: rgba(210, 209, 209, 0.35); } diff --git a/docs/tutorial/patch.qmd b/docs/tutorial/patch.qmd index 6e502abd..656865c9 100644 --- a/docs/tutorial/patch.qmd +++ b/docs/tutorial/patch.qmd @@ -282,24 +282,24 @@ patch.viz.waterfall(show=True, scale=0.2); Because patches should be treated as immutable objects, they can't be modified with normal attribute assignment. However, DASCore provides several methods that return new patches with modifications. -## New +## Update -[`Patch.new`](`dascore.core.patch.Patch.new`) uses the Patch as a template and returns a new Patch with one or more aspects modified. +[`Patch.update`](`dascore.core.patch.Patch.update`) uses the `Patch` instances as a template and returns a new `Patch` instances with one or more aspects modified. ```{python} import dascore as dc pa = dc.get_example_patch() # Create a copy of patch with new data but coords and attrs stay the same. -new_data_patch = pa.new(data=pa.data * 10) +new_data_patch = pa.update(data=pa.data * 10) # Completely replace the attributes. -new_data_patch = pa.new(attrs=dict(station="TMU")) +new_data_patch = pa.update(attrs=dict(station="TMU")) ``` ## Update attrs -[`Patch.update_attrs`](`dascore.core.patch.Patch.update_attrs`) is for making changes to the attrs (metadata) while keeping the unaffected metadata (`Patch.new` completely replaces the old attrs). +[`Patch.update_attrs`](`dascore.core.patch.Patch.update_attrs`) is for making changes to the attrs (metadata) while keeping the unaffected metadata (`Patch.update` would completely replace the old attrs). ```{python} import dascore as dc @@ -351,7 +351,6 @@ new_time = pa.coords.min('time') + one_second new = pa.update_coords(time_min=new_time) ``` - ### Adding coordinates Commonly, additional coordinates, such as latitude/longitude, are attached to a particular dimension such as distance. It is also possible to include coordinates that are not associated with any dimensions. @@ -406,7 +405,7 @@ patch_detached_lat = patch.update_coords(latitude=(None, lat)) ## Dropping coordinates -Non-dimensional coordinates can be dropped using `Patch.drop_coords`. Dimensional coordinates, however, cannot be dropped doing so would force the patch data to become degenerate. +Non-dimensional coordinates can be dropped using [`Patch.drop_coords`](`dascore.proc.coords.drop_coords`). Dimensional coordinates, however, cannot be dropped doing so would force the patch data to become degenerate. ```{python} import dascore as dc diff --git a/tests/test_core/test_patch.py b/tests/test_core/test_patch.py index 0f5f2259..3a3971f6 100644 --- a/tests/test_core/test_patch.py +++ b/tests/test_core/test_patch.py @@ -218,7 +218,7 @@ def test_erase_history(self, random_patch): assert patch.attrs.history new_attrs = dict(patch.attrs) new_attrs["history"] = [] - new_patch = patch.new(attrs=new_attrs) + new_patch = patch.update(attrs=new_attrs) assert not new_patch.attrs.history def test_new_coord_dict_order(self, random_patch): @@ -238,7 +238,7 @@ def test_new_coord_dict_order(self, random_patch): def test_attrs_preserved_when_not_specified(self, random_patch): """If attrs is not passed to new, old attrs should remain.""" pa = random_patch.update_attrs(network="bob", tag="2", station="10") - new_1 = pa.new(data=pa.data * 10) + new_1 = pa.update(data=pa.data * 10) assert new_1.attrs == pa.attrs def test_new_dims_renames_dims(self, random_patch): @@ -368,7 +368,7 @@ def test_one_coord_not_equal(self, wacky_dim_patch): coord_array[20:30] *= 0.9 assert not np.allclose(coord_array, coords.get_array("distance")) new_patch = patch.update_coords(distance=coord_array) - new = patch.new(coords=new_patch.coords) + new = patch.update(coords=new_patch.coords) assert new != patch def test_other_types(self, random_patch): diff --git a/tests/test_core/test_spool.py b/tests/test_core/test_spool.py index f6d0701e..71814faf 100644 --- a/tests/test_core/test_spool.py +++ b/tests/test_core/test_spool.py @@ -484,7 +484,7 @@ def test_changed_memory_spool(self, random_patch): # create new patch with cleared history new_attrs = dict(patch.attrs) new_attrs["history"] = [] - new_patch = patch.new(attrs=new_attrs) + new_patch = patch.update(attrs=new_attrs) assert not new_patch.attrs.history # add new patch (w/ no history) to spool, get first patch out. spool = dc.spool([new_patch]) diff --git a/tests/test_proc/test_basic.py b/tests/test_proc/test_basic.py index 4e893b15..fcc9c753 100644 --- a/tests/test_proc/test_basic.py +++ b/tests/test_proc/test_basic.py @@ -331,7 +331,7 @@ def patch_3d_with_null(self, range_patch_3d): nans = [(1, 1, 1), (0, 9, 9)] for nan_ind in nans: data[nan_ind] = np.NaN - patch = range_patch_3d.new(data=data) + patch = range_patch_3d.update(data=data) return patch def test_drop_time_any(self, patch_with_null): diff --git a/tests/test_proc/test_correlate.py b/tests/test_proc/test_correlate.py index 23ab2438..b8adec8a 100644 --- a/tests/test_proc/test_correlate.py +++ b/tests/test_proc/test_correlate.py @@ -27,7 +27,7 @@ def corr_patch(self): time_axis = patch.dims.index("time") data = patch.data norm = np.linalg.norm(data, axis=time_axis, keepdims=True) - return patch.new(data=data / norm) + return patch.update(data=data / norm) @pytest.fixture(scope="session") def ricker_moveout_patch(self): diff --git a/tests/test_proc/test_proc_coords.py b/tests/test_proc/test_proc_coords.py index b73d1406..80cb62b3 100644 --- a/tests/test_proc/test_proc_coords.py +++ b/tests/test_proc/test_proc_coords.py @@ -42,7 +42,7 @@ def test_data_sorted_correctly(self, wacky_dim_patch): for dim, array, ind in zip(dims, array_list, (1, 0)): coord = patch.coords.coord_map[dim] arg_sort = np.argsort(coord.values) - new = patch.new(data=array).sort_coords(dim) + new = patch.update(data=array).sort_coords(dim) data_along_slice = np.take(new.data, 0, ind) assert np.all(np.equal(arg_sort, data_along_slice)) diff --git a/tests/test_proc/test_taper.py b/tests/test_proc/test_taper.py index 2ff50382..fbc915f5 100644 --- a/tests/test_proc/test_taper.py +++ b/tests/test_proc/test_taper.py @@ -22,7 +22,7 @@ def patch_ones(random_patch): def time_tapered_patch(request, patch_ones): """Return a tapered trace.""" # first get a patch with all ones for easy testing - patch = patch_ones.new(data=np.ones_like(patch_ones.data)) + patch = patch_ones.update(data=np.ones_like(patch_ones.data)) out = taper(patch, time=0.05, window_type=request.param) return out diff --git a/tests/test_transform/test_differentiate.py b/tests/test_transform/test_differentiate.py index a64a9e8a..50b9f7d1 100644 --- a/tests/test_transform/test_differentiate.py +++ b/tests/test_transform/test_differentiate.py @@ -74,7 +74,7 @@ def test_all_axis(self, random_patch): def test_uneven_spacing(self, wacky_dim_patch): """Ensure we can diff over uneven dimensions.""" - patch = wacky_dim_patch.new(data=np.ones_like(wacky_dim_patch.data)) + patch = wacky_dim_patch.update(data=np.ones_like(wacky_dim_patch.data)) out = differentiate(patch, "time") # very occasionally, numpy outputs a few nan values from grad when # coordinate spacing is provided. I am still trying to figure out diff --git a/tests/test_viz/test_waterfall.py b/tests/test_viz/test_waterfall.py index d00514ca..b0c2508e 100644 --- a/tests/test_viz/test_waterfall.py +++ b/tests/test_viz/test_waterfall.py @@ -40,7 +40,7 @@ def patch_random_start(event_patch_1): coords["time"] = time + random_starttime attrs["time_min"] = coords["time"].min() attrs["time_max"] = coords["time"].max() - patch = event_patch_1.new(attrs=attrs, coords=coords) + patch = event_patch_1.update(attrs=attrs, coords=coords) return patch