Я хотел бы использовать пользовательское выражение в другом выражении, как в этом искусственно простом примере:
import polars as pl
def expr1(method: str) -> pl.Expr:
return pl.col('A').expr2(method).abs()
def expr2(method: str) -> pl.Expr:
if method == 'ceil':
return pl.col('A').ceil()
elif method == 'floor':
return pl.col('A').floor()
else:
raise ValueError()
df = pl.Series('A', [0]).to_frame()
df.select(
expr1('ceil')
)
Очевидно, что это не сработает, так как моя пользовательская функция не является атрибутом объекта Expr
:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Input In [4], in <cell line: 18>()
13 raise ValueError()
16 df = pl.DataFrame(schema = {'A': pl.Int32})
18 df.select(
---> 19 expr1('ceil')
20 )
Input In [4], in expr1(method)
4 def expr1(method: str) -> pl.Expr:
----> 5 return pl.col('A').expr2(method).abs()
AttributeError: 'Expr' object has no attribute 'expr2'
Это очень искусственный пример для простоты, но общая цель здесь состоит в том, чтобы иметь возможность «вызывать» пользовательское выражение из другого выражения. Как мне это сделать?
Тогда я подумал, что, возможно, сработает цепочка, но это тоже не работает по той же причине:
import polars as pl
def expr1(method: str) -> pl.Expr:
return pl.col('A').abs()
def expr2(method: str) -> pl.Expr:
if method == 'ceil':
return pl.col('A').ceil()
elif method == 'floor':
return pl.col('A').floor()
else:
raise ValueError()
df = pl.Series('A', [0]).to_frame()
df.select(
expr2('ceil').expr1()
)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Input In [6], in <cell line: 18>()
13 raise ValueError()
16 df = pl.DataFrame(schema = {'A': pl.Int32})
18 df.select(
---> 19 pl.col('A').expr2('ceil').expr1()
20 )
AttributeError: 'Expr' object has no attribute 'expr2'
Затем я попытался использовать apply()
или map()
, но это тоже не работает:
import polars as pl
def expr1(method: str) -> pl.Expr:
return pl.col('A').apply(expr2(method)).abs()
def expr2(method: str) -> pl.Expr:
if method == 'ceil':
return pl.col('A').ceil()
elif method == 'floor':
return pl.col('A').floor()
else:
raise ValueError()
df = pl.Series('A', [0]).to_frame()
df.select(
expr1('ceil')
)
---------------------------------------------------------------------------
ComputeError Traceback (most recent call last)
Input In [11], in <cell line: 18>()
13 raise ValueError()
16 df = pl.Series('A', [0]).to_frame()
---> 18 df.select(
19 expr1('ceil')
20 )
File ~/.local/lib/python3.10/site-packages/polars/internals/dataframe/frame.py:6445, in DataFrame.select(self, exprs, *more_exprs, **named_exprs)
6337 def select(
6338 self,
6339 exprs: IntoExpr | Iterable[IntoExpr] | None = None,
6340 *more_exprs: IntoExpr,
6341 **named_exprs: IntoExpr,
6342 ) -> Self:
6343 """
6344 Select columns from this DataFrame.
6345
(...)
6442
6443 """
6444 return self._from_pydf(
-> 6445 self.lazy()
6446 .select(exprs, *more_exprs, **named_exprs)
6447 .collect(no_optimization=True)
6448 ._df
6449 )
File ~/.local/lib/python3.10/site-packages/polars/internals/lazyframe/frame.py:1438, in LazyFrame.collect(self, type_coercion, predicate_pushdown, projection_pushdown, simplify_expression, no_optimization, slice_pushdown, common_subplan_elimination, streaming)
1427 common_subplan_elimination = False
1429 ldf = self._ldf.optimization_toggle(
1430 type_coercion,
1431 predicate_pushdown,
(...)
1436 streaming,
1437 )
-> 1438 return pli.wrap_df(ldf.collect())
ComputeError: TypeError: 'Expr' object is not callable
Мне кажется, что apply()
и map()
нельзя использовать с полярными выражениями, а только с функциями Python.
Тем не менее, должен быть способ связывать и вкладывать пользовательские выражения, верно?
Вот документация по расширению API
Если вы не хотите создавать новое пространство имен, вы можете поместить свои новые выражения в пространство имен pl.Expr
.
Однако ваши expr1
и expr2
несовместимы. В expr1
вы пытаетесь вызвать expr2
из pl.col('A')
, но expr2
не ссылается на себя, она жестко запрограммирована на col('A')
.
Предполагая, что вы намерены создать «нормальное» выражение, в котором нет встроенных жестко закодированных столбцов, вам не нужны два выражения, которые вы просто сделаете так:
def expr2(self, method: str) -> pl.Expr:
if method == 'ceil':
return self.ceil()
elif method == 'floor':
return self.floor()
else:
raise ValueError()
pl.Expr.expr2=expr2 #this is the monkey patching
del expr2 # not strictly necessary but it keeps the global cleaned up
Другая ваша проблема заключается в том, что вы не можете сделать ceil
против int. Итак, если вы начали с чего-то вроде
df=pl.DataFrame({'A':[1.1,2.2,3.3]})
тогда вы могли бы сделать
df.select(pl.col('A').expr2('ceil'))
и
df.select(pl.col("A").expr2('floor'))
Большое спасибо, я думаю, что регистрация выражений будет правильным решением.
Мой пример очень искусственный, максимально простой. Конечно, вы совершенно правы в том, что использование ceil для типов данных int не имеет никакого смысла. Я просто использовал его в демонстрационных целях и забыл о фактическом типе данных.
expr2(method)
этоpl.col('A').ceil()
- так что простоexpr2(method).abs()
вместоpl.col('A').expr2(method).abs()
... если вы об этом спрашиваете?