Skip to content

Commit c12e1da

Browse files
committed
Merge remote-tracking branch 'upstream/master' into interp-na-maxgap
* upstream/master: minor lint tweaks (pydata#3429) Hack around pydata#3440 (pydata#3442) Update Terminology page to account for multidimensional coordinates (pydata#3410) Use cftime master for upstream-dev build (pydata#3439) MAGA (Make Azure Green Again) (pydata#3436) Test that Dataset and DataArray resampling are identical (pydata#3412) Avoid multiplication DeprecationWarning in rasterio backend (pydata#3428) Sync with latest version of cftime (v1.0.4) (pydata#3430) Add cftime git tip to upstream-dev + temporarily pin cftime (pydata#3431)
2 parents 179eff1 + 652dd3c commit c12e1da

26 files changed

+148
-113
lines changed

ci/azure/install.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@ steps:
1515
--no-deps \
1616
--pre \
1717
--upgrade \
18-
numpy \
1918
matplotlib \
20-
pandas \
19+
pandas=0.26.0.dev0+628.g03c1a3db2 \ # FIXME https://github.com/pydata/xarray/issues/3440
2120
scipy
21+
# numpy \ # FIXME https://github.com/pydata/xarray/issues/3409
2222
pip install \
2323
--no-deps \
2424
--upgrade \
2525
git+https://github.com/dask/dask \
2626
git+https://github.com/dask/distributed \
27-
git+https://github.com/zarr-developers/zarr
27+
git+https://github.com/zarr-developers/zarr \
28+
git+https://github.com/Unidata/cftime
2829
condition: eq(variables['UPSTREAM_DEV'], 'true')
2930
displayName: Install upstream dev dependencies
3031

ci/min_deps_check.py

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import sys
77
from concurrent.futures import ThreadPoolExecutor
88
from datetime import datetime, timedelta
9-
from typing import Dict, Iterator, Tuple
9+
from typing import Dict, Iterator, Optional, Tuple
1010

1111
import yaml
1212

@@ -34,10 +34,14 @@ def error(msg: str) -> None:
3434
print("ERROR:", msg)
3535

3636

37-
def parse_requirements(fname) -> Iterator[Tuple[str, int, int]]:
37+
def warning(msg: str) -> None:
38+
print("WARNING:", msg)
39+
40+
41+
def parse_requirements(fname) -> Iterator[Tuple[str, int, int, Optional[int]]]:
3842
"""Load requirements/py36-min-all-deps.yml
3943
40-
Yield (package name, major version, minor version)
44+
Yield (package name, major version, minor version, [patch version])
4145
"""
4246
global has_errors
4347

@@ -52,15 +56,18 @@ def parse_requirements(fname) -> Iterator[Tuple[str, int, int]]:
5256
if pkg.endswith("<") or pkg.endswith(">") or eq != "=":
5357
error("package should be pinned with exact version: " + row)
5458
continue
59+
5560
try:
56-
major, minor = version.split(".")
57-
except ValueError:
58-
error("expected major.minor (without patch): " + row)
59-
continue
60-
try:
61-
yield pkg, int(major), int(minor)
61+
version_tup = tuple(int(x) for x in version.split("."))
6262
except ValueError:
63-
error("failed to parse version: " + row)
63+
raise ValueError("non-numerical version: " + row)
64+
65+
if len(version_tup) == 2:
66+
yield (pkg, *version_tup, None) # type: ignore
67+
elif len(version_tup) == 3:
68+
yield (pkg, *version_tup) # type: ignore
69+
else:
70+
raise ValueError("expected major.minor or major.minor.patch: " + row)
6471

6572

6673
def query_conda(pkg: str) -> Dict[Tuple[int, int], datetime]:
@@ -80,9 +87,9 @@ def query_conda(pkg: str) -> Dict[Tuple[int, int], datetime]:
8087
label = label.strip()
8188
if label == "file name":
8289
value = value.strip()[len(pkg) :]
83-
major, minor = value.split("-")[1].split(".")[:2]
84-
major = int(major)
85-
minor = int(minor)
90+
smajor, sminor = value.split("-")[1].split(".")[:2]
91+
major = int(smajor)
92+
minor = int(sminor)
8693
if label == "timestamp":
8794
assert major is not None
8895
assert minor is not None
@@ -109,17 +116,15 @@ def query_conda(pkg: str) -> Dict[Tuple[int, int], datetime]:
109116

110117

111118
def process_pkg(
112-
pkg: str, req_major: int, req_minor: int
113-
) -> Tuple[str, int, int, str, int, int, str, str]:
119+
pkg: str, req_major: int, req_minor: int, req_patch: Optional[int]
120+
) -> Tuple[str, str, str, str, str, str]:
114121
"""Compare package version from requirements file to available versions in conda.
115122
Return row to build pandas dataframe:
116123
117124
- package name
118-
- major version in requirements file
119-
- minor version in requirements file
125+
- major.minor.[patch] version in requirements file
120126
- publication date of version in requirements file (YYYY-MM-DD)
121-
- major version suggested by policy
122-
- minor version suggested by policy
127+
- major.minor version suggested by policy
123128
- publication date of version suggested by policy (YYYY-MM-DD)
124129
- status ("<", "=", "> (!)")
125130
"""
@@ -130,7 +135,7 @@ def process_pkg(
130135
req_published = versions[req_major, req_minor]
131136
except KeyError:
132137
error("not found in conda: " + pkg)
133-
return pkg, req_major, req_minor, "-", 0, 0, "-", "(!)"
138+
return pkg, fmt_version(req_major, req_minor, req_patch), "-", "-", "-", "(!)"
134139

135140
policy_months = POLICY_MONTHS.get(pkg, POLICY_MONTHS_DEFAULT)
136141
policy_published = datetime.now() - timedelta(days=policy_months * 30)
@@ -153,30 +158,39 @@ def process_pkg(
153158
else:
154159
status = "="
155160

161+
if req_patch is not None:
162+
warning("patch version should not appear in requirements file: " + pkg)
163+
status += " (w)"
164+
156165
return (
157166
pkg,
158-
req_major,
159-
req_minor,
167+
fmt_version(req_major, req_minor, req_patch),
160168
req_published.strftime("%Y-%m-%d"),
161-
policy_major,
162-
policy_minor,
169+
fmt_version(policy_major, policy_minor),
163170
policy_published_actual.strftime("%Y-%m-%d"),
164171
status,
165172
)
166173

167174

175+
def fmt_version(major: int, minor: int, patch: int = None) -> str:
176+
if patch is None:
177+
return f"{major}.{minor}"
178+
else:
179+
return f"{major}.{minor}.{patch}"
180+
181+
168182
def main() -> None:
169183
fname = sys.argv[1]
170184
with ThreadPoolExecutor(8) as ex:
171185
futures = [
172-
ex.submit(process_pkg, pkg, major, minor)
173-
for pkg, major, minor in parse_requirements(fname)
186+
ex.submit(process_pkg, pkg, major, minor, patch)
187+
for pkg, major, minor, patch in parse_requirements(fname)
174188
]
175189
rows = [f.result() for f in futures]
176190

177-
print("Package Required Policy Status")
178-
print("------------- ----------------- ----------------- ------")
179-
fmt = "{:13} {:>1d}.{:<2d} ({:10}) {:>1d}.{:<2d} ({:10}) {}"
191+
print("Package Required Policy Status")
192+
print("------------- -------------------- -------------------- ------")
193+
fmt = "{:13} {:7} ({:10}) {:7} ({:10}) {}"
180194
for row in rows:
181195
print(fmt.format(*row))
182196

ci/requirements/py36-min-all-deps.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ dependencies:
1313
- cartopy=0.17
1414
- cdms2=3.1
1515
- cfgrib=0.9
16-
- cftime=1.0
16+
- cftime=1.0.3 # FIXME need 1.0.5 (not released yet); 1.0.4 is broken
1717
- coveralls
1818
- dask=1.2
1919
- distributed=1.27

ci/requirements/py36.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ dependencies:
99
- cartopy
1010
- cdms2
1111
- cfgrib
12-
- cftime
12+
- cftime<1.0.4 # FIXME need 1.0.5 (not released yet); 1.0.4 is broken
1313
- coveralls
1414
- dask
1515
- distributed
@@ -25,7 +25,7 @@ dependencies:
2525
- nc-time-axis
2626
- netcdf4
2727
- numba
28-
- numpy
28+
- numpy<1.18 # FIXME https://github.com/pydata/xarray/issues/3409
2929
- pandas
3030
- pint
3131
- pip

ci/requirements/py37-windows.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ dependencies:
88
- bottleneck
99
- cartopy
1010
# - cdms2 # Not available on Windows
11-
# - cfgrib>=0.9.2 # Causes Python interpreter crash on Windows
12-
- cftime
11+
# - cfgrib # Causes Python interpreter crash on Windows
12+
- cftime<1.0.4 # FIXME need 1.0.5 (not released yet); 1.0.4 is broken
1313
- coveralls
1414
- dask
1515
- distributed
@@ -25,7 +25,7 @@ dependencies:
2525
- nc-time-axis
2626
- netcdf4
2727
- numba
28-
- numpy
28+
- numpy<1.18 # FIXME https://github.com/pydata/xarray/issues/3409
2929
- pandas
3030
- pint
3131
- pip

ci/requirements/py37.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ dependencies:
99
- cartopy
1010
- cdms2
1111
- cfgrib
12-
- cftime
12+
- cftime<1.0.4 # FIXME need 1.0.5 (not released yet); 1.0.4 is broken
1313
- coveralls
1414
- dask
1515
- distributed
@@ -25,7 +25,7 @@ dependencies:
2525
- nc-time-axis
2626
- netcdf4
2727
- numba
28-
- numpy
28+
- numpy<1.18 # FIXME https://github.com/pydata/xarray/issues/3409
2929
- pandas
3030
- pint
3131
- pip

doc/contributing.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,12 +286,12 @@ How to build the *xarray* documentation
286286
Requirements
287287
~~~~~~~~~~~~
288288
Make sure to follow the instructions on :ref:`creating a development environment above <contributing.dev_env>`, but
289-
to build the docs you need to use the environment file ``doc/environment.yml``.
289+
to build the docs you need to use the environment file ``ci/requirements/doc.yml``.
290290

291291
.. code-block:: none
292292
293293
# Create and activate the docs environment
294-
conda env create -f doc/environment.yml
294+
conda env create -f ci/requirements/doc.yml
295295
conda activate xarray-docs
296296
297297
# or with older versions of Anaconda:

doc/terminology.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ Terminology
2727

2828
----
2929

30-
**Coordinate:** An array that labels a dimension of another ``DataArray``. Loosely, the coordinate array's values can be thought of as tick labels along a dimension. There are two types of coordinate arrays: *dimension coordinates* and *non-dimension coordinates* (see below). A coordinate named ``x`` can be retrieved from ``arr.coords[x]``. A ``DataArray`` can have more coordinates than dimensions because a single dimension can be assigned multiple coordinate arrays. However, only one coordinate array can be a assigned as a particular dimension's dimension coordinate array. As a consequence, ``len(arr.dims) <= len(arr.coords)`` in general.
30+
**Coordinate:** An array that labels a dimension or set of dimensions of another ``DataArray``. In the usual one-dimensional case, the coordinate array's values can loosely be thought of as tick labels along a dimension. There are two types of coordinate arrays: *dimension coordinates* and *non-dimension coordinates* (see below). A coordinate named ``x`` can be retrieved from ``arr.coords[x]``. A ``DataArray`` can have more coordinates than dimensions because a single dimension can be labeled by multiple coordinate arrays. However, only one coordinate array can be a assigned as a particular dimension's dimension coordinate array. As a consequence, ``len(arr.dims) <= len(arr.coords)`` in general.
3131

3232
----
3333

34-
**Dimension coordinate:** A coordinate array assigned to ``arr`` with both a name and dimension name in ``arr.dims``. Dimension coordinates are used for label-based indexing and alignment, like the index found on a :py:class:`pandas.DataFrame` or :py:class:`pandas.Series`. In fact, dimension coordinates use :py:class:`pandas.Index` objects under the hood for efficient computation. Dimension coordinates are marked by ``*`` when printing a ``DataArray`` or ``Dataset``.
34+
**Dimension coordinate:** A one-dimensional coordinate array assigned to ``arr`` with both a name and dimension name in ``arr.dims``. Dimension coordinates are used for label-based indexing and alignment, like the index found on a :py:class:`pandas.DataFrame` or :py:class:`pandas.Series`. In fact, dimension coordinates use :py:class:`pandas.Index` objects under the hood for efficient computation. Dimension coordinates are marked by ``*`` when printing a ``DataArray`` or ``Dataset``.
3535

3636
----
3737

38-
**Non-dimension coordinate:** A coordinate array assigned to ``arr`` with a name in ``arr.dims`` but a dimension name *not* in ``arr.dims``. These coordinate arrays are useful for auxiliary labeling. However, non-dimension coordinates are not indexed, and any operation on non-dimension coordinates that leverages indexing will fail. Printing ``arr.coords`` will print all of ``arr``'s coordinate names, with the assigned dimensions in parentheses. For example, ``coord_name (dim_name) 1 2 3 ...``.
38+
**Non-dimension coordinate:** A coordinate array assigned to ``arr`` with a name in ``arr.coords`` but *not* in ``arr.dims``. These coordinates arrays can be one-dimensional or multidimensional, and they are useful for auxiliary labeling. As an example, multidimensional coordinates are often used in geoscience datasets when :doc:`the data's physical coordinates (such as latitude and longitude) differ from their logical coordinates <examples/multidimensional-coords>`. However, non-dimension coordinates are not indexed, and any operation on non-dimension coordinates that leverages indexing will fail. Printing ``arr.coords`` will print all of ``arr``'s coordinate names, with the corresponding dimension(s) in parentheses. For example, ``coord_name (dim_name) 1 2 3 ...``.
3939

4040
----
4141

doc/whats-new.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ What's New
1818
v0.14.1 (unreleased)
1919
--------------------
2020

21+
Breaking changes
22+
~~~~~~~~~~~~~~~~
23+
24+
- Minimum cftime version is now 1.0.3. By `Deepak Cherian <https://github.com/dcherian>`_.
25+
2126
New Features
2227
~~~~~~~~~~~~
2328
- Added the ``max_gap`` kwarg to :py:meth:`~xarray.DataArray.interpolate_na` and
@@ -39,13 +44,19 @@ Bug fixes
3944
- Fix regression introduced in v0.14.0 that would cause a crash if dask is installed
4045
but cloudpickle isn't (:issue:`3401`) by `Rhys Doyle <https://github.com/rdoyle45>`_
4146

47+
- Sync with cftime by removing `dayofwk=-1` for cftime>=1.0.4.
48+
By `Anderson Banihirwe <https://github.com/andersy005>`_.
49+
50+
4251
Documentation
4352
~~~~~~~~~~~~~
4453

4554
- Fix the documentation of :py:meth:`DataArray.resample` and
4655
:py:meth:`Dataset.resample` and explicitly state that a
4756
datetime-like dimension is required. (:pull:`3400`)
4857
By `Justus Magin <https://github.com/keewis>`_.
58+
- Update the terminology page to address multidimensional coordinates. (:pull:`3410`)
59+
By `Jon Thielen <https://github.com/jthielen>`_.
4960

5061
Internal Changes
5162
~~~~~~~~~~~~~~~~

xarray/backends/h5netcdf_.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ def prepare_variable(
245245
dtype=dtype,
246246
dimensions=variable.dims,
247247
fillvalue=fillvalue,
248-
**kwargs
248+
**kwargs,
249249
)
250250
else:
251251
nc4_var = self.ds[name]

xarray/backends/rasterio_.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,8 @@ def open_rasterio(filename, parse_coordinates=None, chunks=None, cache=None, loc
257257
if parse:
258258
nx, ny = riods.width, riods.height
259259
# xarray coordinates are pixel centered
260-
x, _ = (np.arange(nx) + 0.5, np.zeros(nx) + 0.5) * riods.transform
261-
_, y = (np.zeros(ny) + 0.5, np.arange(ny) + 0.5) * riods.transform
260+
x, _ = riods.transform * (np.arange(nx) + 0.5, np.zeros(nx) + 0.5)
261+
_, y = riods.transform * (np.zeros(ny) + 0.5, np.arange(ny) + 0.5)
262262
coords["y"] = y
263263
coords["x"] = x
264264
else:

xarray/backends/zarr.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ def open_zarr(
467467
drop_variables=None,
468468
consolidated=False,
469469
overwrite_encoded_chunks=False,
470-
**kwargs
470+
**kwargs,
471471
):
472472
"""Load and decode a dataset from a Zarr store.
473473

xarray/coding/cftime_offsets.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
from ..core.pdcompat import count_not_none
5151
from .cftimeindex import CFTimeIndex, _parse_iso8601_with_reso
5252
from .times import format_cftime_datetime
53+
from distutils.version import LooseVersion
5354

5455

5556
def get_date_type(calendar):
@@ -222,6 +223,8 @@ def _adjust_n_years(other, n, month, reference_day):
222223
def _shift_month(date, months, day_option="start"):
223224
"""Shift the date to a month start or end a given number of months away.
224225
"""
226+
import cftime
227+
225228
delta_year = (date.month + months) // 12
226229
month = (date.month + months) % 12
227230

@@ -237,11 +240,14 @@ def _shift_month(date, months, day_option="start"):
237240
day = _days_in_month(reference)
238241
else:
239242
raise ValueError(day_option)
240-
# dayofwk=-1 is required to update the dayofwk and dayofyr attributes of
241-
# the returned date object in versions of cftime between 1.0.2 and
242-
# 1.0.3.4. It can be removed for versions of cftime greater than
243-
# 1.0.3.4.
244-
return date.replace(year=year, month=month, day=day, dayofwk=-1)
243+
if LooseVersion(cftime.__version__) < LooseVersion("1.0.4"):
244+
# dayofwk=-1 is required to update the dayofwk and dayofyr attributes of
245+
# the returned date object in versions of cftime between 1.0.2 and
246+
# 1.0.3.4. It can be removed for versions of cftime greater than
247+
# 1.0.3.4.
248+
return date.replace(year=year, month=month, day=day, dayofwk=-1)
249+
else:
250+
return date.replace(year=year, month=month, day=day)
245251

246252

247253
def roll_qtrday(other, n, month, day_option, modby=3):

xarray/coding/cftimeindex.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ def parse_iso8601(datetime_string):
9696

9797

9898
def _parse_iso8601_with_reso(date_type, timestr):
99+
import cftime
100+
99101
default = date_type(1, 1, 1)
100102
result = parse_iso8601(timestr)
101103
replace = {}
@@ -107,12 +109,12 @@ def _parse_iso8601_with_reso(date_type, timestr):
107109
# TODO: Consider adding support for sub-second resolution?
108110
replace[attr] = int(value)
109111
resolution = attr
110-
111-
# dayofwk=-1 is required to update the dayofwk and dayofyr attributes of
112-
# the returned date object in versions of cftime between 1.0.2 and
113-
# 1.0.3.4. It can be removed for versions of cftime greater than
114-
# 1.0.3.4.
115-
replace["dayofwk"] = -1
112+
if LooseVersion(cftime.__version__) < LooseVersion("1.0.4"):
113+
# dayofwk=-1 is required to update the dayofwk and dayofyr attributes of
114+
# the returned date object in versions of cftime between 1.0.2 and
115+
# 1.0.3.4. It can be removed for versions of cftime greater than
116+
# 1.0.3.4.
117+
replace["dayofwk"] = -1
116118
return default.replace(**replace), resolution
117119

118120

0 commit comments

Comments
 (0)