Added SNS plot/chart nodes to use Seaborn for plot/chart moving forward.

This commit is contained in:
HowToSD
2025-02-23 21:59:54 -08:00
parent 41eb70b661
commit 213c861579
17 changed files with 524 additions and 7 deletions
+4 -4
View File
@@ -8,7 +8,7 @@ ComfyUI was designed to make Stable Diffusion accessible to non-engineers while
At the end of this page, you’ll find a tutorial on how to use this package to answer the MLB hits question.
I hope you enjoy using it and have a great time exploring data.
I hope you find it useful and engaging for data exploration.
— Hide Inada
@@ -36,7 +36,7 @@ Here are just some of the included nodes. For a complete list of nodes, check ou
You can connect these nodes to conduct complex analysis in an intuitive graphical way.
You can also use other Comfy custom nodes to fit your needs (e.g., combining images).
These custom nodes aim to make data exploration and analysis more efficient and enjoyable.
These custom nodes enhance efficiency and usability in data exploration and analysis.
## Installation
Note: GPU is not required to run this custom module.
@@ -58,7 +58,7 @@ Note: GPU is not required to run this custom module.
```
This will create a new subdirectory ComfyUI-Data-Analysis.
3. Rename the folder name from ComfyUI-Data-Analysis to data-analysis.
If you skip this, example workflows will not work as those flows use this folder name.
Skipping this step will cause example workflows to fail, as they rely on this folder name.
4. Check if your ComfyUI environment already has pandas and matplotlib. If not, install them using pip.
```
pip install -r requirements.txt
@@ -75,7 +75,7 @@ Refer to [User's Guide](docs/ug.md) or check out [Node Reference](docs/reference
For a practical example, see the tutorial on baseball data: [How to Find Out Who Had the Most Hits](docs/baseball_example1.md).
## Update
Please refer to the release notes. I try to avoid backward-incompatible changes as much as possible, but sometimes they are unavoidable because the project is still in its early stage and I discover issues along the way. The release notes contain those changes so that you know what to expect before upgrading your version.
Refer to the release notes. While backward-incompatible changes are minimized, some may be necessary. The release notes contain those changes so that you know what to expect before upgrading your version.
## Contribution
🚫 **This project does not accept pull requests.**
+9
View File
@@ -105,6 +105,9 @@ from .modules.pandas_wrapper.pandas_var import PandasVar
from .modules.pandas_wrapper.pandas_vertical_concat import PandasVerticalConcat
from .modules.pandas_wrapper.pandas_vertical_split import PandasVerticalSplit
from .modules.pandas_wrapper.pandas_xs import PandasXs
from .modules.seaborn_wrapper.sns_bar import SNSBar
from .modules.seaborn_wrapper.sns_line import SNSLine
from .modules.seaborn_wrapper.sns_scatter import SNSScatter
T = TypeVar("T")
@@ -219,6 +222,9 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PandasVerticalConcat": PandasVerticalConcat,
"PandasVerticalSplit": PandasVerticalSplit,
"PandasXs": PandasXs,
"SNSBar": SNSBar,
"SNSLine": SNSLine,
"SNSScatter": SNSScatter,
}
@@ -333,6 +339,9 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PandasVerticalConcat": "Pandas Vertical Concat",
"PandasVerticalSplit": "Pandas Vertical Split",
"PandasXs": "Pandas Xs",
"SNSBar": "SNS Bar Chart",
"SNSLine": "SNS Line Plot",
"SNSScatter": "SNS Scatter Plot",
}
+5 -1
View File
@@ -76,12 +76,16 @@ def format_display_name(class_name):
("CD A", "CDA"),
("H T M L", "HTML"),
("MP L", "MPL"),
("SN S", "SNS"),
(" N A", " NA"),
("Data Frame", "DataFrame"),
("Multi Index", "MultiIndex"),
("MPL Bar", "MPL Bar Chart"),
("MPL Line", "MPL Line Plot"),
("MPL Scatter", "MPL Scatter Plot")
("MPL Scatter", "MPL Scatter Plot"),
("SNS Bar", "SNS Bar Chart"),
("SNS Line", "SNS Line Plot"),
("SNS Scatter", "SNS Scatter Plot")
]
for p in replace_pairs:
final_result = final_result.replace(p[0], p[1])
Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

