Added Pandas Save CSV node

This commit is contained in:
HowToSD
2025-02-13 23:38:27 -08:00
parent 85b592d7a1
commit f74f091c01
6 changed files with 109 additions and 2 deletions
+2 -1
View File
@@ -7,7 +7,8 @@ ComfyUI-Data-Analysis is a custom module for analyzing data using Pandas and Mat
Currently, it supports the following nodes:
- Loading CSV as a DataFrame
- Loading a CSV file as a DataFrame
- Saving a DataFrame to a CSV file
- Selecting a subset of columns from a DataFrame
- Selecting a subset of rows from a DataFrame by specifying a filter condition
- Sorting a DataFrame by a column
+4
View File
@@ -5,6 +5,7 @@ from .modules.matplotlib.mpl_scatter import MPLScatter
from .modules.pandas.pandas_head import PandasHead
from .modules.pandas.pandas_join import PandasJoin
from .modules.pandas.pandas_load_csv import PandasLoadCSV
from .modules.pandas.pandas_save_csv import PandasSaveCSV
from .modules.pandas.pandas_select_columns import PandasSelectColumns
from .modules.pandas.pandas_select_rows import PandasSelectRows
from .modules.pandas.pandas_show_dataframe import PandasShowDataFrame
@@ -21,6 +22,7 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PandasHead": PandasHead,
"PandasJoin": PandasJoin,
"PandasLoadCSV": PandasLoadCSV,
"PandasSaveCSV": PandasSaveCSV,
"PandasSelectColumns": PandasSelectColumns,
"PandasSelectRows": PandasSelectRows,
"PandasShowDataFrame": PandasShowDataFrame,
@@ -38,6 +40,7 @@ NODE_CLASS_MAPPINGS (Dict[str, Type[T]]):
- "PandasHead": Handles retrieving the first few rows of a DataFrame.
- "PandasJoin": Provides functionality to join multiple DataFrames.
- "PandasLoadCSV": Loads CSV data into a DataFrame.
- "PandasSaveCSV": Save a DataFrame to a CSV file.
- "PandasSelectColumns": Selects specific columns from a DataFrame.
- "PandasSelectRows": Filters specific rows from a DataFrame.
- "PandasShowDataFrame": Displays the content of the DataFrame.
@@ -53,6 +56,7 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PandasHead": "Pandas Head",
"PandasJoin": "Pandas Join",
"PandasLoadCSV": "Pandas Load CSV",
"PandasSaveCSV": "Pandas Save CSV",
"PandasSelectColumns": "Pandas Select Columns",
"PandasSelectRows": "Pandas Select Rows",
"PandasShowDataFrame": "Pandas Show DataFrame",
+3
View File
@@ -35,6 +35,9 @@ Use **Pandas Load CSV** node to load a CSV.
This will emit the DataFrame in a JSON string.
CSV file path is relative to the ComfyUI installation directory unless you specify the absolute file path.
## Saving data
Use **Pandas Save CSV** node to save the DataFrame to a CSV file.
## Selecting columns and rows
Use **Pandas Select Columns** for selecting columns, **Pandas Select Rows** to select rows by a filter condition.
+63
View File
@@ -0,0 +1,63 @@
"""
This code is based on
https://github.com/pythongosssss/ComfyUI-Custom-Scripts/blob/main/py/show_text.py
See credit/credit.md for the full license.
"""
from typing import Any, Dict
from io import StringIO
import pandas as pd
class PandasSaveCSV:
"""
PandasSaveCSV:
A class for saving a PandasDataFrame to a CSV file.
"""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"dataframe_json": ("STRING", {"forceInput": True}),
"file_path": ("STRING", {"multiline": False})
},
"hidden": {
"unique_id": "UNIQUE_ID",
"extra_pnginfo": "EXTRA_PNGINFO",
},
}
INPUT_IS_LIST = True
RETURN_TYPES = ("STRING",)
FUNCTION = "save_csv"
OUTPUT_NODE = True
OUTPUT_IS_LIST = (True,)
CATEGORY = "Data Analysis"
def save_csv(self, dataframe_json,file_path=None, unique_id=None, extra_pnginfo=None):
text = None
if unique_id is not None and extra_pnginfo is not None:
if not isinstance(extra_pnginfo, list):
print("Error: extra_pnginfo is not a list")
elif (
not isinstance(extra_pnginfo[0], dict)
or "workflow" not in extra_pnginfo[0]
):
print("Error: extra_pnginfo[0] is not a dict or missing 'workflow' key")
elif file_path is None or len(file_path) == 0:
print("Error: file path is not specified")
else:
# Deserialize JSON string to DataFrame
df = pd.read_json(StringIO(dataframe_json[0]))
df.to_csv(path_or_buf=file_path[0]) # Save as CSV
text = [str(df)] # wrap in list
workflow = extra_pnginfo[0]["workflow"]
node = next(
(x for x in workflow["nodes"] if str(x["id"]) == str(unique_id[0])),
None,
)
if node:
node["widgets_values"] = [text]
else:
print("unique_id or extra_pnginfo is None.")
return {"ui": {"text": text}, "result": (text,)}
+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.0.4"
version = "1.0.5"
license = {file = "LICENSE"}
[project.urls]
+36
View File
@@ -0,0 +1,36 @@
/*
This code is based on
https://github.com/pythongosssss/ComfyUI-Custom-Scripts/blob/main/web/js/showText.js
See credit/credit.md for the full license.
*/
import { app } from "../../../scripts/app.js";
import { ComfyWidgets } from "../../../scripts/widgets.js";
// Displays input text on a node
app.registerExtension({
name: "HowToSD.SaveCSV",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name === "PandasSaveCSV") { // This needs to match NODE_CLASS_MAPPINGS in Python
function populate(text) {
// No op as we are not updating the node's UI for now.
}
// When the node is executed we will be sent the input text, display this in the widget
const onExecuted = nodeType.prototype.onExecuted;
nodeType.prototype.onExecuted = function (message) {
onExecuted?.apply(this, arguments);
populate.call(this, message.text);
};
const onConfigure = nodeType.prototype.onConfigure;
nodeType.prototype.onConfigure = function () {
onConfigure?.apply(this, arguments);
if (this.widgets_values?.length) {
populate.call(this, this.widgets_values.slice(+this.widgets_values.length > 1));
}
};
}
},
});