From bece33a026849f73c3a61fd1ba8aaf9283f1b4e5 Mon Sep 17 00:00:00 2001 From: Cenz Wong <44856918+cenzwong@users.noreply.github.com> Date: Thu, 17 Oct 2024 10:04:02 +0000 Subject: [PATCH] feat: changing columns name to pure function --- dist/pysparky-0.1.0-py3-none-any.whl | Bin 18580 -> 18215 bytes dist/pysparky-0.1.0.tar.gz | Bin 18722 -> 18555 bytes example/dev.ipynb | 86 +++++++++++++----------- pysparky/decorator.py | 96 --------------------------- pysparky/enabler.py | 24 +++++++ pysparky/functions/general.py | 4 +- pysparky/functions/math_.py | 1 - pysparky/spark_ext.py | 35 +++++++--- tests/test_decorator.py | 29 +------- tests/test_enabler.py | 18 +++++ tests/test_spark_ext.py | 28 +++++++- 11 files changed, 142 insertions(+), 179 deletions(-) diff --git a/dist/pysparky-0.1.0-py3-none-any.whl b/dist/pysparky-0.1.0-py3-none-any.whl index ca7c4e606a9ca9ed1f39751c954a57a1d27425ee..704d214a7c666aac2347840d6600f1ca002584c5 100644 GIT binary patch delta 7419 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 delta 7818 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 literal 18722 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 M113OFasV&_01@$E3;+NC diff --git a/example/dev.ipynb b/example/dev.ipynb index 95b4b41..392bb46 100644 --- a/example/dev.ipynb +++ b/example/dev.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 13, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -14,7 +14,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -23,6 +23,19 @@ "text": [ "3.5.2\n" ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "24/10/17 09:54:54 WARN Utils: Your hostname, codespaces-0aafae resolves to a loopback address: 127.0.0.1; using 10.0.10.147 instead (on interface eth0)\n", + "24/10/17 09:54:54 WARN Utils: Set SPARK_LOCAL_IP if you need to bind to another address\n", + "Setting default log level to \"WARN\".\n", + "To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).\n", + "24/10/17 09:54:55 WARN NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable\n", + "24/10/17 09:54:56 WARN Utils: Service 'SparkUI' could not bind on port 4040. Attempting port 4041.\n", + "24/10/17 09:54:56 WARN Utils: Service 'SparkUI' could not bind on port 4041. Attempting port 4042.\n" + ] } ], "source": [ @@ -43,7 +56,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -52,49 +65,42 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Column<'trim(regexp_replace(hi, \\s+, , 1))'>" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "F_.single_space_and_trim(\"hi\")" - ] - }, - { - "cell_type": "code", - "execution_count": 14, + "execution_count": 11, "metadata": {}, "outputs": [ { - "data": { - "text/plain": [ - "Column<'CASE WHEN (hi = 1) THEN Ture END'>" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "+---+\n", + "|ID1|\n", + "+---+\n", + "| 1|\n", + "| 2|\n", + "| 3|\n", + "| 4|\n", + "+---+\n", + "\n", + "+---+\n", + "|ID1|\n", + "+---+\n", + "| 1|\n", + "| 2|\n", + "| 3|\n", + "| 4|\n", + "+---+\n", + "\n" + ] } ], "source": [ - "F_.when_mapping(\"hi\", {1: \"Ture\"})" + "list_ = [1, 2, 3, 4]\n", + "column_names = [\"ID1\"]\n", + "df = convert_1d_list_to_dataframe(spark, list_, column_names, axis=\"column\")\n", + "expected_data = [(1,), (2,), (3,), (4,)]\n", + "expected_df = spark.createDataFrame(expected_data, schema=column_names)\n", + "df.show()\n", + "expected_df.show()" ] }, { diff --git a/pysparky/decorator.py b/pysparky/decorator.py index 94d60d0..e7f81ec 100644 --- a/pysparky/decorator.py +++ b/pysparky/decorator.py @@ -1,101 +1,5 @@ import functools -from pyspark.sql import functions as F - - -def pyspark_column_or_name_enabler(*param_names): - """ - A decorator to enable PySpark functions to accept either column names (as strings) or Column objects. - - Parameters: - param_names (str): Names of the parameters that should be converted from strings to Column objects. - - Returns: - function: The decorated function with specified parameters converted to Column objects if they are strings. - - Example - @pyspark_column_or_name_enabler("column_or_name") - def your_function(column_or_name): - return column_or_name.startswith(bins) - """ - - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - # Convert args to a list to modify them - # args: This is the list of argument of the function. - # Get the parameter indices from the function signature - # func.__code__.co_varnames : Return the function parameter names as a tuple. - # param_names : the list of parameter from the decorator - - # Merging the args into Kwargs - args_name_used = func.__code__.co_varnames[: len(args)] - kw_from_args = dict(zip(args_name_used, args)) - kwargs = kw_from_args | kwargs - - # print(kwargs) - # transform all the input param - for param_name in param_names: - # if it is string, wrap it as string, else do nth - kwargs[param_name] = ( - F.col(kwargs[param_name]) - if isinstance(kwargs[param_name], str) - else kwargs[param_name] - ) - - return func(**kwargs) - - return wrapper - - return decorator - - -def column_name_or_column_names_enabler(*param_names): - """ - A decorator to enable PySpark functions to accept either column names (as strings) or Column objects. - - Parameters: - param_names (str): Names of the parameters that should be converted from strings to Column objects. - - Returns: - function: The decorated function with specified parameters converted to Column objects if they are strings. - - Example - @pyspark_column_or_name_enabler("column_or_name") - def your_function(column_or_name): - return column_or_name.startswith(bins) - """ - - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - # Convert args to a list to modify them - # args: This is the list of argument of the function. - # Get the parameter indices from the function signature - # func.__code__.co_varnames : Return the function parameter names as a tuple. - # param_names : the list of parameter from the decorator - - # Merging the args into Kwargs - args_name_used = func.__code__.co_varnames[: len(args)] - kw_from_args = dict(zip(args_name_used, args)) - kwargs = kw_from_args | kwargs - - # print(kwargs) - # transform all the input param - for param_name in param_names: - # if it is string, wrap it as string, else do nth - kwargs[param_name] = ( - [kwargs[param_name]] - if isinstance(kwargs[param_name], str) - else kwargs[param_name] - ) - - return func(**kwargs) - - return wrapper - - return decorator - def extension_enabler(cls): """ diff --git a/pysparky/enabler.py b/pysparky/enabler.py index bfcd85a..f0ef984 100644 --- a/pysparky/enabler.py +++ b/pysparky/enabler.py @@ -23,3 +23,27 @@ def column_or_name_enabler(*columns: ColumnOrName) -> tuple[Column, ...]: lambda column: F.col(column) if isinstance(column, str) else column, columns ) ) + + +def column_name_or_column_names_enabler( + column_names: str | list[str], +) -> list[str]: + """ + Ensures that the input is always returned as a list of column names. + + Parameters: + column_names (str | list[str]): A single column name (as a string) or a list of column names. + + Returns: + list[str]: A list containing the column names. + + Example: + >>> column_name_or_column_names_enabler("col1") + ['col1'] + >>> column_name_or_column_names_enabler(["col1", "col2"]) + ['col1', 'col2'] + """ + + column_names = [column_names] if isinstance(column_names, str) else column_names + + return column_names diff --git a/pysparky/functions/general.py b/pysparky/functions/general.py index 03d31b0..1cbc0a5 100644 --- a/pysparky/functions/general.py +++ b/pysparky/functions/general.py @@ -67,7 +67,6 @@ def chain(self, func, *args, **kwargs) -> Column: @decorator.extension_enabler(Column) -@decorator.pyspark_column_or_name_enabler("column_or_name") def startswiths( column_or_name: ColumnOrName, list_of_strings: list[str] ) -> pyspark.sql.Column: @@ -81,10 +80,11 @@ def startswiths( Returns: Column: A PySpark Column expression that evaluates to True if the column starts with any string in the list, otherwise False. """ + (column,) = column_or_name_enabler(column_or_name) return functools.reduce( operator.or_, - map(column_or_name.startswith, list_of_strings), + map(column.startswith, list_of_strings), F.lit(False), ).alias(f"startswiths_len{len(list_of_strings)}") diff --git a/pysparky/functions/math_.py b/pysparky/functions/math_.py index 58c8ba1..9192a31 100644 --- a/pysparky/functions/math_.py +++ b/pysparky/functions/math_.py @@ -7,7 +7,6 @@ @decorator.extension_enabler(Column) -@decorator.pyspark_column_or_name_enabler("lat1", "long1", "lat2", "long2") def haversine_distance( lat1: ColumnOrName, long1: ColumnOrName, diff --git a/pysparky/spark_ext.py b/pysparky/spark_ext.py index a6d6b1c..0356ef9 100644 --- a/pysparky/spark_ext.py +++ b/pysparky/spark_ext.py @@ -3,11 +3,11 @@ from pyspark.sql import Column, DataFrame, SparkSession from pyspark.sql import functions as F -from pysparky import decorator +from pysparky import decorator, enabler @decorator.extension_enabler(SparkSession) -def column_function(spark, column_obj: Column) -> DataFrame: +def column_function(spark: SparkSession, column_obj: Column) -> DataFrame: """ Evaluates a Column expression in the context of a single-row DataFrame. @@ -71,7 +71,10 @@ def column_function(spark, column_obj: Column) -> DataFrame: @decorator.extension_enabler(SparkSession) def convert_dict_to_dataframe( - spark, dict_: dict[str, Any], column_names: list[str], explode: bool = False + spark: SparkSession, + dict_: dict[str, Any], + column_names: list[str], + explode: bool = False, ) -> DataFrame: """ Transforms a dictionary with list values into a Spark DataFrame. @@ -111,8 +114,13 @@ def convert_dict_to_dataframe( @decorator.extension_enabler(SparkSession) -@decorator.column_name_or_column_names_enabler("column_names") -def convert_1d_list_to_dataframe(spark, list_, column_names, axis="column"): +# @decorator.column_name_or_column_names_enabler("column_names") +def convert_1d_list_to_dataframe( + spark: SparkSession, + list_: list[Any], + column_names: str | list[str], + axis: str = "column", +) -> DataFrame: """ Converts a 1-dimensional list into a PySpark DataFrame. @@ -156,20 +164,25 @@ def convert_1d_list_to_dataframe(spark, list_, column_names, axis="column"): | 1| 2| 3| 4| +---+---+---+---+ """ + column_names = enabler.column_name_or_column_names_enabler(column_names) + if axis not in ["column", "row"]: - raise AttributeError + raise AttributeError( + f"Invalid axis value: {axis}. Acceptable values are 'column' or 'row'." + ) if axis == "column": - tuple_list = ((x,) for x in list_) - output_sdf = spark.createDataFrame(tuple_list, schema=column_names) + tuple_list = ((x,) for x in list_) # type: ignore elif axis == "row": - tuple_list = (tuple(list_),) - output_sdf = spark.createDataFrame(tuple_list, schema=column_names) + tuple_list = (tuple(list_),) # type: ignore + + output_sdf = spark.createDataFrame(tuple_list, schema=column_names) + return output_sdf @decorator.extension_enabler(SparkSession) -def createDataFrame_from_dict(spark, dict_: dict) -> DataFrame: +def createDataFrame_from_dict(spark: SparkSession, dict_: dict) -> DataFrame: """ Creates a Spark DataFrame from a dictionary in a pandas-like style. diff --git a/tests/test_decorator.py b/tests/test_decorator.py index 2dcef4c..1668a62 100644 --- a/tests/test_decorator.py +++ b/tests/test_decorator.py @@ -3,34 +3,7 @@ from pyspark.sql import functions as F # Now import the decorators -from pysparky.decorator import (extension_enabler, - pyspark_column_or_name_enabler) - - -def test_pyspark_column_or_name_enabler(spark): - # It require spark session in the decorator - @pyspark_column_or_name_enabler("col1", "col2") - def test_function(col1, col2, col3): - return col1, col2, col3 - - # Test with string input - result = test_function("name1", "name2", "name3") - assert isinstance(result[0], Column) - assert isinstance(result[1], Column) - assert isinstance(result[2], str) - - # Test with Column input - col_input = F.col("col_name") - result = test_function(col_input, "name2", col_input) - assert result[0] is col_input - assert isinstance(result[1], Column) - assert result[2] is col_input - - # Test with keyword arguments - result = test_function(col1="name1", col2=Column("col2"), col3="name3") - assert isinstance(result[0], Column) - assert isinstance(result[1], Column) - assert isinstance(result[2], str) +from pysparky.decorator import extension_enabler def test_extension_enabler(): diff --git a/tests/test_enabler.py b/tests/test_enabler.py index 459ac49..64da21b 100644 --- a/tests/test_enabler.py +++ b/tests/test_enabler.py @@ -23,5 +23,23 @@ def test_column_or_name_enabler_with_1(spark): assert isinstance(col1, Column) +def test_column_name_or_column_names_enabler_with_string(): + result = enabler.column_name_or_column_names_enabler("col1") + expected = ["col1"] + assert result == expected + + +def test_column_name_or_column_names_enabler_with_list(): + result = enabler.column_name_or_column_names_enabler(["col1", "col2"]) + expected = ["col1", "col2"] + assert result == expected + + +def test_column_name_or_column_names_enabler_with_empty_list(): + result = enabler.column_name_or_column_names_enabler([]) + expected = [] + assert result == expected + + if __name__ == "__main__": pytest.main() diff --git a/tests/test_spark_ext.py b/tests/test_spark_ext.py index 38ab37c..57956e1 100644 --- a/tests/test_spark_ext.py +++ b/tests/test_spark_ext.py @@ -1,6 +1,7 @@ import pytest -from pysparky.spark_ext import createDataFrame_from_dict +from pysparky.spark_ext import (convert_1d_list_to_dataframe, + createDataFrame_from_dict) def test_createDataFrame_from_dict(spark): @@ -17,5 +18,30 @@ def test_createDataFrame_from_dict(spark): assert result_df.columns == expected_columns +def test_convert_1d_list_to_dataframe_column(spark): + list_ = [1, 2, 3, 4] + column_names = "ID1" + df = convert_1d_list_to_dataframe(spark, list_, column_names, axis="column") + expected_data = [(1,), (2,), (3,), (4,)] + expected_df = spark.createDataFrame(expected_data, schema=[column_names]) + assert df.collect() == expected_df.collect() + + +def test_convert_1d_list_to_dataframe_row(spark): + list_ = [1, 2, 3, 4] + column_names = ["ID1", "ID2", "ID3", "ID4"] + df = convert_1d_list_to_dataframe(spark, list_, column_names, axis="row") + expected_data = [(1, 2, 3, 4)] + expected_df = spark.createDataFrame(expected_data, schema=column_names) + assert df.collect() == expected_df.collect() + + +def test_convert_1d_list_to_dataframe_invalid_axis(spark): + list_ = [1, 2, 3, 4] + column_names = ["numbers"] + with pytest.raises(AttributeError): + convert_1d_list_to_dataframe(spark, list_, column_names, axis="invalid") + + if __name__ == "__main__": pytest.main([__file__])