Added Py Kv Float/Int/String nodes. Added Pandas Create From Dict, Pandas Create From Multiple Dict, Pandas Create Series From Dict nodes.

This commit is contained in:
HowToSD
2025-02-24 15:52:50 -08:00
parent d6ea742f2e
commit 4a0d561d8f
43 changed files with 874 additions and 12 deletions
+18
View File
@@ -7,6 +7,9 @@ from .modules.cda.cda_show_float import CDAShowFloat
from .modules.cda.cda_show_int import CDAShowInt
from .modules.cda.cda_string_create import CDAStringCreate
from .modules.cda.cda_text_create import CDATextCreate
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.matplotlib.mpl_bar import MPLBar
from .modules.matplotlib.mpl_line import MPLLine
from .modules.matplotlib.mpl_scatter import MPLScatter
@@ -23,6 +26,9 @@ from .modules.pandas_wrapper.pandas_cos import PandasCos
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_multiple_dict import PandasCreateFromMultipleDict
from .modules.pandas_wrapper.pandas_create_series_from_dict import PandasCreateSeriesFromDict
from .modules.pandas_wrapper.pandas_create_with_index import PandasCreateWithIndex
from .modules.pandas_wrapper.pandas_cummax import PandasCummax
from .modules.pandas_wrapper.pandas_cummin import PandasCummin
@@ -141,6 +147,9 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PandasCount": PandasCount,
"PandasCov": PandasCov,
"PandasCreate": PandasCreate,
"PandasCreateFromDict": PandasCreateFromDict,
"PandasCreateFromMultipleDict": PandasCreateFromMultipleDict,
"PandasCreateSeriesFromDict": PandasCreateSeriesFromDict,
"PandasCreateWithIndex": PandasCreateWithIndex,
"PandasCummax": PandasCummax,
"PandasCummin": PandasCummin,
@@ -223,6 +232,9 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PandasVerticalConcat": PandasVerticalConcat,
"PandasVerticalSplit": PandasVerticalSplit,
"PandasXs": PandasXs,
"PyKvFloatCreate": PyKvFloatCreate,
"PyKvIntCreate": PyKvIntCreate,
"PyKvStringCreate": PyKvStringCreate,
"SNSBar": SNSBar,
"SNSHistogram": SNSHistogram,
"SNSLine": SNSLine,
@@ -259,6 +271,9 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PandasCount": "Pandas Count",
"PandasCov": "Pandas Cov",
"PandasCreate": "Pandas Create",
"PandasCreateFromDict": "Pandas Create From Dict",
"PandasCreateFromMultipleDict": "Pandas Create From Multiple Dict",
"PandasCreateSeriesFromDict": "Pandas Create Series From Dict",
"PandasCreateWithIndex": "Pandas Create With Index",
"PandasCummax": "Pandas Cummax",
"PandasCummin": "Pandas Cummin",
@@ -341,6 +356,9 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PandasVerticalConcat": "Pandas Vertical Concat",
"PandasVerticalSplit": "Pandas Vertical Split",
"PandasXs": "Pandas Xs",
"PyKvFloatCreate": "Py Kv Float Create",
"PyKvIntCreate": "Py Kv Int Create",
"PyKvStringCreate": "Py Kv String Create",
"SNSBar": "SNS Bar Chart",
"SNSHistogram": "SNS Histogram",
"SNSLine": "SNS Line Plot",
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
# Run this command on project root
# ```
# dev/scripts/run_tests.sh
# ````
export PYTHONPATH=modules:$PYTHONPATH
echo $PYTHONPATH
python -m unittest discover -s tests -p "*_test.py" -t .
+9
View File
@@ -92,6 +92,9 @@
| [CDA String Create](cda_string_create.md) | Creates a Python String using a value entered in a single-line text field. |
| [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 Multiple Dict](pandas_create_from_multiple_dict.md) | Creates a pandas DataFrame from multiple Python dictionaries. |
| [Pandas Create Series From Dict](pandas_create_series_from_dict.md) | Creates a Pandas Series from a Python dictionary. |
| [Pandas Create With Index](pandas_create_with_index.md) | Creates a pandas DataFrame using values entered in the text field. Input data is assumed to have an index. |
| [Pandas Load CSV](pandas_load_csv.md) | Loads CSV files into a pandas DataFrame. |
| [Pandas Load CSV With Encoding](pandas_load_csv_with_encoding.md) | Loads CSV files into a pandas DataFrame. |
@@ -118,6 +121,12 @@
| [SNS Histogram](sns_histogram.md) | Generates a histogram from a Pandas DataFrame using Seaborn. |
| [SNS Line Plot](sns_line.md) | Generates a line plot from a pandas DataFrame using Seaborn. |
| [SNS Scatter Plot](sns_scatter.md) | Generates a scatter plot from a pandas DataFrame using Seaborn. |
## Python wrapper
| Node | Description |
| --- | --- |
| [Py Kv Float Create](py_kv_float_create.md) | Creates a Python dictionary with a string key and a float value. |
| [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. |
## Summary statistics
| Node | Description |
| --- | --- |
+17
View File
@@ -0,0 +1,17 @@
# Pandas Create From Dict
Creates a pandas DataFrame from a Python dictionary.
## Input
| Name | Data type |
|---|---|
| data | Pydict |
## Output
| Data type |
|---|
| DataFrame |
<HR>
Category: IO
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
@@ -0,0 +1,32 @@
# Pandas Create From Multiple Dict
Creates a pandas DataFrame from multiple Python dictionaries.
Use this node to create a DataFrame from below nodes:
* Py Kv Float Create
* Py Kv Int Create
* Py Kv String Create
Each of above can be used to set a column name and the value for the column.
## Input
| Name | Data type |
|---|---|
| dict1 | Pydict |
| dict2 | Pydict |
| dict3 | Pydict |
| dict4 | Pydict |
| dict5 | Pydict |
| dict6 | Pydict |
| dict7 | Pydict |
| dict8 | Pydict |
| dict9 | Pydict |
| dict10 | Pydict |
## Output
| Data type |
|---|
| DataFrame |
<HR>
Category: IO
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
@@ -0,0 +1,17 @@
# Pandas Create Series From Dict
Creates a Pandas Series from a Python dictionary.
## Input
| Name | Data type |
|---|---|
| data | Pydict |
## Output
| Data type |
|---|
| Series |
<HR>
Category: IO
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+18
View File
@@ -0,0 +1,18 @@
# Py Kv Float Create
Creates a Python dictionary with a string key and a float value.
## Input
| Name | Data type |
|---|---|
| key | String |
| value | Float |
## Output
| Data type |
|---|
| Pydict |
<HR>
Category: Python wrapper
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+18
View File
@@ -0,0 +1,18 @@
# Py Kv Int Create
Creates a Python dictionary with an string key and an integer value.
## Input
| Name | Data type |
|---|---|
| key | String |
| value | Int |
## Output
| Data type |
|---|
| Pydict |
<HR>
Category: Python wrapper
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+18
View File
@@ -0,0 +1,18 @@
# Py Kv String Create
Creates a Python dictionary with a string key and a string value.
## Input
| Name | Data type |
|---|---|
| key | String |
| value | String |
## Output
| Data type |
|---|
| Pydict |
<HR>
Category: Python wrapper
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+1
View File
@@ -1,6 +1,7 @@
# SNS Histogram
Generates a histogram from a Pandas DataFrame using Seaborn.
Currently, plotting multiple series of data is not supported.
For style and palette parameters, refer to [SNS Line Plot documentation](sns_line.md).
## Output
+42
View File
@@ -0,0 +1,42 @@
from typing import Any, Dict
class PyKvFloatCreate:
"""
Creates a Python dictionary with a string key and a float value.
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": {
"key": ("STRING", {"multiline": False}),
"value": ("FLOAT", {"default": 0, "min": -2**63, "max": 2**63, "step": 0.00000001})
}
}
RETURN_TYPES: tuple = ("PYDICT",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, key: str, value: float) -> tuple:
"""
Creates a Python dictionary with a string key and a float value.
Args:
key (str): String key for the dictionary.
value (float): Float value for the dictionary.
Returns:
tuple: A tuple containing the dictionary.
"""
return ({key:value},)
+42
View File
@@ -0,0 +1,42 @@
from typing import Any, Dict
class PyKvIntCreate:
"""
Creates a Python dictionary with an string key and an integer value.
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": {
"key": ("STRING", {"multiline": False}),
"value": ("INT", {"default": 0, "min": -2**31, "max": 2**31})
}
}
RETURN_TYPES: tuple = ("PYDICT",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, key: str, value: int) -> tuple:
"""
Creates a Python dictionary with an string key and an integer value.
Args:
key (str): String key for the dictionary.
value (int): Integer value for the dictionary.
Returns:
tuple: A tuple containing the dictionary.
"""
return ({key:value},)
+41
View File
@@ -0,0 +1,41 @@
from typing import Any, Dict
class PyKvStringCreate:
"""
Creates a Python dictionary with a string key and a string value.
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": {
"key": ("STRING", {"multiline": False}),
"value": ("STRING", {"multiline": True})
}
}
RETURN_TYPES: tuple = ("PYDICT",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, key: str, value: float) -> tuple:
"""
Creates a Python dictionary with a string key and a string value.
Args:
key (str): String key for the dictionary.
value (str): String value for the dictionary.
Returns:
tuple: A tuple containing the dictionary.
"""
return ({key:value},)
+30
View File
@@ -0,0 +1,30 @@
from typing import Dict, Any
def python_dict_first_value_len(d: Dict[Any, Any]) -> int:
r"""
Computes the length of the first value in a dictionary.
- If the first value is a `list`, returns its length.
- If the first value is a `dict`, returns the number of keys in the dictionary.
- Otherwise, returns `1` for scalar values.
Args:
d (Dict[Any, Any]): A dictionary where the first value's length will be computed.
Returns:
int: The computed length based on the type of the first value.
Raises:
ValueError: If `d` is `None` or empty.
"""
if not d:
raise ValueError("Input dictionary is None or empty.")
first_value = next(iter(d.values())) # Get the first value
if isinstance(first_value, list):
return len(first_value)
elif isinstance(first_value, dict):
return len(first_value.keys()) # Return the number of keys in the dictionary
else:
return 1 # Scalar case (int, float, str, etc.)
@@ -0,0 +1,52 @@
import os
import sys
from typing import Any, Dict
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 PandasCreateFromDict:
"""
Creates a pandas DataFrame from a Python dictionary.
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", {}),
}
}
RETURN_TYPES: tuple = ("DATAFRAME",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, data: Dict) -> tuple:
"""
Creates a pandas DataFrame from a Python dictionary.
Args:
data (Dict): A Python dictionary.
Returns:
tuple: A tuple containing a the DataFrame.
"""
if not data:
df = pd.DataFrame() # Return an empty DataFrame if no data is provided.
else:
value_length = python_dict_first_value_len(data)
df = pd.DataFrame(data, index=list(range(value_length)))
return (df,)
@@ -0,0 +1,94 @@
import os
import sys
from typing import Any, Dict
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 PandasCreateFromMultipleDict:
"""
Creates a pandas DataFrame from multiple Python dictionaries.
Use this node to create a DataFrame from below nodes:
* Py Kv Float Create
* Py Kv Int Create
* Py Kv String Create
Each of above can be used to set a column name and the value for the column.
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": {
"dict1": ("PYDICT", {}),
"dict2": ("PYDICT", {}),
},
"optional":{
"dict3": ("PYDICT", {}),
"dict4": ("PYDICT", {}),
"dict5": ("PYDICT", {}),
"dict6": ("PYDICT", {}),
"dict7": ("PYDICT", {}),
"dict8": ("PYDICT", {}),
"dict9": ("PYDICT", {}),
"dict10": ("PYDICT", {})
}
}
RETURN_TYPES: tuple = ("DATAFRAME",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self,
dict1: Dict,
dict2: Dict,
dict3: Dict=None,
dict4: Dict=None,
dict5: Dict=None,
dict6: Dict=None,
dict7: Dict=None,
dict8: Dict=None,
dict9: Dict=None,
dict10: Dict=None
) -> tuple:
"""
Creates a pandas DataFrame from a Python dictionary.
Args:
dict1, 2, ... ,10 (Dict): A Python dictionary.
Returns:
tuple: A tuple containing a the DataFrame.
"""
dict_list = [d for d in (dict1, dict2, dict3, dict4, dict5, dict6, dict7, dict8, dict9, dict10) if d]
if dict_list[0] is None or dict_list[1] is None:
raise ValueError("Either or both of the first two dictionaries are None.")
if not dict_list[0] or not dict_list[1]:
# Return an empty DataFrame if an empty dict is provided for the first dictionary
# or the second dictionary.
df = pd.DataFrame()
else:
expected_length = python_dict_first_value_len(dict_list[0])
for d in dict_list[1:]:
if expected_length != python_dict_first_value_len(d):
raise ValueError("Length of the value elements are not the same.")
merged_dict = {}
for d in dict_list:
merged_dict.update(d)
df = pd.DataFrame(merged_dict, index=list(range(expected_length)))
return (df,)
@@ -0,0 +1,60 @@
import os
import sys
from typing import Dict, Any, Tuple
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 PandasCreateSeriesFromDict:
"""
Creates a Pandas Series from a Python dictionary.
category: IO
"""
@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": {
"data": ("PYDICT", {}),
}
}
RETURN_TYPES: tuple = ("PDSERIES",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, data: Dict[Any, Any]) -> Tuple[pd.Series]:
"""
Creates a Pandas Series from a Python dictionary.
Args:
data (Dict[Any, Any]): A dictionary with exactly one key-value pair.
Returns:
Tuple[pd.Series]: A tuple containing a Pandas Series.
Raises:
ValueError: If the dictionary does not have exactly one key-value pair,
or if the value is not a scalar.
"""
if not isinstance(data, dict):
raise ValueError("Input must be a dictionary.")
if len(data) == 0:
return (pd.Series(),)
else:
for v in iter(data.values()):
if isinstance(v, (list, dict, set, tuple)):
raise ValueError("Value must be a scalar.")
return (pd.Series(data),)
@@ -29,6 +29,10 @@ class PandasLoadCSV:
FUNCTION: str = "load_csv"
CATEGORY: str = "Data Analysis"
@classmethod
def IS_CHANGED(cls, **kw):
return float("NaN")
def load_csv(self, file_path: str) -> tuple:
"""
Loads a CSV file into a pandas DataFrame.
@@ -30,6 +30,10 @@ class PandasLoadCSVWithEncoding:
FUNCTION: str = "load_csv"
CATEGORY: str = "Data Analysis"
@classmethod
def IS_CHANGED(cls, **kw):
return float("NaN")
def load_csv(self, file_path: str, encoding: str) -> tuple:
"""
Loads a CSV file into a pandas DataFrame. Allows specifying the file encoding to support formats other than UTF-8.
@@ -31,6 +31,10 @@ class PandasLoadCSVWithIndex:
FUNCTION: str = "load_csv"
CATEGORY: str = "Data Analysis"
@classmethod
def IS_CHANGED(cls, **kw):
return float("NaN")
def load_csv(self, file_path: str, index_col: int=0) -> tuple:
"""
Loads a CSV file into a pandas DataFrame.
@@ -43,6 +43,10 @@ class PandasLoadHTML:
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
@classmethod
def IS_CHANGED(cls, **kw):
return float("NaN")
def f(self, file_path: str) -> tuple:
"""
Loads an HTML file into a pandas DataFrame.
@@ -29,6 +29,13 @@ class PandasLoadJSON:
FUNCTION: str = "load_json"
CATEGORY: str = "Data Analysis"
# Mark this class to always execute.
# Refer to
# https://docs.comfy.org/custom-nodes/backend/server_overview
@classmethod
def IS_CHANGED(cls, **kw):
return float("NaN")
def load_json(self, file_path: str) -> tuple:
"""
Loads a JSON file into a pandas DataFrame.
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "data-analysis"
description = "Data analysis custom modules for ComfyUI - Use Pandas & Matplotlib within ComfyUI"
version = "1.2.1"
version = "1.2.2"
license = {file = "LICENSE"}
[project.urls]
View File
View File
+27
View File
@@ -0,0 +1,27 @@
import os
import sys
import unittest
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.py_kv_float_create import PyKvFloatCreate
class TestPyKvFloatCreat(unittest.TestCase):
def setUp(self):
"""Set up test data."""
self.node = PyKvFloatCreate()
self.data = [
{"key": "a", "value": 1.2345678}
]
def test_create_key_value_pair(self):
"""Test key-value pair creation."""
retval = self.node.f(key=self.data[0]["key"], value=self.data[0]["value"])[0]
self.assertEqual({self.data[0]["key"]: self.data[0]["value"]}, retval)
if __name__ == "__main__":
unittest.main()
+28
View File
@@ -0,0 +1,28 @@
import os
import sys
import unittest
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.py_kv_int_create import PyKvIntCreate
class TestPyKvIntCreat(unittest.TestCase):
def setUp(self):
"""Set up test data."""
self.node = PyKvIntCreate()
self.data = [
{"key": "a", "value": 12345678}
]
def test_create_key_value_pair(self):
"""Test key-value pair creation."""
retval = self.node.f(key=self.data[0]["key"], value=self.data[0]["value"])[0]
self.assertEqual({self.data[0]["key"]: self.data[0]["value"]}, retval)
if __name__ == "__main__":
unittest.main()
+27
View File
@@ -0,0 +1,27 @@
import os
import sys
import unittest
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.py_kv_string_create import PyKvStringCreate
class TestPyKvStringCreat(unittest.TestCase):
def setUp(self):
"""Set up test data."""
self.node = PyKvStringCreate()
self.data = [
{"key": "a", "value": "apple"}
]
def test_create_key_value_pair(self):
"""Test key-value pair creation."""
retval = self.node.f(key=self.data[0]["key"], value=self.data[0]["value"])[0]
self.assertEqual({self.data[0]["key"]: self.data[0]["value"]}, retval)
if __name__ == "__main__":
unittest.main()
+40
View File
@@ -0,0 +1,40 @@
import os
import sys
import unittest
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 TestUtils(unittest.TestCase):
def test_python_dict_first_value_len(self):
"""Test python_dict_first_value_len with various input cases."""
test_cases = [
({"a": [1, 2, 3]}, 3, "List case"),
({"a": {"x": 1, "y": 2}}, 2, "Dict case"),
({"a": 42}, 1, "Integer case"),
({"a": 3.14}, 1, "Float case"),
({"a": "hello"}, 1, "String case"),
({"a": True}, 1, "Boolean case"),
({"a": None}, 1, "None as value case"),
({"a": [], "b": "extra"}, 0, "Empty list case"),
({"a": {}}, 0, "Empty dict case"),
]
for data, expected, desc in test_cases:
with self.subTest(msg=desc, data=data):
self.assertEqual(python_dict_first_value_len(data), expected)
# Test ValueError for None input
with self.assertRaises(ValueError, msg="None input case"):
python_dict_first_value_len(None)
# Test ValueError for empty dictionary
with self.assertRaises(ValueError, msg="Empty dictionary case"):
python_dict_first_value_len({})
if __name__ == "__main__":
unittest.main()
View File
@@ -0,0 +1,46 @@
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 import PandasCreateFromDict
class TestPandasCreateFromDict(unittest.TestCase):
def setUp(self):
"""Set up test data."""
self.node = PandasCreateFromDict()
self.test_cases = [
({"a": 12345}, pd.DataFrame({"a": [12345]}, index=[0])),
({"a": 12345.6}, pd.DataFrame({"a": [12345.6]}, index=[0])),
({"a": "apple"}, pd.DataFrame({"a": ["apple"]}, index=[0])),
({"a": [12345, 6789]}, pd.DataFrame({"a": [12345, 6789]}, index=[0, 1])),
({"a": [12345.6, 7890.1]}, pd.DataFrame({"a": [12345.6, 7890.1]}, index=[0, 1])),
({"a": ["apple", "banana"]}, pd.DataFrame({"a": ["apple", "banana"]}, index=[0, 1])),
({"a": 123, "b": 456}, pd.DataFrame({"a": [123], "b": [456]}, index=[0])),
({"a": 12.3, "b": 45.6}, pd.DataFrame({"a": [12.3], "b": [45.6]}, index=[0])),
({"a": "foo", "b": "bar"}, pd.DataFrame({"a": ["foo"], "b": ["bar"]}, index=[0])),
({"a": [1, 2], "b": [3, 4]}, pd.DataFrame({"a": [1, 2], "b": [3, 4]}, index=[0, 1])),
({"a": [1.1, 2.2], "b": [3.3, 4.4]}, pd.DataFrame({"a": [1.1, 2.2], "b": [3.3, 4.4]}, index=[0, 1])),
({"a": ["x", "y"], "b": ["z", "w"]}, pd.DataFrame({"a": ["x", "y"], "b": ["z", "w"]}, index=[0, 1]))
]
def test_create_dataframe(self):
"""Test DataFrame creation from various dictionary inputs."""
for data, expected_df in self.test_cases:
with self.subTest(data=data):
retval = self.node.f(data)[0]
# Test if DataFrame structure matches
self.assertTrue(isinstance(retval, pd.DataFrame))
# Test if index matches
self.assertTrue(retval.index.equals(expected_df.index), f"Index mismatch for input: {data}")
# Test if data matches
pd.testing.assert_frame_equal(retval, expected_df)
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,78 @@
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_multiple_dict import PandasCreateFromMultipleDict
class TestPandasCreateFromMultipleDict(unittest.TestCase):
def setUp(self):
"""Set up test data."""
self.node = PandasCreateFromMultipleDict()
# Valid test cases with at least two dictionaries
self.test_cases = [
({"a": [12345]}, {"b": [67890]}, pd.DataFrame({"a": [12345], "b": [67890]}, index=[0])),
({"a": [12345.6]}, {"b": [7890.1]}, pd.DataFrame({"a": [12345.6], "b": [7890.1]}, index=[0])),
({"a": ["apple"]}, {"b": ["banana"]}, pd.DataFrame({"a": ["apple"], "b": ["banana"]}, index=[0])),
({"a": [12345, 6789]}, {"b": [1111, 2222]}, pd.DataFrame({"a": [12345, 6789], "b": [1111, 2222]}, index=[0, 1])),
({"a": [123, 456]}, {"b": [789, 101]}, pd.DataFrame({"a": [123, 456], "b": [789, 101]}, index=[0, 1])),
]
# Case where all 10 dictionaries are passed
self.all_dicts_case = (
{"a": [1, 2]}, {"b": [3, 4]}, {"c": [5, 6]}, {"d": [7, 8]}, {"e": [9, 10]},
{"f": [11, 12]}, {"g": [13, 14]}, {"h": [15, 16]}, {"i": [17, 18]}, {"j": [19, 20]},
pd.DataFrame({
"a": [1, 2], "b": [3, 4], "c": [5, 6], "d": [7, 8], "e": [9, 10],
"f": [11, 12], "g": [13, 14], "h": [15, 16], "i": [17, 18], "j": [19, 20]
}, index=[0, 1])
)
# Failure cases where first dictionary's value length differs from second's
self.length_mismatch_cases = [
({"a": [1, 2]}, {"b": [3]}, "Length of the value elements are not the same."),
({"x": [10]}, {"y": [20, 30]}, "Length of the value elements are not the same."),
]
def test_create_dataframe(self):
"""Test DataFrame creation from valid dictionary inputs."""
for data1, data2, expected_df in self.test_cases:
with self.subTest(data1=data1, data2=data2):
retval = self.node.f(data1, data2)[0] # Pass at least two dictionaries
# Test if DataFrame structure matches
self.assertTrue(isinstance(retval, pd.DataFrame))
# Test if index matches
self.assertTrue(retval.index.equals(expected_df.index), f"Index mismatch for input: {data1}, {data2}")
# Test if data matches
pd.testing.assert_frame_equal(retval, expected_df)
def test_all_dicts(self):
"""Test case where all 10 dictionaries are provided."""
data1, data2, data3, data4, data5, data6, data7, data8, data9, data10, expected_df = self.all_dicts_case
retval = self.node.f(data1, data2, data3, data4, data5, data6, data7, data8, data9, data10)[0]
pd.testing.assert_frame_equal(retval, expected_df)
def test_no_dict_failure(self):
"""Test failure when no dictionaries are passed."""
with self.assertRaises(TypeError):
self.node.f() # No arguments should raise TypeError
def test_length_mismatch_failure(self):
"""Test failure when first dictionary's value length differs from second's."""
for data1, data2, error_message in self.length_mismatch_cases:
with self.subTest(data1=data1, data2=data2):
with self.assertRaises(ValueError) as context:
self.node.f(data1, data2)
self.assertEqual(str(context.exception), error_message)
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,76 @@
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_series_from_dict import PandasCreateSeriesFromDict
class TestPandasCreateSeriesFromDict(unittest.TestCase):
def setUp(self):
"""Set up test data."""
self.node = PandasCreateSeriesFromDict()
self.valid_test_cases = [
# Empty dict
({}, pd.Series({})),
# Single key-value pairs
({"a": 12345}, pd.Series({"a": 12345})),
({"a": 12345.6}, pd.Series({"a": 12345.6})),
({"a": "apple"}, pd.Series({"a": "apple"})),
({"a": True}, pd.Series({"a": True})),
({"a": None}, pd.Series({"a": None})), # None should be treated as a valid value
# Multiple key-value pairs
({"a": 123, "b": 456}, pd.Series({"a": 123, "b": 456})),
({"x": 1.5, "y": 3.2, "z": -7.8}, pd.Series({"x": 1.5, "y": 3.2, "z": -7.8})),
({"name": "Alice", "age": 30, "city": "NYC"}, pd.Series({"name": "Alice", "age": 30, "city": "NYC"})),
({"flag": False, "score": 99, "pi": 3.14159}, pd.Series({"flag": False, "score": 99, "pi": 3.14159})),
]
self.invalid_test_cases = [
# Non-dictionary inputs
None,
[],
(),
"string",
1234,
56.78,
{1, 2, 3}, # Set
[{"a": 1}], # List containing a dict
# Dictionaries with non-scalar values
{"a": [12345, 6789]}, # List value
{"a": {"key": "value"}}, # Dict value
{"a": (1, 2)}, # Tuple value
{"a": {1, 2}}, # Set value
# Mixed valid and invalid values
{"a": 123, "b": [456, 789]}, # One scalar, one list
{"x": 1.5, "y": {"nested": "dict"}}, # One scalar, one dict
{"flag": False, "data": (3, 4)}, # One scalar, one tuple
]
def test_create_series(self):
"""Test Series creation from dictionaries with multiple key-value pairs."""
for data, expected_series in self.valid_test_cases:
with self.subTest(data=data):
retval = self.node.f(data)[0]
# Test if output is a Pandas Series
self.assertTrue(isinstance(retval, pd.Series), f"Expected Series, got {type(retval)}")
# Test if Series matches expected output
pd.testing.assert_series_equal(retval, expected_series)
def test_invalid_inputs(self):
"""Test that invalid inputs raise ValueError."""
for data in self.invalid_test_cases:
with self.subTest(data=data):
with self.assertRaises(ValueError):
self.node.f(data)
if __name__ == "__main__":
unittest.main()
@@ -3,7 +3,7 @@ import sys
import unittest
import pandas as pd
PROJECT_ROOT = os.path.realpath(os.path.join(__file__, "..", "..", ".."))
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)
+1 -1
View File
@@ -3,7 +3,7 @@ import sys
import unittest
import pandas as pd
PROJECT_ROOT = os.path.realpath(os.path.join(__file__, "..", "..", ".."))
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)
+1 -1
View File
@@ -3,7 +3,7 @@ import sys
import unittest
import pandas as pd
PROJECT_ROOT = os.path.realpath(os.path.join(__file__, "..", "..", ".."))
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)
@@ -4,7 +4,7 @@ import unittest
import pandas as pd
import pandas.api.types as ptypes
PROJECT_ROOT = os.path.realpath(os.path.join(__file__, "..", "..", ".."))
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)
print(MODULE_ROOT)
@@ -4,7 +4,7 @@ import unittest
import pandas as pd
import pandas.api.types as ptypes
PROJECT_ROOT = os.path.realpath(os.path.join(__file__, "..", "..", ".."))
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)
print(MODULE_ROOT)
@@ -4,7 +4,7 @@ import unittest
import pandas as pd
from typing import List, Union
PROJECT_ROOT = os.path.realpath(os.path.join(__file__, "..", "..", ".."))
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)
@@ -3,7 +3,7 @@ import sys
import unittest
from typing import List
PROJECT_ROOT = os.path.realpath(os.path.join(__file__, "..", "..", ".."))
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)
@@ -3,7 +3,7 @@ import sys
import unittest
from typing import Any, List, Tuple, Union, Set
PROJECT_ROOT = os.path.realpath(os.path.join(__file__, "..", "..", ".."))
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)
@@ -5,7 +5,7 @@ import json
import numpy as np
import pandas as pd
PROJECT_ROOT = os.path.realpath(os.path.join(__file__, "..", "..", ".."))
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)
print(MODULE_ROOT)
@@ -5,10 +5,9 @@ import json
import numpy as np
import pandas as pd
PROJECT_ROOT = os.path.realpath(os.path.join(__file__, "..", "..", ".."))
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)
print(MODULE_ROOT)
from pandas_wrapper.utils import series_to_jsons
from pandas_wrapper.utils import jsons_to_series