Added Pandas Create from Tensor, Pandas Rename Advanced nodes. Added an option to reindex the column in Pandas Horizontal Concat node.
This commit is contained in:
@@ -34,6 +34,7 @@ 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_from_numpy import PandasCreateFromNumpy
|
||||
from .modules.pandas_wrapper.pandas_create_from_tensor import PandasCreateFromTensor
|
||||
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_crosstab import PandasCrosstab
|
||||
@@ -88,6 +89,7 @@ from .modules.pandas_wrapper.pandas_pow_scalar_float import PandasPowScalarFloat
|
||||
from .modules.pandas_wrapper.pandas_pow_scalar_int import PandasPowScalarInt
|
||||
from .modules.pandas_wrapper.pandas_pow_series import PandasPowSeries
|
||||
from .modules.pandas_wrapper.pandas_rename import PandasRename
|
||||
from .modules.pandas_wrapper.pandas_rename_advanced import PandasRenameAdvanced
|
||||
from .modules.pandas_wrapper.pandas_replace import PandasReplace
|
||||
from .modules.pandas_wrapper.pandas_save_csv import PandasSaveCSV
|
||||
from .modules.pandas_wrapper.pandas_save_json import PandasSaveJSON
|
||||
@@ -254,6 +256,7 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
|
||||
"PandasCreateFromDict": PandasCreateFromDict,
|
||||
"PandasCreateFromMultipleDict": PandasCreateFromMultipleDict,
|
||||
"PandasCreateFromNumpy": PandasCreateFromNumpy,
|
||||
"PandasCreateFromTensor": PandasCreateFromTensor,
|
||||
"PandasCreateSeriesFromDict": PandasCreateSeriesFromDict,
|
||||
"PandasCreateWithIndex": PandasCreateWithIndex,
|
||||
"PandasCrosstab": PandasCrosstab,
|
||||
@@ -308,6 +311,7 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
|
||||
"PandasPowScalarInt": PandasPowScalarInt,
|
||||
"PandasPowSeries": PandasPowSeries,
|
||||
"PandasRename": PandasRename,
|
||||
"PandasRenameAdvanced": PandasRenameAdvanced,
|
||||
"PandasReplace": PandasReplace,
|
||||
"PandasSaveCSV": PandasSaveCSV,
|
||||
"PandasSaveJSON": PandasSaveJSON,
|
||||
@@ -477,6 +481,7 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
|
||||
"PandasCreateFromDict": "Pandas Create From Dict",
|
||||
"PandasCreateFromMultipleDict": "Pandas Create From Multiple Dict",
|
||||
"PandasCreateFromNumpy": "Pandas Create From Numpy",
|
||||
"PandasCreateFromTensor": "Pandas Create From Tensor",
|
||||
"PandasCreateSeriesFromDict": "Pandas Create Series From Dict",
|
||||
"PandasCreateWithIndex": "Pandas Create With Index",
|
||||
"PandasCrosstab": "Pandas Crosstab",
|
||||
@@ -531,6 +536,7 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
|
||||
"PandasPowScalarInt": "Pandas Pow Scalar Int",
|
||||
"PandasPowSeries": "Pandas Pow Series",
|
||||
"PandasRename": "Pandas Rename",
|
||||
"PandasRenameAdvanced": "Pandas Rename Advanced",
|
||||
"PandasReplace": "Pandas Replace",
|
||||
"PandasSaveCSV": "Pandas Save CSV",
|
||||
"PandasSaveJSON": "Pandas Save JSON",
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
| [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 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. |
|
||||
| [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. |
|
||||
@@ -297,6 +298,7 @@
|
||||
| [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 Rename Advanced](pandas_rename_advanced.md) | Renames one or more indices (row labels) or column labels to new labels. |
|
||||
| [Pandas Series To Data Frame](pandas_series_to_dataframe.md) | Converts a Pandas Series to a DataFrame. |
|
||||
| [Pandas Set Index](pandas_set_index.md) | Create index (row label) from existing column or columns. |
|
||||
| [Pandas Sort](pandas_sort.md) | Sorts a pandas DataFrame by a specified column. |
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# Pandas Create From Tensor
|
||||
Creates a pandas DataFrame from a PyTorch tensor.
|
||||
|
||||
## Input
|
||||
| Name | Data type |
|
||||
|---|---|
|
||||
| tens | Tensor |
|
||||
|
||||
## Output
|
||||
| Data type |
|
||||
|---|
|
||||
| DataFrame |
|
||||
|
||||
<HR>
|
||||
Category: IO
|
||||
|
||||
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
|
||||
@@ -1,11 +1,14 @@
|
||||
# Pandas Horizontal Concat
|
||||
Horizontally concatenates two pandas DataFrames.
|
||||
|
||||
If reset labels is set to True, column labels are reset to a sequential integer index.
|
||||
|
||||
## Input
|
||||
| Name | Data type |
|
||||
|---|---|
|
||||
| a_dataframe | DataFrame |
|
||||
| b_dataframe | DataFrame |
|
||||
| reset_labels | Boolean |
|
||||
|
||||
## Output
|
||||
| Data type |
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Pandas Rename Advanced
|
||||
Renames one or more indices (row labels) or column labels to new labels.
|
||||
|
||||
Specify the replacement in a Python dictionary format:
|
||||
```
|
||||
{
|
||||
"old_foo": "new_bar",
|
||||
0: 5
|
||||
}
|
||||
```
|
||||
A string label has to be double-quoted.
|
||||
|
||||
## Input
|
||||
| Name | Data type |
|
||||
|---|---|
|
||||
| dataframe | DataFrame |
|
||||
| axis | |
|
||||
| replacement_pairs | String |
|
||||
|
||||
## Output
|
||||
| Data type |
|
||||
|---|
|
||||
| DataFrame |
|
||||
|
||||
<HR>
|
||||
Category: Transformation
|
||||
|
||||
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
|
||||
@@ -4,6 +4,7 @@ from io import StringIO
|
||||
import pandas as pd
|
||||
from .common import common_input_types
|
||||
from .util import plot_post_steps
|
||||
from ..pandas_wrapper.utils import column_label_string_to_target_type
|
||||
|
||||
class MPLBar:
|
||||
"""
|
||||
@@ -56,6 +57,8 @@ class MPLBar:
|
||||
|
||||
# Create the plot
|
||||
fig, ax = plt.subplots()
|
||||
x_column_name = column_label_string_to_target_type(df, x_column_name)
|
||||
y_column_name = column_label_string_to_target_type(df, y_column_name)
|
||||
ax.bar(df[x_column_name], df[y_column_name])
|
||||
|
||||
return plot_post_steps(
|
||||
|
||||
@@ -4,6 +4,7 @@ from io import StringIO
|
||||
import pandas as pd
|
||||
from .common import common_input_types
|
||||
from .util import plot_post_steps
|
||||
from ..pandas_wrapper.utils import column_label_string_to_target_type
|
||||
|
||||
class MPLLine:
|
||||
"""
|
||||
@@ -53,6 +54,8 @@ class MPLLine:
|
||||
"""
|
||||
|
||||
df = dataframe
|
||||
x_column_name = column_label_string_to_target_type(df, x_column_name)
|
||||
y_column_name = column_label_string_to_target_type(df, y_column_name)
|
||||
|
||||
# Create the plot
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
@@ -2,6 +2,7 @@ from typing import Any, Dict
|
||||
from matplotlib import pyplot as plt
|
||||
import pandas as pd
|
||||
from .util import plot_post_steps
|
||||
from ..pandas_wrapper.utils import column_label_string_to_target_type
|
||||
|
||||
class MPLPieChart:
|
||||
"""
|
||||
@@ -54,6 +55,8 @@ class MPLPieChart:
|
||||
"""
|
||||
|
||||
df = dataframe
|
||||
label_column_name = column_label_string_to_target_type(df, label_column_name)
|
||||
data_column_name = column_label_string_to_target_type(df, data_column_name)
|
||||
|
||||
# Create the plot
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
@@ -4,6 +4,7 @@ from io import StringIO
|
||||
import pandas as pd
|
||||
from .common import common_input_types
|
||||
from .util import plot_post_steps
|
||||
from ..pandas_wrapper.utils import column_label_string_to_target_type
|
||||
|
||||
class MPLScatter:
|
||||
"""
|
||||
@@ -53,6 +54,8 @@ class MPLScatter:
|
||||
"""
|
||||
|
||||
df = dataframe
|
||||
x_column_name = column_label_string_to_target_type(df, x_column_name)
|
||||
y_column_name = column_label_string_to_target_type(df, y_column_name)
|
||||
|
||||
# Create the plot
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
from typing import Any, Dict
|
||||
import pandas as pd
|
||||
import torch
|
||||
|
||||
|
||||
class PandasCreateFromTensor:
|
||||
"""
|
||||
Creates a pandas DataFrame from a PyTorch tensor.
|
||||
|
||||
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": {
|
||||
"tens": ("TENSOR", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES: tuple = ("DATAFRAME",)
|
||||
FUNCTION: str = "f"
|
||||
CATEGORY: str = "Data Analysis"
|
||||
|
||||
def f(self, tens: torch.Tensor) -> tuple:
|
||||
"""
|
||||
Creates a pandas DataFrame from a PyTorch tensor.
|
||||
|
||||
Args:
|
||||
tens (torch.Tensor): A PyTorch tensor.
|
||||
|
||||
Returns:
|
||||
tuple: A tuple containing the DataFrame.
|
||||
|
||||
Raises:
|
||||
ValueError: If the tensor is not rank 1 or rank 2.
|
||||
"""
|
||||
if tens.dim() not in [1, 2]:
|
||||
raise ValueError("Only rank 1 and rank 2 tensors are supported.")
|
||||
|
||||
return (pd.DataFrame(tens.numpy()),)
|
||||
|
||||
@@ -5,6 +5,8 @@ import pandas as pd
|
||||
class PandasHorizontalConcat:
|
||||
"""
|
||||
Horizontally concatenates two pandas DataFrames.
|
||||
|
||||
If reset labels is set to True, column labels are reset to a sequential integer index.
|
||||
|
||||
category: Transformation
|
||||
"""
|
||||
@@ -20,7 +22,8 @@ class PandasHorizontalConcat:
|
||||
return {
|
||||
"required": {
|
||||
"a_dataframe": ("DATAFRAME", {}),
|
||||
"b_dataframe": ("DATAFRAME", {})
|
||||
"b_dataframe": ("DATAFRAME", {}),
|
||||
"reset_labels": ("BOOLEAN", {"default": False})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,16 +31,23 @@ class PandasHorizontalConcat:
|
||||
FUNCTION: str = "f"
|
||||
CATEGORY: str = "Data Analysis"
|
||||
|
||||
def f(self, a_dataframe: pd.DataFrame, b_dataframe: pd.DataFrame) -> tuple:
|
||||
def f(self,
|
||||
a_dataframe: pd.DataFrame,
|
||||
b_dataframe: pd.DataFrame,
|
||||
reset_labels: bool) -> tuple:
|
||||
"""
|
||||
Horizontally concatenate two pandas DataFrames.
|
||||
|
||||
Args:
|
||||
a_dataframe (DataFrame): The first DataFrame.
|
||||
b_dataframe (DataFrame): The second DataFrame.
|
||||
reset_labels (bool): If reset labels is set to True, column labels are reset to a sequential integer index.
|
||||
|
||||
Returns:
|
||||
tuple: A tuple containing the concatenated DataFrame.
|
||||
"""
|
||||
df_out = pd.concat([a_dataframe, b_dataframe], axis=1)
|
||||
if reset_labels:
|
||||
df_out.columns = range(df_out.shape[1])
|
||||
|
||||
return (df_out,)
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
from typing import Any, Dict
|
||||
import pandas as pd
|
||||
import ast
|
||||
|
||||
class PandasRenameAdvanced:
|
||||
"""
|
||||
Renames one or more indices (row labels) or column labels to new labels.
|
||||
|
||||
Specify the replacement in a Python dictionary format:
|
||||
```
|
||||
{
|
||||
"old_foo": "new_bar",
|
||||
0: 5
|
||||
}
|
||||
```
|
||||
A string label has to be double-quoted.
|
||||
|
||||
category: Transformation
|
||||
"""
|
||||
|
||||
@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": {
|
||||
"dataframe": ("DATAFRAME", {}),
|
||||
"axis": (("columns", "index"),),
|
||||
"replacement_pairs": ("STRING", {"default":"{}", "multiline": True})
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES: tuple = ("DATAFRAME",)
|
||||
FUNCTION: str = "f"
|
||||
CATEGORY: str = "Data Analysis"
|
||||
|
||||
def f(self, dataframe: pd.DataFrame, axis: str, replacement_pairs: str) -> tuple:
|
||||
"""
|
||||
Renames one or more indices (row labels) or column labels to new labels.
|
||||
|
||||
Args:
|
||||
dataframe (DataFrame): The DataFrame.
|
||||
axis (str): Index (row label) or column label.
|
||||
replacement_pairs (str): Replacement pairs specified in the Python dictionary format.
|
||||
|
||||
Returns:
|
||||
tuple: A tuple containing the DataFrame.
|
||||
"""
|
||||
try:
|
||||
d = ast.literal_eval(replacement_pairs)
|
||||
if not isinstance(d, dict):
|
||||
raise ValueError("replacement_pairs must be a dictionary.")
|
||||
|
||||
# Check that values in the dictionary are not lists, tuples, or dictionaries
|
||||
if any(isinstance(value, (list, tuple, dict)) for value in d.values()):
|
||||
raise ValueError("replacement_pairs values must not be lists, tuples, or dictionaries.")
|
||||
|
||||
except (SyntaxError, ValueError) as e:
|
||||
raise ValueError(f"Invalid replacement_pairs format: {e}")
|
||||
|
||||
if axis == "index":
|
||||
df_out = dataframe.rename(index=d)
|
||||
elif axis == "columns":
|
||||
df_out = dataframe.rename(columns=d)
|
||||
else:
|
||||
raise ValueError("Invalid axis. Only 'index' or 'columns' are supported.")
|
||||
|
||||
return (df_out,)
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Any, List, Tuple, Union, Set
|
||||
from typing import Any, List, Tuple, Union, Set, Optional
|
||||
import json
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
@@ -54,7 +54,7 @@ def column_labels_string_to_list(dataframe:pd.DataFrame, s:str, no_check:bool=Fa
|
||||
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
|
||||
no_check (bool): If True, input string is processed as a string without checking the column labels
|
||||
of the DataFrame.
|
||||
|
||||
Returns:
|
||||
@@ -84,6 +84,48 @@ def column_labels_string_to_list(dataframe:pd.DataFrame, s:str, no_check:bool=Fa
|
||||
return output_columns
|
||||
|
||||
|
||||
def column_label_string_to_target_type(dataframe: pd.DataFrame, label: str, return_none: bool=False) -> Optional[Union[str, int, float]]:
|
||||
"""
|
||||
Converts a string column label into the data type used for the corresponding label in the DataFrame.
|
||||
|
||||
If a column name does not exist in the DataFrame, a ValueError is raised.
|
||||
|
||||
Args:
|
||||
dataframe (pd.DataFrame): The DataFrame to validate column names against.
|
||||
label (str): A string representing a column label.
|
||||
return_none (bool): If True, if the label is not found, None is returned instead of raising an exception.
|
||||
Returns:
|
||||
Union[str, int, float]: The column label in the correct type used in the DataFrame.
|
||||
|
||||
Raises:
|
||||
ValueError: If `s` does not match any existing column name.
|
||||
"""
|
||||
valid_columns = flatten_list_tuple_unique(dataframe.columns)
|
||||
s = label.strip()
|
||||
|
||||
if s in valid_columns:
|
||||
return s
|
||||
|
||||
try:
|
||||
numeric_c = int(s)
|
||||
if numeric_c in valid_columns:
|
||||
return numeric_c
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
numeric_c = float(s)
|
||||
if numeric_c in valid_columns:
|
||||
return numeric_c
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if return_none:
|
||||
return None
|
||||
|
||||
raise ValueError(f"Column '{s}' not found in the DataFrame.")
|
||||
|
||||
|
||||
def series_to_jsons(series: pd.Series) -> str:
|
||||
"""
|
||||
Converts a pandas Series to a JSON string.
|
||||
|
||||
@@ -6,7 +6,7 @@ from .common import common_input_types
|
||||
from .util import plot_post_steps
|
||||
from .util import set_up_sns_style_palette
|
||||
from .util import process_y_columns_and_labels
|
||||
|
||||
from ..pandas_wrapper.utils import column_label_string_to_target_type
|
||||
|
||||
class SNSBar:
|
||||
"""
|
||||
@@ -63,6 +63,7 @@ class SNSBar:
|
||||
"""
|
||||
df = dataframe
|
||||
set_up_sns_style_palette(style, palette)
|
||||
x_column_name = column_label_string_to_target_type(df, x_column_name)
|
||||
y_columns, labels = process_y_columns_and_labels(df, y_column_name, legend_label)
|
||||
|
||||
# Create the plot
|
||||
|
||||
@@ -4,7 +4,7 @@ import pandas as pd
|
||||
import seaborn as sns
|
||||
from .util import plot_post_steps
|
||||
from .util import set_up_sns_style_palette
|
||||
|
||||
from ..pandas_wrapper.utils import column_label_string_to_target_type
|
||||
|
||||
class SNSBoxplot:
|
||||
"""
|
||||
@@ -81,6 +81,8 @@ class SNSBoxplot:
|
||||
"""
|
||||
df = dataframe
|
||||
set_up_sns_style_palette(style, palette)
|
||||
category_column_name = column_label_string_to_target_type(df, category_column_name, return_none=True)
|
||||
value_column_name = column_label_string_to_target_type(df, value_column_name)
|
||||
|
||||
# Create the plot
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
@@ -6,7 +6,7 @@ from .common import common_input_types
|
||||
from .util import plot_post_steps
|
||||
from .util import set_up_sns_style_palette
|
||||
from .util import process_y_columns_and_labels
|
||||
|
||||
from ..pandas_wrapper.utils import column_label_string_to_target_type
|
||||
|
||||
class SNSLine:
|
||||
"""
|
||||
@@ -83,6 +83,7 @@ class SNSLine:
|
||||
"""
|
||||
df = dataframe
|
||||
set_up_sns_style_palette(style, palette)
|
||||
x_column_name = column_label_string_to_target_type(df, x_column_name)
|
||||
y_columns, labels = process_y_columns_and_labels(df, y_column_name, legend_label)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
@@ -4,6 +4,7 @@ import pandas as pd
|
||||
import seaborn as sns
|
||||
from .util import plot_post_steps
|
||||
from .util import set_up_sns_style_palette
|
||||
from ..pandas_wrapper.utils import column_label_string_to_target_type
|
||||
|
||||
|
||||
class SNSPairplot:
|
||||
@@ -60,6 +61,7 @@ class SNSPairplot:
|
||||
set_up_sns_style_palette(style, palette)
|
||||
|
||||
# Create the plot
|
||||
hue_column_name = column_label_string_to_target_type(df, hue_column_name, return_none=True)
|
||||
if hue_column_name:
|
||||
pp = sns.pairplot(df, hue=hue_column_name)
|
||||
else:
|
||||
|
||||
@@ -6,7 +6,7 @@ from .common import common_input_types
|
||||
from .util import plot_post_steps
|
||||
from .util import set_up_sns_style_palette
|
||||
from .util import process_y_columns_and_labels
|
||||
|
||||
from ..pandas_wrapper.utils import column_label_string_to_target_type
|
||||
|
||||
class SNSScatter:
|
||||
"""
|
||||
@@ -63,6 +63,7 @@ class SNSScatter:
|
||||
"""
|
||||
df = dataframe
|
||||
set_up_sns_style_palette(style, palette)
|
||||
x_column_name = column_label_string_to_target_type(df, x_column_name)
|
||||
y_columns, labels = process_y_columns_and_labels(df, y_column_name, legend_label)
|
||||
|
||||
# Create the plot
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "data-analysis"
|
||||
description = "Data analysis custom modules for ComfyUI - Use Pandas & Matplotlib within ComfyUI"
|
||||
version = "1.4.5"
|
||||
version = "1.4.6"
|
||||
license = {file = "LICENSE"}
|
||||
|
||||
[project.urls]
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
import torch
|
||||
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_tensor import PandasCreateFromTensor
|
||||
|
||||
class TestPandasCreateFromTensor(unittest.TestCase):
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.node = PandasCreateFromTensor()
|
||||
|
||||
self.test_cases = [
|
||||
# Rank 0 (Invalid)
|
||||
(torch.tensor(42), ValueError),
|
||||
(torch.tensor(3.14), ValueError),
|
||||
(torch.tensor(True), ValueError),
|
||||
|
||||
# Rank 1 (Valid)
|
||||
(torch.tensor([1, 2, 3], dtype=torch.float32), pd.DataFrame([1, 2, 3], dtype="float32")),
|
||||
(torch.tensor([1.1, 2.2, 3.3], dtype=torch.float32), pd.DataFrame([1.1, 2.2, 3.3], dtype="float32")),
|
||||
(torch.tensor([True, False, True], dtype=torch.float32), pd.DataFrame([1.0, 0.0, 1.0], dtype="float32")),
|
||||
|
||||
# Rank 2 (Valid)
|
||||
(torch.tensor([[1, 2], [3, 4]], dtype=torch.float32), pd.DataFrame([[1, 2], [3, 4]], dtype="float32")),
|
||||
(torch.tensor([[1.1, 2.2], [3.3, 4.4]], dtype=torch.float32), pd.DataFrame([[1.1, 2.2], [3.3, 4.4]], dtype="float32")),
|
||||
|
||||
# Rank 3+ (Invalid)
|
||||
(torch.rand(2, 2, 2), ValueError),
|
||||
(torch.rand(2, 2, 2, 2), ValueError),
|
||||
]
|
||||
|
||||
def test_create_dataframe(self):
|
||||
"""Test DataFrame creation from various PyTorch tensors."""
|
||||
for data, expected in self.test_cases:
|
||||
with self.subTest(data=data):
|
||||
if isinstance(expected, type) and issubclass(expected, Exception):
|
||||
with self.assertRaises(expected):
|
||||
self.node.f(data)
|
||||
else:
|
||||
retval = self.node.f(data)[0]
|
||||
|
||||
# Test if DataFrame structure matches
|
||||
self.assertTrue(isinstance(retval, pd.DataFrame))
|
||||
|
||||
# Test if data matches
|
||||
pd.testing.assert_frame_equal(retval, expected)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,48 @@
|
||||
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_horizontal_concat import PandasHorizontalConcat
|
||||
|
||||
|
||||
class TestPandasHorizontalConcat(unittest.TestCase):
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.node = PandasHorizontalConcat()
|
||||
|
||||
# Sample DataFrames
|
||||
self.df1 = pd.DataFrame({"A": [1, 2], "B": [3, 4]})
|
||||
self.df2 = pd.DataFrame({"C": [5, 6], "D": [7, 8]})
|
||||
|
||||
def test_concat_without_reset_labels(self):
|
||||
"""Test horizontal concatenation without resetting column labels."""
|
||||
expected_df = pd.DataFrame({
|
||||
"A": [1, 2],
|
||||
"B": [3, 4],
|
||||
"C": [5, 6],
|
||||
"D": [7, 8]
|
||||
})
|
||||
|
||||
retval = self.node.f(self.df1, self.df2, reset_labels=False)[0]
|
||||
pd.testing.assert_frame_equal(retval, expected_df)
|
||||
|
||||
def test_concat_with_reset_labels(self):
|
||||
"""Test horizontal concatenation with resetting column labels."""
|
||||
expected_df = pd.DataFrame({
|
||||
0: [1, 2],
|
||||
1: [3, 4],
|
||||
2: [5, 6],
|
||||
3: [7, 8]
|
||||
})
|
||||
|
||||
retval = self.node.f(self.df1, self.df2, reset_labels=True)[0]
|
||||
pd.testing.assert_frame_equal(retval, expected_df)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,107 @@
|
||||
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_rename_advanced import PandasRenameAdvanced
|
||||
|
||||
class TestPandasRenameAdvanced(unittest.TestCase):
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.node = PandasRenameAdvanced()
|
||||
|
||||
# Sample DataFrame
|
||||
self.df = pd.DataFrame(
|
||||
{
|
||||
"A": [1, 2, 3],
|
||||
"B": [4, 5, 6],
|
||||
100: [7, 8, 9],
|
||||
},
|
||||
index=["row1", "row2", 10]
|
||||
)
|
||||
|
||||
def test_rename_columns_string(self):
|
||||
"""Test renaming string column labels."""
|
||||
replacement_pairs = '{"A": "X", "B": "Y"}'
|
||||
expected_df = pd.DataFrame(
|
||||
{
|
||||
"X": [1, 2, 3],
|
||||
"Y": [4, 5, 6],
|
||||
100: [7, 8, 9],
|
||||
},
|
||||
index=["row1", "row2", 10]
|
||||
)
|
||||
result_df = self.node.f(self.df, "columns", replacement_pairs)[0]
|
||||
pd.testing.assert_frame_equal(result_df, expected_df)
|
||||
|
||||
def test_rename_columns_integer(self):
|
||||
"""Test renaming integer column labels."""
|
||||
replacement_pairs = '{100: 200}'
|
||||
expected_df = pd.DataFrame(
|
||||
{
|
||||
"A": [1, 2, 3],
|
||||
"B": [4, 5, 6],
|
||||
200: [7, 8, 9],
|
||||
},
|
||||
index=["row1", "row2", 10]
|
||||
)
|
||||
result_df = self.node.f(self.df, "columns", replacement_pairs)[0]
|
||||
pd.testing.assert_frame_equal(result_df, expected_df)
|
||||
|
||||
def test_rename_index_string(self):
|
||||
"""Test renaming string row labels."""
|
||||
replacement_pairs = '{"row1": "new_row1", "row2": "new_row2"}'
|
||||
expected_df = pd.DataFrame(
|
||||
{
|
||||
"A": [1, 2, 3],
|
||||
"B": [4, 5, 6],
|
||||
100: [7, 8, 9],
|
||||
},
|
||||
index=["new_row1", "new_row2", 10]
|
||||
)
|
||||
result_df = self.node.f(self.df, "index", replacement_pairs)[0]
|
||||
pd.testing.assert_frame_equal(result_df, expected_df)
|
||||
|
||||
def test_rename_index_integer(self):
|
||||
"""Test renaming integer row labels."""
|
||||
replacement_pairs = '{10: 20}'
|
||||
expected_df = pd.DataFrame(
|
||||
{
|
||||
"A": [1, 2, 3],
|
||||
"B": [4, 5, 6],
|
||||
100: [7, 8, 9],
|
||||
},
|
||||
index=["row1", "row2", 20]
|
||||
)
|
||||
result_df = self.node.f(self.df, "index", replacement_pairs)[0]
|
||||
pd.testing.assert_frame_equal(result_df, expected_df)
|
||||
|
||||
def test_invalid_replacement_pairs_list(self):
|
||||
"""Test invalid replacement_pairs format."""
|
||||
invalid_pairs = '{"A": ["X", "Y"]}' # Not a valid mapping. List is not allowed.
|
||||
with self.assertRaises(ValueError):
|
||||
self.node.f(self.df, "columns", invalid_pairs)
|
||||
|
||||
def test_invalid_replacement_pairs_dict(self):
|
||||
"""Test invalid replacement_pairs format."""
|
||||
invalid_pairs = '{"A": {"X": "Y"}}'
|
||||
with self.assertRaises(ValueError):
|
||||
self.node.f(self.df, "columns", invalid_pairs)
|
||||
|
||||
def test_invalid_replacement_pairs_tuple(self):
|
||||
"""Test invalid replacement_pairs format."""
|
||||
invalid_pairs = '{"A": ()"X", "Y")}'
|
||||
with self.assertRaises(ValueError):
|
||||
self.node.f(self.df, "columns", invalid_pairs)
|
||||
|
||||
def test_invalid_axis(self):
|
||||
"""Test invalid axis value."""
|
||||
replacement_pairs = '{"A": "X"}'
|
||||
with self.assertRaises(ValueError):
|
||||
self.node.f(self.df, "invalid_axis", replacement_pairs)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,61 @@
|
||||
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.utils import column_label_string_to_target_type
|
||||
|
||||
class TestColumnLabelStringToTargetType(unittest.TestCase):
|
||||
def setUp(self):
|
||||
"""Set up test data."""
|
||||
self.df = pd.DataFrame({
|
||||
"A": [1, 2, 3],
|
||||
"B": [4, 5, 6],
|
||||
"C": [7, 8, 9],
|
||||
10: [10, 11, 12], # Integer column name
|
||||
20: [13, 14, 15], # Another integer column name
|
||||
3.5: [16, 17, 18] # Float column name
|
||||
})
|
||||
|
||||
def test_valid_string_column(self):
|
||||
"""Test valid string column names remain unchanged."""
|
||||
self.assertEqual(column_label_string_to_target_type(self.df, "A"), "A")
|
||||
self.assertEqual(column_label_string_to_target_type(self.df, "B"), "B")
|
||||
|
||||
def test_integer_column_labels(self):
|
||||
"""Test conversion when integer column names are referenced as strings."""
|
||||
self.assertEqual(column_label_string_to_target_type(self.df, "10"), 10)
|
||||
self.assertEqual(column_label_string_to_target_type(self.df, "20"), 20)
|
||||
|
||||
def test_float_column_label(self):
|
||||
"""Test conversion when float column names are referenced as strings."""
|
||||
self.assertEqual(column_label_string_to_target_type(self.df, "3.5"), 3.5)
|
||||
|
||||
def test_whitespace_handling(self):
|
||||
"""Test that spaces around column names are stripped before conversion."""
|
||||
self.assertEqual(column_label_string_to_target_type(self.df, " A "), "A")
|
||||
self.assertEqual(column_label_string_to_target_type(self.df, " 10 "), 10)
|
||||
self.assertEqual(column_label_string_to_target_type(self.df, " 3.5 "), 3.5)
|
||||
|
||||
def test_non_existent_column(self):
|
||||
"""Test behavior when a non-existent column is provided."""
|
||||
with self.assertRaises(ValueError) as context:
|
||||
column_label_string_to_target_type(self.df, "D")
|
||||
self.assertIn("Column 'D' not found in the DataFrame.", str(context.exception))
|
||||
|
||||
def test_non_existent_column_with_return_none(self):
|
||||
"""Test behavior when a non-existent column is provided."""
|
||||
retval = column_label_string_to_target_type(self.df, "D", return_none=True)
|
||||
self.assertTrue(retval is None)
|
||||
|
||||
def test_invalid_integer_conversion(self):
|
||||
"""Test that a string that can't be converted to an integer or float raises ValueError."""
|
||||
with self.assertRaises(ValueError) as context:
|
||||
column_label_string_to_target_type(self.df, "xyz")
|
||||
self.assertIn("Column 'xyz' not found in the DataFrame.", str(context.exception))
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user