From 7dc458640efef3f5dc920e4a45f45c693121b340 Mon Sep 17 00:00:00 2001 From: tarsur909 <68882529+tarsur909@users.noreply.github.com> Date: Sat, 29 Jul 2023 00:17:57 -0700 Subject: [PATCH 01/11] fix hardcoded scatter marker size issue #54204 --- doc/source/whatsnew/v2.1.0.rst | 1 + pandas/plotting/_matplotlib/core.py | 11 ++++++++--- pandas/tests/plotting/test_misc.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/doc/source/whatsnew/v2.1.0.rst b/doc/source/whatsnew/v2.1.0.rst index 2817945c55a86..9d9201d6bb1fa 100644 --- a/doc/source/whatsnew/v2.1.0.rst +++ b/doc/source/whatsnew/v2.1.0.rst @@ -785,6 +785,7 @@ Plotting ^^^^^^^^ - Bug in :meth:`Series.plot` when invoked with ``color=None`` (:issue:`51953`) - Fixed UserWarning in :meth:`DataFrame.plot.scatter` when invoked with ``c="b"`` (:issue:`53908`) +- Fixed bug in :meth:`DataFrame.plot.scatter` wherein marker size was previously hardcoded to a default value (:issue:`54204`) Groupby/resample/rolling ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index b8f59363b5107..7759a377de57d 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -1325,9 +1325,14 @@ def __init__( **kwargs, ) -> None: if s is None: - # hide the matplotlib default for size, in case we want to change - # the handling of this argument later - s = 20 + # The default size of the elements in a scatter plot + # is now based on the rcParam ``lines.markersize``. + # This means that if rcParams are temporarily changed, + # the marker size changes as well according to mpl.rc_context(). + if mpl.rcParams["_internal.classic_mode"]: + s = 20 + else: + s = mpl.rcParams["lines.markersize"] ** 2.0 elif is_hashable(s) and s in data.columns: s = data[s] self.s = s diff --git a/pandas/tests/plotting/test_misc.py b/pandas/tests/plotting/test_misc.py index 2d2ac1a437068..96e30c9f5f7b2 100644 --- a/pandas/tests/plotting/test_misc.py +++ b/pandas/tests/plotting/test_misc.py @@ -864,3 +864,32 @@ def test_plot_bar_label_count_expected_success(): [(30, 10, 10, 10), (20, 20, 20, 20), (10, 30, 30, 10)], columns=list("ABCD") ) df.plot(subplots=[("A", "B", "D")], kind="bar", title=["A&B&D", "C"]) + def test_change_scatter_markersize_rcparams(self): + # GH 54204 + # Ensure proper use of lines.markersize to style pandas scatter + # plots like matplotlib does + df = DataFrame(data={"x": [1, 2, 3], "y": [1, 2, 3]}) + + pandas_default = df.plot.scatter( + x="x", y="y", title="pandas scatter, default rc marker size" + ) + + mpl_default = mpl.pyplot.scatter(df["x"], df["y"]) + + # verify that pandas and matplotlib scatter + # default marker size are the same (s = 6^2 = 36) + assert ( + pandas_default.collections[0].get_sizes()[0] == mpl_default.get_sizes()[0] + ) + + with mpl.rc_context({"lines.markersize": 10}): + pandas_changed = df.plot.scatter( + x="x", y="y", title="pandas scatter, changed rc marker size" + ) + mpl_changed = mpl.pyplot.scatter(df["x"], df["y"]) + + # verify that pandas and matplotlib scatter + # default marker size are the same (s = 10^2 = 100) + assert ( + pandas_changed.collections[0].get_sizes()[0] == mpl_changed.get_sizes()[0] + ) From 6fdbbdbab07f5fd4d6ad18f65050d6b261ec9958 Mon Sep 17 00:00:00 2001 From: tarsur909 <68882529+tarsur909@users.noreply.github.com> Date: Mon, 31 Jul 2023 21:56:09 -0700 Subject: [PATCH 02/11] fix hardcoded scatter marker size issue #54204 --- pandas/plotting/_matplotlib/core.py | 12 ++++++++---- pandas/tests/plotting/test_misc.py | 6 ++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index 7759a377de57d..f34aecf24ac59 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -1329,10 +1329,14 @@ def __init__( # is now based on the rcParam ``lines.markersize``. # This means that if rcParams are temporarily changed, # the marker size changes as well according to mpl.rc_context(). - if mpl.rcParams["_internal.classic_mode"]: - s = 20 - else: - s = mpl.rcParams["lines.markersize"] ** 2.0 + warnings.warn( + """The default of s=20 is deprecated and + has changed to mpl.rcParams['lines.markersize']. + Specify `s` to suppress this warning""", + DeprecationWarning, + stacklevel=find_stack_level(), + ) + s = mpl.rcParams["lines.markersize"] ** 2.0 elif is_hashable(s) and s in data.columns: s = data[s] self.s = s diff --git a/pandas/tests/plotting/test_misc.py b/pandas/tests/plotting/test_misc.py index 96e30c9f5f7b2..1db0924b35134 100644 --- a/pandas/tests/plotting/test_misc.py +++ b/pandas/tests/plotting/test_misc.py @@ -864,10 +864,12 @@ def test_plot_bar_label_count_expected_success(): [(30, 10, 10, 10), (20, 20, 20, 20), (10, 30, 30, 10)], columns=list("ABCD") ) df.plot(subplots=[("A", "B", "D")], kind="bar", title=["A&B&D", "C"]) + @pytest.mark.filterwarnings("default") def test_change_scatter_markersize_rcparams(self): # GH 54204 # Ensure proper use of lines.markersize to style pandas scatter - # plots like matplotlib does + # plots like matplotlib does. + # Will raise deprecation warnings. df = DataFrame(data={"x": [1, 2, 3], "y": [1, 2, 3]}) pandas_default = df.plot.scatter( @@ -889,7 +891,7 @@ def test_change_scatter_markersize_rcparams(self): mpl_changed = mpl.pyplot.scatter(df["x"], df["y"]) # verify that pandas and matplotlib scatter - # default marker size are the same (s = 10^2 = 100) + # changed marker size are the same (s = 10^2 = 100) assert ( pandas_changed.collections[0].get_sizes()[0] == mpl_changed.get_sizes()[0] ) From 98abab2891aedafc5a0cf31f588b1e4a182a37f3 Mon Sep 17 00:00:00 2001 From: tarsur909 <68882529+tarsur909@users.noreply.github.com> Date: Mon, 31 Jul 2023 22:17:29 -0700 Subject: [PATCH 03/11] fix hardcoded scatter marker size issue #54204 --- .../getting_started/intro_tutorials/04_plotting.rst | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/doc/source/getting_started/intro_tutorials/04_plotting.rst b/doc/source/getting_started/intro_tutorials/04_plotting.rst index e9f83c602d086..0ea99b6505d4c 100644 --- a/doc/source/getting_started/intro_tutorials/04_plotting.rst +++ b/doc/source/getting_started/intro_tutorials/04_plotting.rst @@ -122,11 +122,14 @@ standard Python to get an overview of the available plot methods: .. ipython:: python - [ - method_name - for method_name in dir(air_quality.plot) - if not method_name.startswith("_") - ] + import warnings + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + [ + method_name + for method_name in dir(air_quality.plot) + if not method_name.startswith("_") + ] .. note:: In many development environments such as IPython and From 868fee324f840e042da7257d8356debe88f2b5a8 Mon Sep 17 00:00:00 2001 From: tarsur909 <68882529+tarsur909@users.noreply.github.com> Date: Mon, 31 Jul 2023 22:28:48 -0700 Subject: [PATCH 04/11] fix hardcoded scatter marker size issue #54204 --- doc/source/getting_started/intro_tutorials/04_plotting.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/getting_started/intro_tutorials/04_plotting.rst b/doc/source/getting_started/intro_tutorials/04_plotting.rst index 0ea99b6505d4c..f02b0cef8fc81 100644 --- a/doc/source/getting_started/intro_tutorials/04_plotting.rst +++ b/doc/source/getting_started/intro_tutorials/04_plotting.rst @@ -108,7 +108,7 @@ I want to visually compare the :math:`NO_2` values measured in London versus Par :okwarning: @savefig 04_airqual_scatter.png - air_quality.plot.scatter(x="station_london", y="station_paris", alpha=0.5) + air_quality.plot.scatter(x="station_london", y="station_paris", s = 20, alpha=0.5) plt.show() .. raw:: html From 44ed8f42c17ff0ab88457d8c9c6468b523592756 Mon Sep 17 00:00:00 2001 From: tarsur909 <68882529+tarsur909@users.noreply.github.com> Date: Mon, 31 Jul 2023 22:40:31 -0700 Subject: [PATCH 05/11] fix hardcoded scatter marker size issue #54204 --- doc/source/user_guide/dsintro.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/user_guide/dsintro.rst b/doc/source/user_guide/dsintro.rst index 919dafb291b86..b025a5a0ae6cb 100644 --- a/doc/source/user_guide/dsintro.rst +++ b/doc/source/user_guide/dsintro.rst @@ -577,7 +577,7 @@ greater than 5, calculate the ratio, and plot: SepalRatio=lambda x: x.SepalWidth / x.SepalLength, PetalRatio=lambda x: x.PetalWidth / x.PetalLength, ) - .plot(kind="scatter", x="SepalRatio", y="PetalRatio") + .plot(kind="scatter", x="SepalRatio", y="PetalRatio", s = 20) ) Since a function is passed in, the function is computed on the DataFrame From 2e62132243033115c4db1fc19022f6207c6f164c Mon Sep 17 00:00:00 2001 From: tarsur909 <68882529+tarsur909@users.noreply.github.com> Date: Mon, 31 Jul 2023 22:58:28 -0700 Subject: [PATCH 06/11] fix hardcoded scatter marker size issue #54204 --- doc/source/user_guide/visualization.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/user_guide/visualization.rst b/doc/source/user_guide/visualization.rst index 4b5cdca23103c..388b3d72a2b57 100644 --- a/doc/source/user_guide/visualization.rst +++ b/doc/source/user_guide/visualization.rst @@ -626,7 +626,7 @@ It is recommended to specify ``color`` and ``label`` keywords to distinguish eac ax = df.plot.scatter(x="a", y="b", color="DarkBlue", label="Group 1") @savefig scatter_plot_repeated.png - df.plot.scatter(x="c", y="d", color="DarkGreen", label="Group 2", ax=ax); + df.plot.scatter(x="c", y="d", color="DarkGreen", label="Group 2", ax=ax, s = 20); .. ipython:: python :suppress: From a67ae8d69ea01305e0337c28dca6ed000700e799 Mon Sep 17 00:00:00 2001 From: HollisHolmes <68450234+HollisHolmes@users.noreply.github.com> Date: Thu, 30 Oct 2025 02:14:23 +0000 Subject: [PATCH 07/11] set default to 20 and raise PandasFutureWarning --- pandas/plotting/_matplotlib/core.py | 34 +++++++++++++++-------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index f34aecf24ac59..e9c13dd3294d1 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -19,11 +19,13 @@ ) import warnings -import matplotlib as mpl import numpy as np from pandas._libs import lib -from pandas.errors import AbstractMethodError +from pandas.errors import ( + AbstractMethodError, + PandasFutureWarning, +) from pandas.util._decorators import cache_readonly from pandas.util._exceptions import find_stack_level @@ -54,6 +56,7 @@ ) from pandas.core.dtypes.missing import isna +import matplotlib as mpl import pandas.core.common as com from pandas.io.formats.printing import pprint_thing @@ -81,11 +84,6 @@ ) if TYPE_CHECKING: - from matplotlib.artist import Artist - from matplotlib.axes import Axes - from matplotlib.axis import Axis - from matplotlib.figure import Figure - from pandas._typing import ( IndexLabel, NDFrameT, @@ -93,6 +91,10 @@ npt, ) + from matplotlib.artist import Artist + from matplotlib.axes import Axes + from matplotlib.axis import Axis + from matplotlib.figure import Figure from pandas import ( DataFrame, Index, @@ -1325,18 +1327,18 @@ def __init__( **kwargs, ) -> None: if s is None: - # The default size of the elements in a scatter plot - # is now based on the rcParam ``lines.markersize``. - # This means that if rcParams are temporarily changed, - # the marker size changes as well according to mpl.rc_context(). + # The default size of the elements in a scatter plot + # is 20, but this will change in a future version. + # In the future the value will be derived from + # mpl.rcParams["lines.markersize"] if not provided warnings.warn( - """The default of s=20 is deprecated and - has changed to mpl.rcParams['lines.markersize']. - Specify `s` to suppress this warning""", - DeprecationWarning, + "The default of s=20 will be changed to use " + "mpl.rcParams['lines.markersize'] in the future. " + "Specify `s` to suppress this warning", + PandasFutureWarning, stacklevel=find_stack_level(), ) - s = mpl.rcParams["lines.markersize"] ** 2.0 + s = 20 elif is_hashable(s) and s in data.columns: s = data[s] self.s = s From 62472c9c86dadafb92c9086cbfee40dadf474596 Mon Sep 17 00:00:00 2001 From: HollisHolmes <68450234+HollisHolmes@users.noreply.github.com> Date: Thu, 30 Oct 2025 02:14:42 +0000 Subject: [PATCH 08/11] update docs warning --- .../intro_tutorials/04_plotting.rst | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/doc/source/getting_started/intro_tutorials/04_plotting.rst b/doc/source/getting_started/intro_tutorials/04_plotting.rst index f02b0cef8fc81..e963a63881cd8 100644 --- a/doc/source/getting_started/intro_tutorials/04_plotting.rst +++ b/doc/source/getting_started/intro_tutorials/04_plotting.rst @@ -121,15 +121,13 @@ number of alternatives are available to plot data. Let’s use some standard Python to get an overview of the available plot methods: .. ipython:: python + :okwarning: - import warnings - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - [ - method_name - for method_name in dir(air_quality.plot) - if not method_name.startswith("_") - ] + [ + method_name + for method_name in dir(air_quality.plot) + if not method_name.startswith("_") + ] .. note:: In many development environments such as IPython and From b967f9fe3adbe629491a0de26b815d244440b4ce Mon Sep 17 00:00:00 2001 From: HollisHolmes <68450234+HollisHolmes@users.noreply.github.com> Date: Thu, 30 Oct 2025 02:17:31 +0000 Subject: [PATCH 09/11] check default and FutureWarning thrown --- pandas/tests/plotting/test_misc.py | 60 ++++++++++++++++++------------ 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/pandas/tests/plotting/test_misc.py b/pandas/tests/plotting/test_misc.py index 1db0924b35134..6cb9605f431f8 100644 --- a/pandas/tests/plotting/test_misc.py +++ b/pandas/tests/plotting/test_misc.py @@ -5,6 +5,7 @@ import numpy as np import pytest +from pandas.errors import PandasFutureWarning import pandas.util._test_decorators as td from pandas import ( @@ -864,34 +865,45 @@ def test_plot_bar_label_count_expected_success(): [(30, 10, 10, 10), (20, 20, 20, 20), (10, 30, 30, 10)], columns=list("ABCD") ) df.plot(subplots=[("A", "B", "D")], kind="bar", title=["A&B&D", "C"]) - @pytest.mark.filterwarnings("default") - def test_change_scatter_markersize_rcparams(self): - # GH 54204 - # Ensure proper use of lines.markersize to style pandas scatter - # plots like matplotlib does. - # Will raise deprecation warnings. - df = DataFrame(data={"x": [1, 2, 3], "y": [1, 2, 3]}) - - pandas_default = df.plot.scatter( + + +def testhhh_change_scatter_markersize_future_warning(): + # GH 54204 + # Will raise FutureWarning if s not provided to df.plot.scatter + df = DataFrame(data={"x": [1, 2, 3], "y": [1, 2, 3]}) + with tm.assert_produces_warning(PandasFutureWarning): + pandas_default_without_rcparams = df.plot.scatter( x="x", y="y", title="pandas scatter, default rc marker size" ) + # Verify that pandas still defaults to 20 + assert pandas_default_without_rcparams.collections[0].get_sizes()[0] == 20 - mpl_default = mpl.pyplot.scatter(df["x"], df["y"]) - # verify that pandas and matplotlib scatter - # default marker size are the same (s = 6^2 = 36) - assert ( - pandas_default.collections[0].get_sizes()[0] == mpl_default.get_sizes()[0] +@pytest.mark.filterwarnings( + "ignore:The default of s=20 will be changed:pandas.errors.PandasFutureWarning" +) +def testhhh_scatter_markersize_same_default_with_rcparams(): + # GH 54204 + # Ensure default markersize is still 20 if no rcparams + df = DataFrame(data={"x": [1, 2, 3], "y": [1, 2, 3]}) + with mpl.rc_context({"lines.markersize": 10}): + pandas_default_with_rcparams = df.plot.scatter( + x="x", y="y", title="pandas scatter, changed rc marker size" ) + # Verify that pandas default markersize is still 20 with rc_params + assert pandas_default_with_rcparams.collections[0].get_sizes()[0] == 20 - with mpl.rc_context({"lines.markersize": 10}): - pandas_changed = df.plot.scatter( - x="x", y="y", title="pandas scatter, changed rc marker size" - ) - mpl_changed = mpl.pyplot.scatter(df["x"], df["y"]) - # verify that pandas and matplotlib scatter - # changed marker size are the same (s = 10^2 = 100) - assert ( - pandas_changed.collections[0].get_sizes()[0] == mpl_changed.get_sizes()[0] - ) +@pytest.mark.filterwarnings( + "ignore:The default of s=20 will be changed:pandas.errors.PandasFutureWarning" +) +def testhhh_scatter_markersize_same_default_without_rcparams(): + # GH 54204 + # Ensure default markersize is still 20 if no rcparams + df = DataFrame(data={"x": [1, 2, 3], "y": [1, 2, 3]}) + + pandas_default_with_rcparams = df.plot.scatter( + x="x", y="y", title="pandas scatter, changed rc marker size" + ) + # Verify that pandas default markersize is still 20 without rc_params + assert pandas_default_with_rcparams.collections[0].get_sizes()[0] == 20 From 9ae77ebd1b542c96a838c563398fc544f52b57a2 Mon Sep 17 00:00:00 2001 From: HollisHolmes <68450234+HollisHolmes@users.noreply.github.com> Date: Thu, 30 Oct 2025 02:20:10 +0000 Subject: [PATCH 10/11] fix test name --- pandas/tests/plotting/test_misc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/tests/plotting/test_misc.py b/pandas/tests/plotting/test_misc.py index 6cb9605f431f8..cbd287726c760 100644 --- a/pandas/tests/plotting/test_misc.py +++ b/pandas/tests/plotting/test_misc.py @@ -867,7 +867,7 @@ def test_plot_bar_label_count_expected_success(): df.plot(subplots=[("A", "B", "D")], kind="bar", title=["A&B&D", "C"]) -def testhhh_change_scatter_markersize_future_warning(): +def test_change_scatter_markersize_future_warning(): # GH 54204 # Will raise FutureWarning if s not provided to df.plot.scatter df = DataFrame(data={"x": [1, 2, 3], "y": [1, 2, 3]}) @@ -882,7 +882,7 @@ def testhhh_change_scatter_markersize_future_warning(): @pytest.mark.filterwarnings( "ignore:The default of s=20 will be changed:pandas.errors.PandasFutureWarning" ) -def testhhh_scatter_markersize_same_default_with_rcparams(): +def test_scatter_markersize_same_default_with_rcparams(): # GH 54204 # Ensure default markersize is still 20 if no rcparams df = DataFrame(data={"x": [1, 2, 3], "y": [1, 2, 3]}) @@ -897,7 +897,7 @@ def testhhh_scatter_markersize_same_default_with_rcparams(): @pytest.mark.filterwarnings( "ignore:The default of s=20 will be changed:pandas.errors.PandasFutureWarning" ) -def testhhh_scatter_markersize_same_default_without_rcparams(): +def test_scatter_markersize_same_default_without_rcparams(): # GH 54204 # Ensure default markersize is still 20 if no rcparams df = DataFrame(data={"x": [1, 2, 3], "y": [1, 2, 3]}) From 8afd38dd555112029134ac8e3bcd11bd301165c7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 02:24:53 +0000 Subject: [PATCH 11/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- pandas/plotting/_matplotlib/core.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pandas/plotting/_matplotlib/core.py b/pandas/plotting/_matplotlib/core.py index e9c13dd3294d1..62942778b0d5e 100644 --- a/pandas/plotting/_matplotlib/core.py +++ b/pandas/plotting/_matplotlib/core.py @@ -19,6 +19,7 @@ ) import warnings +import matplotlib as mpl import numpy as np from pandas._libs import lib @@ -56,7 +57,6 @@ ) from pandas.core.dtypes.missing import isna -import matplotlib as mpl import pandas.core.common as com from pandas.io.formats.printing import pprint_thing @@ -84,6 +84,11 @@ ) if TYPE_CHECKING: + from matplotlib.artist import Artist + from matplotlib.axes import Axes + from matplotlib.axis import Axis + from matplotlib.figure import Figure + from pandas._typing import ( IndexLabel, NDFrameT, @@ -91,10 +96,6 @@ npt, ) - from matplotlib.artist import Artist - from matplotlib.axes import Axes - from matplotlib.axis import Axis - from matplotlib.figure import Figure from pandas import ( DataFrame, Index,