From f918096cb67bf39afb203fda68fb9aed95f2fe18 Mon Sep 17 00:00:00 2001 From: HowToSD Date: Fri, 7 Mar 2025 12:03:58 -0800 Subject: [PATCH] Added Pandas logical operations nodes. --- __init__.py | 27 ++++++++ docs/reference/node_reference.md | 12 ++++ docs/reference/pandas_create_from_dict.md | 2 + .../pandas_create_from_dict_index_list.md | 21 +++++++ docs/reference/pandas_eq.md | 18 ++++++ docs/reference/pandas_ge.md | 18 ++++++ docs/reference/pandas_gt.md | 18 ++++++ docs/reference/pandas_le.md | 18 ++++++ docs/reference/pandas_lt.md | 18 ++++++ docs/reference/pandas_ne.md | 18 ++++++ docs/reference/py_string_to_dict.md | 17 +++++ docs/reference/py_string_to_list.md | 17 +++++ modules/cda/cda_json_create.py | 2 +- modules/cda/py_string_to_dict.py | 41 ++++++++++++ modules/cda/py_string_to_list.py | 41 ++++++++++++ .../pandas_wrapper/pandas_create_from_dict.py | 2 + .../pandas_create_from_dict_index_list.py | 59 ++++++++++++++++++ modules/pandas_wrapper/pandas_eq.py | 43 +++++++++++++ modules/pandas_wrapper/pandas_ge.py | 43 +++++++++++++ modules/pandas_wrapper/pandas_gt.py | 43 +++++++++++++ modules/pandas_wrapper/pandas_le.py | 43 +++++++++++++ modules/pandas_wrapper/pandas_lt.py | 43 +++++++++++++ modules/pandas_wrapper/pandas_ne.py | 43 +++++++++++++ pyproject.toml | 2 +- ...pandas_create_from_dict_index_list_test.py | 62 +++++++++++++++++++ tests/pandas_wrapper/pandas_eq_test.py | 58 +++++++++++++++++ tests/pandas_wrapper/pandas_ge_test.py | 58 +++++++++++++++++ tests/pandas_wrapper/pandas_gt_test.py | 58 +++++++++++++++++ tests/pandas_wrapper/pandas_le_test.py | 58 +++++++++++++++++ tests/pandas_wrapper/pandas_lt_test.py | 58 +++++++++++++++++ tests/pandas_wrapper/pandas_ne_test.py | 58 +++++++++++++++++ 31 files changed, 1017 insertions(+), 2 deletions(-) create mode 100644 docs/reference/pandas_create_from_dict_index_list.md create mode 100644 docs/reference/pandas_eq.md create mode 100644 docs/reference/pandas_ge.md create mode 100644 docs/reference/pandas_gt.md create mode 100644 docs/reference/pandas_le.md create mode 100644 docs/reference/pandas_lt.md create mode 100644 docs/reference/pandas_ne.md create mode 100644 docs/reference/py_string_to_dict.md create mode 100644 docs/reference/py_string_to_list.md create mode 100644 modules/cda/py_string_to_dict.py create mode 100644 modules/cda/py_string_to_list.py create mode 100644 modules/pandas_wrapper/pandas_create_from_dict_index_list.py create mode 100644 modules/pandas_wrapper/pandas_eq.py create mode 100644 modules/pandas_wrapper/pandas_ge.py create mode 100644 modules/pandas_wrapper/pandas_gt.py create mode 100644 modules/pandas_wrapper/pandas_le.py create mode 100644 modules/pandas_wrapper/pandas_lt.py create mode 100644 modules/pandas_wrapper/pandas_ne.py create mode 100644 tests/pandas_wrapper/pandas_create_from_dict_index_list_test.py create mode 100644 tests/pandas_wrapper/pandas_eq_test.py create mode 100644 tests/pandas_wrapper/pandas_ge_test.py create mode 100644 tests/pandas_wrapper/pandas_gt_test.py create mode 100644 tests/pandas_wrapper/pandas_le_test.py create mode 100644 tests/pandas_wrapper/pandas_lt_test.py create mode 100644 tests/pandas_wrapper/pandas_ne_test.py diff --git a/__init__.py b/__init__.py index 6276f3c..71d4d8c 100644 --- a/__init__.py +++ b/__init__.py @@ -14,8 +14,10 @@ from .modules.cda.py_kv_float_create import PyKvFloatCreate from .modules.cda.py_kv_int_create import PyKvIntCreate from .modules.cda.py_kv_string_create import PyKvStringCreate from .modules.cda.py_string_to_datetime import PyStringToDatetime +from .modules.cda.py_string_to_dict import PyStringToDict from .modules.cda.py_string_to_float import PyStringToFloat from .modules.cda.py_string_to_int import PyStringToInt +from .modules.cda.py_string_to_list import PyStringToList from .modules.matplotlib.mpl_bar import MPLBar from .modules.matplotlib.mpl_line import MPLLine from .modules.matplotlib.mpl_pie_chart import MPLPieChart @@ -46,6 +48,7 @@ from .modules.pandas_wrapper.pandas_count import PandasCount from .modules.pandas_wrapper.pandas_cov import PandasCov from .modules.pandas_wrapper.pandas_create import PandasCreate from .modules.pandas_wrapper.pandas_create_from_dict import PandasCreateFromDict +from .modules.pandas_wrapper.pandas_create_from_dict_index_list import PandasCreateFromDictIndexList from .modules.pandas_wrapper.pandas_create_from_multiple_dict import PandasCreateFromMultipleDict from .modules.pandas_wrapper.pandas_create_from_numpy import PandasCreateFromNumpy from .modules.pandas_wrapper.pandas_create_from_tensor import PandasCreateFromTensor @@ -62,11 +65,14 @@ from .modules.pandas_wrapper.pandas_div_scalar_int import PandasDivScalarInt from .modules.pandas_wrapper.pandas_div_series import PandasDivSeries from .modules.pandas_wrapper.pandas_drop_duplicates import PandasDropDuplicates from .modules.pandas_wrapper.pandas_dropna import PandasDropNA +from .modules.pandas_wrapper.pandas_eq import PandasEq from .modules.pandas_wrapper.pandas_exp import PandasExp from .modules.pandas_wrapper.pandas_feature_split_to_numpy import PandasFeatureSplitToNumpy from .modules.pandas_wrapper.pandas_fillna_scalar_float import PandasFillNAScalarFloat from .modules.pandas_wrapper.pandas_fillna_scalar_int import PandasFillNAScalarInt +from .modules.pandas_wrapper.pandas_ge import PandasGe from .modules.pandas_wrapper.pandas_group_by import PandasGroupBy +from .modules.pandas_wrapper.pandas_gt import PandasGt from .modules.pandas_wrapper.pandas_head import PandasHead from .modules.pandas_wrapper.pandas_horizontal_concat import PandasHorizontalConcat from .modules.pandas_wrapper.pandas_horizontal_split import PandasHorizontalSplit @@ -86,6 +92,7 @@ from .modules.pandas_wrapper.pandas_index_to_string import PandasIndexToString from .modules.pandas_wrapper.pandas_isna import PandasIsNA from .modules.pandas_wrapper.pandas_join import PandasJoin from .modules.pandas_wrapper.pandas_kurtosis import PandasKurtosis +from .modules.pandas_wrapper.pandas_le import PandasLe from .modules.pandas_wrapper.pandas_load_csv import PandasLoadCSV from .modules.pandas_wrapper.pandas_load_csv_with_encoding import PandasLoadCSVWithEncoding from .modules.pandas_wrapper.pandas_load_csv_with_index import PandasLoadCSVWithIndex @@ -96,6 +103,7 @@ from .modules.pandas_wrapper.pandas_loc_cell_string import PandasLocCellString from .modules.pandas_wrapper.pandas_loc_row_multiindex_dataframe import PandasLocRowMultiIndexDataFrame from .modules.pandas_wrapper.pandas_loc_row_series import PandasLocRowSeries from .modules.pandas_wrapper.pandas_log import PandasLog +from .modules.pandas_wrapper.pandas_lt import PandasLt from .modules.pandas_wrapper.pandas_max import PandasMax from .modules.pandas_wrapper.pandas_mean import PandasMean from .modules.pandas_wrapper.pandas_median import PandasMedian @@ -106,6 +114,7 @@ from .modules.pandas_wrapper.pandas_mul import PandasMul from .modules.pandas_wrapper.pandas_mul_scalar_float import PandasMulScalarFloat from .modules.pandas_wrapper.pandas_mul_scalar_int import PandasMulScalarInt from .modules.pandas_wrapper.pandas_mul_series import PandasMulSeries +from .modules.pandas_wrapper.pandas_ne import PandasNe from .modules.pandas_wrapper.pandas_pivot import PandasPivot from .modules.pandas_wrapper.pandas_pow import PandasPow from .modules.pandas_wrapper.pandas_pow_scalar_float import PandasPowScalarFloat @@ -298,6 +307,7 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = { "PandasCov": PandasCov, "PandasCreate": PandasCreate, "PandasCreateFromDict": PandasCreateFromDict, + "PandasCreateFromDictIndexList": PandasCreateFromDictIndexList, "PandasCreateFromMultipleDict": PandasCreateFromMultipleDict, "PandasCreateFromNumpy": PandasCreateFromNumpy, "PandasCreateFromTensor": PandasCreateFromTensor, @@ -314,11 +324,14 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = { "PandasDivSeries": PandasDivSeries, "PandasDropDuplicates": PandasDropDuplicates, "PandasDropNA": PandasDropNA, + "PandasEq": PandasEq, "PandasExp": PandasExp, "PandasFeatureSplitToNumpy": PandasFeatureSplitToNumpy, "PandasFillNAScalarFloat": PandasFillNAScalarFloat, "PandasFillNAScalarInt": PandasFillNAScalarInt, + "PandasGe": PandasGe, "PandasGroupBy": PandasGroupBy, + "PandasGt": PandasGt, "PandasHead": PandasHead, "PandasHorizontalConcat": PandasHorizontalConcat, "PandasHorizontalSplit": PandasHorizontalSplit, @@ -338,6 +351,7 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = { "PandasIsNA": PandasIsNA, "PandasJoin": PandasJoin, "PandasKurtosis": PandasKurtosis, + "PandasLe": PandasLe, "PandasLoadCSV": PandasLoadCSV, "PandasLoadCSVWithEncoding": PandasLoadCSVWithEncoding, "PandasLoadCSVWithIndex": PandasLoadCSVWithIndex, @@ -348,6 +362,7 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = { "PandasLocRowMultiIndexDataFrame": PandasLocRowMultiIndexDataFrame, "PandasLocRowSeries": PandasLocRowSeries, "PandasLog": PandasLog, + "PandasLt": PandasLt, "PandasMax": PandasMax, "PandasMean": PandasMean, "PandasMedian": PandasMedian, @@ -358,6 +373,7 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = { "PandasMulScalarFloat": PandasMulScalarFloat, "PandasMulScalarInt": PandasMulScalarInt, "PandasMulSeries": PandasMulSeries, + "PandasNe": PandasNe, "PandasPivot": PandasPivot, "PandasPow": PandasPow, "PandasPowScalarFloat": PandasPowScalarFloat, @@ -504,8 +520,10 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = { "PyKvIntCreate": PyKvIntCreate, "PyKvStringCreate": PyKvStringCreate, "PyStringToDatetime": PyStringToDatetime, + "PyStringToDict": PyStringToDict, "PyStringToFloat": PyStringToFloat, "PyStringToInt": PyStringToInt, + "PyStringToList": PyStringToList, "SNSBar": SNSBar, "SNSBoxplot": SNSBoxplot, "SNSHeatmap": SNSHeatmap, @@ -559,6 +577,7 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = { "PandasCov": "Pandas Cov", "PandasCreate": "Pandas Create", "PandasCreateFromDict": "Pandas Create From Dict", + "PandasCreateFromDictIndexList": "Pandas Create From Dict Index List", "PandasCreateFromMultipleDict": "Pandas Create From Multiple Dict", "PandasCreateFromNumpy": "Pandas Create From Numpy", "PandasCreateFromTensor": "Pandas Create From Tensor", @@ -575,11 +594,14 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = { "PandasDivSeries": "Pandas Div Series", "PandasDropDuplicates": "Pandas Drop Duplicates", "PandasDropNA": "Pandas Drop NA", + "PandasEq": "Pandas Eq", "PandasExp": "Pandas Exp", "PandasFeatureSplitToNumpy": "Pandas Feature Split To Numpy", "PandasFillNAScalarFloat": "Pandas Fill NA Scalar Float", "PandasFillNAScalarInt": "Pandas Fill NA Scalar Int", + "PandasGe": "Pandas Ge", "PandasGroupBy": "Pandas Group By", + "PandasGt": "Pandas Gt", "PandasHead": "Pandas Head", "PandasHorizontalConcat": "Pandas Horizontal Concat", "PandasHorizontalSplit": "Pandas Horizontal Split", @@ -599,6 +621,7 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = { "PandasIsNA": "Pandas Is NA", "PandasJoin": "Pandas Join", "PandasKurtosis": "Pandas Kurtosis", + "PandasLe": "Pandas Le", "PandasLoadCSV": "Pandas Load CSV", "PandasLoadCSVWithEncoding": "Pandas Load CSV With Encoding", "PandasLoadCSVWithIndex": "Pandas Load CSV With Index", @@ -609,6 +632,7 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = { "PandasLocRowMultiIndexDataFrame": "Pandas Loc Row MultiIndex DataFrame", "PandasLocRowSeries": "Pandas Loc Row Series", "PandasLog": "Pandas Log", + "PandasLt": "Pandas Lt", "PandasMax": "Pandas Max", "PandasMean": "Pandas Mean", "PandasMedian": "Pandas Median", @@ -619,6 +643,7 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = { "PandasMulScalarFloat": "Pandas Mul Scalar Float", "PandasMulScalarInt": "Pandas Mul Scalar Int", "PandasMulSeries": "Pandas Mul Series", + "PandasNe": "Pandas Ne", "PandasPivot": "Pandas Pivot", "PandasPow": "Pandas Pow", "PandasPowScalarFloat": "Pandas Pow Scalar Float", @@ -765,8 +790,10 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = { "PyKvIntCreate": "Py Kv Int Create", "PyKvStringCreate": "Py Kv String Create", "PyStringToDatetime": "Py String To Datetime", + "PyStringToDict": "Py String To Dict", "PyStringToFloat": "Py String To Float", "PyStringToInt": "Py String To Int", + "PyStringToList": "Py String To List", "SNSBar": "SNS Bar Chart", "SNSBoxplot": "SNS Boxplot", "SNSHeatmap": "SNS Heatmap", diff --git a/docs/reference/node_reference.md b/docs/reference/node_reference.md index f115712..5920c60 100644 --- a/docs/reference/node_reference.md +++ b/docs/reference/node_reference.md @@ -109,6 +109,7 @@ | [CDA Text Create](cda_text_create.md) | Creates a Python String using a value entered in a multi-line text field. | | [Pandas Create](pandas_create.md) | Creates a pandas DataFrame using values entered in the text field. | | [Pandas Create From Dict](pandas_create_from_dict.md) | Creates a pandas DataFrame from a Python dictionary. | +| [Pandas Create From Dict Index List](pandas_create_from_dict_index_list.md) | Creates a pandas DataFrame from a Python dictionary and a list for index. | | [Pandas Create From Multiple Dict](pandas_create_from_multiple_dict.md) | Creates a pandas DataFrame from multiple Python dictionaries. | | [Pandas Create From Numpy](pandas_create_from_numpy.md) | Creates a pandas DataFrame from a NumPy ndarray. | | [Pandas Create From Tensor](pandas_create_from_tensor.md) | Creates a pandas DataFrame from a PyTorch tensor. | @@ -123,6 +124,15 @@ | [Pandas Save CSV](pandas_save_csv.md) | Saves a PandasDataFrame to a CSV file. | | [Pandas Save JSON](pandas_save_json.md) | Saves a PandasDataFrame to a JSON file. | | [Pt Save Model](pt_save_model.md) | A wrapper class for saving a PyTorch model. | +## Logical operations +| Node | Description | +| --- | --- | +| [Pandas Eq](pandas_eq.md) | Determines whether each element in the first pandas DataFrame is equal to the corresponding element in the second DataFrame. | +| [Pandas Ge](pandas_ge.md) | Determines whether each element in the first pandas DataFrame is greater than or equal to the corresponding element in the second DataFrame. | +| [Pandas Gt](pandas_gt.md) | Determines whether each element in the first pandas DataFrame is greater than the corresponding element in the second DataFrame. | +| [Pandas Le](pandas_le.md) | Determines whether each element in the first pandas DataFrame is less than or equal to the corresponding element in the second DataFrame. | +| [Pandas Lt](pandas_lt.md) | Determines whether each element in the first pandas DataFrame is less than the corresponding element in the second DataFrame. | +| [Pandas Ne](pandas_ne.md) | Determines whether each element in the first pandas DataFrame is not equal to the corresponding element in the second DataFrame. | ## Math | Node | Description | | --- | --- | @@ -297,8 +307,10 @@ | [Py Kv Int Create](py_kv_int_create.md) | Creates a Python dictionary with an string key and an integer value. | | [Py Kv String Create](py_kv_string_create.md) | Creates a Python dictionary with a string key and a string value. | | [Py String To Datetime](py_string_to_datetime.md) | Converts a Python string to a Python datetime.datetime. | +| [Py String To Dict](py_string_to_dict.md) | Creates a Python dictionary using the string values entered in the text field. | | [Py String To Float](py_string_to_float.md) | Converts a Python string to a float. | | [Py String To Int](py_string_to_int.md) | Converts a Python string to an int. | +| [Py String To List](py_string_to_list.md) | Creates a Python list using the string values entered in the text field. | ## Summary statistics | Node | Description | | --- | --- | diff --git a/docs/reference/pandas_create_from_dict.md b/docs/reference/pandas_create_from_dict.md index d53872d..b0c310e 100644 --- a/docs/reference/pandas_create_from_dict.md +++ b/docs/reference/pandas_create_from_dict.md @@ -1,6 +1,8 @@ # Pandas Create From Dict Creates a pandas DataFrame from a Python dictionary. +Use `Py String To Dict` node to generate a dict from a string. + ## Input | Name | Data type | |---|---| diff --git a/docs/reference/pandas_create_from_dict_index_list.md b/docs/reference/pandas_create_from_dict_index_list.md new file mode 100644 index 0000000..b11c391 --- /dev/null +++ b/docs/reference/pandas_create_from_dict_index_list.md @@ -0,0 +1,21 @@ +# Pandas Create From Dict Index List +Creates a pandas DataFrame from a Python dictionary and a list for index. + +Use `Py String To Dict` node to generate a dict for cell values from a string. +Use `Py String To List` node to generate a list for index from a string. + +## Input +| Name | Data type | +|---|---| +| data | Pydict | +| index | Pylist | + +## Output +| Data type | +|---| +| DataFrame | + +
+Category: IO + +ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved. diff --git a/docs/reference/pandas_eq.md b/docs/reference/pandas_eq.md new file mode 100644 index 0000000..ef997b0 --- /dev/null +++ b/docs/reference/pandas_eq.md @@ -0,0 +1,18 @@ +# Pandas Eq +Determines whether each element in the first pandas DataFrame is equal to the corresponding element in the second DataFrame. + +## Input +| Name | Data type | +|---|---| +| a_dataframe | DataFrame | +| b_dataframe | DataFrame | + +## Output +| Data type | +|---| +| DataFrame | + +
+Category: Logical operations + +ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved. diff --git a/docs/reference/pandas_ge.md b/docs/reference/pandas_ge.md new file mode 100644 index 0000000..3ba2cf8 --- /dev/null +++ b/docs/reference/pandas_ge.md @@ -0,0 +1,18 @@ +# Pandas Ge +Determines whether each element in the first pandas DataFrame is greater than or equal to the corresponding element in the second DataFrame. + +## Input +| Name | Data type | +|---|---| +| a_dataframe | DataFrame | +| b_dataframe | DataFrame | + +## Output +| Data type | +|---| +| DataFrame | + +
+Category: Logical operations + +ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved. diff --git a/docs/reference/pandas_gt.md b/docs/reference/pandas_gt.md new file mode 100644 index 0000000..a16b57d --- /dev/null +++ b/docs/reference/pandas_gt.md @@ -0,0 +1,18 @@ +# Pandas Gt +Determines whether each element in the first pandas DataFrame is greater than the corresponding element in the second DataFrame. + +## Input +| Name | Data type | +|---|---| +| a_dataframe | DataFrame | +| b_dataframe | DataFrame | + +## Output +| Data type | +|---| +| DataFrame | + +
+Category: Logical operations + +ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved. diff --git a/docs/reference/pandas_le.md b/docs/reference/pandas_le.md new file mode 100644 index 0000000..585b0ad --- /dev/null +++ b/docs/reference/pandas_le.md @@ -0,0 +1,18 @@ +# Pandas Le +Determines whether each element in the first pandas DataFrame is less than or equal to the corresponding element in the second DataFrame. + +## Input +| Name | Data type | +|---|---| +| a_dataframe | DataFrame | +| b_dataframe | DataFrame | + +## Output +| Data type | +|---| +| DataFrame | + +
+Category: Logical operations + +ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved. diff --git a/docs/reference/pandas_lt.md b/docs/reference/pandas_lt.md new file mode 100644 index 0000000..08b726b --- /dev/null +++ b/docs/reference/pandas_lt.md @@ -0,0 +1,18 @@ +# Pandas Lt +Determines whether each element in the first pandas DataFrame is less than the corresponding element in the second DataFrame. + +## Input +| Name | Data type | +|---|---| +| a_dataframe | DataFrame | +| b_dataframe | DataFrame | + +## Output +| Data type | +|---| +| DataFrame | + +
+Category: Logical operations + +ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved. diff --git a/docs/reference/pandas_ne.md b/docs/reference/pandas_ne.md new file mode 100644 index 0000000..650e784 --- /dev/null +++ b/docs/reference/pandas_ne.md @@ -0,0 +1,18 @@ +# Pandas Ne +Determines whether each element in the first pandas DataFrame is not equal to the corresponding element in the second DataFrame. + +## Input +| Name | Data type | +|---|---| +| a_dataframe | DataFrame | +| b_dataframe | DataFrame | + +## Output +| Data type | +|---| +| DataFrame | + +
+Category: Logical operations + +ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved. diff --git a/docs/reference/py_string_to_dict.md b/docs/reference/py_string_to_dict.md new file mode 100644 index 0000000..1bc67d2 --- /dev/null +++ b/docs/reference/py_string_to_dict.md @@ -0,0 +1,17 @@ +# Py String To Dict +Creates a Python dictionary using the string values entered in the text field. + +## Input +| Name | Data type | +|---|---| +| data | String | + +## Output +| Data type | +|---| +| Pydict | + +
+Category: Python wrapper + +ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved. diff --git a/docs/reference/py_string_to_list.md b/docs/reference/py_string_to_list.md new file mode 100644 index 0000000..a4288f5 --- /dev/null +++ b/docs/reference/py_string_to_list.md @@ -0,0 +1,17 @@ +# Py String To List +Creates a Python list using the string values entered in the text field. + +## Input +| Name | Data type | +|---|---| +| data | String | + +## Output +| Data type | +|---| +| Pylist | + +
+Category: Python wrapper + +ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved. diff --git a/modules/cda/cda_json_create.py b/modules/cda/cda_json_create.py index 3be19ec..8ec873e 100644 --- a/modules/cda/cda_json_create.py +++ b/modules/cda/cda_json_create.py @@ -35,7 +35,7 @@ class CDAJSONCreate: data (str): String value in the text field. Returns: - tuple: A tuple containing a JSON string representation of the DataFrame. + tuple: A tuple containing a JSON string. """ # Convert to JSON json_obj = json.loads(data) diff --git a/modules/cda/py_string_to_dict.py b/modules/cda/py_string_to_dict.py new file mode 100644 index 0000000..a9a1ba4 --- /dev/null +++ b/modules/cda/py_string_to_dict.py @@ -0,0 +1,41 @@ +from typing import Any, Dict +import ast + +class PyStringToDict: + """ + Creates a Python dictionary using the string values entered in the text field. + + category: Python wrapper + """ + + @classmethod + def INPUT_TYPES(cls) -> Dict[str, Any]: + """ + Defines the input types for the `create` function. + + Returns: + Dict[str, Any]: A dictionary specifying required input types. + """ + return { + "required": { + "data": ("STRING", {"multiline": True}), + } + } + + RETURN_TYPES: tuple = ("PYDICT",) + FUNCTION: str = "f" + CATEGORY: str = "Data Analysis" + + def f(self, data: str) -> tuple: + """ + Creates a Python dictionary using the string values entered in the text field. + + Args: + data (str): String value in the text field. + + Returns: + tuple: A dict converted from the input string. + """ + d = ast.literal_eval(data) + return (d,) + diff --git a/modules/cda/py_string_to_list.py b/modules/cda/py_string_to_list.py new file mode 100644 index 0000000..740d3c8 --- /dev/null +++ b/modules/cda/py_string_to_list.py @@ -0,0 +1,41 @@ +from typing import Any, List +import ast + +class PyStringToList: + """ + Creates a Python list using the string values entered in the text field. + + category: Python wrapper + """ + + @classmethod + def INPUT_TYPES(cls) -> List[str]: + """ + Defines the input types for the `create` function. + + Returns: + List[str, Any]: A list specifying required input types. + """ + return { + "required": { + "data": ("STRING", {"multiline": True}), + } + } + + RETURN_TYPES: tuple = ("PYLIST",) + FUNCTION: str = "f" + CATEGORY: str = "Data Analysis" + + def f(self, data: str) -> tuple: + """ + Creates a Python list using the string values entered in the text field. + + Args: + data (str): String value in the text field. + + Returns: + tuple: A list converted from the input string. + """ + d = ast.literal_eval(data) + return (d,) + diff --git a/modules/pandas_wrapper/pandas_create_from_dict.py b/modules/pandas_wrapper/pandas_create_from_dict.py index a385b18..46d5c63 100644 --- a/modules/pandas_wrapper/pandas_create_from_dict.py +++ b/modules/pandas_wrapper/pandas_create_from_dict.py @@ -12,6 +12,8 @@ class PandasCreateFromDict: """ Creates a pandas DataFrame from a Python dictionary. + Use `Py String To Dict` node to generate a dict from a string. + category: IO """ diff --git a/modules/pandas_wrapper/pandas_create_from_dict_index_list.py b/modules/pandas_wrapper/pandas_create_from_dict_index_list.py new file mode 100644 index 0000000..a4dae23 --- /dev/null +++ b/modules/pandas_wrapper/pandas_create_from_dict_index_list.py @@ -0,0 +1,59 @@ +import os +import sys +from typing import Any, Dict, List +import pandas as pd + +PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "..")) +MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules") +sys.path.append(MODULE_ROOT) +from cda.utils import python_dict_first_value_len + +class PandasCreateFromDictIndexList: + """ + Creates a pandas DataFrame from a Python dictionary and a list for index. + + Use `Py String To Dict` node to generate a dict for cell values from a string. + Use `Py String To List` node to generate a list for index from a string. + + category: IO + """ + + @classmethod + def INPUT_TYPES(cls) -> Dict[str, Any]: + """ + Defines the input types for the `create` function. + + Returns: + Dict[str, Any]: A dictionary specifying required input types. + """ + return { + "required": { + "data": ("PYDICT", {}), + "index": ("PYLIST", {}), + } + } + + RETURN_TYPES: tuple = ("DATAFRAME",) + FUNCTION: str = "f" + CATEGORY: str = "Data Analysis" + + def f(self, data: Dict, index: List) -> tuple: + """ + Creates a pandas DataFrame from a Python dictionary. + + Args: + data (Dict): A Python dictionary containing cell values. + index (List): A Python list containing index. + + Returns: + tuple: A tuple containing a the DataFrame. + """ + if not data: + df = pd.DataFrame() # Return an empty DataFrame if no data is provided. + else: + if not list: + value_length = python_dict_first_value_len(data) + df = pd.DataFrame(data, index=list(range(value_length))) + else: + df = pd.DataFrame(data, index=index) + return (df,) diff --git a/modules/pandas_wrapper/pandas_eq.py b/modules/pandas_wrapper/pandas_eq.py new file mode 100644 index 0000000..9bae37c --- /dev/null +++ b/modules/pandas_wrapper/pandas_eq.py @@ -0,0 +1,43 @@ +from typing import Any, Dict +import pandas as pd + + +class PandasEq: + """ + Determines whether each element in the first pandas DataFrame is equal to the corresponding element in the second DataFrame. + + category: Logical operations + """ + + @classmethod + def INPUT_TYPES(cls) -> Dict[str, Any]: + """ + Defines the input types for the function. + + Returns: + Dict[str, Any]: A dictionary specifying required input types. + """ + return { + "required": { + "a_dataframe": ("DATAFRAME", {}), + "b_dataframe": ("DATAFRAME", {}) + } + } + + RETURN_TYPES: tuple = ("DATAFRAME",) + FUNCTION: str = "f" + CATEGORY: str = "Data Analysis" + + def f(self, a_dataframe: pd.DataFrame, b_dataframe: pd.DataFrame) -> tuple: + """ + Determines whether each element in the first pandas DataFrame is equal to the corresponding element in the second DataFrame. + + Args: + a_dataframe (DataFrame): The left DataFrame. + b_dataframe (DataFrame): The right DataFrame. + + Returns: + tuple: A tuple containing the DataFrame containing the result of operation. + """ + df_out = a_dataframe.eq(b_dataframe) + return (df_out,) diff --git a/modules/pandas_wrapper/pandas_ge.py b/modules/pandas_wrapper/pandas_ge.py new file mode 100644 index 0000000..3923ea3 --- /dev/null +++ b/modules/pandas_wrapper/pandas_ge.py @@ -0,0 +1,43 @@ +from typing import Any, Dict +import pandas as pd + + +class PandasGe: + """ + Determines whether each element in the first pandas DataFrame is greater than or equal to the corresponding element in the second DataFrame. + + category: Logical operations + """ + + @classmethod + def INPUT_TYPES(cls) -> Dict[str, Any]: + """ + Defines the input types for the function. + + Returns: + Dict[str, Any]: A dictionary specifying required input types. + """ + return { + "required": { + "a_dataframe": ("DATAFRAME", {}), + "b_dataframe": ("DATAFRAME", {}) + } + } + + RETURN_TYPES: tuple = ("DATAFRAME",) + FUNCTION: str = "f" + CATEGORY: str = "Data Analysis" + + def f(self, a_dataframe: pd.DataFrame, b_dataframe: pd.DataFrame) -> tuple: + """ + Determines whether each element in the first pandas DataFrame is greater than or equal to the corresponding element in the second DataFrame. + + Args: + a_dataframe (DataFrame): The left DataFrame. + b_dataframe (DataFrame): The right DataFrame. + + Returns: + tuple: A tuple containing the DataFrame containing the result of operation. + """ + df_out = a_dataframe.ge(b_dataframe) + return (df_out,) diff --git a/modules/pandas_wrapper/pandas_gt.py b/modules/pandas_wrapper/pandas_gt.py new file mode 100644 index 0000000..a25c14c --- /dev/null +++ b/modules/pandas_wrapper/pandas_gt.py @@ -0,0 +1,43 @@ +from typing import Any, Dict +import pandas as pd + + +class PandasGt: + """ + Determines whether each element in the first pandas DataFrame is greater than the corresponding element in the second DataFrame. + + category: Logical operations + """ + + @classmethod + def INPUT_TYPES(cls) -> Dict[str, Any]: + """ + Defines the input types for the function. + + Returns: + Dict[str, Any]: A dictionary specifying required input types. + """ + return { + "required": { + "a_dataframe": ("DATAFRAME", {}), + "b_dataframe": ("DATAFRAME", {}) + } + } + + RETURN_TYPES: tuple = ("DATAFRAME",) + FUNCTION: str = "f" + CATEGORY: str = "Data Analysis" + + def f(self, a_dataframe: pd.DataFrame, b_dataframe: pd.DataFrame) -> tuple: + """ + Determines whether each element in the first pandas DataFrame is greater than the corresponding element in the second DataFrame. + + Args: + a_dataframe (DataFrame): The left DataFrame. + b_dataframe (DataFrame): The right DataFrame. + + Returns: + tuple: A tuple containing the DataFrame containing the result of operation. + """ + df_out = a_dataframe.gt(b_dataframe) + return (df_out,) diff --git a/modules/pandas_wrapper/pandas_le.py b/modules/pandas_wrapper/pandas_le.py new file mode 100644 index 0000000..066ed62 --- /dev/null +++ b/modules/pandas_wrapper/pandas_le.py @@ -0,0 +1,43 @@ +from typing import Any, Dict +import pandas as pd + + +class PandasLe: + """ + Determines whether each element in the first pandas DataFrame is less than or equal to the corresponding element in the second DataFrame. + + category: Logical operations + """ + + @classmethod + def INPUT_TYPES(cls) -> Dict[str, Any]: + """ + Defines the input types for the function. + + Returns: + Dict[str, Any]: A dictionary specifying required input types. + """ + return { + "required": { + "a_dataframe": ("DATAFRAME", {}), + "b_dataframe": ("DATAFRAME", {}) + } + } + + RETURN_TYPES: tuple = ("DATAFRAME",) + FUNCTION: str = "f" + CATEGORY: str = "Data Analysis" + + def f(self, a_dataframe: pd.DataFrame, b_dataframe: pd.DataFrame) -> tuple: + """ + Determines whether each element in the first pandas DataFrame is less than or equal to the corresponding element in the second DataFrame. + + Args: + a_dataframe (DataFrame): The left DataFrame. + b_dataframe (DataFrame): The right DataFrame. + + Returns: + tuple: A tuple containing the DataFrame containing the result of operation. + """ + df_out = a_dataframe.le(b_dataframe) + return (df_out,) diff --git a/modules/pandas_wrapper/pandas_lt.py b/modules/pandas_wrapper/pandas_lt.py new file mode 100644 index 0000000..e35d573 --- /dev/null +++ b/modules/pandas_wrapper/pandas_lt.py @@ -0,0 +1,43 @@ +from typing import Any, Dict +import pandas as pd + + +class PandasLt: + """ + Determines whether each element in the first pandas DataFrame is less than the corresponding element in the second DataFrame. + + category: Logical operations + """ + + @classmethod + def INPUT_TYPES(cls) -> Dict[str, Any]: + """ + Defines the input types for the function. + + Returns: + Dict[str, Any]: A dictionary specifying required input types. + """ + return { + "required": { + "a_dataframe": ("DATAFRAME", {}), + "b_dataframe": ("DATAFRAME", {}) + } + } + + RETURN_TYPES: tuple = ("DATAFRAME",) + FUNCTION: str = "f" + CATEGORY: str = "Data Analysis" + + def f(self, a_dataframe: pd.DataFrame, b_dataframe: pd.DataFrame) -> tuple: + """ + Determines whether each element in the first pandas DataFrame is less than the corresponding element in the second DataFrame. + + Args: + a_dataframe (DataFrame): The left DataFrame. + b_dataframe (DataFrame): The right DataFrame. + + Returns: + tuple: A tuple containing the DataFrame containing the result of operation. + """ + df_out = a_dataframe.lt(b_dataframe) + return (df_out,) diff --git a/modules/pandas_wrapper/pandas_ne.py b/modules/pandas_wrapper/pandas_ne.py new file mode 100644 index 0000000..b4ede78 --- /dev/null +++ b/modules/pandas_wrapper/pandas_ne.py @@ -0,0 +1,43 @@ +from typing import Any, Dict +import pandas as pd + + +class PandasNe: + """ + Determines whether each element in the first pandas DataFrame is not equal to the corresponding element in the second DataFrame. + + category: Logical operations + """ + + @classmethod + def INPUT_TYPES(cls) -> Dict[str, Any]: + """ + Defines the input types for the function. + + Returns: + Dict[str, Any]: A dictionary specifying required input types. + """ + return { + "required": { + "a_dataframe": ("DATAFRAME", {}), + "b_dataframe": ("DATAFRAME", {}) + } + } + + RETURN_TYPES: tuple = ("DATAFRAME",) + FUNCTION: str = "f" + CATEGORY: str = "Data Analysis" + + def f(self, a_dataframe: pd.DataFrame, b_dataframe: pd.DataFrame) -> tuple: + """ + Determines whether each element in the first pandas DataFrame is not equal to the corresponding element in the second DataFrame. + + Args: + a_dataframe (DataFrame): The left DataFrame. + b_dataframe (DataFrame): The right DataFrame. + + Returns: + tuple: A tuple containing the DataFrame containing the result of operation. + """ + df_out = a_dataframe.ne(b_dataframe) + return (df_out,) diff --git a/pyproject.toml b/pyproject.toml index 8152dc7..b7a8c1e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "data-analysis" description = "Data analysis custom modules for ComfyUI – Integrates Pandas, Seaborn and Matplotlib into ComfyUI, featuring extensive PyTorch wrapper nodes for seamless tensor operations. Experimental PyTorch model training was added in v2.0." -version = "2.0.8" +version = "2.0.9" license = {file = "LICENSE"} [project.urls] diff --git a/tests/pandas_wrapper/pandas_create_from_dict_index_list_test.py b/tests/pandas_wrapper/pandas_create_from_dict_index_list_test.py new file mode 100644 index 0000000..89ff0af --- /dev/null +++ b/tests/pandas_wrapper/pandas_create_from_dict_index_list_test.py @@ -0,0 +1,62 @@ +import os +import sys +import unittest +import pandas as pd + +PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "..")) +MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules") +sys.path.append(MODULE_ROOT) +from pandas_wrapper.pandas_create_from_dict_index_list import PandasCreateFromDictIndexList + +class TestPandasCreateFromDictIndexList(unittest.TestCase): + def setUp(self): + """Set up test data.""" + self.node = PandasCreateFromDictIndexList() + self.test_cases = [ + # Test with a standard dictionary and provided index + ( + {"a": [1, 2, 3], "b": [4, 5, 6]}, + ["row1", "row2", "row3"], + pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]}, index=["row1", "row2", "row3"]) + ), + # Test with an empty dictionary (should return an empty DataFrame) + ( + {}, + ["row1", "row2"], + pd.DataFrame() + ), + # Test with missing index (should use default range index) + ( + {"a": [7, 8, 9]}, + None, + pd.DataFrame({"a": [7, 8, 9]}, index=[0, 1, 2]) + ), + # Test with integer index values + ( + {"x": [10, 20], "y": [30, 40]}, + [100, 101], + pd.DataFrame({"x": [10, 20], "y": [30, 40]}, index=[100, 101]) + ), + # Test with a single column and mismatched index length (should raise an error) + ( + {"c": [1, 2]}, + ["only_one_row"], + ValueError # Expecting an error because index length doesn't match data length + ), + ] + + def test_create_dataframe(self): + """Test creating a pandas DataFrame from a dictionary with an index.""" + for data, index, expected in self.test_cases: + with self.subTest(data=data, index=index): + if expected is ValueError: + with self.assertRaises(ValueError): + self.node.f(data, index) + else: + retval = self.node.f(data, index) + self.assertIsInstance(retval, tuple, "Return value should be a tuple.") + self.assertEqual(len(retval), 1, "Return tuple should have a single DataFrame.") + pd.testing.assert_frame_equal(retval[0], expected) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/pandas_wrapper/pandas_eq_test.py b/tests/pandas_wrapper/pandas_eq_test.py new file mode 100644 index 0000000..5880495 --- /dev/null +++ b/tests/pandas_wrapper/pandas_eq_test.py @@ -0,0 +1,58 @@ +import os +import sys +import unittest +import pandas as pd + +PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "..")) +MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules") +sys.path.append(MODULE_ROOT) +from pandas_wrapper.pandas_eq import PandasEq + +class TestPandasEq(unittest.TestCase): + def setUp(self): + """Set up test data.""" + self.node = PandasEq() + self.test_cases = [ + # Test where all values are True (a == b) + ( + pd.DataFrame({"a": [1.5, 2.5]}, index=["row1", "row2"]), + pd.DataFrame({"a": [1.5, 2.5]}, index=["row1", "row2"]), + pd.DataFrame({"a": [True, True]}, index=["row1", "row2"]) + ), + # Test with mixed float and integer (True for all) + ( + pd.DataFrame({"a": [3.0, 4]}, index=[0, 1]), # Second value is an integer + pd.DataFrame({"a": [3.0, 4]}, index=[0, 1]), # Second value is an integer + pd.DataFrame({"a": [True, True]}, index=[0, 1]) + ), + # Test with integer column labels (True for all) + ( + pd.DataFrame({1: [10.0, 20]}, index=[0, 1]), # Second value is an integer + pd.DataFrame({1: [10.0, 20]}, index=[0, 1]), # Second value is an integer + pd.DataFrame({1: [True, True]}, index=[0, 1]) + ), + # Test with a False case (a != b) + ( + pd.DataFrame({"a": [7.0]}, index=["row1"]), + pd.DataFrame({"a": [7.5]}, index=["row1"]), + pd.DataFrame({"a": [False]}, index=["row1"]) + ), + # Another False case with an integer column label + ( + pd.DataFrame({2: [9]}, index=["row1"]), # Integer value + pd.DataFrame({2: [10]}, index=["row1"]), # Different integer value + pd.DataFrame({2: [False]}, index=["row1"]) + ), + ] + + def test_eq_operation(self): + """Test equality operation between two pandas DataFrames.""" + for df_a, df_b, expected in self.test_cases: + with self.subTest(df_a=df_a, df_b=df_b): + retval = self.node.f(df_a, df_b) + self.assertIsInstance(retval, tuple, "Return value should be a tuple.") + self.assertEqual(len(retval), 1, "Return tuple should have a single DataFrame.") + pd.testing.assert_frame_equal(retval[0], expected) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/pandas_wrapper/pandas_ge_test.py b/tests/pandas_wrapper/pandas_ge_test.py new file mode 100644 index 0000000..d65e958 --- /dev/null +++ b/tests/pandas_wrapper/pandas_ge_test.py @@ -0,0 +1,58 @@ +import os +import sys +import unittest +import pandas as pd + +PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "..")) +MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules") +sys.path.append(MODULE_ROOT) +from pandas_wrapper.pandas_ge import PandasGe + +class TestPandasGe(unittest.TestCase): + def setUp(self): + """Set up test data.""" + self.node = PandasGe() + self.test_cases = [ + # Test where all values are True (a >= b) + ( + pd.DataFrame({"a": [2.0, 3.0]}, index=["row1", "row2"]), + pd.DataFrame({"a": [1.5, 2.5]}, index=["row1", "row2"]), + pd.DataFrame({"a": [True, True]}, index=["row1", "row2"]) + ), + # Test with equality and lt + ( + pd.DataFrame({"a": [2.5, 4.5]}, index=[0, 1]), + pd.DataFrame({"a": [3.0, 4.5]}, index=[0, 1]), + pd.DataFrame({"a": [False, True]}, index=[0, 1]) + ), + # Test with integer column labels (True) + ( + pd.DataFrame({1: [15.0, 25.0]}, index=[0, 1]), + pd.DataFrame({1: [10.0, 20.0]}, index=[0, 1]), + pd.DataFrame({1: [True, True]}, index=[0, 1]) + ), + # Test with a False case (a < b) + ( + pd.DataFrame({"a": [7.0]}, index=["row1"]), + pd.DataFrame({"a": [7.5]}, index=["row1"]), + pd.DataFrame({"a": [False]}, index=["row1"]) + ), + # True case with an integer column label + ( + pd.DataFrame({2: [9.5]}, index=["row1"]), + pd.DataFrame({2: [9.5]}, index=["row1"]), + pd.DataFrame({2: [True]}, index=["row1"]) + ), + ] + + def test_ge_operation(self): + """Test greater-than-or-equal operation between two pandas DataFrames.""" + for df_a, df_b, expected in self.test_cases: + with self.subTest(df_a=df_a, df_b=df_b): + retval = self.node.f(df_a, df_b) + self.assertIsInstance(retval, tuple, "Return value should be a tuple.") + self.assertEqual(len(retval), 1, "Return tuple should have a single DataFrame.") + pd.testing.assert_frame_equal(retval[0], expected) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/pandas_wrapper/pandas_gt_test.py b/tests/pandas_wrapper/pandas_gt_test.py new file mode 100644 index 0000000..ee2204d --- /dev/null +++ b/tests/pandas_wrapper/pandas_gt_test.py @@ -0,0 +1,58 @@ +import os +import sys +import unittest +import pandas as pd + +PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "..")) +MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules") +sys.path.append(MODULE_ROOT) +from pandas_wrapper.pandas_gt import PandasGt + +class TestPandasGt(unittest.TestCase): + def setUp(self): + """Set up test data.""" + self.node = PandasGt() + self.test_cases = [ + # Test where all values are True + ( + pd.DataFrame({"a": [2.5, 3.5]}, index=["row1", "row2"]), + pd.DataFrame({"a": [2.0, 3.0]}, index=["row1", "row2"]), + pd.DataFrame({"a": [True, True]}, index=["row1", "row2"]) + ), + # Test with mixed result + ( + pd.DataFrame({"a": [3.6, 4.9]}, index=[0, 1]), + pd.DataFrame({"a": [3.5, 5.0]}, index=[0, 1]), + pd.DataFrame({"a": [True, False]}, index=[0, 1]) + ), + # Test with integer column labels + ( + pd.DataFrame({1: [14.0, 26.0]}, index=[0, 1]), + pd.DataFrame({1: [15.0, 25.0]}, index=[0, 1]), + pd.DataFrame({1: [False, True]}, index=[0, 1]) + ), + # Test with a False case (a < b) + ( + pd.DataFrame({"a": [6.0]}, index=["row1"]), + pd.DataFrame({"a": [7.0]}, index=["row1"]), + pd.DataFrame({"a": [False]}, index=["row1"]) + ), + # Another False case with an integer column label + ( + pd.DataFrame({2: [7.5]}, index=["row1"]), + pd.DataFrame({2: [8.0]}, index=["row1"]), + pd.DataFrame({2: [False]}, index=["row1"]) + ), + ] + + def test_gt_operation(self): + """Test greater-than operation between two pandas DataFrames.""" + for df_a, df_b, expected in self.test_cases: + with self.subTest(df_a=df_a, df_b=df_b): + retval = self.node.f(df_a, df_b) + self.assertIsInstance(retval, tuple, "Return value should be a tuple.") + self.assertEqual(len(retval), 1, "Return tuple should have a single DataFrame.") + pd.testing.assert_frame_equal(retval[0], expected) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/pandas_wrapper/pandas_le_test.py b/tests/pandas_wrapper/pandas_le_test.py new file mode 100644 index 0000000..fbdf472 --- /dev/null +++ b/tests/pandas_wrapper/pandas_le_test.py @@ -0,0 +1,58 @@ +import os +import sys +import unittest +import pandas as pd + +PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "..")) +MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules") +sys.path.append(MODULE_ROOT) +from pandas_wrapper.pandas_le import PandasLe + +class TestPandasLe(unittest.TestCase): + def setUp(self): + """Set up test data.""" + self.node = PandasLe() + self.test_cases = [ + # Test where all values are True + ( + pd.DataFrame({"a": [1.5, 2.5]}, index=["row1", "row2"]), + pd.DataFrame({"a": [2.0, 2.5]}, index=["row1", "row2"]), + pd.DataFrame({"a": [True, True]}, index=["row1", "row2"]) + ), + # Test with integer (still True) + ( + pd.DataFrame({"a": [3, 4]}, index=[0, 1]), + pd.DataFrame({"a": [3, 4]}, index=[0, 1]), + pd.DataFrame({"a": [True, True]}, index=[0, 1]) + ), + # Test with integer column labels (True) + ( + pd.DataFrame({1: [10.0, 20.0]}, index=[0, 1]), + pd.DataFrame({1: [15.0, 20.0]}, index=[0, 1]), + pd.DataFrame({1: [True, True]}, index=[0, 1]) + ), + # Test with a False case (a > b) + ( + pd.DataFrame({"a": [7.0]}, index=["row1"]), + pd.DataFrame({"a": [6.5]}, index=["row1"]), + pd.DataFrame({"a": [False]}, index=["row1"]) + ), + # Another False case with an integer column label + ( + pd.DataFrame({2: [9.5]}, index=["row1"]), + pd.DataFrame({2: [8.0]}, index=["row1"]), + pd.DataFrame({2: [False]}, index=["row1"]) + ), + ] + + def test_le_operation(self): + """Test less-than-or-equal operation between two pandas DataFrames.""" + for df_a, df_b, expected in self.test_cases: + with self.subTest(df_a=df_a, df_b=df_b): + retval = self.node.f(df_a, df_b) + self.assertIsInstance(retval, tuple, "Return value should be a tuple.") + self.assertEqual(len(retval), 1, "Return tuple should have a single DataFrame.") + pd.testing.assert_frame_equal(retval[0], expected) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/pandas_wrapper/pandas_lt_test.py b/tests/pandas_wrapper/pandas_lt_test.py new file mode 100644 index 0000000..cee44c6 --- /dev/null +++ b/tests/pandas_wrapper/pandas_lt_test.py @@ -0,0 +1,58 @@ +import os +import sys +import unittest +import pandas as pd + +PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "..")) +MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules") +sys.path.append(MODULE_ROOT) +from pandas_wrapper.pandas_lt import PandasLt + +class TestPandasLt(unittest.TestCase): + def setUp(self): + """Set up test data.""" + self.node = PandasLt() + self.test_cases = [ + # Test where all values are True + ( + pd.DataFrame({"a": [1.5, 2.5]}, index=["row1", "row2"]), + pd.DataFrame({"a": [2.0, 3.0]}, index=["row1", "row2"]), + pd.DataFrame({"a": [True, True]}, index=["row1", "row2"]) + ), + # Test with integer (still True) + ( + pd.DataFrame({"a": [3, 40]}, index=[0, 1]), + pd.DataFrame({"a": [4, 50]}, index=[0, 1]), + pd.DataFrame({"a": [True, True]}, index=[0, 1]) + ), + # Test with integer column labels + ( + pd.DataFrame({1: [10.0, 20.0]}, index=[0, 1]), + pd.DataFrame({1: [15.0, 20.0]}, index=[0, 1]), + pd.DataFrame({1: [True, False]}, index=[0, 1]) + ), + # Test with a False case (a >= b) + ( + pd.DataFrame({"a": [7.0]}, index=["row1"]), + pd.DataFrame({"a": [7.0]}, index=["row1"]), + pd.DataFrame({"a": [False]}, index=["row1"]) + ), + # Another False case with an integer column label + ( + pd.DataFrame({2: [9.5]}, index=["row1"]), + pd.DataFrame({2: [8.0]}, index=["row1"]), + pd.DataFrame({2: [False]}, index=["row1"]) + ), + ] + + def test_lt_operation(self): + """Test less-than operation between two pandas DataFrames.""" + for df_a, df_b, expected in self.test_cases: + with self.subTest(df_a=df_a, df_b=df_b): + retval = self.node.f(df_a, df_b) + self.assertIsInstance(retval, tuple, "Return value should be a tuple.") + self.assertEqual(len(retval), 1, "Return tuple should have a single DataFrame.") + pd.testing.assert_frame_equal(retval[0], expected) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/pandas_wrapper/pandas_ne_test.py b/tests/pandas_wrapper/pandas_ne_test.py new file mode 100644 index 0000000..836ec35 --- /dev/null +++ b/tests/pandas_wrapper/pandas_ne_test.py @@ -0,0 +1,58 @@ +import os +import sys +import unittest +import pandas as pd + +PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "..")) +MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules") +sys.path.append(MODULE_ROOT) +from pandas_wrapper.pandas_ne import PandasNe + +class TestPandasNe(unittest.TestCase): + def setUp(self): + """Set up test data.""" + self.node = PandasNe() + self.test_cases = [ + # Test where all values are True (a != b) + ( + pd.DataFrame({"a": [1.5, 2.5]}, index=["row1", "row2"]), + pd.DataFrame({"a": [2.0, 3.0]}, index=["row1", "row2"]), + pd.DataFrame({"a": [True, True]}, index=["row1", "row2"]) + ), + # Test with mixed float and integer (True for all) + ( + pd.DataFrame({"a": [3.0, 4]}, index=[0, 1]), # Second value is an integer + pd.DataFrame({"a": [3.5, 5]}, index=[0, 1]), # Second value is an integer + pd.DataFrame({"a": [True, True]}, index=[0, 1]) + ), + # Test with integer column labels (True for all) + ( + pd.DataFrame({1: [10.0, 20]}, index=[0, 1]), # Second value is an integer + pd.DataFrame({1: [15.0, 25]}, index=[0, 1]), # Second value is an integer + pd.DataFrame({1: [True, True]}, index=[0, 1]) + ), + # Test with a False case (a == b) + ( + pd.DataFrame({"a": [7.0]}, index=["row1"]), + pd.DataFrame({"a": [7.0]}, index=["row1"]), + pd.DataFrame({"a": [False]}, index=["row1"]) + ), + # Another False case with an integer column label + ( + pd.DataFrame({2: [9]}, index=["row1"]), # Integer value + pd.DataFrame({2: [9]}, index=["row1"]), # Integer value + pd.DataFrame({2: [False]}, index=["row1"]) + ), + ] + + def test_ne_operation(self): + """Test not-equal operation between two pandas DataFrames.""" + for df_a, df_b, expected in self.test_cases: + with self.subTest(df_a=df_a, df_b=df_b): + retval = self.node.f(df_a, df_b) + self.assertIsInstance(retval, tuple, "Return value should be a tuple.") + self.assertEqual(len(retval), 1, "Return tuple should have a single DataFrame.") + pd.testing.assert_frame_equal(retval[0], expected) + +if __name__ == "__main__": + unittest.main()