generated from ni/github-repo-template
-
Notifications
You must be signed in to change notification settings - Fork 2
Create an XYData class #204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 4 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
14f7e89
Create an XYData class
mjohanse-emr a82a7c0
Add more unit tests for corner cases.
mjohanse-emr 22c5bce
Update intro.inc and the RTD link in README.
mjohanse-emr 52d8065
Refactors and fixes based on review feedback.
mjohanse-emr a2eab62
Re-implement using NumPy. Remove append functionality.
mjohanse-emr 23eed27
Remove/make constants private.
mjohanse-emr 4266e9a
Add a few more unit tests.
mjohanse-emr 81d6ec1
Fix doctests.
mjohanse-emr 221c233
Fix repr tests on oldest python/numpy.
mjohanse-emr 83d39c2
Fix linting issue.
mjohanse-emr bb7d850
Include non-units properties in __repr__().
mjohanse-emr 96b1790
Fix linting issues.
mjohanse-emr 48b0c85
Update stale docs. Remove old spectrum references.
mjohanse-emr d796f7b
Make TypeVars public
mjohanse-emr beea7c8
Rename x_values to x_data and y_values to y_data.
mjohanse-emr 15ccb5c
Remove x_units and y_units from reduce()
mjohanse-emr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,237 @@ | ||
| """XYData type for NI Python APIs. | ||
|
|
||
| XYData Data Type | ||
| ================= | ||
| :class:`XYData`: An XYData object represents two axes (sequences) of numeric values with units | ||
| information. Valid types for the numeric values are :any:`int` and :any:`float`. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from collections.abc import Iterable, MutableSequence | ||
| from typing import TYPE_CHECKING, Any, Generic, Union | ||
|
|
||
| from typing_extensions import TypeVar, final | ||
|
|
||
| from nitypes._exceptions import invalid_arg_type | ||
|
|
||
| if TYPE_CHECKING: | ||
| # Import from the public package so the docs don't reference private submodules. | ||
| from nitypes.waveform import ExtendedPropertyDictionary | ||
| else: | ||
| from nitypes.waveform._extended_properties import ExtendedPropertyDictionary | ||
|
|
||
| # Extended property keys for X and Y units. | ||
| UNIT_DESCRIPTION_X = "NI_UnitDescription_x" | ||
| UNIT_DESCRIPTION_Y = "NI_UnitDescription_y" | ||
mjohanse-emr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
mjohanse-emr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| # Constants for indexing into the underlying list of lists | ||
| X_INDEX = 0 | ||
| Y_INDEX = 1 | ||
mjohanse-emr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| TNumeric = TypeVar("TNumeric", bound=Union[int, float]) | ||
|
|
||
|
|
||
| @final | ||
| class XYData(Generic[TNumeric]): | ||
| """Two axes (sequences) of numeric values with units information. | ||
|
|
||
| Constructing | ||
| ^^^^^^^^^^^^ | ||
|
|
||
| To construct an XYData object, use the :class:`XYData` class: | ||
|
|
||
| >>> XYData([1.0, 2.0, 3.0], [4.0, 5.0, 6.0]) | ||
| nitypes.xy_data.XYData(x_data=[1.0, 2.0, 3.0], y_data=[4.0, 5.0, 6.0], x_units='', y_units='') | ||
| >>> XYData([1, 2, 3], [4, 5, 6], "A", "V") | ||
| nitypes.xy_data.XYData(x_data=[1, 2, 3], y_data=[4, 5, 6], x_units='A', y_units='V') | ||
| """ | ||
|
|
||
| __slots__ = [ | ||
| "_values", | ||
| "_value_type", | ||
| "_extended_properties", | ||
| ] | ||
|
|
||
| _values: list[list[TNumeric]] | ||
| _value_type: type[TNumeric] | ||
| _extended_properties: ExtendedPropertyDictionary | ||
|
|
||
| def __init__( | ||
| self, | ||
| x_values: Iterable[TNumeric], | ||
| y_values: Iterable[TNumeric], | ||
| x_units: str = "", | ||
| y_units: str = "", | ||
| *, | ||
| value_type: type[TNumeric] | None = None, | ||
| ) -> None: | ||
mjohanse-emr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| """Initialize a new XYData. | ||
|
|
||
| Args: | ||
| x_values: The numeric values to store in first dimension of this object. | ||
| y_values: The numeric values to store in second dimension of this object. | ||
| x_units: The units string associated with x_values. | ||
| y_units: The units string associated with y_values. | ||
| value_type: The type of values that will be added to this XYData. | ||
| This parameter should only be used when creating an XYData with | ||
| empty Iterables. | ||
|
|
||
| Returns: | ||
| An XYData object. | ||
| """ | ||
| x_list = list(x_values) | ||
| y_list = list(y_values) | ||
mjohanse-emr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| # Determine _value_type | ||
| if not x_list or not y_list: | ||
| if not value_type: | ||
| raise TypeError( | ||
| "You must specify x_values and y_values as non-empty or specify value_type." | ||
| ) | ||
| self._value_type = value_type | ||
| else: | ||
| # Use the first x value to determine _value_type. | ||
| self._value_type = type(x_list[0]) | ||
|
|
||
| # Validate the values inputs | ||
| if len(x_list) != len(y_list): | ||
| raise ValueError("x_values and y_values must be the same length.") | ||
| self._validate_axis_data(x_list) | ||
| self._validate_axis_data(y_list) | ||
|
|
||
| if not isinstance(x_units, str): | ||
| raise invalid_arg_type("x_units", "str", x_units) | ||
|
|
||
| if not isinstance(y_units, str): | ||
| raise invalid_arg_type("y_units", "str", y_units) | ||
|
|
||
| self._values = [x_list, y_list] | ||
|
|
||
| self._extended_properties = ExtendedPropertyDictionary() | ||
| self._extended_properties[UNIT_DESCRIPTION_X] = x_units | ||
| self._extended_properties[UNIT_DESCRIPTION_Y] = y_units | ||
|
|
||
| @property | ||
| def x_data(self) -> MutableSequence[TNumeric]: | ||
| """The x-axis data of this XYData.""" | ||
| return self._values[X_INDEX] | ||
|
|
||
| @property | ||
| def y_data(self) -> MutableSequence[TNumeric]: | ||
| """The y-axis data of this XYData.""" | ||
| return self._values[Y_INDEX] | ||
|
|
||
| @property | ||
| def x_units(self) -> str: | ||
| """The unit of measurement, such as volts, of x_data.""" | ||
| value = self._extended_properties.get(UNIT_DESCRIPTION_X, "") | ||
| assert isinstance(value, str) | ||
| return value | ||
|
|
||
| @x_units.setter | ||
| def x_units(self, value: str) -> None: | ||
| if not isinstance(value, str): | ||
| raise invalid_arg_type("x_units", "str", value) | ||
| self._extended_properties[UNIT_DESCRIPTION_X] = value | ||
|
|
||
| @property | ||
| def y_units(self) -> str: | ||
| """The unit of measurement, such as volts, of y_data.""" | ||
| value = self._extended_properties.get(UNIT_DESCRIPTION_Y, "") | ||
| assert isinstance(value, str) | ||
| return value | ||
|
|
||
| @y_units.setter | ||
| def y_units(self, value: str) -> None: | ||
| if not isinstance(value, str): | ||
| raise invalid_arg_type("y_units", "str", value) | ||
| self._extended_properties[UNIT_DESCRIPTION_Y] = value | ||
|
|
||
| @property | ||
| def extended_properties(self) -> ExtendedPropertyDictionary: | ||
| """The extended properties for the XYData. | ||
|
|
||
| .. note:: | ||
| Data stored in the extended properties dictionary may not be encrypted when you send it | ||
| over the network or write it to a TDMS file. | ||
| """ | ||
| return self._extended_properties | ||
|
|
||
| def append(self, x_value: TNumeric, y_value: TNumeric) -> None: | ||
| """Append an x and y value pair to this XYData.""" | ||
| self._validate_axis_data([x_value]) | ||
| self._validate_axis_data([y_value]) | ||
| self.x_data.append(x_value) | ||
| self.y_data.append(y_value) | ||
|
|
||
| def extend( | ||
| self, x_values: MutableSequence[TNumeric], y_values: MutableSequence[TNumeric] | ||
| ) -> None: | ||
| """Extend x_data and y_data with the input sequences.""" | ||
| if len(x_values) != len(y_values): | ||
| raise ValueError("X and Y sequences to extend must be the same length.") | ||
|
|
||
| self._validate_axis_data(x_values) | ||
| self._validate_axis_data(y_values) | ||
| self.x_data.extend(x_values) | ||
| self.y_data.extend(y_values) | ||
|
|
||
| def __len__(self) -> int: | ||
| """Return the length of x_data and y_data.""" | ||
| return len(self.x_data) | ||
mjohanse-emr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| def __eq__(self, value: object, /) -> bool: | ||
| """Return self==value.""" | ||
| if not isinstance(value, self.__class__): | ||
| return NotImplemented | ||
| return ( | ||
| self._values == value._values | ||
| and self.x_units == value.x_units | ||
| and self.y_units == value.y_units | ||
| ) | ||
|
|
||
| def __reduce__(self) -> tuple[Any, ...]: | ||
| """Return object state for pickling.""" | ||
| return (self.__class__, (self.x_data, self.y_data, self.x_units, self.y_units)) | ||
mjohanse-emr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| def __repr__(self) -> str: | ||
| """Return repr(self).""" | ||
| args = [ | ||
mjohanse-emr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| f"x_data={self.x_data!r}", | ||
| f"y_data={self.y_data!r}", | ||
| f"x_units={self.x_units!r}", | ||
| f"y_units={self.y_units!r}", | ||
| ] | ||
| return f"{self.__class__.__module__}.{self.__class__.__name__}({', '.join(args)})" | ||
|
|
||
| def __str__(self) -> str: | ||
| """Return str(self).""" | ||
| x_str = XYData._format_values_with_units(self.x_data, self.x_units) | ||
| y_str = XYData._format_values_with_units(self.y_data, self.y_units) | ||
| return f"[{x_str}, {y_str}]" | ||
|
|
||
| def _validate_axis_data(self, values: Iterable[TNumeric]) -> None: | ||
| for value in values: | ||
| if not isinstance(value, (int, float)): | ||
| raise invalid_arg_type("XYData input data", "int or float", value) | ||
|
|
||
| if not isinstance(value, self._value_type): | ||
| raise self._create_value_mismatch_exception(value) | ||
|
|
||
| def _create_value_mismatch_exception(self, value: object) -> TypeError: | ||
mjohanse-emr marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return TypeError( | ||
| f"Input data does not match expected type. Input Type: {type(value)} " | ||
| f"Expected Type: {self._value_type}" | ||
| ) | ||
|
|
||
| @staticmethod | ||
| def _format_values_with_units(values: MutableSequence[TNumeric], units: str) -> str: | ||
| if units: | ||
| values_with_units = [f"{value} {units}" for value in values] | ||
| values_str = ", ".join(values_with_units) | ||
| else: | ||
| values_without_units = [f"{value}" for value in values] | ||
| values_str = ", ".join(values_without_units) | ||
|
|
||
| return f"[{values_str}]" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| """Unit tests for the nitypes.xy_data package.""" | ||
mjohanse-emr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.