Improved default behavior when concatenating DataArrays - #2777

Closed
Zac-HD wants to merge 3 commits into
pydata:masterfrom
Zac-HD:concat-arrays
Closed

Improved default behavior when concatenating DataArrays#2777
Zac-HD wants to merge 3 commits into
pydata:masterfrom
Zac-HD:concat-arrays

Conversation

@Zac-HD

@Zac-HDZac-HD commented Feb 19, 2019

Copy link
Copy Markdown
Contributor

This is really nice to have when producing faceted plots of satellite observations in various bands, and should be somewhere between useful and harmless in other cases.

Example code:

ds=xr.Dataset({
k: xr.DataArray(np.random.random((2, 2)), dims="x y".split(), name=k) forkin"blue green red".split()
})
xr.concat([ds.blue, ds.green, ds.red], dim="band").plot.imshow(col="band")

Before - facets have an index, colorbar has misleading label:

image

After - facets have meaningful labels, colorbar has no label:

image

@Zac-HD
Zac-HDforce-pushed the concat-arrays branch 2 times, most recently from 280ce92 to a2df249CompareFebruary 19, 2019 06:36

@shoyershoyer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a nice usability improvement!

Comment threadxarray/core/combine.py Outdated
Comment threadxarray/core/combine.py Outdated
@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

Thanks for the support and quick review @shoyer!

Any idea when Xarray 0.12 might be out? I'm teaching some remote sensing workshops in mid-March and would love to have this merged, as a colleague's review of those notebooks prompted this PR 😄

@Zac-HDZac-HD mentioned this pull request Feb 20, 2019
Comment threadxarray/core/combine.py Outdated
@Zac-HD

Zac-HD commented Feb 20, 2019

Copy link
Copy Markdown
ContributorAuthor

The docs build failed due to a (transient) http error when loading tutorial data for the docs, so I've also finalised the planned conversion from xarray.tutorial.load_dataset to xarray.tutorial.open_dataset.

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

Hmm, it looks like the failure to download the naturalearth coastlines.zip wasn't so transient after all - but it does work on my machine!

@Zac-HDZac-HD closed this Feb 22, 2019
@Zac-HDZac-HD reopened this Feb 22, 2019
@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

OK! @shoyer, I've got everything passing and it's ready for review.

Even the accidental tutorial/docs fixes 😄

Comment threaddoc/whats-new.rst Outdated
Comment threadxarray/core/combine.py Outdated
@shoyer

shoyer commented Feb 22, 2019 via email

Copy link
Copy Markdown
Member

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

@shoyer - don't worry about the docs build, I'm pretty sure that was just a flaky network from Travis and it's working now in any case.

I've left tutorial.load_dataset in, just changed "removed in 0.12" to "removed in a future version".

@shoyershoyer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me now. I'll merge in a day or two unless anyone else has review comments to add.

Comment threadxarray/core/combine.py Outdated
@pep8speaks

pep8speaks commented Feb 26, 2019

Copy link
Copy Markdown

Hello @Zac-HD! Thanks for updating the PR.

Cheers ! There are no PEP8 issues in this Pull Request. 🍻

Comment last updated on February 27, 2019 at 00:51 Hours UTC

@shoyer

Copy link
Copy Markdown
Member

@pep8speaks seems to have gone hay-wire -- maybe you have a syntax error?

Thinking about this a little more, one hazard of converting names into index labels is that we lose the invariant that you get the same result regardless of order in which you call concat, e.g., something like these expressions could now give different results:

xarray.concat([a, b], dim='x')

vs

xarray.concat([xarray.concat([a], dim='x'), xarray.concat([b], dim='x')], dim='x')

If a and b have the same name, then you'd get an index with the two duplicate entries in the second case but not the first case.

