Skip to content

Commit 1b007b9

Browse files
committed
attempt at ibis compiler
1 parent 18a6c76 commit 1b007b9

File tree

6 files changed

+91
-19
lines changed

6 files changed

+91
-19
lines changed

bigframes/core/compile/ibis_compiler/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@
2121
from __future__ import annotations
2222

2323
import bigframes.core.compile.ibis_compiler.operations.generic_ops # noqa: F401
24+
import bigframes.core.compile.ibis_compiler.operations.geo_ops # noqa: F401
2425
import bigframes.core.compile.ibis_compiler.scalar_op_registry # noqa: F401
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from __future__ import annotations
16+
17+
from bigframes_vendored import ibis
18+
from bigframes_vendored.ibis.expr import types as ibis_types
19+
import bigframes_vendored.ibis.expr.datatypes as ibis_dtypes
20+
import bigframes_vendored.ibis.expr.operations.geospatial as ibis_geo
21+
22+
from bigframes.core.compile.ibis_compiler import scalar_op_compiler
23+
from bigframes.operations import geo_ops
24+
25+
register_unary_op = scalar_op_compiler.scalar_op_compiler.register_unary_op
26+
27+
28+
@register_unary_op(geo_ops.StRegionStatsOp, pass_op=True)
29+
def st_regionstats(
30+
geography: ibis_types.Value,
31+
op: geo_ops.StRegionStatsOp,
32+
):
33+
34+
if op.band:
35+
band = ibis.literal(op.band, type=ibis_dtypes.string())
36+
else:
37+
band = None
38+
39+
if op.include:
40+
include = ibis.literal(op.include, type=ibis_dtypes.string())
41+
else:
42+
include = None
43+
44+
if op.options:
45+
options = ibis.literal(op.options, type=ibis_dtypes.string())
46+
else:
47+
options = None
48+
49+
return ibis_geo.GeoRegionStats(
50+
arg=geography,
51+
raster_id=ibis.literal(op.raster_id, type=ibis_dtypes.string()),
52+
band=band,
53+
include=include,
54+
options=options,
55+
)

bigframes/core/compile/ibis_compiler/scalar_op_compiler.py

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from bigframes.core import agg_expressions, ordering
2727
import bigframes.core.compile.ibis_types
2828
import bigframes.core.expression as ex
29-
from bigframes.operations import geo_ops, numeric_ops
29+
from bigframes.operations import numeric_ops
3030

3131
if TYPE_CHECKING:
3232
import bigframes.operations as ops
@@ -286,19 +286,3 @@ def isnanornull(arg):
286286
@scalar_op_compiler.register_unary_op(numeric_ops.isfinite_op)
287287
def isfinite(arg):
288288
return arg.isinf().negate() & arg.isnan().negate()
289-
290-
291-
@scalar_op_compiler.register_unary_op(geo_ops.StRegionStatsOp, pass_op=True)
292-
def st_regionstats(
293-
geography: ibis_types.Value,
294-
op: geo_ops.StRegionStatsOp,
295-
):
296-
args = [geography] # TODO: get band, include, and other properies from op.
297-
if op.options:
298-
args.append(bigframes_vendored.ibis.literal(op.options, type="json"))
299-
# TODO: We may need a custom ibis op so that we can pass arguments by name instead of position.
300-
return bigframes_vendored.ibis.remote_function(
301-
"st_regionstats",
302-
args,
303-
output_type="struct<min: float, max: float, sum: float, count: int, mean: float, area: float>", # type: ignore
304-
)

tests/system/small/geopandas/test_geoseries.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
Polygon,
3131
)
3232

33+
import bigframes.bigquery
3334
import bigframes.geopandas
3435
import bigframes.pandas
3536
import bigframes.series
@@ -557,9 +558,8 @@ def test_st_regionstats(session: bigframes.session.Session):
557558
]
558559
)
559560
geos = bigframes.geopandas.GeoSeries([polygon], session=session)
560-
rasters = bigframes.pandas.Series([raster], dtype="string", session=session)
561561
result = bigframes.bigquery.st_regionstats(
562-
geos, rasters, "loss", options={"scale": 1000}
562+
geos, raster, "loss", options={"scale": 1000}
563563
).to_pandas()
564564
assert result is not None
565565
assert "mean" in result.columns

third_party/bigframes_vendored/ibis/backends/sql/compilers/bigquery/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,16 @@ def visit_BoundingBox(self, op, *, arg):
261261

262262
visit_GeoXMax = visit_GeoXMin = visit_GeoYMax = visit_GeoYMin = visit_BoundingBox
263263

264+
def visit_GeoRegionStats(self, op, *, arg, raster_id, band, include, options):
265+
args = [arg, raster_id]
266+
if op.band:
267+
args.append(sge.Kwarg(this="band", expression=band))
268+
if op.include:
269+
args.append(sge.Kwarg(this="include", expression=include))
270+
if op.options:
271+
args.append(sge.Kwarg(this="options", expression=options))
272+
return sge.func("ST_REGIONSTATS", *args)
273+
264274
def visit_GeoSimplify(self, op, *, arg, tolerance, preserve_collapsed):
265275
if (
266276
not isinstance(op.preserve_collapsed, ops.Literal)

third_party/bigframes_vendored/ibis/expr/operations/geospatial.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,28 @@ class GeoNRings(GeoSpatialUnOp):
343343
dtype = dt.int64
344344

345345

346+
@public
347+
class GeoRegionStats(GeoSpatialUnOp):
348+
"""Returns results of ST_REGIONSTATS."""
349+
350+
raster_id: Value[dt.String]
351+
band: Value[dt.String]
352+
include: Value[dt.String]
353+
options: Value[dt.String]
354+
355+
dtype = dt.Struct(
356+
fields={
357+
"count": dt.int64,
358+
"min": dt.float64,
359+
"max": dt.float64,
360+
"stdDev": dt.float64,
361+
"sum": dt.float64,
362+
"mean": dt.float64,
363+
"area": dt.float64,
364+
}
365+
)
366+
367+
346368
@public
347369
class GeoSRID(GeoSpatialUnOp):
348370
"""Returns the spatial reference identifier for the ST_Geometry."""

0 commit comments

Comments
 (0)