[ 
https://issues.apache.org/jira/browse/SPARK-58213?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

jiangxintong updated SPARK-58213:
---------------------------------
    Description: 
h2. Background

{{corr(x, y)}} computes Pearson correlation as {{ck / sqrt(xMk * yMk)}}. When 
one of the columns is constant, the corresponding variance term ({{xMk}} or 
{{yMk}} in Welford's incremental algorithm) is exactly {{0.0}}, making the 
denominator zero. In ANSI mode this throws {{DIVIDE_BY_ZERO}}; in non-ANSI mode 
{{ck / 0.0}} returns {{NaN}} or {{±Infinity}}. Neither is a meaningful 
correlation for a constant column.

The sibling function {{regr_r2}} (same {{PearsonCorrelation}} base class) 
already guards against {{yMk === 0.0}} and {{xMk === 0.0}} in its 
{{evaluateExpression}}. The Spark 3.1 migration guide lists {{corr}} among 
functions that should return {{NULL}} instead of {{NaN}} on divide-by-zero, but 
the guard was never added.

h2. Repro

{code:sql}
SET spark.sql.ansi.enabled = true;
SELECT corr(CAST(x AS DOUBLE), CAST(y AS DOUBLE))
FROM VALUES (1, 1), (1, 2), (1, 3) AS t(x, y);
-- Before fix: DIVIDE_BY_ZERO
-- After fix:  NULL
{code}

h2. Fix

Add a guard {{xMk === 0.0 || yMk === 0.0}} in the {{evaluateExpression}} of 
{{Corr}}, branching to the existing {{divideByZeroEvalResult}}. This is the 
same mechanism already used by {{n === 1.0}} (single data point) and shared 
with {{regr_r2}}. The guard prevents the division from ever being evaluated 
when the denominator is zero, so {{DIVIDE_BY_ZERO}} is never thrown. Output is 
controlled by {{divideByZeroEvalResult}}:

* *Default* (both ANSI and non-ANSI): {{NULL}}, matching the Spark 3.1 
migration guide and {{regr_r2}}'s behavior
* *Legacy* ({{spark.sql.legacyStatisticalAggregate=true}}): {{NaN}}, preserving 
the pre-3.1 behavior

The Welford accumulator guarantees {{xMk}} and {{yMk}} are exact zero for 
constant input (deviation from mean is always 0), so the {{=== 0.0}} comparison 
is safe.

h2. Testing

* {{linear-regression.sql}}: x-constant and y-constant cases with inline 
{{VALUES}}, both returning {{NULL}}
* Corresponding analyzer golden file regenerated
* {{SQLQueryTestSuite}} passes

  was:
## Background

corr(x, y) computes Pearson correlation as ck / sqrt(xMk * yMk). When one of 
the columns is constant, the corresponding variance term (xMk or yMk in 
Welford's incremental algorithm) is exactly 0.0, making the denominator zero. 
In ANSI mode this throws DIVIDE_BY_ZERO; in non-ANSI mode ck / 0.0 returns NaN 
or ±Infinity. Neither is a meaningful correlation for a constant column.

The sibling function regr_r2 (same PearsonCorrelation base class) already 
guards against yMk === 0.0 and xMk === 0.0 in its evaluateExpression. The Spark 
3.1 migration guide lists corr among functions that should return NULL instead 
of NaN on divide-by-zero, but the guard was never added.

## Repro

SET spark.sql.ansi.enabled = true;
SELECT corr(CAST(x AS DOUBLE), CAST(y AS DOUBLE))
FROM VALUES (1, 1), (1, 2), (1, 3) AS t(x, y);
-- Before fix: DIVIDE_BY_ZERO
-- After fix:  NULL

## Fix

Add a guard xMk === 0.0 || yMk === 0.0 in the evaluateExpression of Corr, 
branching to the existing divideByZeroEvalResult. This is the same mechanism 
already used by n === 1.0 (single data point) and shared with regr_r2. The 
guard prevents the division from ever being evaluated when the denominator is 
zero, so DIVIDE_BY_ZERO is never thrown. Output is controlled by 
divideByZeroEvalResult:

- Default (both ANSI and non-ANSI): NULL, matching the Spark 3.1 migration 
guide and regr_r2's behavior
- Legacy (spark.sql.legacyStatisticalAggregate=true): NaN, preserving the 
pre-3.1 behavior

The Welford accumulator guarantees xMk and yMk are exact zero for constant 
input (deviation from mean is always 0), so the === 0.0 comparison is safe.

## Testing

- linear-regression.sql: x-constant and y-constant cases with inline VALUES, 
both returning NULL
- Corresponding analyzer golden file regenerated
- SQLQueryTestSuite passes


> corr should return NULL when a column has zero variance
> -------------------------------------------------------
>
>                 Key: SPARK-58213
>                 URL: https://issues.apache.org/jira/browse/SPARK-58213
>             Project: Spark
>          Issue Type: Bug
>          Components: SQL
>    Affects Versions: 5.0.0
>            Reporter: jiangxintong
>            Priority: Major
>
> h2. Background
> {{corr(x, y)}} computes Pearson correlation as {{ck / sqrt(xMk * yMk)}}. When 
> one of the columns is constant, the corresponding variance term ({{xMk}} or 
> {{yMk}} in Welford's incremental algorithm) is exactly {{0.0}}, making the 
> denominator zero. In ANSI mode this throws {{DIVIDE_BY_ZERO}}; in non-ANSI 
> mode {{ck / 0.0}} returns {{NaN}} or {{±Infinity}}. Neither is a meaningful 
> correlation for a constant column.
> The sibling function {{regr_r2}} (same {{PearsonCorrelation}} base class) 
> already guards against {{yMk === 0.0}} and {{xMk === 0.0}} in its 
> {{evaluateExpression}}. The Spark 3.1 migration guide lists {{corr}} among 
> functions that should return {{NULL}} instead of {{NaN}} on divide-by-zero, 
> but the guard was never added.
> h2. Repro
> {code:sql}
> SET spark.sql.ansi.enabled = true;
> SELECT corr(CAST(x AS DOUBLE), CAST(y AS DOUBLE))
> FROM VALUES (1, 1), (1, 2), (1, 3) AS t(x, y);
> -- Before fix: DIVIDE_BY_ZERO
> -- After fix:  NULL
> {code}
> h2. Fix
> Add a guard {{xMk === 0.0 || yMk === 0.0}} in the {{evaluateExpression}} of 
> {{Corr}}, branching to the existing {{divideByZeroEvalResult}}. This is the 
> same mechanism already used by {{n === 1.0}} (single data point) and shared 
> with {{regr_r2}}. The guard prevents the division from ever being evaluated 
> when the denominator is zero, so {{DIVIDE_BY_ZERO}} is never thrown. Output 
> is controlled by {{divideByZeroEvalResult}}:
> * *Default* (both ANSI and non-ANSI): {{NULL}}, matching the Spark 3.1 
> migration guide and {{regr_r2}}'s behavior
> * *Legacy* ({{spark.sql.legacyStatisticalAggregate=true}}): {{NaN}}, 
> preserving the pre-3.1 behavior
> The Welford accumulator guarantees {{xMk}} and {{yMk}} are exact zero for 
> constant input (deviation from mean is always 0), so the {{=== 0.0}} 
> comparison is safe.
> h2. Testing
> * {{linear-regression.sql}}: x-constant and y-constant cases with inline 
> {{VALUES}}, both returning {{NULL}}
> * Corresponding analyzer golden file regenerated
> * {{SQLQueryTestSuite}} passes



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to