Skip to content

add nan corr tests - #247

Merged
raybellwaves merged 6 commits into
xarray-contrib:masterfrom
raybellwaves:use-nan-from-conftest
Jan 19, 2021
Merged

add nan corr tests#247
raybellwaves merged 6 commits into
xarray-contrib:masterfrom
raybellwaves:use-nan-from-conftest

Conversation

@raybellwaves

@raybellwaves raybellwaves commented Jan 16, 2021

Copy link
Copy Markdown
Member

Closes #246

  • refactoring

Ignore the name of the branch. I worked on added nans to in the correlation tests.

I impact did a little bit of work on #245 but this doesn't close that.

I took out the has_nan parameter to the tests and instead moved to another test for nans which I could use a_nan from the conftest.py. Unfortunately it means duplicating most of the code in the test function but from reading some pytest docs they recommend using fixtures in the conftest.py as function imputs. Not sure if we create another function inside test_deterministic or even move some upstream to conftest.py to avoid the duplicated code.

@aaronspring

aaronspring commented Jan 16, 2021

Copy link
Copy Markdown
Collaborator

EDIT: Not too much related to this PR.
We use pytest-lazy-fixture in climpred to parametrize fixtures.
If it is only about chunking or setting a few NaNs, you can parametrize input as xr.dataset, xr.dataarray, chunked, has_nans and call a function depending on input.

@aaronspring aaronspring left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I am doubting the purpose of these tests now. Aren’t we essentially only testing whether the xs.metric function does the same as the np_deterministic function? Is there are reason why this should not work? Isn’t this all independent of chunking and nans?

This feedback is not too much about your changes to the code but rather what the tests check, which is not useful IMO. I would rather like to see tests checking that nans change the result for skipna True or False. Maybe this would then go into another file though...

