5
5
import ctypes as ctp
6
6
import warnings
7
7
from collections .abc import Sequence
8
+ from typing import Any
8
9
9
10
import numpy as np
11
+ import pandas as pd
12
+ import xarray as xr
13
+ from packaging .version import Version
10
14
from pygmt .exceptions import GMTInvalidInput
11
15
12
16
13
- def dataarray_to_matrix (grid ):
17
+ def dataarray_to_matrix (
18
+ grid : xr .DataArray ,
19
+ ) -> tuple [np .ndarray , list [float ], list [float ]]:
14
20
"""
15
- Transform an xarray.DataArray into a data 2-D array and metadata.
21
+ Transform an xarray.DataArray into a 2-D numpy array and metadata.
16
22
17
- Use this to extract the underlying numpy array of data and the region and
18
- increment for the grid.
23
+ Use this to extract the underlying numpy array of data and the region and increment
24
+ for the grid.
19
25
20
- Only allows grids with two dimensions and constant grid spacing (GMT
21
- doesn't allow variable grid spacing ). If the latitude and/or longitude
22
- increments of the input grid are negative, the output matrix will be
23
- sorted by the DataArray coordinates to yield positive increments.
26
+ Only allows grids with two dimensions and constant grid spacings (GMT doesn't allow
27
+ variable grid spacings ). If the latitude and/or longitude increments of the input
28
+ grid are negative, the output matrix will be sorted by the DataArray coordinates to
29
+ yield positive increments.
24
30
25
- If the underlying data array is not C contiguous, for example if it's a
26
- slice of a larger grid, a copy will need to be generated.
31
+ If the underlying data array is not C contiguous, for example, if it's a slice of a
32
+ larger grid, a copy will need to be generated.
27
33
28
34
Parameters
29
35
----------
30
- grid : xarray.DataArray
31
- The input grid as a DataArray instance. Information is retrieved from
32
- the coordinate arrays, not from headers.
36
+ grid
37
+ The input grid as a DataArray instance. Information is retrieved from the
38
+ coordinate arrays, not from headers.
33
39
34
40
Returns
35
41
-------
36
- matrix : 2-D array
42
+ matrix
37
43
The 2-D array of data from the grid.
38
- region : list
44
+ region
39
45
The West, East, South, North boundaries of the grid.
40
- inc : list
46
+ inc
41
47
The grid spacing in East-West and North-South, respectively.
42
48
43
49
Raises
@@ -62,8 +68,8 @@ def dataarray_to_matrix(grid):
62
68
(180, 360)
63
69
>>> matrix.flags.c_contiguous
64
70
True
65
- >>> # Using a slice of the grid, the matrix will be copied to guarantee
66
- >>> # that it's C-contiguous in memory. The increment should be unchanged.
71
+ >>> # Using a slice of the grid, the matrix will be copied to guarantee that it's
72
+ >>> # C-contiguous in memory. The increment should be unchanged.
67
73
>>> matrix, region, inc = dataarray_to_matrix(grid[10:41, 30:101])
68
74
>>> matrix.flags.c_contiguous
69
75
True
@@ -73,7 +79,7 @@ def dataarray_to_matrix(grid):
73
79
[-150.0, -79.0, -80.0, -49.0]
74
80
>>> print(inc)
75
81
[1.0, 1.0]
76
- >>> # but not if only taking every other grid point.
82
+ >>> # The increment should change accordingly if taking every other grid point.
77
83
>>> matrix, region, inc = dataarray_to_matrix(grid[10:41:2, 30:101:2])
78
84
>>> matrix.flags.c_contiguous
79
85
True
@@ -85,21 +91,19 @@ def dataarray_to_matrix(grid):
85
91
[2.0, 2.0]
86
92
"""
87
93
if len (grid .dims ) != 2 :
88
- raise GMTInvalidInput (
89
- f"Invalid number of grid dimensions ' { len ( grid . dims ) } '. Must be 2."
90
- )
94
+ msg = f"Invalid number of grid dimensions 'len( { grid . dims } )'. Must be 2."
95
+ raise GMTInvalidInput ( msg )
96
+
91
97
# Extract region and inc from the grid
92
- region = []
93
- inc = []
94
- # Reverse the dims because it is rows, columns ordered. In geographic
95
- # grids, this would be North-South, East-West. GMT's region and inc are
96
- # East-West, North-South.
98
+ region , inc = [], []
99
+ # Reverse the dims because it is rows, columns ordered. In geographic grids, this
100
+ # would be North-South, East-West. GMT's region and inc are East-West, North-South.
97
101
for dim in grid .dims [::- 1 ]:
98
102
coord = grid .coords [dim ].to_numpy ()
99
- coord_incs = coord [1 :] - coord [0 :- 1 ]
103
+ coord_incs = coord [1 :] - coord [:- 1 ]
100
104
coord_inc = coord_incs [0 ]
101
105
if not np .allclose (coord_incs , coord_inc ):
102
- # calculate the increment if irregular spacing is found
106
+ # Calculate the increment if irregular spacing is found.
103
107
coord_inc = (coord [- 1 ] - coord [0 ]) / (coord .size - 1 )
104
108
msg = (
105
109
f"Grid may have irregular spacing in the '{ dim } ' dimension, "
@@ -108,9 +112,8 @@ def dataarray_to_matrix(grid):
108
112
)
109
113
warnings .warn (msg , category = RuntimeWarning , stacklevel = 2 )
110
114
if coord_inc == 0 :
111
- raise GMTInvalidInput (
112
- f"Grid has a zero increment in the '{ dim } ' dimension."
113
- )
115
+ msg = f"Grid has a zero increment in the '{ dim } ' dimension."
116
+ raise GMTInvalidInput (msg )
114
117
region .extend (
115
118
[
116
119
coord .min () - coord_inc / 2 * grid .gmt .registration ,
@@ -129,26 +132,25 @@ def dataarray_to_matrix(grid):
129
132
return matrix , region , inc
130
133
131
134
132
- def vectors_to_arrays (vectors ) :
135
+ def vectors_to_arrays (vectors : Sequence [ Any ]) -> list [ np . ndarray ] :
133
136
"""
134
- Convert 1-D vectors (lists, arrays , or pandas.Series ) to C contiguous 1-D arrays.
137
+ Convert 1-D vectors (scalars, lists , or array-like ) to C contiguous 1-D arrays.
135
138
136
- Arrays must be in C contiguous order for us to pass their memory pointers
137
- to GMT. If any are not, convert them to C order (which requires copying the
138
- memory). This usually happens when vectors are columns of a 2-D array or
139
- have been sliced.
139
+ Arrays must be in C contiguous order for us to pass their memory pointers to GMT.
140
+ If any are not, convert them to C order (which requires copying the memory). This
141
+ usually happens when vectors are columns of a 2-D array or have been sliced.
140
142
141
- If a vector is a list or pandas.Series, get the underlying numpy array .
143
+ The returned arrays are guaranteed to be C contiguous and at least 1-D .
142
144
143
145
Parameters
144
146
----------
145
- vectors : list of lists, 1-D arrays, or pandas.Series
147
+ vectors
146
148
The vectors that must be converted.
147
149
148
150
Returns
149
151
-------
150
- arrays : list of 1-D arrays
151
- The converted numpy arrays
152
+ arrays
153
+ List of converted numpy arrays.
152
154
153
155
Examples
154
156
--------
@@ -178,6 +180,10 @@ def vectors_to_arrays(vectors):
178
180
>>> [i.ndim for i in data] # Check that they are 1-D arrays
179
181
[1, 1, 1]
180
182
183
+ >>> series = pd.Series(data=[0, 4, pd.NA, 8, 6], dtype=pd.Int32Dtype())
184
+ >>> vectors_to_arrays([series])
185
+ [array([ 0., 4., nan, 8., 6.])]
186
+
181
187
>>> import datetime
182
188
>>> import pytest
183
189
>>> pa = pytest.importorskip("pyarrow")
@@ -205,8 +211,20 @@ def vectors_to_arrays(vectors):
205
211
}
206
212
arrays = []
207
213
for vector in vectors :
208
- vec_dtype = str (getattr (vector , "dtype" , "" ))
209
- arrays .append (np .ascontiguousarray (vector , dtype = dtypes .get (vec_dtype )))
214
+ if (
215
+ hasattr (vector , "isna" )
216
+ and vector .isna ().any ()
217
+ and Version (pd .__version__ ) < Version ("2.2" )
218
+ ):
219
+ # Workaround for dealing with pd.NA with pandas < 2.2.
220
+ # Bug report at: https://github.com/GenericMappingTools/pygmt/issues/2844
221
+ # Following SPEC0, pandas 2.1 will be dropped in 2025 Q3, so it's likely
222
+ # we can remove the workaround in PyGMT v0.17.0.
223
+ array = np .ascontiguousarray (vector .astype (float ))
224
+ else :
225
+ vec_dtype = str (getattr (vector , "dtype" , "" ))
226
+ array = np .ascontiguousarray (vector , dtype = dtypes .get (vec_dtype ))
227
+ arrays .append (array )
210
228
return arrays
211
229
212
230
@@ -289,16 +307,15 @@ def strings_to_ctypes_array(strings: Sequence[str]) -> ctp.Array:
289
307
return (ctp .c_char_p * len (strings ))(* [s .encode () for s in strings ])
290
308
291
309
292
- def array_to_datetime (array ) :
310
+ def array_to_datetime (array : Sequence [ Any ]) -> np . ndarray :
293
311
"""
294
312
Convert a 1-D datetime array from various types into numpy.datetime64.
295
313
296
- If the input array is not in legal datetime formats, raise a ValueError
297
- exception.
314
+ If the input array is not in legal datetime formats, raise a ValueError exception.
298
315
299
316
Parameters
300
317
----------
301
- array : list or 1-D array
318
+ array
302
319
The input datetime array in various formats.
303
320
304
321
Supported types:
@@ -310,7 +327,8 @@ def array_to_datetime(array):
310
327
311
328
Returns
312
329
-------
313
- array : 1-D datetime array in numpy.datetime64
330
+ array
331
+ 1-D datetime array in numpy.datetime64.
314
332
315
333
Raises
316
334
------
0 commit comments