15
15
from pygmt .exceptions import GMTInvalidInput
16
16
17
17
18
- def _validate_data_input (
19
- data = None , x = None , y = None , z = None , required_z = False , required_data = True , kind = None
18
+ def validate_data_input (
19
+ data = None , vectors = None , ncols = 2 , required_data = True , kind = None
20
20
):
21
21
"""
22
- Check if the combination of data/x/y/z is valid.
22
+ Check if the data input is valid.
23
23
24
24
Examples
25
25
--------
26
- >>> _validate_data_input (data="infile")
27
- >>> _validate_data_input(x=[ 1, 2, 3], y= [4, 5, 6])
28
- >>> _validate_data_input(x=[ 1, 2, 3], y= [4, 5, 6], z= [7, 8, 9])
29
- >>> _validate_data_input (data=None, required_data=False)
30
- >>> _validate_data_input ()
26
+ >>> validate_data_input (data="infile")
27
+ >>> validate_data_input(vectors=[[ 1, 2, 3], [4, 5, 6]], ncols=2 )
28
+ >>> validate_data_input(vectors=[[ 1, 2, 3], [4, 5, 6], [7, 8, 9]], ncols=3 )
29
+ >>> validate_data_input (data=None, required_data=False)
30
+ >>> validate_data_input ()
31
31
Traceback (most recent call last):
32
32
...
33
33
pygmt.exceptions.GMTInvalidInput: No input data provided.
34
- >>> _validate_data_input(x=[ 1, 2, 3])
34
+ >>> validate_data_input(vectors=[[ 1, 2, 3], None], ncols=2 )
35
35
Traceback (most recent call last):
36
36
...
37
- pygmt.exceptions.GMTInvalidInput: Must provide both x and y .
38
- >>> _validate_data_input(y=[ 4, 5, 6])
37
+ pygmt.exceptions.GMTInvalidInput: The 'y' column can't be None .
38
+ >>> validate_data_input(vectors=[None, [ 4, 5, 6]], ncols=2 )
39
39
Traceback (most recent call last):
40
40
...
41
- pygmt.exceptions.GMTInvalidInput: Must provide both x and y .
42
- >>> _validate_data_input(x=[ 1, 2, 3], y= [4, 5, 6], required_z=True )
41
+ pygmt.exceptions.GMTInvalidInput: The 'x' column can't be None .
42
+ >>> validate_data_input(vectors=[[ 1, 2, 3], [4, 5, 6], None], ncols=3 )
43
43
Traceback (most recent call last):
44
44
...
45
- pygmt.exceptions.GMTInvalidInput: Must provide x, y, and z .
45
+ pygmt.exceptions.GMTInvalidInput: The 'z' column can't be None .
46
46
>>> import numpy as np
47
47
>>> import pandas as pd
48
48
>>> import xarray as xr
49
49
>>> data = np.arange(8).reshape((4, 2))
50
- >>> _validate_data_input (data=data, required_z=True , kind="matrix")
50
+ >>> validate_data_input (data=data, ncols=3 , kind="matrix")
51
51
Traceback (most recent call last):
52
52
...
53
- pygmt.exceptions.GMTInvalidInput: data must provide x, y, and z columns.
54
- >>> _validate_data_input (
53
+ pygmt.exceptions.GMTInvalidInput: data must have at least 3 columns.
54
+ >>> validate_data_input (
55
55
... data=pd.DataFrame(data, columns=["x", "y"]),
56
- ... required_z=True ,
56
+ ... ncols=3 ,
57
57
... kind="matrix",
58
58
... )
59
59
Traceback (most recent call last):
60
60
...
61
- pygmt.exceptions.GMTInvalidInput: data must provide x, y, and z columns.
62
- >>> _validate_data_input (
61
+ pygmt.exceptions.GMTInvalidInput: data must have at least 3 columns.
62
+ >>> validate_data_input (
63
63
... data=xr.Dataset(pd.DataFrame(data, columns=["x", "y"])),
64
- ... required_z=True ,
64
+ ... ncols=3 ,
65
65
... kind="matrix",
66
66
... )
67
67
Traceback (most recent call last):
68
68
...
69
- pygmt.exceptions.GMTInvalidInput: data must provide x, y, and z columns.
70
- >>> _validate_data_input (data="infile", x=[ 1, 2, 3])
69
+ pygmt.exceptions.GMTInvalidInput: data must have at least 3 columns.
70
+ >>> validate_data_input (data="infile", vectors=[[ 1, 2, 3], None ])
71
71
Traceback (most recent call last):
72
72
...
73
- pygmt.exceptions.GMTInvalidInput: Too much data. Use either data or x/y/z.
74
- >>> _validate_data_input (data="infile", y=[ 4, 5, 6])
73
+ pygmt.exceptions.GMTInvalidInput: Too much data. Pass in either ' data' or 1-D arrays. # noqa: W505
74
+ >>> validate_data_input (data="infile", vectors=[None, [ 4, 5, 6] ])
75
75
Traceback (most recent call last):
76
76
...
77
- pygmt.exceptions.GMTInvalidInput: Too much data. Use either data or x/y/z.
78
- >>> _validate_data_input (data="infile", z=[ 7, 8, 9])
77
+ pygmt.exceptions.GMTInvalidInput: Too much data. Pass in either ' data' or 1-D arrays. # noqa: W505
78
+ >>> validate_data_input (data="infile", vectors=[None, None, [ 7, 8, 9] ])
79
79
Traceback (most recent call last):
80
80
...
81
- pygmt.exceptions.GMTInvalidInput: Too much data. Use either data or x/y/z.
81
+ pygmt.exceptions.GMTInvalidInput: Too much data. Pass in either ' data' or 1-D arrays. # noqa: W505
82
82
83
83
Raises
84
84
------
85
85
GMTInvalidInput
86
86
If the data input is not valid.
87
87
"""
88
- if data is None : # data is None
89
- if x is None and y is None : # both x and y are None
90
- if required_data : # data is not optional
91
- raise GMTInvalidInput ("No input data provided." )
92
- elif x is None or y is None : # either x or y is None
93
- raise GMTInvalidInput ("Must provide both x and y." )
94
- if required_z and z is None : # both x and y are not None, now check z
95
- raise GMTInvalidInput ("Must provide x, y, and z." )
96
- else : # data is not None
97
- if x is not None or y is not None or z is not None :
98
- raise GMTInvalidInput ("Too much data. Use either data or x/y/z." )
99
- # For 'matrix' kind, check if data has the required z column
100
- if kind == "matrix" and required_z :
88
+ if kind is None :
89
+ kind = data_kind (data = data , required = required_data )
90
+
91
+ if kind == "vectors" : # From data_kind, we know that data is None
92
+ if vectors is None :
93
+ raise GMTInvalidInput ("No input data provided." )
94
+ if len (vectors ) < ncols :
95
+ raise GMTInvalidInput (
96
+ f"Requires { ncols } 1-D arrays but got { len (vectors )} ."
97
+ )
98
+ for i , v in enumerate (vectors [:ncols ]):
99
+ if v is None :
100
+ if i < 3 :
101
+ msg = f"The '{ 'xyz' [i ]} ' column can't be None."
102
+ else :
103
+ msg = "Column {i} can't be None."
104
+ raise GMTInvalidInput (msg )
105
+ else :
106
+ if vectors is not None and any (v is not None for v in vectors ):
107
+ raise GMTInvalidInput ("Too much data. Pass in either 'data' or 1-D arrays." )
108
+ if kind == "matrix" : # check number of columns for matrix-like data
101
109
if hasattr (data , "shape" ): # np.ndarray or pd.DataFrame
102
- if len (data .shape ) == 1 and data .shape [0 ] < 3 :
103
- raise GMTInvalidInput ("data must provide x, y, and z columns." )
104
- if len (data .shape ) > 1 and data .shape [1 ] < 3 :
105
- raise GMTInvalidInput ("data must provide x, y, and z columns." )
106
- if hasattr (data , "data_vars" ) and len (data .data_vars ) < 3 : # xr.Dataset
107
- raise GMTInvalidInput ("data must provide x, y, and z columns." )
110
+ if len (data .shape ) == 1 and data .shape [0 ] < ncols :
111
+ raise GMTInvalidInput (f "data must have at least { ncols } columns." )
112
+ if len (data .shape ) > 1 and data .shape [1 ] < ncols :
113
+ raise GMTInvalidInput (f "data must have at least { ncols } columns." )
114
+ if hasattr (data , "data_vars" ) and len (data .data_vars ) < ncols : # xr.Dataset
115
+ raise GMTInvalidInput (f "data must have at least { ncols } columns." )
108
116
109
117
110
- def data_kind (data = None , x = None , y = None , z = None , required_z = False , required_data = True ):
118
+ def data_kind (data = None , required = True ):
111
119
"""
112
- Check what kind of data is provided to a module.
120
+ Determine the kind of data that will be passed to a module.
113
121
114
- Possible types:
122
+ It checks the type of the ``data`` argument and determines the kind of
123
+ data. Falls back to ``"vectors"`` if ``data`` is None but required.
115
124
116
- * a file name provided as 'data'
117
- * a pathlib.PurePath object provided as 'data'
118
- * an xarray.DataArray object provided as 'data'
119
- * a 2-D matrix provided as 'data'
120
- * 1-D arrays x and y (and z, optionally)
121
- * an optional argument (None, bool, int or float) provided as 'data'
125
+ Possible data kinds:
122
126
123
- Arguments should be ``None`` if not used. If doesn't fit any of these
124
- categories (or fits more than one), will raise an exception.
127
+ - ``'file'``: a file name or a pathlib.PurePath object providfed as 'data'
128
+ - ``'arg'``: an optional argument (None, bool, int or float) provided
129
+ as 'data'
130
+ - ``'grid'``: an xarray.DataArray with 2 dimensions provided as 'data'
131
+ - ``'image'``: an xarray.DataArray with 3 dimensions provided as 'data'
132
+ - ``'geojson'``: a geo-like Python object that implements
133
+ ``__geo_interface__`` (geopandas.GeoDataFrame or shapely.geometry)
134
+ provided as 'data'
135
+ - ``'matrix'``: a 2-D array provided as 'data'
136
+ - ``'vectors'``: a list of 1-D arrays provided as 'vectors'
125
137
126
138
Parameters
127
139
----------
128
140
data : str, pathlib.PurePath, None, bool, xarray.DataArray or {table-like}
129
141
Pass in either a file name or :class:`pathlib.Path` to an ASCII data
130
142
table, an :class:`xarray.DataArray`, a 1-D/2-D
131
143
{table-classes} or an option argument.
132
- x/y : 1-D arrays or None
133
- x and y columns as numpy arrays.
134
- z : 1-D array or None
135
- z column as numpy array. To be used optionally when x and y are given.
136
- required_z : bool
137
- State whether the 'z' column is required.
138
- required_data : bool
144
+ required : bool
139
145
Set to True when 'data' is required, or False when dealing with
140
146
optional virtual files. [Default is True].
141
147
@@ -151,49 +157,39 @@ def data_kind(data=None, x=None, y=None, z=None, required_z=False, required_data
151
157
>>> import numpy as np
152
158
>>> import xarray as xr
153
159
>>> import pathlib
154
- >>> data_kind(data=None, x=np.array([1, 2, 3]), y=np.array([4, 5, 6]) )
160
+ >>> data_kind(data=None)
155
161
'vectors'
156
- >>> data_kind(data=np.arange(10).reshape((5, 2)), x=None, y=None )
162
+ >>> data_kind(data=np.arange(10).reshape((5, 2)))
157
163
'matrix'
158
- >>> data_kind(data="my-data-file.txt", x=None, y=None )
164
+ >>> data_kind(data="my-data-file.txt")
159
165
'file'
160
- >>> data_kind(data=pathlib.Path("my-data-file.txt"), x=None, y=None )
166
+ >>> data_kind(data=pathlib.Path("my-data-file.txt"))
161
167
'file'
162
- >>> data_kind(data=None, x=None, y=None, required_data =False)
168
+ >>> data_kind(data=None, required =False)
163
169
'arg'
164
- >>> data_kind(data=2.0, x=None, y=None, required_data =False)
170
+ >>> data_kind(data=2.0, required =False)
165
171
'arg'
166
- >>> data_kind(data=True, x=None, y=None, required_data =False)
172
+ >>> data_kind(data=True, required =False)
167
173
'arg'
168
174
>>> data_kind(data=xr.DataArray(np.random.rand(4, 3)))
169
175
'grid'
170
176
>>> data_kind(data=xr.DataArray(np.random.rand(3, 4, 5)))
171
177
'image'
172
178
"""
173
- # determine the data kind
174
179
if isinstance (data , (str , pathlib .PurePath )):
175
180
kind = "file"
176
- elif isinstance (data , (bool , int , float )) or (data is None and not required_data ):
181
+ elif isinstance (data , (bool , int , float )) or (data is None and not required ):
177
182
kind = "arg"
178
183
elif isinstance (data , xr .DataArray ):
179
184
kind = "image" if len (data .dims ) == 3 else "grid"
180
185
elif hasattr (data , "__geo_interface__" ):
181
186
# geo-like Python object that implements ``__geo_interface__``
182
187
# (geopandas.GeoDataFrame or shapely.geometry)
183
188
kind = "geojson"
184
- elif data is not None :
189
+ elif data is not None : # anything but None is taken as a matrix
185
190
kind = "matrix"
186
- else :
191
+ else : # fallback to vectors if data is None but required
187
192
kind = "vectors"
188
- _validate_data_input (
189
- data = data ,
190
- x = x ,
191
- y = y ,
192
- z = z ,
193
- required_z = required_z ,
194
- required_data = required_data ,
195
- kind = kind ,
196
- )
197
193
return kind
198
194
199
195
0 commit comments