3131import bigframes .dtypes
3232import bigframes .operations as ops
3333import bigframes .operations .aggregations as agg_ops
34+ import bigframes .operations .array_ops as arr_ops
3435import bigframes .operations .bool_ops as bool_ops
3536import bigframes .operations .comparison_ops as comp_ops
37+ import bigframes .operations .date_ops as date_ops
3638import bigframes .operations .datetime_ops as dt_ops
39+ import bigframes .operations .frequency_ops as freq_ops
3740import bigframes .operations .generic_ops as gen_ops
3841import bigframes .operations .json_ops as json_ops
3942import bigframes .operations .numeric_ops as num_ops
@@ -74,6 +77,20 @@ def decorator(func):
7477
7578
7679if polars_installed :
80+ _FREQ_MAPPING = {
81+ "Y" : "1y" ,
82+ "Q" : "1q" ,
83+ "M" : "1mo" ,
84+ "W" : "1w" ,
85+ "D" : "1d" ,
86+ "h" : "1h" ,
87+ "min" : "1m" ,
88+ "s" : "1s" ,
89+ "ms" : "1ms" ,
90+ "us" : "1us" ,
91+ "ns" : "1ns" ,
92+ }
93+
7794 _DTYPE_MAPPING = {
7895 # Direct mappings
7996 bigframes .dtypes .INT_DTYPE : pl .Int64 (),
@@ -301,11 +318,76 @@ def _(self, op: ops.ScalarOp, l_input: pl.Expr, r_input: pl.Expr) -> pl.Expr:
301318 assert isinstance (op , string_ops .StrConcatOp )
302319 return pl .concat_str (l_input , r_input )
303320
321+ @compile_op .register (string_ops .StrContainsOp )
322+ def _ (self , op : ops .ScalarOp , input : pl .Expr ) -> pl .Expr :
323+ assert isinstance (op , string_ops .StrContainsOp )
324+ return input .str .contains (pattern = op .pat , literal = True )
325+
326+ @compile_op .register (string_ops .StrContainsRegexOp )
327+ def _ (self , op : ops .ScalarOp , input : pl .Expr ) -> pl .Expr :
328+ assert isinstance (op , string_ops .StrContainsRegexOp )
329+ return input .str .contains (pattern = op .pat , literal = False )
330+
331+ @compile_op .register (string_ops .StartsWithOp )
332+ def _ (self , op : ops .ScalarOp , input : pl .Expr ) -> pl .Expr :
333+ assert isinstance (op , string_ops .StartsWithOp )
334+ if len (op .pat ) == 1 :
335+ return input .str .starts_with (op .pat [0 ])
336+ else :
337+ return pl .any_horizontal (
338+ * (input .str .starts_with (pat ) for pat in op .pat )
339+ )
340+
341+ @compile_op .register (string_ops .EndsWithOp )
342+ def _ (self , op : ops .ScalarOp , input : pl .Expr ) -> pl .Expr :
343+ assert isinstance (op , string_ops .EndsWithOp )
344+ if len (op .pat ) == 1 :
345+ return input .str .ends_with (op .pat [0 ])
346+ else :
347+ return pl .any_horizontal (* (input .str .ends_with (pat ) for pat in op .pat ))
348+
349+ @compile_op .register (freq_ops .FloorDtOp )
350+ def _ (self , op : ops .ScalarOp , input : pl .Expr ) -> pl .Expr :
351+ assert isinstance (op , freq_ops .FloorDtOp )
352+ return input .dt .truncate (every = _FREQ_MAPPING [op .freq ])
353+
304354 @compile_op .register (dt_ops .StrftimeOp )
305355 def _ (self , op : ops .ScalarOp , input : pl .Expr ) -> pl .Expr :
306356 assert isinstance (op , dt_ops .StrftimeOp )
307357 return input .dt .strftime (op .date_format )
308358
359+ @compile_op .register (date_ops .YearOp )
360+ def _ (self , op : ops .ScalarOp , input : pl .Expr ) -> pl .Expr :
361+ return input .dt .year ()
362+
363+ @compile_op .register (date_ops .QuarterOp )
364+ def _ (self , op : ops .ScalarOp , input : pl .Expr ) -> pl .Expr :
365+ return input .dt .quarter ()
366+
367+ @compile_op .register (date_ops .MonthOp )
368+ def _ (self , op : ops .ScalarOp , input : pl .Expr ) -> pl .Expr :
369+ return input .dt .month ()
370+
371+ @compile_op .register (date_ops .DayOfWeekOp )
372+ def _ (self , op : ops .ScalarOp , input : pl .Expr ) -> pl .Expr :
373+ return input .dt .weekday () - 1
374+
375+ @compile_op .register (date_ops .DayOp )
376+ def _ (self , op : ops .ScalarOp , input : pl .Expr ) -> pl .Expr :
377+ return input .dt .day ()
378+
379+ @compile_op .register (date_ops .IsoYearOp )
380+ def _ (self , op : ops .ScalarOp , input : pl .Expr ) -> pl .Expr :
381+ return input .dt .iso_year ()
382+
383+ @compile_op .register (date_ops .IsoWeekOp )
384+ def _ (self , op : ops .ScalarOp , input : pl .Expr ) -> pl .Expr :
385+ return input .dt .week ()
386+
387+ @compile_op .register (date_ops .IsoDayOp )
388+ def _ (self , op : ops .ScalarOp , input : pl .Expr ) -> pl .Expr :
389+ return input .dt .weekday ()
390+
309391 @compile_op .register (dt_ops .ParseDatetimeOp )
310392 def _ (self , op : ops .ScalarOp , input : pl .Expr ) -> pl .Expr :
311393 assert isinstance (op , dt_ops .ParseDatetimeOp )
@@ -325,6 +407,36 @@ def _(self, op: ops.ScalarOp, input: pl.Expr) -> pl.Expr:
325407 assert isinstance (op , json_ops .JSONDecode )
326408 return input .str .json_decode (_DTYPE_MAPPING [op .to_type ])
327409
410+ @compile_op .register (arr_ops .ToArrayOp )
411+ def _ (self , op : ops .ToArrayOp , * inputs : pl .Expr ) -> pl .Expr :
412+ return pl .concat_list (* inputs )
413+
414+ @compile_op .register (arr_ops .ArrayReduceOp )
415+ def _ (self , op : ops .ArrayReduceOp , input : pl .Expr ) -> pl .Expr :
416+ # TODO: Unify this with general aggregation compilation?
417+ if isinstance (op .aggregation , agg_ops .MinOp ):
418+ return input .list .min ()
419+ if isinstance (op .aggregation , agg_ops .MaxOp ):
420+ return input .list .max ()
421+ if isinstance (op .aggregation , agg_ops .SumOp ):
422+ return input .list .sum ()
423+ if isinstance (op .aggregation , agg_ops .MeanOp ):
424+ return input .list .mean ()
425+ if isinstance (op .aggregation , agg_ops .CountOp ):
426+ return input .list .len ()
427+ if isinstance (op .aggregation , agg_ops .StdOp ):
428+ return input .list .std ()
429+ if isinstance (op .aggregation , agg_ops .VarOp ):
430+ return input .list .var ()
431+ if isinstance (op .aggregation , agg_ops .AnyOp ):
432+ return input .list .any ()
433+ if isinstance (op .aggregation , agg_ops .AllOp ):
434+ return input .list .all ()
435+ else :
436+ raise NotImplementedError (
437+ f"Haven't implemented array aggregation: { op .aggregation } "
438+ )
439+
328440 @dataclasses .dataclass (frozen = True )
329441 class PolarsAggregateCompiler :
330442 scalar_compiler = PolarsExpressionCompiler ()
0 commit comments