diff --git a/doc/changelog.qmd b/doc/changelog.qmd index 372019406..feb2569b9 100644 --- a/doc/changelog.qmd +++ b/doc/changelog.qmd @@ -60,6 +60,12 @@ title: Changelog layer(geom=geom_point()) ``` +- `geom.DEFAULT_PARAMS` now implicitly includes `stat="identity"`, `position="identity` + and `na_rm=False`. + +- `stat.DEFAULT_PARAMS` now implicitly includes `geom="blank"`, `position="identity` + and `na_rm=False`. + ### Bug Fixes - Fixed [](:class:`~plotnine.geom_smooth`) / [](:class:`~plotnine.stat_smooth`) when using a linear model via "lm" with weights for the model to do a weighted regression. This bug did not affect the formula API of the linear model. ({{< issue 1005 >}}) diff --git a/plotnine/doctools.py b/plotnine/doctools.py index 4aa23a89b..ff9a4fb89 100644 --- a/plotnine/doctools.py +++ b/plotnine/doctools.py @@ -3,11 +3,11 @@ import itertools import os import re -import typing from functools import lru_cache from textwrap import dedent, indent +from typing import TYPE_CHECKING -if typing.TYPE_CHECKING: +if TYPE_CHECKING: from typing import Any, Sequence, Type, TypeVar from plotnine.geoms.geom import geom @@ -429,13 +429,15 @@ def document_geom(geom: type[geom]) -> type[geom]: It replaces `{usage}`, `{common_parameters}` and `{aesthetics}` with generated documentation. """ + from plotnine.geoms.geom import _BASE_PARAMS + docstring = dedent(geom.__doc__ or "") docstring = append_to_section(geom_kwargs, docstring, "Parameters") # usage signature = make_signature( geom.__name__, - geom.DEFAULT_PARAMS, + _BASE_PARAMS | geom.DEFAULT_PARAMS, common_geom_params, common_geom_param_values, ) @@ -479,6 +481,8 @@ def document_stat(stat: type[stat]) -> type[stat]: It replaces `{usage}`, `{common_parameters}` and `{aesthetics}` with generated documentation. """ + from plotnine.stats.stat import _BASE_PARAMS + # Dedented so that it lineups (in sphinx) with the part # generated parts when put together docstring = dedent(stat.__doc__ or "") @@ -487,7 +491,7 @@ def document_stat(stat: type[stat]) -> type[stat]: # usage: signature = make_signature( stat.__name__, - stat.DEFAULT_PARAMS, + _BASE_PARAMS | stat.DEFAULT_PARAMS, common_stat_params, common_stat_param_values, ) diff --git a/plotnine/geoms/annotation_logticks.py b/plotnine/geoms/annotation_logticks.py index 33513e0f6..57494b23c 100644 --- a/plotnine/geoms/annotation_logticks.py +++ b/plotnine/geoms/annotation_logticks.py @@ -33,9 +33,6 @@ class _geom_logticks(geom_rug): DEFAULT_AES = {} DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, "sides": "bl", "alpha": 1, "color": "black", diff --git a/plotnine/geoms/annotation_stripes.py b/plotnine/geoms/annotation_stripes.py index d9b540d2a..e4c8254c7 100644 --- a/plotnine/geoms/annotation_stripes.py +++ b/plotnine/geoms/annotation_stripes.py @@ -82,9 +82,6 @@ class _geom_stripes(geom): DEFAULT_AES = {} REQUIRED_AES = set() DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, "color": None, "fill": ("#AAAAAA", "#CCCCCC"), "linetype": "solid", diff --git a/plotnine/geoms/geom.py b/plotnine/geoms/geom.py index 38ff079b6..f172e6a9e 100644 --- a/plotnine/geoms/geom.py +++ b/plotnine/geoms/geom.py @@ -33,6 +33,13 @@ from plotnine.typing import DataLike +_BASE_PARAMS: dict[str, Any] = { + "stat": "identity", + "position": "identity", + "na_rm": False, +} + + class geom(ABC, metaclass=Register): """Base class of all Geoms""" @@ -82,11 +89,12 @@ def __init__( self._raw_kwargs = kwargs # Will be used to create stat & layer # separate aesthetics and parameters + possible_params = _BASE_PARAMS | self.DEFAULT_PARAMS self.aes_params = { ae: kwargs[ae] for ae in self.aesthetics() & set(kwargs) } - self.params = self.DEFAULT_PARAMS | { - k: v for k, v in kwargs.items() if k in self.DEFAULT_PARAMS + self.params = possible_params | { + k: v for k, v in kwargs.items() if k in possible_params } self.mapping = kwargs["mapping"] self.data = kwargs["data"] @@ -459,7 +467,7 @@ def handle_na(self, data: pd.DataFrame) -> pd.DataFrame: """ return remove_missing( data, - self.params["na_rm"], + self.params.get("na_rm", False), list(self.REQUIRED_AES | self.NON_MISSING_AES), self.__class__.__name__, ) diff --git a/plotnine/geoms/geom_abline.py b/plotnine/geoms/geom_abline.py index 823d099da..ca0a64448 100644 --- a/plotnine/geoms/geom_abline.py +++ b/plotnine/geoms/geom_abline.py @@ -43,12 +43,7 @@ class geom_abline(geom): "alpha": 1, "size": 0.5, } - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, - "inherit_aes": False, - } + DEFAULT_PARAMS = {"inherit_aes": False} REQUIRED_AES = {"slope", "intercept"} draw_legend = staticmethod(geom_path.draw_legend) diff --git a/plotnine/geoms/geom_area.py b/plotnine/geoms/geom_area.py index 880c26e62..05ca24bd0 100644 --- a/plotnine/geoms/geom_area.py +++ b/plotnine/geoms/geom_area.py @@ -30,11 +30,7 @@ class geom_area(geom_ribbon): """ REQUIRED_AES = {"x", "y"} - DEFAULT_PARAMS = { - **geom_ribbon.DEFAULT_PARAMS, - "position": "stack", - "outline_type": "upper", - } + DEFAULT_PARAMS = {"position": "stack", "outline_type": "upper"} def setup_data(self, data: pd.DataFrame) -> pd.DataFrame: data["ymin"] = 0 diff --git a/plotnine/geoms/geom_bar.py b/plotnine/geoms/geom_bar.py index bf6d02538..8eba32635 100644 --- a/plotnine/geoms/geom_bar.py +++ b/plotnine/geoms/geom_bar.py @@ -40,7 +40,6 @@ class geom_bar(geom_rect): DEFAULT_PARAMS = { "stat": "count", "position": "stack", - "na_rm": False, "just": 0.5, "width": None, } diff --git a/plotnine/geoms/geom_bin_2d.py b/plotnine/geoms/geom_bin_2d.py index 97a816496..019f80cc5 100644 --- a/plotnine/geoms/geom_bin_2d.py +++ b/plotnine/geoms/geom_bin_2d.py @@ -23,7 +23,7 @@ class geom_bin_2d(geom_rect): plotnine.stat_bin_2d : The default stat for this `geom`. """ - DEFAULT_PARAMS = {"stat": "bin_2d", "position": "identity", "na_rm": False} + DEFAULT_PARAMS = {"stat": "bin_2d"} geom_bin2d = geom_bin_2d diff --git a/plotnine/geoms/geom_blank.py b/plotnine/geoms/geom_blank.py index 2fd00f532..0aae26c87 100644 --- a/plotnine/geoms/geom_blank.py +++ b/plotnine/geoms/geom_blank.py @@ -25,12 +25,6 @@ class geom_blank(geom): {common_parameters} """ - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, - } - def draw_panel( self, data: pd.DataFrame, diff --git a/plotnine/geoms/geom_boxplot.py b/plotnine/geoms/geom_boxplot.py index effbba664..d428f3afc 100644 --- a/plotnine/geoms/geom_boxplot.py +++ b/plotnine/geoms/geom_boxplot.py @@ -89,7 +89,6 @@ class geom_boxplot(geom): DEFAULT_PARAMS = { "stat": "boxplot", "position": "dodge2", - "na_rm": False, "width": None, "outlier_alpha": 1, "outlier_color": None, diff --git a/plotnine/geoms/geom_col.py b/plotnine/geoms/geom_col.py index 8235fe2e9..fc824f169 100644 --- a/plotnine/geoms/geom_col.py +++ b/plotnine/geoms/geom_col.py @@ -33,10 +33,4 @@ class geom_col(geom_bar): REQUIRED_AES = {"x", "y"} NON_MISSING_AES = {"xmin", "xmax", "ymin", "ymax"} - DEFAULT_PARAMS = { - "stat": "identity", - "position": "stack", - "na_rm": False, - "just": 0.5, - "width": None, - } + DEFAULT_PARAMS = {"position": "stack", "just": 0.5, "width": None} diff --git a/plotnine/geoms/geom_count.py b/plotnine/geoms/geom_count.py index 9dd629112..fc309aec8 100644 --- a/plotnine/geoms/geom_count.py +++ b/plotnine/geoms/geom_count.py @@ -22,4 +22,4 @@ class geom_count(geom_point): plotnine.stat_sum : The default `stat` for this `geom`. """ - DEFAULT_PARAMS = {"stat": "sum", "position": "identity", "na_rm": False} + DEFAULT_PARAMS = {"stat": "sum"} diff --git a/plotnine/geoms/geom_crossbar.py b/plotnine/geoms/geom_crossbar.py index fbaad161a..736ceaec5 100644 --- a/plotnine/geoms/geom_crossbar.py +++ b/plotnine/geoms/geom_crossbar.py @@ -50,13 +50,7 @@ class geom_crossbar(geom): "size": 0.5, } REQUIRED_AES = {"x", "y", "ymin", "ymax"} - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, - "width": 0.5, - "fatten": 2, - } + DEFAULT_PARAMS = {"width": 0.5, "fatten": 2} legend_key_size = staticmethod(geom_segment.legend_key_size) diff --git a/plotnine/geoms/geom_density.py b/plotnine/geoms/geom_density.py index 0ae32f388..4965d2059 100644 --- a/plotnine/geoms/geom_density.py +++ b/plotnine/geoms/geom_density.py @@ -25,8 +25,4 @@ class geom_density(geom_area): "weight": 1, } - DEFAULT_PARAMS = { - **geom_area.DEFAULT_PARAMS, - "stat": "density", - "position": "identity", - } + DEFAULT_PARAMS = {"stat": "density", "outline_type": "upper"} diff --git a/plotnine/geoms/geom_density_2d.py b/plotnine/geoms/geom_density_2d.py index d96393a6f..1d756e3f5 100644 --- a/plotnine/geoms/geom_density_2d.py +++ b/plotnine/geoms/geom_density_2d.py @@ -20,8 +20,4 @@ class geom_density_2d(geom_path): plotnine.stat_density_2d : The default `stat` for this `geom`. """ - DEFAULT_PARAMS = { - "stat": "density_2d", - "position": "identity", - "na_rm": False, - } + DEFAULT_PARAMS = {"stat": "density_2d"} diff --git a/plotnine/geoms/geom_dotplot.py b/plotnine/geoms/geom_dotplot.py index 2fb36ec6d..ee0031916 100644 --- a/plotnine/geoms/geom_dotplot.py +++ b/plotnine/geoms/geom_dotplot.py @@ -54,8 +54,6 @@ class geom_dotplot(geom): NON_MISSING_AES = {"size", "shape"} DEFAULT_PARAMS = { "stat": "bindot", - "position": "identity", - "na_rm": False, "stackdir": "up", "stackratio": 1, "dotsize": 1, diff --git a/plotnine/geoms/geom_errorbar.py b/plotnine/geoms/geom_errorbar.py index 9edced099..7b1f19e89 100644 --- a/plotnine/geoms/geom_errorbar.py +++ b/plotnine/geoms/geom_errorbar.py @@ -41,12 +41,8 @@ class geom_errorbar(geom): "size": 0.5, } REQUIRED_AES = {"x", "ymin", "ymax"} - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, - "width": 0.5, - } + DEFAULT_PARAMS = {"width": 0.5} + draw_legend = staticmethod(geom_path.draw_legend) def setup_data(self, data: pd.DataFrame) -> pd.DataFrame: diff --git a/plotnine/geoms/geom_errorbarh.py b/plotnine/geoms/geom_errorbarh.py index 5c7fa7d5f..408ecbbcc 100644 --- a/plotnine/geoms/geom_errorbarh.py +++ b/plotnine/geoms/geom_errorbarh.py @@ -41,12 +41,8 @@ class geom_errorbarh(geom): "size": 0.5, } REQUIRED_AES = {"y", "xmin", "xmax"} - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, - "height": 0.5, - } + DEFAULT_PARAMS = {"height": 0.5} + draw_legend = staticmethod(geom_path.draw_legend) def setup_data(self, data: pd.DataFrame) -> pd.DataFrame: diff --git a/plotnine/geoms/geom_freqpoly.py b/plotnine/geoms/geom_freqpoly.py index d1a9ee30c..6fea81dbd 100644 --- a/plotnine/geoms/geom_freqpoly.py +++ b/plotnine/geoms/geom_freqpoly.py @@ -1,9 +1,6 @@ from ..doctools import document from .geom_path import geom_path -_params = geom_path.DEFAULT_PARAMS.copy() -_params["stat"] = "bin" - @document class geom_freqpoly(geom_path): @@ -16,4 +13,9 @@ class geom_freqpoly(geom_path): of the parameters. """ - DEFAULT_PARAMS = _params + DEFAULT_PARAMS = { + "stat": "bin", + "lineend": "butt", + "linejoin": "round", + "arrow": None, + } diff --git a/plotnine/geoms/geom_histogram.py b/plotnine/geoms/geom_histogram.py index c96209f2b..f3d2ce07a 100644 --- a/plotnine/geoms/geom_histogram.py +++ b/plotnine/geoms/geom_histogram.py @@ -18,4 +18,4 @@ class geom_histogram(geom_bar): plotnine.geom_bar : The default `stat` for this `geom`. """ - DEFAULT_PARAMS = {"stat": "bin", "position": "stack", "na_rm": False} + DEFAULT_PARAMS = {"stat": "bin", "position": "stack"} diff --git a/plotnine/geoms/geom_hline.py b/plotnine/geoms/geom_hline.py index 393f40a0e..8ee9302e3 100644 --- a/plotnine/geoms/geom_hline.py +++ b/plotnine/geoms/geom_hline.py @@ -43,12 +43,7 @@ class geom_hline(geom): "alpha": 1, } REQUIRED_AES = {"yintercept"} - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, - "inherit_aes": False, - } + DEFAULT_PARAMS = {"inherit_aes": False} draw_legend = staticmethod(geom_path.draw_legend) legend_key_size = staticmethod(geom_path.legend_key_size) diff --git a/plotnine/geoms/geom_jitter.py b/plotnine/geoms/geom_jitter.py index 6157bed36..d6ca6d90a 100644 --- a/plotnine/geoms/geom_jitter.py +++ b/plotnine/geoms/geom_jitter.py @@ -43,9 +43,7 @@ class geom_jitter(geom_point): """ DEFAULT_PARAMS = { - "stat": "identity", "position": "jitter", - "na_rm": False, "width": None, "height": None, "random_state": None, diff --git a/plotnine/geoms/geom_label.py b/plotnine/geoms/geom_label.py index 9498d28a5..6de3f43d4 100644 --- a/plotnine/geoms/geom_label.py +++ b/plotnine/geoms/geom_label.py @@ -15,27 +15,6 @@ from plotnine.layer import layer -_aes = geom_text.DEFAULT_AES.copy() -_aes["fill"] = "white" - -_params = geom_text.DEFAULT_PARAMS.copy() -_params.update( - { - # boxstyle is one of - # circle, larrow, rarrow, round, round4, - # roundtooth, sawtooth, square - # - # Documentation at matplotlib.patches.BoxStyle - "boxstyle": "round", - "boxcolor": None, - "label_padding": 0.25, - "label_r": 0.25, - "label_size": 0.7, - "tooth_size": None, - } -) - - @document class geom_label(geom_text): """ @@ -80,8 +59,21 @@ class geom_label(geom_text): parameters that affect the boxstyle. """ - DEFAULT_AES = _aes - DEFAULT_PARAMS = _params + DEFAULT_AES = {**geom_text.DEFAULT_AES, "fill": "white"} + DEFAULT_PARAMS = { + **geom_text.DEFAULT_PARAMS, + # boxstyle is one of + # circle, larrow, rarrow, round, round4, + # roundtooth, sawtooth, square + # + # Documentation at matplotlib.patches.BoxStyle + "boxstyle": "round", + "boxcolor": None, + "label_padding": 0.25, + "label_r": 0.25, + "label_size": 0.7, + "tooth_size": None, + } @staticmethod def draw_legend( diff --git a/plotnine/geoms/geom_linerange.py b/plotnine/geoms/geom_linerange.py index 3a3c2bd28..aef96d8d0 100644 --- a/plotnine/geoms/geom_linerange.py +++ b/plotnine/geoms/geom_linerange.py @@ -36,11 +36,7 @@ class geom_linerange(geom): "size": 0.5, } REQUIRED_AES = {"x", "ymin", "ymax"} - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, - } + draw_legend = staticmethod(geom_path.draw_legend) @staticmethod diff --git a/plotnine/geoms/geom_map.py b/plotnine/geoms/geom_map.py index bf2473092..f914287c4 100644 --- a/plotnine/geoms/geom_map.py +++ b/plotnine/geoms/geom_map.py @@ -57,11 +57,6 @@ class geom_map(geom): "size": 0.5, "stroke": 0.5, } - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, - } REQUIRED_AES = {"geometry"} def __init__( diff --git a/plotnine/geoms/geom_path.py b/plotnine/geoms/geom_path.py index d830953fc..7f0fa12b8 100644 --- a/plotnine/geoms/geom_path.py +++ b/plotnine/geoms/geom_path.py @@ -58,9 +58,6 @@ class geom_path(geom): REQUIRED_AES = {"x", "y"} DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, "lineend": "butt", "linejoin": "round", "arrow": None, diff --git a/plotnine/geoms/geom_point.py b/plotnine/geoms/geom_point.py index 51f00c381..96559b385 100644 --- a/plotnine/geoms/geom_point.py +++ b/plotnine/geoms/geom_point.py @@ -43,11 +43,6 @@ class geom_point(geom): } REQUIRED_AES = {"x", "y"} NON_MISSING_AES = {"color", "shape", "size"} - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, - } def draw_panel( self, diff --git a/plotnine/geoms/geom_pointdensity.py b/plotnine/geoms/geom_pointdensity.py index 8edb58f77..8a008e1b7 100644 --- a/plotnine/geoms/geom_pointdensity.py +++ b/plotnine/geoms/geom_pointdensity.py @@ -18,8 +18,4 @@ class geom_pointdensity(geom_point): plotnine.stat_pointdensity : The default `stat` for this `geom`. """ - DEFAULT_PARAMS = { - "stat": "pointdensity", - "position": "identity", - "na_rm": False, - } + DEFAULT_PARAMS = {"stat": "pointdensity"} diff --git a/plotnine/geoms/geom_pointrange.py b/plotnine/geoms/geom_pointrange.py index b5daa40ca..7f4ee87fb 100644 --- a/plotnine/geoms/geom_pointrange.py +++ b/plotnine/geoms/geom_pointrange.py @@ -45,12 +45,7 @@ class geom_pointrange(geom): "size": 0.5, } REQUIRED_AES = {"x", "y", "ymin", "ymax"} - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, - "fatten": 4, - } + DEFAULT_PARAMS = {"fatten": 4} @staticmethod def draw_group( diff --git a/plotnine/geoms/geom_polygon.py b/plotnine/geoms/geom_polygon.py index 7c43fe6bd..add11541e 100644 --- a/plotnine/geoms/geom_polygon.py +++ b/plotnine/geoms/geom_polygon.py @@ -44,11 +44,6 @@ class geom_polygon(geom): "linetype": "solid", "size": 0.5, } - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, - } REQUIRED_AES = {"x", "y"} legend_key_size = staticmethod(geom_path.legend_key_size) diff --git a/plotnine/geoms/geom_qq.py b/plotnine/geoms/geom_qq.py index 15f02fb89..efe1c4b21 100644 --- a/plotnine/geoms/geom_qq.py +++ b/plotnine/geoms/geom_qq.py @@ -18,4 +18,4 @@ class geom_qq(geom_point): plotnine.stat_qq : The default `stat` for this `geom`. """ - DEFAULT_PARAMS = {"stat": "qq", "position": "identity", "na_rm": False} + DEFAULT_PARAMS = {"stat": "qq"} diff --git a/plotnine/geoms/geom_qq_line.py b/plotnine/geoms/geom_qq_line.py index 4464317bb..495418812 100644 --- a/plotnine/geoms/geom_qq_line.py +++ b/plotnine/geoms/geom_qq_line.py @@ -18,8 +18,4 @@ class geom_qq_line(geom_path): plotnine.stat_qq_line : The default `stat` for this `geom`. """ - DEFAULT_PARAMS = { - "stat": "qq_line", - "position": "identity", - "na_rm": False, - } + DEFAULT_PARAMS = {"stat": "qq_line"} diff --git a/plotnine/geoms/geom_quantile.py b/plotnine/geoms/geom_quantile.py index ec33fe77c..9beef4525 100644 --- a/plotnine/geoms/geom_quantile.py +++ b/plotnine/geoms/geom_quantile.py @@ -30,8 +30,6 @@ class geom_quantile(geom_path): } DEFAULT_PARAMS = { "stat": "quantile", - "position": "identity", - "na_rm": False, "lineend": "butt", "linejoin": "round", } diff --git a/plotnine/geoms/geom_raster.py b/plotnine/geoms/geom_raster.py index 701653da1..68cc78084 100644 --- a/plotnine/geoms/geom_raster.py +++ b/plotnine/geoms/geom_raster.py @@ -82,9 +82,6 @@ class geom_raster(geom): REQUIRED_AES = {"x", "y"} NON_MISSING_AES = {"fill", "xmin", "xmax", "ymin", "ymax"} DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, "vjust": 0.5, "hjust": 0.5, "interpolation": None, diff --git a/plotnine/geoms/geom_rect.py b/plotnine/geoms/geom_rect.py index f02a0febd..eecae786c 100644 --- a/plotnine/geoms/geom_rect.py +++ b/plotnine/geoms/geom_rect.py @@ -39,11 +39,7 @@ class geom_rect(geom): "alpha": 1, } REQUIRED_AES = {"xmax", "xmin", "ymax", "ymin"} - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, - } + draw_legend = staticmethod(geom_polygon.draw_legend) def draw_panel( diff --git a/plotnine/geoms/geom_ribbon.py b/plotnine/geoms/geom_ribbon.py index c5680f9d1..b28527f31 100644 --- a/plotnine/geoms/geom_ribbon.py +++ b/plotnine/geoms/geom_ribbon.py @@ -68,12 +68,7 @@ class geom_ribbon(geom): "where": True, } REQUIRED_AES = {"x", "ymax", "ymin"} - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "outline_type": "both", - "na_rm": False, - } + DEFAULT_PARAMS = {"outline_type": "both"} draw_legend = staticmethod(geom_polygon.draw_legend) def handle_na(self, data: pd.DataFrame) -> pd.DataFrame: diff --git a/plotnine/geoms/geom_rug.py b/plotnine/geoms/geom_rug.py index a36e43b62..33711c995 100644 --- a/plotnine/geoms/geom_rug.py +++ b/plotnine/geoms/geom_rug.py @@ -45,13 +45,8 @@ class geom_rug(geom): "size": 0.5, "linetype": "solid", } - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, - "sides": "bl", - "length": 0.03, - } + DEFAULT_PARAMS = {"sides": "bl", "length": 0.03} + draw_legend = staticmethod(geom_path.draw_legend) @staticmethod diff --git a/plotnine/geoms/geom_segment.py b/plotnine/geoms/geom_segment.py index b4c971735..b97d71583 100644 --- a/plotnine/geoms/geom_segment.py +++ b/plotnine/geoms/geom_segment.py @@ -47,13 +47,7 @@ class geom_segment(geom): } REQUIRED_AES = {"x", "y", "xend", "yend"} NON_MISSING_AES = {"linetype", "size", "shape"} - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, - "lineend": "butt", - "arrow": None, - } + DEFAULT_PARAMS = {"lineend": "butt", "arrow": None} draw_legend = staticmethod(geom_path.draw_legend) legend_key_size = staticmethod(geom_path.legend_key_size) diff --git a/plotnine/geoms/geom_sina.py b/plotnine/geoms/geom_sina.py index 8a74f298d..c00278d5b 100644 --- a/plotnine/geoms/geom_sina.py +++ b/plotnine/geoms/geom_sina.py @@ -31,4 +31,4 @@ class geom_sina(geom_point): J. Comp. Graph. Stat 27: 673–76. """ - DEFAULT_PARAMS = {"stat": "sina", "position": "dodge", "na_rm": False} + DEFAULT_PARAMS = {"stat": "sina", "position": "dodge"} diff --git a/plotnine/geoms/geom_smooth.py b/plotnine/geoms/geom_smooth.py index 36f8d8a00..5d84cb8d6 100644 --- a/plotnine/geoms/geom_smooth.py +++ b/plotnine/geoms/geom_smooth.py @@ -50,12 +50,7 @@ class geom_smooth(geom): "ymax": None, } REQUIRED_AES = {"x", "y"} - DEFAULT_PARAMS = { - "stat": "smooth", - "position": "identity", - "na_rm": False, - "legend_fill_ratio": 0.5, - } + DEFAULT_PARAMS = {"stat": "smooth", "legend_fill_ratio": 0.5} legend_key_size = staticmethod(geom_path.legend_key_size) diff --git a/plotnine/geoms/geom_spoke.py b/plotnine/geoms/geom_spoke.py index 624c18b57..08e08e7bf 100644 --- a/plotnine/geoms/geom_spoke.py +++ b/plotnine/geoms/geom_spoke.py @@ -1,13 +1,13 @@ from __future__ import annotations -import typing +from typing import TYPE_CHECKING import numpy as np from ..doctools import document from .geom_segment import geom_segment -if typing.TYPE_CHECKING: +if TYPE_CHECKING: import pandas as pd diff --git a/plotnine/geoms/geom_step.py b/plotnine/geoms/geom_step.py index 24b710338..258b564d0 100644 --- a/plotnine/geoms/geom_step.py +++ b/plotnine/geoms/geom_step.py @@ -40,12 +40,8 @@ class geom_step(geom_path): plotnine.geom_path : For documentation of extra parameters. """ - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, - "direction": "hv", - } + DEFAULT_PARAMS = {"direction": "hv"} + draw_panel = geom.draw_panel @staticmethod diff --git a/plotnine/geoms/geom_text.py b/plotnine/geoms/geom_text.py index 53df3f357..4f7f810b9 100644 --- a/plotnine/geoms/geom_text.py +++ b/plotnine/geoms/geom_text.py @@ -144,9 +144,6 @@ class geom_text(geom): } REQUIRED_AES = {"label", "x", "y"} DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, "parse": False, "nudge_x": 0, "nudge_y": 0, diff --git a/plotnine/geoms/geom_tile.py b/plotnine/geoms/geom_tile.py index e8c08798e..503d6fc73 100644 --- a/plotnine/geoms/geom_tile.py +++ b/plotnine/geoms/geom_tile.py @@ -36,11 +36,6 @@ class geom_tile(geom_rect): "height": None, } REQUIRED_AES = {"x", "y"} - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, - } def setup_data(self, data: pd.DataFrame) -> pd.DataFrame: try: diff --git a/plotnine/geoms/geom_violin.py b/plotnine/geoms/geom_violin.py index 1ac7a15da..578ff654b 100644 --- a/plotnine/geoms/geom_violin.py +++ b/plotnine/geoms/geom_violin.py @@ -68,7 +68,6 @@ class geom_violin(geom): "scale": "area", "trim": True, "width": None, - "na_rm": False, } draw_legend = staticmethod(geom_polygon.draw_legend) diff --git a/plotnine/geoms/geom_vline.py b/plotnine/geoms/geom_vline.py index 6e8e32adb..f45e88d51 100644 --- a/plotnine/geoms/geom_vline.py +++ b/plotnine/geoms/geom_vline.py @@ -44,12 +44,7 @@ class geom_vline(geom): "alpha": 1, } REQUIRED_AES = {"xintercept"} - DEFAULT_PARAMS = { - "stat": "identity", - "position": "identity", - "na_rm": False, - "inherit_aes": False, - } + DEFAULT_PARAMS = {"inherit_aes": False} legend_key_size = staticmethod(geom_segment.legend_key_size) diff --git a/plotnine/guides/guide_legend.py b/plotnine/guides/guide_legend.py index 00effc985..1dfb51ade 100644 --- a/plotnine/guides/guide_legend.py +++ b/plotnine/guides/guide_legend.py @@ -196,7 +196,7 @@ def create_geoms(self): data = remove_missing( data, - l.geom.params["na_rm"], + l.geom.params.get("na_rm", False), list(l.geom.REQUIRED_AES | l.geom.NON_MISSING_AES), f"{l.geom.__class__.__name__} legend", ) diff --git a/plotnine/layer.py b/plotnine/layer.py index 190115b5a..b502de9c8 100644 --- a/plotnine/layer.py +++ b/plotnine/layer.py @@ -94,9 +94,9 @@ def __init__( if stat is not None: stat_ref = _lookup_stat(stat) if isinstance(stat_ref, type): - geom = stat_ref.DEFAULT_PARAMS.get("geom", "blank") + geom = stat_ref.DEFAULT_PARAMS["geom"] else: - geom = stat_ref.params.get("geom", "blank") + geom = stat_ref.params["geom"] # Forward stat instance's kwargs to the geom if mapping is None and data is None and not kwargs: mapping = stat_ref._raw_kwargs.get("mapping") @@ -154,6 +154,9 @@ def _verify_arguments(geom: geom, stat: stat) -> None: "data", "mapping", "geom", + "stat", + "position", + "na_rm", "show_legend", "inherit_aes", "raster", @@ -635,6 +638,11 @@ def _resolve_geom( from .geoms.geom import geom as geom_cls if isinstance(geom_spec, geom_cls): + for param in set(geom_spec.aesthetics()) & set(kwargs): + geom_spec.aes_params[param] = kwargs[param] + + for param in set(geom_spec.DEFAULT_PARAMS) & set(kwargs): + geom_spec.params[param] = kwargs[param] return geom_spec if isinstance(geom_spec, type) and issubclass(geom_spec, geom_cls): @@ -670,7 +678,7 @@ def _lookup_stat( # Duck-type guard for module reloads if not isinstance(stat_spec, type) and hasattr(stat_spec, "compute_layer"): - return stat_spec # type: ignore[return-value] + return stat_spec # pyright: ignore[reportReturnType] if isinstance(stat_spec, stat_cls): return stat_spec @@ -707,9 +715,15 @@ def _resolve_stat( if stat_spec is None: stat_spec = geom_obj.params["stat"] - result = _lookup_stat(stat_spec) # type: ignore[arg-type] + result = _lookup_stat(stat_spec) # pyright: ignore[reportArgumentType] if isinstance(result, stat_cls): + kwargs = result._raw_kwargs + for param in set(result.aesthetics()) & set(kwargs): + result.aes_params[param] = kwargs[param] + + for param in set(result.DEFAULT_PARAMS) & set(kwargs): + result.params[param] = kwargs[param] return result # It's a class — instantiate with filtered geom kwargs @@ -740,7 +754,7 @@ def _resolve_position( from .positions.position import position as position_cls if position_spec is None: - position_spec = geom_obj.params["position"] + position_spec = geom_obj.params.get("position", "identity") if isinstance(position_spec, position_cls): return position_spec diff --git a/plotnine/qplot.py b/plotnine/qplot.py index 1878dfe03..9eaff7170 100644 --- a/plotnine/qplot.py +++ b/plotnine/qplot.py @@ -198,7 +198,8 @@ def get_facet_type(facets: str) -> Literal["grid", "wrap", "null"]: for g in geom: geom_name = f"geom_{g}" geom_klass = Registry[geom_name] - stat_name = f"stat_{geom_klass.DEFAULT_PARAMS['stat']}" + s = geom_klass.DEFAULT_PARAMS.get("stat", "identity") + stat_name = f"stat_{s}" stat_klass = Registry[stat_name] # find params recognized = kwargs.keys() & ( diff --git a/plotnine/stats/stat.py b/plotnine/stats/stat.py index cc2537665..bbcacee75 100644 --- a/plotnine/stats/stat.py +++ b/plotnine/stats/stat.py @@ -27,6 +27,12 @@ from abc import ABC +_BASE_PARAMS = { + "geom": "blank", + "position": "identity", + "na_rm": False, +} + class stat(ABC, metaclass=Register): """Base class of all stats""" @@ -40,7 +46,7 @@ class stat(ABC, metaclass=Register): NON_MISSING_AES: set[str] = set() """Required aesthetics for the stat""" - DEFAULT_PARAMS: dict[str, Any] = {"geom": "blank"} + DEFAULT_PARAMS: dict[str, Any] = {} """Required parameters for the stat""" CREATES: set[str] = set() @@ -70,10 +76,12 @@ def __init__( data: DataLike | None = None, **kwargs: Any, ): + possible_params = _BASE_PARAMS | self.DEFAULT_PARAMS + possible_params_set = set(possible_params) kwargs = data_mapping_as_kwargs((data, mapping), kwargs) self._raw_kwargs = kwargs # Will be used to create the geom - self.params = self.DEFAULT_PARAMS | { - k: v for k, v in kwargs.items() if k in self.DEFAULT_PARAMS + self.params = possible_params | { + k: v for k, v in kwargs.items() if k in possible_params_set } self.DEFAULT_AES = aes(**self.DEFAULT_AES) self.aes_params = { diff --git a/plotnine/stats/stat_bin.py b/plotnine/stats/stat_bin.py index ca4651461..9d8ee0ec2 100644 --- a/plotnine/stats/stat_bin.py +++ b/plotnine/stats/stat_bin.py @@ -77,7 +77,6 @@ class stat_bin(stat): DEFAULT_PARAMS = { "geom": "histogram", "position": "stack", - "na_rm": False, "binwidth": None, "bins": None, "breaks": None, diff --git a/plotnine/stats/stat_bin_2d.py b/plotnine/stats/stat_bin_2d.py index c5c6f7f60..63fee8c99 100644 --- a/plotnine/stats/stat_bin_2d.py +++ b/plotnine/stats/stat_bin_2d.py @@ -59,8 +59,6 @@ class stat_bin_2d(stat): REQUIRED_AES = {"x", "y"} DEFAULT_PARAMS = { "geom": "rect", - "position": "identity", - "na_rm": False, "bins": 30, "breaks": None, "binwidth": None, diff --git a/plotnine/stats/stat_bindot.py b/plotnine/stats/stat_bindot.py index c5fdc6102..9539e20af 100644 --- a/plotnine/stats/stat_bindot.py +++ b/plotnine/stats/stat_bindot.py @@ -90,8 +90,6 @@ class stat_bindot(stat): NON_MISSING_AES = {"weight"} DEFAULT_PARAMS = { "geom": "dotplot", - "position": "identity", - "na_rm": False, "bins": None, "binwidth": None, "origin": None, diff --git a/plotnine/stats/stat_boxplot.py b/plotnine/stats/stat_boxplot.py index 15201e8be..87baa79cb 100644 --- a/plotnine/stats/stat_boxplot.py +++ b/plotnine/stats/stat_boxplot.py @@ -64,7 +64,6 @@ class stat_boxplot(stat): DEFAULT_PARAMS = { "geom": "boxplot", "position": "dodge", - "na_rm": False, "coef": 1.5, "width": None, } diff --git a/plotnine/stats/stat_count.py b/plotnine/stats/stat_count.py index b99e7a566..bf2b3470e 100644 --- a/plotnine/stats/stat_count.py +++ b/plotnine/stats/stat_count.py @@ -43,7 +43,6 @@ class stat_count(stat): DEFAULT_PARAMS = { "geom": "histogram", "position": "stack", - "na_rm": False, "width": None, } DEFAULT_AES = {"y": after_stat("count")} diff --git a/plotnine/stats/stat_density.py b/plotnine/stats/stat_density.py index 4089bcf39..b3bce5efd 100644 --- a/plotnine/stats/stat_density.py +++ b/plotnine/stats/stat_density.py @@ -111,7 +111,6 @@ class stat_density(stat): DEFAULT_PARAMS = { "geom": "density", "position": "stack", - "na_rm": False, "kernel": "gaussian", "adjust": 1, "trim": False, diff --git a/plotnine/stats/stat_density_2d.py b/plotnine/stats/stat_density_2d.py index 0467969a1..8046240c7 100644 --- a/plotnine/stats/stat_density_2d.py +++ b/plotnine/stats/stat_density_2d.py @@ -63,8 +63,6 @@ class stat_density_2d(stat): REQUIRED_AES = {"x"} DEFAULT_PARAMS = { "geom": "density_2d", - "position": "identity", - "na_rm": False, "contour": True, "package": "statsmodels", "kde_params": None, diff --git a/plotnine/stats/stat_ecdf.py b/plotnine/stats/stat_ecdf.py index c17413432..013b382e4 100644 --- a/plotnine/stats/stat_ecdf.py +++ b/plotnine/stats/stat_ecdf.py @@ -40,13 +40,7 @@ class stat_ecdf(stat): """ REQUIRED_AES = {"x"} - DEFAULT_PARAMS = { - "geom": "step", - "position": "identity", - "na_rm": False, - "n": None, - "pad": True, - } + DEFAULT_PARAMS = {"geom": "step", "n": None, "pad": True} DEFAULT_AES = {"y": after_stat("ecdf")} CREATES = {"ecdf"} diff --git a/plotnine/stats/stat_ellipse.py b/plotnine/stats/stat_ellipse.py index bc335577f..9c182d5b3 100644 --- a/plotnine/stats/stat_ellipse.py +++ b/plotnine/stats/stat_ellipse.py @@ -46,8 +46,6 @@ class stat_ellipse(stat): REQUIRED_AES = {"x", "y"} DEFAULT_PARAMS = { "geom": "path", - "position": "identity", - "na_rm": False, "type": "t", "level": 0.95, "segments": 51, diff --git a/plotnine/stats/stat_function.py b/plotnine/stats/stat_function.py index 03a07766f..3f0b02332 100644 --- a/plotnine/stats/stat_function.py +++ b/plotnine/stats/stat_function.py @@ -57,8 +57,6 @@ class stat_function(stat): DEFAULT_PARAMS = { "geom": "path", - "position": "identity", - "na_rm": False, "fun": None, "n": 101, "args": None, diff --git a/plotnine/stats/stat_hull.py b/plotnine/stats/stat_hull.py index 3f238edcc..668f64add 100644 --- a/plotnine/stats/stat_hull.py +++ b/plotnine/stats/stat_hull.py @@ -43,12 +43,7 @@ class stat_hull(stat): """ REQUIRED_AES = {"x", "y"} - DEFAULT_PARAMS = { - "geom": "path", - "position": "identity", - "na_rm": False, - "qhull_options": None, - } + DEFAULT_PARAMS = {"geom": "path", "qhull_options": None} CREATES = {"area"} def compute_group(self, data, scales): diff --git a/plotnine/stats/stat_identity.py b/plotnine/stats/stat_identity.py index 539954666..4d3201a8f 100644 --- a/plotnine/stats/stat_identity.py +++ b/plotnine/stats/stat_identity.py @@ -18,7 +18,7 @@ class stat_identity(stat): plotnine.geom_point : The default `geom` for this `stat`. """ - DEFAULT_PARAMS = {"geom": "point", "position": "identity", "na_rm": False} + DEFAULT_PARAMS = {"geom": "point"} def compute_panel(self, data, scales): return data diff --git a/plotnine/stats/stat_pointdensity.py b/plotnine/stats/stat_pointdensity.py index 92b03969c..d3a517430 100644 --- a/plotnine/stats/stat_pointdensity.py +++ b/plotnine/stats/stat_pointdensity.py @@ -49,8 +49,6 @@ class stat_pointdensity(stat): DEFAULT_AES = {"color": after_stat("density")} DEFAULT_PARAMS = { "geom": "density_2d", - "position": "identity", - "na_rm": False, "package": "statsmodels", "kde_params": None, } diff --git a/plotnine/stats/stat_qq.py b/plotnine/stats/stat_qq.py index caa47de46..4bfde5f81 100644 --- a/plotnine/stats/stat_qq.py +++ b/plotnine/stats/stat_qq.py @@ -67,8 +67,6 @@ class stat_qq(stat): DEFAULT_AES = {"x": after_stat("theoretical"), "y": after_stat("sample")} DEFAULT_PARAMS = { "geom": "qq", - "position": "identity", - "na_rm": False, "distribution": "norm", "dparams": {}, "quantiles": None, diff --git a/plotnine/stats/stat_qq_line.py b/plotnine/stats/stat_qq_line.py index 86010f351..a9ad2a1f6 100644 --- a/plotnine/stats/stat_qq_line.py +++ b/plotnine/stats/stat_qq_line.py @@ -54,8 +54,6 @@ class stat_qq_line(stat): REQUIRED_AES = {"sample"} DEFAULT_PARAMS = { "geom": "qq_line", - "position": "identity", - "na_rm": False, "distribution": "norm", "dparams": {}, "quantiles": None, diff --git a/plotnine/stats/stat_quantile.py b/plotnine/stats/stat_quantile.py index c2d5c7e27..3ce65347d 100644 --- a/plotnine/stats/stat_quantile.py +++ b/plotnine/stats/stat_quantile.py @@ -50,8 +50,6 @@ class stat_quantile(stat): REQUIRED_AES = {"x", "y"} DEFAULT_PARAMS = { "geom": "quantile", - "position": "identity", - "na_rm": False, "quantiles": (0.25, 0.5, 0.75), "formula": "y ~ x", "method_args": {}, diff --git a/plotnine/stats/stat_sina.py b/plotnine/stats/stat_sina.py index 6189df5f9..07f8ff8a0 100644 --- a/plotnine/stats/stat_sina.py +++ b/plotnine/stats/stat_sina.py @@ -95,7 +95,6 @@ class stat_sina(stat): DEFAULT_PARAMS = { "geom": "sina", "position": "dodge", - "na_rm": False, "binwidth": None, "bins": None, "method": "density", diff --git a/plotnine/stats/stat_smooth.py b/plotnine/stats/stat_smooth.py index df8be2bf1..c180db949 100644 --- a/plotnine/stats/stat_smooth.py +++ b/plotnine/stats/stat_smooth.py @@ -143,8 +143,6 @@ def my_smoother(data, xseq, params): REQUIRED_AES = {"x", "y"} DEFAULT_PARAMS = { "geom": "smooth", - "position": "identity", - "na_rm": False, "method": "auto", "se": True, "n": 80, diff --git a/plotnine/stats/stat_sum.py b/plotnine/stats/stat_sum.py index 1fe5d22fc..36d758bba 100644 --- a/plotnine/stats/stat_sum.py +++ b/plotnine/stats/stat_sum.py @@ -35,7 +35,7 @@ class stat_sum(stat): """ REQUIRED_AES = {"x", "y"} - DEFAULT_PARAMS = {"geom": "point", "position": "identity", "na_rm": False} + DEFAULT_PARAMS = {"geom": "point"} DEFAULT_AES = {"size": after_stat("n"), "weight": 1} CREATES = {"n", "prop"} diff --git a/plotnine/stats/stat_summary.py b/plotnine/stats/stat_summary.py index b4ec87202..1a6b95db8 100644 --- a/plotnine/stats/stat_summary.py +++ b/plotnine/stats/stat_summary.py @@ -270,8 +270,6 @@ class stat_summary(stat): REQUIRED_AES = {"x", "y"} DEFAULT_PARAMS = { "geom": "pointrange", - "position": "identity", - "na_rm": False, "fun_data": "mean_cl_boot", "fun_y": None, "fun_ymin": None, diff --git a/plotnine/stats/stat_summary_bin.py b/plotnine/stats/stat_summary_bin.py index 92b79c784..476cb4f2a 100644 --- a/plotnine/stats/stat_summary_bin.py +++ b/plotnine/stats/stat_summary_bin.py @@ -90,8 +90,6 @@ class stat_summary_bin(stat): REQUIRED_AES = {"x", "y"} DEFAULT_PARAMS = { "geom": "pointrange", - "position": "identity", - "na_rm": False, "bins": 30, "breaks": None, "binwidth": None, diff --git a/plotnine/stats/stat_unique.py b/plotnine/stats/stat_unique.py index 33f63ac4c..d53a2b4cd 100644 --- a/plotnine/stats/stat_unique.py +++ b/plotnine/stats/stat_unique.py @@ -18,7 +18,7 @@ class stat_unique(stat): plotnine.geom_point : The default `geom` for this `stat`. """ - DEFAULT_PARAMS = {"geom": "point", "position": "identity", "na_rm": False} + DEFAULT_PARAMS = {"geom": "point"} def compute_panel(self, data, scales): return data.drop_duplicates() diff --git a/plotnine/stats/stat_ydensity.py b/plotnine/stats/stat_ydensity.py index e8d1378f6..8b4efdf3d 100644 --- a/plotnine/stats/stat_ydensity.py +++ b/plotnine/stats/stat_ydensity.py @@ -92,7 +92,6 @@ class stat_ydensity(stat): DEFAULT_PARAMS = { "geom": "violin", "position": "dodge", - "na_rm": False, "adjust": 1, "kernel": "gaussian", "n": 1024,