+3
View File
@@ -114,6 +114,9 @@
| [MPL Bar Chart](mpl_bar.md) | Generates a bar chart from a Pandas DataFrame. |
| [MPL Line Plot](mpl_line.md) | Generates a line plot from a pandas DataFrame. |
| [MPL Scatter Plot](mpl_scatter.md) | Generates a scatter plot from a pandas DataFrame. |
| [SNS Bar Chart](sns_bar.md) | Generates a bar chart 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. |
## Summary statistics
| Node | Description |
| --- | --- |
+14
View File
@@ -0,0 +1,14 @@
# SNS Bar Chart
Generates a bar chart from a Pandas DataFrame using Seaborn.
For style and palette parameters, refer to [SNS Line Plot documentation](sns_line.md).
## Output
| Data type |
|---|
| Image |
<HR>
Category: Plot
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+33
View File
@@ -0,0 +1,33 @@
# SNS Line Plot
Generates a line plot from a pandas DataFrame using Seaborn.
For plotting multiple lines, enter a comma-separated column labels in y_column_name.
If you want to display a legend for each line, enter a comma-separated legend label
in legend label.
![SNS Line Plot](../images/sns_line.png)
The `style` parameter can be chosen from:
* darkgrid (default) – Grid with a dark background.
* whitegrid – Grid with a white background.
* dark – Dark background, no grid.
* white – White background, no grid.
* ticks – White background with tick marks.
The `palette` parameter can be chosen from:
* deep (default) – High contrast, good for multiple categories.
* muted – Softer colors, easier on the eyes.
* pastel – Light colors, suitable for minimalistic styles.
* bright – Vibrant and highly saturated colors.
* dark – Darker, high-contrast colors.
* colorblind – Optimized for color vision deficiencies.
## Output
| Data type |
|---|
| Image |
<HR>
Category: Plot
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+14
View File
@@ -0,0 +1,14 @@
# SNS Scatter Plot
Generates a scatter plot from a pandas DataFrame using Seaborn.
For style and palette parameters, refer to [SNS Line Plot documentation](sns_line.md).
## Output
| Data type |
|---|
| Image |
<HR>
Category: Plot
ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
+15
View File
@@ -27,6 +27,21 @@ def flatten_list_tuple_unique(lst: Union[List[Any], Tuple[Any, ...]]) -> Set[Any
recurse(lst)
return result
def comma_separated_labels_string_to_list(s:str) -> List[str]:
"""
Converts a comma-separated labels string into a list of string labels.
Args:
s (str): A comma-separated labels.
Returns:
List[str]: A list of valid labels.
"""
if s is None or s == "":
return []
label_list = [e.strip() for e in s.split(",")]
return label_list
def column_labels_string_to_list(dataframe:pd.DataFrame, s:str, no_check:bool=False) -> List[Union[str, int]]:
"""
+21
View File
@@ -0,0 +1,21 @@
def common_input_types():
"""
Defines the input types for a plot function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"dataframe": ("DATAFRAME", {}),
"x_column_name": ("STRING", {"default": ""}),
"y_column_name": ("STRING", {"default": ""}),
"title": ("STRING", {"default": ""}),
"x_axis_label": ("STRING", {"default": ""}),
"y_axis_label": ("STRING", {"default": ""}),
"legend_label": ("STRING", {"default": ""}),
"x_tick_as_int": ("BOOLEAN", {"default": False}), # To show 2002 instead of 2002.5 on x-axis
"style": ("STRING", {"default": ""}),
"palette": ("STRING", {"default": ""})
}
}
+80
View File
@@ -0,0 +1,80 @@
from typing import Any, Dict
from matplotlib import pyplot as plt
import pandas as pd
import seaborn as sns
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
class SNSBar:
"""
SNS Bar Chart:
Generates a bar chart from a Pandas DataFrame using Seaborn.
For style and palette parameters, refer to [SNS Line Plot documentation](sns_line.md).
category: Plot
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the `bar_chart` function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return common_input_types()
RETURN_TYPES: tuple = ("IMAGE",)
FUNCTION: str = "bar_chart"
CATEGORY: str = "Data Analysis"
def bar_chart(self,
dataframe: pd.DataFrame,
x_column_name: str,
y_column_name: str,
title: str,
x_axis_label: str,
y_axis_label: str,
legend_label: str,
x_tick_as_int: bool,
style: str=None,
palette: str=None) -> tuple:
"""
Generates a bar chart from a pandas DataFrame.
Args:
dataframe (DataFrame): The DataFrame.
x_column_name (str): The column name for the x-axis.
y_column_name (str): The column name for the y-axis.
title (str): The title of the plot.
x_axis_label (str): The label for the x-axis data.
y_axis_label (str): The label for the y-axis data.
x_tick_as_int (bool): If set, use integers for x ticks. For example,
if ticks show floating point numbers for years, e.g. 2002.5,
setting this flag will show integer values instead.
style (str): Style of the plot
palette (str): Palette of the plot
Returns:
tuple: A tuple containing the image tensor representation of the plot.
"""
df = dataframe
set_up_sns_style_palette(style, palette)
y_columns, labels = process_y_columns_and_labels(df, y_column_name, legend_label)
# Create the plot
fig, ax = plt.subplots()
for i in range(len(y_columns)):
sns.barplot(x=df[x_column_name], y=df[y_columns[i]], ax=ax, label=labels[i])
return plot_post_steps(
fig,
ax,
plt,
title,
x_axis_label,
y_axis_label,
x_tick_as_int)
+99
View File
@@ -0,0 +1,99 @@
from typing import Any, Dict
from matplotlib import pyplot as plt
import seaborn as sns
import pandas as pd
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
class SNSLine:
"""
SNS Line Plot:
Generates a line plot from a pandas DataFrame using Seaborn.
For plotting multiple lines, enter a comma-separated column labels in y_column_name.
If you want to display a legend for each line, enter a comma-separated legend label
in legend label.
![SNS Line Plot](../images/sns_line.png)
The `style` parameter can be chosen from:
* darkgrid (default) – Grid with a dark background.
* whitegrid – Grid with a white background.
* dark – Dark background, no grid.
* white – White background, no grid.
* ticks – White background with tick marks.
The `palette` parameter can be chosen from:
* deep (default) – High contrast, good for multiple categories.
* muted – Softer colors, easier on the eyes.
* pastel – Light colors, suitable for minimalistic styles.
* bright – Vibrant and highly saturated colors.
* dark – Darker, high-contrast colors.
* colorblind – Optimized for color vision deficiencies.
category: Plot
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the `line_plot` function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return common_input_types()
RETURN_TYPES: tuple = ("IMAGE",)
FUNCTION: str = "line_plot"
CATEGORY: str = "Data Analysis"
def line_plot(self,
dataframe: pd.DataFrame,
x_column_name: str,
y_column_name: str,
title: str,
x_axis_label: str,
y_axis_label: str,
legend_label: str,
x_tick_as_int: bool,
style: str=None,
palette: str=None) -> tuple:
"""
Generates a line plot from a pandas DataFrame.
Args:
dataframe (DataFrame): The DataFrame.
x_column_name (str): The column name for the x-axis.
y_column_name (str): The column name for the y-axis.
title (str): The title of the plot.
x_axis_label (str): The label for the x-axis data.
y_axis_label (str): The label for the y-axis data.
legend_label (str): The legend label of the plot.
x_tick_as_int (bool): If set, use integers for x ticks. For example,
if ticks show floating point numbers for years, e.g. 2002.5,
setting this flag will show integer values instead.
style (str): Style of the plot
palette (str): Palette of the plot
Returns:
tuple: A tuple containing the image tensor representation of the plot.
"""
df = dataframe
set_up_sns_style_palette(style, palette)
y_columns, labels = process_y_columns_and_labels(df, y_column_name, legend_label)
fig, ax = plt.subplots()
for i in range(len(y_columns)):
sns.lineplot(x=df[x_column_name], y=df[y_columns[i]], ax=ax, label=labels[i])
return plot_post_steps(
fig,
ax,
plt,
title,
x_axis_label,
y_axis_label,
x_tick_as_int)
+80
View File
@@ -0,0 +1,80 @@
from typing import Any, Dict
from matplotlib import pyplot as plt
import seaborn as sns
import pandas as pd
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
class SNSScatter:
"""
SNS Scatter Plot:
Generates a scatter plot from a pandas DataFrame using Seaborn.
For style and palette parameters, refer to [SNS Line Plot documentation](sns_line.md).
category: Plot
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the `scatter_plot` function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return common_input_types()
RETURN_TYPES: tuple = ("IMAGE",)
FUNCTION: str = "scatter_plot"
CATEGORY: str = "Data Analysis"
def scatter_plot(self,
dataframe: pd.DataFrame,
x_column_name: str,
y_column_name: str,
title: str,
x_axis_label: str,
y_axis_label: str,
legend_label: str,
x_tick_as_int: bool,
style: str=None,
palette: str=None) -> tuple:
"""
Generates a scatter plot from a pandas DataFrame.
Args:
dataframe (DataFrame): The DataFrame.
x_column_name (str): The column name for the x-axis.
y_column_name (str): The column name for the y-axis.
title (str): The title of the plot.
x_axis_label (str): The label for the x-axis data.
y_axis_label (str): The label for the y-axis data.
x_tick_as_int (bool): If set, use integers for x ticks. For example,
if ticks show floating point numbers for years, e.g. 2002.5,
setting this flag will show integer values instead.
style (str): Style of the plot
palette (str): Palette of the plot
Returns:
tuple: A tuple containing the image tensor representation of the plot.
"""
df = dataframe
set_up_sns_style_palette(style, palette)
y_columns, labels = process_y_columns_and_labels(df, y_column_name, legend_label)
# Create the plot
fig, ax = plt.subplots()
for i in range(len(y_columns)):
sns.scatterplot(x=df[x_column_name], y=df[y_columns[i]], ax=ax, label=labels[i])
return plot_post_steps(
fig,
ax,
plt,
title,
x_axis_label,
y_axis_label,
x_tick_as_int)
+106
View File
@@ -0,0 +1,106 @@
import io
from typing import List, Tuple
import numpy as np
import matplotlib.figure
import matplotlib.axes
from matplotlib import pyplot as plt
import matplotlib.ticker as mticker
import seaborn as sns
from PIL import Image
import torch
import pandas as pd
import seaborn as sns
from typing import Optional
from ..pandas_wrapper.utils import column_labels_string_to_list
from ..pandas_wrapper.utils import comma_separated_labels_string_to_list
def process_y_columns_and_labels(df:pd.DataFrame, y_column_name: str, legend_label: str) -> Tuple[List[str], List[str]]:
"""
Generates a y column list and legend label list from the comma-separated y column names and legend label string.
Args:
df (pd.DataFrame): The input dataframe containing the data.
y_column_name (str): A comma-separated string of column names to be used as y-values.
legend_label (str): A comma-separated string of labels corresponding to the y columns.
Returns:
List[str]: A list of processed y column names.
List[str]: A list of processed legend labels.
"""
y_columns = column_labels_string_to_list(df, y_column_name)
if legend_label:
labels = comma_separated_labels_string_to_list(legend_label)
if len(labels) < len(y_columns):
labels = labels + [""] * (len(y_columns) - len(labels))
else:
labels = [""] * len(y_columns)
return y_columns, labels
def set_up_sns_style_palette(style: Optional[str], palette: Optional[str]) -> None:
"""
Sets the Seaborn theme with the given style and palette.
Args:
style (Optional[str]): The style to apply to the Seaborn theme (e.g., 'darkgrid', 'whitegrid').
palette (Optional[str]): The color palette to apply (e.g., 'deep', 'muted').
Returns:
None
"""
if style and palette:
sns.set_theme(style=style, palette=palette)
elif style:
sns.set_theme(style=style)
elif palette:
sns.set_theme(palette=palette)
else:
sns.set_theme()
def plot_post_steps(
fig: matplotlib.figure.Figure,
ax: matplotlib.axes.Axes,
plt: plt,
title: str,
x_axis_label: str,
y_axis_label: str,
x_tick_as_int: bool
) -> tuple:
r"""Plots a post-processed step plot using Matplotlib.
Args:
fig (matplotlib.figure.Figure): The figure object for the plot.
ax (matplotlib.axes.Axes): The axes object for the plot.
plt (module): The Matplotlib module.
title (str): The title of the plot.
x_axis_label (str): The label for the x-axis.
y_axis_label (str): The label for the y-axis.
x_tick_as_int (bool): If ``True``, use integers for x-axis ticks.
For example, if ticks display floating point numbers (e.g., 2002.5),
setting this flag forces integer values instead.
Returns:
tuple: A tuple containing the image tensor representation of the plot.
"""
if x_tick_as_int:
ax.xaxis.set_major_locator(mticker.MaxNLocator(integer=True))
# Set title and labels
ax.set_title(title)
ax.set_xlabel(x_axis_label)
ax.set_ylabel(y_axis_label)
# Save the plot to a buffer
buf = io.BytesIO()
fig.savefig(buf, format='PNG')
buf.seek(0)
plt.close(fig) # Close the figure to free memory
# Load image and convert to tensor
image = Image.open(buf).convert("RGB")
image_tensor = torch.from_numpy(np.array(image)).float() / 255.0 # Normalize to [0,1]
image_tensor = image_tensor.unsqueeze(0)
return (image_tensor,)
+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.1.25"
version = "1.2.0"
license = {file = "LICENSE"}
[project.urls]
+2 -1
View File
@@ -1,3 +1,4 @@
matplotlib>=3.10.0
pandas>=2.2.3
seaborn>=0.13.2
scipy>=1.14.1
@@ -0,0 +1,38 @@
import os
import sys
import unittest
from typing import List
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 comma_separated_labels_string_to_list
class TestCommaSeparatedLabelsStringToList(unittest.TestCase):
"""
Unit tests for `comma_separated_labels_string_to_list` function.
"""
def setUp(self) -> None:
"""
Sets up the test environment before each test case.
"""
pass
def test_valid_column_strings(self) -> None:
"""
Tests `comma_separated_labels_string_to_list` with various valid input strings.
"""
self.assertEqual(comma_separated_labels_string_to_list("label1,label2,label3"), ["label1", "label2", "label3"])
self.assertEqual(comma_separated_labels_string_to_list(" label1 , label2 ,label3 "), ["label1", "label2", "label3"])
self.assertEqual(comma_separated_labels_string_to_list("a,b,c,d"), ["a", "b", "c", "d"])
self.assertEqual(comma_separated_labels_string_to_list("one"), ["one"])
self.assertEqual(comma_separated_labels_string_to_list(""), [])
self.assertEqual(comma_separated_labels_string_to_list(None), [])
self.assertEqual(comma_separated_labels_string_to_list(" , , , "), ["", "", "", ""])
if __name__ == "__main__":
unittest.main()