I'm not entirely sure this is a deal-breaker but it makes me a little nervous reluctant. In particular, it might break some the invariants we're relying upon for the next version of open_mfdataset (#2616, cc @TomNicholas )

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

maybe you have a syntax error?

...yep, an unmatched paren. 😥

If a and b have the same name, then you'd get an index with the two duplicate entries in the second case but not the first case. [which would be bad]

I think it's impossible to avoid this when using inference in the general case. Two options I think would be decent-if-unsatisfying:

  1. Explicitly manage this in the new combining functions, e.g. clear the concat dim coords if they are not unique and the input arrays did not have coords in that dimension.
  2. Add an argument to xr.concat to enable or disable this, e.g. infer_coords=True, and disable it when calling xr.concat from other combining functions.

Zac-HDand others added 2 commits February 27, 2019 11:50
This is really nice to have when using concat to produce faceted plots of various kinds, and harmless when it's useless.
it's still deprecated, but we'll leave it for a bit longer before removal.
@TomNicholas

TomNicholas commented Feb 27, 2019

Copy link
Copy Markdown
Member

@Zac-HD forgive me for this but I think this PR is unnecessary because what you need basically already exists in the API.

Going back to your original example, you could have got the same indexing by creating a DataArray to use as a coordinate to concatenate over:

colors="blue green red".split()
ds=xr.Dataset({
k: xr.DataArray(np.random.random((2, 2)), dims="x y".split(), name=k)
forkincolors
})
band=xr.DataArray(colors, name="band", dims=["band"])
xr.concat([ds.blue, ds.green, ds.red], dim=band).plot.imshow(col="band")

figure_1

This still leaves the wrong label on the colorbar, but that could be fixed separately and has to do with concat using the attrs of the first dataset in the list for the final dataset (a similar problem to #2382). I think it would be easier to change that behaviour instead (perhaps to if all names the same, use that name, else name of result = None, but this also relates to #1614).

Creating a new coordinate using a DataArray is in the docstring for xr.concat:

If dimension is provided as a DataArray or Index, its name is used as the dimension to concatenate along and the values are added as a coordinate.

but I think there should be an example there too. (Also I think this is relevant to #1646)

I'm not entirely sure this is a deal-breaker but it makes me a little nervous

@shoyer I agree, although I like the idea then I think this could introduce all sorts of complex concatentation edge cases.

At the very least the new API should have symmetry properties something like:

da1=DataArray(name='a', data=[[0]], dims=['x', 'y'])
da2=DataArray(name='b', data=[[1]], dims=['x', 'y'])
da3=DataArray(name='a', data=[[2]], dims=['x', 'y'])
da4=DataArray(name='b', data=[[3]], dims=['x', 'y'])
xr.manual_combine([[da1, da2], [da3, da4]], concat_dim=['x', 'y'])
# should give the same result as xr.manual_combine([[da1, da3], [da2, da4]], concat_dim=['y', 'x'])

but with this PR I don't think it would. In the first case the x coord would be created with values ['a', 'b'], and no y coord would be created, while in the second case no y coord would be created, and the intermediate DataSet would be nameless, so then no x coord would be created either.

I think my suggestion for naming would pass this test because the result would be nameless and have no coords either way.

I might have got that wrong but I definitely think this kind of change should be carefully considered 😕

(EDIT: I just added this example as a test to #2616)

@TomNicholasTomNicholas mentioned this pull request Feb 27, 2019
3 tasks
@TomNicholas

TomNicholas commented Feb 27, 2019

Copy link
Copy Markdown
Member

I've just submitted a PR which solves this issue in the way I just suggested instead #2792.

@TomNicholas

Copy link
Copy Markdown
Member

@Zac-HD there's actually another way to get the indexing behaviour you wanted with the current API:

colors="blue green red".split()
das= [xr.DataArray(np.random.random((2, 2)), dims="x y".split(),
coords={"band": k})
forkincolors]
xr.concat(das, dim="band").plot.imshow(col="band")

Here instead of using the name attribute to label each band I've used a scalar coordinate called "band", so that when you concat along "band" it will just stack along that coordinate.

This never touches the names so actually gives the desired output without the need for #2792:
figure_2

@shoyer

Copy link
Copy Markdown
Member

I guess we should probably roll back the "name to scalar coordinates" part of this change.

@Zac-HD do you want to do that here or should we go with @TomNicholas's PR?

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

No objection to going with #2792; I'm just happy to have the change merged 😄

It would be nice for someone to cherry-pick 63da214 before releasing 0.12 though, just to fix that warning.

@Zac-HDZac-HD closed this Mar 3, 2019
@shoyershoyer mentioned this pull request Mar 12, 2019
3 tasks
shoyer pushed a commit that referenced this pull request Jun 25, 2019
* concatenates along a single dimension
* Wrote function to find correct tile_IDs from nested list of datasets
* Wrote function to check that combined_tile_ids structure is valid
* Added test of 2d-concatenation
* Tests now check that dataset ordering is correct
* Test concatentation along a new dimension
* Started generalising auto_combine to N-D by integrating the N-D concatentation algorithm
* All unit tests now passing
* Fixed a failing test which I didn't notice because I don't have pseudoNetCDF
* Began updating open_mfdataset to handle N-D input
* Refactored to remove duplicate logic in open_mfdataset & auto_combine
* Implemented Shoyers suggestion in #2553 to rewrite the recursive nested list traverser as an iterator
* --amend
* Now raises ValueError if input not ordered correctly before concatenation
* Added some more prototype tests defining desired behaviour more clearly
* Now raises informative errors on invalid forms of input
* Refactoring to alos merge along each dimension
* Refactored to literally just apply the old auto_combine along each dimension
* Added unit tests for open_mfdatset
* Removed TODOs
* Removed format strings
* test_get_new_tile_ids now doesn't assume dicts are ordered
* Fixed failing tests on python3.5 caused by accidentally assuming dict was ordered
* Test for getting new tile id
* Fixed itertoolz import so that it's compatible with older versions
* Increased test coverage
* Added toolz as an explicit dependency to pass tests on python2.7
* Updated 'what's new'
* No longer attempts to shortcut all concatenation at once if concat_dims=None
* Rewrote using itertools.groupby instead of toolz.itertoolz.groupby to remove hidden dependency on toolz
* Fixed erroneous removal of utils import
* Updated docstrings to include an example of multidimensional concatenation
* Clarified auto_combine docstring for N-D behaviour
* Added unit test for nested list of Datasets with different variables
* Minor spelling and pep8 fixes
* Started working on a new api with both auto_combine and manual_combine
* Wrote basic function to infer concatenation order from coords.
Needs better error handling though.
* Attempt at finalised version of public-facing API.
All the internals still need to be redone to match though.
* No longer uses entire old auto_combine internally, only concat or merge
* Updated what's new
* Removed uneeded addition to what's new for old release
* Fixed incomplete merge in docstring for open_mfdataset
* Tests for manual combine passing
* Tests for auto_combine now passing
* xfailed weird behaviour with manual_combine trying to determine concat_dim
* Add auto_combine and manual_combine to API page of docs
* Tests now passing for open_mfdataset
* Completed merge so that #2648 is respected, and added tests.
Also moved concat to it's own file to avoid a circular dependency
* Separated the tests for concat and both combines
* Some PEP8 fixes
* Pre-empting a test which will fail with opening uamiv format
* Satisfy pep8speaks bot
* Python 3.5 compatibile after changing some error string formatting
* Order coords using pandas.Index objects
* Fixed performance bug from GH #2662
* Removed ToDos about natural sorting of string coords
* Generalized auto_combine to handle monotonically-decreasing coords too
* Added more examples to docstring for manual_combine
* Added note about globbing aspect of open_mfdataset
* Removed auto-inferring of concatenation dimension in manual_combine
* Added example to docstring for auto_combine
* Minor correction to docstring
* Another very minor docstring correction
* Added test to guard against issue #2777
* Started deprecation cycle for auto_combine
* Fully reverted open_mfdataset tests
* Updated what's new to match deprecation cycle
* Reverted uamiv test
* Removed dependency on itertools
* Deprecation tests fixed
* Satisfy pycodestyle
* Started deprecation cycle of auto_combine
* Added specific error for edge case combine_manual can't handle
* Check that global coordinates are monotonic
* Highlighted weird behaviour when concatenating with no data variables
* Added test for impossible-to-auto-combine coordinates
* Removed uneeded test
* Satisfy linter
* Added airspeedvelocity benchmark for combining functions
* Benchmark will take longer now
* Updated version numbers in deprecation warnings to fit with recent release of 0.12
* Updated api docs for new function names
* Fixed docs build failure
* Revert "Fixed docs build failure"
This reverts commit ddfc6dd.
* Updated documentation with section explaining new functions
* Suppressed deprecation warnings in test suite
* Resolved ToDo by pointing to issue with concat, see #2975
* Various docs fixes
* Slightly renamed tests to match new name of tested function
* Included minor suggestions from shoyer
* Removed trailing whitespace
* Simplified error message for case combine_manual can't handle
* Removed filter for deprecation warnings, and added test for if user doesn't supply concat_dim
* Simple fixes suggested by shoyer
* Change deprecation warning behaviour
* linting
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@Zac-HD@shoyer@pep8speaks@TomNicholas@dcherian
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Improved default behavior when concatenating DataArrays - #2777

Closed
Zac-HD wants to merge 3 commits into
pydata:masterfrom
Zac-HD:concat-arrays
Closed

Improved default behavior when concatenating DataArrays#2777
Zac-HD wants to merge 3 commits into
pydata:masterfrom
Zac-HD:concat-arrays

Conversation

@Zac-HD

@Zac-HDZac-HD commented Feb 19, 2019

Copy link
Copy Markdown
Contributor

This is really nice to have when producing faceted plots of satellite observations in various bands, and should be somewhere between useful and harmless in other cases.

Example code:

ds=xr.Dataset({
k: xr.DataArray(np.random.random((2, 2)), dims="x y".split(), name=k) forkin"blue green red".split()
})
xr.concat([ds.blue, ds.green, ds.red], dim="band").plot.imshow(col="band")

Before - facets have an index, colorbar has misleading label:

image

After - facets have meaningful labels, colorbar has no label:

image

@Zac-HD
Zac-HDforce-pushed the concat-arrays branch 2 times, most recently from 280ce92 to a2df249CompareFebruary 19, 2019 06:36

@shoyershoyer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a nice usability improvement!

Comment threadxarray/core/combine.py Outdated
Comment threadxarray/core/combine.py Outdated
@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

Thanks for the support and quick review @shoyer!

Any idea when Xarray 0.12 might be out? I'm teaching some remote sensing workshops in mid-March and would love to have this merged, as a colleague's review of those notebooks prompted this PR 😄

@Zac-HDZac-HD mentioned this pull request Feb 20, 2019
Comment threadxarray/core/combine.py Outdated
@Zac-HD

Zac-HD commented Feb 20, 2019

Copy link
Copy Markdown
ContributorAuthor

The docs build failed due to a (transient) http error when loading tutorial data for the docs, so I've also finalised the planned conversion from xarray.tutorial.load_dataset to xarray.tutorial.open_dataset.

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

Hmm, it looks like the failure to download the naturalearth coastlines.zip wasn't so transient after all - but it does work on my machine!

@Zac-HDZac-HD closed this Feb 22, 2019
@Zac-HDZac-HD reopened this Feb 22, 2019
@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

OK! @shoyer, I've got everything passing and it's ready for review.

Even the accidental tutorial/docs fixes 😄

Comment threaddoc/whats-new.rst Outdated
Comment threadxarray/core/combine.py Outdated
@shoyer

shoyer commented Feb 22, 2019 via email

Copy link
Copy Markdown
Member

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

@shoyer - don't worry about the docs build, I'm pretty sure that was just a flaky network from Travis and it's working now in any case.

I've left tutorial.load_dataset in, just changed "removed in 0.12" to "removed in a future version".

@shoyershoyer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me now. I'll merge in a day or two unless anyone else has review comments to add.

Comment threadxarray/core/combine.py Outdated
@pep8speaks

pep8speaks commented Feb 26, 2019

Copy link
Copy Markdown

Hello @Zac-HD! Thanks for updating the PR.

Cheers ! There are no PEP8 issues in this Pull Request. 🍻

Comment last updated on February 27, 2019 at 00:51 Hours UTC

@shoyer

Copy link
Copy Markdown
Member

@pep8speaks seems to have gone hay-wire -- maybe you have a syntax error?

Thinking about this a little more, one hazard of converting names into index labels is that we lose the invariant that you get the same result regardless of order in which you call concat, e.g., something like these expressions could now give different results:

xarray.concat([a, b], dim='x')

vs

xarray.concat([xarray.concat([a], dim='x'), xarray.concat([b], dim='x')], dim='x')

If a and b have the same name, then you'd get an index with the two duplicate entries in the second case but not the first case.

I'm not entirely sure this is a deal-breaker but it makes me a little nervous reluctant. In particular, it might break some the invariants we're relying upon for the next version of open_mfdataset (#2616, cc @TomNicholas )

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

maybe you have a syntax error?

...yep, an unmatched paren. 😥

If a and b have the same name, then you'd get an index with the two duplicate entries in the second case but not the first case. [which would be bad]

I think it's impossible to avoid this when using inference in the general case. Two options I think would be decent-if-unsatisfying:

  1. Explicitly manage this in the new combining functions, e.g. clear the concat dim coords if they are not unique and the input arrays did not have coords in that dimension.
  2. Add an argument to xr.concat to enable or disable this, e.g. infer_coords=True, and disable it when calling xr.concat from other combining functions.

Zac-HDand others added 2 commits February 27, 2019 11:50
This is really nice to have when using concat to produce faceted plots of various kinds, and harmless when it's useless.
it's still deprecated, but we'll leave it for a bit longer before removal.
@TomNicholas

TomNicholas commented Feb 27, 2019

Copy link
Copy Markdown
Member

@Zac-HD forgive me for this but I think this PR is unnecessary because what you need basically already exists in the API.

Going back to your original example, you could have got the same indexing by creating a DataArray to use as a coordinate to concatenate over:

colors="blue green red".split()
ds=xr.Dataset({
k: xr.DataArray(np.random.random((2, 2)), dims="x y".split(), name=k)
forkincolors
})
band=xr.DataArray(colors, name="band", dims=["band"])
xr.concat([ds.blue, ds.green, ds.red], dim=band).plot.imshow(col="band")

figure_1

This still leaves the wrong label on the colorbar, but that could be fixed separately and has to do with concat using the attrs of the first dataset in the list for the final dataset (a similar problem to #2382). I think it would be easier to change that behaviour instead (perhaps to if all names the same, use that name, else name of result = None, but this also relates to #1614).

Creating a new coordinate using a DataArray is in the docstring for xr.concat:

If dimension is provided as a DataArray or Index, its name is used as the dimension to concatenate along and the values are added as a coordinate.

but I think there should be an example there too. (Also I think this is relevant to #1646)

I'm not entirely sure this is a deal-breaker but it makes me a little nervous

@shoyer I agree, although I like the idea then I think this could introduce all sorts of complex concatentation edge cases.

At the very least the new API should have symmetry properties something like:

da1=DataArray(name='a', data=[[0]], dims=['x', 'y'])
da2=DataArray(name='b', data=[[1]], dims=['x', 'y'])
da3=DataArray(name='a', data=[[2]], dims=['x', 'y'])
da4=DataArray(name='b', data=[[3]], dims=['x', 'y'])
xr.manual_combine([[da1, da2], [da3, da4]], concat_dim=['x', 'y'])
# should give the same result as xr.manual_combine([[da1, da3], [da2, da4]], concat_dim=['y', 'x'])

but with this PR I don't think it would. In the first case the x coord would be created with values ['a', 'b'], and no y coord would be created, while in the second case no y coord would be created, and the intermediate DataSet would be nameless, so then no x coord would be created either.

I think my suggestion for naming would pass this test because the result would be nameless and have no coords either way.

I might have got that wrong but I definitely think this kind of change should be carefully considered 😕

(EDIT: I just added this example as a test to #2616)

@TomNicholasTomNicholas mentioned this pull request Feb 27, 2019
3 tasks
@TomNicholas

TomNicholas commented Feb 27, 2019

Copy link
Copy Markdown
Member

I've just submitted a PR which solves this issue in the way I just suggested instead #2792.

@TomNicholas

Copy link
Copy Markdown
Member

@Zac-HD there's actually another way to get the indexing behaviour you wanted with the current API:

colors="blue green red".split()
das= [xr.DataArray(np.random.random((2, 2)), dims="x y".split(),
coords={"band": k})
forkincolors]
xr.concat(das, dim="band").plot.imshow(col="band")

Here instead of using the name attribute to label each band I've used a scalar coordinate called "band", so that when you concat along "band" it will just stack along that coordinate.

This never touches the names so actually gives the desired output without the need for #2792:
figure_2

@shoyer

Copy link
Copy Markdown
Member

I guess we should probably roll back the "name to scalar coordinates" part of this change.

@Zac-HD do you want to do that here or should we go with @TomNicholas's PR?

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

No objection to going with #2792; I'm just happy to have the change merged 😄

It would be nice for someone to cherry-pick 63da214 before releasing 0.12 though, just to fix that warning.

@Zac-HDZac-HD closed this Mar 3, 2019
@shoyershoyer mentioned this pull request Mar 12, 2019
3 tasks
shoyer pushed a commit that referenced this pull request Jun 25, 2019
* concatenates along a single dimension
* Wrote function to find correct tile_IDs from nested list of datasets
* Wrote function to check that combined_tile_ids structure is valid
* Added test of 2d-concatenation
* Tests now check that dataset ordering is correct
* Test concatentation along a new dimension
* Started generalising auto_combine to N-D by integrating the N-D concatentation algorithm
* All unit tests now passing
* Fixed a failing test which I didn't notice because I don't have pseudoNetCDF
* Began updating open_mfdataset to handle N-D input
* Refactored to remove duplicate logic in open_mfdataset & auto_combine
* Implemented Shoyers suggestion in #2553 to rewrite the recursive nested list traverser as an iterator
* --amend
* Now raises ValueError if input not ordered correctly before concatenation
* Added some more prototype tests defining desired behaviour more clearly
* Now raises informative errors on invalid forms of input
* Refactoring to alos merge along each dimension
* Refactored to literally just apply the old auto_combine along each dimension
* Added unit tests for open_mfdatset
* Removed TODOs
* Removed format strings
* test_get_new_tile_ids now doesn't assume dicts are ordered
* Fixed failing tests on python3.5 caused by accidentally assuming dict was ordered
* Test for getting new tile id
* Fixed itertoolz import so that it's compatible with older versions
* Increased test coverage
* Added toolz as an explicit dependency to pass tests on python2.7
* Updated 'what's new'
* No longer attempts to shortcut all concatenation at once if concat_dims=None
* Rewrote using itertools.groupby instead of toolz.itertoolz.groupby to remove hidden dependency on toolz
* Fixed erroneous removal of utils import
* Updated docstrings to include an example of multidimensional concatenation
* Clarified auto_combine docstring for N-D behaviour
* Added unit test for nested list of Datasets with different variables
* Minor spelling and pep8 fixes
* Started working on a new api with both auto_combine and manual_combine
* Wrote basic function to infer concatenation order from coords.
Needs better error handling though.
* Attempt at finalised version of public-facing API.
All the internals still need to be redone to match though.
* No longer uses entire old auto_combine internally, only concat or merge
* Updated what's new
* Removed uneeded addition to what's new for old release
* Fixed incomplete merge in docstring for open_mfdataset
* Tests for manual combine passing
* Tests for auto_combine now passing
* xfailed weird behaviour with manual_combine trying to determine concat_dim
* Add auto_combine and manual_combine to API page of docs
* Tests now passing for open_mfdataset
* Completed merge so that #2648 is respected, and added tests.
Also moved concat to it's own file to avoid a circular dependency
* Separated the tests for concat and both combines
* Some PEP8 fixes
* Pre-empting a test which will fail with opening uamiv format
* Satisfy pep8speaks bot
* Python 3.5 compatibile after changing some error string formatting
* Order coords using pandas.Index objects
* Fixed performance bug from GH #2662
* Removed ToDos about natural sorting of string coords
* Generalized auto_combine to handle monotonically-decreasing coords too
* Added more examples to docstring for manual_combine
* Added note about globbing aspect of open_mfdataset
* Removed auto-inferring of concatenation dimension in manual_combine
* Added example to docstring for auto_combine
* Minor correction to docstring
* Another very minor docstring correction
* Added test to guard against issue #2777
* Started deprecation cycle for auto_combine
* Fully reverted open_mfdataset tests
* Updated what's new to match deprecation cycle
* Reverted uamiv test
* Removed dependency on itertools
* Deprecation tests fixed
* Satisfy pycodestyle
* Started deprecation cycle of auto_combine
* Added specific error for edge case combine_manual can't handle
* Check that global coordinates are monotonic
* Highlighted weird behaviour when concatenating with no data variables
* Added test for impossible-to-auto-combine coordinates
* Removed uneeded test
* Satisfy linter
* Added airspeedvelocity benchmark for combining functions
* Benchmark will take longer now
* Updated version numbers in deprecation warnings to fit with recent release of 0.12
* Updated api docs for new function names
* Fixed docs build failure
* Revert "Fixed docs build failure"
This reverts commit ddfc6dd.
* Updated documentation with section explaining new functions
* Suppressed deprecation warnings in test suite
* Resolved ToDo by pointing to issue with concat, see #2975
* Various docs fixes
* Slightly renamed tests to match new name of tested function
* Included minor suggestions from shoyer
* Removed trailing whitespace
* Simplified error message for case combine_manual can't handle
* Removed filter for deprecation warnings, and added test for if user doesn't supply concat_dim
* Simple fixes suggested by shoyer
* Change deprecation warning behaviour
* linting
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@Zac-HD@shoyer@pep8speaks@TomNicholas@dcherian
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Improved default behavior when concatenating DataArrays - #2777

Closed
Zac-HD wants to merge 3 commits into
pydata:masterfrom
Zac-HD:concat-arrays
Closed

Improved default behavior when concatenating DataArrays#2777
Zac-HD wants to merge 3 commits into
pydata:masterfrom
Zac-HD:concat-arrays

Conversation

@Zac-HD

@Zac-HDZac-HD commented Feb 19, 2019

Copy link
Copy Markdown
Contributor

This is really nice to have when producing faceted plots of satellite observations in various bands, and should be somewhere between useful and harmless in other cases.

Example code:

ds=xr.Dataset({
k: xr.DataArray(np.random.random((2, 2)), dims="x y".split(), name=k) forkin"blue green red".split()
})
xr.concat([ds.blue, ds.green, ds.red], dim="band").plot.imshow(col="band")

Before - facets have an index, colorbar has misleading label:

image

After - facets have meaningful labels, colorbar has no label:

image

@Zac-HD
Zac-HDforce-pushed the concat-arrays branch 2 times, most recently from 280ce92 to a2df249CompareFebruary 19, 2019 06:36

@shoyershoyer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a nice usability improvement!

Comment threadxarray/core/combine.py Outdated
Comment threadxarray/core/combine.py Outdated
@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

Thanks for the support and quick review @shoyer!

Any idea when Xarray 0.12 might be out? I'm teaching some remote sensing workshops in mid-March and would love to have this merged, as a colleague's review of those notebooks prompted this PR 😄

@Zac-HDZac-HD mentioned this pull request Feb 20, 2019
Comment threadxarray/core/combine.py Outdated
@Zac-HD

Zac-HD commented Feb 20, 2019

Copy link
Copy Markdown
ContributorAuthor

The docs build failed due to a (transient) http error when loading tutorial data for the docs, so I've also finalised the planned conversion from xarray.tutorial.load_dataset to xarray.tutorial.open_dataset.

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

Hmm, it looks like the failure to download the naturalearth coastlines.zip wasn't so transient after all - but it does work on my machine!

@Zac-HDZac-HD closed this Feb 22, 2019
@Zac-HDZac-HD reopened this Feb 22, 2019
@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

OK! @shoyer, I've got everything passing and it's ready for review.

Even the accidental tutorial/docs fixes 😄

Comment threaddoc/whats-new.rst Outdated
Comment threadxarray/core/combine.py Outdated
@shoyer

shoyer commented Feb 22, 2019 via email

Copy link
Copy Markdown
Member

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

@shoyer - don't worry about the docs build, I'm pretty sure that was just a flaky network from Travis and it's working now in any case.

I've left tutorial.load_dataset in, just changed "removed in 0.12" to "removed in a future version".

@shoyershoyer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me now. I'll merge in a day or two unless anyone else has review comments to add.

Comment threadxarray/core/combine.py Outdated
@pep8speaks

pep8speaks commented Feb 26, 2019

Copy link
Copy Markdown

Hello @Zac-HD! Thanks for updating the PR.

Cheers ! There are no PEP8 issues in this Pull Request. 🍻

Comment last updated on February 27, 2019 at 00:51 Hours UTC

@shoyer

Copy link
Copy Markdown
Member

@pep8speaks seems to have gone hay-wire -- maybe you have a syntax error?

Thinking about this a little more, one hazard of converting names into index labels is that we lose the invariant that you get the same result regardless of order in which you call concat, e.g., something like these expressions could now give different results:

xarray.concat([a, b], dim='x')

vs

xarray.concat([xarray.concat([a], dim='x'), xarray.concat([b], dim='x')], dim='x')

If a and b have the same name, then you'd get an index with the two duplicate entries in the second case but not the first case.

I'm not entirely sure this is a deal-breaker but it makes me a little nervous reluctant. In particular, it might break some the invariants we're relying upon for the next version of open_mfdataset (#2616, cc @TomNicholas )

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

maybe you have a syntax error?

...yep, an unmatched paren. 😥

If a and b have the same name, then you'd get an index with the two duplicate entries in the second case but not the first case. [which would be bad]

I think it's impossible to avoid this when using inference in the general case. Two options I think would be decent-if-unsatisfying:

  1. Explicitly manage this in the new combining functions, e.g. clear the concat dim coords if they are not unique and the input arrays did not have coords in that dimension.
  2. Add an argument to xr.concat to enable or disable this, e.g. infer_coords=True, and disable it when calling xr.concat from other combining functions.

Zac-HDand others added 2 commits February 27, 2019 11:50
This is really nice to have when using concat to produce faceted plots of various kinds, and harmless when it's useless.
it's still deprecated, but we'll leave it for a bit longer before removal.
@TomNicholas

TomNicholas commented Feb 27, 2019

Copy link
Copy Markdown
Member

@Zac-HD forgive me for this but I think this PR is unnecessary because what you need basically already exists in the API.

Going back to your original example, you could have got the same indexing by creating a DataArray to use as a coordinate to concatenate over:

colors="blue green red".split()
ds=xr.Dataset({
k: xr.DataArray(np.random.random((2, 2)), dims="x y".split(), name=k)
forkincolors
})
band=xr.DataArray(colors, name="band", dims=["band"])
xr.concat([ds.blue, ds.green, ds.red], dim=band).plot.imshow(col="band")

figure_1

This still leaves the wrong label on the colorbar, but that could be fixed separately and has to do with concat using the attrs of the first dataset in the list for the final dataset (a similar problem to #2382). I think it would be easier to change that behaviour instead (perhaps to if all names the same, use that name, else name of result = None, but this also relates to #1614).

Creating a new coordinate using a DataArray is in the docstring for xr.concat:

If dimension is provided as a DataArray or Index, its name is used as the dimension to concatenate along and the values are added as a coordinate.

but I think there should be an example there too. (Also I think this is relevant to #1646)

I'm not entirely sure this is a deal-breaker but it makes me a little nervous

@shoyer I agree, although I like the idea then I think this could introduce all sorts of complex concatentation edge cases.

At the very least the new API should have symmetry properties something like:

da1=DataArray(name='a', data=[[0]], dims=['x', 'y'])
da2=DataArray(name='b', data=[[1]], dims=['x', 'y'])
da3=DataArray(name='a', data=[[2]], dims=['x', 'y'])
da4=DataArray(name='b', data=[[3]], dims=['x', 'y'])
xr.manual_combine([[da1, da2], [da3, da4]], concat_dim=['x', 'y'])
# should give the same result as xr.manual_combine([[da1, da3], [da2, da4]], concat_dim=['y', 'x'])

but with this PR I don't think it would. In the first case the x coord would be created with values ['a', 'b'], and no y coord would be created, while in the second case no y coord would be created, and the intermediate DataSet would be nameless, so then no x coord would be created either.

I think my suggestion for naming would pass this test because the result would be nameless and have no coords either way.

I might have got that wrong but I definitely think this kind of change should be carefully considered 😕

(EDIT: I just added this example as a test to #2616)

@TomNicholasTomNicholas mentioned this pull request Feb 27, 2019
3 tasks
@TomNicholas

TomNicholas commented Feb 27, 2019

Copy link
Copy Markdown
Member

I've just submitted a PR which solves this issue in the way I just suggested instead #2792.

@TomNicholas

Copy link
Copy Markdown
Member

@Zac-HD there's actually another way to get the indexing behaviour you wanted with the current API:

colors="blue green red".split()
das= [xr.DataArray(np.random.random((2, 2)), dims="x y".split(),
coords={"band": k})
forkincolors]
xr.concat(das, dim="band").plot.imshow(col="band")

Here instead of using the name attribute to label each band I've used a scalar coordinate called "band", so that when you concat along "band" it will just stack along that coordinate.

This never touches the names so actually gives the desired output without the need for #2792:
figure_2

@shoyer

Copy link
Copy Markdown
Member

I guess we should probably roll back the "name to scalar coordinates" part of this change.

@Zac-HD do you want to do that here or should we go with @TomNicholas's PR?

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

No objection to going with #2792; I'm just happy to have the change merged 😄

It would be nice for someone to cherry-pick 63da214 before releasing 0.12 though, just to fix that warning.

@Zac-HDZac-HD closed this Mar 3, 2019
@shoyershoyer mentioned this pull request Mar 12, 2019
3 tasks
shoyer pushed a commit that referenced this pull request Jun 25, 2019
* concatenates along a single dimension
* Wrote function to find correct tile_IDs from nested list of datasets
* Wrote function to check that combined_tile_ids structure is valid
* Added test of 2d-concatenation
* Tests now check that dataset ordering is correct
* Test concatentation along a new dimension
* Started generalising auto_combine to N-D by integrating the N-D concatentation algorithm
* All unit tests now passing
* Fixed a failing test which I didn't notice because I don't have pseudoNetCDF
* Began updating open_mfdataset to handle N-D input
* Refactored to remove duplicate logic in open_mfdataset & auto_combine
* Implemented Shoyers suggestion in #2553 to rewrite the recursive nested list traverser as an iterator
* --amend
* Now raises ValueError if input not ordered correctly before concatenation
* Added some more prototype tests defining desired behaviour more clearly
* Now raises informative errors on invalid forms of input
* Refactoring to alos merge along each dimension
* Refactored to literally just apply the old auto_combine along each dimension
* Added unit tests for open_mfdatset
* Removed TODOs
* Removed format strings
* test_get_new_tile_ids now doesn't assume dicts are ordered
* Fixed failing tests on python3.5 caused by accidentally assuming dict was ordered
* Test for getting new tile id
* Fixed itertoolz import so that it's compatible with older versions
* Increased test coverage
* Added toolz as an explicit dependency to pass tests on python2.7
* Updated 'what's new'
* No longer attempts to shortcut all concatenation at once if concat_dims=None
* Rewrote using itertools.groupby instead of toolz.itertoolz.groupby to remove hidden dependency on toolz
* Fixed erroneous removal of utils import
* Updated docstrings to include an example of multidimensional concatenation
* Clarified auto_combine docstring for N-D behaviour
* Added unit test for nested list of Datasets with different variables
* Minor spelling and pep8 fixes
* Started working on a new api with both auto_combine and manual_combine
* Wrote basic function to infer concatenation order from coords.
Needs better error handling though.
* Attempt at finalised version of public-facing API.
All the internals still need to be redone to match though.
* No longer uses entire old auto_combine internally, only concat or merge
* Updated what's new
* Removed uneeded addition to what's new for old release
* Fixed incomplete merge in docstring for open_mfdataset
* Tests for manual combine passing
* Tests for auto_combine now passing
* xfailed weird behaviour with manual_combine trying to determine concat_dim
* Add auto_combine and manual_combine to API page of docs
* Tests now passing for open_mfdataset
* Completed merge so that #2648 is respected, and added tests.
Also moved concat to it's own file to avoid a circular dependency
* Separated the tests for concat and both combines
* Some PEP8 fixes
* Pre-empting a test which will fail with opening uamiv format
* Satisfy pep8speaks bot
* Python 3.5 compatibile after changing some error string formatting
* Order coords using pandas.Index objects
* Fixed performance bug from GH #2662
* Removed ToDos about natural sorting of string coords
* Generalized auto_combine to handle monotonically-decreasing coords too
* Added more examples to docstring for manual_combine
* Added note about globbing aspect of open_mfdataset
* Removed auto-inferring of concatenation dimension in manual_combine
* Added example to docstring for auto_combine
* Minor correction to docstring
* Another very minor docstring correction
* Added test to guard against issue #2777
* Started deprecation cycle for auto_combine
* Fully reverted open_mfdataset tests
* Updated what's new to match deprecation cycle
* Reverted uamiv test
* Removed dependency on itertools
* Deprecation tests fixed
* Satisfy pycodestyle
* Started deprecation cycle of auto_combine
* Added specific error for edge case combine_manual can't handle
* Check that global coordinates are monotonic
* Highlighted weird behaviour when concatenating with no data variables
* Added test for impossible-to-auto-combine coordinates
* Removed uneeded test
* Satisfy linter
* Added airspeedvelocity benchmark for combining functions
* Benchmark will take longer now
* Updated version numbers in deprecation warnings to fit with recent release of 0.12
* Updated api docs for new function names
* Fixed docs build failure
* Revert "Fixed docs build failure"
This reverts commit ddfc6dd.
* Updated documentation with section explaining new functions
* Suppressed deprecation warnings in test suite
* Resolved ToDo by pointing to issue with concat, see #2975
* Various docs fixes
* Slightly renamed tests to match new name of tested function
* Included minor suggestions from shoyer
* Removed trailing whitespace
* Simplified error message for case combine_manual can't handle
* Removed filter for deprecation warnings, and added test for if user doesn't supply concat_dim
* Simple fixes suggested by shoyer
* Change deprecation warning behaviour
* linting
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@Zac-HD@shoyer@pep8speaks@TomNicholas@dcherian
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Improved default behavior when concatenating DataArrays - #2777

Closed
Zac-HD wants to merge 3 commits into
pydata:masterfrom
Zac-HD:concat-arrays
Closed

Improved default behavior when concatenating DataArrays#2777
Zac-HD wants to merge 3 commits into
pydata:masterfrom
Zac-HD:concat-arrays

Conversation

@Zac-HD

@Zac-HDZac-HD commented Feb 19, 2019

Copy link
Copy Markdown
Contributor

This is really nice to have when producing faceted plots of satellite observations in various bands, and should be somewhere between useful and harmless in other cases.

Example code:

ds=xr.Dataset({
k: xr.DataArray(np.random.random((2, 2)), dims="x y".split(), name=k) forkin"blue green red".split()
})
xr.concat([ds.blue, ds.green, ds.red], dim="band").plot.imshow(col="band")

Before - facets have an index, colorbar has misleading label:

image

After - facets have meaningful labels, colorbar has no label:

image

@Zac-HD
Zac-HDforce-pushed the concat-arrays branch 2 times, most recently from 280ce92 to a2df249CompareFebruary 19, 2019 06:36

@shoyershoyer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a nice usability improvement!

Comment threadxarray/core/combine.py Outdated
Comment threadxarray/core/combine.py Outdated
@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

Thanks for the support and quick review @shoyer!

Any idea when Xarray 0.12 might be out? I'm teaching some remote sensing workshops in mid-March and would love to have this merged, as a colleague's review of those notebooks prompted this PR 😄

@Zac-HDZac-HD mentioned this pull request Feb 20, 2019
Comment threadxarray/core/combine.py Outdated
@Zac-HD

Zac-HD commented Feb 20, 2019

Copy link
Copy Markdown
ContributorAuthor

The docs build failed due to a (transient) http error when loading tutorial data for the docs, so I've also finalised the planned conversion from xarray.tutorial.load_dataset to xarray.tutorial.open_dataset.

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

Hmm, it looks like the failure to download the naturalearth coastlines.zip wasn't so transient after all - but it does work on my machine!

@Zac-HDZac-HD closed this Feb 22, 2019
@Zac-HDZac-HD reopened this Feb 22, 2019
@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

OK! @shoyer, I've got everything passing and it's ready for review.

Even the accidental tutorial/docs fixes 😄

Comment threaddoc/whats-new.rst Outdated
Comment threadxarray/core/combine.py Outdated
@shoyer

shoyer commented Feb 22, 2019 via email

Copy link
Copy Markdown
Member

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

@shoyer - don't worry about the docs build, I'm pretty sure that was just a flaky network from Travis and it's working now in any case.

I've left tutorial.load_dataset in, just changed "removed in 0.12" to "removed in a future version".

@shoyershoyer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me now. I'll merge in a day or two unless anyone else has review comments to add.

Comment threadxarray/core/combine.py Outdated
@pep8speaks

pep8speaks commented Feb 26, 2019

Copy link
Copy Markdown

Hello @Zac-HD! Thanks for updating the PR.

Cheers ! There are no PEP8 issues in this Pull Request. 🍻

Comment last updated on February 27, 2019 at 00:51 Hours UTC

@shoyer

Copy link
Copy Markdown
Member

@pep8speaks seems to have gone hay-wire -- maybe you have a syntax error?

Thinking about this a little more, one hazard of converting names into index labels is that we lose the invariant that you get the same result regardless of order in which you call concat, e.g., something like these expressions could now give different results:

xarray.concat([a, b], dim='x')

vs

xarray.concat([xarray.concat([a], dim='x'), xarray.concat([b], dim='x')], dim='x')

If a and b have the same name, then you'd get an index with the two duplicate entries in the second case but not the first case.

I'm not entirely sure this is a deal-breaker but it makes me a little nervous reluctant. In particular, it might break some the invariants we're relying upon for the next version of open_mfdataset (#2616, cc @TomNicholas )

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

maybe you have a syntax error?

...yep, an unmatched paren. 😥

If a and b have the same name, then you'd get an index with the two duplicate entries in the second case but not the first case. [which would be bad]

I think it's impossible to avoid this when using inference in the general case. Two options I think would be decent-if-unsatisfying:

  1. Explicitly manage this in the new combining functions, e.g. clear the concat dim coords if they are not unique and the input arrays did not have coords in that dimension.
  2. Add an argument to xr.concat to enable or disable this, e.g. infer_coords=True, and disable it when calling xr.concat from other combining functions.

Zac-HDand others added 2 commits February 27, 2019 11:50
This is really nice to have when using concat to produce faceted plots of various kinds, and harmless when it's useless.
it's still deprecated, but we'll leave it for a bit longer before removal.
@TomNicholas

TomNicholas commented Feb 27, 2019

Copy link
Copy Markdown
Member

@Zac-HD forgive me for this but I think this PR is unnecessary because what you need basically already exists in the API.

Going back to your original example, you could have got the same indexing by creating a DataArray to use as a coordinate to concatenate over:

colors="blue green red".split()
ds=xr.Dataset({
k: xr.DataArray(np.random.random((2, 2)), dims="x y".split(), name=k)
forkincolors
})
band=xr.DataArray(colors, name="band", dims=["band"])
xr.concat([ds.blue, ds.green, ds.red], dim=band).plot.imshow(col="band")

figure_1

This still leaves the wrong label on the colorbar, but that could be fixed separately and has to do with concat using the attrs of the first dataset in the list for the final dataset (a similar problem to #2382). I think it would be easier to change that behaviour instead (perhaps to if all names the same, use that name, else name of result = None, but this also relates to #1614).

Creating a new coordinate using a DataArray is in the docstring for xr.concat:

If dimension is provided as a DataArray or Index, its name is used as the dimension to concatenate along and the values are added as a coordinate.

but I think there should be an example there too. (Also I think this is relevant to #1646)

I'm not entirely sure this is a deal-breaker but it makes me a little nervous

@shoyer I agree, although I like the idea then I think this could introduce all sorts of complex concatentation edge cases.

At the very least the new API should have symmetry properties something like:

da1=DataArray(name='a', data=[[0]], dims=['x', 'y'])
da2=DataArray(name='b', data=[[1]], dims=['x', 'y'])
da3=DataArray(name='a', data=[[2]], dims=['x', 'y'])
da4=DataArray(name='b', data=[[3]], dims=['x', 'y'])
xr.manual_combine([[da1, da2], [da3, da4]], concat_dim=['x', 'y'])
# should give the same result as xr.manual_combine([[da1, da3], [da2, da4]], concat_dim=['y', 'x'])

but with this PR I don't think it would. In the first case the x coord would be created with values ['a', 'b'], and no y coord would be created, while in the second case no y coord would be created, and the intermediate DataSet would be nameless, so then no x coord would be created either.

I think my suggestion for naming would pass this test because the result would be nameless and have no coords either way.

I might have got that wrong but I definitely think this kind of change should be carefully considered 😕

(EDIT: I just added this example as a test to #2616)

@TomNicholasTomNicholas mentioned this pull request Feb 27, 2019
3 tasks
@TomNicholas

TomNicholas commented Feb 27, 2019

Copy link
Copy Markdown
Member

I've just submitted a PR which solves this issue in the way I just suggested instead #2792.

@TomNicholas

Copy link
Copy Markdown
Member

@Zac-HD there's actually another way to get the indexing behaviour you wanted with the current API:

colors="blue green red".split()
das= [xr.DataArray(np.random.random((2, 2)), dims="x y".split(),
coords={"band": k})
forkincolors]
xr.concat(das, dim="band").plot.imshow(col="band")

Here instead of using the name attribute to label each band I've used a scalar coordinate called "band", so that when you concat along "band" it will just stack along that coordinate.

This never touches the names so actually gives the desired output without the need for #2792:
figure_2

@shoyer

Copy link
Copy Markdown
Member

I guess we should probably roll back the "name to scalar coordinates" part of this change.

@Zac-HD do you want to do that here or should we go with @TomNicholas's PR?

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

No objection to going with #2792; I'm just happy to have the change merged 😄

It would be nice for someone to cherry-pick 63da214 before releasing 0.12 though, just to fix that warning.

@Zac-HDZac-HD closed this Mar 3, 2019
@shoyershoyer mentioned this pull request Mar 12, 2019
3 tasks
shoyer pushed a commit that referenced this pull request Jun 25, 2019
* concatenates along a single dimension
* Wrote function to find correct tile_IDs from nested list of datasets
* Wrote function to check that combined_tile_ids structure is valid
* Added test of 2d-concatenation
* Tests now check that dataset ordering is correct
* Test concatentation along a new dimension
* Started generalising auto_combine to N-D by integrating the N-D concatentation algorithm
* All unit tests now passing
* Fixed a failing test which I didn't notice because I don't have pseudoNetCDF
* Began updating open_mfdataset to handle N-D input
* Refactored to remove duplicate logic in open_mfdataset & auto_combine
* Implemented Shoyers suggestion in #2553 to rewrite the recursive nested list traverser as an iterator
* --amend
* Now raises ValueError if input not ordered correctly before concatenation
* Added some more prototype tests defining desired behaviour more clearly
* Now raises informative errors on invalid forms of input
* Refactoring to alos merge along each dimension
* Refactored to literally just apply the old auto_combine along each dimension
* Added unit tests for open_mfdatset
* Removed TODOs
* Removed format strings
* test_get_new_tile_ids now doesn't assume dicts are ordered
* Fixed failing tests on python3.5 caused by accidentally assuming dict was ordered
* Test for getting new tile id
* Fixed itertoolz import so that it's compatible with older versions
* Increased test coverage
* Added toolz as an explicit dependency to pass tests on python2.7
* Updated 'what's new'
* No longer attempts to shortcut all concatenation at once if concat_dims=None
* Rewrote using itertools.groupby instead of toolz.itertoolz.groupby to remove hidden dependency on toolz
* Fixed erroneous removal of utils import
* Updated docstrings to include an example of multidimensional concatenation
* Clarified auto_combine docstring for N-D behaviour
* Added unit test for nested list of Datasets with different variables
* Minor spelling and pep8 fixes
* Started working on a new api with both auto_combine and manual_combine
* Wrote basic function to infer concatenation order from coords.
Needs better error handling though.
* Attempt at finalised version of public-facing API.
All the internals still need to be redone to match though.
* No longer uses entire old auto_combine internally, only concat or merge
* Updated what's new
* Removed uneeded addition to what's new for old release
* Fixed incomplete merge in docstring for open_mfdataset
* Tests for manual combine passing
* Tests for auto_combine now passing
* xfailed weird behaviour with manual_combine trying to determine concat_dim
* Add auto_combine and manual_combine to API page of docs
* Tests now passing for open_mfdataset
* Completed merge so that #2648 is respected, and added tests.
Also moved concat to it's own file to avoid a circular dependency
* Separated the tests for concat and both combines
* Some PEP8 fixes
* Pre-empting a test which will fail with opening uamiv format
* Satisfy pep8speaks bot
* Python 3.5 compatibile after changing some error string formatting
* Order coords using pandas.Index objects
* Fixed performance bug from GH #2662
* Removed ToDos about natural sorting of string coords
* Generalized auto_combine to handle monotonically-decreasing coords too
* Added more examples to docstring for manual_combine
* Added note about globbing aspect of open_mfdataset
* Removed auto-inferring of concatenation dimension in manual_combine
* Added example to docstring for auto_combine
* Minor correction to docstring
* Another very minor docstring correction
* Added test to guard against issue #2777
* Started deprecation cycle for auto_combine
* Fully reverted open_mfdataset tests
* Updated what's new to match deprecation cycle
* Reverted uamiv test
* Removed dependency on itertools
* Deprecation tests fixed
* Satisfy pycodestyle
* Started deprecation cycle of auto_combine
* Added specific error for edge case combine_manual can't handle
* Check that global coordinates are monotonic
* Highlighted weird behaviour when concatenating with no data variables
* Added test for impossible-to-auto-combine coordinates
* Removed uneeded test
* Satisfy linter
* Added airspeedvelocity benchmark for combining functions
* Benchmark will take longer now
* Updated version numbers in deprecation warnings to fit with recent release of 0.12
* Updated api docs for new function names
* Fixed docs build failure
* Revert "Fixed docs build failure"
This reverts commit ddfc6dd.
* Updated documentation with section explaining new functions
* Suppressed deprecation warnings in test suite
* Resolved ToDo by pointing to issue with concat, see #2975
* Various docs fixes
* Slightly renamed tests to match new name of tested function
* Included minor suggestions from shoyer
* Removed trailing whitespace
* Simplified error message for case combine_manual can't handle
* Removed filter for deprecation warnings, and added test for if user doesn't supply concat_dim
* Simple fixes suggested by shoyer
* Change deprecation warning behaviour
* linting
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@Zac-HD@shoyer@pep8speaks@TomNicholas@dcherian
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Improved default behavior when concatenating DataArrays - #2777

Closed
Zac-HD wants to merge 3 commits into
pydata:masterfrom
Zac-HD:concat-arrays
Closed

Improved default behavior when concatenating DataArrays#2777
Zac-HD wants to merge 3 commits into
pydata:masterfrom
Zac-HD:concat-arrays

Conversation

@Zac-HD

@Zac-HDZac-HD commented Feb 19, 2019

Copy link
Copy Markdown
Contributor

This is really nice to have when producing faceted plots of satellite observations in various bands, and should be somewhere between useful and harmless in other cases.

Example code:

ds=xr.Dataset({
k: xr.DataArray(np.random.random((2, 2)), dims="x y".split(), name=k) forkin"blue green red".split()
})
xr.concat([ds.blue, ds.green, ds.red], dim="band").plot.imshow(col="band")

Before - facets have an index, colorbar has misleading label:

image

After - facets have meaningful labels, colorbar has no label:

image

@Zac-HD
Zac-HDforce-pushed the concat-arrays branch 2 times, most recently from 280ce92 to a2df249CompareFebruary 19, 2019 06:36

@shoyershoyer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a nice usability improvement!

Comment threadxarray/core/combine.py Outdated
Comment threadxarray/core/combine.py Outdated
@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

Thanks for the support and quick review @shoyer!

Any idea when Xarray 0.12 might be out? I'm teaching some remote sensing workshops in mid-March and would love to have this merged, as a colleague's review of those notebooks prompted this PR 😄

@Zac-HDZac-HD mentioned this pull request Feb 20, 2019
Comment threadxarray/core/combine.py Outdated
@Zac-HD

Zac-HD commented Feb 20, 2019

Copy link
Copy Markdown
ContributorAuthor

The docs build failed due to a (transient) http error when loading tutorial data for the docs, so I've also finalised the planned conversion from xarray.tutorial.load_dataset to xarray.tutorial.open_dataset.

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

Hmm, it looks like the failure to download the naturalearth coastlines.zip wasn't so transient after all - but it does work on my machine!

@Zac-HDZac-HD closed this Feb 22, 2019
@Zac-HDZac-HD reopened this Feb 22, 2019
@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

OK! @shoyer, I've got everything passing and it's ready for review.

Even the accidental tutorial/docs fixes 😄

Comment threaddoc/whats-new.rst Outdated
Comment threadxarray/core/combine.py Outdated
@shoyer

shoyer commented Feb 22, 2019 via email

Copy link
Copy Markdown
Member

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

@shoyer - don't worry about the docs build, I'm pretty sure that was just a flaky network from Travis and it's working now in any case.

I've left tutorial.load_dataset in, just changed "removed in 0.12" to "removed in a future version".

@shoyershoyer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me now. I'll merge in a day or two unless anyone else has review comments to add.

Comment threadxarray/core/combine.py Outdated
@pep8speaks

pep8speaks commented Feb 26, 2019

Copy link
Copy Markdown

Hello @Zac-HD! Thanks for updating the PR.

Cheers ! There are no PEP8 issues in this Pull Request. 🍻

Comment last updated on February 27, 2019 at 00:51 Hours UTC

@shoyer

Copy link
Copy Markdown
Member

@pep8speaks seems to have gone hay-wire -- maybe you have a syntax error?

Thinking about this a little more, one hazard of converting names into index labels is that we lose the invariant that you get the same result regardless of order in which you call concat, e.g., something like these expressions could now give different results:

xarray.concat([a, b], dim='x')

vs

xarray.concat([xarray.concat([a], dim='x'), xarray.concat([b], dim='x')], dim='x')

If a and b have the same name, then you'd get an index with the two duplicate entries in the second case but not the first case.

I'm not entirely sure this is a deal-breaker but it makes me a little nervous reluctant. In particular, it might break some the invariants we're relying upon for the next version of open_mfdataset (#2616, cc @TomNicholas )

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

maybe you have a syntax error?

...yep, an unmatched paren. 😥

If a and b have the same name, then you'd get an index with the two duplicate entries in the second case but not the first case. [which would be bad]

I think it's impossible to avoid this when using inference in the general case. Two options I think would be decent-if-unsatisfying:

  1. Explicitly manage this in the new combining functions, e.g. clear the concat dim coords if they are not unique and the input arrays did not have coords in that dimension.
  2. Add an argument to xr.concat to enable or disable this, e.g. infer_coords=True, and disable it when calling xr.concat from other combining functions.

Zac-HDand others added 2 commits February 27, 2019 11:50
This is really nice to have when using concat to produce faceted plots of various kinds, and harmless when it's useless.
it's still deprecated, but we'll leave it for a bit longer before removal.
@TomNicholas

TomNicholas commented Feb 27, 2019

Copy link
Copy Markdown
Member

@Zac-HD forgive me for this but I think this PR is unnecessary because what you need basically already exists in the API.

Going back to your original example, you could have got the same indexing by creating a DataArray to use as a coordinate to concatenate over:

colors="blue green red".split()
ds=xr.Dataset({
k: xr.DataArray(np.random.random((2, 2)), dims="x y".split(), name=k)
forkincolors
})
band=xr.DataArray(colors, name="band", dims=["band"])
xr.concat([ds.blue, ds.green, ds.red], dim=band).plot.imshow(col="band")

figure_1

This still leaves the wrong label on the colorbar, but that could be fixed separately and has to do with concat using the attrs of the first dataset in the list for the final dataset (a similar problem to #2382). I think it would be easier to change that behaviour instead (perhaps to if all names the same, use that name, else name of result = None, but this also relates to #1614).

Creating a new coordinate using a DataArray is in the docstring for xr.concat:

If dimension is provided as a DataArray or Index, its name is used as the dimension to concatenate along and the values are added as a coordinate.

but I think there should be an example there too. (Also I think this is relevant to #1646)

I'm not entirely sure this is a deal-breaker but it makes me a little nervous

@shoyer I agree, although I like the idea then I think this could introduce all sorts of complex concatentation edge cases.

At the very least the new API should have symmetry properties something like:

da1=DataArray(name='a', data=[[0]], dims=['x', 'y'])
da2=DataArray(name='b', data=[[1]], dims=['x', 'y'])
da3=DataArray(name='a', data=[[2]], dims=['x', 'y'])
da4=DataArray(name='b', data=[[3]], dims=['x', 'y'])
xr.manual_combine([[da1, da2], [da3, da4]], concat_dim=['x', 'y'])
# should give the same result as xr.manual_combine([[da1, da3], [da2, da4]], concat_dim=['y', 'x'])

but with this PR I don't think it would. In the first case the x coord would be created with values ['a', 'b'], and no y coord would be created, while in the second case no y coord would be created, and the intermediate DataSet would be nameless, so then no x coord would be created either.

I think my suggestion for naming would pass this test because the result would be nameless and have no coords either way.

I might have got that wrong but I definitely think this kind of change should be carefully considered 😕

(EDIT: I just added this example as a test to #2616)

@TomNicholasTomNicholas mentioned this pull request Feb 27, 2019
3 tasks
@TomNicholas

TomNicholas commented Feb 27, 2019

Copy link
Copy Markdown
Member

I've just submitted a PR which solves this issue in the way I just suggested instead #2792.

@TomNicholas

Copy link
Copy Markdown
Member

@Zac-HD there's actually another way to get the indexing behaviour you wanted with the current API:

colors="blue green red".split()
das= [xr.DataArray(np.random.random((2, 2)), dims="x y".split(),
coords={"band": k})
forkincolors]
xr.concat(das, dim="band").plot.imshow(col="band")

Here instead of using the name attribute to label each band I've used a scalar coordinate called "band", so that when you concat along "band" it will just stack along that coordinate.

This never touches the names so actually gives the desired output without the need for #2792:
figure_2

@shoyer

Copy link
Copy Markdown
Member

I guess we should probably roll back the "name to scalar coordinates" part of this change.

@Zac-HD do you want to do that here or should we go with @TomNicholas's PR?

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

No objection to going with #2792; I'm just happy to have the change merged 😄

It would be nice for someone to cherry-pick 63da214 before releasing 0.12 though, just to fix that warning.

@Zac-HDZac-HD closed this Mar 3, 2019
@shoyershoyer mentioned this pull request Mar 12, 2019
3 tasks
shoyer pushed a commit that referenced this pull request Jun 25, 2019
* concatenates along a single dimension
* Wrote function to find correct tile_IDs from nested list of datasets
* Wrote function to check that combined_tile_ids structure is valid
* Added test of 2d-concatenation
* Tests now check that dataset ordering is correct
* Test concatentation along a new dimension
* Started generalising auto_combine to N-D by integrating the N-D concatentation algorithm
* All unit tests now passing
* Fixed a failing test which I didn't notice because I don't have pseudoNetCDF
* Began updating open_mfdataset to handle N-D input
* Refactored to remove duplicate logic in open_mfdataset & auto_combine
* Implemented Shoyers suggestion in #2553 to rewrite the recursive nested list traverser as an iterator
* --amend
* Now raises ValueError if input not ordered correctly before concatenation
* Added some more prototype tests defining desired behaviour more clearly
* Now raises informative errors on invalid forms of input
* Refactoring to alos merge along each dimension
* Refactored to literally just apply the old auto_combine along each dimension
* Added unit tests for open_mfdatset
* Removed TODOs
* Removed format strings
* test_get_new_tile_ids now doesn't assume dicts are ordered
* Fixed failing tests on python3.5 caused by accidentally assuming dict was ordered
* Test for getting new tile id
* Fixed itertoolz import so that it's compatible with older versions
* Increased test coverage
* Added toolz as an explicit dependency to pass tests on python2.7
* Updated 'what's new'
* No longer attempts to shortcut all concatenation at once if concat_dims=None
* Rewrote using itertools.groupby instead of toolz.itertoolz.groupby to remove hidden dependency on toolz
* Fixed erroneous removal of utils import
* Updated docstrings to include an example of multidimensional concatenation
* Clarified auto_combine docstring for N-D behaviour
* Added unit test for nested list of Datasets with different variables
* Minor spelling and pep8 fixes
* Started working on a new api with both auto_combine and manual_combine
* Wrote basic function to infer concatenation order from coords.
Needs better error handling though.
* Attempt at finalised version of public-facing API.
All the internals still need to be redone to match though.
* No longer uses entire old auto_combine internally, only concat or merge
* Updated what's new
* Removed uneeded addition to what's new for old release
* Fixed incomplete merge in docstring for open_mfdataset
* Tests for manual combine passing
* Tests for auto_combine now passing
* xfailed weird behaviour with manual_combine trying to determine concat_dim
* Add auto_combine and manual_combine to API page of docs
* Tests now passing for open_mfdataset
* Completed merge so that #2648 is respected, and added tests.
Also moved concat to it's own file to avoid a circular dependency
* Separated the tests for concat and both combines
* Some PEP8 fixes
* Pre-empting a test which will fail with opening uamiv format
* Satisfy pep8speaks bot
* Python 3.5 compatibile after changing some error string formatting
* Order coords using pandas.Index objects
* Fixed performance bug from GH #2662
* Removed ToDos about natural sorting of string coords
* Generalized auto_combine to handle monotonically-decreasing coords too
* Added more examples to docstring for manual_combine
* Added note about globbing aspect of open_mfdataset
* Removed auto-inferring of concatenation dimension in manual_combine
* Added example to docstring for auto_combine
* Minor correction to docstring
* Another very minor docstring correction
* Added test to guard against issue #2777
* Started deprecation cycle for auto_combine
* Fully reverted open_mfdataset tests
* Updated what's new to match deprecation cycle
* Reverted uamiv test
* Removed dependency on itertools
* Deprecation tests fixed
* Satisfy pycodestyle
* Started deprecation cycle of auto_combine
* Added specific error for edge case combine_manual can't handle
* Check that global coordinates are monotonic
* Highlighted weird behaviour when concatenating with no data variables
* Added test for impossible-to-auto-combine coordinates
* Removed uneeded test
* Satisfy linter
* Added airspeedvelocity benchmark for combining functions
* Benchmark will take longer now
* Updated version numbers in deprecation warnings to fit with recent release of 0.12
* Updated api docs for new function names
* Fixed docs build failure
* Revert "Fixed docs build failure"
This reverts commit ddfc6dd.
* Updated documentation with section explaining new functions
* Suppressed deprecation warnings in test suite
* Resolved ToDo by pointing to issue with concat, see #2975
* Various docs fixes
* Slightly renamed tests to match new name of tested function
* Included minor suggestions from shoyer
* Removed trailing whitespace
* Simplified error message for case combine_manual can't handle
* Removed filter for deprecation warnings, and added test for if user doesn't supply concat_dim
* Simple fixes suggested by shoyer
* Change deprecation warning behaviour
* linting
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@Zac-HD@shoyer@pep8speaks@TomNicholas@dcherian
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Improved default behavior when concatenating DataArrays - #2777

Closed
Zac-HD wants to merge 3 commits into
pydata:masterfrom
Zac-HD:concat-arrays
Closed

Improved default behavior when concatenating DataArrays#2777
Zac-HD wants to merge 3 commits into
pydata:masterfrom
Zac-HD:concat-arrays

Conversation

@Zac-HD

@Zac-HDZac-HD commented Feb 19, 2019

Copy link
Copy Markdown
Contributor

This is really nice to have when producing faceted plots of satellite observations in various bands, and should be somewhere between useful and harmless in other cases.

Example code:

ds=xr.Dataset({
k: xr.DataArray(np.random.random((2, 2)), dims="x y".split(), name=k) forkin"blue green red".split()
})
xr.concat([ds.blue, ds.green, ds.red], dim="band").plot.imshow(col="band")

Before - facets have an index, colorbar has misleading label:

image

After - facets have meaningful labels, colorbar has no label:

image

@Zac-HD
Zac-HDforce-pushed the concat-arrays branch 2 times, most recently from 280ce92 to a2df249CompareFebruary 19, 2019 06:36

@shoyershoyer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a nice usability improvement!

Comment threadxarray/core/combine.py Outdated
Comment threadxarray/core/combine.py Outdated
@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

Thanks for the support and quick review @shoyer!

Any idea when Xarray 0.12 might be out? I'm teaching some remote sensing workshops in mid-March and would love to have this merged, as a colleague's review of those notebooks prompted this PR 😄

@Zac-HDZac-HD mentioned this pull request Feb 20, 2019
Comment threadxarray/core/combine.py Outdated
@Zac-HD

Zac-HD commented Feb 20, 2019

Copy link
Copy Markdown
ContributorAuthor

The docs build failed due to a (transient) http error when loading tutorial data for the docs, so I've also finalised the planned conversion from xarray.tutorial.load_dataset to xarray.tutorial.open_dataset.

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

Hmm, it looks like the failure to download the naturalearth coastlines.zip wasn't so transient after all - but it does work on my machine!

@Zac-HDZac-HD closed this Feb 22, 2019
@Zac-HDZac-HD reopened this Feb 22, 2019
@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

OK! @shoyer, I've got everything passing and it's ready for review.

Even the accidental tutorial/docs fixes 😄

Comment threaddoc/whats-new.rst Outdated
Comment threadxarray/core/combine.py Outdated
@shoyer

shoyer commented Feb 22, 2019 via email

Copy link
Copy Markdown
Member

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

@shoyer - don't worry about the docs build, I'm pretty sure that was just a flaky network from Travis and it's working now in any case.

I've left tutorial.load_dataset in, just changed "removed in 0.12" to "removed in a future version".

@shoyershoyer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me now. I'll merge in a day or two unless anyone else has review comments to add.

Comment threadxarray/core/combine.py Outdated
@pep8speaks

pep8speaks commented Feb 26, 2019

Copy link
Copy Markdown

Hello @Zac-HD! Thanks for updating the PR.

Cheers ! There are no PEP8 issues in this Pull Request. 🍻

Comment last updated on February 27, 2019 at 00:51 Hours UTC

@shoyer

Copy link
Copy Markdown
Member

@pep8speaks seems to have gone hay-wire -- maybe you have a syntax error?

Thinking about this a little more, one hazard of converting names into index labels is that we lose the invariant that you get the same result regardless of order in which you call concat, e.g., something like these expressions could now give different results:

xarray.concat([a, b], dim='x')

vs

xarray.concat([xarray.concat([a], dim='x'), xarray.concat([b], dim='x')], dim='x')

If a and b have the same name, then you'd get an index with the two duplicate entries in the second case but not the first case.

I'm not entirely sure this is a deal-breaker but it makes me a little nervous reluctant. In particular, it might break some the invariants we're relying upon for the next version of open_mfdataset (#2616, cc @TomNicholas )

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

maybe you have a syntax error?

...yep, an unmatched paren. 😥

If a and b have the same name, then you'd get an index with the two duplicate entries in the second case but not the first case. [which would be bad]

I think it's impossible to avoid this when using inference in the general case. Two options I think would be decent-if-unsatisfying:

  1. Explicitly manage this in the new combining functions, e.g. clear the concat dim coords if they are not unique and the input arrays did not have coords in that dimension.
  2. Add an argument to xr.concat to enable or disable this, e.g. infer_coords=True, and disable it when calling xr.concat from other combining functions.

Zac-HDand others added 2 commits February 27, 2019 11:50
This is really nice to have when using concat to produce faceted plots of various kinds, and harmless when it's useless.
it's still deprecated, but we'll leave it for a bit longer before removal.
@TomNicholas

TomNicholas commented Feb 27, 2019

Copy link
Copy Markdown
Member

@Zac-HD forgive me for this but I think this PR is unnecessary because what you need basically already exists in the API.

Going back to your original example, you could have got the same indexing by creating a DataArray to use as a coordinate to concatenate over:

colors="blue green red".split()
ds=xr.Dataset({
k: xr.DataArray(np.random.random((2, 2)), dims="x y".split(), name=k)
forkincolors
})
band=xr.DataArray(colors, name="band", dims=["band"])
xr.concat([ds.blue, ds.green, ds.red], dim=band).plot.imshow(col="band")

figure_1

This still leaves the wrong label on the colorbar, but that could be fixed separately and has to do with concat using the attrs of the first dataset in the list for the final dataset (a similar problem to #2382). I think it would be easier to change that behaviour instead (perhaps to if all names the same, use that name, else name of result = None, but this also relates to #1614).

Creating a new coordinate using a DataArray is in the docstring for xr.concat:

If dimension is provided as a DataArray or Index, its name is used as the dimension to concatenate along and the values are added as a coordinate.

but I think there should be an example there too. (Also I think this is relevant to #1646)

I'm not entirely sure this is a deal-breaker but it makes me a little nervous

@shoyer I agree, although I like the idea then I think this could introduce all sorts of complex concatentation edge cases.

At the very least the new API should have symmetry properties something like:

da1=DataArray(name='a', data=[[0]], dims=['x', 'y'])
da2=DataArray(name='b', data=[[1]], dims=['x', 'y'])
da3=DataArray(name='a', data=[[2]], dims=['x', 'y'])
da4=DataArray(name='b', data=[[3]], dims=['x', 'y'])
xr.manual_combine([[da1, da2], [da3, da4]], concat_dim=['x', 'y'])
# should give the same result as xr.manual_combine([[da1, da3], [da2, da4]], concat_dim=['y', 'x'])

but with this PR I don't think it would. In the first case the x coord would be created with values ['a', 'b'], and no y coord would be created, while in the second case no y coord would be created, and the intermediate DataSet would be nameless, so then no x coord would be created either.

I think my suggestion for naming would pass this test because the result would be nameless and have no coords either way.

I might have got that wrong but I definitely think this kind of change should be carefully considered 😕

(EDIT: I just added this example as a test to #2616)

@TomNicholasTomNicholas mentioned this pull request Feb 27, 2019
3 tasks
@TomNicholas

TomNicholas commented Feb 27, 2019

Copy link
Copy Markdown
Member

I've just submitted a PR which solves this issue in the way I just suggested instead #2792.

@TomNicholas

Copy link
Copy Markdown
Member

@Zac-HD there's actually another way to get the indexing behaviour you wanted with the current API:

colors="blue green red".split()
das= [xr.DataArray(np.random.random((2, 2)), dims="x y".split(),
coords={"band": k})
forkincolors]
xr.concat(das, dim="band").plot.imshow(col="band")

Here instead of using the name attribute to label each band I've used a scalar coordinate called "band", so that when you concat along "band" it will just stack along that coordinate.

This never touches the names so actually gives the desired output without the need for #2792:
figure_2

@shoyer

Copy link
Copy Markdown
Member

I guess we should probably roll back the "name to scalar coordinates" part of this change.

@Zac-HD do you want to do that here or should we go with @TomNicholas's PR?

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

No objection to going with #2792; I'm just happy to have the change merged 😄

It would be nice for someone to cherry-pick 63da214 before releasing 0.12 though, just to fix that warning.

@Zac-HDZac-HD closed this Mar 3, 2019
@shoyershoyer mentioned this pull request Mar 12, 2019
3 tasks
shoyer pushed a commit that referenced this pull request Jun 25, 2019
* concatenates along a single dimension
* Wrote function to find correct tile_IDs from nested list of datasets
* Wrote function to check that combined_tile_ids structure is valid
* Added test of 2d-concatenation
* Tests now check that dataset ordering is correct
* Test concatentation along a new dimension
* Started generalising auto_combine to N-D by integrating the N-D concatentation algorithm
* All unit tests now passing
* Fixed a failing test which I didn't notice because I don't have pseudoNetCDF
* Began updating open_mfdataset to handle N-D input
* Refactored to remove duplicate logic in open_mfdataset & auto_combine
* Implemented Shoyers suggestion in #2553 to rewrite the recursive nested list traverser as an iterator
* --amend
* Now raises ValueError if input not ordered correctly before concatenation
* Added some more prototype tests defining desired behaviour more clearly
* Now raises informative errors on invalid forms of input
* Refactoring to alos merge along each dimension
* Refactored to literally just apply the old auto_combine along each dimension
* Added unit tests for open_mfdatset
* Removed TODOs
* Removed format strings
* test_get_new_tile_ids now doesn't assume dicts are ordered
* Fixed failing tests on python3.5 caused by accidentally assuming dict was ordered
* Test for getting new tile id
* Fixed itertoolz import so that it's compatible with older versions
* Increased test coverage
* Added toolz as an explicit dependency to pass tests on python2.7
* Updated 'what's new'
* No longer attempts to shortcut all concatenation at once if concat_dims=None
* Rewrote using itertools.groupby instead of toolz.itertoolz.groupby to remove hidden dependency on toolz
* Fixed erroneous removal of utils import
* Updated docstrings to include an example of multidimensional concatenation
* Clarified auto_combine docstring for N-D behaviour
* Added unit test for nested list of Datasets with different variables
* Minor spelling and pep8 fixes
* Started working on a new api with both auto_combine and manual_combine
* Wrote basic function to infer concatenation order from coords.
Needs better error handling though.
* Attempt at finalised version of public-facing API.
All the internals still need to be redone to match though.
* No longer uses entire old auto_combine internally, only concat or merge
* Updated what's new
* Removed uneeded addition to what's new for old release
* Fixed incomplete merge in docstring for open_mfdataset
* Tests for manual combine passing
* Tests for auto_combine now passing
* xfailed weird behaviour with manual_combine trying to determine concat_dim
* Add auto_combine and manual_combine to API page of docs
* Tests now passing for open_mfdataset
* Completed merge so that #2648 is respected, and added tests.
Also moved concat to it's own file to avoid a circular dependency
* Separated the tests for concat and both combines
* Some PEP8 fixes
* Pre-empting a test which will fail with opening uamiv format
* Satisfy pep8speaks bot
* Python 3.5 compatibile after changing some error string formatting
* Order coords using pandas.Index objects
* Fixed performance bug from GH #2662
* Removed ToDos about natural sorting of string coords
* Generalized auto_combine to handle monotonically-decreasing coords too
* Added more examples to docstring for manual_combine
* Added note about globbing aspect of open_mfdataset
* Removed auto-inferring of concatenation dimension in manual_combine
* Added example to docstring for auto_combine
* Minor correction to docstring
* Another very minor docstring correction
* Added test to guard against issue #2777
* Started deprecation cycle for auto_combine
* Fully reverted open_mfdataset tests
* Updated what's new to match deprecation cycle
* Reverted uamiv test
* Removed dependency on itertools
* Deprecation tests fixed
* Satisfy pycodestyle
* Started deprecation cycle of auto_combine
* Added specific error for edge case combine_manual can't handle
* Check that global coordinates are monotonic
* Highlighted weird behaviour when concatenating with no data variables
* Added test for impossible-to-auto-combine coordinates
* Removed uneeded test
* Satisfy linter
* Added airspeedvelocity benchmark for combining functions
* Benchmark will take longer now
* Updated version numbers in deprecation warnings to fit with recent release of 0.12
* Updated api docs for new function names
* Fixed docs build failure
* Revert "Fixed docs build failure"
This reverts commit ddfc6dd.
* Updated documentation with section explaining new functions
* Suppressed deprecation warnings in test suite
* Resolved ToDo by pointing to issue with concat, see #2975
* Various docs fixes
* Slightly renamed tests to match new name of tested function
* Included minor suggestions from shoyer
* Removed trailing whitespace
* Simplified error message for case combine_manual can't handle
* Removed filter for deprecation warnings, and added test for if user doesn't supply concat_dim
* Simple fixes suggested by shoyer
* Change deprecation warning behaviour
* linting
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@Zac-HD@shoyer@pep8speaks@TomNicholas@dcherian
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Improved default behavior when concatenating DataArrays - #2777

Closed
Zac-HD wants to merge 3 commits into
pydata:masterfrom
Zac-HD:concat-arrays
Closed

Improved default behavior when concatenating DataArrays#2777
Zac-HD wants to merge 3 commits into
pydata:masterfrom
Zac-HD:concat-arrays

Conversation

@Zac-HD

@Zac-HDZac-HD commented Feb 19, 2019

Copy link
Copy Markdown
Contributor

This is really nice to have when producing faceted plots of satellite observations in various bands, and should be somewhere between useful and harmless in other cases.

Example code:

ds=xr.Dataset({
k: xr.DataArray(np.random.random((2, 2)), dims="x y".split(), name=k) forkin"blue green red".split()
})
xr.concat([ds.blue, ds.green, ds.red], dim="band").plot.imshow(col="band")

Before - facets have an index, colorbar has misleading label:

image

After - facets have meaningful labels, colorbar has no label:

image

@Zac-HD
Zac-HDforce-pushed the concat-arrays branch 2 times, most recently from 280ce92 to a2df249CompareFebruary 19, 2019 06:36

@shoyershoyer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a nice usability improvement!

Comment threadxarray/core/combine.py Outdated
Comment threadxarray/core/combine.py Outdated
@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

Thanks for the support and quick review @shoyer!

Any idea when Xarray 0.12 might be out? I'm teaching some remote sensing workshops in mid-March and would love to have this merged, as a colleague's review of those notebooks prompted this PR 😄

@Zac-HDZac-HD mentioned this pull request Feb 20, 2019
Comment threadxarray/core/combine.py Outdated
@Zac-HD

Zac-HD commented Feb 20, 2019

Copy link
Copy Markdown
ContributorAuthor

The docs build failed due to a (transient) http error when loading tutorial data for the docs, so I've also finalised the planned conversion from xarray.tutorial.load_dataset to xarray.tutorial.open_dataset.

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

Hmm, it looks like the failure to download the naturalearth coastlines.zip wasn't so transient after all - but it does work on my machine!

@Zac-HDZac-HD closed this Feb 22, 2019
@Zac-HDZac-HD reopened this Feb 22, 2019
@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

OK! @shoyer, I've got everything passing and it's ready for review.

Even the accidental tutorial/docs fixes 😄

Comment threaddoc/whats-new.rst Outdated
Comment threadxarray/core/combine.py Outdated
@shoyer

shoyer commented Feb 22, 2019 via email

Copy link
Copy Markdown
Member

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

@shoyer - don't worry about the docs build, I'm pretty sure that was just a flaky network from Travis and it's working now in any case.

I've left tutorial.load_dataset in, just changed "removed in 0.12" to "removed in a future version".

@shoyershoyer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me now. I'll merge in a day or two unless anyone else has review comments to add.

Comment threadxarray/core/combine.py Outdated
@pep8speaks

pep8speaks commented Feb 26, 2019

Copy link
Copy Markdown

Hello @Zac-HD! Thanks for updating the PR.

Cheers ! There are no PEP8 issues in this Pull Request. 🍻

Comment last updated on February 27, 2019 at 00:51 Hours UTC

@shoyer

Copy link
Copy Markdown
Member

@pep8speaks seems to have gone hay-wire -- maybe you have a syntax error?

Thinking about this a little more, one hazard of converting names into index labels is that we lose the invariant that you get the same result regardless of order in which you call concat, e.g., something like these expressions could now give different results:

xarray.concat([a, b], dim='x')

vs

xarray.concat([xarray.concat([a], dim='x'), xarray.concat([b], dim='x')], dim='x')

If a and b have the same name, then you'd get an index with the two duplicate entries in the second case but not the first case.

I'm not entirely sure this is a deal-breaker but it makes me a little nervous reluctant. In particular, it might break some the invariants we're relying upon for the next version of open_mfdataset (#2616, cc @TomNicholas )

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

maybe you have a syntax error?

...yep, an unmatched paren. 😥

If a and b have the same name, then you'd get an index with the two duplicate entries in the second case but not the first case. [which would be bad]

I think it's impossible to avoid this when using inference in the general case. Two options I think would be decent-if-unsatisfying:

  1. Explicitly manage this in the new combining functions, e.g. clear the concat dim coords if they are not unique and the input arrays did not have coords in that dimension.
  2. Add an argument to xr.concat to enable or disable this, e.g. infer_coords=True, and disable it when calling xr.concat from other combining functions.

Zac-HDand others added 2 commits February 27, 2019 11:50
This is really nice to have when using concat to produce faceted plots of various kinds, and harmless when it's useless.
it's still deprecated, but we'll leave it for a bit longer before removal.
@TomNicholas

TomNicholas commented Feb 27, 2019

Copy link
Copy Markdown
Member

@Zac-HD forgive me for this but I think this PR is unnecessary because what you need basically already exists in the API.

Going back to your original example, you could have got the same indexing by creating a DataArray to use as a coordinate to concatenate over:

colors="blue green red".split()
ds=xr.Dataset({
k: xr.DataArray(np.random.random((2, 2)), dims="x y".split(), name=k)
forkincolors
})
band=xr.DataArray(colors, name="band", dims=["band"])
xr.concat([ds.blue, ds.green, ds.red], dim=band).plot.imshow(col="band")

figure_1

This still leaves the wrong label on the colorbar, but that could be fixed separately and has to do with concat using the attrs of the first dataset in the list for the final dataset (a similar problem to #2382). I think it would be easier to change that behaviour instead (perhaps to if all names the same, use that name, else name of result = None, but this also relates to #1614).

Creating a new coordinate using a DataArray is in the docstring for xr.concat:

If dimension is provided as a DataArray or Index, its name is used as the dimension to concatenate along and the values are added as a coordinate.

but I think there should be an example there too. (Also I think this is relevant to #1646)

I'm not entirely sure this is a deal-breaker but it makes me a little nervous

@shoyer I agree, although I like the idea then I think this could introduce all sorts of complex concatentation edge cases.

At the very least the new API should have symmetry properties something like:

da1=DataArray(name='a', data=[[0]], dims=['x', 'y'])
da2=DataArray(name='b', data=[[1]], dims=['x', 'y'])
da3=DataArray(name='a', data=[[2]], dims=['x', 'y'])
da4=DataArray(name='b', data=[[3]], dims=['x', 'y'])
xr.manual_combine([[da1, da2], [da3, da4]], concat_dim=['x', 'y'])
# should give the same result as xr.manual_combine([[da1, da3], [da2, da4]], concat_dim=['y', 'x'])

but with this PR I don't think it would. In the first case the x coord would be created with values ['a', 'b'], and no y coord would be created, while in the second case no y coord would be created, and the intermediate DataSet would be nameless, so then no x coord would be created either.

I think my suggestion for naming would pass this test because the result would be nameless and have no coords either way.

I might have got that wrong but I definitely think this kind of change should be carefully considered 😕

(EDIT: I just added this example as a test to #2616)

@TomNicholasTomNicholas mentioned this pull request Feb 27, 2019
3 tasks
@TomNicholas

TomNicholas commented Feb 27, 2019

Copy link
Copy Markdown
Member

I've just submitted a PR which solves this issue in the way I just suggested instead #2792.

@TomNicholas

Copy link
Copy Markdown
Member

@Zac-HD there's actually another way to get the indexing behaviour you wanted with the current API:

colors="blue green red".split()
das= [xr.DataArray(np.random.random((2, 2)), dims="x y".split(),
coords={"band": k})
forkincolors]
xr.concat(das, dim="band").plot.imshow(col="band")

Here instead of using the name attribute to label each band I've used a scalar coordinate called "band", so that when you concat along "band" it will just stack along that coordinate.

This never touches the names so actually gives the desired output without the need for #2792:
figure_2

@shoyer

Copy link
Copy Markdown
Member

I guess we should probably roll back the "name to scalar coordinates" part of this change.

@Zac-HD do you want to do that here or should we go with @TomNicholas's PR?

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

No objection to going with #2792; I'm just happy to have the change merged 😄

It would be nice for someone to cherry-pick 63da214 before releasing 0.12 though, just to fix that warning.

@Zac-HDZac-HD closed this Mar 3, 2019
@shoyershoyer mentioned this pull request Mar 12, 2019
3 tasks
shoyer pushed a commit that referenced this pull request Jun 25, 2019
* concatenates along a single dimension
* Wrote function to find correct tile_IDs from nested list of datasets
* Wrote function to check that combined_tile_ids structure is valid
* Added test of 2d-concatenation
* Tests now check that dataset ordering is correct
* Test concatentation along a new dimension
* Started generalising auto_combine to N-D by integrating the N-D concatentation algorithm
* All unit tests now passing
* Fixed a failing test which I didn't notice because I don't have pseudoNetCDF
* Began updating open_mfdataset to handle N-D input
* Refactored to remove duplicate logic in open_mfdataset & auto_combine
* Implemented Shoyers suggestion in #2553 to rewrite the recursive nested list traverser as an iterator
* --amend
* Now raises ValueError if input not ordered correctly before concatenation
* Added some more prototype tests defining desired behaviour more clearly
* Now raises informative errors on invalid forms of input
* Refactoring to alos merge along each dimension
* Refactored to literally just apply the old auto_combine along each dimension
* Added unit tests for open_mfdatset
* Removed TODOs
* Removed format strings
* test_get_new_tile_ids now doesn't assume dicts are ordered
* Fixed failing tests on python3.5 caused by accidentally assuming dict was ordered
* Test for getting new tile id
* Fixed itertoolz import so that it's compatible with older versions
* Increased test coverage
* Added toolz as an explicit dependency to pass tests on python2.7
* Updated 'what's new'
* No longer attempts to shortcut all concatenation at once if concat_dims=None
* Rewrote using itertools.groupby instead of toolz.itertoolz.groupby to remove hidden dependency on toolz
* Fixed erroneous removal of utils import
* Updated docstrings to include an example of multidimensional concatenation
* Clarified auto_combine docstring for N-D behaviour
* Added unit test for nested list of Datasets with different variables
* Minor spelling and pep8 fixes
* Started working on a new api with both auto_combine and manual_combine
* Wrote basic function to infer concatenation order from coords.
Needs better error handling though.
* Attempt at finalised version of public-facing API.
All the internals still need to be redone to match though.
* No longer uses entire old auto_combine internally, only concat or merge
* Updated what's new
* Removed uneeded addition to what's new for old release
* Fixed incomplete merge in docstring for open_mfdataset
* Tests for manual combine passing
* Tests for auto_combine now passing
* xfailed weird behaviour with manual_combine trying to determine concat_dim
* Add auto_combine and manual_combine to API page of docs
* Tests now passing for open_mfdataset
* Completed merge so that #2648 is respected, and added tests.
Also moved concat to it's own file to avoid a circular dependency
* Separated the tests for concat and both combines
* Some PEP8 fixes
* Pre-empting a test which will fail with opening uamiv format
* Satisfy pep8speaks bot
* Python 3.5 compatibile after changing some error string formatting
* Order coords using pandas.Index objects
* Fixed performance bug from GH #2662
* Removed ToDos about natural sorting of string coords
* Generalized auto_combine to handle monotonically-decreasing coords too
* Added more examples to docstring for manual_combine
* Added note about globbing aspect of open_mfdataset
* Removed auto-inferring of concatenation dimension in manual_combine
* Added example to docstring for auto_combine
* Minor correction to docstring
* Another very minor docstring correction
* Added test to guard against issue #2777
* Started deprecation cycle for auto_combine
* Fully reverted open_mfdataset tests
* Updated what's new to match deprecation cycle
* Reverted uamiv test
* Removed dependency on itertools
* Deprecation tests fixed
* Satisfy pycodestyle
* Started deprecation cycle of auto_combine
* Added specific error for edge case combine_manual can't handle
* Check that global coordinates are monotonic
* Highlighted weird behaviour when concatenating with no data variables
* Added test for impossible-to-auto-combine coordinates
* Removed uneeded test
* Satisfy linter
* Added airspeedvelocity benchmark for combining functions
* Benchmark will take longer now
* Updated version numbers in deprecation warnings to fit with recent release of 0.12
* Updated api docs for new function names
* Fixed docs build failure
* Revert "Fixed docs build failure"
This reverts commit ddfc6dd.
* Updated documentation with section explaining new functions
* Suppressed deprecation warnings in test suite
* Resolved ToDo by pointing to issue with concat, see #2975
* Various docs fixes
* Slightly renamed tests to match new name of tested function
* Included minor suggestions from shoyer
* Removed trailing whitespace
* Simplified error message for case combine_manual can't handle
* Removed filter for deprecation warnings, and added test for if user doesn't supply concat_dim
* Simple fixes suggested by shoyer
* Change deprecation warning behaviour
* linting
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@Zac-HD@shoyer@pep8speaks@TomNicholas@dcherian
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Improved default behavior when concatenating DataArrays - #2777

Closed
Zac-HD wants to merge 3 commits into
pydata:masterfrom
Zac-HD:concat-arrays
Closed

Improved default behavior when concatenating DataArrays#2777
Zac-HD wants to merge 3 commits into
pydata:masterfrom
Zac-HD:concat-arrays

Conversation

@Zac-HD

@Zac-HDZac-HD commented Feb 19, 2019

Copy link
Copy Markdown
Contributor

This is really nice to have when producing faceted plots of satellite observations in various bands, and should be somewhere between useful and harmless in other cases.

Example code:

ds=xr.Dataset({
k: xr.DataArray(np.random.random((2, 2)), dims="x y".split(), name=k) forkin"blue green red".split()
})
xr.concat([ds.blue, ds.green, ds.red], dim="band").plot.imshow(col="band")

Before - facets have an index, colorbar has misleading label:

image

After - facets have meaningful labels, colorbar has no label:

image

@Zac-HD
Zac-HDforce-pushed the concat-arrays branch 2 times, most recently from 280ce92 to a2df249CompareFebruary 19, 2019 06:36

@shoyershoyer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a nice usability improvement!

Comment threadxarray/core/combine.py Outdated
Comment threadxarray/core/combine.py Outdated
@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

Thanks for the support and quick review @shoyer!

Any idea when Xarray 0.12 might be out? I'm teaching some remote sensing workshops in mid-March and would love to have this merged, as a colleague's review of those notebooks prompted this PR 😄

@Zac-HDZac-HD mentioned this pull request Feb 20, 2019
Comment threadxarray/core/combine.py Outdated
@Zac-HD

Zac-HD commented Feb 20, 2019

Copy link
Copy Markdown
ContributorAuthor

The docs build failed due to a (transient) http error when loading tutorial data for the docs, so I've also finalised the planned conversion from xarray.tutorial.load_dataset to xarray.tutorial.open_dataset.

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

Hmm, it looks like the failure to download the naturalearth coastlines.zip wasn't so transient after all - but it does work on my machine!

@Zac-HDZac-HD closed this Feb 22, 2019
@Zac-HDZac-HD reopened this Feb 22, 2019
@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

OK! @shoyer, I've got everything passing and it's ready for review.

Even the accidental tutorial/docs fixes 😄

Comment threaddoc/whats-new.rst Outdated
Comment threadxarray/core/combine.py Outdated
@shoyer

shoyer commented Feb 22, 2019 via email

Copy link
Copy Markdown
Member

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

@shoyer - don't worry about the docs build, I'm pretty sure that was just a flaky network from Travis and it's working now in any case.

I've left tutorial.load_dataset in, just changed "removed in 0.12" to "removed in a future version".

@shoyershoyer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me now. I'll merge in a day or two unless anyone else has review comments to add.

Comment threadxarray/core/combine.py Outdated
@pep8speaks

pep8speaks commented Feb 26, 2019

Copy link
Copy Markdown

Hello @Zac-HD! Thanks for updating the PR.

Cheers ! There are no PEP8 issues in this Pull Request. 🍻

Comment last updated on February 27, 2019 at 00:51 Hours UTC

@shoyer

Copy link
Copy Markdown
Member

@pep8speaks seems to have gone hay-wire -- maybe you have a syntax error?

Thinking about this a little more, one hazard of converting names into index labels is that we lose the invariant that you get the same result regardless of order in which you call concat, e.g., something like these expressions could now give different results:

xarray.concat([a, b], dim='x')

vs

xarray.concat([xarray.concat([a], dim='x'), xarray.concat([b], dim='x')], dim='x')

If a and b have the same name, then you'd get an index with the two duplicate entries in the second case but not the first case.

I'm not entirely sure this is a deal-breaker but it makes me a little nervous reluctant. In particular, it might break some the invariants we're relying upon for the next version of open_mfdataset (#2616, cc @TomNicholas )

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

maybe you have a syntax error?

...yep, an unmatched paren. 😥

If a and b have the same name, then you'd get an index with the two duplicate entries in the second case but not the first case. [which would be bad]

I think it's impossible to avoid this when using inference in the general case. Two options I think would be decent-if-unsatisfying:

  1. Explicitly manage this in the new combining functions, e.g. clear the concat dim coords if they are not unique and the input arrays did not have coords in that dimension.
  2. Add an argument to xr.concat to enable or disable this, e.g. infer_coords=True, and disable it when calling xr.concat from other combining functions.

Zac-HDand others added 2 commits February 27, 2019 11:50
This is really nice to have when using concat to produce faceted plots of various kinds, and harmless when it's useless.
it's still deprecated, but we'll leave it for a bit longer before removal.
@TomNicholas

TomNicholas commented Feb 27, 2019

Copy link
Copy Markdown
Member

@Zac-HD forgive me for this but I think this PR is unnecessary because what you need basically already exists in the API.

Going back to your original example, you could have got the same indexing by creating a DataArray to use as a coordinate to concatenate over:

colors="blue green red".split()
ds=xr.Dataset({
k: xr.DataArray(np.random.random((2, 2)), dims="x y".split(), name=k)
forkincolors
})
band=xr.DataArray(colors, name="band", dims=["band"])
xr.concat([ds.blue, ds.green, ds.red], dim=band).plot.imshow(col="band")

figure_1

This still leaves the wrong label on the colorbar, but that could be fixed separately and has to do with concat using the attrs of the first dataset in the list for the final dataset (a similar problem to #2382). I think it would be easier to change that behaviour instead (perhaps to if all names the same, use that name, else name of result = None, but this also relates to #1614).

Creating a new coordinate using a DataArray is in the docstring for xr.concat:

If dimension is provided as a DataArray or Index, its name is used as the dimension to concatenate along and the values are added as a coordinate.

but I think there should be an example there too. (Also I think this is relevant to #1646)

I'm not entirely sure this is a deal-breaker but it makes me a little nervous

@shoyer I agree, although I like the idea then I think this could introduce all sorts of complex concatentation edge cases.

At the very least the new API should have symmetry properties something like:

da1=DataArray(name='a', data=[[0]], dims=['x', 'y'])
da2=DataArray(name='b', data=[[1]], dims=['x', 'y'])
da3=DataArray(name='a', data=[[2]], dims=['x', 'y'])
da4=DataArray(name='b', data=[[3]], dims=['x', 'y'])
xr.manual_combine([[da1, da2], [da3, da4]], concat_dim=['x', 'y'])
# should give the same result as xr.manual_combine([[da1, da3], [da2, da4]], concat_dim=['y', 'x'])

but with this PR I don't think it would. In the first case the x coord would be created with values ['a', 'b'], and no y coord would be created, while in the second case no y coord would be created, and the intermediate DataSet would be nameless, so then no x coord would be created either.

I think my suggestion for naming would pass this test because the result would be nameless and have no coords either way.

I might have got that wrong but I definitely think this kind of change should be carefully considered 😕

(EDIT: I just added this example as a test to #2616)

@TomNicholasTomNicholas mentioned this pull request Feb 27, 2019
3 tasks
@TomNicholas

TomNicholas commented Feb 27, 2019

Copy link
Copy Markdown
Member

I've just submitted a PR which solves this issue in the way I just suggested instead #2792.

@TomNicholas

Copy link
Copy Markdown
Member

@Zac-HD there's actually another way to get the indexing behaviour you wanted with the current API:

colors="blue green red".split()
das= [xr.DataArray(np.random.random((2, 2)), dims="x y".split(),
coords={"band": k})
forkincolors]
xr.concat(das, dim="band").plot.imshow(col="band")

Here instead of using the name attribute to label each band I've used a scalar coordinate called "band", so that when you concat along "band" it will just stack along that coordinate.

This never touches the names so actually gives the desired output without the need for #2792:
figure_2

@shoyer

Copy link
Copy Markdown
Member

I guess we should probably roll back the "name to scalar coordinates" part of this change.

@Zac-HD do you want to do that here or should we go with @TomNicholas's PR?

@Zac-HD

Copy link
Copy Markdown
ContributorAuthor

No objection to going with #2792; I'm just happy to have the change merged 😄

It would be nice for someone to cherry-pick 63da214 before releasing 0.12 though, just to fix that warning.

@Zac-HDZac-HD closed this Mar 3, 2019
@shoyershoyer mentioned this pull request Mar 12, 2019
3 tasks
shoyer pushed a commit that referenced this pull request Jun 25, 2019
* concatenates along a single dimension
* Wrote function to find correct tile_IDs from nested list of datasets
* Wrote function to check that combined_tile_ids structure is valid
* Added test of 2d-concatenation
* Tests now check that dataset ordering is correct
* Test concatentation along a new dimension
* Started generalising auto_combine to N-D by integrating the N-D concatentation algorithm
* All unit tests now passing
* Fixed a failing test which I didn't notice because I don't have pseudoNetCDF
* Began updating open_mfdataset to handle N-D input
* Refactored to remove duplicate logic in open_mfdataset & auto_combine
* Implemented Shoyers suggestion in #2553 to rewrite the recursive nested list traverser as an iterator
* --amend
* Now raises ValueError if input not ordered correctly before concatenation
* Added some more prototype tests defining desired behaviour more clearly
* Now raises informative errors on invalid forms of input
* Refactoring to alos merge along each dimension
* Refactored to literally just apply the old auto_combine along each dimension
* Added unit tests for open_mfdatset
* Removed TODOs
* Removed format strings
* test_get_new_tile_ids now doesn't assume dicts are ordered
* Fixed failing tests on python3.5 caused by accidentally assuming dict was ordered
* Test for getting new tile id
* Fixed itertoolz import so that it's compatible with older versions
* Increased test coverage
* Added toolz as an explicit dependency to pass tests on python2.7
* Updated 'what's new'
* No longer attempts to shortcut all concatenation at once if concat_dims=None
* Rewrote using itertools.groupby instead of toolz.itertoolz.groupby to remove hidden dependency on toolz
* Fixed erroneous removal of utils import
* Updated docstrings to include an example of multidimensional concatenation
* Clarified auto_combine docstring for N-D behaviour
* Added unit test for nested list of Datasets with different variables
* Minor spelling and pep8 fixes
* Started working on a new api with both auto_combine and manual_combine
* Wrote basic function to infer concatenation order from coords.
Needs better error handling though.
* Attempt at finalised version of public-facing API.
All the internals still need to be redone to match though.
* No longer uses entire old auto_combine internally, only concat or merge
* Updated what's new
* Removed uneeded addition to what's new for old release
* Fixed incomplete merge in docstring for open_mfdataset
* Tests for manual combine passing
* Tests for auto_combine now passing
* xfailed weird behaviour with manual_combine trying to determine concat_dim
* Add auto_combine and manual_combine to API page of docs
* Tests now passing for open_mfdataset
* Completed merge so that #2648 is respected, and added tests.
Also moved concat to it's own file to avoid a circular dependency
* Separated the tests for concat and both combines
* Some PEP8 fixes
* Pre-empting a test which will fail with opening uamiv format
* Satisfy pep8speaks bot
* Python 3.5 compatibile after changing some error string formatting
* Order coords using pandas.Index objects
* Fixed performance bug from GH #2662
* Removed ToDos about natural sorting of string coords
* Generalized auto_combine to handle monotonically-decreasing coords too
* Added more examples to docstring for manual_combine
* Added note about globbing aspect of open_mfdataset
* Removed auto-inferring of concatenation dimension in manual_combine
* Added example to docstring for auto_combine
* Minor correction to docstring
* Another very minor docstring correction
* Added test to guard against issue #2777
* Started deprecation cycle for auto_combine
* Fully reverted open_mfdataset tests
* Updated what's new to match deprecation cycle
* Reverted uamiv test
* Removed dependency on itertools
* Deprecation tests fixed
* Satisfy pycodestyle
* Started deprecation cycle of auto_combine
* Added specific error for edge case combine_manual can't handle
* Check that global coordinates are monotonic
* Highlighted weird behaviour when concatenating with no data variables
* Added test for impossible-to-auto-combine coordinates
* Removed uneeded test
* Satisfy linter
* Added airspeedvelocity benchmark for combining functions
* Benchmark will take longer now
* Updated version numbers in deprecation warnings to fit with recent release of 0.12
* Updated api docs for new function names
* Fixed docs build failure
* Revert "Fixed docs build failure"
This reverts commit ddfc6dd.
* Updated documentation with section explaining new functions
* Suppressed deprecation warnings in test suite
* Resolved ToDo by pointing to issue with concat, see #2975
* Various docs fixes
* Slightly renamed tests to match new name of tested function
* Included minor suggestions from shoyer
* Removed trailing whitespace
* Simplified error message for case combine_manual can't handle
* Removed filter for deprecation warnings, and added test for if user doesn't supply concat_dim
* Simple fixes suggested by shoyer
* Change deprecation warning behaviour
* linting
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@Zac-HD@shoyer@pep8speaks@TomNicholas@dcherian