From b30739522a0dc26a12a61009526f161e076a9425 Mon Sep 17 00:00:00 2001 From: Aman Nijjar Date: Mon, 16 Sep 2024 21:32:11 -0700 Subject: [PATCH 01/14] Added check for Pandas Dataframe in table.table --- lib/matplotlib/table.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/table.py b/lib/matplotlib/table.py index 0f75021926fd..adfcbae4b0c9 100644 --- a/lib/matplotlib/table.py +++ b/lib/matplotlib/table.py @@ -25,6 +25,7 @@ """ import numpy as np +import pandas as pd from . import _api, _docstring from .artist import Artist, allow_rasterization @@ -655,7 +656,7 @@ def table(ax, cellLoc='right', colWidths=None, rowLabels=None, rowColours=None, rowLoc='left', colLabels=None, colColours=None, colLoc='center', - loc='bottom', bbox=None, edges='closed', + loc='bottom', bbox=None, edges='closed', df=None, **kwargs): """ Add a table to an `~.axes.Axes`. @@ -674,7 +675,7 @@ def table(ax, Parameters ---------- - cellText : 2D list of str, optional + cellText : 2D list of str, Pandas.DataFrame, optional The texts to place into the table cells. *Note*: Line breaks in the strings are currently not accounted for and @@ -743,6 +744,11 @@ def table(ax, rows = len(cellColours) cols = len(cellColours[0]) cellText = [[''] * cols] * rows + # Convert the Pandas dataframe to a 2d string array + elif isinstance(cellText, pd.DataFrame): + headers = cellText.columns.tolist() + data = cellText.map(str).values + cellText = np.vstack([headers, data]) rows = len(cellText) cols = len(cellText[0]) From 7cb26b19a0debb69f62b507edc56a34e08511bc5 Mon Sep 17 00:00:00 2001 From: Aman Nijjar Date: Wed, 18 Sep 2024 15:08:06 -0700 Subject: [PATCH 02/14] Created _is_pandas_dataframe(x) and modified _unpack_to_numpy(x) to handle Pandas DataFrame Objects --- lib/matplotlib/cbook.py | 18 +++++++++++++++++- lib/matplotlib/tests/test_cbook.py | 27 +++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 2411784af3ec..b4ab030db4c4 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2406,7 +2406,11 @@ def _unpack_to_numpy(x): # so in this case we do not want to return a function if isinstance(xtmp, np.ndarray): return xtmp - if _is_torch_array(x) or _is_jax_array(x) or _is_tensorflow_array(x): + if (_is_torch_array(x) or + _is_jax_array(x) or + _is_tensorflow_array(x) or + _is_pandas_dataframe(x)): + # using np.asarray() instead of explicitly __array__(), as the latter is # only _one_ of many methods, and it's the last resort, see also # https://numpy.org/devdocs/user/basics.interoperability.html#using-arbitrary-objects-in-numpy @@ -2444,3 +2448,15 @@ def _auto_format_str(fmt, value): return fmt % (value,) except (TypeError, ValueError): return fmt.format(value) + + +def _is_pandas_dataframe(x): + """Check if 'x' is a Pandas DataFrame.""" + try: + # we're intentionally not attempting to import Pandas. If somebody + # has created a Pandas DataFrame, Pandas should already be in sys.modules + return isinstance(x, sys.modules['pandas'].DataFrame) + except Exception: # TypeError, KeyError, AttributeError, maybe others? + # we're attempting to access attributes on imported modules which + # may have arbitrary user code, so we deliberately catch all exceptions + return False diff --git a/lib/matplotlib/tests/test_cbook.py b/lib/matplotlib/tests/test_cbook.py index 222cc23b7e4d..845277e117ab 100644 --- a/lib/matplotlib/tests/test_cbook.py +++ b/lib/matplotlib/tests/test_cbook.py @@ -1034,3 +1034,30 @@ def __array__(self): # if not mocked, and the implementation does not guarantee it # is the same Python object, just the same values. assert_array_equal(result, data) + + +def test_unpack_to_numpy_from_pandas(): + """ + Test that Pandas DataFrame are converted to NumPy arrays. + + We don't want to create a dependency on Pandas in the test suite, so we mock it. + """ + class Pandas: + def __init__(self, data): + self.data = data + + def __array__(self): + return self.data + + pandas = ModuleType('pandas') + pandas.Pandas = Pandas + sys.modules['pandas'] = pandas + + data = np.arange(10) + pandas_dataframe = pandas.Pandas(data) + + result = cbook._unpack_to_numpy(pandas_dataframe) + # compare results, do not check for identity: the latter would fail + # if not mocked, and the implementation does not guarantee it + # is the same Python object, just the same values. + assert_array_equal(result, data) From 10d69f263ff9e68de43f44e750e197e504a5591c Mon Sep 17 00:00:00 2001 From: Aman Nijjar Date: Wed, 18 Sep 2024 15:33:01 -0700 Subject: [PATCH 03/14] Modified table.py to remove Pandas Dependency and use _unpack_to_numpy and _is_pandas_dataframe with associated test. --- lib/matplotlib/table.py | 17 ++++++++++------- lib/matplotlib/tests/test_table.py | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/table.py b/lib/matplotlib/table.py index adfcbae4b0c9..3ba45ca08038 100644 --- a/lib/matplotlib/table.py +++ b/lib/matplotlib/table.py @@ -25,7 +25,6 @@ """ import numpy as np -import pandas as pd from . import _api, _docstring from .artist import Artist, allow_rasterization @@ -34,6 +33,8 @@ from .transforms import Bbox from .path import Path +from .cbook import _unpack_to_numpy, _is_pandas_dataframe + class Cell(Rectangle): """ @@ -656,7 +657,7 @@ def table(ax, cellLoc='right', colWidths=None, rowLabels=None, rowColours=None, rowLoc='left', colLabels=None, colColours=None, colLoc='center', - loc='bottom', bbox=None, edges='closed', df=None, + loc='bottom', bbox=None, edges='closed', **kwargs): """ Add a table to an `~.axes.Axes`. @@ -744,11 +745,13 @@ def table(ax, rows = len(cellColours) cols = len(cellColours[0]) cellText = [[''] * cols] * rows - # Convert the Pandas dataframe to a 2d string array - elif isinstance(cellText, pd.DataFrame): - headers = cellText.columns.tolist() - data = cellText.map(str).values - cellText = np.vstack([headers, data]) + + # Check if we have a Pandas DataFrame + if _is_pandas_dataframe(cellText): + # Convert to numpy array + header = _unpack_to_numpy(cellText.columns) + data = _unpack_to_numpy(cellText) + cellText = np.vstack([header, data]) rows = len(cellText) cols = len(cellText[0]) diff --git a/lib/matplotlib/tests/test_table.py b/lib/matplotlib/tests/test_table.py index ea31ac124e4a..068c92f2e3c6 100644 --- a/lib/matplotlib/tests/test_table.py +++ b/lib/matplotlib/tests/test_table.py @@ -264,3 +264,27 @@ def __repr__(self): munits.registry.pop(FakeUnit) assert not munits.registry.get_converter(FakeUnit) + + +def test_table_dataframe(): + # Test if Pandas Data Frame can be passed in cellText + import pandas as pd + + data = { + 'Letter': ['A', 'B', 'C'], + 'Number': [100, 200, 300] + } + + df = pd.DataFrame(data) + fig, ax = plt.subplots() + ax.axis('off') + table = ax.table(df, loc='center') + + assert table[0, 0].get_text().get_text() == 'Letter' + assert table[0, 1].get_text().get_text() == 'Number' + assert table[1, 0].get_text().get_text() == 'A' + assert table[1, 1].get_text().get_text() == str(100) + assert table[2, 0].get_text().get_text() == 'B' + assert table[2, 1].get_text().get_text() == str(200) + assert table[3, 0].get_text().get_text() == 'C' + assert table[3, 1].get_text().get_text() == str(300) From f73fa6a8ff0c76d1d4de13216b5359d504185623 Mon Sep 17 00:00:00 2001 From: Aman Nijjar Date: Wed, 18 Sep 2024 17:23:40 -0700 Subject: [PATCH 04/14] Adjusted files following timhoffm review on commit 10d69f2 --- lib/matplotlib/cbook.py | 6 +----- lib/matplotlib/table.py | 6 +++--- lib/matplotlib/tests/test_cbook.py | 27 --------------------------- lib/matplotlib/tests/test_table.py | 1 - 4 files changed, 4 insertions(+), 36 deletions(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index b4ab030db4c4..4267c99aa34f 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2406,11 +2406,7 @@ def _unpack_to_numpy(x): # so in this case we do not want to return a function if isinstance(xtmp, np.ndarray): return xtmp - if (_is_torch_array(x) or - _is_jax_array(x) or - _is_tensorflow_array(x) or - _is_pandas_dataframe(x)): - + if _is_torch_array(x) or _is_jax_array(x) or _is_tensorflow_array(x): # using np.asarray() instead of explicitly __array__(), as the latter is # only _one_ of many methods, and it's the last resort, see also # https://numpy.org/devdocs/user/basics.interoperability.html#using-arbitrary-objects-in-numpy diff --git a/lib/matplotlib/table.py b/lib/matplotlib/table.py index 3ba45ca08038..6217e532c94e 100644 --- a/lib/matplotlib/table.py +++ b/lib/matplotlib/table.py @@ -33,7 +33,7 @@ from .transforms import Bbox from .path import Path -from .cbook import _unpack_to_numpy, _is_pandas_dataframe +from .cbook import _is_pandas_dataframe class Cell(Rectangle): @@ -749,8 +749,8 @@ def table(ax, # Check if we have a Pandas DataFrame if _is_pandas_dataframe(cellText): # Convert to numpy array - header = _unpack_to_numpy(cellText.columns) - data = _unpack_to_numpy(cellText) + header = cellText.columns.to_numpy() + data = cellText.to_numpy() cellText = np.vstack([header, data]) rows = len(cellText) diff --git a/lib/matplotlib/tests/test_cbook.py b/lib/matplotlib/tests/test_cbook.py index 845277e117ab..222cc23b7e4d 100644 --- a/lib/matplotlib/tests/test_cbook.py +++ b/lib/matplotlib/tests/test_cbook.py @@ -1034,30 +1034,3 @@ def __array__(self): # if not mocked, and the implementation does not guarantee it # is the same Python object, just the same values. assert_array_equal(result, data) - - -def test_unpack_to_numpy_from_pandas(): - """ - Test that Pandas DataFrame are converted to NumPy arrays. - - We don't want to create a dependency on Pandas in the test suite, so we mock it. - """ - class Pandas: - def __init__(self, data): - self.data = data - - def __array__(self): - return self.data - - pandas = ModuleType('pandas') - pandas.Pandas = Pandas - sys.modules['pandas'] = pandas - - data = np.arange(10) - pandas_dataframe = pandas.Pandas(data) - - result = cbook._unpack_to_numpy(pandas_dataframe) - # compare results, do not check for identity: the latter would fail - # if not mocked, and the implementation does not guarantee it - # is the same Python object, just the same values. - assert_array_equal(result, data) diff --git a/lib/matplotlib/tests/test_table.py b/lib/matplotlib/tests/test_table.py index 068c92f2e3c6..3bd91229cf6d 100644 --- a/lib/matplotlib/tests/test_table.py +++ b/lib/matplotlib/tests/test_table.py @@ -277,7 +277,6 @@ def test_table_dataframe(): df = pd.DataFrame(data) fig, ax = plt.subplots() - ax.axis('off') table = ax.table(df, loc='center') assert table[0, 0].get_text().get_text() == 'Letter' From 89f79cb5b3a8cfc70685cb9d9be262507078d172 Mon Sep 17 00:00:00 2001 From: Aman Nijjar Date: Fri, 20 Sep 2024 12:21:46 -0700 Subject: [PATCH 05/14] added next_whats_new file explaining changes to table --- .../pass_pandasDataFrame_into_table.rst | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst diff --git a/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst b/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst new file mode 100644 index 000000000000..0c42e3875680 --- /dev/null +++ b/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst @@ -0,0 +1,21 @@ +New Feature - passing Pandas.DataFrame into ax.table(...) +---------------------------------------------------------- + +Pandas.DataFrame objects can now be used to add a tables to an axes. The cellText argument of +matplotlib.table.table was modified to accept it. + +.. code-block:: python + + import matplotlib.pyplot as plt + import pandas as pd + + data = { + 'Letter': ['A', 'B', 'C'], + 'Number': [100, 200, 300] + } + + df = pd.DataFrame(data) + fig, ax = plt.subplots() + table = ax.table(df, loc='center') + ax.axis('off') + plt.show() From e919949079df13f84547be7e2222d73aac4428d7 Mon Sep 17 00:00:00 2001 From: Aman Nijjar Date: Sun, 22 Sep 2024 21:19:29 -0700 Subject: [PATCH 06/14] modified miniver.txt and clarified pass_xxx.rst file --- .../next_whats_new/pass_pandasDataFrame_into_table.rst | 7 ++++--- requirements/testing/minver.txt | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst b/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst index 0c42e3875680..e8b82a42d51f 100644 --- a/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst +++ b/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst @@ -1,8 +1,9 @@ New Feature - passing Pandas.DataFrame into ax.table(...) ---------------------------------------------------------- -Pandas.DataFrame objects can now be used to add a tables to an axes. The cellText argument of -matplotlib.table.table was modified to accept it. +Pandas.DataFrame objects can now be passed directly to the ax.table +plotting method via the cellText argument. See the following code +block for an example implementation. .. code-block:: python @@ -16,6 +17,6 @@ matplotlib.table.table was modified to accept it. df = pd.DataFrame(data) fig, ax = plt.subplots() - table = ax.table(df, loc='center') + table = ax.table(df, loc='center') # or table = ax.table(cellText=df, loc='center') ax.axis('off') plt.show() diff --git a/requirements/testing/minver.txt b/requirements/testing/minver.txt index 3932e68eb015..737d09e6f3fc 100644 --- a/requirements/testing/minver.txt +++ b/requirements/testing/minver.txt @@ -13,3 +13,4 @@ pillow==8.3.2 pyparsing==2.3.1 pytest==7.0.0 python-dateutil==2.7 +pandas!=0.25.0 From 193a64f04de6a7816031381632578ca96a57286a Mon Sep 17 00:00:00 2001 From: Aman Nijjar Date: Sun, 22 Sep 2024 22:52:56 -0700 Subject: [PATCH 07/14] reverted minver.txt and modified tests.yml to run extra.txt --- .github/workflows/tests.yml | 10 +++++++++- requirements/testing/minver.txt | 1 - 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4de46a1ed80f..95b9dfb1a4ef 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -51,7 +51,9 @@ jobs: - name-suffix: "(Minimum Versions)" os: ubuntu-20.04 python-version: '3.10' - extra-requirements: '-c requirements/testing/minver.txt' + extra-requirements: + - '-c requirements/testing/minver.txt' + - '-r requirements/testing/extra.txt' delete-font-cache: true # Oldest versions with Py3.10 wheels. pyqt5-ver: '==5.15.5 sip==6.3.0' @@ -85,12 +87,14 @@ jobs: pyqt6-ver: '!=6.6.0' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' + extra-requirements: '-r requirements/testing/extra.txt' - os: ubuntu-22.04 python-version: '3.13' # https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html pyqt6-ver: '!=6.6.0' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' + extra-requirements: '-r requirements/testing/extra.txt' - name-suffix: "Free-threaded" os: ubuntu-22.04 python-version: '3.13t' @@ -98,18 +102,22 @@ jobs: pyqt6-ver: '!=6.6.0' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' + extra-requirements: '-r requirements/testing/extra.txt' - os: macos-12 # This runner is on Intel chips. python-version: '3.10' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' + extra-requirements: '-r requirements/testing/extra.txt' - os: macos-14 # This runner is on M1 (arm64) chips. python-version: '3.12' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' + extra-requirements: '-r requirements/testing/extra.txt' - os: macos-14 # This runner is on M1 (arm64) chips. python-version: '3.13' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' + extra-requirements: '-r requirements/testing/extra.txt' steps: - uses: actions/checkout@v4 diff --git a/requirements/testing/minver.txt b/requirements/testing/minver.txt index 737d09e6f3fc..3932e68eb015 100644 --- a/requirements/testing/minver.txt +++ b/requirements/testing/minver.txt @@ -13,4 +13,3 @@ pillow==8.3.2 pyparsing==2.3.1 pytest==7.0.0 python-dateutil==2.7 -pandas!=0.25.0 From e73c8ae115ecb6b35d698da7a057c977f85ac2d2 Mon Sep 17 00:00:00 2001 From: Aman Nijjar Date: Tue, 24 Sep 2024 13:56:14 -0700 Subject: [PATCH 08/14] reverted tests.yml and modified stub, .rst file, and test --- .github/workflows/tests.yml | 10 +--------- .../next_whats_new/pass_pandasDataFrame_into_table.rst | 9 ++++----- lib/matplotlib/table.py | 2 +- lib/matplotlib/tests/test_table.py | 2 +- 4 files changed, 7 insertions(+), 16 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 95b9dfb1a4ef..4de46a1ed80f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -51,9 +51,7 @@ jobs: - name-suffix: "(Minimum Versions)" os: ubuntu-20.04 python-version: '3.10' - extra-requirements: - - '-c requirements/testing/minver.txt' - - '-r requirements/testing/extra.txt' + extra-requirements: '-c requirements/testing/minver.txt' delete-font-cache: true # Oldest versions with Py3.10 wheels. pyqt5-ver: '==5.15.5 sip==6.3.0' @@ -87,14 +85,12 @@ jobs: pyqt6-ver: '!=6.6.0' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' - extra-requirements: '-r requirements/testing/extra.txt' - os: ubuntu-22.04 python-version: '3.13' # https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html pyqt6-ver: '!=6.6.0' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' - extra-requirements: '-r requirements/testing/extra.txt' - name-suffix: "Free-threaded" os: ubuntu-22.04 python-version: '3.13t' @@ -102,22 +98,18 @@ jobs: pyqt6-ver: '!=6.6.0' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' - extra-requirements: '-r requirements/testing/extra.txt' - os: macos-12 # This runner is on Intel chips. python-version: '3.10' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' - extra-requirements: '-r requirements/testing/extra.txt' - os: macos-14 # This runner is on M1 (arm64) chips. python-version: '3.12' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' - extra-requirements: '-r requirements/testing/extra.txt' - os: macos-14 # This runner is on M1 (arm64) chips. python-version: '3.13' # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-2346 pyside6-ver: '!=6.5.1' - extra-requirements: '-r requirements/testing/extra.txt' steps: - uses: actions/checkout@v4 diff --git a/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst b/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst index e8b82a42d51f..68b62e68e3b4 100644 --- a/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst +++ b/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst @@ -1,9 +1,8 @@ -New Feature - passing Pandas.DataFrame into ax.table(...) ----------------------------------------------------------- +``ax.table`` will accept a pandas dataframe +-------------------------------------------- -Pandas.DataFrame objects can now be passed directly to the ax.table -plotting method via the cellText argument. See the following code -block for an example implementation. +The `~Axes.axes.table` method can now accept a data frame for the ``cellText`` method, which +it attempts to render with column headers set by ``df.columns.to_numpy()`` and cell data set by ``df.to_numpy()``. .. code-block:: python diff --git a/lib/matplotlib/table.py b/lib/matplotlib/table.py index 6217e532c94e..183aac91f55b 100644 --- a/lib/matplotlib/table.py +++ b/lib/matplotlib/table.py @@ -676,7 +676,7 @@ def table(ax, Parameters ---------- - cellText : 2D list of str, Pandas.DataFrame, optional + cellText : 2D list of str or pandas.DataFrame, optional The texts to place into the table cells. *Note*: Line breaks in the strings are currently not accounted for and diff --git a/lib/matplotlib/tests/test_table.py b/lib/matplotlib/tests/test_table.py index 3bd91229cf6d..7e9be12b64bb 100644 --- a/lib/matplotlib/tests/test_table.py +++ b/lib/matplotlib/tests/test_table.py @@ -268,7 +268,7 @@ def __repr__(self): def test_table_dataframe(): # Test if Pandas Data Frame can be passed in cellText - import pandas as pd + pd = pytest.importorskip("pandas") data = { 'Letter': ['A', 'B', 'C'], From 80a2390f035d53cfcffb0bf555af70f5cd674984 Mon Sep 17 00:00:00 2001 From: Aman Nijjar Date: Tue, 24 Sep 2024 15:45:30 -0700 Subject: [PATCH 09/14] Modified test+table_dataframe to use the pd test fixture --- lib/matplotlib/tests/test_table.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/matplotlib/tests/test_table.py b/lib/matplotlib/tests/test_table.py index 7e9be12b64bb..230d031e2a64 100644 --- a/lib/matplotlib/tests/test_table.py +++ b/lib/matplotlib/tests/test_table.py @@ -266,9 +266,8 @@ def __repr__(self): assert not munits.registry.get_converter(FakeUnit) -def test_table_dataframe(): +def test_table_dataframe(pd): # Test if Pandas Data Frame can be passed in cellText - pd = pytest.importorskip("pandas") data = { 'Letter': ['A', 'B', 'C'], From f71380695c8e33b1d39d19f2a71493d6a07cdae9 Mon Sep 17 00:00:00 2001 From: Aman Nijjar Date: Fri, 27 Sep 2024 21:26:47 -0700 Subject: [PATCH 10/14] modified stub to include DataFrame --- lib/matplotlib/table.pyi | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/table.pyi b/lib/matplotlib/table.pyi index 0108ecd99f89..a441c76ed55a 100644 --- a/lib/matplotlib/table.pyi +++ b/lib/matplotlib/table.pyi @@ -8,7 +8,12 @@ from .transforms import Bbox from .typing import ColorType from collections.abc import Sequence -from typing import Any, Literal +from typing import Any, Literal, TYPE_CHECKING + +if TYPE_CHECKING: + from pandas import DataFrame +else: + DataFrame = None class Cell(Rectangle): PAD: float @@ -68,7 +73,7 @@ class Table(Artist): def table( ax: Axes, - cellText: Sequence[Sequence[str]] | None = ..., + cellText: Sequence[Sequence[str]] | DataFrame | None = ..., cellColours: Sequence[Sequence[ColorType]] | None = ..., cellLoc: Literal["left", "center", "right"] = ..., colWidths: Sequence[float] | None = ..., From 707bc72aa8998acfcb3643b42c056dbd1f7d9c34 Mon Sep 17 00:00:00 2001 From: Aman Nijjar Date: Mon, 30 Sep 2024 16:04:02 -0700 Subject: [PATCH 11/14] modified .rst file and the test file following story645 recommendations --- .../pass_pandasDataFrame_into_table.rst | 3 +-- lib/matplotlib/tests/test_table.py | 11 +++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst b/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst index 68b62e68e3b4..1fff220c2dad 100644 --- a/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst +++ b/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst @@ -1,8 +1,7 @@ ``ax.table`` will accept a pandas dataframe -------------------------------------------- -The `~Axes.axes.table` method can now accept a data frame for the ``cellText`` method, which -it attempts to render with column headers set by ``df.columns.to_numpy()`` and cell data set by ``df.to_numpy()``. +The `~Axes.axes.table` method can now accept a Pandas DataFrame for the ``cellText`` argument. .. code-block:: python diff --git a/lib/matplotlib/tests/test_table.py b/lib/matplotlib/tests/test_table.py index 230d031e2a64..61d8ff2aad47 100644 --- a/lib/matplotlib/tests/test_table.py +++ b/lib/matplotlib/tests/test_table.py @@ -278,11 +278,6 @@ def test_table_dataframe(pd): fig, ax = plt.subplots() table = ax.table(df, loc='center') - assert table[0, 0].get_text().get_text() == 'Letter' - assert table[0, 1].get_text().get_text() == 'Number' - assert table[1, 0].get_text().get_text() == 'A' - assert table[1, 1].get_text().get_text() == str(100) - assert table[2, 0].get_text().get_text() == 'B' - assert table[2, 1].get_text().get_text() == str(200) - assert table[3, 0].get_text().get_text() == 'C' - assert table[3, 1].get_text().get_text() == str(300) + for r, (index, row) in enumerate(df.iterrows()): + for c, col in enumerate(df.columns if r == 0 else row.values): + assert table[r if r == 0 else r+1, c].get_text().get_text() == str(col) From dbcb91f26a2d7901a62328f86e7556e4bb091973 Mon Sep 17 00:00:00 2001 From: Aman Nijjar Date: Sun, 20 Oct 2024 21:42:31 -0700 Subject: [PATCH 12/14] Modified implementation to follow Pandas tools.py --- lib/matplotlib/table.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/table.py b/lib/matplotlib/table.py index 183aac91f55b..a344351ecc0d 100644 --- a/lib/matplotlib/table.py +++ b/lib/matplotlib/table.py @@ -748,10 +748,18 @@ def table(ax, # Check if we have a Pandas DataFrame if _is_pandas_dataframe(cellText): - # Convert to numpy array - header = cellText.columns.to_numpy() - data = cellText.to_numpy() - cellText = np.vstack([header, data]) + # if rowLabels/colLabels are empty, use DataFrame entries. + # Otherwise, throw an error. + if rowLabels is None: + rowLabels = cellText.index + else: + raise ValueError("rowLabels cannot be used alongside Pandas DataFrame") + if colLabels is None: + colLabels = cellText.columns + else: + raise ValueError("colLabels cannot be used alongside Pandas DataFrame") + # Update cellText with only values + cellText = cellText.values rows = len(cellText) cols = len(cellText[0]) From 19462841d36596d1daa42ccd00fde5af36aa432d Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Tue, 29 Oct 2024 09:18:57 +0100 Subject: [PATCH 13/14] Apply suggestions from code review Cleanup. --- doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst | 2 +- lib/matplotlib/table.pyi | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst b/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst index 1fff220c2dad..63ea3354a4b4 100644 --- a/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst +++ b/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst @@ -1,4 +1,4 @@ -``ax.table`` will accept a pandas dataframe +``ax.table`` will accept a pandas DataFrame -------------------------------------------- The `~Axes.axes.table` method can now accept a Pandas DataFrame for the ``cellText`` argument. diff --git a/lib/matplotlib/table.pyi b/lib/matplotlib/table.pyi index a441c76ed55a..07d2427f66dc 100644 --- a/lib/matplotlib/table.pyi +++ b/lib/matplotlib/table.pyi @@ -10,10 +10,7 @@ from .typing import ColorType from collections.abc import Sequence from typing import Any, Literal, TYPE_CHECKING -if TYPE_CHECKING: - from pandas import DataFrame -else: - DataFrame = None +from pandas import DataFrame class Cell(Rectangle): PAD: float From ef7d139ea25d817d9f98b0e162efcdb26435cd8a Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Tue, 29 Oct 2024 09:36:05 +0100 Subject: [PATCH 14/14] Fix code link --- doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst b/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst index 63ea3354a4b4..aaefa1b2011b 100644 --- a/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst +++ b/doc/users/next_whats_new/pass_pandasDataFrame_into_table.rst @@ -1,7 +1,7 @@ ``ax.table`` will accept a pandas DataFrame -------------------------------------------- -The `~Axes.axes.table` method can now accept a Pandas DataFrame for the ``cellText`` argument. +The `~.axes.Axes.table` method can now accept a Pandas DataFrame for the ``cellText`` argument. .. code-block:: python