@@ -58,17 +58,20 @@ class CFAccessor:
58
58
"""Dataframe accessor analogous to cf-xarray accessor."""
59
59
60
60
def __init__ (self , pandas_obj ):
61
- self ._validate (pandas_obj )
61
+ # don't automatically validate but can when needed
62
+ # self._validate(pandas_obj)
62
63
self ._obj = pandas_obj
63
64
64
- @staticmethod
65
- def _validate (obj ):
65
+ # @staticmethod
66
+ def _validate (self ):
66
67
"""what is necessary for basic use."""
67
68
68
69
# verify that necessary keys are present. Z would also be nice but might be missing.
69
70
# but don't use the accessor to check
70
71
keys = ["T" , "longitude" , "latitude" ]
71
- missing_keys = [key for key in keys if len (_get_axis_coord (obj , key )) == 0 ]
72
+ missing_keys = [
73
+ key for key in keys if len (_get_axis_coord (self ._obj , key )) == 0
74
+ ]
72
75
if len (missing_keys ) > 0 :
73
76
raise AttributeError (
74
77
f'{ "longitude" , "latitude" , "time" } must be identifiable in DataFrame but { missing_keys } are missing.'
@@ -110,9 +113,12 @@ def __getitem__(self, key: str) -> Union[pd.Series, pd.DataFrame]:
110
113
else :
111
114
col_names = _get_custom_criteria (self ._obj , key )
112
115
113
- # return series
114
- if len (col_names ) == 1 :
116
+ # return series for column
117
+ if len (col_names ) == 1 and col_names [ 0 ] in self . _obj . columns :
115
118
return self ._obj [col_names [0 ]]
119
+ # return index
120
+ elif len (col_names ) == 1 and col_names [0 ] in self ._obj .index .names :
121
+ return self ._obj .index .get_level_values (col_names [0 ])
116
122
# return DataFrame
117
123
elif len (col_names ) > 1 :
118
124
return self ._obj [col_names ]
@@ -248,6 +254,32 @@ def custom_keys(self):
248
254
249
255
return vardict
250
256
257
+ @property
258
+ def axes_cols (self ) -> List [str ]:
259
+ """
260
+ Property that returns a list of column names from the axes mapping.
261
+
262
+ Returns
263
+ -------
264
+ list
265
+ Variable names that are the column names which represent axes.
266
+ """
267
+
268
+ return list (itertools .chain (* [* self .axes .values ()]))
269
+
270
+ @property
271
+ def coordinates_cols (self ) -> List [str ]:
272
+ """
273
+ Property that returns a list of column names from the coordinates mapping.
274
+
275
+ Returns
276
+ -------
277
+ list
278
+ Variable names that are the column names which represent coordinates.
279
+ """
280
+
281
+ return list (itertools .chain (* [* self .coordinates .values ()]))
282
+
251
283
@property
252
284
def standard_names (self ):
253
285
"""
@@ -313,26 +345,14 @@ def _get_axis_coord(obj: Union[DataFrame, Series], key: str) -> list:
313
345
f"cf_xarray did not understand key { key !r} . Expected one of { valid_keys !r} "
314
346
)
315
347
316
- # search_in = set()
317
- # attrs_or_encoding = ChainMap(obj.attrs, obj.encoding)
318
- # coordinates = attrs_or_encoding.get("coordinates", None)
319
-
320
- # # Handles case where the coordinates attribute is None
321
- # # This is used to tell xarray to not write a coordinates attribute
322
- # if coordinates:
323
- # search_in.update(coordinates.split(" "))
324
- # if not search_in:
325
- # search_in = set(obj.coords)
326
-
327
- # # maybe only do this for key in _AXIS_NAMES?
328
- # search_in.update(obj.indexes)
329
-
330
- # search_in = search_in & set(obj.coords)
348
+ # loop over column names and index names
331
349
results : set = set ()
332
- for col in obj .columns :
333
- # var = obj.coords[coord]
350
+ cols_and_indices = list (obj .columns )
351
+ cols_and_indices += obj .index .names
352
+ # remove None if in names from index
353
+ cols_and_indices = [name for name in cols_and_indices if name is not None ]
354
+ for col in cols_and_indices :
334
355
if key in coordinate_criteria :
335
- # import pdb; pdb.set_trace()
336
356
for criterion , expected in coordinate_criteria [key ].items ():
337
357
# allow for the column header having a space in it that separate
338
358
# the name from the units, for example
@@ -350,14 +370,19 @@ def _get_axis_coord(obj: Union[DataFrame, Series], key: str) -> list:
350
370
# units = getattr(col.data, "units", None)
351
371
# if units in expected:
352
372
# results.update((col,))
353
-
354
373
# also use the guess_regex approach by default, but only if no results so far
355
374
# this takes the logic from cf-xarray guess_coord_axis
356
375
if len (results ) == 0 :
357
- if key in ("T" , "time" ) and _is_datetime_like (obj [col ]):
358
- results .update ((col ,))
359
- continue # prevent second detection
360
-
376
+ if col in obj .columns :
377
+ if key in ("T" , "time" ) and _is_datetime_like (obj [col ]):
378
+ results .update ((col ,))
379
+ continue # prevent second detection
380
+ elif col in obj .index .names :
381
+ if key in ("T" , "time" ) and _is_datetime_like (
382
+ obj .index .get_level_values (col )
383
+ ):
384
+ results .update ((col ,))
385
+ continue # prevent second detection
361
386
pattern = guess_regex [key ]
362
387
if pattern .match (col .lower ()):
363
388
results .update ((col ,))
0 commit comments