[SPARK-58582][PS] Use native Spark function for NumPy fmod - #57784
[SPARK-58582][PS] Use native Spark function for NumPy fmod#57784zhengruifeng wants to merge 12 commits into
Conversation
| .cast("double"), | ||
| "fmin": lambda c1, c2: F.least(c1, c2).cast("double"), | ||
| "fmod": pandas_udf(lambda s1, s2: np.fmod(s1, s2), DoubleType()), # type: ignore[call-overload] | ||
| "fmod": lambda c1, c2: F.when( |
There was a problem hiding this comment.
When c2 is NULL and c1 is non-null, the outer when(c1.isNull() | c2.isNull(), c1.cast("double")) returns the dividend's value rather than NULL. Under the old pandas-udf, Spark NULL was converted to pandas NaN before calling np.fmod, so np.fmod(x, NaN) = NaN; the null-divisor propagated as NaN. The new code returns c1 unchanged for a null divisor. In practice, float columns in pandas-on-Spark use NaN (not NULL) for missing values, so the practically-affected population is nullable-integer columns with Python None entries (e.g. pd.array([1, 2, None], dtype="Int64")); those are fully supported and the divergence is reachable. Fix: the outer NULL clause is superfluous — (c1 % c2) already propagates NULL through Spark's built-in null semantics in both positions; removing the outer when clause entirely is cleaner and fixes the regression.
There was a problem hiding this comment.
Addressed in ebdec05. Removed the incorrect null branch; the zero-divisor case now also requires a non-null dividend, so native remainder propagates nulls. Added nullable Int64 coverage, and the focused test passes.
### What changes were proposed in this pull request? Replaces the pandas UDF used for `np.fmod` in the pandas API on Spark with Spark’s native remainder expression. Adds coverage for integral and floating inputs, including zero divisors, infinities, and NaN. ### Why are the changes needed? Using native Spark expressions avoids pandas UDF serialization and lets Spark optimize and code-generate this operation while preserving NumPy-compatible behavior. ### Does this PR introduce _any_ user-facing change? No. This is a behavior-preserving implementation change. ### How was this patch tested? `source ~/.zshrc && conda run -n spark-dev-313 python/run-tests --testnames pyspark.pandas.tests.test_numpy_compat.NumPyCompatTests.test_np_fmod` ### Was this patch authored or co-authored using generative AI tooling? Generated-by: Codex (GPT-5) Closes #57784 from zhengruifeng/pandas-native-fmod-dev2. Authored-by: Ruifeng Zheng <ruifengz@apache.org> Signed-off-by: Ruifeng Zheng <ruifengz@apache.org> (cherry picked from commit 6ef2cdb) Signed-off-by: Ruifeng Zheng <ruifengz@apache.org>
What changes were proposed in this pull request?
Replaces the pandas UDF used for
np.fmodin the pandas API on Spark with Spark’s native remainder expression.Adds coverage for integral and floating inputs, including zero divisors, infinities, and NaN.
Why are the changes needed?
Using native Spark expressions avoids pandas UDF serialization and lets Spark optimize and code-generate this operation while preserving NumPy-compatible behavior.
Does this PR introduce any user-facing change?
No. This is a behavior-preserving implementation change.
How was this patch tested?
source ~/.zshrc && conda run -n spark-dev-313 python/run-tests --testnames pyspark.pandas.tests.test_numpy_compat.NumPyCompatTests.test_np_fmodWas this patch authored or co-authored using generative AI tooling?
Generated-by: Codex (GPT-5)