Comment thread xskillscore/tests/test_deterministic.py Outdated
@pytest.mark.parametrize("dim", AXES)
@pytest.mark.parametrize("weight_bool", [True, False])
@pytest.mark.parametrize("skipna", [True, False])
def test_distance_metrics_xr_nan(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This test could be parametrize with lazy fixture

Comment thread xskillscore/tests/test_deterministic.py Outdated
):
"""Test whether distance metrics for xarray functions can be lazy when
chunked by using dask and give same results."""
chunked by using dask and give same results as np array."""

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yes the test name was misleading. We don't really check whether result is also chunked, as we often do when calling the function _dask

Comment thread xskillscore/tests/test_deterministic.py Outdated
else:
expected = metric(a.load(), b.load(), dim, _weights, skipna=skipna)
assert expected.chunks is None
assert_allclose(actual.compute(), expected)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We don't even need all the computes and loads. Assert triggers that on its own...

Comment thread xskillscore/tests/test_deterministic.py Outdated
@pytest.mark.parametrize("weight_bool", [True, False])
@pytest.mark.parametrize("skipna", [True, False])
@pytest.mark.parametrize("has_nan", [True])
def test_correlation_metrics_xr_dask_nan(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Try param lazy fixture

Comment thread xskillscore/tests/test_deterministic.py Outdated
@pytest.mark.parametrize("dim", AXES)
@pytest.mark.parametrize("weight_bool", [True, False])
@pytest.mark.parametrize("skipna", [True, False])
@pytest.mark.parametrize("has_nan", [True])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Only one here

Comment thread xskillscore/tests/test_deterministic.py Outdated
@pytest.mark.parametrize("metrics", distance_metrics)
@pytest.mark.parametrize("dim", AXES)
@pytest.mark.parametrize("weight_bool", [True, False])
def test_distance_metrics_xr(a, b, dim, weight_bool, weights, metrics):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Too me the test names here were all misleading. The docstring reflects what the tests do, but not the titles...

Comment thread xskillscore/tests/test_deterministic.py Outdated
def test_correlation_metrics_xr_nan(
a_nan, b_nan, dim, weight_bool, weights, metrics, skipna
):
"""Test whether correlation metric for xarray functions (from

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I am doubting the purpose of these tests now. Aren’t we essentially only testing whether the xs.metric function does the same as the np_deterministic function? Is there are reason why this should not work? Isn’t this all independent of chunking and nans?

@raybellwaves

Copy link
Copy Markdown
Member Author

Removed the extra tests. There's probably a bigger question about the purpose of the tests etc. but I this PR is ok for getting the correlation tests in line with the distance tests. I'm not familiar with pytest lazy fixture.

def test_correlation_metrics_xr(a, b, dim, weight_bool, weights, metrics):
@pytest.mark.parametrize("skipna", [True, False])
@pytest.mark.parametrize("has_nan", [True, False])
def test_correlation_metrics_ufunc_same_np(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

how to work with lazy fixtures:

  1. add pytest-lazy-fixture to env
  2. pytest.mark.parametrize(fixtures) https://github.com/TvoroG/pytest-lazy-fixture See https://github.com/pangeo-data/climpred/blob/fdec3af0aabac42de1787aecc0fcbef64a4800f7/climpred/tests/test_bootstrap.py#L227

can be done here for this test and ufunc_dask_np

Comment thread xskillscore/tests/conftest.py Outdated


@pytest.fixture
def a_nan_land(a):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we call this fixed mask and the other random nan?

if _weights is not None:
_weights = _weights.load()
if metric in temporal_only_metrics:
expected = metric(a.load(), b.load(), dim, skipna=skipna)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Compute not needed IMO

Comment thread xskillscore/tests/test_deterministic.py Outdated
expected = metric(a.load(), b.load(), dim, skipna=skipna)
else:
expected = metric(a.load(), b.load(), dim, _weights, skipna=skipna)
assert expected.chunks is None

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is that assert needed for the docstring description? IMO not

# check that chunks for chunk inputs
assert actual.chunks is not None
if _weights is not None:
_weights = _weights.load()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Compute / load now only needed because a,b,weights require being all either or not chunked

deterministic.py) give save numerical results as for numpy functions from
deterministic.py) give save numerical results as for numpy functions (from
np_deterministic.py)."""
if has_nan:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Didn’t you want to use a_nan from conftest here?

@raybellwaves

Copy link
Copy Markdown
Member Author

Thanks for creating #248

One thing I learned here is a we have a function that is named test_distance_metrics_xr_dask. In actual fact it's testing that you get the same result if the DataArray is made up of a dask array compared to if the DataArray is made up of a numpy array. So I renamed it test_distance_metrics_daskda_same_npda.

Couple things left.

Yes I would like to use a_rand_nan when we set "has_nan" = True. I'm unsure about calling something from conftest.py instead of a function. i.e. I know you can do def test(a, ...) of def test(a_rand_nan, ...) but i'm not sure how to do

def test(a, ...)
if has_nan:
    a = a_rand_nan(a)

without getting NameError: name 'a_rand_nan' is not defined

Would you mind making a code suggestion on the pytest-lazy-fixture useage: https://haacked.com/archive/2019/06/03/suggested-changes/

@aaronspring

Copy link
Copy Markdown
Collaborator

One thing I learned here is a we have a function that is named test_distance_metrics_xr_dask. In actual fact it's testing that you get the same result if the DataArray is made up of a dask array compared to if the DataArray is made up of a numpy array. So I renamed it test_distance_metrics_daskda_same_npda.

I see what you were doing. I was critizising the purpose of both of these tests.

@aaronspring

Copy link
Copy Markdown
Collaborator

Yes I would like to use a_rand_nan when we set "has_nan" = True. I'm unsure about calling something from conftest.py instead of a function. i.e. I know you can do def test(a, ...) of def test(a_rand_nan, ...) but i'm not sure how to do

We either use functions inside the test to modify inputs, or we list fixtures test function arguments with pytestlazyfixture. But these different approaches shouldn't mix. I will provide a code suggestion

Comment on lines +139 to +150
@pytest.mark.parametrize("metrics", correlation_metrics)
@pytest.mark.parametrize("dim", AXES)
@pytest.mark.parametrize("weight_bool", [True, False])
@pytest.mark.parametrize("skipna", [True, False])
@pytest.mark.parametrize("has_nan", [True, False])
def test_correlation_metrics_daskda_same_npda(
a_dask, b_dask, dim, weight_bool, weights_dask, metrics, skipna, has_nan
):
"""Test whether correlation metric for xarray functions can be lazy when
chunked by using dask and give same results as DataArray with numpy array."""
a = a_dask.copy()
b = b_dask.copy()

@aaronspring aaronspring Jan 18, 2021

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
@pytest.mark.parametrize("metrics", correlation_metrics)
@pytest.mark.parametrize("dim", AXES)
@pytest.mark.parametrize("weight_bool", [True, False])
@pytest.mark.parametrize("skipna", [True, False])
@pytest.mark.parametrize("has_nan", [True, False])
def test_correlation_metrics_daskda_same_npda(
a_dask, b_dask, dim, weight_bool, weights_dask, metrics, skipna, has_nan
):
"""Test whether correlation metric for xarray functions can be lazy when
chunked by using dask and give same results as DataArray with numpy array."""
a = a_dask.copy()
b = b_dask.copy()
@pytest.mark.parametrize(
"a2, b2",
[
(
pytest.lazy_fixture("a_dask"),
pytest.lazy_fixture("b_dask"),
),
(
pytest.lazy_fixture("a"),
pytest.lazy_fixture("b"),
),
(
pytest.lazy_fixture("a_nan"),
pytest.lazy_fixture("b_nan"),
),
],
)
@pytest.mark.parametrize("metrics", correlation_metrics)
@pytest.mark.parametrize("dim", AXES)
@pytest.mark.parametrize("weight_bool", [True, False])
@pytest.mark.parametrize("skipna", [True, False])
def test_correlation_metrics_daskda_same_npda(
a2, b2, dim, weight_bool, weights_dask, metrics, skipna, has_nan
):
"""Test whether correlation metric for xarray functions can be lazy when
chunked by using dask and give same results as DataArray with numpy array."""
a = a2.copy()
b = b2.copy()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

requires: pip install pytest-lazy-fixture

maybe also rename a to a_1d so we can use a in the parametrized tests as input. cannot give a2 and a in this example both the same name.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this example now with a_nan is probably not better than has_nan [True, False]. has_nan has the advantage that the small function changes the code. on the other hand you could also add a_nan_dask to the list of inputs, but this would be growing and growing...

there will be no 100% clean and everything matching solution in this...

@raybellwaves

Copy link
Copy Markdown
Member Author

I believe this PR is good to go. This closes the issue linked with a little bit of renaming to make further test development easier (or even standardized). I've opted not to use pytest-lazy-fixture but thank you for teaching me about it. I think it's very useful but as you mention I would prefer to have one method of testing per file

@raybellwaves
raybellwaves merged commit 7e2ed96 into xarray-contrib:master Jan 19, 2021
@raybellwaves
raybellwaves deleted the use-nan-from-conftest branch January 19, 2021 14:26
Sign up for free to 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.

Add test skipna for correlation metrcs

2 participants