diff --git a/__init__.py b/__init__.py index a9170bb..7934a6e 100644 --- a/__init__.py +++ b/__init__.py @@ -6,11 +6,13 @@ from .modules.matplotlib.mpl_scatter import MPLScatter from .modules.pandas_wrapper.pandas_columns import PandasColumns 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 from .modules.pandas_wrapper.pandas_group_by import PandasGroupBy from .modules.pandas_wrapper.pandas_head import PandasHead from .modules.pandas_wrapper.pandas_iloc_row_series import PandasIlocRowSeries from .modules.pandas_wrapper.pandas_iloc_rows_dataframe import PandasIlocRowsDataFrame from .modules.pandas_wrapper.pandas_index import PandasIndex +from .modules.pandas_wrapper.pandas_isna import PandasIsNA 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_encoding import PandasLoadCSVWithEncoding @@ -39,11 +41,13 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = { "PandasColumns": PandasColumns, "PandasCreate": PandasCreate, "PandasCreateWithIndex": PandasCreateWithIndex, + "PandasDropNA": PandasDropNA, "PandasGroupBy": PandasGroupBy, "PandasHead": PandasHead, "PandasIlocRowSeries": PandasIlocRowSeries, "PandasIlocRowsDataFrame": PandasIlocRowsDataFrame, "PandasIndex": PandasIndex, + "PandasIsNA": PandasIsNA, "PandasJoin": PandasJoin, "PandasLoadCSV": PandasLoadCSV, "PandasLoadCSVWithEncoding": PandasLoadCSVWithEncoding, @@ -75,11 +79,13 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = { "PandasColumns": "Pandas Columns", "PandasCreate": "Pandas Create", "PandasCreateWithIndex": "Pandas Create With Index", + "PandasDropNA": "Pandas Drop NA", "PandasGroupBy": "Pandas Group By", "PandasHead": "Pandas Head", "PandasIlocRowSeries": "Pandas Iloc Row Series", "PandasIlocRowsDataFrame": "Pandas Iloc Rows DataFrame", "PandasIndex": "Pandas Index", + "PandasIsNA": "Pandas Is NA", "PandasJoin": "Pandas Join", "PandasLoadCSV": "Pandas Load CSV", "PandasLoadCSVWithEncoding": "Pandas Load CSV With Encoding", diff --git a/docs/images/isna.png b/docs/images/isna.png new file mode 100644 index 0000000..a210d2c Binary files /dev/null and b/docs/images/isna.png differ diff --git a/docs/ug.md b/docs/ug.md index 341d273..8b0ab9e 100644 --- a/docs/ug.md +++ b/docs/ug.md @@ -52,6 +52,12 @@ You can right-click, select **Add Node**, go to **Data Analysis**, and look for | **Pandas Show Text** | Display Text | | **Pandas Summary** | Extract DataFrame statistics | +**Missing value processing** +| Node Name | Functionality | +|-------------------------|-------------------------------------| +| **Pandas Is NA** | Check DataFrame for missing values | +| **Pandas Drop NA** | Drop missing values from DataFrame | + **Converting to displayable string** | Node Name | Functionality | |-------------------------|-------------------------------------| @@ -173,6 +179,11 @@ The workflow file examples/workflows/join.json contains all of the above cases. ## Extract the first few rows Use **Pandas Head**. +## Missing value processing +Use **Pandas Is NA** for checking DataFrame for missing values, and **Pandas Drop NA** for dropping missing values from DataFrame. + +![Pandas Is NA](images/isna.png) + ## Display text ## Use **Pandas Show Text** node. diff --git a/modules/pandas_wrapper/pandas_dropna.py b/modules/pandas_wrapper/pandas_dropna.py new file mode 100644 index 0000000..75bf1dd --- /dev/null +++ b/modules/pandas_wrapper/pandas_dropna.py @@ -0,0 +1,42 @@ +from typing import Any, Dict +from io import StringIO +import pandas as pd +from .utils import index_to_jsons + +class PandasDropNA: + """ + PandasColumns: + A class for droping missing values from 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 = "dropna" + CATEGORY: str = "Data Analysis" + + def dropna(self, dataframe_json: str) -> tuple: + """ + Returns a DataFrame with missing values removed. + + 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.dropna().to_json(),) diff --git a/modules/pandas_wrapper/pandas_isna.py b/modules/pandas_wrapper/pandas_isna.py new file mode 100644 index 0000000..bcf7b85 --- /dev/null +++ b/modules/pandas_wrapper/pandas_isna.py @@ -0,0 +1,42 @@ +from typing import Any, Dict +from io import StringIO +import pandas as pd +from .utils import index_to_jsons + +class PandasIsNA: + """ + PandasColumns: + A class for checking a pandas DataFrame for missing values. + """ + + @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 = "isna" + CATEGORY: str = "Data Analysis" + + def isna(self, dataframe_json: str) -> tuple: + """ + Returns a DataFrame with boolean values with True indicating a missing value. + + 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.isna().to_json(),) diff --git a/pyproject.toml b/pyproject.toml index ec6259a..2d346f3 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.0.17" +version = "1.0.18" license = {file = "LICENSE"} [project.urls]