Skip to content

Commit

Permalink
expressions: add to_string for num namespace
Browse files Browse the repository at this point in the history
  • Loading branch information
nikhilgarg28 committed Nov 24, 2024
1 parent ead2748 commit 83fb361
Show file tree
Hide file tree
Showing 14 changed files with 451 additions and 1,603 deletions.
1 change: 1 addition & 0 deletions docs/api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ sidebar:
- "api-reference/expressions/num/ceil"
- "api-reference/expressions/num/floor"
- "api-reference/expressions/num/round"
- "api-reference/expressions/num/to_string"

- slug: "api-reference/expressions/str"
title: "String Expressions"
Expand Down
23 changes: 23 additions & 0 deletions docs/examples/api-reference/expressions/num.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,26 @@ def test_round():

with pytest.raises(Exception):
expr = col("x").round(1.1)


def test_to_string():
# docsnip to_string
from fennel.expr import col

# docsnip-highlight next-line
expr = col("x").num.to_string()

# type is str or optional str
assert expr.typeof(schema={"x": int}) == str
assert expr.typeof(schema={"x": Optional[int]}) == Optional[str]
assert expr.typeof(schema={"x": float}) == str
assert expr.typeof(schema={"x": Optional[float]}) == Optional[str]

# can be evaluated with a dataframe
df = pd.DataFrame({"x": pd.Series([1.1, -2.3, None])})
assert expr.eval(df, schema={"x": Optional[float]}).tolist() == [
"1.1",
"-2.3",
pd.NA,
]
# /docsnip
26 changes: 26 additions & 0 deletions docs/pages/api-reference/expressions/num/to_string.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
title: To String
order: 0
status: published
---

### To String

Function in `num` namespace to convert a number to a string.

#### Returns
<Expandable type="Expr">
Returns an expression object denoting the string value of the input data. The
data type of the resulting expression is `str` (or `Optional[str]` if the input
is an optional number).
</Expandable>

<pre snippet="api-reference/expressions/num#to_string"
status="success" message="Converting a number to a string using Fennel expressions">
</pre>

#### Errors
<Expandable title="Invoking on a non-numeric type">
Error during `typeof` or `eval` if the input expression is not of type int,
float, optional int or optional float.
</Expandable>
3 changes: 3 additions & 0 deletions fennel/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## [1.5.56] - 2024-11-24
- Add to_string method to num namespace in expressions

## [1.5.55] - 2024-11-14
- Add print visitor for couple missing string operations

Expand Down
7 changes: 7 additions & 0 deletions fennel/expr/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,10 @@ class Floor(MathOp):
pass


class NumToStr(MathOp):
pass


class MathNoop(MathOp):
pass

Expand All @@ -504,6 +508,9 @@ def ceil(self) -> _Number:
def floor(self) -> _Number:
return _Number(self, Floor())

def to_string(self) -> _String:
return _String(_Number(self, NumToStr()), StringNoop())


#########################################################
# String Functions
Expand Down
3 changes: 3 additions & 0 deletions fennel/expr/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
MathNoop,
Round,
Ceil,
NumToStr,
Abs,
Floor,
StringNoop,
Expand Down Expand Up @@ -231,6 +232,8 @@ def visitNumber(self, obj):
expr.math_fn.fn.CopyFrom(proto.MathOp(abs=proto.Abs()))
elif isinstance(obj.op, Floor):
expr.math_fn.fn.CopyFrom(proto.MathOp(floor=proto.Floor()))
elif isinstance(obj.op, NumToStr):
expr.math_fn.fn.CopyFrom(proto.MathOp(to_string=proto.ToString()))
else:
raise InvalidExprException("invalid number operation: %s" % obj.op)
expr.math_fn.operand.CopyFrom(self.visit(obj.operand))
Expand Down
11 changes: 11 additions & 0 deletions fennel/expr/test_expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,17 @@ def test_math_expr():
assert ref_extractor.refs == {"a", "b", "d"}


def test_math_expr_to_string():
expr = col("a").num.to_string()
printer = ExprPrinter()
assert printer.print(expr.root) == 'TO_STRING(col("a"))'

df = pd.DataFrame({"a": [1.1232, 2.1232, 3.1232]})
ret = expr.eval(df, {"a": float})
assert ret.tolist() == ["1.1232", "2.1232", "3.1232"]
assert expr.typeof({"a": float}) == str


def test_bool_expr():
expr = (col("a") == 5) | ((col("b") == "random") & (col("c") == 3.2))
printer = ExprPrinter()
Expand Down
3 changes: 3 additions & 0 deletions fennel/expr/visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
MathNoop,
Round,
Ceil,
NumToStr,
Abs,
Floor,
StringNoop,
Expand Down Expand Up @@ -267,6 +268,8 @@ def visitNumber(self, obj):
return "CEIL(%s)" % self.visit(obj.operand)
elif isinstance(obj.op, Abs):
return "ABS(%s)" % self.visit(obj.operand)
elif isinstance(obj.op, NumToStr):
return f"TO_STRING({self.visit(obj.operand)})"
else:
raise InvalidExprException("invalid number operation: %s" % obj.op)

Expand Down
241 changes: 115 additions & 126 deletions fennel/gen/connector_pb2.py

Large diffs are not rendered by default.

Loading

0 comments on commit 83fb361

Please sign in to comment.