Skip to content

Commit 55e4c76

Browse files
Blahpapto1Avi Goel
andauthored
Add Utility Method in SampleMeasurement to convert Double2DArray To/From NDArray and List(List(Float)) (#1036)
* feat(array_utils): add utilities for converting Double2DArray to/from numpy NDArray and list(list(float)) * feat(measurement): Update SampleMeasurement to use utility method. * feat(array_utils): add type hints to conversion functions and update mypy configuration * fix lint issues * fix: remove unused numpy import and update variable names * feat(array_utils): update method signature and add tests for conversion methods * fix: TypeError in _array_utils * feat(array_utils): Add NDArray Type Def in method signatures * refactor(array_utils): Update type hints and improve import structure for numpy compatibility * refactor(tests): Rename utility tests for clarity and improve test function names --------- Co-authored-by: Avi Goel <[email protected]>
1 parent 4002977 commit 55e4c76

File tree

5 files changed

+152
-0
lines changed

5 files changed

+152
-0
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""Double2DArray Conversion Utilities."""
2+
3+
from __future__ import annotations
4+
5+
from typing import TYPE_CHECKING, List
6+
7+
from ni_measurement_plugin_sdk_service._internal.stubs.ni.protobuf.types import (
8+
array_pb2,
9+
)
10+
11+
if TYPE_CHECKING:
12+
import numpy as np
13+
import numpy.typing as npt
14+
15+
16+
def double2darray_to_ndarray(double2darray: array_pb2.Double2DArray) -> npt.NDArray[np.float64]:
17+
"""Convert Double2DArray to numpy NDArray."""
18+
import numpy as np
19+
20+
return np.array(double2darray.data, dtype=np.float64).reshape(
21+
double2darray.rows, double2darray.columns
22+
)
23+
24+
25+
def ndarray_to_double2darray(ndarray: npt.NDArray[np.float64]) -> array_pb2.Double2DArray:
26+
"""Convert numpy NDArray to Double2DArray."""
27+
return array_pb2.Double2DArray(
28+
data=ndarray.flatten().tolist(), rows=ndarray.shape[0], columns=ndarray.shape[1]
29+
)
30+
31+
32+
def list_to_double2darray(data: List[List[float]]) -> array_pb2.Double2DArray:
33+
"""Convert list of lists to Double2DArray."""
34+
rows = len(data)
35+
columns = len(data[0]) if rows > 0 else 0
36+
flattened_data = [item for sublist in data for item in sublist]
37+
return array_pb2.Double2DArray(data=flattened_data, rows=rows, columns=columns)
38+
39+
40+
def double2darray_to_list(double2darray: array_pb2.Double2DArray) -> List[List[float]]:
41+
"""Convert Double2DArray to list of lists."""
42+
data = double2darray.data
43+
rows = double2darray.rows
44+
columns = double2darray.columns
45+
return [data[i * columns : (i + 1) * columns] for i in range(rows)]

examples/sample_measurement/measurement.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from enum import Enum
77
from typing import Iterable, Tuple
88

9+
import _array_utils
910
import click
1011
import ni_measurement_plugin_sdk_service as nims
1112
from _helpers import configure_logging, verbosity_option
@@ -39,6 +40,13 @@ class Color(Enum):
3940
BLUE = 3
4041

4142

43+
# Define a list of lists of floats
44+
_list_of_lists = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]
45+
46+
# Convert the list of lists to a Double2DArray
47+
_converted_double_2d_array = _array_utils.list_to_double2darray(_list_of_lists)
48+
49+
4250
@measurement_service.register_measurement
4351
@measurement_service.configuration("Float In", nims.DataType.Float, 0.06)
4452
@measurement_service.configuration("Double Array In", nims.DataType.DoubleArray1D, [0.1, 0.2, 0.3])
@@ -64,6 +72,7 @@ class Color(Enum):
6472
)
6573
@measurement_service.output("String Array out", nims.DataType.StringArray1D)
6674
@measurement_service.output("Double 2D Array Out", nims.DataType.Double2DArray)
75+
@measurement_service.output("Converted Double 2D Array", nims.DataType.Double2DArray)
6776
def measure(
6877
float_input: float,
6978
double_array_input: Iterable[float],
@@ -81,6 +90,7 @@ def measure(
8190
color_pb2.ProtobufColor.ValueType,
8291
Iterable[str],
8392
array_pb2.Double2DArray,
93+
array_pb2.Double2DArray,
8494
]:
8595
"""Perform a loopback measurement with various data types."""
8696
logging.info("Executing measurement")
@@ -100,6 +110,7 @@ def cancel_callback() -> None:
100110
double_2d_array_output = array_pb2.Double2DArray(
101111
rows=2, columns=3, data=[1.5, 2.5, 3.5, 4.5, 5.5, 6.5]
102112
)
113+
converted_double_2d_array_output = _converted_double_2d_array
103114
logging.info("Completed measurement")
104115

105116
return (
@@ -111,6 +122,7 @@ def cancel_callback() -> None:
111122
protobuf_enum_output,
112123
string_array_output,
113124
double_2d_array_output,
125+
converted_double_2d_array_output,
114126
)
115127

116128

examples/sample_measurement/pyproject.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,12 @@ build-backend = "poetry.core.masonry.api"
2727
[tool.mypy]
2828
disallow_untyped_defs = true
2929

30+
[[tool.mypy.overrides]]
31+
module = [
32+
# numpy has type hints but this example does not depend on numpy.
33+
"numpy.*",
34+
]
35+
ignore_missing_imports = true
36+
3037
[tool.ni-python-styleguide]
3138
extend_exclude = '*_pb2_grpc.py,*_pb2_grpc.pyi,*_pb2.py,*_pb2.pyi'
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import numpy
2+
import numpy.typing
3+
4+
from ni_measurement_plugin_sdk_service._internal.stubs.ni.protobuf.types import (
5+
array_pb2,
6+
)
7+
from tests.utilities import _array_utils
8+
9+
10+
def test___valid_double2darray___double2darray_to_ndarray___converts_data():
11+
double2darray = array_pb2.Double2DArray(data=[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], rows=2, columns=3)
12+
13+
ndarray = _array_utils.double2darray_to_ndarray(double2darray)
14+
15+
assert numpy.array_equal(ndarray, numpy.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]))
16+
17+
18+
def test___valid_ndarray___ndarray_to_double2darray___converts_data():
19+
ndarray = numpy.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
20+
21+
double2darray = _array_utils.ndarray_to_double2darray(ndarray)
22+
23+
assert double2darray == array_pb2.Double2DArray(
24+
data=[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], rows=2, columns=3
25+
)
26+
27+
28+
def test___valid_list___list_to_double2darray___converts_data():
29+
data = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
30+
31+
double2darray = _array_utils.list_to_double2darray(data)
32+
33+
assert double2darray == array_pb2.Double2DArray(
34+
data=[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], rows=2, columns=3
35+
)
36+
37+
38+
def test___valid_double2darray___double2darray_to_list___converts_data():
39+
double2darray = array_pb2.Double2DArray(data=[1.0, 2.0, 3.0, 4.0, 5.0, 6.0], rows=2, columns=3)
40+
41+
data = _array_utils.double2darray_to_list(double2darray)
42+
43+
assert data == [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""Double2DArray Conversion Utilities."""
2+
3+
from __future__ import annotations
4+
5+
from typing import TYPE_CHECKING, List
6+
7+
from ni_measurement_plugin_sdk_service._internal.stubs.ni.protobuf.types import (
8+
array_pb2,
9+
)
10+
11+
if TYPE_CHECKING:
12+
import numpy as np
13+
import numpy.typing as npt
14+
15+
16+
def double2darray_to_ndarray(double2darray: array_pb2.Double2DArray) -> npt.NDArray[np.float64]:
17+
"""Convert Double2DArray to numpy NDArray."""
18+
import numpy as np
19+
20+
return np.array(double2darray.data, dtype=np.float64).reshape(
21+
double2darray.rows, double2darray.columns
22+
)
23+
24+
25+
def ndarray_to_double2darray(ndarray: npt.NDArray[np.float64]) -> array_pb2.Double2DArray:
26+
"""Convert numpy NDArray to Double2DArray."""
27+
return array_pb2.Double2DArray(
28+
data=ndarray.flatten().tolist(), rows=ndarray.shape[0], columns=ndarray.shape[1]
29+
)
30+
31+
32+
def list_to_double2darray(data: List[List[float]]) -> array_pb2.Double2DArray:
33+
"""Convert list of lists to Double2DArray."""
34+
rows = len(data)
35+
columns = len(data[0]) if rows > 0 else 0
36+
flattened_data = [item for sublist in data for item in sublist]
37+
return array_pb2.Double2DArray(data=flattened_data, rows=rows, columns=columns)
38+
39+
40+
def double2darray_to_list(double2darray: array_pb2.Double2DArray) -> List[List[float]]:
41+
"""Convert Double2DArray to list of lists."""
42+
data = double2darray.data
43+
rows = double2darray.rows
44+
columns = double2darray.columns
45+
return [data[i * columns : (i + 1) * columns] for i in range(rows)]

0 commit comments

Comments
 (0)