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()