@@ -138,96 +138,96 @@ def open_example_mfdataset(names, *args, **kwargs) -> Dataset:
138
138
)
139
139
140
140
141
- def create_masked_and_scaled_data () -> Dataset :
142
- x = np .array ([np .nan , np .nan , 10 , 10.1 , 10.2 ], dtype = np . float32 )
141
+ def create_masked_and_scaled_data (dtype = np . float32 ) -> Dataset :
142
+ x = np .array ([np .nan , np .nan , 10 , 10.1 , 10.2 ], dtype = dtype )
143
143
encoding = {
144
144
"_FillValue" : - 1 ,
145
145
"add_offset" : 10 ,
146
- "scale_factor" : np . float32 (0.1 ),
146
+ "scale_factor" : dtype (0.1 ),
147
147
"dtype" : "i2" ,
148
148
}
149
149
return Dataset ({"x" : ("t" , x , {}, encoding )})
150
150
151
151
152
- def create_encoded_masked_and_scaled_data () -> Dataset :
153
- attributes = {"_FillValue" : - 1 , "add_offset" : 10 , "scale_factor" : np . float32 (0.1 )}
152
+ def create_encoded_masked_and_scaled_data (dtype = np . float32 ) -> Dataset :
153
+ attributes = {"_FillValue" : - 1 , "add_offset" : 10 , "scale_factor" : dtype (0.1 )}
154
154
return Dataset (
155
155
{"x" : ("t" , np .array ([- 1 , - 1 , 0 , 1 , 2 ], dtype = np .int16 ), attributes )}
156
156
)
157
157
158
158
159
- def create_unsigned_masked_scaled_data () -> Dataset :
159
+ def create_unsigned_masked_scaled_data (dtype = np . float32 ) -> Dataset :
160
160
encoding = {
161
161
"_FillValue" : 255 ,
162
162
"_Unsigned" : "true" ,
163
163
"dtype" : "i1" ,
164
164
"add_offset" : 10 ,
165
- "scale_factor" : np . float32 (0.1 ),
165
+ "scale_factor" : dtype (0.1 ),
166
166
}
167
- x = np .array ([10.0 , 10.1 , 22.7 , 22.8 , np .nan ], dtype = np . float32 )
167
+ x = np .array ([10.0 , 10.1 , 22.7 , 22.8 , np .nan ], dtype = dtype )
168
168
return Dataset ({"x" : ("t" , x , {}, encoding )})
169
169
170
170
171
- def create_encoded_unsigned_masked_scaled_data () -> Dataset :
171
+ def create_encoded_unsigned_masked_scaled_data (dtype = np . float32 ) -> Dataset :
172
172
# These are values as written to the file: the _FillValue will
173
173
# be represented in the signed form.
174
174
attributes = {
175
175
"_FillValue" : - 1 ,
176
176
"_Unsigned" : "true" ,
177
177
"add_offset" : 10 ,
178
- "scale_factor" : np . float32 (0.1 ),
178
+ "scale_factor" : dtype (0.1 ),
179
179
}
180
180
# Create unsigned data corresponding to [0, 1, 127, 128, 255] unsigned
181
181
sb = np .asarray ([0 , 1 , 127 , - 128 , - 1 ], dtype = "i1" )
182
182
return Dataset ({"x" : ("t" , sb , attributes )})
183
183
184
184
185
- def create_bad_unsigned_masked_scaled_data () -> Dataset :
185
+ def create_bad_unsigned_masked_scaled_data (dtype = np . float32 ) -> Dataset :
186
186
encoding = {
187
187
"_FillValue" : 255 ,
188
188
"_Unsigned" : True ,
189
189
"dtype" : "i1" ,
190
190
"add_offset" : 10 ,
191
- "scale_factor" : np . float32 (0.1 ),
191
+ "scale_factor" : dtype (0.1 ),
192
192
}
193
- x = np .array ([10.0 , 10.1 , 22.7 , 22.8 , np .nan ], dtype = np . float32 )
193
+ x = np .array ([10.0 , 10.1 , 22.7 , 22.8 , np .nan ], dtype = dtype )
194
194
return Dataset ({"x" : ("t" , x , {}, encoding )})
195
195
196
196
197
- def create_bad_encoded_unsigned_masked_scaled_data () -> Dataset :
197
+ def create_bad_encoded_unsigned_masked_scaled_data (dtype = np . float32 ) -> Dataset :
198
198
# These are values as written to the file: the _FillValue will
199
199
# be represented in the signed form.
200
200
attributes = {
201
201
"_FillValue" : - 1 ,
202
202
"_Unsigned" : True ,
203
203
"add_offset" : 10 ,
204
- "scale_factor" : np . float32 (0.1 ),
204
+ "scale_factor" : dtype (0.1 ),
205
205
}
206
206
# Create signed data corresponding to [0, 1, 127, 128, 255] unsigned
207
207
sb = np .asarray ([0 , 1 , 127 , - 128 , - 1 ], dtype = "i1" )
208
208
return Dataset ({"x" : ("t" , sb , attributes )})
209
209
210
210
211
- def create_signed_masked_scaled_data () -> Dataset :
211
+ def create_signed_masked_scaled_data (dtype = np . float32 ) -> Dataset :
212
212
encoding = {
213
213
"_FillValue" : - 127 ,
214
214
"_Unsigned" : "false" ,
215
215
"dtype" : "i1" ,
216
216
"add_offset" : 10 ,
217
- "scale_factor" : np . float32 (0.1 ),
217
+ "scale_factor" : dtype (0.1 ),
218
218
}
219
- x = np .array ([- 1.0 , 10.1 , 22.7 , np .nan ], dtype = np . float32 )
219
+ x = np .array ([- 1.0 , 10.1 , 22.7 , np .nan ], dtype = dtype )
220
220
return Dataset ({"x" : ("t" , x , {}, encoding )})
221
221
222
222
223
- def create_encoded_signed_masked_scaled_data () -> Dataset :
223
+ def create_encoded_signed_masked_scaled_data (dtype = np . float32 ) -> Dataset :
224
224
# These are values as written to the file: the _FillValue will
225
225
# be represented in the signed form.
226
226
attributes = {
227
227
"_FillValue" : - 127 ,
228
228
"_Unsigned" : "false" ,
229
229
"add_offset" : 10 ,
230
- "scale_factor" : np . float32 (0.1 ),
230
+ "scale_factor" : dtype (0.1 ),
231
231
}
232
232
# Create signed data corresponding to [0, 1, 127, 128, 255] unsigned
233
233
sb = np .asarray ([- 110 , 1 , 127 , - 127 ], dtype = "i1" )
@@ -857,6 +857,7 @@ def test_roundtrip_string_with_fill_value_nchar(self) -> None:
857
857
with self .roundtrip (original ) as actual :
858
858
assert_identical (expected , actual )
859
859
860
+ @pytest .mark .parametrize ("dtype" , [np .float32 , np .float64 ])
860
861
@pytest .mark .parametrize (
861
862
"decoded_fn, encoded_fn" ,
862
863
[
@@ -876,12 +877,19 @@ def test_roundtrip_string_with_fill_value_nchar(self) -> None:
876
877
(create_masked_and_scaled_data , create_encoded_masked_and_scaled_data ),
877
878
],
878
879
)
879
- def test_roundtrip_mask_and_scale (self , decoded_fn , encoded_fn ) -> None :
880
- decoded = decoded_fn ()
881
- encoded = encoded_fn ()
880
+ def test_roundtrip_mask_and_scale (self , decoded_fn , encoded_fn , dtype ) -> None :
881
+ if dtype == np .float32 and isinstance (
882
+ self , (TestZarrDirectoryStore , TestZarrDictStore )
883
+ ):
884
+ pytest .skip (
885
+ "zarr attributes (eg. `scale_factor` are unconditionally promoted to `float64`"
886
+ )
887
+ decoded = decoded_fn (dtype )
888
+ encoded = encoded_fn (dtype )
882
889
883
890
with self .roundtrip (decoded ) as actual :
884
891
for k in decoded .variables :
892
+ print (k , decoded .variables [k ].dtype )
885
893
assert decoded .variables [k ].dtype == actual .variables [k ].dtype
886
894
assert_allclose (decoded , actual , decode_bytes = False )
887
895
@@ -899,7 +907,7 @@ def test_roundtrip_mask_and_scale(self, decoded_fn, encoded_fn) -> None:
899
907
900
908
# make sure roundtrip encoding didn't change the
901
909
# original dataset.
902
- assert_allclose (encoded , encoded_fn (), decode_bytes = False )
910
+ assert_allclose (encoded , encoded_fn (dtype ), decode_bytes = False )
903
911
904
912
with self .roundtrip (encoded ) as actual :
905
913
for k in decoded .variables :
@@ -1533,29 +1541,32 @@ def test_encoding_chunksizes_unlimited(self) -> None:
1533
1541
with self .roundtrip (ds ) as actual :
1534
1542
assert_equal (ds , actual )
1535
1543
1536
- def test_mask_and_scale (self ) -> None :
1544
+ @pytest .mark .parametrize ("dtype" , [np .float32 , np .float64 ])
1545
+ def test_mask_and_scale (self , dtype ) -> None :
1537
1546
with create_tmp_file () as tmp_file :
1538
1547
with nc4 .Dataset (tmp_file , mode = "w" ) as nc :
1539
1548
nc .createDimension ("t" , 5 )
1540
1549
nc .createVariable ("x" , "int16" , ("t" ,), fill_value = - 1 )
1541
1550
v = nc .variables ["x" ]
1542
1551
v .set_auto_maskandscale (False )
1543
1552
v .add_offset = 10
1544
- v .scale_factor = 0.1
1553
+ v .scale_factor = dtype ( 0.1 )
1545
1554
v [:] = np .array ([- 1 , - 1 , 0 , 1 , 2 ])
1546
1555
1547
1556
# first make sure netCDF4 reads the masked and scaled data
1548
1557
# correctly
1549
1558
with nc4 .Dataset (tmp_file , mode = "r" ) as nc :
1550
1559
expected = np .ma .array (
1551
- [- 1 , - 1 , 10 , 10.1 , 10.2 ], mask = [True , True , False , False , False ]
1560
+ [- 1 , - 1 , 10 , 10.1 , 10.2 ],
1561
+ mask = [True , True , False , False , False ],
1562
+ dtype = dtype ,
1552
1563
)
1553
1564
actual = nc .variables ["x" ][:]
1554
1565
assert_array_equal (expected , actual )
1555
1566
1556
1567
# now check xarray
1557
1568
with open_dataset (tmp_file ) as ds :
1558
- expected = create_masked_and_scaled_data ()
1569
+ expected = create_masked_and_scaled_data (dtype )
1559
1570
assert_identical (expected , ds )
1560
1571
1561
1572
def test_0dimensional_variable (self ) -> None :
0 commit comments