Added Pandas Count, Max, Mean, Median, Min, Mode, Std, Sum, Var, Show Series nodes.

This commit is contained in:
HowToSD
2025-02-17 01:49:46 -08:00
parent 8a59b2f5c6
commit d3bdcace9e
15 changed files with 573 additions and 2 deletions
+31
View File
@@ -4,6 +4,7 @@ from .modules.matplotlib.mpl_bar import MPLBar
from .modules.matplotlib.mpl_line import MPLLine
from .modules.matplotlib.mpl_scatter import MPLScatter
from .modules.pandas_wrapper.pandas_columns import PandasColumns
from .modules.pandas_wrapper.pandas_count import PandasCount
from .modules.pandas_wrapper.pandas_create import PandasCreate
from .modules.pandas_wrapper.pandas_create_with_index import PandasCreateWithIndex
from .modules.pandas_wrapper.pandas_dropna import PandasDropNA
@@ -20,16 +21,26 @@ from .modules.pandas_wrapper.pandas_load_csv_with_index import PandasLoadCSVWith
from .modules.pandas_wrapper.pandas_load_json import PandasLoadJSON
from .modules.pandas_wrapper.pandas_loc_cell_str import PandasLocCellStr
from .modules.pandas_wrapper.pandas_loc_row_series import PandasLocRowSeries
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_min import PandasMin
from .modules.pandas_wrapper.pandas_mode import PandasMode
from .modules.pandas_wrapper.pandas_save_csv import PandasSaveCSV
from .modules.pandas_wrapper.pandas_save_json import PandasSaveJSON
from .modules.pandas_wrapper.pandas_select_columns import PandasSelectColumns
from .modules.pandas_wrapper.pandas_select_rows import PandasSelectRows
from .modules.pandas_wrapper.pandas_series_to_string import PandasSeriesToString
from .modules.pandas_wrapper.pandas_show_dataframe import PandasShowDataFrame
from .modules.pandas_wrapper.pandas_show_series import PandasShowSeries
from .modules.pandas_wrapper.pandas_show_text import PandasShowText
from .modules.pandas_wrapper.pandas_sort import PandasSort
from .modules.pandas_wrapper.pandas_std import PandasStd
from .modules.pandas_wrapper.pandas_sum import PandasSum
from .modules.pandas_wrapper.pandas_summary import PandasSummary
from .modules.pandas_wrapper.pandas_to_string import PandasToString
from .modules.pandas_wrapper.pandas_var import PandasVar
T = TypeVar("T")
@@ -39,6 +50,7 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"MPLLine": MPLLine,
"MPLScatter": MPLScatter,
"PandasColumns": PandasColumns,
"PandasCount": PandasCount,
"PandasCreate": PandasCreate,
"PandasCreateWithIndex": PandasCreateWithIndex,
"PandasDropNA": PandasDropNA,
@@ -55,16 +67,25 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PandasLoadJSON": PandasLoadJSON,
"PandasLocCellStr": PandasLocCellStr,
"PandasLocRowSeries": PandasLocRowSeries,
"PandasMax": PandasMax,
"PandasMean": PandasMean,
"PandasMedian": PandasMedian,
"PandasMin": PandasMin,
"PandasMode": PandasMode,
"PandasSaveCSV": PandasSaveCSV,
"PandasSaveJSON": PandasSaveJSON,
"PandasSelectColumns": PandasSelectColumns,
"PandasSelectRows": PandasSelectRows,
"PandasSeriesToString": PandasSeriesToString,
"PandasShowDataFrame": PandasShowDataFrame,
"PandasShowSeries": PandasShowSeries,
"PandasShowText": PandasShowText,
"PandasSort": PandasSort,
"PandasStd": PandasStd,
"PandasSum": PandasSum,
"PandasSummary": PandasSummary,
"PandasToString": PandasToString,
"PandasVar": PandasVar
}
"""
NODE_CLASS_MAPPINGS (Dict[str, Type[T]]):
@@ -77,6 +98,7 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"MPLLine": "MPL Line Plot",
"MPLScatter": "MPL Scatter Plot",
"PandasColumns": "Pandas Columns",
"PandasCount": "Pandas Count",
"PandasCreate": "Pandas Create",
"PandasCreateWithIndex": "Pandas Create With Index",
"PandasDropNA": "Pandas Drop NA",
@@ -93,16 +115,25 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PandasLoadJSON": "Pandas Load JSON",
"PandasLocCellStr": "Pandas Loc Cell Str",
"PandasLocRowSeries": "Pandas Loc Row Series",
"PandasMax": "Pandas Max",
"PandasMean": "Pandas Mean",
"PandasMedian": "Pandas Median",
"PandasMin": "Pandas Min",
"PandasMode": "Pandas Mode",
"PandasSaveCSV": "Pandas Save CSV",
"PandasSaveJSON": "Pandas Save JSON",
"PandasSelectColumns": "Pandas Select Columns",
"PandasSelectRows": "Pandas Select Rows",
"PandasSeriesToString": "Pandas Series To String",
"PandasShowDataFrame": "Pandas Show DataFrame",
"PandasShowSeries": "Pandas Show Series",
"PandasShowText": "Pandas Show Text",
"PandasSort": "Pandas Sort",
"PandasStd": "Pandas Std",
"PandasSum": "Pandas Sum",
"PandasSummary": "Pandas Summary",
"PandasToString": "Pandas To String",
"PandasVar": "Pandas Var",
}
"""
NODE_DISPLAY_NAME_MAPPINGS (Dict[str, str]):
+43 -1
View File
@@ -49,6 +49,7 @@ You can right-click, select **Add Node**, go to **Data Analysis**, and look for
| Node Name | Functionality |
|-------------------------|-------------------------------------|
| **Pandas Show DataFrame** | Display DataFrame contents |
| **Pandas Show Series** | Display Series contents |
| **Pandas Show Text** | Display Text |
| **Pandas Summary** | Extract DataFrame statistics |
@@ -58,6 +59,19 @@ You can right-click, select **Add Node**, go to **Data Analysis**, and look for
| **Pandas Is NA** | Check DataFrame for missing values |
| **Pandas Drop NA** | Drop missing values from DataFrame |
**Summary statistics**
| Node Name | Functionality |
|-------------------------|-------------------------------------|
| **Pandas Count** | Number of rows |
| **Pandas Max** | Maximum value of row values |
| **Pandas Mean** | Mean of row values |
| **Pandas Median** | Median of row values |
| **Pandas Min** | Minimum value of row values |
| **Pandas Mode** | Mode of row values |
| **Pandas Std** | Standard deviation of row values |
| **Pandas Sum** | Sum of row values |
| **Pandas Var** | Variance of row values |
**Converting to displayable string**
| Node Name | Functionality |
|-------------------------|-------------------------------------|
@@ -192,7 +206,10 @@ https://github.com/pythongosssss/ComfyUI-Custom-Scripts/blob/main/py/show_text.p
This node has been copied to display text without requiring the user to install the above custom nodes package. However, if you already have ComfyUI-Custom-Scripts, you can also use the Show Text node from the package.
## Displaying DataFrame contents
Use **Pandas Show DataFrame** node. When the number of rows is large, Pandas automatically hides middle rows during string conversion, so the display size should be always manageable.
Use **Pandas Show DataFrame** node. When the number of rows is large, This node automatically hides middle rows during string conversion, so the display size should be always manageable.
## Displaying Series contents
Use **Pandas Show Series** node.
## Extracting DataFrame statistics
Use **Pandas Summary** to extract statistics then connect the output to **Pandas Show Text** for display.
@@ -203,6 +220,31 @@ Use **Pandas Sort**.
![Pandas Sort](images/sort.jpg)
## Summary statistics
You can compute summary statistics for each row in a DataFrame using below nodes:
* **Pandas Count** to get the number of rows.
* **Pandas Min** to get the minimum row value.
* **Pandas Max** to get the maximum value of row values.
* **Pandas Mean** to get the mean of row values.
* **Pandas Median** to get the median of row values.
* **Pandas Mode** to get the mode of row values.
* **Pandas Sum** to get the sum of row values.
* **Pandas Std** to get the standard deviation of row values.
* **Pandas Var** to get the variance of row values.
All nodes return a Series except for Pandas Mode, which returns a DataFrame. This is necessary because multiple values may occur with the highest frequency. For example:
```
apple
apple
apple
banana
donut
coconut
coconut
coconut
```
In this case, both apple and coconut are the mode.
## Convert DataFrame to a string
Use **Pandas To String**. This node is to convert the DataFrame to a displayable string, and this truncates the data when the data is large. This can be used to display the data content in a text display node (e.g. Show Text node). However, you can use Show DataFrame nodes to display the DataFrame content, so you do not normally need to use this.
File diff suppressed because one or more lines are too long
+42
View File
@@ -0,0 +1,42 @@
from typing import Any, Dict
from io import StringIO
import pandas as pd
from .utils import series_to_jsons
class PandasCount:
"""
PandasCount:
A class for computing the count of a pandas DataFrame.
"""
@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_json": ("STRING", {"multiline": True})
}
}
RETURN_TYPES: tuple = ("STRING",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, dataframe_json: str) -> tuple:
"""
Returns a Series with count for each column.
Args:
dataframe_json (str): A JSON string representation of the DataFrame.
Returns:
tuple: A tuple containing a JSON string for the Series.
"""
# Deserialize JSON string to DataFrame
df = pd.read_json(StringIO(dataframe_json))
return (series_to_jsons(df.count()),)
+42
View File
@@ -0,0 +1,42 @@
from typing import Any, Dict
from io import StringIO
import pandas as pd
from .utils import series_to_jsons
class PandasMax:
"""
PandasMin:
A class for computing the max of a pandas DataFrame.
"""
@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_json": ("STRING", {"multiline": True})
}
}
RETURN_TYPES: tuple = ("STRING",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, dataframe_json: str) -> tuple:
"""
Returns a Series with max for each column.
Args:
dataframe_json (str): A JSON string representation of the DataFrame.
Returns:
tuple: A tuple containing a JSON string for the Series.
"""
# Deserialize JSON string to DataFrame
df = pd.read_json(StringIO(dataframe_json))
return (series_to_jsons(df.max()),)
+42
View File
@@ -0,0 +1,42 @@
from typing import Any, Dict
from io import StringIO
import pandas as pd
from .utils import series_to_jsons
class PandasMean:
"""
PandasMin:
A class for computing the mean of a pandas DataFrame.
"""
@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_json": ("STRING", {"multiline": True})
}
}
RETURN_TYPES: tuple = ("STRING",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, dataframe_json: str) -> tuple:
"""
Returns a Series with mean for each column.
Args:
dataframe_json (str): A JSON string representation of the DataFrame.
Returns:
tuple: A tuple containing a JSON string for the Series.
"""
# Deserialize JSON string to DataFrame
df = pd.read_json(StringIO(dataframe_json))
return (series_to_jsons(df.mean()),)
+42
View File
@@ -0,0 +1,42 @@
from typing import Any, Dict
from io import StringIO
import pandas as pd
from .utils import series_to_jsons
class PandasMedian:
"""
PandasMin:
A class for computing the median of a pandas DataFrame.
"""
@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_json": ("STRING", {"multiline": True})
}
}
RETURN_TYPES: tuple = ("STRING",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, dataframe_json: str) -> tuple:
"""
Returns a Series with median for each column.
Args:
dataframe_json (str): A JSON string representation of the DataFrame.
Returns:
tuple: A tuple containing a JSON string for the Series.
"""
# Deserialize JSON string to DataFrame
df = pd.read_json(StringIO(dataframe_json))
return (series_to_jsons(df.median()),)
+42
View File
@@ -0,0 +1,42 @@
from typing import Any, Dict
from io import StringIO
import pandas as pd
from .utils import series_to_jsons
class PandasMin:
"""
PandasMin:
A class for computing the min of a pandas DataFrame.
"""
@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_json": ("STRING", {"multiline": True})
}
}
RETURN_TYPES: tuple = ("STRING",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, dataframe_json: str) -> tuple:
"""
Returns a Series with min for each column.
Args:
dataframe_json (str): A JSON string representation of the DataFrame.
Returns:
tuple: A tuple containing a JSON string for the Series.
"""
# Deserialize JSON string to DataFrame
df = pd.read_json(StringIO(dataframe_json))
return (series_to_jsons(df.min()),)
+41
View File
@@ -0,0 +1,41 @@
from typing import Any, Dict
from io import StringIO
import pandas as pd
class PandasMode:
"""
PandasMode:
A class for computing the mode of a pandas DataFrame.
"""
@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_json": ("STRING", {"multiline": True})
}
}
RETURN_TYPES: tuple = ("STRING",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, dataframe_json: str) -> tuple:
"""
Returns a DataFrame with mode for each column.
Args:
dataframe_json (str): A JSON string representation of the DataFrame.
Returns:
tuple: A tuple containing a JSON string for the DataFrame.
"""
# Deserialize JSON string to DataFrame
df = pd.read_json(StringIO(dataframe_json))
return (df.mode().to_json(),)
@@ -0,0 +1,56 @@
"""
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 io import StringIO
import pandas as pd
from .utils import series_to_jsons
from .utils import jsons_to_series
class PandasShowSeries:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"series_json": ("STRING", {"forceInput": True}),
},
"hidden": {
"unique_id": "UNIQUE_ID",
"extra_pnginfo": "EXTRA_PNGINFO",
},
}
INPUT_IS_LIST = True
RETURN_TYPES = ("STRING",)
FUNCTION = "notify"
OUTPUT_NODE = True
OUTPUT_IS_LIST = (True,)
CATEGORY = "Data Analysis"
def notify(self, series_json, 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")
else:
# Deserialize JSON string to DataFrame
series = jsons_to_series(series_json[0])
text = [series_to_jsons(series)] # 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,)}
+42
View File
@@ -0,0 +1,42 @@
from typing import Any, Dict
from io import StringIO
import pandas as pd
from .utils import series_to_jsons
class PandasStd:
"""
PandasStd:
A class for computing the standard deviation of a pandas DataFrame.
"""
@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_json": ("STRING", {"multiline": True})
}
}
RETURN_TYPES: tuple = ("STRING",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, dataframe_json: str) -> tuple:
"""
Returns a Series with standard deviation for each column.
Args:
dataframe_json (str): A JSON string representation of the DataFrame.
Returns:
tuple: A tuple containing a JSON string for the Series.
"""
# Deserialize JSON string to DataFrame
df = pd.read_json(StringIO(dataframe_json))
return (series_to_jsons(df.std()),)
+42
View File
@@ -0,0 +1,42 @@
from typing import Any, Dict
from io import StringIO
import pandas as pd
from .utils import series_to_jsons
class PandasSum:
"""
PandasSum:
A class for computing the sum of a pandas DataFrame.
"""
@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_json": ("STRING", {"multiline": True})
}
}
RETURN_TYPES: tuple = ("STRING",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, dataframe_json: str) -> tuple:
"""
Returns a Series with sum for each column.
Args:
dataframe_json (str): A JSON string representation of the DataFrame.
Returns:
tuple: A tuple containing a JSON string for the Series.
"""
# Deserialize JSON string to DataFrame
df = pd.read_json(StringIO(dataframe_json))
return (series_to_jsons(df.sum()),)
+42
View File
@@ -0,0 +1,42 @@
from typing import Any, Dict
from io import StringIO
import pandas as pd
from .utils import series_to_jsons
class PandasVar:
"""
PandasVar:
A class for computing the variance of a pandas DataFrame.
"""
@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_json": ("STRING", {"multiline": True})
}
}
RETURN_TYPES: tuple = ("STRING",)
FUNCTION: str = "f"
CATEGORY: str = "Data Analysis"
def f(self, dataframe_json: str) -> tuple:
"""
Returns a Series with variance for each column.
Args:
dataframe_json (str): A JSON string representation of the DataFrame.
Returns:
tuple: A tuple containing a JSON string for the Series.
"""
# Deserialize JSON string to DataFrame
df = pd.read_json(StringIO(dataframe_json))
return (series_to_jsons(df.var()),)
+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.18"
version = "1.0.19"
license = {file = "LICENSE"}
[project.urls]
+64
View File
@@ -0,0 +1,64 @@
/*
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.ShowSeries",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name === "PandasShowSeries") { // This needs to match NODE_CLASS_MAPPINGS in Python
function populate(text) {
if (this.widgets) {
for (let i = 1; i < this.widgets.length; i++) {
this.widgets[i].onRemove?.();
}
this.widgets.length = 1;
}
const v = [...text];
if (!v[0]) {
v.shift();
}
for (const list of v) {
const w = ComfyWidgets["STRING"](this, "text2", ["STRING", { multiline: true }], app).widget;
w.inputEl.readOnly = true;
w.inputEl.style.opacity = 0.6;
w.value = list;
}
requestAnimationFrame(() => {
const sz = this.computeSize();
if (sz[0] < this.size[0]) {
sz[0] = this.size[0];
}
if (sz[1] < this.size[1]) {
sz[1] = this.size[1];
}
this.onResize?.(sz);
app.graph.setDirtyCanvas(true, false);
});
}
// 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));
}
};
}
},
});