ggplot2-python 4.0.2.9000__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. ggplot2_py/__init__.py +852 -0
  2. ggplot2_py/_compat.py +475 -0
  3. ggplot2_py/_plugins.py +129 -0
  4. ggplot2_py/_utils.py +544 -0
  5. ggplot2_py/aes.py +586 -0
  6. ggplot2_py/annotation.py +540 -0
  7. ggplot2_py/coord.py +2108 -0
  8. ggplot2_py/coords/__init__.py +49 -0
  9. ggplot2_py/datasets.py +265 -0
  10. ggplot2_py/draw_key.py +454 -0
  11. ggplot2_py/facet.py +1456 -0
  12. ggplot2_py/fortify.py +95 -0
  13. ggplot2_py/geom.py +4516 -0
  14. ggplot2_py/geoms/__init__.py +12 -0
  15. ggplot2_py/ggproto.py +279 -0
  16. ggplot2_py/guide.py +2925 -0
  17. ggplot2_py/guide_axis.py +615 -0
  18. ggplot2_py/guide_colourbar.py +657 -0
  19. ggplot2_py/guide_legend.py +1061 -0
  20. ggplot2_py/guides/__init__.py +8 -0
  21. ggplot2_py/labeller.py +296 -0
  22. ggplot2_py/labels.py +309 -0
  23. ggplot2_py/layer.py +954 -0
  24. ggplot2_py/layout.py +754 -0
  25. ggplot2_py/limits.py +314 -0
  26. ggplot2_py/plot.py +1401 -0
  27. ggplot2_py/plot_render.py +866 -0
  28. ggplot2_py/position.py +1269 -0
  29. ggplot2_py/protocols.py +171 -0
  30. ggplot2_py/py.typed +0 -0
  31. ggplot2_py/qplot.py +233 -0
  32. ggplot2_py/resources/diamonds.csv +53941 -0
  33. ggplot2_py/resources/economics.csv +575 -0
  34. ggplot2_py/resources/economics_long.csv +2871 -0
  35. ggplot2_py/resources/faithfuld.csv +5626 -0
  36. ggplot2_py/resources/luv_colours.csv +658 -0
  37. ggplot2_py/resources/midwest.csv +438 -0
  38. ggplot2_py/resources/mpg.csv +235 -0
  39. ggplot2_py/resources/msleep.csv +84 -0
  40. ggplot2_py/resources/presidential.csv +13 -0
  41. ggplot2_py/resources/seals.csv +1156 -0
  42. ggplot2_py/resources/txhousing.csv +8603 -0
  43. ggplot2_py/save.py +316 -0
  44. ggplot2_py/scale.py +2727 -0
  45. ggplot2_py/scales/__init__.py +4252 -0
  46. ggplot2_py/stat.py +6071 -0
  47. ggplot2_py/stats/__init__.py +9 -0
  48. ggplot2_py/theme.py +490 -0
  49. ggplot2_py/theme_defaults.py +1350 -0
  50. ggplot2_py/theme_elements.py +2052 -0
  51. ggplot2_python-4.0.2.9000.dist-info/METADATA +179 -0
  52. ggplot2_python-4.0.2.9000.dist-info/RECORD +54 -0
  53. ggplot2_python-4.0.2.9000.dist-info/WHEEL +4 -0
  54. ggplot2_python-4.0.2.9000.dist-info/licenses/LICENSE +3 -0
@@ -0,0 +1,171 @@
1
+ """
2
+ Structural typing protocols for ggplot2_py GOG components.
3
+
4
+ These :class:`~typing.Protocol` definitions specify the **contracts** that
5
+ custom Geom, Stat, Scale, Coord, Facet, and Position classes should satisfy.
6
+ They are ``@runtime_checkable``, so you can use ``isinstance()`` to verify
7
+ compliance without requiring inheritance from the base classes.
8
+
9
+ This is a **Python-exclusive** feature — R's ggplot2 has no equivalent
10
+ compile-time or runtime contract checking.
11
+
12
+ Usage
13
+ -----
14
+ Mypy / pyright will flag violations statically::
15
+
16
+ class MyStat(Stat):
17
+ required_aes = ("x",)
18
+ # Missing compute_group → type error
19
+
20
+ At runtime you can check::
21
+
22
+ from ggplot2_py.protocols import StatProtocol
23
+ assert isinstance(my_stat, StatProtocol)
24
+
25
+ Notes
26
+ -----
27
+ These protocols describe the *minimum* interface required for each
28
+ component to participate in the GOG pipeline. They do **not** replace
29
+ the base classes (``Geom``, ``Stat``, etc.) — they complement them by
30
+ enabling structural (duck-typed) checking.
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ from typing import (
36
+ Any,
37
+ Dict,
38
+ List,
39
+ Protocol,
40
+ Sequence,
41
+ Tuple,
42
+ Union,
43
+ runtime_checkable,
44
+ )
45
+
46
+ import pandas as pd
47
+
48
+ __all__ = [
49
+ "GeomProtocol",
50
+ "StatProtocol",
51
+ "ScaleProtocol",
52
+ "CoordProtocol",
53
+ "FacetProtocol",
54
+ "PositionProtocol",
55
+ ]
56
+
57
+
58
+ # ---------------------------------------------------------------------------
59
+ # Geom
60
+ # ---------------------------------------------------------------------------
61
+
62
+ @runtime_checkable
63
+ class GeomProtocol(Protocol):
64
+ """Contract for geometry objects.
65
+
66
+ A conforming Geom must declare its aesthetic requirements and provide
67
+ at least ``draw_panel`` or ``draw_group`` for rendering.
68
+ """
69
+
70
+ required_aes: Union[Tuple[str, ...], List[str]]
71
+ default_aes: Any # Mapping or dict
72
+ draw_key: Any # callable
73
+
74
+ def setup_params(self, data: pd.DataFrame, params: dict) -> dict: ...
75
+ def setup_data(self, data: pd.DataFrame, params: dict) -> pd.DataFrame: ...
76
+ def draw_panel(self, data: pd.DataFrame, panel_params: dict,
77
+ coord: Any, **kwargs: Any) -> Any: ...
78
+
79
+
80
+ # ---------------------------------------------------------------------------
81
+ # Stat
82
+ # ---------------------------------------------------------------------------
83
+
84
+ @runtime_checkable
85
+ class StatProtocol(Protocol):
86
+ """Contract for statistical transformation objects.
87
+
88
+ A conforming Stat must declare its aesthetic requirements and provide
89
+ ``compute_group`` (or ``compute_panel`` / ``compute_layer``).
90
+ """
91
+
92
+ required_aes: Union[Tuple[str, ...], List[str]]
93
+ default_aes: Any
94
+
95
+ def setup_params(self, data: pd.DataFrame, params: dict) -> dict: ...
96
+ def setup_data(self, data: pd.DataFrame, params: dict) -> pd.DataFrame: ...
97
+ def compute_group(self, data: pd.DataFrame, scales: Any,
98
+ **params: Any) -> pd.DataFrame: ...
99
+
100
+
101
+ # ---------------------------------------------------------------------------
102
+ # Scale
103
+ # ---------------------------------------------------------------------------
104
+
105
+ @runtime_checkable
106
+ class ScaleProtocol(Protocol):
107
+ """Contract for scale objects.
108
+
109
+ A conforming Scale mediates between data space and aesthetic space
110
+ via train / transform / map.
111
+ """
112
+
113
+ aesthetics: Any # list of str
114
+
115
+ def train(self, x: Any) -> None: ...
116
+ def transform(self, x: Any) -> Any: ...
117
+ def map(self, x: Any) -> Any: ...
118
+ def get_breaks(self) -> Any: ...
119
+ def get_labels(self, breaks: Any = None) -> Any: ...
120
+ def clone(self) -> Any: ...
121
+
122
+
123
+ # ---------------------------------------------------------------------------
124
+ # Coord
125
+ # ---------------------------------------------------------------------------
126
+
127
+ @runtime_checkable
128
+ class CoordProtocol(Protocol):
129
+ """Contract for coordinate system objects.
130
+
131
+ A conforming Coord transforms data positions into viewport positions
132
+ and renders background / axes.
133
+ """
134
+
135
+ def setup_params(self, data: list) -> dict: ...
136
+ def transform(self, data: pd.DataFrame, panel_params: dict) -> pd.DataFrame: ...
137
+ def setup_panel_params(self, scale_x: Any, scale_y: Any,
138
+ params: dict = ...) -> dict: ...
139
+
140
+
141
+ # ---------------------------------------------------------------------------
142
+ # Facet
143
+ # ---------------------------------------------------------------------------
144
+
145
+ @runtime_checkable
146
+ class FacetProtocol(Protocol):
147
+ """Contract for faceting specification objects.
148
+
149
+ A conforming Facet computes panel layout and assigns data to panels.
150
+ """
151
+
152
+ def compute_layout(self, data: list, params: dict) -> pd.DataFrame: ...
153
+ def map_data(self, data: pd.DataFrame, layout: pd.DataFrame,
154
+ params: dict) -> pd.DataFrame: ...
155
+
156
+
157
+ # ---------------------------------------------------------------------------
158
+ # Position
159
+ # ---------------------------------------------------------------------------
160
+
161
+ @runtime_checkable
162
+ class PositionProtocol(Protocol):
163
+ """Contract for position adjustment objects.
164
+
165
+ A conforming Position adjusts data coordinates (e.g. dodge, stack)
166
+ after stat computation but before coordinate transformation.
167
+ """
168
+
169
+ def setup_params(self, data: pd.DataFrame) -> dict: ...
170
+ def compute_layer(self, data: pd.DataFrame, params: dict,
171
+ layout: Any) -> pd.DataFrame: ...
ggplot2_py/py.typed ADDED
File without changes
ggplot2_py/qplot.py ADDED
@@ -0,0 +1,233 @@
1
+ """
2
+ Quick plotting shortcut (deprecated).
3
+
4
+ Provides :func:`qplot` / :func:`quickplot` for creating simple plots
5
+ with less typing. This interface is deprecated in favour of the full
6
+ ``ggplot() + geom_*()`` pipeline.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import warnings
12
+ from typing import Any, List, Optional, Sequence, Union
13
+
14
+ import numpy as np
15
+ import pandas as pd
16
+
17
+ from ggplot2_py._compat import cli_warn
18
+
19
+ __all__ = [
20
+ "qplot",
21
+ "quickplot",
22
+ ]
23
+
24
+
25
+ def qplot(
26
+ x: Any = None,
27
+ y: Any = None,
28
+ *,
29
+ data: Optional[pd.DataFrame] = None,
30
+ geom: Union[str, List[str]] = "auto",
31
+ facets: Any = None,
32
+ xlim: Optional[Sequence] = None,
33
+ ylim: Optional[Sequence] = None,
34
+ log: str = "",
35
+ main: Optional[str] = None,
36
+ xlab: Optional[str] = None,
37
+ ylab: Optional[str] = None,
38
+ asp: Optional[float] = None,
39
+ colour: Any = None,
40
+ color: Any = None,
41
+ fill: Any = None,
42
+ size: Any = None,
43
+ shape: Any = None,
44
+ alpha: Any = None,
45
+ **kwargs: Any,
46
+ ) -> Any:
47
+ """Create a quick plot (deprecated).
48
+
49
+ ``qplot`` is a shorthand for ``ggplot() + geom_*()``. It is
50
+ deprecated; prefer the full ggplot pipeline.
51
+
52
+ Parameters
53
+ ----------
54
+ x, y : str or array-like, optional
55
+ Variables for the x and y axes. If strings, they are interpreted
56
+ as column names in *data*.
57
+ data : DataFrame, optional
58
+ Dataset.
59
+ geom : str or list of str, optional
60
+ Geom name(s) to use (e.g. ``"point"``, ``"histogram"``).
61
+ ``"auto"`` guesses from the supplied aesthetics.
62
+ facets : str or None, optional
63
+ Faceting formula (stub).
64
+ xlim, ylim : sequence, optional
65
+ Axis limits.
66
+ log : str, optional
67
+ Which axes to log-transform (``"x"``, ``"y"``, or ``"xy"``).
68
+ main : str, optional
69
+ Plot title.
70
+ xlab, ylab : str, optional
71
+ Axis labels.
72
+ asp : float, optional
73
+ Aspect ratio (y/x).
74
+ colour, color, fill, size, shape, alpha : str or array-like, optional
75
+ Additional aesthetics.
76
+ **kwargs
77
+ Other parameters passed to the geom.
78
+
79
+ Returns
80
+ -------
81
+ GGPlot
82
+ A ggplot object.
83
+
84
+ Warnings
85
+ --------
86
+ ``qplot()`` is deprecated since ggplot2 3.4.0. Use ``ggplot()``
87
+ instead.
88
+ """
89
+ warnings.warn(
90
+ "qplot() is deprecated. Use ggplot() + geom_*() instead.",
91
+ FutureWarning,
92
+ stacklevel=2,
93
+ )
94
+
95
+ from ggplot2_py.aes import aes
96
+ from ggplot2_py.plot import ggplot, GGPlot
97
+ from ggplot2_py.labels import labs
98
+
99
+ # Normalise colour/color
100
+ if color is not None and colour is None:
101
+ colour = color
102
+
103
+ # Build data if not provided
104
+ if data is None:
105
+ data_dict: dict = {}
106
+ if x is not None and not isinstance(x, str):
107
+ data_dict["x"] = np.asarray(x)
108
+ x = "x"
109
+ if y is not None and not isinstance(y, str):
110
+ data_dict["y"] = np.asarray(y)
111
+ y = "y"
112
+ if colour is not None and not isinstance(colour, str):
113
+ data_dict["colour"] = np.asarray(colour)
114
+ colour = "colour"
115
+ if fill is not None and not isinstance(fill, str):
116
+ data_dict["fill"] = np.asarray(fill)
117
+ fill = "fill"
118
+ if size is not None and not isinstance(size, str):
119
+ data_dict["size"] = np.asarray(size)
120
+ size = "size"
121
+ if shape is not None and not isinstance(shape, str):
122
+ data_dict["shape"] = np.asarray(shape)
123
+ shape = "shape"
124
+ if alpha is not None and not isinstance(alpha, str):
125
+ data_dict["alpha"] = np.asarray(alpha)
126
+ alpha = "alpha"
127
+ if data_dict:
128
+ data = pd.DataFrame(data_dict)
129
+ else:
130
+ data = pd.DataFrame()
131
+
132
+ # Build mapping
133
+ mapping_kwargs: dict = {}
134
+ if x is not None:
135
+ mapping_kwargs["x"] = x
136
+ if y is not None:
137
+ mapping_kwargs["y"] = y
138
+ if colour is not None:
139
+ mapping_kwargs["colour"] = colour
140
+ if fill is not None:
141
+ mapping_kwargs["fill"] = fill
142
+ if size is not None:
143
+ mapping_kwargs["size"] = size
144
+ if shape is not None:
145
+ mapping_kwargs["shape"] = shape
146
+ if alpha is not None:
147
+ mapping_kwargs["alpha"] = alpha
148
+
149
+ mapping = aes(**mapping_kwargs)
150
+
151
+ # Auto-detect geom
152
+ if isinstance(geom, str):
153
+ geoms = [geom]
154
+ else:
155
+ geoms = list(geom)
156
+
157
+ if "auto" in geoms:
158
+ if y is not None:
159
+ geoms = ["point" if g == "auto" else g for g in geoms]
160
+ elif x is not None:
161
+ # Check if x is discrete
162
+ if isinstance(data, pd.DataFrame) and x in data.columns:
163
+ col = data[x]
164
+ if col.dtype == object or hasattr(col, "cat"):
165
+ geoms = ["bar" if g == "auto" else g for g in geoms]
166
+ else:
167
+ geoms = ["histogram" if g == "auto" else g for g in geoms]
168
+ else:
169
+ geoms = ["histogram" if g == "auto" else g for g in geoms]
170
+ else:
171
+ geoms = ["point" if g == "auto" else g for g in geoms]
172
+
173
+ # Determine axis labels
174
+ if xlab is None:
175
+ xlab = str(x) if x is not None else ""
176
+ if ylab is None:
177
+ ylab = str(y) if y is not None else ""
178
+
179
+ # Build plot
180
+ p = ggplot(data, mapping)
181
+
182
+ # Add geoms
183
+ import ggplot2_py.geoms as _geoms_mod
184
+
185
+ for g in geoms:
186
+ geom_fn = getattr(_geoms_mod, f"geom_{g}", None)
187
+ if geom_fn is None:
188
+ cli_warn(f"Unknown geom: 'geom_{g}'.")
189
+ continue
190
+ p = p + geom_fn(**kwargs)
191
+
192
+ # Log transforms
193
+ if "x" in log:
194
+ try:
195
+ from ggplot2_py.scales import scale_x_log10
196
+ p = p + scale_x_log10()
197
+ except ImportError:
198
+ pass
199
+ if "y" in log:
200
+ try:
201
+ from ggplot2_py.scales import scale_y_log10
202
+ p = p + scale_y_log10()
203
+ except ImportError:
204
+ pass
205
+
206
+ # Aspect ratio
207
+ if asp is not None:
208
+ from ggplot2_py.theme import theme
209
+ p = p + theme(aspect_ratio=asp)
210
+
211
+ # Labels
212
+ label_kwargs: dict = {}
213
+ if xlab:
214
+ label_kwargs["x"] = xlab
215
+ if ylab:
216
+ label_kwargs["y"] = ylab
217
+ if main:
218
+ label_kwargs["title"] = main
219
+ if label_kwargs:
220
+ p = p + labs(**label_kwargs)
221
+
222
+ # Axis limits
223
+ if xlim is not None:
224
+ from ggplot2_py.limits import xlim as _xlim
225
+ p = p + _xlim(*xlim)
226
+ if ylim is not None:
227
+ from ggplot2_py.limits import ylim as _ylim
228
+ p = p + _ylim(*ylim)
229
+
230
+ return p
231
+
232
+
233
+ quickplot = qplot