timsaucer opened a new issue, #1764:
URL: https://github.com/apache/datafusion-python/issues/1764

   **Describe the bug**
   
   `Expr.over()` on an aggregate function builds a new window function from 
only the aggregate's function and arguments. Any `order_by`, `null_treatment`, 
`filter`, or `distinct` set on the aggregate is silently dropped, so the window 
result differs from what the aggregate would compute over the same partition.
   
   **To Reproduce**
   
   ```python
   from datafusion import SessionContext, col, functions as F
   from datafusion.common import NullTreatment
   from datafusion.expr import Window
   
   ctx = SessionContext()
   df = ctx.from_pydict({"g": [1, 1, 1], "i": [1, 2, 3], "v": [None, 5, 7]})
   
   cases = {
       "first_value": F.first_value(
           col("v"),
           order_by=col("i").sort(ascending=False),
           null_treatment=NullTreatment.IGNORE_NULLS,
       ),
       "sum filter": F.sum(col("v"), filter=col("i") > 2),
       "count distinct": F.count(col("g"), distinct=True),
   }
   for name, e in cases.items():
       w = e.over(Window(partition_by=[col("g")]))
       print(name, w.canonical_name())
       print("  aggregate:", df.aggregate([], 
[e.alias("r")]).collect_column("r").to_pylist())
       print("  window:   ", 
df.select(w.alias("r")).collect_column("r").to_pylist())
   ```
   
   | Expression | Aggregate | `.over(Window(partition_by=[g]))` |
   |---|---|---|
   | `first_value(v, order_by=i DESC, IGNORE_NULLS)` | `7` | `[None, None, 
None]` |
   | `sum(v, filter=i > 2)` | `7` | `[12, 12, 12]` |
   | `count(g, distinct=True)` | `1` | `[3, 3, 3]` |
   
   The generated name shows the loss, e.g. `first_value(v) PARTITION BY [g] 
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING`, with no ordering or 
`IGNORE NULLS`.
   
   **Expected behavior**
   
   Options set on the aggregate either carry into the window function or raise 
an error. They should never be silently discarded.
   
   **Additional context**
   
   The cause is the `Expr::AggregateFunction` branch of `PyExpr::over` in 
`crates/core/src/expr.rs`, which calls 
`WindowFunction::new(AggregateUDF(agg_fn.func), agg_fn.params.args)` and 
ignores the rest of `agg_fn.params`. The `Expr::WindowFunction` branch had the 
same problem and was fixed in 81735542 by starting from `builder_from_expr`.
   
   Possible approach:
   
   - `null_treatment`, `filter`, and `distinct` map directly onto 
`WindowFunctionParams` and can be carried over. Execution support for `filter` 
/ `distinct` in window aggregates should be confirmed, and an error raised 
where unsupported.
   - `order_by` needs a decision. On an aggregate it orders the values fed to 
the accumulator. On a window it orders rows and switches the default frame from 
the whole partition to a running frame. Carrying it over blindly fixes 
`first_value` but turns `sum(v, order_by=...)` into a running sum, and it 
conflicts when `Window` also sets `order_by`. Options: raise when the aggregate 
has an `order_by`, or carry it only when `Window` sets neither `order_by` nor a 
frame and pin the frame to the whole partition.
   - The `Expr.over` docstring ("how each of the parameters is used is 
determined by the underlying aggregate function") should state the resulting 
behavior.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to