Skip to content

Commit d181813

Browse files
author
Jeff Whitaker
authored
Merge pull request Unidata#1375 from Unidata/issue1374
add get_fill_value Variable method and fill_value='default' option
2 parents 1352561 + 6e30d7b commit d181813

File tree

5 files changed

+117
-10
lines changed

5 files changed

+117
-10
lines changed

Changelog

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
===============================
33
* add static type hints (PR #1302)
44
* Expose nc_rc_set, nc_rc_get (via rc_set, rc_get module functions). (PR #1348)
5+
* Add Variable.get_fill_value and allow `fill_value='default'` to
6+
set `_FillValue` using default fill values. (issue #1374, PR #1375).
7+
* Fix NETCDF3 endian error (issue #1373, PR #1355).
58

69
version 1.7.1 (tag v1.7.1rel)
710
===============================

docs/index.html

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ <h2 id="dimensions-in-a-netcdf-file">Dimensions in a netCDF file</h2>
289289
&lt;class 'netCDF4._netCDF4.Dimension'&gt;: name = 'lon', size = 144
290290
</code></pre>
291291
<p><code><a title="netCDF4.Dimension" href="#netCDF4.Dimension">Dimension</a></code> names can be changed using the
292-
<code>Dataset.renameDimension</code> method of a <code><a title="netCDF4.Dataset" href="#netCDF4.Dataset">Dataset</a></code> or
292+
<code><a title="netCDF4.Dataset.renameDimension" href="#netCDF4.Dataset.renameDimension">Dataset.renameDimension()</a></code> method of a <code><a title="netCDF4.Dataset" href="#netCDF4.Dataset">Dataset</a></code> or
293293
<code><a title="netCDF4.Group" href="#netCDF4.Group">Group</a></code> instance.</p>
294294
<h2 id="variables-in-a-netcdf-file">Variables in a netCDF file</h2>
295295
<p>netCDF variables behave much like python multidimensional array objects
@@ -2676,12 +2676,16 @@ <h3>Instance variables</h3>
26762676
Ignored if <code>significant_digts</code> not specified. If 'BitRound' is used, then
26772677
<code>significant_digits</code> is interpreted as binary (not decimal) digits.</p>
26782678
<p><strong><code>fill_value</code></strong>:
2679-
If specified, the default netCDF <code>_FillValue</code> (the
2679+
If specified, the default netCDF fill value (the
26802680
value that the variable gets filled with before any data is written to it)
2681-
is replaced with this value.
2682-
If fill_value is set to <code>False</code>, then
2683-
the variable is not pre-filled. The default netCDF fill values can be found
2684-
in the dictionary <code>netCDF4.default_fillvals</code>.</p>
2681+
is replaced with this value, and the <code>_FillValue</code> attribute is set.
2682+
If fill_value is set to <code>False</code>, then the variable is not pre-filled.
2683+
The default netCDF fill values can be found in the dictionary <code>netCDF4.default_fillvals</code>.
2684+
If not set, the default fill value will be used but no <code>_FillValue</code> attribute will be created
2685+
(this is the default behavior of the netcdf-c library). If you want to use the
2686+
default fill value, but have the <code>_FillValue</code> attribute set, use
2687+
<code>fill_value='default'</code> (note - this only works for primitive data types). <code><a title="netCDF4.Variable.get_fill_value" href="#netCDF4.Variable.get_fill_value">Variable.get_fill_value()</a></code>
2688+
can be used to retrieve the fill value, even if the <code>_FillValue</code> attribute is not set.</p>
26852689
<p><strong><code>chunk_cache</code></strong>: If specified, sets the chunk cache size for this variable.
26862690
Persists as long as Dataset is open. Use <code>set_var_chunk_cache</code> to
26872691
change it when Dataset is re-opened.</p>
@@ -2806,6 +2810,15 @@ <h3>Methods</h3>
28062810
<p>return a tuple of <code><a title="netCDF4.Dimension" href="#netCDF4.Dimension">Dimension</a></code> instances associated with this
28072811
<code><a title="netCDF4.Variable" href="#netCDF4.Variable">Variable</a></code>.</p></div>
28082812
</dd>
2813+
<dt id="netCDF4.Variable.get_fill_value"><code class="name flex">
2814+
<span>def <span class="ident">get_fill_value</span></span>(<span>self)</span>
2815+
</code></dt>
2816+
<dd>
2817+
<div class="desc"><p><strong><code>get_fill_value(self)</code></strong></p>
2818+
<p>return the fill value associated with this <code><a title="netCDF4.Variable" href="#netCDF4.Variable">Variable</a></code> (returns <code>None</code> if data is not
2819+
pre-filled). Works even if default fill value was used, and <code>_FillValue</code> attribute
2820+
does not exist.</p></div>
2821+
</dd>
28092822
<dt id="netCDF4.Variable.get_var_chunk_cache"><code class="name flex">
28102823
<span>def <span class="ident">get_var_chunk_cache</span></span>(<span>self)</span>
28112824
</code></dt>
@@ -3241,6 +3254,7 @@ <h4><code><a title="netCDF4.Variable" href="#netCDF4.Variable">Variable</a></cod
32413254
<li><code><a title="netCDF4.Variable.filters" href="#netCDF4.Variable.filters">filters</a></code></li>
32423255
<li><code><a title="netCDF4.Variable.getValue" href="#netCDF4.Variable.getValue">getValue</a></code></li>
32433256
<li><code><a title="netCDF4.Variable.get_dims" href="#netCDF4.Variable.get_dims">get_dims</a></code></li>
3257+
<li><code><a title="netCDF4.Variable.get_fill_value" href="#netCDF4.Variable.get_fill_value">get_fill_value</a></code></li>
32443258
<li><code><a title="netCDF4.Variable.get_var_chunk_cache" href="#netCDF4.Variable.get_var_chunk_cache">get_var_chunk_cache</a></code></li>
32453259
<li><code><a title="netCDF4.Variable.getncattr" href="#netCDF4.Variable.getncattr">getncattr</a></code></li>
32463260
<li><code><a title="netCDF4.Variable.group" href="#netCDF4.Variable.group">group</a></code></li>

src/netCDF4/__init__.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ class Variable(Generic[T_Datatype]):
465465
def renameAttribute(self, oldname: str, newname: str) -> None: ...
466466
def assignValue(self, val: Any) -> None: ...
467467
def getValue(self) -> Any: ...
468+
def get_fill_value(self) -> Any: ...
468469
def set_auto_chartostring(self, chartostring: bool) -> None: ...
469470
def use_nc_get_vars(self, use_nc_get_vars: bool) -> None: ...
470471
def set_auto_maskandscale(self, maskandscale: bool) -> None: ...

src/netCDF4/_netCDF4.pyx

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4035,11 +4035,16 @@ behavior is similar to Fortran or Matlab, but different than numpy.
40354035
Ignored if `significant_digts` not specified. If 'BitRound' is used, then
40364036
`significant_digits` is interpreted as binary (not decimal) digits.
40374037
4038-
**`fill_value`**: If specified, the default netCDF `_FillValue` (the
4038+
**`fill_value`**: If specified, the default netCDF fill value (the
40394039
value that the variable gets filled with before any data is written to it)
4040-
is replaced with this value. If fill_value is set to `False`, then
4041-
the variable is not pre-filled. The default netCDF fill values can be found
4042-
in the dictionary `netCDF4.default_fillvals`.
4040+
is replaced with this value, and the `_FillValue` attribute is set.
4041+
If fill_value is set to `False`, then the variable is not pre-filled.
4042+
The default netCDF fill values can be found in the dictionary `netCDF4.default_fillvals`.
4043+
If not set, the default fill value will be used but no `_FillValue` attribute will be created
4044+
(this is the default behavior of the netcdf-c library). If you want to use the
4045+
default fill value, but have the `_FillValue` attribute set, use
4046+
`fill_value='default'` (note - this only works for primitive data types). `Variable.get_fill_value`
4047+
can be used to retrieve the fill value, even if the `_FillValue` attribute is not set.
40434048
40444049
**`chunk_cache`**: If specified, sets the chunk cache size for this variable.
40454050
Persists as long as Dataset is open. Use `set_var_chunk_cache` to
@@ -4403,6 +4408,17 @@ behavior is similar to Fortran or Matlab, but different than numpy.
44034408
if ierr != NC_NOERR:
44044409
if grp.data_model != 'NETCDF4': grp._enddef()
44054410
_ensure_nc_success(ierr, extra_msg=error_info)
4411+
elif fill_value == 'default':
4412+
if self._isprimitive:
4413+
fillval = numpy.array(default_fillvals[self.dtype.str[1:]])
4414+
if not fillval.dtype.isnative: fillval.byteswap(True)
4415+
_set_att(self._grp, self._varid, '_FillValue',\
4416+
fillval, xtype=xtype)
4417+
else:
4418+
msg = """
4419+
WARNING: there is no default fill value for this data type, so fill_value='default'
4420+
does not do anything."""
4421+
warnings.warn(msg)
44064422
else:
44074423
if self._isprimitive or self._isenum or \
44084424
(self._isvlen and self.dtype == str):
@@ -4638,6 +4654,36 @@ behavior is similar to Fortran or Matlab, but different than numpy.
46384654
return the group that this `Variable` is a member of."""
46394655
return self._grp
46404656

4657+
def get_fill_value(self):
4658+
"""
4659+
**`get_fill_value(self)`**
4660+
4661+
return the fill value associated with this `Variable` (returns `None` if data is not
4662+
pre-filled). Works even if default fill value was used, and `_FillValue` attribute
4663+
does not exist."""
4664+
cdef int ierr, no_fill
4665+
with nogil:
4666+
ierr = nc_inq_var_fill(self._grpid,self._varid,&no_fill,NULL)
4667+
_ensure_nc_success(ierr)
4668+
if no_fill == 1: # no filling for this variable
4669+
return None
4670+
else:
4671+
try:
4672+
fillval = self._FillValue
4673+
return fillval
4674+
except AttributeError:
4675+
# _FillValue attribute not set, see if we can retrieve _FillValue.
4676+
# for primitive data types.
4677+
if self._isprimitive:
4678+
#return numpy.array(default_fillvals[self.dtype.str[1:]],self.dtype)
4679+
fillval = numpy.empty((),self.dtype)
4680+
ierr=nc_inq_var_fill(self._grpid,self._varid,&no_fill,PyArray_DATA(fillval))
4681+
_ensure_nc_success(ierr)
4682+
return fillval
4683+
else:
4684+
# no default filling for non-primitive data types.
4685+
return None
4686+
46414687
def ncattrs(self):
46424688
"""
46434689
**`ncattrs(self)`**

test/test_get_fill_value.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import unittest, os, tempfile
2+
import netCDF4
3+
from numpy.testing import assert_array_equal
4+
import numpy as np
5+
6+
fill_val = np.array(9.9e31)
7+
8+
# test Variable.get_fill_value
9+
10+
class TestGetFillValue(unittest.TestCase):
11+
def setUp(self):
12+
self.testfile = tempfile.NamedTemporaryFile(suffix='.nc', delete=False).name
13+
f = netCDF4.Dataset(self.testfile, 'w')
14+
dim = f.createDimension('x',10)
15+
for dt in netCDF4.default_fillvals.keys():
16+
if not dt.startswith('c'):
17+
v = f.createVariable(dt+'_var',dt,dim)
18+
v = f.createVariable('float_var',np.float64,dim,fill_value=fill_val)
19+
# test fill_value='default' option (issue #1374)
20+
v2 = f.createVariable('float_var2',np.float64,dim,fill_value='default')
21+
f.close()
22+
23+
def tearDown(self):
24+
os.remove(self.testfile)
25+
26+
def runTest(self):
27+
f = netCDF4.Dataset(self.testfile, "r")
28+
# no _FillValue set, test that default fill value returned
29+
for dt in netCDF4.default_fillvals.keys():
30+
if not dt.startswith('c'):
31+
fillval = np.array(netCDF4.default_fillvals[dt])
32+
if dt == 'S1': fillval = fillval.astype(dt)
33+
v = f[dt+'_var']
34+
assert_array_equal(fillval, v.get_fill_value())
35+
# _FillValue attribute is set.
36+
v = f['float_var']
37+
assert_array_equal(fill_val, v.get_fill_value())
38+
v = f['float_var2']
39+
assert_array_equal(np.array(netCDF4.default_fillvals['f8']), v._FillValue)
40+
f.close()
41+
42+
if __name__ == '__main__':
43+
unittest.main()

0 commit comments

Comments
 (0)