Added Pandas Loc Row Series & Pandas Series To String nodes.
This commit is contained in:
@@ -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
@@ -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
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
## 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 |
|
||||
|
||||
@@ -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),)
|
||||
@@ -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
@@ -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]
|
||||
|
||||
@@ -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__, "..", "..", ".."))
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user