From 127d8ca67aa1be5a2519821d261ce60f3b11cb10 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 5 Nov 2024 17:35:36 +0800 Subject: [PATCH 01/28] clib.converison._to_numpy: Add tests for numpy numeric dtypes --- pygmt/tests/test_clib_to_numpy.py | 82 +++++++++++++++++++++++++++++++ 1 file changed, 82 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..491bcdba7e5 --- /dev/null +++ b/pygmt/tests/test_clib_to_numpy.py @@ -0,0 +1,82 @@ +""" +Tests for the _to_numpy function in the clib.conversion module. +""" + +import numpy as np +import numpy.testing as npt +import pytest +from pygmt.clib.conversion import _to_numpy +from pygmt.clib.session import DTYPES + + +def _check_result(result, supported): + """ + Check the result of the _to_numpy function. + """ + # Check that the result is a NumPy array and is C-contiguous. + assert isinstance(result, np.ndarray) + assert result.flags.c_contiguous + # Check that the dtype is supported by PyGMT (or the GMT C API). + assert (result.dtype.type in DTYPES) == supported + + +######################################################################################## +# Test the _to_numpy function with NumPy dtypes. +# +# There are 24 fundamental dtypes in NumPy. Not all of them are supported by PyGMT. +# Reference: https://numpy.org/doc/2.1/reference/arrays.scalars.html +# +# - 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 +######################################################################################## +@pytest.mark.parametrize( + ("dtype", "supported"), + [ + (np.int8, True), + (np.int16, True), + (np.int32, True), + (np.int64, True), + (np.longlong, True), + (np.uint8, True), + (np.uint16, True), + (np.uint32, True), + (np.uint64, True), + (np.ulonglong, True), + (np.float16, False), + (np.float32, True), + (np.float64, True), + (np.longdouble, False), + (np.complex64, False), + (np.complex128, False), + (np.clongdouble, False), + ], +) +def test_to_numpy_ndarray_numpy_dtypes_numeric(dtype, supported): + """ + Test the _to_numpy function with NumPy arrays of NumPy numeric dtypes. + + "dtype" is the NumPy dtype to be tested and "supported" is a boolean value + indicating whether the dtype is supported by PyGMT (or the GMT C API). + """ + # 1-D array + array = np.array([1, 2, 3], dtype=dtype) + assert array.dtype == dtype + result = _to_numpy(array) + _check_result(result, supported) + npt.assert_array_equal(result, array) + + # 2-D array + array = np.array([[1, 2, 3], [4, 5, 6]], dtype=dtype) + assert array.dtype == dtype + result = _to_numpy(array) + _check_result(result, supported) + npt.assert_array_equal(result, array) From 6ae1ddbd0d318734e26c3e5e0d300ac05044a3f4 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 5 Nov 2024 18:07:18 +0800 Subject: [PATCH 02/28] Add tests for pandas.Series with NumPy numeric dtypes --- pygmt/tests/test_clib_to_numpy.py | 47 ++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index 491bcdba7e5..397b79b3164 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -4,6 +4,7 @@ import numpy as np import numpy.testing as npt +import pandas as pd import pytest from pygmt.clib.conversion import _to_numpy from pygmt.clib.session import DTYPES @@ -21,7 +22,7 @@ def _check_result(result, supported): ######################################################################################## -# Test the _to_numpy function with NumPy dtypes. +# Test the _to_numpy function with NumPy arrays. # # There are 24 fundamental dtypes in NumPy. Not all of them are supported by PyGMT. # Reference: https://numpy.org/doc/2.1/reference/arrays.scalars.html @@ -80,3 +81,47 @@ def test_to_numpy_ndarray_numpy_dtypes_numeric(dtype, supported): result = _to_numpy(array) _check_result(result, supported) npt.assert_array_equal(result, array) + + +######################################################################################## +# 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 +# +# Reference: https://pandas.pydata.org/docs/reference/arrays.html +######################################################################################## +@pytest.mark.parametrize( + ("dtype", "supported"), + [ + (np.int8, True), + (np.int16, True), + (np.int32, True), + (np.int64, True), + (np.longlong, True), + (np.uint8, True), + (np.uint16, True), + (np.uint32, True), + (np.uint64, True), + (np.ulonglong, True), + (np.float16, False), + (np.float32, True), + (np.float64, True), + (np.longdouble, False), + (np.complex64, False), + (np.complex128, False), + (np.clongdouble, False), + ], +) +def test_to_numpy_pandas_series_numpy_dtypes_numeric(dtype, supported): + """ + Test the _to_numpy function with pandas.Series of NumPy numeric dtypes. + """ + series = pd.Series([1, 2, 3], dtype=dtype) + assert series.dtype == dtype + result = _to_numpy(series) + _check_result(result, supported) + npt.assert_array_equal(result, series) From a9635b5c0364f0f793b6c1ac4ce08271f0c9f316 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 5 Nov 2024 18:32:01 +0800 Subject: [PATCH 03/28] Improve docstrings for tests --- pygmt/tests/test_clib_to_numpy.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index 397b79b3164..50d9bc18a0d 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -92,7 +92,27 @@ def test_to_numpy_ndarray_numpy_dtypes_numeric(dtype, supported): # 2. pandas dtypes # 3. PyArrow dtypes # -# Reference: https://pandas.pydata.org/docs/reference/arrays.html +# pandas provides following dtypes: +# +# - Numeric dtypes: +# - Int8, Int16, Int32, Int64 +# - UInt8, UInt16, UInt32, UInt64 +# - Float32, Float64 +# - DatetimeTZDtype +# - PeriodDtype +# - IntervalDtype +# - StringDtype +# - CategoricalDtype +# - SparseDtype +# - BooleanDtype +# - ArrowDtype +# +# ArrowDtype is a special dtype that is 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", "supported"), From 6f966dbbdd5ec419660ad8e3456e45c078f5d33b Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 6 Nov 2024 14:16:13 +0800 Subject: [PATCH 04/28] Add tests for Python built-in types --- pygmt/tests/test_clib_to_numpy.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index 50d9bc18a0d..2c7f99467f7 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -21,6 +21,25 @@ def _check_result(result, supported): assert (result.dtype.type in DTYPES) == supported +######################################################################################## +# Test the _to_numpy function with Python built-in types. +######################################################################################## +@pytest.mark.parametrize( + ("data", "supported"), + [ + pytest.param([1, 2, 3], True, id="int"), + pytest.param([1.0, 2.0, 3.0], True, id="float"), + ], +) +def test_to_numpy_python_types_numeric(data, supported): + """ + Test the _to_numpy function with Python built-in numeric types. + """ + result = _to_numpy(data) + _check_result(result, supported) + npt.assert_array_equal(result, data) + + ######################################################################################## # Test the _to_numpy function with NumPy arrays. # From f9bf19c5fd7778f9265c53127b62c56a5105db65 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 6 Nov 2024 19:06:21 +0800 Subject: [PATCH 05/28] Refactor a few tests --- pygmt/tests/test_clib_to_numpy.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index 2c7f99467f7..e7ab083514d 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -25,26 +25,25 @@ def _check_result(result, supported): # Test the _to_numpy function with Python built-in types. ######################################################################################## @pytest.mark.parametrize( - ("data", "supported"), + ("data", "expected_dtype"), [ - pytest.param([1, 2, 3], True, id="int"), - pytest.param([1.0, 2.0, 3.0], True, id="float"), + pytest.param([1, 2, 3], np.int64, id="int"), + pytest.param([1.0, 2.0, 3.0], np.float64, id="float"), ], ) -def test_to_numpy_python_types_numeric(data, supported): +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, supported) - npt.assert_array_equal(result, data) + _check_result(result, supported=True) + npt.assert_array_equal(result, np.array(data, dtype=expected_dtype), strict=True) ######################################################################################## # Test the _to_numpy function with NumPy arrays. # # There are 24 fundamental dtypes in NumPy. Not all of them are supported by PyGMT. -# Reference: https://numpy.org/doc/2.1/reference/arrays.scalars.html # # - Numeric dtypes: # - int8, int16, int32, int64, longlong @@ -57,6 +56,8 @@ def test_to_numpy_python_types_numeric(data, supported): # - bytes_ # - object_ # - void +# +# Reference: https://numpy.org/doc/2.1/reference/arrays.scalars.html ######################################################################################## @pytest.mark.parametrize( ("dtype", "supported"), @@ -84,22 +85,19 @@ def test_to_numpy_ndarray_numpy_dtypes_numeric(dtype, supported): """ Test the _to_numpy function with NumPy arrays of NumPy numeric dtypes. - "dtype" is the NumPy dtype to be tested and "supported" is a boolean value - indicating whether the dtype is supported by PyGMT (or the GMT C API). + Test both 1-D and 2-D arrays. """ # 1-D array array = np.array([1, 2, 3], dtype=dtype) - assert array.dtype == dtype result = _to_numpy(array) _check_result(result, supported) - npt.assert_array_equal(result, array) + npt.assert_array_equal(result, array, strict=True) # 2-D array array = np.array([[1, 2, 3], [4, 5, 6]], dtype=dtype) - assert array.dtype == dtype result = _to_numpy(array) _check_result(result, supported) - npt.assert_array_equal(result, array) + npt.assert_array_equal(result, array, strict=True) ######################################################################################## @@ -124,9 +122,7 @@ def test_to_numpy_ndarray_numpy_dtypes_numeric(dtype, supported): # - CategoricalDtype # - SparseDtype # - BooleanDtype -# - ArrowDtype -# -# ArrowDtype is a special dtype that is used to store data in the PyArrow format. +# - ArrowDtype: a special dtype used to store data in the PyArrow format. # # References: # 1. https://pandas.pydata.org/docs/reference/arrays.html From 8bc2f56c253d401ac609ed1a7a1a5bf9bcf74de2 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 6 Nov 2024 21:27:22 +0800 Subject: [PATCH 06/28] Check the expected dtype --- pygmt/tests/test_clib_to_numpy.py | 97 +++++++++++++++---------------- 1 file changed, 47 insertions(+), 50 deletions(-) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index e7ab083514d..81fb82dd965 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -7,18 +7,16 @@ import pandas as pd import pytest from pygmt.clib.conversion import _to_numpy -from pygmt.clib.session import DTYPES -def _check_result(result, supported): +def _check_result(result, expected_dtype): """ - Check the result of the _to_numpy function. + A helper function to check if the result of the _to_numpy function is a C-contiguous + NumPy array with the expected dtype. """ - # Check that the result is a NumPy array and is C-contiguous. assert isinstance(result, np.ndarray) assert result.flags.c_contiguous - # Check that the dtype is supported by PyGMT (or the GMT C API). - assert (result.dtype.type in DTYPES) == supported + assert result.dtype.type == expected_dtype ######################################################################################## @@ -36,8 +34,8 @@ 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, supported=True) - npt.assert_array_equal(result, np.array(data, dtype=expected_dtype), strict=True) + _check_result(result, expected_dtype) + npt.assert_array_equal(result, data) ######################################################################################## @@ -60,28 +58,28 @@ def test_to_numpy_python_types_numeric(data, expected_dtype): # Reference: https://numpy.org/doc/2.1/reference/arrays.scalars.html ######################################################################################## @pytest.mark.parametrize( - ("dtype", "supported"), + ("dtype", "expected_dtype"), [ - (np.int8, True), - (np.int16, True), - (np.int32, True), - (np.int64, True), - (np.longlong, True), - (np.uint8, True), - (np.uint16, True), - (np.uint32, True), - (np.uint64, True), - (np.ulonglong, True), - (np.float16, False), - (np.float32, True), - (np.float64, True), - (np.longdouble, False), - (np.complex64, False), - (np.complex128, False), - (np.clongdouble, False), + 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"), ], ) -def test_to_numpy_ndarray_numpy_dtypes_numeric(dtype, supported): +def test_to_numpy_ndarray_numpy_dtypes_numeric(dtype, expected_dtype): """ Test the _to_numpy function with NumPy arrays of NumPy numeric dtypes. @@ -90,13 +88,13 @@ def test_to_numpy_ndarray_numpy_dtypes_numeric(dtype, supported): # 1-D array array = np.array([1, 2, 3], dtype=dtype) result = _to_numpy(array) - _check_result(result, supported) + _check_result(result, expected_dtype) npt.assert_array_equal(result, array, strict=True) # 2-D array array = np.array([[1, 2, 3], [4, 5, 6]], dtype=dtype) result = _to_numpy(array) - _check_result(result, supported) + _check_result(result, expected_dtype) npt.assert_array_equal(result, array, strict=True) @@ -130,33 +128,32 @@ def test_to_numpy_ndarray_numpy_dtypes_numeric(dtype, supported): # 3. https://pandas.pydata.org/docs/user_guide/pyarrow.html ######################################################################################## @pytest.mark.parametrize( - ("dtype", "supported"), + ("dtype", "expected_dtype"), [ - (np.int8, True), - (np.int16, True), - (np.int32, True), - (np.int64, True), - (np.longlong, True), - (np.uint8, True), - (np.uint16, True), - (np.uint32, True), - (np.uint64, True), - (np.ulonglong, True), - (np.float16, False), - (np.float32, True), - (np.float64, True), - (np.longdouble, False), - (np.complex64, False), - (np.complex128, False), - (np.clongdouble, False), + 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"), ], ) -def test_to_numpy_pandas_series_numpy_dtypes_numeric(dtype, supported): +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], dtype=dtype) - assert series.dtype == dtype result = _to_numpy(series) - _check_result(result, supported) + _check_result(result, expected_dtype) npt.assert_array_equal(result, series) From 42a09519301347b85e63164f5e80e5215e4bbf14 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 7 Nov 2024 07:08:45 +0800 Subject: [PATCH 07/28] Define params list Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com> --- pygmt/tests/test_clib_to_numpy.py | 67 +++++++++++-------------------- 1 file changed, 23 insertions(+), 44 deletions(-) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index 81fb82dd965..b66dd2b29cc 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -57,28 +57,28 @@ def test_to_numpy_python_types_numeric(data, expected_dtype): # # Reference: https://numpy.org/doc/2.1/reference/arrays.scalars.html ######################################################################################## -@pytest.mark.parametrize( - ("dtype", "expected_dtype"), - [ - 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"), - ], -) +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. @@ -127,28 +127,7 @@ def test_to_numpy_ndarray_numpy_dtypes_numeric(dtype, expected_dtype): # 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"), - [ - 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_pandas_series_numpy_dtypes_numeric(dtype, expected_dtype): """ Test the _to_numpy function with pandas.Series of NumPy numeric dtypes. From 933bc6214facf18b59f27cd1b9d79de5ad624cd4 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 7 Nov 2024 07:30:52 +0800 Subject: [PATCH 08/28] Input array now is not C-contiguous --- pygmt/tests/test_clib_to_numpy.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index b66dd2b29cc..f596952ef13 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -85,14 +85,16 @@ def test_to_numpy_ndarray_numpy_dtypes_numeric(dtype, expected_dtype): Test both 1-D and 2-D arrays. """ - # 1-D array - array = np.array([1, 2, 3], dtype=dtype) + # 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 - array = np.array([[1, 2, 3], [4, 5, 6]], dtype=dtype) + # 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) @@ -132,7 +134,7 @@ 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], dtype=dtype) + 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) From 4edfef0491f7244750b50bcc49235722bc11fabb Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 7 Nov 2024 07:56:39 +0800 Subject: [PATCH 09/28] Add test for Python built-in complex dtype Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com> --- pygmt/tests/test_clib_to_numpy.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index f596952ef13..92f061e0ec1 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -27,6 +27,11 @@ def _check_result(result, expected_dtype): [ pytest.param([1, 2, 3], 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): @@ -83,7 +88,7 @@ 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. + 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] From 7222db23bbdfbb298b211c02ab981e71ab433be6 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 5 Nov 2024 18:33:37 +0800 Subject: [PATCH 10/28] Add tests for panda.Series with pandas numeric dtypes --- pygmt/tests/test_clib_to_numpy.py | 51 +++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index 92f061e0ec1..dac8106187e 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -143,3 +143,54 @@ def test_to_numpy_pandas_series_numpy_dtypes_numeric(dtype, expected_dtype): result = _to_numpy(series) _check_result(result, expected_dtype) npt.assert_array_equal(result, series) + + +@pytest.mark.parametrize( + ("dtype", "expected_dtype"), + [ + pytest.param(pd.Int8Dtype(), np.int8, id="Int8"), + pytest.param(pd.Int16Dtype(), np.int16, id="Int16"), + pytest.param(pd.Int32Dtype(), np.int32, id="Int32"), + pytest.param(pd.Int64Dtype(), np.int64, id="Int64"), + pytest.param(pd.UInt8Dtype(), np.uint8, id="UInt8"), + pytest.param(pd.UInt16Dtype(), np.uint16, id="UInt16"), + pytest.param(pd.UInt32Dtype(), np.uint32, id="UInt32"), + pytest.param(pd.UInt64Dtype(), np.uint64, id="UInt64"), + pytest.param(pd.Float32Dtype(), np.float32, id="Float32"), + pytest.param(pd.Float64Dtype(), np.float64, id="Float64"), + ], +) +def test_to_numpy_pandas_series_pandas_dtypes_numeric(dtype, expected_dtype): + """ + Test the _to_numpy function with pandas.Series of pandas 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) + + +@pytest.mark.parametrize( + ("dtype", "expected_dtype"), + [ + pytest.param(pd.Int8Dtype(), np.float64, id="Int8"), + pytest.param(pd.Int16Dtype(), np.float64, id="Int16"), + pytest.param(pd.Int32Dtype(), np.float64, id="Int32"), + pytest.param(pd.Int64Dtype(), np.float64, id="Int64"), + pytest.param(pd.UInt8Dtype(), np.float64, id="UInt8"), + pytest.param(pd.UInt16Dtype(), np.float64, id="UInt16"), + pytest.param(pd.UInt32Dtype(), np.float64, id="UInt32"), + pytest.param(pd.UInt64Dtype(), np.float64, id="UInt64"), + pytest.param(pd.Float32Dtype(), np.float32, id="Float32"), + pytest.param(pd.Float64Dtype(), np.float64, id="Float64"), + ], +) +def test_to_numpy_pandas_series_pandas_dtypes_numeric_with_na(dtype, expected_dtype): + """ + Test the _to_numpy function with pandas.Series of pandas numeric dtypes and NA. + """ + series = pd.Series([1, 2, pd.NA, 4, 5, 6], dtype=dtype)[::2] # Not C-contiguous + assert series.isna().any() + result = _to_numpy(series) + _check_result(result, expected_dtype) + npt.assert_array_equal(result, np.array([1.0, np.nan, 5.0], dtype=expected_dtype)) From a4f15cd778b146cc787450ba81b36da81a3a2efd Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 7 Nov 2024 08:19:38 +0800 Subject: [PATCH 11/28] Add workarounds for pandas nullable dtypes prior pandas v2.1 --- pygmt/clib/conversion.py | 43 ++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/pygmt/clib/conversion.py b/pygmt/clib/conversion.py index af8eb3458d4..b93822e2d4c 100644 --- a/pygmt/clib/conversion.py +++ b/pygmt/clib/conversion.py @@ -162,19 +162,36 @@ def _to_numpy(data: Any) -> np.ndarray: "date64[ms][pyarrow]": np.datetime64, } - if ( - hasattr(data, "isna") - and data.isna().any() - and Version(pd.__version__) < Version("2.2") - ): - # Workaround for dealing with pd.NA with pandas < 2.2. - # Bug report at: https://github.com/GenericMappingTools/pygmt/issues/2844 - # Following SPEC0, pandas 2.1 will be dropped in 2025 Q3, so it's likely - # we can remove the workaround in PyGMT v0.17.0. - array = np.ascontiguousarray(data.astype(float)) - else: - vec_dtype = str(getattr(data, "dtype", "")) - array = np.ascontiguousarray(data, dtype=dtypes.get(vec_dtype)) + # pandas nullable dtypes and pyarrow types were converted to np.object_ dtype + # before, and are converted to suitable numpy dtypes since pandas 2.2. + # Refer to the following link for details: + # https://pandas.pydata.org/docs/whatsnew/v2.2.0.html#to-numpy-for-numpy-nullable-and-arrow-types-converts-to-suitable-numpy-dtype + # Here are the workarounds for pandas < 2.2. + # Following SPEC 0, pandas 2.1 should be dropped in 2025 Q3, so it's likely we can + # remove the workaround in PyGMT v0.17.0. + if Version(pd.__version__) < Version("2.2"): + dtypes.update( + { + "Int8": np.int8, + "Int16": np.int16, + "Int32": np.int32, + "Int64": np.int64, + "UInt8": np.uint8, + "UInt16": np.uint16, + "UInt32": np.uint32, + "UInt64": np.uint64, + "Float32": np.float32, + "Float64": np.float64, + } + ) + if hasattr(data, "isna") and data.isna().any(): + # Integer dtypes with missing values are cast to NumPy float dtypes and NaN + # is used as missing value indicator. + dtype = np.float64 if data.dtype.kind in "iu" else data.dtype.numpy_dtype + data = data.to_numpy(dtype=dtype, na_value=np.nan) + + vec_dtype = str(getattr(data, "dtype", "")) + array = np.ascontiguousarray(data, dtype=dtypes.get(vec_dtype)) return array From b0d7903d9887f49906223fba5004fec97e806002 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 7 Nov 2024 08:25:36 +0800 Subject: [PATCH 12/28] Remove duplicated test test_vectors_to_arrays_pandas_nan --- pygmt/tests/test_clib_vectors_to_arrays.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/pygmt/tests/test_clib_vectors_to_arrays.py b/pygmt/tests/test_clib_vectors_to_arrays.py index e59690069ca..b85912d48d6 100644 --- a/pygmt/tests/test_clib_vectors_to_arrays.py +++ b/pygmt/tests/test_clib_vectors_to_arrays.py @@ -69,17 +69,6 @@ def test_vectors_to_arrays_not_c_contiguous(): _check_arrays(arrays) -def test_vectors_to_arrays_pandas_nan(): - """ - Test the vectors_to_arrays function with pandas Series containing NaNs. - """ - vectors = [pd.Series(data=[0, 4, pd.NA, 8, 6], dtype=pd.Int32Dtype())] - arrays = vectors_to_arrays(vectors) - npt.assert_equal(arrays[0], np.array([0, 4, np.nan, 8, 6], dtype=np.float64)) - assert arrays[0].dtype == np.float64 - _check_arrays(arrays) - - @pytest.mark.skipif(not _HAS_PYARROW, reason="pyarrow is not installed.") def test_vectors_to_arrays_pyarrow_datetime(): """ From 8ca200bc99be235e41651adc03cde547a5c37e1b Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Fri, 8 Nov 2024 00:11:06 +0800 Subject: [PATCH 13/28] Use data.to_numpy(na_value=np.nan).astype(dtype=dtype) for float16 --- pygmt/clib/conversion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/clib/conversion.py b/pygmt/clib/conversion.py index b93822e2d4c..acf43782a73 100644 --- a/pygmt/clib/conversion.py +++ b/pygmt/clib/conversion.py @@ -188,7 +188,7 @@ def _to_numpy(data: Any) -> np.ndarray: # Integer dtypes with missing values are cast to NumPy float dtypes and NaN # is used as missing value indicator. dtype = np.float64 if data.dtype.kind in "iu" else data.dtype.numpy_dtype - data = data.to_numpy(dtype=dtype, na_value=np.nan) + data = data.to_numpy(na_value=np.nan).astype(dtype=dtype) vec_dtype = str(getattr(data, "dtype", "")) array = np.ascontiguousarray(data, dtype=dtypes.get(vec_dtype)) From 3cc596a7b79b2f815a085e6ff65c260c4160b119 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Sun, 10 Nov 2024 15:24:48 +0800 Subject: [PATCH 14/28] Further improve the workaround for pandas dtypes --- pygmt/clib/conversion.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pygmt/clib/conversion.py b/pygmt/clib/conversion.py index acf43782a73..5489f098096 100644 --- a/pygmt/clib/conversion.py +++ b/pygmt/clib/conversion.py @@ -162,14 +162,14 @@ def _to_numpy(data: Any) -> np.ndarray: "date64[ms][pyarrow]": np.datetime64, } - # pandas nullable dtypes and pyarrow types were converted to np.object_ dtype - # before, and are converted to suitable numpy dtypes since pandas 2.2. - # Refer to the following link for details: - # https://pandas.pydata.org/docs/whatsnew/v2.2.0.html#to-numpy-for-numpy-nullable-and-arrow-types-converts-to-suitable-numpy-dtype + # pandas numeric dtypes were converted to np.object_ dtype prior pandas 2.2, and are + # converted to suitable numpy dtypes since pandas 2.2. Refer to the following link + # for details: https://pandas.pydata.org/docs/whatsnew/v2.2.0.html#to-numpy-for-numpy-nullable-and-arrow-types-converts-to-suitable-numpy-dtype # Here are the workarounds for pandas < 2.2. # Following SPEC 0, pandas 2.1 should be dropped in 2025 Q3, so it's likely we can # remove the workaround in PyGMT v0.17.0. if Version(pd.__version__) < Version("2.2"): + # Specify mapping from pandas nullable dtypes to suitable numpy dtypes dtypes.update( { "Int8": np.int8, @@ -184,9 +184,10 @@ def _to_numpy(data: Any) -> np.ndarray: "Float64": np.float64, } ) - if hasattr(data, "isna") and data.isna().any(): - # Integer dtypes with missing values are cast to NumPy float dtypes and NaN - # is used as missing value indicator. + # For pandas.Index/pandas.Series, pandas/pyarrow integer dtypes with missing + # values should be cast to NumPy float dtypes and NaN is used as missing value + # indicator. + if getattr(data, "hasnans", False): # pandas.Index/pandas.Series has 'hasnans' dtype = np.float64 if data.dtype.kind in "iu" else data.dtype.numpy_dtype data = data.to_numpy(na_value=np.nan).astype(dtype=dtype) From a65f6ae2dddae33f80c8d22c7c07148fd64226b8 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 12 Nov 2024 08:53:29 +0800 Subject: [PATCH 15/28] clib.conversion._to_numpy: Add tests for pandas.Series with pyarrow numeric dtypes (#3585) --- pygmt/tests/test_clib_to_numpy.py | 51 ++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index 847acccbda1..e77dd53ea4a 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -10,6 +10,7 @@ import pytest from packaging.version import Version from pygmt.clib.conversion import _to_numpy +from pygmt.helpers.testing import skip_if_no try: import pyarrow as pa @@ -18,6 +19,9 @@ except ImportError: _HAS_PYARROW = False +# Mark tests that require pyarrow +pa_marks = {"marks": skip_if_no(package="pyarrow")} + def _check_result(result, expected_dtype): """ @@ -145,6 +149,11 @@ def test_to_numpy_ndarray_numpy_dtypes_numeric(dtype, expected_dtype): # - BooleanDtype # - ArrowDtype: a special dtype used to store data in the PyArrow format. # +# In pandas, PyArrow types can be specified using the following formats: +# +# - Prefixed with the name of the dtype and "[pyarrow]" (e.g., "int8[pyarrow]") +# - Specified using ``ArrowDType`` (e.g., "pd.ArrowDtype(pa.int8())") +# # References: # 1. https://pandas.pydata.org/docs/reference/arrays.html # 2. https://pandas.pydata.org/docs/user_guide/basics.html#basics-dtypes @@ -174,13 +183,30 @@ def test_to_numpy_pandas_series_numpy_dtypes_numeric(dtype, expected_dtype): pytest.param(pd.UInt64Dtype(), np.uint64, id="UInt64"), pytest.param(pd.Float32Dtype(), np.float32, id="Float32"), pytest.param(pd.Float64Dtype(), np.float64, id="Float64"), + pytest.param("int8[pyarrow]", np.int8, id="int8[pyarrow]", **pa_marks), + pytest.param("int16[pyarrow]", np.int16, id="int16[pyarrow]", **pa_marks), + pytest.param("int32[pyarrow]", np.int32, id="int32[pyarrow]", **pa_marks), + pytest.param("int64[pyarrow]", np.int64, id="int64[pyarrow]", **pa_marks), + pytest.param("uint8[pyarrow]", np.uint8, id="uint8[pyarrow]", **pa_marks), + pytest.param("uint16[pyarrow]", np.uint16, id="uint16[pyarrow]", **pa_marks), + pytest.param("uint32[pyarrow]", np.uint32, id="uint32[pyarrow]", **pa_marks), + pytest.param("uint64[pyarrow]", np.uint64, id="uint64[pyarrow]", **pa_marks), + pytest.param("float16[pyarrow]", np.float16, id="float16[pyarrow]", **pa_marks), + pytest.param("float32[pyarrow]", np.float32, id="float32[pyarrow]", **pa_marks), + pytest.param("float64[pyarrow]", np.float64, id="float64[pyarrow]", **pa_marks), ], ) def test_to_numpy_pandas_series_pandas_dtypes_numeric(dtype, expected_dtype): """ - Test the _to_numpy function with pandas.Series of pandas numeric dtypes. + Test the _to_numpy function with pandas.Series of pandas/PyArrow numeric dtypes. """ - series = pd.Series([1, 2, 3, 4, 5, 6], dtype=dtype)[::2] # Not C-contiguous + data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0] + if dtype == "float16[pyarrow]" and Version(pd.__version__) < Version("2.2"): + # float16 needs special handling for pandas < 2.2. + # Example from https://arrow.apache.org/docs/python/generated/pyarrow.float16.html + data = np.array(data, dtype=np.float16) + + series = pd.Series(data, dtype=dtype)[::2] # Not C-contiguous result = _to_numpy(series) _check_result(result, expected_dtype) npt.assert_array_equal(result, series) @@ -199,13 +225,30 @@ def test_to_numpy_pandas_series_pandas_dtypes_numeric(dtype, expected_dtype): pytest.param(pd.UInt64Dtype(), np.float64, id="UInt64"), pytest.param(pd.Float32Dtype(), np.float32, id="Float32"), pytest.param(pd.Float64Dtype(), np.float64, id="Float64"), + pytest.param("int8[pyarrow]", np.float64, id="int8[pyarrow]", **pa_marks), + pytest.param("int16[pyarrow]", np.float64, id="int16[pyarrow]", **pa_marks), + pytest.param("int32[pyarrow]", np.float64, id="int32[pyarrow]", **pa_marks), + pytest.param("int64[pyarrow]", np.float64, id="int64[pyarrow]", **pa_marks), + pytest.param("uint8[pyarrow]", np.float64, id="uint8[pyarrow]", **pa_marks), + pytest.param("uint16[pyarrow]", np.float64, id="uint16[pyarrow]", **pa_marks), + pytest.param("uint32[pyarrow]", np.float64, id="uint32[pyarrow]", **pa_marks), + pytest.param("uint64[pyarrow]", np.float64, id="uint64[pyarrow]", **pa_marks), + pytest.param("float16[pyarrow]", np.float16, id="float16[pyarrow]", **pa_marks), + pytest.param("float32[pyarrow]", np.float32, id="float32[pyarrow]", **pa_marks), + pytest.param("float64[pyarrow]", np.float64, id="float64[pyarrow]", **pa_marks), ], ) def test_to_numpy_pandas_series_pandas_dtypes_numeric_with_na(dtype, expected_dtype): """ - Test the _to_numpy function with pandas.Series of pandas numeric dtypes and NA. + Test the _to_numpy function with pandas.Series of pandas/PyArrow numeric dtypes and + missing values (NA). """ - series = pd.Series([1, 2, pd.NA, 4, 5, 6], dtype=dtype)[::2] # Not C-contiguous + data = [1.0, 2.0, None, 4.0, 5.0, 6.0] + if dtype == "float16[pyarrow]" and Version(pd.__version__) < Version("2.2"): + # float16 needs special handling for pandas < 2.2. + # Example from https://arrow.apache.org/docs/python/generated/pyarrow.float16.html + data = np.array(data, dtype=np.float16) + series = pd.Series(data, dtype=dtype)[::2] # Not C-contiguous assert series.isna().any() result = _to_numpy(series) _check_result(result, expected_dtype) From 6b0e8d016f220df34b6f0accccd2554bfbc00d9c Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 12 Nov 2024 08:54:30 +0800 Subject: [PATCH 16/28] Fix cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Yvonne Fröhlich <94163266+yvonnefroehlich@users.noreply.github.com> --- pygmt/clib/conversion.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pygmt/clib/conversion.py b/pygmt/clib/conversion.py index 5489f098096..5aa3764c6a5 100644 --- a/pygmt/clib/conversion.py +++ b/pygmt/clib/conversion.py @@ -163,13 +163,13 @@ def _to_numpy(data: Any) -> np.ndarray: } # pandas numeric dtypes were converted to np.object_ dtype prior pandas 2.2, and are - # converted to suitable numpy dtypes since pandas 2.2. Refer to the following link + # converted to suitable NumPy dtypes since pandas 2.2. Refer to the following link # for details: https://pandas.pydata.org/docs/whatsnew/v2.2.0.html#to-numpy-for-numpy-nullable-and-arrow-types-converts-to-suitable-numpy-dtype # Here are the workarounds for pandas < 2.2. # Following SPEC 0, pandas 2.1 should be dropped in 2025 Q3, so it's likely we can # remove the workaround in PyGMT v0.17.0. if Version(pd.__version__) < Version("2.2"): - # Specify mapping from pandas nullable dtypes to suitable numpy dtypes + # Specify mapping from pandas nullable dtypes to suitable NumPy dtypes dtypes.update( { "Int8": np.int8, @@ -184,7 +184,7 @@ def _to_numpy(data: Any) -> np.ndarray: "Float64": np.float64, } ) - # For pandas.Index/pandas.Series, pandas/pyarrow integer dtypes with missing + # For pandas.Index/pandas.Series, pandas/PyArrow integer dtypes with missing # values should be cast to NumPy float dtypes and NaN is used as missing value # indicator. if getattr(data, "hasnans", False): # pandas.Index/pandas.Series has 'hasnans' From 5aa094641e577fed4d229500a2678a748b7f0d81 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 12 Nov 2024 08:57:59 +0800 Subject: [PATCH 17/28] Merge all pandas-related tests into a single test --- pygmt/tests/test_clib_to_numpy.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index e77dd53ea4a..c81502318b3 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -159,20 +159,10 @@ def test_to_numpy_ndarray_numpy_dtypes_numeric(dtype, expected_dtype): # 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) - - @pytest.mark.parametrize( ("dtype", "expected_dtype"), [ + *np_dtype_params, pytest.param(pd.Int8Dtype(), np.int8, id="Int8"), pytest.param(pd.Int16Dtype(), np.int16, id="Int16"), pytest.param(pd.Int32Dtype(), np.int32, id="Int32"), @@ -196,16 +186,16 @@ def test_to_numpy_pandas_series_numpy_dtypes_numeric(dtype, expected_dtype): pytest.param("float64[pyarrow]", np.float64, id="float64[pyarrow]", **pa_marks), ], ) -def test_to_numpy_pandas_series_pandas_dtypes_numeric(dtype, expected_dtype): +def test_to_numpy_pandas_numeric(dtype, expected_dtype): """ - Test the _to_numpy function with pandas.Series of pandas/PyArrow numeric dtypes. + Test the _to_numpy function with pandas.Series of NumPy/pandas/PyArrow numeric + dtypes. """ data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0] if dtype == "float16[pyarrow]" and Version(pd.__version__) < Version("2.2"): # float16 needs special handling for pandas < 2.2. # Example from https://arrow.apache.org/docs/python/generated/pyarrow.float16.html data = np.array(data, dtype=np.float16) - series = pd.Series(data, dtype=dtype)[::2] # Not C-contiguous result = _to_numpy(series) _check_result(result, expected_dtype) From f182efd78a60423bb6e873bd1d956663d39fff91 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 12 Nov 2024 08:59:37 +0800 Subject: [PATCH 18/28] Shorten a few test names --- pygmt/tests/test_clib_to_numpy.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index c81502318b3..df0a0694106 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -104,7 +104,7 @@ def test_to_numpy_python_types_numeric(data, expected_dtype): @pytest.mark.parametrize(("dtype", "expected_dtype"), np_dtype_params) -def test_to_numpy_ndarray_numpy_dtypes_numeric(dtype, expected_dtype): +def test_to_numpy_numpy_numeric(dtype, expected_dtype): """ Test the _to_numpy function with NumPy arrays of NumPy numeric dtypes. @@ -228,7 +228,7 @@ def test_to_numpy_pandas_numeric(dtype, expected_dtype): pytest.param("float64[pyarrow]", np.float64, id="float64[pyarrow]", **pa_marks), ], ) -def test_to_numpy_pandas_series_pandas_dtypes_numeric_with_na(dtype, expected_dtype): +def test_to_numpy_pandas_numeric_with_na(dtype, expected_dtype): """ Test the _to_numpy function with pandas.Series of pandas/PyArrow numeric dtypes and missing values (NA). @@ -279,7 +279,7 @@ def test_to_numpy_pandas_series_pandas_dtypes_numeric_with_na(dtype, expected_dt pytest.param("float64", np.float64, id="float64"), ], ) -def test_to_numpy_pyarrow_array_pyarrow_dtypes_numeric(dtype, expected_dtype): +def test_to_numpy_pyarrow_numeric(dtype, expected_dtype): """ Test the _to_numpy function with PyArrow arrays of PyArrow numeric types. """ @@ -310,7 +310,7 @@ def test_to_numpy_pyarrow_array_pyarrow_dtypes_numeric(dtype, expected_dtype): pytest.param("float64", np.float64, id="float64"), ], ) -def test_to_numpy_pyarrow_array_pyarrow_dtypes_numeric_with_na(dtype, expected_dtype): +def test_to_numpy_pyarrow_numeric_with_na(dtype, expected_dtype): """ Test the _to_numpy function with PyArrow arrays of PyArrow numeric types and NA. """ From 0615b5b7c2ec2a9716ef5d64c0dbf0f44f3fd37d Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 12 Nov 2024 09:08:34 +0800 Subject: [PATCH 19/28] Test pandas.Series with numpy dtypes --- pygmt/tests/test_clib_to_numpy.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index df0a0694106..98808f24929 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -205,6 +205,10 @@ def test_to_numpy_pandas_numeric(dtype, expected_dtype): @pytest.mark.parametrize( ("dtype", "expected_dtype"), [ + 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(pd.Int8Dtype(), np.float64, id="Int8"), pytest.param(pd.Int16Dtype(), np.float64, id="Int16"), pytest.param(pd.Int32Dtype(), np.float64, id="Int32"), @@ -230,8 +234,8 @@ def test_to_numpy_pandas_numeric(dtype, expected_dtype): ) def test_to_numpy_pandas_numeric_with_na(dtype, expected_dtype): """ - Test the _to_numpy function with pandas.Series of pandas/PyArrow numeric dtypes and - missing values (NA). + Test the _to_numpy function with pandas.Series of NumPy/pandas/PyArrow numeric + dtypes and missing values (NA). """ data = [1.0, 2.0, None, 4.0, 5.0, 6.0] if dtype == "float16[pyarrow]" and Version(pd.__version__) < Version("2.2"): From 4061ec766f8e942bd1538cb50631ca3f258b63f5 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Tue, 12 Nov 2024 10:53:00 +0800 Subject: [PATCH 20/28] Refactor the workaround for pandas<2.2 --- pygmt/clib/conversion.py | 47 ++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/pygmt/clib/conversion.py b/pygmt/clib/conversion.py index 5aa3764c6a5..e7817083fb7 100644 --- a/pygmt/clib/conversion.py +++ b/pygmt/clib/conversion.py @@ -162,37 +162,28 @@ def _to_numpy(data: Any) -> np.ndarray: "date64[ms][pyarrow]": np.datetime64, } + # The expected numpy dtype for the result numpy array, but can be None. + dtype = dtypes.get(str(getattr(data, "dtype", None))) + + # Workarounds for pandas < 2.2. Following SPEC 0, pandas 2.1 should be dropped in + # 2025 Q3, so it's likely we can remove the workaround in PyGMT v0.17.0. + # # pandas numeric dtypes were converted to np.object_ dtype prior pandas 2.2, and are # converted to suitable NumPy dtypes since pandas 2.2. Refer to the following link # for details: https://pandas.pydata.org/docs/whatsnew/v2.2.0.html#to-numpy-for-numpy-nullable-and-arrow-types-converts-to-suitable-numpy-dtype - # Here are the workarounds for pandas < 2.2. - # Following SPEC 0, pandas 2.1 should be dropped in 2025 Q3, so it's likely we can - # remove the workaround in PyGMT v0.17.0. - if Version(pd.__version__) < Version("2.2"): - # Specify mapping from pandas nullable dtypes to suitable NumPy dtypes - dtypes.update( - { - "Int8": np.int8, - "Int16": np.int16, - "Int32": np.int32, - "Int64": np.int64, - "UInt8": np.uint8, - "UInt16": np.uint16, - "UInt32": np.uint32, - "UInt64": np.uint64, - "Float32": np.float32, - "Float64": np.float64, - } - ) - # For pandas.Index/pandas.Series, pandas/PyArrow integer dtypes with missing - # values should be cast to NumPy float dtypes and NaN is used as missing value - # indicator. - if getattr(data, "hasnans", False): # pandas.Index/pandas.Series has 'hasnans' - dtype = np.float64 if data.dtype.kind in "iu" else data.dtype.numpy_dtype - data = data.to_numpy(na_value=np.nan).astype(dtype=dtype) - - vec_dtype = str(getattr(data, "dtype", "")) - array = np.ascontiguousarray(data, dtype=dtypes.get(vec_dtype)) + if ( + Version(pd.__version__) < Version("2.2") + and hasattr(data, "dtype") + and hasattr(data.dtype, "numpy_dtype") + ): # pandas.Series/pandas.Index with pandas nullable dtypes. + dtype = data.dtype.numpy_dtype + if data.hasnans: + if data.dtype.kind in "iu": + # Integers with missing values are converted to float64 + dtype = np.float64 + data = data.to_numpy(na_value=np.nan) + + array = np.ascontiguousarray(data, dtype=dtype) return array From 0e0438b070295e2f1b5b63ddccf8f1d769ec2fa7 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Fri, 15 Nov 2024 13:13:30 +0800 Subject: [PATCH 21/28] Fix the workaround for pandas<2.2 --- pygmt/clib/conversion.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/pygmt/clib/conversion.py b/pygmt/clib/conversion.py index 390b731969d..177f127bc91 100644 --- a/pygmt/clib/conversion.py +++ b/pygmt/clib/conversion.py @@ -171,21 +171,22 @@ def _to_numpy(data: Any) -> np.ndarray: # The expected numpy dtype for the result numpy array, but can be None. dtype = dtypes.get(str(getattr(data, "dtype", getattr(data, "type", None)))) - # Workarounds for pandas < 2.2. Following SPEC 0, pandas 2.1 should be dropped in - # 2025 Q3, so it's likely we can remove the workaround in PyGMT v0.17.0. - # # pandas numeric dtypes were converted to np.object_ dtype prior pandas 2.2, and are # converted to suitable NumPy dtypes since pandas 2.2. Refer to the following link # for details: https://pandas.pydata.org/docs/whatsnew/v2.2.0.html#to-numpy-for-numpy-nullable-and-arrow-types-converts-to-suitable-numpy-dtype + # + # Workarounds for pandas < 2.2. Following SPEC 0, pandas 2.1 should be dropped in + # 2025 Q3, so it's likely we can remove the workaround in PyGMT v0.17.0. if ( - Version(pd.__version__) < Version("2.2") - and hasattr(data, "dtype") - and hasattr(data.dtype, "numpy_dtype") - ): # pandas.Series/pandas.Index with pandas nullable dtypes. - dtype = data.dtype.numpy_dtype - if data.hasnans: - if data.dtype.kind in "iu": - # Integers with missing values are converted to float64 + Version(pd.__version__) < Version("2.2") # pandas < 2.2 only. + and hasattr(data, "dtype") # NumPy array or pandas objects only. + and hasattr(data.dtype, "numpy_dtype") # pandas dtypes only. + and data.dtype.kind in "iuf" # Numeric dtypes only. + ): # pandas Series/Index with pandas nullable numeric dtypes. + dtype = data.dtype.numpy_dtype # The expected numpy dtype. + if getattr(data, "hasnans", False): + if dtype.kind in "iu": + # Integers with missing values are converted to float64. dtype = np.float64 data = data.to_numpy(na_value=np.nan) From 8883f3c27dad30416a6992c1fa0564f4b138276a Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Fri, 15 Nov 2024 13:22:46 +0800 Subject: [PATCH 22/28] Another fix for the workaround --- pygmt/clib/conversion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/clib/conversion.py b/pygmt/clib/conversion.py index 177f127bc91..1c63506f5bf 100644 --- a/pygmt/clib/conversion.py +++ b/pygmt/clib/conversion.py @@ -185,7 +185,7 @@ def _to_numpy(data: Any) -> np.ndarray: ): # pandas Series/Index with pandas nullable numeric dtypes. dtype = data.dtype.numpy_dtype # The expected numpy dtype. if getattr(data, "hasnans", False): - if dtype.kind in "iu": + if data.dtype.kind in "iu": # Integers with missing values are converted to float64. dtype = np.float64 data = data.to_numpy(na_value=np.nan) From afeaa3858546717d2b0be9253eccba73e586f026 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Fri, 15 Nov 2024 13:31:45 +0800 Subject: [PATCH 23/28] dtype defaults to an empty string, rather than None --- pygmt/clib/conversion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/clib/conversion.py b/pygmt/clib/conversion.py index 1c63506f5bf..2967d982336 100644 --- a/pygmt/clib/conversion.py +++ b/pygmt/clib/conversion.py @@ -169,7 +169,7 @@ def _to_numpy(data: Any) -> np.ndarray: } # The expected numpy dtype for the result numpy array, but can be None. - dtype = dtypes.get(str(getattr(data, "dtype", getattr(data, "type", None)))) + dtype = dtypes.get(str(getattr(data, "dtype", getattr(data, "type", "")))) # pandas numeric dtypes were converted to np.object_ dtype prior pandas 2.2, and are # converted to suitable NumPy dtypes since pandas 2.2. Refer to the following link From d3f3e5d1f9544df970991c3b05dab398e30cea30 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Fri, 15 Nov 2024 15:32:26 +0800 Subject: [PATCH 24/28] Shortern more test names --- pygmt/tests/test_clib_to_numpy.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index 16ff9b2ba70..a8067c42d49 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -128,7 +128,7 @@ def test_to_numpy_numpy_numeric(dtype, expected_dtype): @pytest.mark.parametrize("dtype", [None, np.str_, "U10"]) -def test_to_numpy_ndarray_numpy_dtypes_string(dtype): +def test_to_numpy_numpy_string(dtype): """ Test the _to_numpy function with NumPy arrays of string types. """ @@ -273,7 +273,7 @@ def test_to_numpy_pandas_numeric_with_na(dtype, expected_dtype): pytest.param("string[pyarrow_numpy]", marks=skip_if_no(package="pyarrow")), ], ) -def test_to_numpy_pandas_series_pandas_dtypes_string(dtype): +def test_to_numpy_pandas_string(dtype): """ Test the _to_numpy function with pandas.Series of pandas string types. @@ -295,7 +295,7 @@ def test_to_numpy_pandas_series_pandas_dtypes_string(dtype): pytest.param("date64[ms][pyarrow]", "datetime64[ms]", id="date64[ms]"), ], ) -def test_to_numpy_pandas_series_pyarrow_dtypes_date(dtype, expected_dtype): +def test_to_numpy_pandas_date(dtype, expected_dtype): """ Test the _to_numpy function with pandas.Series of PyArrow date32/date64 types. """ @@ -404,7 +404,7 @@ def test_to_numpy_pyarrow_numeric_with_na(dtype, expected_dtype): "string_view", ], ) -def test_to_numpy_pyarrow_array_pyarrow_dtypes_string(dtype): +def test_to_numpy_pyarrow_string(dtype): """ Test the _to_numpy function with PyArrow arrays of PyArrow string types. """ @@ -422,7 +422,7 @@ def test_to_numpy_pyarrow_array_pyarrow_dtypes_string(dtype): pytest.param("date64[ms]", "datetime64[ms]", id="date64[ms]"), ], ) -def test_to_numpy_pyarrow_array_pyarrow_dtypes_date(dtype, expected_dtype): +def test_to_numpy_pyarrow_date(dtype, expected_dtype): """ Test the _to_numpy function with PyArrow arrays of PyArrow date types. From 80fb7a22f2766c8bb188c7c2a655acf32f3f3948 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 28 Nov 2024 18:49:43 +0800 Subject: [PATCH 25/28] Revert "Shortern more test names" This reverts commit d3f3e5d1f9544df970991c3b05dab398e30cea30. --- pygmt/tests/test_clib_to_numpy.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index deafa68214f..0fac06cc2f8 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -128,7 +128,7 @@ def test_to_numpy_numpy_numeric(dtype, expected_dtype): @pytest.mark.parametrize("dtype", [None, np.str_, "U10"]) -def test_to_numpy_numpy_string(dtype): +def test_to_numpy_ndarray_numpy_dtypes_string(dtype): """ Test the _to_numpy function with NumPy arrays of string types. """ @@ -282,7 +282,7 @@ def test_to_numpy_pandas_numeric_with_na(dtype, expected_dtype): ), ], ) -def test_to_numpy_pandas_string(dtype): +def test_to_numpy_pandas_series_pandas_dtypes_string(dtype): """ Test the _to_numpy function with pandas.Series of pandas string types. @@ -304,7 +304,7 @@ def test_to_numpy_pandas_string(dtype): pytest.param("date64[ms][pyarrow]", "datetime64[ms]", id="date64[ms]"), ], ) -def test_to_numpy_pandas_date(dtype, expected_dtype): +def test_to_numpy_pandas_series_pyarrow_dtypes_date(dtype, expected_dtype): """ Test the _to_numpy function with pandas.Series of PyArrow date32/date64 types. """ @@ -413,7 +413,7 @@ def test_to_numpy_pyarrow_numeric_with_na(dtype, expected_dtype): "string_view", ], ) -def test_to_numpy_pyarrow_string(dtype): +def test_to_numpy_pyarrow_array_pyarrow_dtypes_string(dtype): """ Test the _to_numpy function with PyArrow arrays of PyArrow string types. """ @@ -431,7 +431,7 @@ def test_to_numpy_pyarrow_string(dtype): pytest.param("date64[ms]", "datetime64[ms]", id="date64[ms]"), ], ) -def test_to_numpy_pyarrow_date(dtype, expected_dtype): +def test_to_numpy_pyarrow_array_pyarrow_dtypes_date(dtype, expected_dtype): """ Test the _to_numpy function with PyArrow arrays of PyArrow date types. From 20c447b49f9646b0c6d4c511a28347c9a7ccce2e Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 28 Nov 2024 18:50:34 +0800 Subject: [PATCH 26/28] Revert "Shorten a few test names" This reverts commit f182efd78a60423bb6e873bd1d956663d39fff91. --- pygmt/tests/test_clib_to_numpy.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index ad2134d9f39..83510c95c86 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -118,7 +118,7 @@ def test_to_numpy_python_types(data, expected_dtype): @pytest.mark.parametrize(("dtype", "expected_dtype"), np_dtype_params) -def test_to_numpy_numpy_numeric(dtype, expected_dtype): +def test_to_numpy_ndarray_numpy_dtypes_numeric(dtype, expected_dtype): """ Test the _to_numpy function with NumPy arrays of NumPy numeric dtypes. @@ -257,7 +257,7 @@ def test_to_numpy_pandas_numeric(dtype, expected_dtype): pytest.param("float64[pyarrow]", np.float64, id="float64[pyarrow]", **pa_marks), ], ) -def test_to_numpy_pandas_numeric_with_na(dtype, expected_dtype): +def test_to_numpy_pandas_series_pandas_dtypes_numeric_with_na(dtype, expected_dtype): """ Test the _to_numpy function with pandas.Series of NumPy/pandas/PyArrow numeric dtypes and missing values (NA). @@ -369,7 +369,7 @@ def test_to_numpy_pandas_series_pyarrow_dtypes_date(dtype, expected_dtype): pytest.param("float64", np.float64, id="float64"), ], ) -def test_to_numpy_pyarrow_numeric(dtype, expected_dtype): +def test_to_numpy_pyarrow_array_pyarrow_dtypes_numeric(dtype, expected_dtype): """ Test the _to_numpy function with PyArrow arrays of PyArrow numeric types. """ @@ -400,7 +400,7 @@ def test_to_numpy_pyarrow_numeric(dtype, expected_dtype): pytest.param("float64", np.float64, id="float64"), ], ) -def test_to_numpy_pyarrow_numeric_with_na(dtype, expected_dtype): +def test_to_numpy_pyarrow_array_pyarrow_dtypes_numeric_with_na(dtype, expected_dtype): """ Test the _to_numpy function with PyArrow arrays of PyArrow numeric types and NA. """ From bb1cb0733110b0ca8643fbe472fe8f7bf280aa50 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Fri, 29 Nov 2024 00:58:43 +0800 Subject: [PATCH 27/28] Fix test name --- pygmt/tests/test_clib_to_numpy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index 69572e4c3db..e8a3eb055c4 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -256,7 +256,7 @@ def test_to_numpy_pandas_numeric(dtype, expected_dtype): pytest.param("float64[pyarrow]", np.float64, id="float64[pyarrow]", **pa_marks), ], ) -def test_to_numpy_pandas_series_pandas_dtypes_numeric_with_na(dtype, expected_dtype): +def test_to_numpy_pandas_numeric_with_na(dtype, expected_dtype): """ Test the _to_numpy function with pandas.Series of NumPy/pandas/PyArrow numeric dtypes and missing values (NA). From 28fa95438f490c2dbbaa80210959fc030436a62b Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 2 Dec 2024 23:13:07 +0800 Subject: [PATCH 28/28] Separate variable 'dtype' and 'numpy_dtype' for the input and result array --- pygmt/clib/conversion.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pygmt/clib/conversion.py b/pygmt/clib/conversion.py index ee1855b9537..5a1d1cf51b9 100644 --- a/pygmt/clib/conversion.py +++ b/pygmt/clib/conversion.py @@ -168,8 +168,10 @@ def _to_numpy(data: Any) -> np.ndarray: "date64[ms][pyarrow]": "datetime64[ms]", } - # The expected numpy dtype for the result numpy array, but can be None. - dtype = dtypes.get(str(getattr(data, "dtype", getattr(data, "type", "")))) + # The dtype for the input object. + dtype = getattr(data, "dtype", getattr(data, "type", "")) + # The numpy dtype for the result numpy array, but can be None. + numpy_dtype = dtypes.get(str(dtype)) # pandas numeric dtypes were converted to np.object_ dtype prior pandas 2.2, and are # converted to suitable NumPy dtypes since pandas 2.2. Refer to the following link @@ -183,20 +185,20 @@ def _to_numpy(data: Any) -> np.ndarray: and hasattr(data.dtype, "numpy_dtype") # pandas dtypes only. and data.dtype.kind in "iuf" # Numeric dtypes only. ): # pandas Series/Index with pandas nullable numeric dtypes. - dtype = data.dtype.numpy_dtype # The expected numpy dtype. + # The numpy dtype of the result numpy array. + numpy_dtype = data.dtype.numpy_dtype if getattr(data, "hasnans", False): if data.dtype.kind in "iu": # Integers with missing values are converted to float64. - dtype = np.float64 + numpy_dtype = np.float64 data = data.to_numpy(na_value=np.nan) - array = np.ascontiguousarray(data, dtype=dtype) + array = np.ascontiguousarray(data, dtype=numpy_dtype) # Check if a np.object_ array can be converted to np.str_. if array.dtype == np.object_: with contextlib.suppress(TypeError, ValueError): return np.ascontiguousarray(array, dtype=np.str_) - return array