From 949b1241d3d055f02573ca49107a3eba476a42e7 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Wed, 6 Nov 2024 17:27:44 +0800 Subject: [PATCH 1/5] clib.conversion._to_numpy: Add tests for arrays with Python built-in datetimes or ISO-strings --- pygmt/clib/conversion.py | 5 +++++ pygmt/tests/test_clib_to_numpy.py | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/pygmt/clib/conversion.py b/pygmt/clib/conversion.py index 5a1d1cf51b9..40cbeacadf4 100644 --- a/pygmt/clib/conversion.py +++ b/pygmt/clib/conversion.py @@ -195,6 +195,11 @@ def _to_numpy(data: Any) -> np.ndarray: array = np.ascontiguousarray(data, dtype=numpy_dtype) + # Check if a np.object_ or np.str_ array can be converted to np.datetime64. + if array.dtype.type in {np.object_, np.str_}: + with contextlib.suppress(TypeError, ValueError): + return np.ascontiguousarray(array, dtype=np.datetime64) + # Check if a np.object_ array can be converted to np.str_. if array.dtype == np.object_: with contextlib.suppress(TypeError, ValueError): diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index 29fc50826ab..553f6060d6c 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -79,6 +79,30 @@ def test_to_numpy_python_types(data, expected_dtype): npt.assert_array_equal(result, data) +@pytest.mark.parametrize( + "data", + [ + pytest.param( + ["2018", "2018-02", "2018-03-01", "2018-04-01T01:02:03"], id="iso8601" + ), + pytest.param( + [datetime.date(2018, 1, 1), datetime.datetime(2019, 1, 1)], + id="datetime", + ), + pytest.param( + ["2018-01-01", np.datetime64("2018-01-01"), datetime.datetime(2018, 1, 1)], + id="mixed", + ), + ], +) +def test_to_numpy_python_datetime(data): + """ + Test the _to_numpy function with Python built-in datetime types. + """ + result = _to_numpy(data) + assert result.dtype.type == np.datetime64 + + ######################################################################################## # Test the _to_numpy function with NumPy arrays. # From 0eba5d2d2f998ae6dcfc0fad590f8e094fbc40a2 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 9 Jan 2025 10:05:10 +0800 Subject: [PATCH 2/5] Add more tests by Wei Ji Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com> --- pygmt/tests/test_clib_to_numpy.py | 48 ++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index 1ba0c68f87b..22d823d4d02 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -2,8 +2,8 @@ Tests for the _to_numpy function in the clib.conversion module. """ +import datetime import sys -from datetime import date, datetime import numpy as np import numpy.testing as npt @@ -87,11 +87,30 @@ def test_to_numpy_python_types(data, expected_dtype): ["2018", "2018-02", "2018-03-01", "2018-04-01T01:02:03"], id="iso8601" ), pytest.param( - [datetime.date(2018, 1, 1), datetime.datetime(2019, 1, 1)], + [ + datetime.date(2018, 1, 1), + datetime.datetime(2018, 2, 1), + datetime.date(2018, 3, 1), + datetime.datetime(2018, 4, 1, 1, 2, 3), + ], id="datetime", ), pytest.param( - ["2018-01-01", np.datetime64("2018-01-01"), datetime.datetime(2018, 1, 1)], + [ + pd.Timestamp("2018-01-01"), + pd.Timestamp("2018-02-01"), + pd.Timestamp("2018-03-01"), + pd.Timestamp("2018-04-01T01:02:03"), + ], + id="pd_timestamp", + ), + pytest.param( + [ + "2018-01-01", + np.datetime64("2018-02-01"), + datetime.datetime(2018, 3, 1), + pd.Timestamp("2018-04-01T01:02:03"), + ], id="mixed", ), ], @@ -102,6 +121,18 @@ def test_to_numpy_python_datetime(data): """ result = _to_numpy(data) assert result.dtype.type == np.datetime64 + npt.assert_array_equal( + result, + np.array( + [ + "2018-01-01T00:00:00", + "2018-02-01T00:00:00", + "2018-03-01T00:00:00", + "2018-04-01T01:02:03", + ], + dtype="datetime64[s]", + ), + ) ######################################################################################## @@ -520,9 +551,9 @@ def test_to_numpy_pyarrow_date(dtype, expected_dtype): Here we explicitly check the dtype and date unit of the result. """ data = [ - date(2024, 1, 1), - datetime(2024, 1, 2), - datetime(2024, 1, 3), + datetime.date(2024, 1, 1), + datetime.datetime(2024, 1, 2), + datetime.datetime(2024, 1, 3), ] array = pa.array(data, type=dtype) result = _to_numpy(array) @@ -566,7 +597,10 @@ def test_to_numpy_pyarrow_timestamp(dtype, expected_dtype): Reference: https://arrow.apache.org/docs/python/generated/pyarrow.timestamp.html """ - data = [datetime(2024, 1, 2, 3, 4, 5), datetime(2024, 1, 2, 3, 4, 6)] + data = [ + datetime.datetime(2024, 1, 2, 3, 4, 5), + datetime.datetime(2024, 1, 2, 3, 4, 6), + ] array = pa.array(data, type=dtype) result = _to_numpy(array) _check_result(result, np.datetime64) From 3aa52332155da96d8295b54d1295789ccee7ecb4 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 9 Jan 2025 10:41:04 +0800 Subject: [PATCH 3/5] Add tests for numpy datetime64 --- pygmt/tests/test_clib_to_numpy.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pygmt/tests/test_clib_to_numpy.py b/pygmt/tests/test_clib_to_numpy.py index 18d635ef206..40b45e466d8 100644 --- a/pygmt/tests/test_clib_to_numpy.py +++ b/pygmt/tests/test_clib_to_numpy.py @@ -95,6 +95,15 @@ def test_to_numpy_python_types(data, expected_dtype): ], id="datetime", ), + pytest.param( + [ + np.datetime64("2018"), + np.datetime64("2018-02"), + np.datetime64("2018-03-01"), + np.datetime64("2018-04-01T01:02:03"), + ], + id="np_datetime64", + ), pytest.param( [ pd.Timestamp("2018-01-01"), @@ -117,7 +126,7 @@ def test_to_numpy_python_types(data, expected_dtype): ) def test_to_numpy_python_datetime(data): """ - Test the _to_numpy function with Python built-in datetime types. + Test the _to_numpy function with Python sequence of datetime types. """ result = _to_numpy(data) assert result.dtype.type == np.datetime64 From faaa45cb2a03982b8878519fbaefd61940c970c4 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 9 Jan 2025 19:12:47 +0800 Subject: [PATCH 4/5] Convert unrecognized objects to datetime before converting to string dtype --- pygmt/clib/conversion.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pygmt/clib/conversion.py b/pygmt/clib/conversion.py index 52eec0d2479..7823aa32103 100644 --- a/pygmt/clib/conversion.py +++ b/pygmt/clib/conversion.py @@ -205,6 +205,11 @@ def _to_numpy(data: Any) -> np.ndarray: array = np.ascontiguousarray(data, dtype=numpy_dtype) + # Check if a np.object_ or np.str_ array can be converted to np.datetime64. + if array.dtype.type in {np.object_, np.str_}: + with contextlib.suppress(TypeError, ValueError): + return np.ascontiguousarray(array, dtype=np.datetime64) + # Check if a np.object_ array can be converted to np.str_. if array.dtype == np.object_: with contextlib.suppress(TypeError, ValueError): From 892f44449fd8113f501be0bf65a24be5f3108e2d Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Fri, 10 Jan 2025 09:20:09 +0800 Subject: [PATCH 5/5] Add tests for sequence of pd.Timestamp --- pygmt/tests/baseline/test_plot_datetime.png.dvc | 4 ++-- pygmt/tests/test_plot.py | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pygmt/tests/baseline/test_plot_datetime.png.dvc b/pygmt/tests/baseline/test_plot_datetime.png.dvc index 714104995ba..1450b29ef82 100644 --- a/pygmt/tests/baseline/test_plot_datetime.png.dvc +++ b/pygmt/tests/baseline/test_plot_datetime.png.dvc @@ -1,5 +1,5 @@ outs: -- md5: 583947facaa873122f0bf18137809cd4 - size: 12695 +- md5: 0a2eae0da1e3d5b71d7392de1c081346 + size: 13124 path: test_plot_datetime.png hash: md5 diff --git a/pygmt/tests/test_plot.py b/pygmt/tests/test_plot.py index 721b7841307..c2f2b846724 100644 --- a/pygmt/tests/test_plot.py +++ b/pygmt/tests/test_plot.py @@ -467,9 +467,14 @@ def test_plot_datetime(): fig.plot(x=x, y=y, style="a0.2c", pen="1p") # the Python built-in datetime and date - x = [datetime.date(2018, 1, 1), datetime.datetime(2019, 1, 1)] + x = [datetime.date(2018, 1, 1), datetime.datetime(2019, 1, 1, 0, 0, 0)] y = [8.5, 9.5] fig.plot(x=x, y=y, style="i0.2c", pen="1p") + + # Python sequence of pd.Timestamp + x = [pd.Timestamp("2018-01-01"), pd.Timestamp("2019-01-01")] + y = [5.5, 6.5] + fig.plot(x=x, y=y, style="d0.2c", pen="1p") return fig