17
17
18
18
"""
19
19
20
-
21
20
# TODO:
22
21
# * properly implement ``_FillValue``.
23
22
# * fix character variables.
35
34
__all__ = ['netcdf_file' , 'netcdf_variable' ]
36
35
37
36
38
- import sys
39
37
import warnings
40
38
import weakref
41
39
from operator import mul
42
- from collections import OrderedDict
40
+ from platform import python_implementation
43
41
44
42
import mmap as mm
45
43
46
44
import numpy as np
47
- from numpy .compat import asbytes , asstr
48
45
from numpy import frombuffer , dtype , empty , array , asarray
49
46
from numpy import little_endian as LITTLE_ENDIAN
50
47
from functools import reduce
51
48
52
- IS_PYPY = ('__pypy__' in sys .modules )
49
+
50
+ IS_PYPY = python_implementation () == 'PyPy'
53
51
54
52
ABSENT = b'\x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 '
55
53
ZERO = b'\x00 \x00 \x00 \x00 '
97
95
('S' , 1 ): NC_CHAR }
98
96
99
97
100
- class netcdf_file ( object ) :
98
+ class netcdf_file :
101
99
"""
102
100
A file object for NetCDF data.
103
101
@@ -141,7 +139,7 @@ class netcdf_file(object):
141
139
NetCDF files are a self-describing binary data format. The file contains
142
140
metadata that describes the dimensions and variables in the file. More
143
141
details about NetCDF files can be found `here
144
- <https://www.unidata.ucar.edu/software/netcdf/docs/user_guide .html>`__. There
142
+ <https://www.unidata.ucar.edu/software/netcdf/guide_toc .html>`__. There
145
143
are three main sections to a NetCDF data structure:
146
144
147
145
1. Dimensions
@@ -180,67 +178,53 @@ class netcdf_file(object):
180
178
--------
181
179
To create a NetCDF file:
182
180
183
- Make a temporary file for testing:
184
-
185
- >>> import os
186
- >>> from tempfile import mkdtemp
187
- >>> tmp_pth = mkdtemp()
188
- >>> fname = os.path.join(tmp_pth, 'test.nc')
189
-
190
- Then:
191
-
192
- >>> f = netcdf_file(fname, 'w')
193
- >>> f.history = 'Created for a test'
194
- >>> f.createDimension('time', 10)
195
- >>> time = f.createVariable('time', 'i', ('time',))
196
- >>> time[:] = np.arange(10)
197
- >>> time.units = 'days since 2008-01-01'
198
- >>> f.close()
181
+ >>> from scipy.io import netcdf_file
182
+ >>> f = netcdf_file('simple.nc', 'w')
183
+ >>> f.history = 'Created for a test'
184
+ >>> f.createDimension('time', 10)
185
+ >>> time = f.createVariable('time', 'i', ('time',))
186
+ >>> time[:] = np.arange(10)
187
+ >>> time.units = 'days since 2008-01-01'
188
+ >>> f.close()
199
189
200
190
Note the assignment of ``arange(10)`` to ``time[:]``. Exposing the slice
201
191
of the time variable allows for the data to be set in the object, rather
202
192
than letting ``arange(10)`` overwrite the ``time`` variable.
203
193
204
194
To read the NetCDF file we just created:
205
195
206
- >>> f = netcdf_file(fname, 'r')
207
- >>> f.history == b'Created for a test'
208
- True
209
- >>> time = f.variables['time']
210
- >>> time.units == b'days since 2008-01-01'
211
- True
212
- >>> time.shape == (10,)
213
- True
214
- >>> time[-1]
215
- 9
196
+ >>> from scipy.io import netcdf_file
197
+ >>> f = netcdf_file('simple.nc', 'r')
198
+ >>> print(f.history)
199
+ b'Created for a test'
200
+ >>> time = f.variables['time']
201
+ >>> print(time.units)
202
+ b'days since 2008-01-01'
203
+ >>> print(time.shape)
204
+ (10,)
205
+ >>> print(time[-1])
206
+ 9
216
207
217
208
NetCDF files, when opened read-only, return arrays that refer
218
209
directly to memory-mapped data on disk:
219
210
220
- >>> data = time[:]
221
- >>> data.base.base # doctest: +ELLIPSIS
222
- <mmap.mmap ...>
211
+ >>> data = time[:]
223
212
224
213
If the data is to be processed after the file is closed, it needs
225
214
to be copied to main memory:
226
215
227
- >>> data = time[:].copy()
228
- >>> del time # References to mmap'd objects can delay full closure
229
- >>> f.close()
230
- >>> data.mean()
231
- 4.5
216
+ >>> data = time[:].copy()
217
+ >>> f.close()
218
+ >>> data.mean()
219
+ 4.5
232
220
233
221
A NetCDF file can also be used as context manager:
234
222
235
- >>> with netcdf_file(fname, 'r') as f:
236
- ... print(f.variables['time'].shape == (10,))
237
- True
238
-
239
- Delete our temporary directory and file:
223
+ >>> from scipy.io import netcdf_file
224
+ >>> with netcdf_file('simple.nc', 'r') as f:
225
+ ... print(f.history)
226
+ b'Created for a test'
240
227
241
- >>> del f # needed for windows unlink
242
- >>> os.unlink(fname)
243
- >>> os.rmdir(tmp_pth)
244
228
"""
245
229
def __init__ (self , filename , mode = 'r' , mmap = None , version = 1 ,
246
230
maskandscale = False ):
@@ -274,8 +258,8 @@ def __init__(self, filename, mode='r', mmap=None, version=1,
274
258
self .version_byte = version
275
259
self .maskandscale = maskandscale
276
260
277
- self .dimensions = OrderedDict ()
278
- self .variables = OrderedDict ()
261
+ self .dimensions = {}
262
+ self .variables = {}
279
263
280
264
self ._dims = []
281
265
self ._recs = 0
@@ -287,7 +271,7 @@ def __init__(self, filename, mode='r', mmap=None, version=1,
287
271
self ._mm = mm .mmap (self .fp .fileno (), 0 , access = mm .ACCESS_READ )
288
272
self ._mm_buf = np .frombuffer (self ._mm , dtype = np .int8 )
289
273
290
- self ._attributes = OrderedDict ()
274
+ self ._attributes = {}
291
275
292
276
if mode in 'ra' :
293
277
self ._read ()
@@ -307,7 +291,7 @@ def close(self):
307
291
try :
308
292
self .flush ()
309
293
finally :
310
- self .variables = OrderedDict ()
294
+ self .variables = {}
311
295
if self ._mm_buf is not None :
312
296
ref = weakref .ref (self ._mm_buf )
313
297
self ._mm_buf = None
@@ -339,7 +323,7 @@ def createDimension(self, name, length):
339
323
Adds a dimension to the Dimension section of the NetCDF data structure.
340
324
341
325
Note that this function merely adds a new dimension that the variables can
342
- reference. The values for the dimension, if desired, should be added as
326
+ reference. The values for the dimension, if desired, should be added as
343
327
a variable using `createVariable`, referring to this dimension.
344
328
345
329
Parameters
@@ -392,7 +376,7 @@ def createVariable(self, name, type, dimensions):
392
376
393
377
"""
394
378
shape = tuple ([self .dimensions [dim ] for dim in dimensions ])
395
- shape_ = tuple ([dim or 0 for dim in shape ]) # replace None with 0 for numpy
379
+ shape_ = tuple ([dim or 0 for dim in shape ]) # replace None with 0 for NumPy
396
380
397
381
type = dtype (type )
398
382
typecode , size = type .char , type .itemsize
@@ -499,7 +483,7 @@ def _write_var_metadata(self, name):
499
483
self ._write_att_array (var ._attributes )
500
484
501
485
nc_type = REVERSE [var .typecode (), var .itemsize ()]
502
- self .fp .write (asbytes ( nc_type ) )
486
+ self .fp .write (nc_type )
503
487
504
488
if not var .isrec :
505
489
vsize = var .data .size * var .data .itemsize
@@ -570,12 +554,9 @@ def _write_att_values(self, values):
570
554
if hasattr (values , 'dtype' ):
571
555
nc_type = REVERSE [values .dtype .char , values .dtype .itemsize ]
572
556
else :
573
- types = [
574
- (int , NC_INT ),
575
- (float , NC_FLOAT ),
576
- (str , NC_CHAR )
577
- ]
578
- # bytes index into scalars in py3k. Check for "string" types
557
+ types = [(int , NC_INT ), (float , NC_FLOAT ), (str , NC_CHAR )]
558
+
559
+ # bytes index into scalars in py3k. Check for "string" types
579
560
if isinstance (values , (str , bytes )):
580
561
sample = values
581
562
else :
@@ -590,12 +571,12 @@ def _write_att_values(self, values):
590
571
591
572
typecode , size = TYPEMAP [nc_type ]
592
573
dtype_ = '>%s' % typecode
593
- # asarray() dies with bytes and '>c' in py3k. Change to 'S'
574
+ # asarray() dies with bytes and '>c' in py3k. Change to 'S'
594
575
dtype_ = 'S' if dtype_ == '>c' else dtype_
595
576
596
577
values = asarray (values , dtype = dtype_ )
597
578
598
- self .fp .write (asbytes ( nc_type ) )
579
+ self .fp .write (nc_type )
599
580
600
581
if values .dtype .char == 'S' :
601
582
nelems = values .itemsize
@@ -634,7 +615,7 @@ def _read_dim_array(self):
634
615
count = self ._unpack_int ()
635
616
636
617
for dim in range (count ):
637
- name = asstr ( self ._unpack_string ())
618
+ name = self ._unpack_string (). decode ( 'latin1' )
638
619
length = self ._unpack_int () or None # None for record dimension
639
620
self .dimensions [name ] = length
640
621
self ._dims .append (name ) # preserve order
@@ -649,9 +630,9 @@ def _read_att_array(self):
649
630
raise ValueError ("Unexpected header." )
650
631
count = self ._unpack_int ()
651
632
652
- attributes = OrderedDict ()
633
+ attributes = {}
653
634
for attr in range (count ):
654
- name = asstr ( self ._unpack_string ())
635
+ name = self ._unpack_string (). decode ( 'latin1' )
655
636
attributes [name ] = self ._read_att_values ()
656
637
return attributes
657
638
@@ -667,7 +648,7 @@ def _read_var_array(self):
667
648
for var in range (count ):
668
649
(name , dimensions , shape , attributes ,
669
650
typecode , size , dtype_ , begin_ , vsize ) = self ._read_var ()
670
- # https://www.unidata.ucar.edu/software/netcdf/docs/user_guide .html
651
+ # https://www.unidata.ucar.edu/software/netcdf/guide_toc .html
671
652
# Note that vsize is the product of the dimension lengths
672
653
# (omitting the record dimension) and the number of bytes
673
654
# per value (determined from the type), increased to the
@@ -742,7 +723,7 @@ def _read_var_array(self):
742
723
self .variables [var ].__dict__ ['data' ] = rec_array [var ]
743
724
744
725
def _read_var (self ):
745
- name = asstr ( self ._unpack_string ())
726
+ name = self ._unpack_string (). decode ( 'latin1' )
746
727
dimensions = []
747
728
shape = []
748
729
dims = self ._unpack_int ()
@@ -807,7 +788,7 @@ def _unpack_int64(self):
807
788
def _pack_string (self , s ):
808
789
count = len (s )
809
790
self ._pack_int (count )
810
- self .fp .write (asbytes ( s ))
791
+ self .fp .write (s . encode ( 'latin1' ))
811
792
self .fp .write (b'\x00 ' * (- count % 4 )) # pad
812
793
813
794
def _unpack_string (self ):
@@ -817,7 +798,7 @@ def _unpack_string(self):
817
798
return s
818
799
819
800
820
- class netcdf_variable ( object ) :
801
+ class netcdf_variable :
821
802
"""
822
803
A data object for netcdf files.
823
804
@@ -845,13 +826,13 @@ class netcdf_variable(object):
845
826
size : int
846
827
Desired element size for the data array.
847
828
shape : sequence of ints
848
- The shape of the array. This should match the lengths of the
829
+ The shape of the array. This should match the lengths of the
849
830
variable's dimensions.
850
831
dimensions : sequence of strings
851
- The names of the dimensions used by the variable. Must be in the
832
+ The names of the dimensions used by the variable. Must be in the
852
833
same order of the dimension lengths given by `shape`.
853
834
attributes : dict, optional
854
- Attribute values (any type) keyed by string names. These attributes
835
+ Attribute values (any type) keyed by string names. These attributes
855
836
become attributes for the netcdf_variable object.
856
837
maskandscale : bool, optional
857
838
Whether to automatically scale and/or mask data based on attributes.
@@ -880,7 +861,7 @@ def __init__(self, data, typecode, size, shape, dimensions,
880
861
self .dimensions = dimensions
881
862
self .maskandscale = maskandscale
882
863
883
- self ._attributes = attributes or OrderedDict ()
864
+ self ._attributes = attributes or {}
884
865
for k , v in self ._attributes .items ():
885
866
self .__dict__ [k ] = v
886
867
@@ -949,7 +930,7 @@ def assignValue(self, value):
949
930
# memory-mapped array causes a seg. fault.
950
931
# See NumPy ticket #1622, and SciPy ticket #1202.
951
932
# This check for `writeable` can be removed when the oldest version
952
- # of numpy still supported by scipy contains the fix for #1622.
933
+ # of NumPy still supported by scipy contains the fix for #1622.
953
934
raise RuntimeError ("variable is not writeable" )
954
935
955
936
self .data .itemset (value )
@@ -961,7 +942,7 @@ def typecode(self):
961
942
Returns
962
943
-------
963
944
typecode : char
964
- The character typecode of the variable (eg , 'i' for int).
945
+ The character typecode of the variable (e.g. , 'i' for int).
965
946
966
947
"""
967
948
return self ._typecode
@@ -973,7 +954,7 @@ def itemsize(self):
973
954
Returns
974
955
-------
975
956
itemsize : int
976
- The element size of the variable (eg , 8 for float64).
957
+ The element size of the variable (e.g. , 8 for float64).
977
958
978
959
"""
979
960
return self ._size
0 commit comments