From 4d9f8970e8b66d564d5d75f6505cb36f887754df Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 12 Dec 2024 09:28:33 +0800 Subject: [PATCH] Improve the workarounds for handling pandas nullable dtypes in pandas<=2.1 (#3596) --- pygmt/clib/conversion.py | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/pygmt/clib/conversion.py b/pygmt/clib/conversion.py index b093109cf32..5a1d1cf51b9 100644 --- a/pygmt/clib/conversion.py +++ b/pygmt/clib/conversion.py @@ -168,19 +168,32 @@ def _to_numpy(data: Any) -> np.ndarray: "date64[ms][pyarrow]": "datetime64[ms]", } + # 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 + # 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 ( - 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", getattr(data, "type", ""))) - array = np.ascontiguousarray(data, dtype=dtypes.get(vec_dtype)) + 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. + # 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. + numpy_dtype = np.float64 + data = data.to_numpy(na_value=np.nan) + + array = np.ascontiguousarray(data, dtype=numpy_dtype) # Check if a np.object_ array can be converted to np.str_. if array.dtype == np.object_: