Added Pandas Loc Row Series & Pandas Series To String nodes.

This commit is contained in:
HowToSD
2025-02-14 22:25:20 -08:00
parent 21ccfa8178
commit 71319b98d4
10 changed files with 146 additions and 5 deletions
+6
View File
@@ -11,9 +11,11 @@ from .modules.pandas_wrapper.pandas_join import PandasJoin
from .modules.pandas_wrapper.pandas_load_csv import PandasLoadCSV
from .modules.pandas_wrapper.pandas_load_csv_with_index import PandasLoadCSVWithIndex
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_save_csv import PandasSaveCSV
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_text import PandasShowText
from .modules.pandas_wrapper.pandas_sort import PandasSort
@@ -35,9 +37,11 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PandasLoadCSV": PandasLoadCSV,
"PandasLoadCSVWithIndex": PandasLoadCSVWithIndex,
"PandasLocCellStr": PandasLocCellStr,
"PandasLocRowSeries": PandasLocRowSeries,
"PandasSaveCSV": PandasSaveCSV,
"PandasSelectColumns": PandasSelectColumns,
"PandasSelectRows": PandasSelectRows,
"PandasSeriesToString": PandasSeriesToString,
"PandasShowDataFrame": PandasShowDataFrame,
"PandasShowText": PandasShowText,
"PandasSort": PandasSort,
@@ -62,9 +66,11 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PandasLoadCSV": "Pandas Load CSV",
"PandasLoadCSVWithIndex": "Pandas Load CSV with Index",
"PandasLocCellStr": "Pandas Loc Cell Str",
"PandasLocRowSeries": "Pandas Loc Row Series",
"PandasSaveCSV": "Pandas Save CSV",
"PandasSelectColumns": "Pandas Select Columns",
"PandasSelectRows": "Pandas Select Rows",
"PandasSeriesToString": "Pandas Series To String",
"PandasShowDataFrame": "Pandas Show DataFrame",
"PandasShowText": "Pandas Show Text",
"PandasSort": "Pandas Sort",
Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

+10 -1
View File
@@ -22,6 +22,7 @@ You can right-click, select **Add Node**, go to **Data Analysis**, and look for
| **Pandas Index** | Extract row labels (index) |
| **Pandas Select Columns** | Select specific columns from a DataFrame |
| **Pandas Select Rows** | Filter rows based on conditions |
| **Pandas Loc Row Series** | Select a row by row label (index) |
| **Pandas Loc Cell Str** | Select a cell by row label (index) and column label (index) |
| **Pandas Join** | Join two DataFrames |
| **Pandas Head** | Extract the first few rows |
@@ -29,7 +30,8 @@ You can right-click, select **Add Node**, go to **Data Analysis**, and look for
| **Pandas Show Text** | Display Text |
| **Pandas Summary** | Extract DataFrame statistics |
| **Pandas Sort** | Sort DataFrame by a column |
| **Pandas To String** | Convert DataFrame to a string |
| **Pandas Series To String** | Convert Series to a displayable string |
| **Pandas To String** | Convert DataFrame to a displayable string |
| **MPL Bar Chart** | Generate a bar chart |
| **MPL Line Plot** | Generate a line plot |
| **MPL Scatter Plot** | Generate a scatter plot |
@@ -64,6 +66,10 @@ Use **Pandas Select Columns** for selecting columns, **Pandas Select Rows** to s
![Pandas select subset](images/subset.jpg)
You can use **Pandas Loc Row Series** to select a row by row label (index). This node returns a JSON-serialized Series object. Data type of the labels needs to be specified as shown below:
![Pandas Loc Row Series](images/loc_row_series.png)
## Selecting a cell by row label (index) and column label (index)
Use **Pandas Loc Cell Str** for selecting a cell by row label (index) and column label (index). Data type of the labels needs to be specified as shown below:
@@ -120,6 +126,9 @@ Use **Pandas Sort**.
## 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.
## Convert Series to a string
Use **Pandas Series To String**. This node converts a Pandas Series to a displayable string. You can use this as an input to the **Pandas Show Text** node.
## Displaying charts or plots
Use one of the below:
| Node | Purpose |
+1
View File
@@ -0,0 +1 @@
{"last_node_id":18,"last_link_id":16,"nodes":[{"id":14,"type":"PandasSeriesToString","pos":[-75.9743423461914,1615.0836181640625],"size":[210,39.39631271362305],"flags":{},"order":6,"mode":0,"inputs":[{"name":"series_json","type":"STRING","link":12,"widget":{"name":"series_json"}}],"outputs":[{"name":"STRING","type":"STRING","links":[13],"slot_index":0,"localized_name":"STRING"}],"properties":{"Node name for S&R":"PandasSeriesToString"},"widgets_values":[""]},{"id":10,"type":"PandasShowDataFrame","pos":[-332.0985412597656,1870.44482421875],"size":[210,116.62158966064453],"flags":{},"order":4,"mode":0,"inputs":[{"name":"dataframe_json","type":"STRING","link":7,"widget":{"name":"dataframe_json"}}],"outputs":[{"name":"STRING","type":"STRING","links":null,"shape":6,"localized_name":"STRING"}],"properties":{"Node name for S&R":"PandasShowDataFrame"},"widgets_values":[""," A B\n0 1 10\n1 2 2345678\n2 3 30"]},{"id":8,"type":"PandasShowText","pos":[175.51785278320312,1615.8155517578125],"size":[210,101.80709075927734],"flags":{},"order":8,"mode":0,"inputs":[{"name":"text","type":"STRING","link":13,"widget":{"name":"text"}}],"outputs":[{"name":"STRING","type":"STRING","links":null,"shape":6,"localized_name":"STRING"}],"properties":{"Node name for S&R":"PandasShowText"},"widgets_values":["","A 2\nB 123456\nName: France, dtype: int64"]},{"id":16,"type":"PandasSeriesToString","pos":[-49.06276321411133,2046.5435791015625],"size":[210,39.39631271362305],"flags":{},"order":7,"mode":0,"inputs":[{"name":"series_json","type":"STRING","link":14,"widget":{"name":"series_json"}}],"outputs":[{"name":"STRING","type":"STRING","links":[16],"slot_index":0,"localized_name":"STRING"}],"properties":{"Node name for S&R":"PandasSeriesToString"},"widgets_values":[""]},{"id":17,"type":"PandasShowText","pos":[201.8267822265625,2044.7362060546875],"size":[210,101.80709075927734],"flags":{},"order":9,"mode":0,"inputs":[{"name":"text","type":"STRING","link":16,"widget":{"name":"text"}}],"outputs":[{"name":"STRING","type":"STRING","links":null,"shape":6,"localized_name":"STRING"}],"properties":{"Node name for S&R":"PandasShowText"},"widgets_values":["","A 2\nB 2345678\nName: 1, dtype: int64"]},{"id":4,"type":"PandasShowDataFrame","pos":[-331.5325012207031,1436.6846923828125],"size":[210,116.62158966064453],"flags":{},"order":2,"mode":0,"inputs":[{"name":"dataframe_json","type":"STRING","link":2,"widget":{"name":"dataframe_json"}}],"outputs":[{"name":"STRING","type":"STRING","links":null,"shape":6,"localized_name":"STRING"}],"properties":{"Node name for S&R":"PandasShowDataFrame"},"widgets_values":[""," A B\nUS 1 10\nFrance 2 123456\nGermany 3 30"]},{"id":3,"type":"PandasCreateWithIndex","pos":[-594.9833374023438,1436.66015625],"size":[210,136.70285034179688],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"STRING","type":"STRING","links":[2,11],"slot_index":0,"localized_name":"STRING"}],"properties":{"Node name for S&R":"PandasCreateWithIndex"},"widgets_values":["index,A,B\nUS,1,10\nFrance,2,123456\nGermany,3,30",0]},{"id":9,"type":"PandasCreate","pos":[-609.8991088867188,1870.20849609375],"size":[210,113.97640228271484],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"STRING","type":"STRING","links":[7,15],"slot_index":0,"localized_name":"STRING"}],"properties":{"Node name for S&R":"PandasCreate"},"widgets_values":["A,B\n1,10\n2,2345678\n3,30"]},{"id":13,"type":"PandasLocRowSeries","pos":[-337.280517578125,1615.083251953125],"size":[221.5128936767578,82.3223648071289],"flags":{},"order":3,"mode":0,"inputs":[{"name":"dataframe_json","type":"STRING","link":11,"widget":{"name":"dataframe_json"}}],"outputs":[{"name":"STRING","type":"STRING","links":[12],"slot_index":0,"localized_name":"STRING"}],"properties":{"Node name for S&R":"PandasLocRowSeries"},"widgets_values":["","France","string"]},{"id":15,"type":"PandasLocRowSeries","pos":[-323.39080810546875,2047.4111328125],"size":[219.60301208496094,89.9618911743164],"flags":{},"order":5,"mode":0,"inputs":[{"name":"dataframe_json","type":"STRING","link":15,"widget":{"name":"dataframe_json"}}],"outputs":[{"name":"STRING","type":"STRING","links":[14],"slot_index":0,"localized_name":"STRING"}],"properties":{"Node name for S&R":"PandasLocRowSeries"},"widgets_values":["","1","int"]}],"links":[[2,3,0,4,0,"STRING"],[7,9,0,10,0,"STRING"],[11,3,0,13,0,"STRING"],[12,13,0,14,0,"STRING"],[13,14,0,8,0,"STRING"],[14,15,0,16,0,"STRING"],[15,9,0,15,0,"STRING"],[16,16,0,17,0,"STRING"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.7853889628731393,"offset":[968.8282590918203,-1332.2955501507915]},"node_versions":{"data-analysis":"29af129ba3913df6cabbb3b7536c61317426759e"},"ue_links":[]},"version":0.4}
@@ -0,0 +1,53 @@
from typing import Any, Dict
from io import StringIO
import pandas as pd
from .utils import series_to_jsons
class PandasLocRowSeries:
"""
PandasLocRowSeries:
A class for selecting a row from a pandas DataFrame and output as a Series.
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the `select_row` function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"dataframe_json": ("STRING", {"multiline": True}),
"row_index": ("STRING", {"default": ""}),
"row_index_type":(("string", "int"),)
}
}
RETURN_TYPES: tuple = ("STRING",)
FUNCTION: str = "select_row"
CATEGORY: str = "Data Analysis"
def select_row(self, dataframe_json: str,
row_index: str,
row_index_type: str) -> tuple:
"""
Selects a specific row using a row label (row index) from a pandas DataFrame,
and returns the JSON serialized Series object.
Args:
dataframe_json (str): A JSON string representation of the DataFrame.
row_index (str): The row index (row label) for the row.
row_index_type (str): The data type of the row index.
Returns:
tuple: A tuple containing the value of the row in string. The string is
a JSON string containing the serialized Pandas Series object.
"""
# Deserialize JSON string to DataFrame
df = pd.read_json(StringIO(dataframe_json))
row_index = int(row_index) if row_index_type == "int" else row_index
value = df.loc[row_index]
return (series_to_jsons(value),)
@@ -0,0 +1,42 @@
from typing import Any, Dict
from io import StringIO
import pandas as pd
from .utils import jsons_to_series
class PandasSeriesToString:
"""
PandasSeriesToString:
A class for converting a pandas Series to a string representation.
"""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
"""
Defines the input types for the `to_string` function.
Returns:
Dict[str, Any]: A dictionary specifying required input types.
"""
return {
"required": {
"series_json": ("STRING", {"multiline": True}),
}
}
RETURN_TYPES: tuple = ("STRING",)
FUNCTION: str = "series_to_string"
CATEGORY: str = "Data Analysis"
def series_to_string(self, series_json: str) -> tuple:
"""
Converts a Pandas Series to a string representation.
Args:
series_json (str): A JSON string representation of the Series.
Returns:
tuple: A tuple containing the string representation of the Series.
"""
# Deserialize JSON string to DataFrame
series = jsons_to_series(series_json)
return (str(series),)
+23 -2
View File
@@ -1,4 +1,5 @@
import json
import numpy as np
import pandas as pd
def series_to_jsons(series: pd.Series) -> str:
@@ -11,13 +12,13 @@ def series_to_jsons(series: pd.Series) -> str:
Returns:
str: A JSON string representing the Series, including its data, index, and name.
"""
array = [x.item() if hasattr(x, "item") else x for x in series.tolist()] # Convert NumPy types to Python types
array = [x.item() if hasattr(x, "item") else x for x in series.array.tolist()] # Convert NumPy types to Python types
index = [x.item() if hasattr(x, "item") else x for x in series.index.tolist()] # Convert index to Python types
name = series.name
d = {"array": array, "index": index, "name": name}
return json.dumps(d)
return json.dumps(d, cls=NpEncoder) # See license for NpEncoder(json.JSONEncoder) below
def jsons_to_series(s: str) -> pd.Series:
"""
@@ -61,3 +62,23 @@ def jsons_to_index(s: str) -> pd.Index:
"""
d = json.loads(s)
return pd.Index(d["index"], name=d.get("name")) # Restore index with name
class NpEncoder(json.JSONEncoder):
"""
Source: https://stackoverflow.com/questions/50916422/python-typeerror-object-of-type-int64-is-not-json-serializable
Author: Jie Yang, Tommy
Licensed under CC BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0/)
"""
def default(self, obj):
if isinstance(obj, np.integer):
return int(obj)
if isinstance(obj, np.floating):
return float(obj)
if isinstance(obj, np.bool_):
return bool(obj)
if isinstance(obj, np.ndarray):
return obj.tolist()
return super(NpEncoder, self).default(obj)
+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.9"
version = "1.0.10"
license = {file = "LICENSE"}
[project.urls]
+1
View File
@@ -2,6 +2,7 @@ import os
import sys
import unittest
import json
import numpy as np
import pandas as pd
PROJECT_ROOT = os.path.realpath(os.path.join(__file__, "..", "..", ".."))
+9 -1
View File
@@ -2,6 +2,7 @@ import os
import sys
import unittest
import json
import numpy as np
import pandas as pd
PROJECT_ROOT = os.path.realpath(os.path.join(__file__, "..", "..", ".."))
@@ -15,13 +16,20 @@ class TestSeriesJsonConversion(unittest.TestCase):
def setUp(self):
"""Set up test data"""
self.series_int = pd.Series([10, 20, 30], index=["a", "b", "c"], name="test_series_int")
self.series_np_int64 = pd.Series(np.array([10, 20, 30], dtype=np.int64),
index=["a", "b", "c"],
name="test_series_np_int64")
self.series_np_int64_2 = pd.Series(np.array([10, 20, 30], dtype=np.int64),
index=np.array([1, 2, 3], dtype=np.int64),
name="test_series_np_int64_2")
self.series_float = pd.Series([1.1, 2.2, 3.3], index=["x", "y", "z"], name="test_series_float")
self.series_str = pd.Series(["apple", "banana", "cherry"], index=[0, 1, 2], name="test_series_str")
self.series_mixed = pd.Series([1, "text", 3.5], index=["one", "two", "three"], name="test_series_mixed")
def test_series_to_jsons_and_back(self):
"""Test conversion from Series to JSON and back"""
for series in [self.series_int, self.series_float, self.series_str, self.series_mixed]:
for series in [self.series_int, self.series_np_int64, self.series_np_int64_2,
self.series_float, self.series_str, self.series_mixed]:
with self.subTest(series=series):
json_str = series_to_jsons(series)
reconstructed_series = jsons_to_series(json_str)