@@ -886,15 +886,134 @@ def __getattr__(self, attr):
886
886
)
887
887
888
888
889
+ def create_flag_dict (da ):
890
+ if not da .cf .is_flag_variable :
891
+ raise ValueError (
892
+ "Comparisons are only supported for DataArrays that represent CF flag variables."
893
+ ".attrs must contain 'flag_values' and 'flag_meanings'"
894
+ )
895
+
896
+ flag_meanings = da .attrs ["flag_meanings" ].split (" " )
897
+ flag_values = da .attrs ["flag_values" ]
898
+ # TODO: assert flag_values is iterable
899
+ assert len (flag_values ) == len (flag_meanings )
900
+ return dict (zip (flag_meanings , flag_values ))
901
+
902
+
889
903
class CFAccessor :
890
904
"""
891
905
Common Dataset and DataArray accessor functionality.
892
906
"""
893
907
894
- def __init__ (self , da ):
895
- self ._obj = da
908
+ def __init__ (self , obj ):
909
+ self ._obj = obj
896
910
self ._all_cell_measures = None
897
911
912
+ def _assert_valid_other_comparison (self , other ):
913
+ flag_dict = create_flag_dict (self ._obj )
914
+ if other not in flag_dict :
915
+ raise ValueError (
916
+ f"Did not find flag value meaning [{ other } ] in known flag meanings: [{ flag_dict .keys ()!r} ]"
917
+ )
918
+ return flag_dict
919
+
920
+ def __eq__ (self , other ):
921
+ """
922
+ Compare flag values against `other`.
923
+
924
+ `other` must be in the 'flag_meanings' attribute.
925
+ `other` is mapped to the corresponding value in the 'flag_values' attribute, and then
926
+ compared.
927
+ """
928
+ flag_dict = self ._assert_valid_other_comparison (other )
929
+ return self ._obj == flag_dict [other ]
930
+
931
+ def __ne__ (self , other ):
932
+ """
933
+ Compare flag values against `other`.
934
+
935
+ `other` must be in the 'flag_meanings' attribute.
936
+ `other` is mapped to the corresponding value in the 'flag_values' attribute, and then
937
+ compared.
938
+ """
939
+ flag_dict = self ._assert_valid_other_comparison (other )
940
+ return self ._obj != flag_dict [other ]
941
+
942
+ def __lt__ (self , other ):
943
+ """
944
+ Compare flag values against `other`.
945
+
946
+ `other` must be in the 'flag_meanings' attribute.
947
+ `other` is mapped to the corresponding value in the 'flag_values' attribute, and then
948
+ compared.
949
+ """
950
+ flag_dict = self ._assert_valid_other_comparison (other )
951
+ return self ._obj < flag_dict [other ]
952
+
953
+ def __le__ (self , other ):
954
+ """
955
+ Compare flag values against `other`.
956
+
957
+ `other` must be in the 'flag_meanings' attribute.
958
+ `other` is mapped to the corresponding value in the 'flag_values' attribute, and then
959
+ compared.
960
+ """
961
+ flag_dict = self ._assert_valid_other_comparison (other )
962
+ return self ._obj <= flag_dict [other ]
963
+
964
+ def __gt__ (self , other ):
965
+ """
966
+ Compare flag values against `other`.
967
+
968
+ `other` must be in the 'flag_meanings' attribute.
969
+ `other` is mapped to the corresponding value in the 'flag_values' attribute, and then
970
+ compared.
971
+ """
972
+ flag_dict = self ._assert_valid_other_comparison (other )
973
+ return self ._obj > flag_dict [other ]
974
+
975
+ def __ge__ (self , other ):
976
+ """
977
+ Compare flag values against `other`.
978
+
979
+ `other` must be in the 'flag_meanings' attribute.
980
+ `other` is mapped to the corresponding value in the 'flag_values' attribute, and then
981
+ compared.
982
+ """
983
+ flag_dict = self ._assert_valid_other_comparison (other )
984
+ return self ._obj >= flag_dict [other ]
985
+
986
+ def isin (self , test_elements ):
987
+ """Test each value in the array for whether it is in test_elements.
988
+
989
+ Parameters
990
+ ----------
991
+ test_elements : array_like, 1D
992
+ The values against which to test each value of `element`.
993
+ These must be in "flag_meanings" attribute, and are mapped
994
+ to the corresponding value in "flag_values" before passing
995
+ that on to DataArray.isin.
996
+
997
+
998
+ Returns
999
+ -------
1000
+ isin : DataArray
1001
+ Has the same type and shape as this object, but with a bool dtype.
1002
+ """
1003
+ if not isinstance (self ._obj , DataArray ):
1004
+ raise ValueError (
1005
+ ".cf.isin is only supported on DataArrays that contain CF flag attributes."
1006
+ )
1007
+ flag_dict = create_flag_dict (self ._obj )
1008
+ mapped_test_elements = []
1009
+ for elem in test_elements :
1010
+ if elem not in flag_dict :
1011
+ raise ValueError (
1012
+ f"Did not find flag value meaning [{ elem } ] in known flag meanings: [{ flag_dict .keys ()!r} ]"
1013
+ )
1014
+ mapped_test_elements .append (flag_dict [elem ])
1015
+ return self ._obj .isin (mapped_test_elements )
1016
+
898
1017
def _get_all_cell_measures (self ):
899
1018
"""
900
1019
Get all cell measures defined in the object, adding CF pre-defined measures.
@@ -1130,7 +1249,12 @@ def make_text_section(subtitle, attr, valid_values, default_keys=None):
1130
1249
1131
1250
return "\n " .join (rows ) + "\n "
1132
1251
1133
- text = "Coordinates:"
1252
+ if isinstance (self ._obj , DataArray ) and self ._obj .cf .is_flag_variable :
1253
+ flag_dict = create_flag_dict (self ._obj )
1254
+ text = f"CF Flag variable with mapping:\n \t { flag_dict !r} \n \n "
1255
+ else :
1256
+ text = ""
1257
+ text += "Coordinates:"
1134
1258
text += make_text_section ("CF Axes" , "axes" , coords , _AXIS_NAMES )
1135
1259
text += make_text_section ("CF Coordinates" , "coordinates" , coords , _COORD_NAMES )
1136
1260
text += make_text_section (
@@ -2057,4 +2181,18 @@ def __getitem__(self, key: Union[str, List[str]]) -> DataArray:
2057
2181
2058
2182
return _getitem (self , key )
2059
2183
2060
- pass
2184
+ @property
2185
+ def is_flag_variable (self ):
2186
+ """
2187
+ Returns True if the DataArray satisfies CF conventions for flag variables.
2188
+
2189
+ Flag masks are not supported yet.
2190
+ """
2191
+ if (
2192
+ isinstance (self ._obj , DataArray )
2193
+ and "flag_meanings" in self ._obj .attrs
2194
+ and "flag_values" in self ._obj .attrs
2195
+ ):
2196
+ return True
2197
+ else :
2198
+ return False
0 commit comments