diff --git a/.gitignore b/.gitignore
index 17a60fc..8c82aed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,7 @@
__pycache__/
**/__pycache__/
-datasets/
models/
*.pth
+/datasets/
+!*/foo/
+examples/datasets/.~lock*
\ No newline at end of file
diff --git a/__init__.py b/__init__.py
index baeddf4..8d8af09 100644
--- a/__init__.py
+++ b/__init__.py
@@ -67,6 +67,7 @@ from .modules.pandas_wrapper.pandas_kurtosis import PandasKurtosis
from .modules.pandas_wrapper.pandas_load_csv import PandasLoadCSV
from .modules.pandas_wrapper.pandas_load_csv_with_encoding import PandasLoadCSVWithEncoding
from .modules.pandas_wrapper.pandas_load_csv_with_index import PandasLoadCSVWithIndex
+from .modules.pandas_wrapper.pandas_load_excel import PandasLoadExcel
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
@@ -300,6 +301,7 @@ NODE_CLASS_MAPPINGS: Dict[str, Type[T]] = {
"PandasLoadCSV": PandasLoadCSV,
"PandasLoadCSVWithEncoding": PandasLoadCSVWithEncoding,
"PandasLoadCSVWithIndex": PandasLoadCSVWithIndex,
+ "PandasLoadExcel": PandasLoadExcel,
"PandasLoadHTML": PandasLoadHTML,
"PandasLoadJSON": PandasLoadJSON,
"PandasLocCellStr": PandasLocCellStr,
@@ -536,6 +538,7 @@ NODE_DISPLAY_NAME_MAPPINGS: Dict[str, str] = {
"PandasLoadCSV": "Pandas Load CSV",
"PandasLoadCSVWithEncoding": "Pandas Load CSV With Encoding",
"PandasLoadCSVWithIndex": "Pandas Load CSV With Index",
+ "PandasLoadExcel": "Pandas Load Excel",
"PandasLoadHTML": "Pandas Load HTML",
"PandasLoadJSON": "Pandas Load JSON",
"PandasLocCellStr": "Pandas Loc Cell Str",
diff --git a/docs/reference/node_reference.md b/docs/reference/node_reference.md
index 1623b38..6bc1a96 100644
--- a/docs/reference/node_reference.md
+++ b/docs/reference/node_reference.md
@@ -109,6 +109,7 @@
| [Pandas Load CSV](pandas_load_csv.md) | Loads CSV files into a pandas DataFrame. |
| [Pandas Load CSV With Encoding](pandas_load_csv_with_encoding.md) | Loads CSV files into a pandas DataFrame. |
| [Pandas Load CSV With Index](pandas_load_csv_with_index.md) | Loads CSV files into a pandas DataFrame. |
+| [Pandas Load Excel](pandas_load_excel.md) | Loads Excel files into a pandas DataFrame. |
| [Pandas Load HTML](pandas_load_html.md) | Scans an HTML file or webpage for tables and returns the number of DataFrames |
| [Pandas Load JSON](pandas_load_json.md) | Loads JSON files into a pandas DataFrame. |
| [Pandas Save CSV](pandas_save_csv.md) | Saves a PandasDataFrame to a CSV file. |
diff --git a/docs/reference/pandas_load_excel.md b/docs/reference/pandas_load_excel.md
new file mode 100644
index 0000000..1371ee6
--- /dev/null
+++ b/docs/reference/pandas_load_excel.md
@@ -0,0 +1,23 @@
+# Pandas Load Excel
+Loads Excel files into a pandas DataFrame.
+
+This node uses read_excel method.
+See [Pandas documentation](https://pandas.pydata.org/docs/reference/api/pandas.read_excel.html) for details.
+
+If sheet_name is not specified, first sheet will be read.
+
+## Input
+| Name | Data type |
+|---|---|
+| file_path | String |
+| sheet_name | String |
+
+## Output
+| Data type |
+|---|
+| DataFrame |
+
+
+Category: IO
+
+ComfyUI Data Analysis Node Reference. © 2025 Hide Inada (HowToSD.com). All rights reserved.
diff --git a/examples/datasets/test_file.xlsx b/examples/datasets/test_file.xlsx
new file mode 100644
index 0000000..d0466d8
Binary files /dev/null and b/examples/datasets/test_file.xlsx differ
diff --git a/modules/pandas_wrapper/pandas_load_excel.py b/modules/pandas_wrapper/pandas_load_excel.py
new file mode 100644
index 0000000..00f92b0
--- /dev/null
+++ b/modules/pandas_wrapper/pandas_load_excel.py
@@ -0,0 +1,55 @@
+from typing import Any, Dict
+from io import StringIO
+import pandas as pd
+
+
+class PandasLoadExcel:
+ """
+ Loads Excel files into a pandas DataFrame.
+
+ This node uses read_excel method.
+ See [Pandas documentation](https://pandas.pydata.org/docs/reference/api/pandas.read_excel.html) for details.
+
+ If sheet_name is not specified, first sheet will be read.
+
+ category: IO
+ """
+
+ @classmethod
+ def INPUT_TYPES(cls) -> Dict[str, Any]:
+ """
+ Defines the input types for the `load_csv` function.
+
+ Returns:
+ Dict[str, Any]: A dictionary specifying required input types.
+ """
+ return {
+ "required": {
+ "file_path": ("STRING", {"multiline": False}),
+ "sheet_name": ("STRING", {"default": ""}),
+
+ }
+ }
+
+ RETURN_TYPES: tuple = ("DATAFRAME",)
+ FUNCTION: str = "f"
+ CATEGORY: str = "Data Analysis"
+
+ @classmethod
+ def IS_CHANGED(cls, **kw):
+ return float("NaN")
+
+ def f(self, file_path: str, sheet_name: str) -> tuple:
+ """
+ Loads an Excel file into a pandas DataFrame.
+
+ Args:
+ file_path (str): The path to the Excel file.
+
+ Returns:
+ tuple: A tuple containing a DataFrame.
+ """
+ if len(sheet_name) == 0:
+ sheet_name = 0 # Read first sheet
+ df = pd.read_excel(file_path, sheet_name=sheet_name)
+ return (df,)
diff --git a/pyproject.toml b/pyproject.toml
index 50e24d1..bd99782 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[project]
name = "data-analysis"
description = "Data analysis custom modules for ComfyUI – Integrates Pandas, Seaborn and Matplotlib into ComfyUI, featuring extensive PyTorch wrapper nodes for seamless tensor operations. Experimental PyTorch model training was added in v2.0."
-version = "2.0.4"
+version = "2.0.5"
license = {file = "LICENSE"}
[project.urls]
diff --git a/requirements.txt b/requirements.txt
index 58e2f64..b504a51 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,4 +2,5 @@ matplotlib>=3.10.0
pandas>=2.2.3
seaborn>=0.13.2
scipy>=1.14.1
-scikit-learn>=1.6.1
\ No newline at end of file
+scikit-learn>=1.6.1
+openpyxl>=3.1.5
\ No newline at end of file
diff --git a/tests/pandas_wrapper/pandas_load_excel_test.py b/tests/pandas_wrapper/pandas_load_excel_test.py
new file mode 100644
index 0000000..fd481db
--- /dev/null
+++ b/tests/pandas_wrapper/pandas_load_excel_test.py
@@ -0,0 +1,64 @@
+import os
+import sys
+import unittest
+import pandas as pd
+
+PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
+MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
+sys.path.append(MODULE_ROOT)
+from pandas_wrapper.pandas_load_excel import PandasLoadExcel
+
+
+class TestPandasLoadExcel(unittest.TestCase):
+ def setUp(self):
+ """Set up test data."""
+ self.node = PandasLoadExcel()
+ self.test_file_path = os.path.join(
+ os.path.dirname(__file__), "..", "..",
+ "examples", "datasets", "test_file.xlsx")
+
+ self.test_data_list = [
+ (
+ "Sheet1",
+ pd.DataFrame(
+ {
+ "Item": ["Bat", "Racquet", "Glove", "Hockey stick", "Soccer ball"],
+ "Qty": [3, 4, 5, 6, 7]
+ },
+ index = [0, 1, 2, 3, 4]
+ ),
+ ),
+ (
+ "FruitPrice",
+ pd.DataFrame(
+ {
+ "Fruit": ["Apple", "Banana", "Coconut", "Donut"],
+ "Price": [1.23, 2.34, 3.45, 4.56]
+ },
+ index = [0, 1, 2, 3]
+ )
+ ),
+ (
+ "", # Leaving the sheet name blank should read the first sheet.
+ pd.DataFrame(
+ {
+ "Item": ["Bat", "Racquet", "Glove", "Hockey stick", "Soccer ball"],
+ "Qty": [3, 4, 5, 6, 7]
+ },
+ index = [0, 1, 2, 3, 4]
+ ),
+ ),
+ ]
+ def test_read_excel(self):
+ """Reads multiple sheets from an excel file.
+ """
+ for test_data in self.test_data_list:
+ with self.subTest(test_data=test_data):
+ sheet_name = test_data[0]
+ expected = test_data[1]
+ df = self.node.f(self.test_file_path, sheet_name=sheet_name)[0]
+ pd.testing.assert_frame_equal(df, expected)
+
+
+if __name__ == "__main__":
+ unittest.main()