[ 
https://issues.apache.org/jira/browse/SPARK-59505?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18115346#comment-18115346
 ] 

Li Guo commented on SPARK-59505:
--------------------------------

Opened a PR for this: https://github.com/apache/spark/pull/58797

[~podongfeng] this sits in the decimal rescaling from SPARK-53938, so I would 
appreciate your eyes on it when you get a chance. The change is one line 
(ROUND_HALF_EVEN to ROUND_HALF_UP); the rest is tests covering the Arrow UDF, 
the Arrow UDTF, a Python data source and createDataFrame in Classic and 
Connect. cc [~gurwls223]

> Arrow conversion rounds decimals HALF_EVEN, so Arrow Python UDFs and Connect 
> createDataFrame return different values than SQL and Classic
> -----------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: SPARK-59505
>                 URL: https://issues.apache.org/jira/browse/SPARK-59505
>             Project: Spark
>          Issue Type: Bug
>          Components: Connect, PySpark
>    Affects Versions: 4.2.0, 4.3.0, 5.0.0, 4.1.3
>            Reporter: Li Guo
>            Priority: Major
>              Labels: pull-request-available
>
> {{LocalDataToArrowConversion}} rescales a Python {{Decimal}} to the declared 
> scale with {{decimal.Context(prec=..., rounding=decimal.ROUND_HALF_EVEN)}} 
> (python/pyspark/sql/conversion.py, added by SPARK-53938). The JVM rounds 
> HALF_UP whenever it rescales a decimal ({{Decimal.set}}, 
> {{Decimal.changePrecision}}, {{CAST(x AS DECIMAL(p, s))}}), and the pickled 
> Python UDF and UDTF paths and Classic {{createDataFrame}} inherit that 
> because the JVM does the rescale for them. So the Arrow-optimized Python UDF 
> (the default since 4.2), Arrow UDTFs, Python data sources and Spark Connect's 
> {{createDataFrame}} produce a different number than SQL for any value that 
> sits on a rounding tie.
> {code:python}
> from decimal import Decimal
> from pyspark.sql.functions import col, udf
> from pyspark.sql.types import DecimalType
> df = spark.sql("SELECT * FROM VALUES ('1.005'), ('1.025'), ('0.125') AS t(v)")
> [r[0] for r in df.select(col("v").cast(DecimalType(20, 2))).collect()]
> # [Decimal('1.01'), Decimal('1.03'), Decimal('0.13')]
> f = udf(lambda v: Decimal(v), DecimalType(20, 2), useArrow=True)   # the 
> default since 4.2
> [r[0] for r in df.select(f("v")).collect()]
> # [Decimal('1.00'), Decimal('1.02'), Decimal('0.12')]
> f2 = udf(lambda v: Decimal(v), DecimalType(20, 2), useArrow=False)
> [r[0] for r in df.select(f2("v")).collect()]
> # [Decimal('1.01'), Decimal('1.03'), Decimal('0.13')]
> {code}
> Spark Connect and Classic also disagree on the same input: 
> {{spark.createDataFrame([(Decimal("1.005"),)], "d decimal(20, 2)")}} gives 
> 1.01 in Classic and 1.00 through a Connect session. Reproduced on released 
> 4.2.0 and on master, in Classic and Connect sessions.
> Fix: use {{ROUND_HALF_UP}} in that context. One line plus tests on the 
> converter, the Arrow UDF, the Arrow UDTF, a Python data source and 
> createDataFrame in both modes. branch-4.1, branch-4.2 and branch-4.3 carry 
> the same line, so the fix backports cleanly.



--
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