import%20marimo%0A%0A__generated_with%20%3D%20%220.13.7%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%23%20Motivation%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20from%20datetime%20import%20datetime%0A%0A%20%20%20%20import%20pandas%20as%20pd%0A%0A%20%20%20%20df%20%3D%20pd.DataFrame(%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22date%22%3A%20%5Bdatetime(2020%2C%201%2C%201)%2C%20datetime(2020%2C%201%2C%208)%2C%20datetime(2020%2C%202%2C%203)%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22price%22%3A%20%5B1%2C%204%2C%203%5D%2C%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20df%0A%20%20%20%20return%20datetime%2C%20df%2C%20pd%0A%0A%0A%40app.cell%0Adef%20_(df)%3A%0A%20%20%20%20def%20monthly_aggregate_pandas(user_df)%3A%0A%20%20%20%20%20%20%20%20return%20user_df.resample(%22MS%22%2C%20on%3D%22date%22)%5B%5B%22price%22%5D%5D.mean()%0A%0A%20%20%20%20monthly_aggregate_pandas(df)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%20Dataframe-agnostic%20data%20science%0A%0A%20%20%20%20Let's%20define%20a%20dataframe-agnostic%20function%20to%20calculate%20monthly%20average%20prices.%20It%20needs%20to%20support%20pandas%2C%20Polars%2C%20PySpark%2C%20DuckDB%2C%20PyArrow%2C%20Dask%2C%20and%20cuDF%2C%20without%20doing%20any%20conversion%20between%20libraries.%0A%0A%20%20%20%20%23%23%20Bad%20solution%3A%20just%20convert%20to%20pandas%0A%0A%20%20%20%20This%20kind%20of%20works%2C%20but%3A%0A%0A%20%20%20%20-%20It%20doesn't%20return%20to%20the%20user%20the%20same%20class%20they%20started%20with.%0A%20%20%20%20-%20It%20kills%20lazy%20execution.%0A%20%20%20%20-%20It%20kills%20GPU%20acceleration.%0A%20%20%20%20-%20If%20forces%20pandas%20as%20a%20required%20dependency.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20duckdb%0A%20%20%20%20import%20polars%20as%20pl%0A%20%20%20%20import%20pyarrow%20as%20pa%0A%20%20%20%20import%20pyspark%0A%20%20%20%20import%20pyspark.sql.functions%20as%20F%0A%20%20%20%20from%20pyspark.sql%20import%20SparkSession%0A%0A%20%20%20%20return%20F%2C%20SparkSession%2C%20duckdb%2C%20pa%2C%20pl%2C%20pyspark%0A%0A%0A%40app.cell%0Adef%20_(duckdb%2C%20pa%2C%20pd%2C%20pl%2C%20pyspark)%3A%0A%20%20%20%20def%20monthly_aggregate_bad(user_df)%3A%0A%20%20%20%20%20%20%20%20if%20isinstance(user_df%2C%20pd.DataFrame)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20df%20%3D%20user_df%0A%20%20%20%20%20%20%20%20elif%20isinstance(user_df%2C%20pl.DataFrame)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20df%20%3D%20user_df.to_pandas()%0A%20%20%20%20%20%20%20%20elif%20isinstance(user_df%2C%20duckdb.DuckDBPyRelation)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20df%20%3D%20user_df.df()%0A%20%20%20%20%20%20%20%20elif%20isinstance(user_df%2C%20pa.Table)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20df%20%3D%20user_df.to_pandas()%0A%20%20%20%20%20%20%20%20elif%20isinstance(user_df%2C%20pyspark.sql.dataframe.DataFrame)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20df%20%3D%20user_df.toPandas()%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20TypeError(%22Unsupported%20DataFrame%20type%3A%20cannot%20convert%20to%20pandas%22)%0A%0A%20%20%20%20%20%20%20%20return%20df.resample(%22MS%22%2C%20on%3D%22date%22)%5B%5B%22price%22%5D%5D.mean()%0A%0A%20%20%20%20return%20(monthly_aggregate_bad%2C)%0A%0A%0A%40app.cell%0Adef%20_(datetime)%3A%0A%20%20%20%20data%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%22date%22%3A%20%5Bdatetime(2020%2C%201%2C%201)%2C%20datetime(2020%2C%201%2C%208)%2C%20datetime(2020%2C%202%2C%203)%5D%2C%0A%20%20%20%20%20%20%20%20%22price%22%3A%20%5B1%2C%204%2C%203%5D%2C%0A%20%20%20%20%7D%0A%20%20%20%20return%20(data%2C)%0A%0A%0A%40app.cell%0Adef%20_(SparkSession%2C%20data%2C%20duckdb%2C%20monthly_aggregate_bad%2C%20pa%2C%20pd%2C%20pl)%3A%0A%20%20%20%20%23%20pandas%0A%20%20%20%20pandas_df%20%3D%20pd.DataFrame(data)%0A%20%20%20%20monthly_aggregate_bad(pandas_df)%0A%0A%20%20%20%20%23%20polars%0A%20%20%20%20polars_df%20%3D%20pl.DataFrame(data)%0A%20%20%20%20monthly_aggregate_bad(polars_df)%0A%0A%20%20%20%20%23%20duckdb%0A%20%20%20%20duckdb_df%20%3D%20duckdb.from_df(pandas_df)%0A%20%20%20%20monthly_aggregate_bad(duckdb_df)%0A%0A%20%20%20%20%23%20pyspark%0A%20%20%20%20spark%20%3D%20SparkSession.builder.getOrCreate()%0A%20%20%20%20spark_df%20%3D%20spark.createDataFrame(pandas_df)%0A%20%20%20%20monthly_aggregate_bad(spark_df)%0A%0A%20%20%20%20%23%20pyarrow%0A%20%20%20%20arrow_table%20%3D%20pa.table(data)%0A%20%20%20%20monthly_aggregate_bad(arrow_table)%0A%20%20%20%20return%20arrow_table%2C%20duckdb_df%2C%20pandas_df%2C%20polars_df%2C%20spark_df%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%20Unmaintainable%20solution%3A%20different%20branches%20for%20each%20library%0A%0A%20%20%20%20This%20works%2C%20but%20is%20unfeasibly%20difficult%20to%20test%20and%20maintain%2C%20especially%20when%20also%20factoring%20in%20API%20changes%20between%20different%20versions%20of%20the%20same%20library%20(e.g.%20pandas%20%601.*%60%20vs%20pandas%20%602.*%60).%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(F%2C%20pd%2C%20pl%2C%20pyspark)%3A%0A%20%20%20%20def%20monthly_aggregate_unmaintainable(user_df)%3A%0A%20%20%20%20%20%20%20%20if%20isinstance(user_df%2C%20pd.DataFrame)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20result%20%3D%20user_df.resample(%22MS%22%2C%20on%3D%22date%22)%5B%5B%22price%22%5D%5D.mean()%0A%20%20%20%20%20%20%20%20elif%20isinstance(user_df%2C%20pl.DataFrame)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20result%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20user_df.group_by(pl.col(%22date%22).dt.truncate(%221mo%22))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.agg(pl.col(%22price%22).mean())%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.sort(%22date%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20elif%20isinstance(user_df%2C%20pyspark.sql.dataframe.DataFrame)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20result%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20user_df.withColumn(%22date_month%22%2C%20F.date_trunc(%22month%22%2C%20F.col(%22date%22)))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.groupBy(%22date_month%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.agg(F.mean(%22price%22).alias(%22price_mean%22))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.orderBy(%22date_month%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%23%20TODO%3A%20more%20branches%20for%20DuckDB%2C%20PyArrow%2C%20Dask%2C%20etc...%20%3Asob%3A%0A%20%20%20%20%20%20%20%20return%20result%0A%0A%20%20%20%20return%20(monthly_aggregate_unmaintainable%2C)%0A%0A%0A%40app.cell%0Adef%20_(monthly_aggregate_unmaintainable%2C%20pandas_df%2C%20polars_df%2C%20spark_df)%3A%0A%20%20%20%20%23%20pandas%0A%20%20%20%20monthly_aggregate_unmaintainable(pandas_df)%0A%0A%20%20%20%20%23%20polars%0A%20%20%20%20monthly_aggregate_unmaintainable(polars_df)%0A%0A%20%20%20%20%23%20pyspark%0A%20%20%20%20monthly_aggregate_unmaintainable(spark_df)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%20Best%20solution%3A%20Narwhals%20as%20a%20unified%20dataframe%20interface%0A%0A%20%20%20%20-%20Preserves%20lazy%20execution%20and%20GPU%20acceleration.%0A%20%20%20%20-%20Users%20get%20back%20what%20they%20started%20with.%0A%20%20%20%20-%20Easy%20to%20write%20and%20maintain.%0A%20%20%20%20-%20Strong%20and%20complete%20static%20typing.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20narwhals%20as%20nw%0A%20%20%20%20from%20narwhals.typing%20import%20IntoFrameT%0A%0A%20%20%20%20def%20monthly_aggregate(user_df%3A%20IntoFrameT)%20-%3E%20IntoFrameT%3A%0A%20%20%20%20%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20nw.from_native(user_df)%0A%20%20%20%20%20%20%20%20%20%20%20%20.group_by(nw.col(%22date%22).dt.truncate(%221mo%22))%0A%20%20%20%20%20%20%20%20%20%20%20%20.agg(nw.col(%22price%22).mean())%0A%20%20%20%20%20%20%20%20%20%20%20%20.sort(%22date%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20.to_native()%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20return%20(monthly_aggregate%2C)%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20arrow_table%2C%0A%20%20%20%20duckdb_df%2C%0A%20%20%20%20monthly_aggregate%2C%0A%20%20%20%20pandas_df%2C%0A%20%20%20%20polars_df%2C%0A%20%20%20%20spark_df%2C%0A)%3A%0A%20%20%20%20%23%20pandas%0A%20%20%20%20monthly_aggregate(pandas_df)%0A%0A%20%20%20%20%23%20polars%0A%20%20%20%20monthly_aggregate(polars_df)%0A%0A%20%20%20%20%23%20duckdb%0A%20%20%20%20monthly_aggregate(duckdb_df)%0A%0A%20%20%20%20%23%20pyarrow%0A%20%20%20%20monthly_aggregate(arrow_table)%0A%0A%20%20%20%20%23%20pyspark%0A%20%20%20%20monthly_aggregate(spark_df)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%20Bonus%20-%20can%20we%20generate%20SQL%3F%0A%0A%20%20%20%20Narwhals%20comes%20with%20an%20extra%20bonus%20feature%3A%20by%20combining%20it%20with%20%5BSQLFrame%5D(https%3A%2F%2Fgithub.com%2Feakmanrq%2Fsqlframe)%2C%20we%20can%20easily%20transpiling%20the%20Polars%20API%20to%20any%20major%20SQL%20dialect.%20For%20example%2C%20to%20translate%20to%20the%20DataBricks%20SQL%20dialect%2C%20we%20can%20do%3A%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(monthly_aggregate%2C%20pandas_df)%3A%0A%20%20%20%20from%20sqlframe.duckdb%20import%20DuckDBSession%0A%0A%20%20%20%20sqlframe%20%3D%20DuckDBSession()%0A%20%20%20%20sqlframe_df%20%3D%20sqlframe.createDataFrame(pandas_df)%0A%20%20%20%20sqlframe_result%20%3D%20monthly_aggregate(sqlframe_df)%0A%20%20%20%20print(sqlframe_result.sql(dialect%3D%22databricks%22))%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
e25690abf599f66417fceed8b2711073dbb31d7a36a4db902f9c59e089f39b7b