From dd786931e04488e1655bd702435dd7a62d4a7d48 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 7 Nov 2024 18:38:25 +0800 Subject: [PATCH] clib.converison._to_numpy: Add tests for numpy arrays of numpy numeric dtypes (#3583) Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com> --- pygmt/tests/test_clib_to_numpy.py | 154 ++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 pygmt/tests/test_clib_to_numpy.py diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py new file mode 100644 index 00000000000..12b8c1d782d --- /dev/null +++ b/pygmt/tests/test_clib_to_numpy.py @@ -0,0 +1,154 @@ +""" +Tests for the _to_numpy function in the clib.conversion module. +""" + +import sys + +import numpy as np +import numpy.testing as npt +import pandas as pd +import pytest +from packaging.version import Version +from pygmt.clib.conversion import _to_numpy + + +def _check_result(result, expected_dtype): + """ + A helper function to check if the result of the _to_numpy function is a C-contiguous + NumPy array with the expected dtype. + """ + assert isinstance(result, np.ndarray) + assert result.flags.c_contiguous + assert result.dtype.type == expected_dtype + + +######################################################################################## +# Test the _to_numpy function with Python built-in types. +######################################################################################## +@pytest.mark.parametrize( + ("data", "expected_dtype"), + [ + pytest.param( + [1, 2, 3], + np.int32 + if sys.platform == "win32" and Version(np.__version__) < Version("2.0") + else np.int64, + id="int", + ), + pytest.param([1.0, 2.0, 3.0], np.float64, id="float"), + pytest.param( + [complex(+1), complex(-2j), complex("-Infinity+NaNj")], + np.complex128, + id="complex", + ), + ], +) +def test_to_numpy_python_types_numeric(data, expected_dtype): + """ + Test the _to_numpy function with Python built-in numeric types. + """ + result = _to_numpy(data) + _check_result(result, expected_dtype) + npt.assert_array_equal(result, data) + + +######################################################################################## +# Test the _to_numpy function with NumPy arrays. +# +# There are 24 fundamental dtypes in NumPy. Not all of them are supported by PyGMT. +# +# - Numeric dtypes: +# - int8, int16, int32, int64, longlong +# - uint8, uint16, uint32, uint64, ulonglong +# - float16, float32, float64, longdouble +# - complex64, complex128, clongdouble +# - bool +# - datetime64, timedelta64 +# - str_ +# - bytes_ +# - object_ +# - void +# +# Reference: https://numpy.org/doc/2.1/reference/arrays.scalars.html +######################################################################################## +np_dtype_params = [ + pytest.param(np.int8, np.int8, id="int8"), + pytest.param(np.int16, np.int16, id="int16"), + pytest.param(np.int32, np.int32, id="int32"), + pytest.param(np.int64, np.int64, id="int64"), + pytest.param(np.longlong, np.longlong, id="longlong"), + pytest.param(np.uint8, np.uint8, id="uint8"), + pytest.param(np.uint16, np.uint16, id="uint16"), + pytest.param(np.uint32, np.uint32, id="uint32"), + pytest.param(np.uint64, np.uint64, id="uint64"), + pytest.param(np.ulonglong, np.ulonglong, id="ulonglong"), + pytest.param(np.float16, np.float16, id="float16"), + pytest.param(np.float32, np.float32, id="float32"), + pytest.param(np.float64, np.float64, id="float64"), + pytest.param(np.longdouble, np.longdouble, id="longdouble"), + pytest.param(np.complex64, np.complex64, id="complex64"), + pytest.param(np.complex128, np.complex128, id="complex128"), + pytest.param(np.clongdouble, np.clongdouble, id="clongdouble"), +] + + +@pytest.mark.parametrize(("dtype", "expected_dtype"), np_dtype_params) +def test_to_numpy_ndarray_numpy_dtypes_numeric(dtype, expected_dtype): + """ + Test the _to_numpy function with NumPy arrays of NumPy numeric dtypes. + + Test both 1-D and 2-D arrays which are not C-contiguous. + """ + # 1-D array that is not C-contiguous + array = np.array([1, 2, 3, 4, 5, 6], dtype=dtype)[::2] + assert array.flags.c_contiguous is False + result = _to_numpy(array) + _check_result(result, expected_dtype) + npt.assert_array_equal(result, array, strict=True) + + # 2-D array that is not C-contiguous + array = np.array([[1, 2, 3, 4], [5, 6, 7, 8]], dtype=dtype)[::2, ::2] + assert array.flags.c_contiguous is False + result = _to_numpy(array) + _check_result(result, expected_dtype) + npt.assert_array_equal(result, array, strict=True) + + +######################################################################################## +# Test the _to_numpy function with pandas.Series. +# +# In pandas, dtype can be specified by +# +# 1. NumPy dtypes (see above) +# 2. pandas dtypes +# 3. PyArrow dtypes +# +# pandas provides following dtypes: +# +# - Numeric dtypes: +# - Int8, Int16, Int32, Int64 +# - UInt8, UInt16, UInt32, UInt64 +# - Float32, Float64 +# - DatetimeTZDtype +# - PeriodDtype +# - IntervalDtype +# - StringDtype +# - CategoricalDtype +# - SparseDtype +# - BooleanDtype +# - ArrowDtype: a special dtype used to store data in the PyArrow format. +# +# References: +# 1. https://pandas.pydata.org/docs/reference/arrays.html +# 2. https://pandas.pydata.org/docs/user_guide/basics.html#basics-dtypes +# 3. https://pandas.pydata.org/docs/user_guide/pyarrow.html +######################################################################################## +@pytest.mark.parametrize(("dtype", "expected_dtype"), np_dtype_params) +def test_to_numpy_pandas_series_numpy_dtypes_numeric(dtype, expected_dtype): + """ + Test the _to_numpy function with pandas.Series of NumPy numeric dtypes. + """ + series = pd.Series([1, 2, 3, 4, 5, 6], dtype=dtype)[::2] # Not C-contiguous + result = _to_numpy(series) + _check_result(result, expected_dtype) + npt.assert_array_equal(result, series)