diff --git a/__init__.py b/__init__.py
index fb125a8..06b0791 100644
--- a/__init__.py
+++ b/__init__.py
@@ -60,6 +60,7 @@ from .modules.pandas_wrapper.pandas_log import PandasLog
from .modules.pandas_wrapper.pandas_max import PandasMax
from .modules.pandas_wrapper.pandas_mean import PandasMean
from .modules.pandas_wrapper.pandas_median import PandasMedian
+from .modules.pandas_wrapper.pandas_melt import PandasMelt
from .modules.pandas_wrapper.pandas_min import PandasMin
from .modules.pandas_wrapper.pandas_mode import PandasMode
from .modules.pandas_wrapper.pandas_mul import PandasMul
@@ -172,6 +173,7 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PandasMax": PandasMax,
"PandasMean": PandasMean,
"PandasMedian": PandasMedian,
+ "PandasMelt": PandasMelt,
"PandasMin": PandasMin,
"PandasMode": PandasMode,
"PandasMul": PandasMul,
@@ -284,6 +286,7 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PandasMax": "Pandas Max",
"PandasMean": "Pandas Mean",
"PandasMedian": "Pandas Median",
+ "PandasMelt": "Pandas Melt",
"PandasMin": "Pandas Min",
"PandasMode": "Pandas Mode",
"PandasMul": "Pandas Mul",
diff --git a/docs/images/melt.png b/docs/images/melt.png
new file mode 100644
index 0000000..03e2e33
Binary files /dev/null and b/docs/images/melt.png differ
diff --git a/docs/reference/node_reference.md b/docs/reference/node_reference.md
index bc9182b..192dc65 100644
--- a/docs/reference/node_reference.md
+++ b/docs/reference/node_reference.md
@@ -136,6 +136,7 @@
| [Pandas Horizontal Concat](pandas_horizontal_concat.md) | Horizontally concatenates two pandas DataFrames. |
| [Pandas Horizontal Split](pandas_horizontal_split.md) | Horizontally splits a Pandas DataFrame into two pandas DataFrames. |
| [Pandas Join](pandas_join.md) | Merges two pandas DataFrames based on a common column. |
+| [Pandas Melt](pandas_melt.md) | Performs an unpivot operation (melt) on a pandas DataFrame. |
| [Pandas Pivot](pandas_pivot.md) | Computes a pivot table from a pandas DataFrame. |
| [Pandas Rename](pandas_rename.md) | Renames an index (row label) or a column label. |
| [Pandas Series To Data Frame](pandas_series_to_dataframe.md) | Converts a Pandas Series to a DataFrame. |
diff --git a/docs/reference/pandas_melt.md b/docs/reference/pandas_melt.md
new file mode 100644
index 0000000..b348fe5
--- /dev/null
+++ b/docs/reference/pandas_melt.md
@@ -0,0 +1,109 @@
+# Pandas Melt
+Performs an unpivot operation (melt) on a pandas DataFrame.
+
+## Understanding Melt (Unpivot)
+The melt operation converts a DataFrame from **wide format** (where multiple columns represent separate variables) into **long format** (where values are stacked under a single column).
+
+### Example: Frozen Food Prices Table
+
+Given the following DataFrame:
+
+```
+Frozen Food Prices
+
+Manufacturer Pizza Burger
+---------------------------------------------------
+Aimee Italian Foods 10 12
+Olivia Cuisine 11 8
+Maddie Foods 12 7
+```
+
+To demonstrate **melting a single column**, we will **only transform the `"Pizza"` column** and **temporarily exclude `"Burger"`** from the output. `"Burger"` is not affected in this step because we are only selecting `"Pizza"` for melting.
+
+```
+Frozen Food Prices
+
+Manufacturer X Y
+---------------------------------------------------
+Aimee Italian Foods Pizza 10
+Olivia Cuisine Pizza 11
+Maddie Foods Pizza 12
+```
+
+Here:
+
+- Column `X` is assigned to `"Item"` (`var_name`).
+- Column `Y` is assigned to `"Price"` (`value_name`).
+- `"Pizza"` is explicitly specified in `value_vars` as the column being unpivoted.
+- `"Manufacturer"` remains unchanged and is included in `id_vars` to preserve row identity.
+
+The final transformed DataFrame:
+
+```
+Frozen Food Prices
+
+Manufacturer Item Price
+----------------------------------------------------
+Aimee Italian Foods Pizza 10
+Olivia Cuisine Pizza 11
+Maddie Foods Pizza 12
+```
+
+### Expanding the Melt Operation
+
+If we want to **melt both `"Pizza"` and `"Burger"` together**, we specify both columns in `value_vars`:
+
+```
+Frozen Food Prices
+
+Manufacturer Item Price
+----------------------------------------------------
+Aimee Italian Foods Pizza 10
+Olivia Cuisine Pizza 11
+Maddie Foods Pizza 12
+Aimee Italian Foods Burger 12
+Olivia Cuisine Burger 8
+Maddie Foods Burger 7
+```
+
+Here, we explicitly define:
+
+- `"Pizza"` and `"Burger"` in `value_vars`.
+- A new `"Item"` column (`var_name`), which holds the original column names (`"Pizza"`, `"Burger"`).
+- A `"Price"` column, which stores the corresponding values.
+
+To specify both columns in this node, provide:
+
+```
+Pizza,Burger
+```
+
+This node automatically converts the string into a list and passes it to `pandas.DataFrame.melt()`.
+
+
+
+
+Refer to the [Pandas documentation](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.melt.html) for more information.
+
+## Limitation
+MultiIndex columns are not supported.
+
+## Input
+| Name | Data type |
+|---|---|
+| dataframe | DataFrame |
+| id_vars | String |
+| value_vars | String |
+| var_name | String |
+| value_name | String |
+| ignore_index | Boolean |
+
+## Output
+| Data type |
+|---|
+| DataFrame |
+
+
+Category: Transformation
+
+ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
diff --git a/examples/workflows/melt.json b/examples/workflows/melt.json
new file mode 100644
index 0000000..339da99
--- /dev/null
+++ b/examples/workflows/melt.json
@@ -0,0 +1 @@
+{"last_node_id":17,"last_link_id":14,"nodes":[{"id":12,"type":"PandasShowDataFrame","pos":[-1679.7327880859375,6962.63037109375],"size":[329.3037414550781,107.76365661621094],"flags":{},"order":1,"mode":0,"inputs":[{"name":"dataframe","type":"DATAFRAME","link":10}],"outputs":[{"name":"STRING","type":"STRING","links":null,"shape":6}],"properties":{"Node name for S&R":"PandasShowDataFrame"},"widgets_values":[" Manufacturer Pizza Burger\n0 Aimee Italian Foods 10 12\n1 Olivia Cuisine 11 8\n2 Maddie Foods 12 7"]},{"id":14,"type":"PandasShowDataFrame","pos":[-1328.8665771484375,7141.0478515625],"size":[284.7939758300781,116.40038299560547],"flags":{},"order":4,"mode":0,"inputs":[{"name":"dataframe","type":"DATAFRAME","link":12}],"outputs":[{"name":"STRING","type":"STRING","links":null,"shape":6}],"properties":{"Node name for S&R":"PandasShowDataFrame"},"widgets_values":[" Manufacturer Item Price\n0 Aimee Italian Foods Pizza 10\n1 Olivia Cuisine Pizza 11\n2 Maddie Foods Pizza 12"]},{"id":13,"type":"PandasMelt","pos":[-1679.666259765625,7141.65576171875],"size":[315,178],"flags":{},"order":2,"mode":0,"inputs":[{"name":"dataframe","type":"DATAFRAME","link":11}],"outputs":[{"name":"DATAFRAME","type":"DATAFRAME","links":[12],"slot_index":0}],"properties":{"Node name for S&R":"PandasMelt"},"widgets_values":["Manufacturer","Pizza","Item","Price",0]},{"id":17,"type":"PandasMelt","pos":[-1683.4326171875,7385.7001953125],"size":[315,178],"flags":{},"order":3,"mode":0,"inputs":[{"name":"dataframe","type":"DATAFRAME","link":13}],"outputs":[{"name":"DATAFRAME","type":"DATAFRAME","links":[14],"slot_index":0}],"properties":{"Node name for S&R":"PandasMelt"},"widgets_values":["Manufacturer","Pizza,Burger","Item","Price",true]},{"id":15,"type":"PandasShowDataFrame","pos":[-1331.198974609375,7390.00537109375],"size":[294.8554382324219,158.5748748779297],"flags":{},"order":5,"mode":0,"inputs":[{"name":"dataframe","type":"DATAFRAME","link":14}],"outputs":[{"name":"STRING","type":"STRING","links":null,"shape":6}],"properties":{"Node name for S&R":"PandasShowDataFrame"},"widgets_values":[" Manufacturer Item Price\n0 Aimee Italian Foods Pizza 10\n1 Olivia Cuisine Pizza 11\n2 Maddie Foods Pizza 12\n3 Aimee Italian Foods Burger 12\n4 Olivia Cuisine Burger 8\n5 Maddie Foods Burger 7"]},{"id":10,"type":"PandasCreate","pos":[-1952.2723388671875,6961.17138671875],"size":[210,120.59086608886719],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"DATAFRAME","type":"DATAFRAME","links":[10,11,13],"slot_index":0}],"properties":{"Node name for S&R":"PandasCreate"},"widgets_values":["Manufacturer,Pizza,Burger\nAimee Italian Foods,10,12\nOlivia Cuisine,11,8\nMaddie Foods,12,7\n"]}],"links":[[10,10,0,12,0,"DATAFRAME"],[11,10,0,13,0,"DATAFRAME"],[12,13,0,14,0,"DATAFRAME"],[13,10,0,17,0,"DATAFRAME"],[14,17,0,15,0,"DATAFRAME"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.0860798070987705,"offset":[8287.305591731167,3463.780243294214]},"node_versions":{"data-analysis":"94680d227e6b18b5992578b3c77f471b3c19666b"},"ue_links":[]},"version":0.4}
\ No newline at end of file
diff --git a/modules/pandas_wrapper/pandas_melt.py b/modules/pandas_wrapper/pandas_melt.py
new file mode 100644
index 0000000..2e51271
--- /dev/null
+++ b/modules/pandas_wrapper/pandas_melt.py
@@ -0,0 +1,168 @@
+from typing import Any, Dict
+import pandas as pd
+from .utils import column_labels_string_to_list
+
+class PandasMelt:
+ """
+ Performs an unpivot operation (melt) on a pandas DataFrame.
+
+ ## Understanding Melt (Unpivot)
+ The melt operation converts a DataFrame from **wide format** (where multiple columns represent separate variables) into **long format** (where values are stacked under a single column).
+
+ ### Example: Frozen Food Prices Table
+
+ Given the following DataFrame:
+
+ ```
+ Frozen Food Prices
+
+ Manufacturer Pizza Burger
+ ---------------------------------------------------
+ Aimee Italian Foods 10 12
+ Olivia Cuisine 11 8
+ Maddie Foods 12 7
+ ```
+
+ To demonstrate **melting a single column**, we will **only transform the `"Pizza"` column** and **temporarily exclude `"Burger"`** from the output. `"Burger"` is not affected in this step because we are only selecting `"Pizza"` for melting.
+
+ ```
+ Frozen Food Prices
+
+ Manufacturer X Y
+ ---------------------------------------------------
+ Aimee Italian Foods Pizza 10
+ Olivia Cuisine Pizza 11
+ Maddie Foods Pizza 12
+ ```
+
+ Here:
+
+ - Column `X` is assigned to `"Item"` (`var_name`).
+ - Column `Y` is assigned to `"Price"` (`value_name`).
+ - `"Pizza"` is explicitly specified in `value_vars` as the column being unpivoted.
+ - `"Manufacturer"` remains unchanged and is included in `id_vars` to preserve row identity.
+
+ The final transformed DataFrame:
+
+ ```
+ Frozen Food Prices
+
+ Manufacturer Item Price
+ ----------------------------------------------------
+ Aimee Italian Foods Pizza 10
+ Olivia Cuisine Pizza 11
+ Maddie Foods Pizza 12
+ ```
+
+ ### Expanding the Melt Operation
+
+ If we want to **melt both `"Pizza"` and `"Burger"` together**, we specify both columns in `value_vars`:
+
+ ```
+ Frozen Food Prices
+
+ Manufacturer Item Price
+ ----------------------------------------------------
+ Aimee Italian Foods Pizza 10
+ Olivia Cuisine Pizza 11
+ Maddie Foods Pizza 12
+ Aimee Italian Foods Burger 12
+ Olivia Cuisine Burger 8
+ Maddie Foods Burger 7
+ ```
+
+ Here, we explicitly define:
+
+ - `"Pizza"` and `"Burger"` in `value_vars`.
+ - A new `"Item"` column (`var_name`), which holds the original column names (`"Pizza"`, `"Burger"`).
+ - A `"Price"` column, which stores the corresponding values.
+
+ To specify both columns in this node, provide:
+
+ ```
+ Pizza,Burger
+ ```
+
+ This node automatically converts the string into a list and passes it to `pandas.DataFrame.melt()`.
+
+ 
+
+
+ Refer to the [Pandas documentation](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.melt.html) for more information.
+
+ ## Limitation
+ MultiIndex columns are not supported.
+
+ category: Transformation
+ """
+
+ @classmethod
+ def INPUT_TYPES(cls) -> Dict[str, Any]:
+ """
+ Defines the input types for the `select_columns` function.
+
+ Returns:
+ Dict[str, Any]: A dictionary specifying required input types.
+ """
+ return {
+ "required": {
+ "dataframe": ("DATAFRAME", {}),
+ "id_vars": ("STRING", {"default": ""}),
+ "value_vars": ("STRING", {"default": ""}),
+ "var_name": ("STRING", {"default": ""}),
+ "value_name": ("STRING", {"default": ""}),
+ "ignore_index": ("BOOLEAN", {"default": True})
+ }
+ }
+
+ RETURN_TYPES: tuple = ("DATAFRAME",)
+ FUNCTION: str = "f"
+ CATEGORY: str = "Data Analysis"
+
+ def f(self, dataframe: pd.DataFrame,
+ id_vars: str,
+ value_vars: str,
+ var_name: str,
+ value_name: str,
+ ignore_index: bool) -> tuple:
+ """
+ Selects specific columns from a pandas DataFrame.
+
+ Args:
+ dataframe (DataFrame): The input DataFrame.
+ id_vars (str): Comma-separated string to specify ID variable columns.
+ value_vars (str): Comma-separated string to specify column(s) to be unpivoted.
+ var_name (str): New column label for the column that contains original column labels as row values.
+ value_name (str): Name of the new column that holds the values for columns specified in `value_vars`.
+ ignore_index (bool): Flag that specifies whether to keep the original index.
+ If ``True``, a new sequential index is generated.
+ If ``False``, the original index is retained and repeated as needed.
+
+ Returns:
+ tuple: A tuple containing the DataFrame.
+ """
+ id_vars = column_labels_string_to_list(dataframe, id_vars)
+ value_vars = column_labels_string_to_list(dataframe, value_vars)
+ var_name = column_labels_string_to_list(dataframe, var_name, no_check=True)
+ value_name = column_labels_string_to_list(dataframe, value_name, no_check=True)
+
+ if len(id_vars) == 1:
+ id_vars = id_vars[0]
+ if len(value_vars) == 1:
+ value_vars = value_vars[0]
+ if len(var_name) == 0:
+ var_name = None
+ elif len(var_name) == 1:
+ var_name = var_name[0]
+ if len(value_name) == 0:
+ value_name = None
+ elif len(value_name) == 1:
+ value_name = value_name[0]
+
+ df2 = dataframe.melt(
+ id_vars=id_vars,
+ value_vars=value_vars,
+ var_name=var_name,
+ value_name=value_name,
+ ignore_index=ignore_index)
+ return (df2,)
diff --git a/modules/pandas_wrapper/utils.py b/modules/pandas_wrapper/utils.py
index d2a0c09..9cac0c6 100644
--- a/modules/pandas_wrapper/utils.py
+++ b/modules/pandas_wrapper/utils.py
@@ -1,11 +1,34 @@
-from typing import List, Union
+from typing import Any, List, Tuple, Union, Set
import json
import numpy as np
import pandas as pd
-import pandas as pd
-def column_labels_string_to_list(dataframe: pd.DataFrame, s: str) -> List[Union[str, int]]:
+def flatten_list_tuple_unique(lst: Union[List[Any], Tuple[Any, ...]]) -> Set[Any]:
+ """
+ Flattens a nested list or tuple and returns a set of unique elements.
+
+ Args:
+ lst (Union[List[Any], Tuple[Any, ...]]):
+ A list or tuple containing elements of any type, including nested lists/tuples.
+
+ Returns:
+ Set[Any]: A set containing unique elements from the input, flattened.
+ """
+ result: Set[Any] = set()
+
+ def recurse(sublist: Union[List[Any], Tuple[Any, ...]]) -> None:
+ for item in sublist:
+ if isinstance(item, (list, tuple)):
+ recurse(item)
+ else:
+ result.add(item)
+
+ recurse(lst)
+ return result
+
+
+def column_labels_string_to_list(dataframe:pd.DataFrame, s:str, no_check:bool=False) -> List[Union[str, int]]:
"""
Converts a comma-separated string of column labels into a list of valid column names for a Pandas DataFrame.
@@ -16,6 +39,8 @@ def column_labels_string_to_list(dataframe: pd.DataFrame, s: str) -> List[Union[
Args:
dataframe (pd.DataFrame): The DataFrame to validate column names against.
s (str): A comma-separated string representing column labels.
+ no_check (str): If True, input string is processed as a string without checking the column labels
+ of the DataFrame.
Returns:
List[Union[str, int]]: A list of valid column labels.
@@ -24,11 +49,12 @@ def column_labels_string_to_list(dataframe: pd.DataFrame, s: str) -> List[Union[
ValueError: If any label in `s` does not match an existing column name.
"""
columns = [col.strip() for col in s.split(",")]
- valid_columns = set(dataframe.columns) # Use a set for faster lookups
+
+ valid_columns = flatten_list_tuple_unique(dataframe.columns)
output_columns = []
for c in columns:
- if c in valid_columns:
+ if c in valid_columns or no_check:
output_columns.append(c)
else:
try:
diff --git a/pyproject.toml b/pyproject.toml
index 44c966c..57fc9cd 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[project]
name = "data-analysis"
description = "Data analysis custom modules for ComfyUI - Use Pandas & Matplotlib within ComfyUI"
-version = "1.1.23"
+version = "1.1.24"
license = {file = "LICENSE"}
[project.urls]
diff --git a/tests/pandas_wrapper/pandas_melt_test.py b/tests/pandas_wrapper/pandas_melt_test.py
new file mode 100644
index 0000000..ba223a3
--- /dev/null
+++ b/tests/pandas_wrapper/pandas_melt_test.py
@@ -0,0 +1,125 @@
+import os
+import sys
+import unittest
+import pandas as pd
+
+PROJECT_ROOT = os.path.realpath(os.path.join(__file__, "..", "..", ".."))
+MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
+sys.path.append(MODULE_ROOT)
+
+from pandas_wrapper.pandas_melt import PandasMelt
+
+class TestPandasMelt(unittest.TestCase):
+ def setUp(self):
+ """Set up test data"""
+ self.node = PandasMelt()
+
+ self.df = pd.DataFrame({
+ "Manufacturer": ["Aimee Italian Foods", "Olivia Cuisine", "Maddie Foods"],
+ "Pizza": [10, 11, 12],
+ "Burger": [12, 8, 7]
+ })
+
+ def test_pandas_melt_single_column(self):
+ """Test melting a single column"""
+ result_df = self.node.f(
+ dataframe=self.df,
+ id_vars="Manufacturer",
+ value_vars="Pizza",
+ var_name="Item",
+ value_name="Price",
+ ignore_index=True
+ )[0] # Extract DataFrame
+
+ expected_df = pd.DataFrame({
+ "Manufacturer": ["Aimee Italian Foods", "Olivia Cuisine", "Maddie Foods"],
+ "Item": ["Pizza", "Pizza", "Pizza"],
+ "Price": [10, 11, 12]
+ })
+
+ pd.testing.assert_frame_equal(result_df, expected_df)
+
+ def test_pandas_melt_multiple_columns(self):
+ """Test melting multiple columns"""
+ result_df = self.node.f(
+ dataframe=self.df,
+ id_vars="Manufacturer",
+ value_vars="Pizza,Burger",
+ var_name="Item",
+ value_name="Price",
+ ignore_index=True
+ )[0] # Extract DataFrame
+
+ expected_df = pd.DataFrame({
+ "Manufacturer": [
+ "Aimee Italian Foods", "Olivia Cuisine", "Maddie Foods",
+ "Aimee Italian Foods", "Olivia Cuisine", "Maddie Foods"
+ ],
+ "Item": ["Pizza", "Pizza", "Pizza", "Burger", "Burger", "Burger"],
+ "Price": [10, 11, 12, 12, 8, 7]
+ })
+
+ pd.testing.assert_frame_equal(result_df, expected_df)
+
+ def test_pandas_melt_ignore_index_true(self):
+ """Test melting with ignore_index=True"""
+ # Modify the original DataFrame to have a custom index
+ self.df.index = pd.Index([10, 20, 30]) # Explicitly set a non-default index
+
+ result_df = self.node.f(
+ dataframe=self.df,
+ id_vars="Manufacturer",
+ value_vars="Pizza,Burger",
+ var_name="Item",
+ value_name="Price",
+ ignore_index=True
+ )[0] # Extract DataFrame
+
+ expected_df = pd.DataFrame({
+ "Manufacturer": [
+ "Aimee Italian Foods", "Olivia Cuisine", "Maddie Foods",
+ "Aimee Italian Foods", "Olivia Cuisine", "Maddie Foods"
+ ],
+ "Item": ["Pizza", "Pizza", "Pizza", "Burger", "Burger", "Burger"],
+ "Price": [10, 11, 12, 12, 8, 7]
+ })
+
+ # Check the data itself
+ pd.testing.assert_frame_equal(result_df, expected_df, check_index_type=False)
+
+ # Explicitly verify that the index is reset to a sequential range
+ expected_index = pd.RangeIndex(start=0, stop=len(expected_df), step=1)
+ self.assertTrue(result_df.index.equals(expected_index), "Index is not fully reset.")
+
+ def test_pandas_melt_ignore_index_false(self):
+ """Test melting with ignore_index=False"""
+ self.df.index = pd.Index([10, 20, 30]) # Explicitly set a non-default index
+
+ result_df = self.node.f(
+ dataframe=self.df,
+ id_vars="Manufacturer",
+ value_vars="Pizza,Burger",
+ var_name="Item",
+ value_name="Price",
+ ignore_index=False
+ )[0] # Extract DataFrame
+
+ expected_df = pd.DataFrame({
+ "Manufacturer": [
+ "Aimee Italian Foods", "Olivia Cuisine", "Maddie Foods",
+ "Aimee Italian Foods", "Olivia Cuisine", "Maddie Foods"
+ ],
+ "Item": ["Pizza", "Pizza", "Pizza", "Burger", "Burger", "Burger"],
+ "Price": [10, 11, 12, 12, 8, 7]
+ }, index=[10, 20, 30, 10, 20, 30]) # Correctly retain and repeat original index
+
+ # Compare both data and index
+ pd.testing.assert_frame_equal(result_df, expected_df)
+
+ # Explicitly verify that the index is correctly retained and repeated
+ expected_index = pd.Index([10, 20, 30, 10, 20, 30])
+ self.assertTrue(result_df.index.equals(expected_index), "Index was not retained correctly.")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/pandas_wrapper/utils_column_labels_string_to_list_test.py b/tests/pandas_wrapper/utils_column_labels_string_to_list_test.py
index e6b0099..36106c8 100644
--- a/tests/pandas_wrapper/utils_column_labels_string_to_list_test.py
+++ b/tests/pandas_wrapper/utils_column_labels_string_to_list_test.py
@@ -21,6 +21,8 @@ class TestColumnLabelsStringToList(unittest.TestCase):
20: [13, 14, 15] # Another integer column name
})
+ self.no_check_labels = ["X", "Y"]
+
def test_valid_column_strings(self):
"""Test valid column name conversion from string to list."""
self.assertEqual(column_labels_string_to_list(self.df, "A,B"), ["A", "B"])
@@ -60,6 +62,12 @@ class TestColumnLabelsStringToList(unittest.TestCase):
column_labels_string_to_list(self.df, "A,xyz")
self.assertIn("Column 'xyz' not found in the DataFrame.", str(context.exception))
+ def test_no_check_labels(self):
+ """Test no check labels"""
+ self.assertEquals(
+ column_labels_string_to_list(self.df, "X,Y", no_check=True),
+ self.no_check_labels)
+
if __name__ == "__main__":
unittest.main()
diff --git a/tests/pandas_wrapper/utils_flatten_list_tuple_unique_test.py b/tests/pandas_wrapper/utils_flatten_list_tuple_unique_test.py
new file mode 100644
index 0000000..495ee8e
--- /dev/null
+++ b/tests/pandas_wrapper/utils_flatten_list_tuple_unique_test.py
@@ -0,0 +1,36 @@
+import os
+import sys
+import unittest
+from typing import Any, List, Tuple, Union, Set
+
+PROJECT_ROOT = os.path.realpath(os.path.join(__file__, "..", "..", ".."))
+MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
+sys.path.append(MODULE_ROOT)
+
+from pandas_wrapper.utils import flatten_list_tuple_unique
+
+
+class TestFlattenListTupleUnique(unittest.TestCase):
+ def setUp(self):
+ self.test_cases = [
+ (["a", "b", "c"], {"a", "b", "c"}), # Strings only
+ ([1, 2, 3], {1, 2, 3}), # Integers only
+ (["a", 1], {"a", 1}), # Mixed types
+ (["a", ("b", "c", "d"), (1, 2, 3), ("b", "c", "e")], {"a", "b", "c", "d", 1, 2, 3, "e"}), # Nested tuples
+ ([("x", "y"), ["y", "z"]], {"x", "y", "z"}), # Nested lists and tuples
+ ([1, (2, [3, (4, 5)]), 6, (7, [8, 9])], {1, 2, 3, 4, 5, 6, 7, 8, 9}), # Deep nesting
+ ([], set()), # Empty list
+ ([()], set()), # Empty tuple inside a list
+ ([None, [None, None]], {None}), # None values
+ ([True, False, 1, 0], {True, False}), # Boolean values (Python treats True as 1 and False as 0)
+ ]
+
+ def test_flatten_list_tuple_unique(self):
+ for input_data, expected_output in self.test_cases:
+ with self.subTest(input_data=input_data):
+ self.assertEqual(flatten_list_tuple_unique(input_data), expected_output)
+
+
+if __name__ == "__main__":
+ unittest.main()
+