-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ignore_nan argument to concordance_cc() #43
Changes from 10 commits
e260b6c
e6319d6
294abb5
f157132
380377f
6ade721
bbf53f8
7e07356
86de2d4
5e170d8
8136e95
81b7cb5
f21d879
1f57ef0
bf55c58
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import numpy as np | ||
import pandas as pd | ||
import pytest | ||
|
||
import audmetric | ||
|
||
|
||
def expected_ccc(truth, prediction, ignore_nan): | ||
r"""Expecte Concordance Correlation Coefficient. | ||
|
||
This is a direct implementation of its math equation. | ||
|
||
If only a single sample is given, | ||
it should return NaN. | ||
|
||
""" | ||
prediction = np.array(list(prediction)) | ||
truth = np.array(list(truth)) | ||
|
||
if ignore_nan: | ||
mask = ~(np.isnan(truth) | np.isnan(prediction)) | ||
truth = truth[mask] | ||
prediction = prediction[mask] | ||
|
||
if len(prediction) < 2: | ||
ccc = np.NaN | ||
else: | ||
denominator = ( | ||
prediction.std() ** 2 | ||
+ truth.std() ** 2 | ||
+ (prediction.mean() - truth.mean()) ** 2 | ||
) | ||
if denominator == 0: | ||
ccc = np.NaN | ||
else: | ||
r = np.corrcoef(list(prediction), list(truth))[0][1] | ||
numerator = 2 * r * prediction.std() * truth.std() | ||
ccc = numerator / denominator | ||
return ccc | ||
|
||
|
||
@pytest.mark.parametrize( | ||
'truth, prediction, ignore_nan', | ||
[ | ||
( | ||
np.random.randint(0, 10, size=5), | ||
np.random.randint(0, 10, size=5), | ||
False, | ||
), | ||
( | ||
pd.Series(np.random.randint(0, 10, size=5)).astype('Int64'), | ||
pd.Series(np.random.randint(0, 10, size=5)).astype('Int64'), | ||
False, | ||
), | ||
( | ||
np.random.randint(0, 10, size=1), | ||
np.random.randint(0, 10, size=1), | ||
False, | ||
), | ||
( | ||
np.random.randint(0, 10, size=10), | ||
np.random.randint(0, 10, size=10), | ||
False, | ||
), | ||
( | ||
np.random.randint(0, 2, size=100), | ||
np.random.randint(0, 2, size=100), | ||
False, | ||
), | ||
( | ||
np.array([]), | ||
np.array([]), | ||
False, | ||
), | ||
( | ||
np.zeros(10), | ||
np.zeros(10), | ||
False, | ||
), | ||
( | ||
[0, 1, 2, 3, 4, 5, 6, np.NaN], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think in addition we should also add cases where There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I updated the tests and added now an additional test for different |
||
[0, 2, 3, 5, 6, 7, 7, np.NaN], | ||
False, | ||
), | ||
( | ||
[0, 1, 2, 3, 4, 5, 6, np.NaN], | ||
[0, 2, 3, 5, 6, 7, 7, np.NaN], | ||
True, | ||
), | ||
] | ||
) | ||
def test_concordance_cc(truth, prediction, ignore_nan): | ||
|
||
ccc = audmetric.concordance_cc(truth, prediction, ignore_nan=ignore_nan) | ||
|
||
np.testing.assert_almost_equal( | ||
ccc, | ||
expected_ccc(truth, prediction, ignore_nan), | ||
) | ||
|
||
|
||
@pytest.mark.parametrize('ignore_nan', [True, False]) | ||
@pytest.mark.parametrize( | ||
'truth, prediction', | ||
[ | ||
( | ||
[], | ||
[], | ||
), | ||
( | ||
[0], | ||
[0], | ||
), | ||
( | ||
[0, np.NaN], | ||
[0, np.NaN], | ||
), | ||
] | ||
) | ||
def test_concordance_cc_expected_nan(truth, prediction, ignore_nan): | ||
ccc = audmetric.concordance_cc(truth, prediction, ignore_nan=ignore_nan) | ||
assert np.isnan(ccc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we actually need those special cases where we return
np.NaN
or can we simplify the function now?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, forgot to remove this. We don't need this and it is now removed.