Propagate NaN from DataFrame.skew and DataFrame.kurtosis - #12584
Draft
WatchTree-19 wants to merge 1 commit into
Draft
WatchTree-19 wants to merge 1 commit into
WatchTree-19 wants to merge 1 commit into
Conversation
skew() and kurtosis() end with
result = m3 / m2**1.5
if result.ndim == 1:
result = result.fillna(0.0)
result.ndim == 1 is true exactly when the input was a DataFrame, because the
reduction of a DataFrame is a per-column Series. A Series input reduces to a
0-d scalar and skips the fillna entirely. So the two paths disagree: for a
column holding a single NaN, Series.skew() returns NaN and DataFrame.skew()
returns 0.0, and kurtosis returns NaN and -3.0 respectively, from the same
data in the same library.
Both docstrings say the method does not filter NaN, and nan_policy defaults to
"propagate" and is the only accepted value - "omit" raises NotImplementedError.
0.0 and -3.0 are therefore values the documented API says it will not produce.
They are also both inside the plausible range for real data, meaning "symmetric"
and "strongly platykurtic", so nothing downstream looks wrong. On a 200k-row
frame, one missing value in one column is enough to turn a skew of 6.83 into
0.0 and a kurtosis of 155.8 into -3.0.
dask.array.stats.skew, which the docstring says the implementation follows, and
scipy.stats.skew under nan_policy="propagate" both return NaN here.
The existing tests only exercise the Series path, which is the path the fillna
skips, so nothing covered this.
Note on constant columns, since a reviewer will ask: for a column of identical
values pandas returns 0.0, scipy returns NaN, and dask's Series path returns
NaN. This change makes the DataFrame path return NaN, so it agrees with the
Series path and with scipy but not with pandas. The NaN-bearing column is the
unambiguous case; the constant column is a consistency judgement and I am happy
to special-case it if maintainers prefer pandas parity there.
Contributor
Unit Test ResultsSee test report for an extended history of previous test failures. This is useful for diagnosing flaky tests. 25 files ± 0 25 suites ±0 7h 10m 17s ⏱️ + 18m 17s For more details on these failures, see this check. Results for commit 525efed. ± Comparison against base commit 9dc535d. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
skew() and kurtosis() end with
result.ndim == 1 is true exactly when the input was a DataFrame, because the reduction of a DataFrame is a per-column Series. A Series input reduces to a 0-d scalar and skips the fillna entirely. So the two paths disagree: for a column holding a single NaN, Series.skew() returns NaN and DataFrame.skew() returns 0.0, and kurtosis returns NaN and -3.0 respectively, from the same data in the same library.
Both docstrings say the method does not filter NaN, and nan_policy defaults to "propagate" and is the only accepted value - "omit" raises NotImplementedError. 0.0 and -3.0 are therefore values the documented API says it will not produce. They are also both inside the plausible range for real data, meaning "symmetric" and "strongly platykurtic", so nothing downstream looks wrong. On a 200k-row frame, one missing value in one column is enough to turn a skew of 6.83 into 0.0 and a kurtosis of 155.8 into -3.0.
dask.array.stats.skew, which the docstring says the implementation follows, and scipy.stats.skew under nan_policy="propagate" both return NaN here.
The existing tests only exercise the Series path, which is the path the fillna skips, so nothing covered this.
Note on constant columns, since a reviewer will ask: for a column of identical values pandas returns 0.0, scipy returns NaN, and dask's Series path returns NaN. This change makes the DataFrame path return NaN, so it agrees with the Series path and with scipy but not with pandas. The NaN-bearing column is the unambiguous case; the constant column is a consistency judgement and I am happy to special-case it if maintainers prefer pandas parity there.
pixi run lint- ruff, black and mypy all clean on both changed files