diff --git a/__init__.py b/__init__.py index 66c65de..42d3ecb 100644 --- a/__init__.py +++ b/__init__.py @@ -51,6 +51,7 @@ from .modules.pandas_wrapper.pandas_load_csv_with_index import PandasLoadCSVWith from .modules.pandas_wrapper.pandas_load_html import PandasLoadHTML 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_multiindex_dataframe import PandasLocRowMultiIndexDataFrame from .modules.pandas_wrapper.pandas_loc_row_series import PandasLocRowSeries from .modules.pandas_wrapper.pandas_log import PandasLog from .modules.pandas_wrapper.pandas_max import PandasMax @@ -154,6 +155,7 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = { "PandasLoadHTML": PandasLoadHTML, "PandasLoadJSON": PandasLoadJSON, "PandasLocCellStr": PandasLocCellStr, + "PandasLocRowMultiIndexDataFrame": PandasLocRowMultiIndexDataFrame, "PandasLocRowSeries": PandasLocRowSeries, "PandasLog": PandasLog, "PandasMax": PandasMax, @@ -257,6 +259,7 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = { "PandasLoadHTML": "Pandas Load HTML", "PandasLoadJSON": "Pandas Load JSON", "PandasLocCellStr": "Pandas Loc Cell Str", + "PandasLocRowMultiIndexDataFrame": "Pandas Loc Row Multi Index DataFrame", "PandasLocRowSeries": "Pandas Loc Row Series", "PandasLog": "Pandas Log", "PandasMax": "Pandas Max", diff --git a/docs/images/multiindex.png b/docs/images/multiindex.png new file mode 100644 index 0000000..a0cd3fc Binary files /dev/null and b/docs/images/multiindex.png differ diff --git a/docs/reference/node_reference.md b/docs/reference/node_reference.md index f09f1bc..b0de593 100644 --- a/docs/reference/node_reference.md +++ b/docs/reference/node_reference.md @@ -49,6 +49,7 @@ | [Pandas Iloc Row Series](pandas_iloc_row_series.md) | Selects a row from a pandas DataFrame and returning it as a Series. | | [Pandas Iloc Rows Data Frame](pandas_iloc_rows_dataframe.md) | Selects rows from a pandas DataFrame and returning it as a DataFrame. | | [Pandas Loc Cell Str](pandas_loc_cell_str.md) | Selects a cell from a pandas DataFrame and output as a string. | +| [Pandas Loc Row Multi Index Data Frame](pandas_loc_row_multiindex_dataframe.md) | Selects a row from a pandas DataFrame with MultiIndex and output as a DataFrame. | | [Pandas Loc Row Series](pandas_loc_row_series.md) | Selects a row from a pandas DataFrame and output as a Series. | | [Pandas Select Columns](pandas_select_columns.md) | Selects specific columns from a pandas DataFrame. | | [Pandas Select Rows](pandas_select_rows.md) | Selects specific rows from a pandas DataFrame based on a condition. | diff --git a/docs/reference/pandas_loc_row_multiindex_dataframe.md b/docs/reference/pandas_loc_row_multiindex_dataframe.md new file mode 100644 index 0000000..c25fe49 --- /dev/null +++ b/docs/reference/pandas_loc_row_multiindex_dataframe.md @@ -0,0 +1,26 @@ +# Pandas Loc Row Multi Index Data Frame +Selects a row from a pandas DataFrame with MultiIndex and output as a DataFrame. + +Specify comma-separated MultiIndex labels (row index) and the data type for the MultiIndex to select a row. + +Below screenshots illustrate these options: +![PandasLocRowMultiIndexDataFrame](../images/multiindex.png) + +The top DataFrame has an integer-type MultiIndex, while the bottom DataFrame has a string-type MultiIndex. In both cases, two index values are specified to select a single row while the correct MultiIndex data type is also selected. Note that mixed data types for MultiIndex are not currently supported. + +## Input +| Name | Data type | +|---|---| +| dataframe | DataFrame | +| row_index | String | +| row_index_type | | + +## Output +| Data type | +|---| +| DataFrame | + +
+Category: Data subset selection + +ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved. diff --git a/examples/workflows/multiindex.json b/examples/workflows/multiindex.json new file mode 100644 index 0000000..1b6522c --- /dev/null +++ b/examples/workflows/multiindex.json @@ -0,0 +1 @@ +{"last_node_id":80,"last_link_id":85,"nodes":[{"id":78,"type":"PandasShowDataFrame","pos":[-219.4473419189453,4061.099365234375],"size":[285.6177673339844,97.01778411865234],"flags":{},"order":10,"mode":0,"inputs":[{"name":"dataframe","type":"DATAFRAME","link":83}],"outputs":[{"name":"STRING","type":"STRING","links":null,"shape":6}],"properties":{"Node name for S&R":"PandasShowDataFrame"},"widgets_values":[" A B\nIndex C \nW Dog donut 40"]},{"id":77,"type":"PandasLocRowMultiIndexDataFrame","pos":[-220.60975646972656,3891.671142578125],"size":[302.3999938964844,86.90418243408203],"flags":{},"order":7,"mode":0,"inputs":[{"name":"dataframe","type":"DATAFRAME","link":85}],"outputs":[{"name":"DATAFRAME","type":"DATAFRAME","links":[83],"slot_index":0}],"properties":{"Node name for S&R":"PandasLocRowMultiIndexDataFrame"},"widgets_values":["W,Dog","string"]},{"id":73,"type":"PandasShowDataFrame","pos":[-232.98275756835938,3642.0107421875],"size":[285.6177673339844,97.01778411865234],"flags":{},"order":11,"mode":0,"inputs":[{"name":"dataframe","type":"DATAFRAME","link":81}],"outputs":[{"name":"STRING","type":"STRING","links":null,"shape":6}],"properties":{"Node name for S&R":"PandasShowDataFrame"},"widgets_values":[" A C\n B \n1 20 banana Cat"]},{"id":72,"type":"PandasLocRowMultiIndexDataFrame","pos":[-235.06532287597656,3491.6162109375],"size":[302.3999938964844,86.90418243408203],"flags":{},"order":9,"mode":0,"inputs":[{"name":"dataframe","type":"DATAFRAME","link":80}],"outputs":[{"name":"DATAFRAME","type":"DATAFRAME","links":[81],"slot_index":0}],"properties":{"Node name for S&R":"PandasLocRowMultiIndexDataFrame"},"widgets_values":["1,20","int"]},{"id":76,"type":"PandasShowDataFrame","pos":[-487.3261413574219,4040.76318359375],"size":[210,149.5841064453125],"flags":{},"order":6,"mode":0,"inputs":[{"name":"dataframe","type":"DATAFRAME","link":84}],"outputs":[{"name":"STRING","type":"STRING","links":null,"shape":6}],"properties":{"Node name for S&R":"PandasShowDataFrame"},"widgets_values":[" A B\nIndex C \nX Dog apple 10\nY Cat banana 20\nZ Rabbit coconut 30\nW Dog donut 40"]},{"id":67,"type":"PandasSetIndex","pos":[-1046.9400634765625,3486.258544921875],"size":[315,106],"flags":{},"order":5,"mode":0,"inputs":[{"name":"dataframe","type":"DATAFRAME","link":76}],"outputs":[{"name":"DATAFRAME","type":"DATAFRAME","links":[72,80],"slot_index":0}],"properties":{"Node name for S&R":"PandasSetIndex"},"widgets_values":["B",true,true]},{"id":74,"type":"PandasCreateWithIndex","pos":[-1307.4989013671875,3888.37060546875],"size":[210,157.9644317626953],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"DATAFRAME","type":"DATAFRAME","links":[82],"slot_index":0}],"properties":{"Node name for S&R":"PandasCreateWithIndex"},"widgets_values":["Index, A,B,C\nX,apple,10,Dog\nY,banana,20,Cat\nZ,coconut,30,Rabbit\nW,donut,40,Dog",0]},{"id":75,"type":"PandasSetIndex","pos":[-1060.0457763671875,3890.282958984375],"size":[315,106],"flags":{},"order":4,"mode":0,"inputs":[{"name":"dataframe","type":"DATAFRAME","link":82}],"outputs":[{"name":"DATAFRAME","type":"DATAFRAME","links":[84,85],"slot_index":0}],"properties":{"Node name for S&R":"PandasSetIndex"},"widgets_values":["C",true,true]},{"id":80,"type":"MarkdownNote","pos":[-698.1348876953125,3896.85693359375],"size":[320,60],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["### Connected to DataFrame with String MultiIndex"],"color":"#432","bgcolor":"#653"},{"id":79,"type":"MarkdownNote","pos":[-693.662353515625,3498.498291015625],"size":[313.4215393066406,58],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["### Connected to DataFrame with Integer MultiIndex"],"color":"#432","bgcolor":"#653"},{"id":71,"type":"PandasShowDataFrame","pos":[-497.32952880859375,3643.9404296875],"size":[210,149.5841064453125],"flags":{},"order":8,"mode":0,"inputs":[{"name":"dataframe","type":"DATAFRAME","link":72}],"outputs":[{"name":"STRING","type":"STRING","links":null,"shape":6}],"properties":{"Node name for S&R":"PandasShowDataFrame"},"widgets_values":[" A C\n B \n0 10 apple Dog\n1 20 banana Cat\n2 30 coconut Dog\n3 40 donut Dog"]},{"id":63,"type":"PandasCreate","pos":[-1296.004638671875,3489.03857421875],"size":[210,132.85110473632812],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[{"name":"DATAFRAME","type":"DATAFRAME","links":[76],"slot_index":0}],"properties":{"Node name for S&R":"PandasCreate"},"widgets_values":["A,B,C\napple,10,Dog\nbanana,20,Cat\ncoconut,30,Dog\ndonut,40,Dog"]}],"links":[[72,67,0,71,0,"DATAFRAME"],[76,63,0,67,0,"DATAFRAME"],[80,67,0,72,0,"DATAFRAME"],[81,72,0,73,0,"DATAFRAME"],[82,74,0,75,0,"DATAFRAME"],[83,77,0,78,0,"DATAFRAME"],[84,75,0,76,0,"DATAFRAME"],[85,75,0,77,0,"DATAFRAME"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.8936655747208404,"offset":[1278.415275533177,-3326.2076217917866]},"node_versions":{"data-analysis":"554d9dda206203af7225966e218bb0ff8357bf76"},"ue_links":[]},"version":0.4} \ No newline at end of file diff --git a/modules/pandas_wrapper/pandas_loc_row_multiindex_dataframe.py b/modules/pandas_wrapper/pandas_loc_row_multiindex_dataframe.py new file mode 100644 index 0000000..9e17fae --- /dev/null +++ b/modules/pandas_wrapper/pandas_loc_row_multiindex_dataframe.py @@ -0,0 +1,66 @@ +from typing import Any, Dict +import pandas as pd + +class PandasLocRowMultiIndexDataFrame: + """ + Selects a row from a pandas DataFrame with MultiIndex and output as a DataFrame. + + Specify comma-separated MultiIndex labels (row index) and the data type for the MultiIndex to select a row. + + Below screenshots illustrate these options: + ![PandasLocRowMultiIndexDataFrame](../images/multiindex.png) + + The top DataFrame has an integer-type MultiIndex, while the bottom DataFrame has a string-type MultiIndex. In both cases, two index values are specified to select a single row while the correct MultiIndex data type is also selected. Note that mixed data types for MultiIndex are not currently supported. + + category: Data subset selection + """ + + @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": ("DATAFRAME", {}), + "row_index": ("STRING", {"default": ""}), + "row_index_type":(("string", "int"),) + } + } + + RETURN_TYPES: tuple = ("DATAFRAME",) + FUNCTION: str = "f" + CATEGORY: str = "Data Analysis" + + def f(self, dataframe: pd.DataFrame, + row_index: str, + row_index_type: str) -> tuple: + """ + Selects a specific row using a comma-separated MultiIndex labels (row index) from a pandas DataFrame, and returns the DataFrame. + + Args: + dataframe (DataFrame): The DataFrame. + row_index (str): A comma-separated MultiIndex label for the row. + row_index_type (str): The data type of the row index. + + Returns: + tuple: A tuple containing the Pandas DataFrame. + """ + # We need to pass a tuple of MultiIndex. + # e.g. if the first index is A, second is B, we need to pass: + # ("A", "B") + ind_list = [ind.strip() for ind in row_index.split(",")] + + # This will throw an exception if the MultiIndex is not an int. + if row_index_type == "int": + ind_list = [int(ind) for ind in ind_list] + + ind_tuple = tuple(ind_list) + + # We need to wrap it to get the DataFrame. Otherwise, we will get a Series. + ind_tuple_list = [ind_tuple] + value = dataframe.loc[ind_tuple_list] + return (value,) diff --git a/pyproject.toml b/pyproject.toml index 8ca92e3..1614496 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "data-analysis" description = "Data analysis custom modules for ComfyUI - Use Pandas & Matplotlib within ComfyUI" -version = "1.1.15" +version = "1.1.16" license = {file = "LICENSE"} [project.urls]