pylegend 0.10.0__tar.gz → 0.11.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pylegend-0.10.0 → pylegend-0.11.0}/PKG-INFO +1 -1
- pylegend-0.11.0/pylegend/core/tds/pandas_api/frames/functions/aggregate_function.py +441 -0
- pylegend-0.11.0/pylegend/core/tds/pandas_api/frames/functions/merge.py +513 -0
- pylegend-0.11.0/pylegend/core/tds/pandas_api/frames/functions/rename.py +214 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/pandas_api/frames/pandas_api_base_tds_frame.py +275 -0
- pylegend-0.11.0/pylegend/core/tds/pandas_api/frames/pandas_api_groupby_tds_frame.py +325 -0
- pylegend-0.11.0/pylegend/core/tds/pandas_api/frames/pandas_api_tds_frame.py +267 -0
- pylegend-0.11.0/pylegend/extensions/tds/abstract/csv_tds_frame.py +95 -0
- pylegend-0.11.0/pylegend/extensions/tds/legendql_api/frames/legendql_api_csv_input_frame.py +36 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pyproject.toml +1 -1
- pylegend-0.10.0/pylegend/core/tds/pandas_api/frames/functions/aggregate_function.py +0 -316
- pylegend-0.10.0/pylegend/core/tds/pandas_api/frames/pandas_api_tds_frame.py +0 -130
- {pylegend-0.10.0 → pylegend-0.11.0}/LICENSE +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/LICENSE.spdx +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/NOTICE +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/README.md +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/_typing.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/database/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/database/sql_to_string/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/database/sql_to_string/config.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/database/sql_to_string/db_extension.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/database/sql_to_string/generator.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/legacy_api/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/legacy_api/aggregate_specification.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/legacy_api/legacy_api_tds_row.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/legendql_api/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/legendql_api/legendql_api_custom_expressions.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/legendql_api/legendql_api_tds_row.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/pandas_api/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/pandas_api/pandas_api_aggregate_specification.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/pandas_api/pandas_api_custom_expressions.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/pandas_api/pandas_api_series.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/pandas_api/pandas_api_tds_row.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/column_expressions.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/expression.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/functions.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/helpers.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/literal_expressions.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/operations/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/operations/binary_expression.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/operations/boolean_operation_expressions.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/operations/collection_operation_expressions.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/operations/date_operation_expressions.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/operations/float_operation_expressions.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/operations/integer_operation_expressions.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/operations/nary_expression.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/operations/nullary_expression.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/operations/number_operation_expressions.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/operations/primitive_operation_expressions.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/operations/string_operation_expressions.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/operations/unary_expression.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/pct_helpers.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/primitive_collection.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/primitives/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/primitives/boolean.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/primitives/date.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/primitives/datetime.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/primitives/float.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/primitives/integer.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/primitives/number.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/primitives/primitive.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/primitives/strictdate.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/primitives/string.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/language/shared/tds_row.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/project_cooridnates.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/request/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/request/auth.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/request/legend_client.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/request/response_reader.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/request/service_client.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/sql/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/sql/metamodel.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/sql/metamodel_extension.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/abstract/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/abstract/frames/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/abstract/frames/applied_function_tds_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/abstract/frames/base_tds_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/abstract/frames/input_tds_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/abstract/function_helpers.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/functions/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_concatenate_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_distinct_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_drop_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_extend_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_filter_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_group_by_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_head_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_join_by_columns_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_join_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_rename_columns_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_restrict_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_slice_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_sort_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/legacy_api_applied_function_tds_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/legacy_api_base_tds_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/legacy_api_input_tds_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legacy_api/frames/legacy_api_tds_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/functions/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_asofjoin_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_concatenate_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_distinct_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_drop_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_extend_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_filter_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_function_helpers.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_groupby_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_head_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_join_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_project_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_rename_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_select_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_slice_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_sort_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_window_extend_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/legendql_api_applied_function_tds_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/legendql_api_base_tds_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/legendql_api_input_tds_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/legendql_api/frames/legendql_api_tds_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/pandas_api/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/pandas_api/frames/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/pandas_api/frames/functions/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/pandas_api/frames/functions/assign_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/pandas_api/frames/functions/drop.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/pandas_api/frames/functions/filter.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/pandas_api/frames/functions/filtering.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/pandas_api/frames/functions/sort_values_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/pandas_api/frames/functions/truncate_function.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/pandas_api/frames/pandas_api_applied_function_tds_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/pandas_api/frames/pandas_api_input_tds_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/result_handler/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/result_handler/result_handler.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/result_handler/to_csv_file_result_handler.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/result_handler/to_json_file_result_handler.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/result_handler/to_string_result_handler.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/sql_query_helpers.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/tds_column.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/core/tds/tds_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/database/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/database/vendors/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/database/vendors/postgres/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/database/vendors/postgres/postgres_sql_to_string.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/abstract/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/abstract/legend_function_input_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/abstract/legend_service_input_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/abstract/table_spec_input_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/legacy_api/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/legacy_api/frames/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/legacy_api/frames/legacy_api_legend_function_input_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/legacy_api/frames/legacy_api_legend_service_input_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/legacy_api/frames/legacy_api_table_spec_input_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/legendql_api/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/legendql_api/frames/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/legendql_api/frames/legendql_api_legend_function_input_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/legendql_api/frames/legendql_api_legend_service_input_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/legendql_api/frames/legendql_api_table_spec_input_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/pandas_api/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/pandas_api/frames/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/pandas_api/frames/pandas_api_legend_function_input_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/pandas_api/frames/pandas_api_legend_service_input_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/pandas_api/frames/pandas_api_table_spec_input_frame.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/result_handler/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/extensions/tds/result_handler/to_pandas_df_result_handler.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/legacy_api_tds_client.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/legendql_api_tds_client.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/utils/__init__.py +0 -0
- {pylegend-0.10.0 → pylegend-0.11.0}/pylegend/utils/class_utils.py +0 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
# Copyright 2025 Goldman Sachs
|
|
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
|
+
import numpy as np
|
|
16
|
+
import collections.abc
|
|
17
|
+
from pylegend._typing import (
|
|
18
|
+
PyLegendCallable,
|
|
19
|
+
PyLegendSequence,
|
|
20
|
+
PyLegendTuple,
|
|
21
|
+
PyLegendUnion,
|
|
22
|
+
PyLegendList,
|
|
23
|
+
PyLegendMapping,
|
|
24
|
+
)
|
|
25
|
+
from pylegend.core.language.pandas_api.pandas_api_aggregate_specification import (
|
|
26
|
+
PyLegendAggFunc,
|
|
27
|
+
PyLegendAggInput,
|
|
28
|
+
PyLegendAggList,
|
|
29
|
+
)
|
|
30
|
+
from pylegend.core.language.pandas_api.pandas_api_tds_row import PandasApiTdsRow
|
|
31
|
+
from pylegend.core.language.shared.helpers import escape_column_name, generate_pure_lambda
|
|
32
|
+
from pylegend.core.language.shared.literal_expressions import convert_literal_to_literal_expression
|
|
33
|
+
from pylegend.core.language.shared.primitive_collection import PyLegendPrimitiveCollection, create_primitive_collection
|
|
34
|
+
from pylegend.core.language.shared.primitives.boolean import PyLegendBoolean
|
|
35
|
+
from pylegend.core.language.shared.primitives.date import PyLegendDate
|
|
36
|
+
from pylegend.core.language.shared.primitives.datetime import PyLegendDateTime
|
|
37
|
+
from pylegend.core.language.shared.primitives.float import PyLegendFloat
|
|
38
|
+
from pylegend.core.language.shared.primitives.integer import PyLegendInteger
|
|
39
|
+
from pylegend.core.language.shared.primitives.number import PyLegendNumber
|
|
40
|
+
from pylegend.core.language.shared.primitives.primitive import PyLegendPrimitive, PyLegendPrimitiveOrPythonPrimitive
|
|
41
|
+
from pylegend.core.language.shared.primitives.strictdate import PyLegendStrictDate
|
|
42
|
+
from pylegend.core.language.shared.primitives.string import PyLegendString
|
|
43
|
+
from pylegend.core.sql.metamodel import (
|
|
44
|
+
QuerySpecification,
|
|
45
|
+
SelectItem,
|
|
46
|
+
SingleColumn,
|
|
47
|
+
)
|
|
48
|
+
from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import PandasApiAppliedFunction
|
|
49
|
+
from pylegend.core.tds.pandas_api.frames.pandas_api_base_tds_frame import PandasApiBaseTdsFrame
|
|
50
|
+
from pylegend.core.tds.pandas_api.frames.pandas_api_groupby_tds_frame import PandasApiGroupbyTdsFrame
|
|
51
|
+
from pylegend.core.tds.sql_query_helpers import copy_query, create_sub_query
|
|
52
|
+
from pylegend.core.tds.tds_column import PrimitiveTdsColumn, TdsColumn
|
|
53
|
+
from pylegend.core.tds.tds_frame import FrameToPureConfig, FrameToSqlConfig
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class AggregateFunction(PandasApiAppliedFunction):
|
|
57
|
+
__base_frame: PyLegendUnion[PandasApiBaseTdsFrame, PandasApiGroupbyTdsFrame]
|
|
58
|
+
__func: PyLegendAggInput
|
|
59
|
+
__axis: PyLegendUnion[int, str]
|
|
60
|
+
__args: PyLegendSequence[PyLegendPrimitiveOrPythonPrimitive]
|
|
61
|
+
__kwargs: PyLegendMapping[str, PyLegendPrimitiveOrPythonPrimitive]
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def name(cls) -> str:
|
|
65
|
+
return "aggregate" # pragma: no cover
|
|
66
|
+
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
base_frame: PyLegendUnion[PandasApiBaseTdsFrame, PandasApiGroupbyTdsFrame],
|
|
70
|
+
func: PyLegendAggInput,
|
|
71
|
+
axis: PyLegendUnion[int, str],
|
|
72
|
+
*args: PyLegendPrimitiveOrPythonPrimitive,
|
|
73
|
+
**kwargs: PyLegendPrimitiveOrPythonPrimitive,
|
|
74
|
+
) -> None:
|
|
75
|
+
self.__base_frame = base_frame
|
|
76
|
+
self.__func = func
|
|
77
|
+
self.__axis = axis
|
|
78
|
+
self.__args = args
|
|
79
|
+
self.__kwargs = kwargs
|
|
80
|
+
|
|
81
|
+
def to_sql(self, config: FrameToSqlConfig) -> QuerySpecification:
|
|
82
|
+
db_extension = config.sql_to_string_generator().get_db_extension()
|
|
83
|
+
|
|
84
|
+
base_query: QuerySpecification = self.base_frame().to_sql_query_object(config)
|
|
85
|
+
|
|
86
|
+
should_create_sub_query = (
|
|
87
|
+
len(base_query.groupBy) > 0
|
|
88
|
+
or base_query.select.distinct
|
|
89
|
+
or base_query.offset is not None
|
|
90
|
+
or base_query.limit is not None
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
new_query: QuerySpecification
|
|
94
|
+
if should_create_sub_query:
|
|
95
|
+
new_query = create_sub_query(base_query, config, "root")
|
|
96
|
+
else:
|
|
97
|
+
new_query = copy_query(base_query)
|
|
98
|
+
|
|
99
|
+
new_select_items: PyLegendList[SelectItem] = []
|
|
100
|
+
|
|
101
|
+
if isinstance(self.__base_frame, PandasApiGroupbyTdsFrame):
|
|
102
|
+
columns_to_retain: PyLegendList[str] = [
|
|
103
|
+
db_extension.quote_identifier(x) for x in self.__base_frame.grouping_column_name_list()
|
|
104
|
+
]
|
|
105
|
+
new_cols_with_index: PyLegendList[PyLegendTuple[int, "SelectItem"]] = []
|
|
106
|
+
for col in new_query.select.selectItems:
|
|
107
|
+
if not isinstance(col, SingleColumn):
|
|
108
|
+
raise ValueError(
|
|
109
|
+
"Group By operation not supported for queries " "with columns other than SingleColumn"
|
|
110
|
+
) # pragma: no cover
|
|
111
|
+
if col.alias is None:
|
|
112
|
+
raise ValueError(
|
|
113
|
+
"Group By operation not supported for queries " "with SingleColumns with missing alias"
|
|
114
|
+
) # pragma: no cover
|
|
115
|
+
if col.alias in columns_to_retain:
|
|
116
|
+
new_cols_with_index.append((columns_to_retain.index(col.alias), col))
|
|
117
|
+
|
|
118
|
+
new_select_items = [y[1] for y in sorted(new_cols_with_index, key=lambda x: x[0])]
|
|
119
|
+
|
|
120
|
+
for agg in self.__aggregates_list:
|
|
121
|
+
agg_sql_expr = agg[2].to_sql_expression({"r": new_query}, config)
|
|
122
|
+
|
|
123
|
+
new_select_items.append(SingleColumn(alias=db_extension.quote_identifier(agg[0]), expression=agg_sql_expr))
|
|
124
|
+
|
|
125
|
+
new_query.select.selectItems = new_select_items
|
|
126
|
+
|
|
127
|
+
if isinstance(self.__base_frame, PandasApiGroupbyTdsFrame):
|
|
128
|
+
tds_row = PandasApiTdsRow.from_tds_frame("r", self.base_frame())
|
|
129
|
+
new_query.groupBy = [
|
|
130
|
+
(lambda x: x[c])(tds_row).to_sql_expression({"r": new_query}, config)
|
|
131
|
+
for c in self.__base_frame.grouping_column_name_list()
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
return new_query
|
|
135
|
+
|
|
136
|
+
def to_pure(self, config: FrameToPureConfig) -> str:
|
|
137
|
+
agg_strings = []
|
|
138
|
+
for agg in self.__aggregates_list:
|
|
139
|
+
map_expr_string = (
|
|
140
|
+
agg[1].to_pure_expression(config)
|
|
141
|
+
if isinstance(agg[1], PyLegendPrimitive)
|
|
142
|
+
else convert_literal_to_literal_expression(agg[1]).to_pure_expression(config)
|
|
143
|
+
)
|
|
144
|
+
agg_expr_string = agg[2].to_pure_expression(config).replace(map_expr_string, "$c")
|
|
145
|
+
agg_strings.append(
|
|
146
|
+
f"{escape_column_name(agg[0])}:{generate_pure_lambda('r', map_expr_string)}:"
|
|
147
|
+
f"{generate_pure_lambda('c', agg_expr_string)}"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
if isinstance(self.__base_frame, PandasApiGroupbyTdsFrame):
|
|
151
|
+
group_strings = []
|
|
152
|
+
for col_name in self.__base_frame.grouping_column_name_list():
|
|
153
|
+
group_strings.append(escape_column_name(col_name))
|
|
154
|
+
|
|
155
|
+
pure_expression = (
|
|
156
|
+
f"{self.base_frame().to_pure(config)}{config.separator(1)}" + f"->groupBy({config.separator(2)}"
|
|
157
|
+
f"~[{', '.join(group_strings)}],{config.separator(2, True)}"
|
|
158
|
+
f"~[{', '.join(agg_strings)}]{config.separator(1)}"
|
|
159
|
+
f")"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
return pure_expression
|
|
163
|
+
else:
|
|
164
|
+
return (
|
|
165
|
+
f"{self.__base_frame.to_pure(config)}{config.separator(1)}"
|
|
166
|
+
f"->aggregate({config.separator(2)}"
|
|
167
|
+
f"~[{', '.join(agg_strings)}]{config.separator(1)}"
|
|
168
|
+
f")"
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
def base_frame(self) -> PandasApiBaseTdsFrame:
|
|
172
|
+
if isinstance(self.__base_frame, PandasApiGroupbyTdsFrame):
|
|
173
|
+
return self.__base_frame.base_frame()
|
|
174
|
+
else:
|
|
175
|
+
return self.__base_frame
|
|
176
|
+
|
|
177
|
+
def tds_frame_parameters(self) -> PyLegendList["PandasApiBaseTdsFrame"]:
|
|
178
|
+
return []
|
|
179
|
+
|
|
180
|
+
def calculate_columns(self) -> PyLegendSequence["TdsColumn"]:
|
|
181
|
+
new_columns = []
|
|
182
|
+
|
|
183
|
+
if isinstance(self.__base_frame, PandasApiGroupbyTdsFrame):
|
|
184
|
+
base_cols_map = {c.get_name(): c for c in self.base_frame().columns()}
|
|
185
|
+
for group_col_name in self.__base_frame.grouping_column_name_list():
|
|
186
|
+
if group_col_name in base_cols_map:
|
|
187
|
+
new_columns.append(base_cols_map[group_col_name].copy())
|
|
188
|
+
|
|
189
|
+
for alias, _, agg_expr in self.__aggregates_list:
|
|
190
|
+
new_columns.append(self.__infer_column_from_expression(alias, agg_expr))
|
|
191
|
+
|
|
192
|
+
return new_columns
|
|
193
|
+
|
|
194
|
+
def __infer_column_from_expression(self, name: str, expr: PyLegendPrimitive) -> TdsColumn:
|
|
195
|
+
if isinstance(expr, PyLegendInteger):
|
|
196
|
+
return PrimitiveTdsColumn.integer_column(name)
|
|
197
|
+
elif isinstance(expr, PyLegendFloat):
|
|
198
|
+
return PrimitiveTdsColumn.float_column(name)
|
|
199
|
+
elif isinstance(expr, PyLegendNumber):
|
|
200
|
+
return PrimitiveTdsColumn.number_column(name)
|
|
201
|
+
elif isinstance(expr, PyLegendString):
|
|
202
|
+
return PrimitiveTdsColumn.string_column(name)
|
|
203
|
+
elif isinstance(expr, PyLegendBoolean):
|
|
204
|
+
return PrimitiveTdsColumn.boolean_column(name) # pragma: no cover
|
|
205
|
+
elif isinstance(expr, PyLegendDate):
|
|
206
|
+
return PrimitiveTdsColumn.date_column(name)
|
|
207
|
+
elif isinstance(expr, PyLegendDateTime):
|
|
208
|
+
return PrimitiveTdsColumn.datetime_column(name)
|
|
209
|
+
elif isinstance(expr, PyLegendStrictDate):
|
|
210
|
+
return PrimitiveTdsColumn.strictdate_column(name)
|
|
211
|
+
else:
|
|
212
|
+
raise TypeError(f"Could not infer TdsColumn type for aggregation result type: {type(expr)}") # pragma: no cover
|
|
213
|
+
|
|
214
|
+
def validate(self) -> bool:
|
|
215
|
+
if self.__axis not in [0, "index"]:
|
|
216
|
+
raise NotImplementedError(
|
|
217
|
+
f"The 'axis' parameter of the aggregate function must be 0 or 'index', but got: {self.__axis}"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
if len(self.__args) > 0 or len(self.__kwargs) > 0:
|
|
221
|
+
raise NotImplementedError(
|
|
222
|
+
"AggregateFunction currently does not support additional positional "
|
|
223
|
+
"or keyword arguments. Please remove extra *args/**kwargs."
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
self.__aggregates_list: PyLegendList[PyLegendTuple[str, PyLegendPrimitiveOrPythonPrimitive, PyLegendPrimitive]] = []
|
|
227
|
+
|
|
228
|
+
normalized_func: dict[str, PyLegendUnion[PyLegendAggFunc, PyLegendAggList]] = (
|
|
229
|
+
self.__normalize_input_func_to_standard_dict(self.__func)
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
tds_row = PandasApiTdsRow.from_tds_frame("r", self.base_frame())
|
|
233
|
+
|
|
234
|
+
for column_name, agg_input in normalized_func.items():
|
|
235
|
+
mapper_function: PyLegendCallable[[PandasApiTdsRow], PyLegendPrimitiveOrPythonPrimitive] = eval(
|
|
236
|
+
f'lambda r: r["{column_name}"]'
|
|
237
|
+
)
|
|
238
|
+
map_result: PyLegendPrimitiveOrPythonPrimitive = mapper_function(tds_row)
|
|
239
|
+
collection: PyLegendPrimitiveCollection = create_primitive_collection(map_result)
|
|
240
|
+
|
|
241
|
+
if isinstance(agg_input, list):
|
|
242
|
+
lambda_counter = 0
|
|
243
|
+
for func in agg_input:
|
|
244
|
+
is_anonymous_lambda = False
|
|
245
|
+
if not isinstance(func, str):
|
|
246
|
+
if getattr(func, "__name__", "<lambda>") == "<lambda>":
|
|
247
|
+
is_anonymous_lambda = True
|
|
248
|
+
|
|
249
|
+
if is_anonymous_lambda:
|
|
250
|
+
lambda_counter += 1
|
|
251
|
+
|
|
252
|
+
normalized_agg_func = self.__normalize_agg_func_to_lambda_function(func)
|
|
253
|
+
agg_result = normalized_agg_func(collection)
|
|
254
|
+
|
|
255
|
+
alias = self._generate_column_alias(column_name, func, lambda_counter)
|
|
256
|
+
self.__aggregates_list.append((alias, map_result, agg_result))
|
|
257
|
+
|
|
258
|
+
else:
|
|
259
|
+
normalized_agg_func = self.__normalize_agg_func_to_lambda_function(agg_input)
|
|
260
|
+
agg_result = normalized_agg_func(collection)
|
|
261
|
+
|
|
262
|
+
self.__aggregates_list.append((column_name, map_result, agg_result))
|
|
263
|
+
|
|
264
|
+
return True
|
|
265
|
+
|
|
266
|
+
def __normalize_input_func_to_standard_dict(
|
|
267
|
+
self, func_input: PyLegendAggInput
|
|
268
|
+
) -> dict[str, PyLegendUnion[PyLegendAggFunc, PyLegendAggList]]:
|
|
269
|
+
|
|
270
|
+
validation_columns: PyLegendList[str]
|
|
271
|
+
default_broadcast_columns: PyLegendList[str]
|
|
272
|
+
group_cols: set[str] = set()
|
|
273
|
+
|
|
274
|
+
all_cols = [col.get_name() for col in self.base_frame().columns()]
|
|
275
|
+
|
|
276
|
+
if isinstance(self.__base_frame, PandasApiGroupbyTdsFrame):
|
|
277
|
+
group_cols = set(self.__base_frame.grouping_column_name_list())
|
|
278
|
+
|
|
279
|
+
selected_cols = self.__base_frame.selected_columns()
|
|
280
|
+
|
|
281
|
+
if selected_cols is not None:
|
|
282
|
+
validation_columns = selected_cols
|
|
283
|
+
default_broadcast_columns = selected_cols
|
|
284
|
+
else:
|
|
285
|
+
validation_columns = all_cols
|
|
286
|
+
default_broadcast_columns = [c for c in all_cols if c not in group_cols]
|
|
287
|
+
else:
|
|
288
|
+
validation_columns = all_cols
|
|
289
|
+
default_broadcast_columns = all_cols
|
|
290
|
+
|
|
291
|
+
if isinstance(func_input, collections.abc.Mapping):
|
|
292
|
+
normalized: dict[str, PyLegendUnion[PyLegendAggFunc, PyLegendAggList]] = {}
|
|
293
|
+
|
|
294
|
+
for key, value in func_input.items():
|
|
295
|
+
if not isinstance(key, str):
|
|
296
|
+
raise TypeError(
|
|
297
|
+
f"Invalid `func` argument for the aggregate function.\n"
|
|
298
|
+
f"When a dictionary is provided, all keys must be strings.\n"
|
|
299
|
+
f"But got key: {key!r} (type: {type(key).__name__})\n"
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
if key not in validation_columns:
|
|
303
|
+
raise ValueError(
|
|
304
|
+
f"Invalid `func` argument for the aggregate function.\n"
|
|
305
|
+
f"When a dictionary is provided, all keys must be column names.\n"
|
|
306
|
+
f"Available columns are: {sorted(validation_columns)}\n"
|
|
307
|
+
f"But got key: {key!r} (type: {type(key).__name__})\n"
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
if isinstance(value, collections.abc.Sequence) and not isinstance(value, str):
|
|
311
|
+
for i, f in enumerate(value):
|
|
312
|
+
if not (callable(f) or isinstance(f, str) or isinstance(f, np.ufunc)):
|
|
313
|
+
raise TypeError(
|
|
314
|
+
f"Invalid `func` argument for the aggregate function.\n"
|
|
315
|
+
f"When a list is provided for a column, all elements must be callable, str, or np.ufunc.\n"
|
|
316
|
+
f"But got element at index {i}: {f!r} (type: {type(f).__name__})\n"
|
|
317
|
+
)
|
|
318
|
+
normalized[key] = value
|
|
319
|
+
|
|
320
|
+
else:
|
|
321
|
+
if not (callable(value) or isinstance(value, str) or isinstance(value, np.ufunc)):
|
|
322
|
+
raise TypeError(
|
|
323
|
+
f"Invalid `func` argument for the aggregate function.\n"
|
|
324
|
+
f"When a dictionary is provided, the value must be a callable, str, or np.ufunc "
|
|
325
|
+
f"(or a list containing these).\n"
|
|
326
|
+
f"But got value for key '{key}': {value} (type: {type(value).__name__})\n"
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
if key in group_cols:
|
|
330
|
+
normalized[key] = [value]
|
|
331
|
+
else:
|
|
332
|
+
normalized[key] = value
|
|
333
|
+
|
|
334
|
+
return normalized
|
|
335
|
+
|
|
336
|
+
elif isinstance(func_input, collections.abc.Sequence) and not isinstance(func_input, str):
|
|
337
|
+
for i, f in enumerate(func_input):
|
|
338
|
+
if not (callable(f) or isinstance(f, str) or isinstance(f, np.ufunc)):
|
|
339
|
+
raise TypeError(
|
|
340
|
+
f"Invalid `func` argument for the aggregate function.\n"
|
|
341
|
+
f"When a list is provided as the main argument, all elements must be callable, str, or np.ufunc.\n"
|
|
342
|
+
f"But got element at index {i}: {f!r} (type: {type(f).__name__})\n"
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
return {col: func_input for col in default_broadcast_columns}
|
|
346
|
+
|
|
347
|
+
elif callable(func_input) or isinstance(func_input, str) or isinstance(func_input, np.ufunc):
|
|
348
|
+
return {col: func_input for col in default_broadcast_columns}
|
|
349
|
+
|
|
350
|
+
else:
|
|
351
|
+
raise TypeError(
|
|
352
|
+
"Invalid `func` argument for aggregate function. "
|
|
353
|
+
"Expected a callable, str, np.ufunc, a list containing exactly one of these, "
|
|
354
|
+
"or a mapping[str -> callable/str/ufunc/a list containing exactly one of these]. "
|
|
355
|
+
f"But got: {func_input!r} (type: {type(func_input).__name__})"
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
def __normalize_agg_func_to_lambda_function(
|
|
359
|
+
self, func: PyLegendAggFunc
|
|
360
|
+
) -> PyLegendCallable[[PyLegendPrimitiveCollection], PyLegendPrimitive]:
|
|
361
|
+
|
|
362
|
+
PYTHON_FUNCTION_TO_LEGEND_FUNCTION_MAPPING: PyLegendMapping[str, PyLegendList[str]] = {
|
|
363
|
+
"average": ["mean", "average", "nanmean"],
|
|
364
|
+
"sum": ["sum", "nansum"],
|
|
365
|
+
"min": ["min", "amin", "minimum", "nanmin"],
|
|
366
|
+
"max": ["max", "amax", "maximum", "nanmax"],
|
|
367
|
+
"std_dev_sample": ["std", "std_dev", "nanstd"],
|
|
368
|
+
"variance_sample": ["var", "variance", "nanvar"],
|
|
369
|
+
"count": ["count", "size", "len", "length"],
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
FLATTENED_FUNCTION_MAPPING: dict[str, str] = {}
|
|
373
|
+
for target_method, source_list in PYTHON_FUNCTION_TO_LEGEND_FUNCTION_MAPPING.items():
|
|
374
|
+
for alias in source_list:
|
|
375
|
+
FLATTENED_FUNCTION_MAPPING[alias] = target_method
|
|
376
|
+
|
|
377
|
+
lambda_source: str
|
|
378
|
+
final_lambda: PyLegendCallable[[PyLegendPrimitiveCollection], PyLegendPrimitive]
|
|
379
|
+
|
|
380
|
+
if isinstance(func, str):
|
|
381
|
+
func_lower = func.lower()
|
|
382
|
+
if func_lower in FLATTENED_FUNCTION_MAPPING:
|
|
383
|
+
internal_method_name = FLATTENED_FUNCTION_MAPPING[func_lower]
|
|
384
|
+
else:
|
|
385
|
+
raise NotImplementedError(
|
|
386
|
+
f"Invalid `func` argument for the aggregate function.\n"
|
|
387
|
+
f"The string {func!r} does not correspond to any supported aggregation.\n"
|
|
388
|
+
f"Available string functions are: {sorted(FLATTENED_FUNCTION_MAPPING.keys())}"
|
|
389
|
+
) # pragma: no cover
|
|
390
|
+
lambda_source = self._generate_lambda_source(internal_method_name)
|
|
391
|
+
final_lambda = eval(lambda_source)
|
|
392
|
+
return final_lambda
|
|
393
|
+
|
|
394
|
+
elif isinstance(func, np.ufunc):
|
|
395
|
+
func_name = func.__name__
|
|
396
|
+
if func_name in FLATTENED_FUNCTION_MAPPING:
|
|
397
|
+
internal_method_name = FLATTENED_FUNCTION_MAPPING[func_name]
|
|
398
|
+
else:
|
|
399
|
+
raise NotImplementedError(
|
|
400
|
+
f"Invalid `func` argument for the aggregate function.\n"
|
|
401
|
+
f"The NumPy function {func_name!r} is not supported.\n"
|
|
402
|
+
f"Supported aggregate functions are: {sorted(FLATTENED_FUNCTION_MAPPING.keys())}"
|
|
403
|
+
) # pragma: no cover
|
|
404
|
+
lambda_source = self._generate_lambda_source(internal_method_name)
|
|
405
|
+
final_lambda = eval(lambda_source)
|
|
406
|
+
return final_lambda
|
|
407
|
+
|
|
408
|
+
else:
|
|
409
|
+
func_name = getattr(func, "__name__", "").lower()
|
|
410
|
+
if func_name in FLATTENED_FUNCTION_MAPPING and func_name != "<lambda>":
|
|
411
|
+
internal_method_name = FLATTENED_FUNCTION_MAPPING[func_name]
|
|
412
|
+
lambda_source = self._generate_lambda_source(internal_method_name)
|
|
413
|
+
final_lambda = eval(lambda_source)
|
|
414
|
+
return final_lambda
|
|
415
|
+
else:
|
|
416
|
+
|
|
417
|
+
def validation_wrapper(x: PyLegendPrimitiveCollection) -> PyLegendPrimitive:
|
|
418
|
+
result = func(x)
|
|
419
|
+
if not isinstance(result, PyLegendPrimitive):
|
|
420
|
+
raise TypeError(
|
|
421
|
+
f"Custom aggregation function must return a PyLegendPrimitive (Expression).\n"
|
|
422
|
+
f"But got type: {type(result).__name__}\n"
|
|
423
|
+
f"Value: {result!r}"
|
|
424
|
+
) # pragma: no cover
|
|
425
|
+
return result
|
|
426
|
+
|
|
427
|
+
return validation_wrapper
|
|
428
|
+
|
|
429
|
+
def _generate_lambda_source(self, internal_method_name: str) -> str:
|
|
430
|
+
return f"lambda x: x.{internal_method_name}()"
|
|
431
|
+
|
|
432
|
+
def _generate_column_alias(self, col_name: str, func: PyLegendAggFunc, lambda_counter: int) -> str:
|
|
433
|
+
if isinstance(func, str):
|
|
434
|
+
return f"{func}({col_name})"
|
|
435
|
+
|
|
436
|
+
func_name = getattr(func, "__name__", "<lambda>")
|
|
437
|
+
|
|
438
|
+
if func_name != "<lambda>":
|
|
439
|
+
return f"{func_name}({col_name})"
|
|
440
|
+
else:
|
|
441
|
+
return f"lambda_{lambda_counter}({col_name})"
|