ggplot2-python 4.0.2.9000.post2__py3-none-any.whl → 4.0.2.9000.post4__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.
ggplot2_py/__init__.py CHANGED
@@ -7,7 +7,7 @@ approach to creating statistical visualizations.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
- __version__ = "4.0.2.9000.post1"
10
+ __version__ = "4.0.2.9000.post4"
11
11
  __r_commit__ = "c02c05a"
12
12
 
13
13
  # ---------------------------------------------------------------------------
@@ -153,6 +153,10 @@ from ggplot2_py.geom import (
153
153
  GeomQuantile,
154
154
  GeomJitter,
155
155
  GeomSf,
156
+ GeomCustomAnn,
157
+ GeomLogticks,
158
+ GeomRasterAnn,
159
+ translate_shape_string,
156
160
  geom_point,
157
161
  geom_path,
158
162
  geom_line,
@@ -420,12 +424,14 @@ from ggplot2_py.scales import (
420
424
  scale_size_datetime,
421
425
  scale_radius,
422
426
  scale_shape,
427
+ scale_shape_continuous,
423
428
  scale_shape_discrete,
424
429
  scale_shape_binned,
425
430
  scale_shape_identity,
426
431
  scale_shape_manual,
427
432
  scale_shape_ordinal,
428
433
  scale_linetype,
434
+ scale_linetype_continuous,
429
435
  scale_linetype_discrete,
430
436
  scale_linetype_binned,
431
437
  scale_linetype_identity,
@@ -492,7 +498,11 @@ from ggplot2_py.facet import (
492
498
  facet_grid,
493
499
  facet_wrap,
494
500
  is_facet,
501
+ wrap_dims,
502
+ max_height,
503
+ max_width,
495
504
  )
505
+ from ggplot2_py.layout import Layout
496
506
 
497
507
  # ---------------------------------------------------------------------------
498
508
  # Position adjustments
@@ -549,6 +559,14 @@ from ggplot2_py.guide import (
549
559
  guide_none,
550
560
  guides,
551
561
  is_guide,
562
+ is_guides,
563
+ new_guide,
564
+ old_guide,
565
+ guide_geom,
566
+ guide_train,
567
+ guide_merge,
568
+ guide_transform,
569
+ guide_gengrob,
552
570
  )
553
571
 
554
572
  # ---------------------------------------------------------------------------
@@ -567,6 +585,7 @@ from ggplot2_py.theme import (
567
585
  reset_theme_settings,
568
586
  update_theme,
569
587
  replace_theme,
588
+ from_theme,
570
589
  )
571
590
  from ggplot2_py.theme_elements import (
572
591
  Element,
@@ -582,6 +601,7 @@ from ggplot2_py.theme_elements import (
582
601
  el_def,
583
602
  merge_element,
584
603
  is_theme_element,
604
+ is_margin,
585
605
  Margin,
586
606
  margin,
587
607
  margin_auto,
@@ -603,6 +623,17 @@ from ggplot2_py.theme_defaults import (
603
623
  theme_classic,
604
624
  theme_void,
605
625
  theme_test,
626
+ theme_sub_axis,
627
+ theme_sub_axis_x,
628
+ theme_sub_axis_y,
629
+ theme_sub_axis_top,
630
+ theme_sub_axis_bottom,
631
+ theme_sub_axis_left,
632
+ theme_sub_axis_right,
633
+ theme_sub_legend,
634
+ theme_sub_panel,
635
+ theme_sub_plot,
636
+ theme_sub_strip,
606
637
  )
607
638
 
608
639
  # ---------------------------------------------------------------------------
@@ -668,7 +699,19 @@ from ggplot2_py.autoplot import autoplot, autolayer
668
699
  # ---------------------------------------------------------------------------
669
700
  # Utility re-exports (matching R namespace)
670
701
  # ---------------------------------------------------------------------------
671
- from ggplot2_py._utils import resolution, remove_missing
702
+ from ggplot2_py._utils import (
703
+ resolution, remove_missing,
704
+ cut_interval, cut_number, cut_width,
705
+ transform_position, fill_alpha, pattern_alpha,
706
+ )
707
+ from ggplot2_py._defaults import (
708
+ update_geom_defaults, update_stat_defaults,
709
+ reset_geom_defaults, reset_stat_defaults,
710
+ get_geom_defaults,
711
+ )
712
+ from ggplot2_py._make_constructor import make_constructor
713
+ # zeroGrob alias for R parity (ggplot2 imports it from grid)
714
+ from grid_py import null_grob as zeroGrob
672
715
 
673
716
  # grid re-exports (matching R: importFrom(grid, unit, arrow))
674
717
  from grid_py import Unit as unit, arrow
@@ -739,6 +782,9 @@ __all__ = [
739
782
  "geom_sf", "geom_sf_label", "geom_sf_text",
740
783
  "geom_qq", "geom_qq_line",
741
784
  "is_geom",
785
+ "GeomMap", "GeomQuantile",
786
+ "GeomCustomAnn", "GeomLogticks", "GeomRasterAnn",
787
+ "translate_shape_string",
742
788
  # Stat classes
743
789
  "Stat", "StatIdentity", "StatBin", "StatCount", "StatDensity",
744
790
  "StatSmooth", "StatBoxplot", "StatSummary", "StatSummaryBin",
@@ -796,21 +842,51 @@ __all__ = [
796
842
  "scale_color_viridis_c", "scale_color_viridis_d",
797
843
  "scale_color_grey", "scale_color_identity", "scale_color_manual",
798
844
  "scale_alpha", "scale_alpha_continuous", "scale_alpha_discrete",
845
+ "scale_alpha_binned", "scale_alpha_identity", "scale_alpha_manual",
846
+ "scale_alpha_ordinal", "scale_alpha_date", "scale_alpha_datetime",
799
847
  "scale_size", "scale_size_continuous", "scale_size_area",
800
- "scale_shape", "scale_shape_discrete",
801
- "scale_linetype", "scale_linetype_discrete",
802
- "scale_linewidth", "scale_linewidth_continuous",
848
+ "scale_size_binned", "scale_size_binned_area",
849
+ "scale_size_discrete", "scale_size_identity", "scale_size_manual",
850
+ "scale_size_ordinal", "scale_size_date", "scale_size_datetime",
851
+ "scale_radius",
852
+ "scale_shape", "scale_shape_continuous", "scale_shape_discrete",
853
+ "scale_shape_binned", "scale_shape_identity", "scale_shape_manual",
854
+ "scale_shape_ordinal",
855
+ "scale_linetype", "scale_linetype_continuous", "scale_linetype_discrete",
856
+ "scale_linetype_binned", "scale_linetype_identity", "scale_linetype_manual",
857
+ "scale_linewidth", "scale_linewidth_continuous", "scale_linewidth_discrete",
858
+ "scale_linewidth_binned", "scale_linewidth_identity", "scale_linewidth_manual",
859
+ "scale_linewidth_ordinal",
860
+ "scale_linewidth_date", "scale_linewidth_datetime",
803
861
  "scale_stroke", "scale_stroke_continuous",
862
+ # Binned / stepped / fermenter / distiller / ordinal / date colour & fill scales
863
+ "scale_colour_binned", "scale_colour_distiller", "scale_colour_fermenter",
864
+ "scale_colour_ordinal", "scale_colour_steps", "scale_colour_steps2",
865
+ "scale_colour_stepsn", "scale_colour_viridis_b",
866
+ "scale_colour_date", "scale_colour_datetime",
867
+ "scale_color_binned", "scale_color_distiller", "scale_color_fermenter",
868
+ "scale_color_ordinal", "scale_color_steps", "scale_color_steps2",
869
+ "scale_color_stepsn", "scale_color_viridis_b",
870
+ "scale_color_date", "scale_color_datetime",
871
+ "scale_fill_binned", "scale_fill_distiller", "scale_fill_fermenter",
872
+ "scale_fill_ordinal", "scale_fill_steps", "scale_fill_steps2",
873
+ "scale_fill_stepsn", "scale_fill_viridis_b",
874
+ "scale_fill_date", "scale_fill_datetime",
875
+ # Time / identity / manual variants
876
+ "scale_x_time", "scale_y_time",
877
+ "scale_continuous_identity", "scale_discrete_identity", "scale_discrete_manual",
804
878
  # Coords
805
879
  "Coord", "CoordCartesian", "CoordFixed", "CoordFlip",
806
880
  "CoordPolar", "CoordQuickmap", "CoordRadial", "CoordTrans", "CoordTransform",
807
881
  "coord_cartesian", "coord_equal", "coord_fixed", "coord_flip",
808
882
  "coord_polar", "coord_quickmap", "coord_radial", "coord_sf", "coord_trans", "coord_transform",
809
883
  "CoordSf",
810
- "coord_munch", "is_coord",
884
+ "coord_munch", "is_coord", "sf_transform_xy",
811
885
  # Facets
812
886
  "Facet", "FacetNull", "FacetGrid", "FacetWrap",
813
- "facet_null", "facet_grid", "facet_wrap", "is_facet",
887
+ "facet_null", "facet_grid", "facet_wrap", "is_facet", "wrap_dims",
888
+ "max_height", "max_width",
889
+ "Layout",
814
890
  # Positions
815
891
  "Position", "PositionIdentity", "PositionDodge", "PositionDodge2",
816
892
  "PositionJitter", "PositionJitterdodge", "PositionNudge",
@@ -826,20 +902,31 @@ __all__ = [
826
902
  "guide_old_colourbar", "guide_old_colorbar",
827
903
  "guide_coloursteps", "guide_colorsteps", "guide_bins",
828
904
  "guide_custom", "guide_none", "guides", "is_guide",
905
+ "guide_axis_logticks", "guide_axis_stack", "guide_axis_theta",
906
+ "is_guides", "new_guide", "old_guide",
907
+ "guide_geom", "guide_train", "guide_merge", "guide_transform",
908
+ "guide_gengrob",
829
909
  # Themes
830
910
  "theme", "is_theme", "complete_theme",
831
911
  "theme_get", "theme_set", "theme_update", "theme_replace",
832
912
  "set_theme", "get_theme", "reset_theme_settings",
913
+ "update_theme", "replace_theme", "from_theme",
833
914
  "Element", "element_blank", "element_line", "element_rect",
834
915
  "element_text", "element_point", "element_polygon", "element_geom",
835
916
  "element_grob", "element_render", "merge_element",
836
- "Margin", "margin", "Rel", "rel",
917
+ "el_def", "is_theme_element", "is_margin",
918
+ "Margin", "margin", "margin_auto", "margin_part", "Rel", "rel",
837
919
  "calc_element", "get_element_tree", "register_theme_elements",
838
920
  "theme_grey", "theme_gray", "theme_bw", "theme_linedraw",
839
921
  "theme_light", "theme_dark", "theme_minimal", "theme_classic",
840
922
  "theme_void", "theme_test",
923
+ "theme_sub_axis", "theme_sub_axis_x", "theme_sub_axis_y",
924
+ "theme_sub_axis_top", "theme_sub_axis_bottom",
925
+ "theme_sub_axis_left", "theme_sub_axis_right",
926
+ "theme_sub_legend", "theme_sub_panel", "theme_sub_plot",
927
+ "theme_sub_strip",
841
928
  # Labels, limits
842
- "labs", "xlab", "ylab", "ggtitle",
929
+ "labs", "xlab", "ylab", "ggtitle", "update_labels",
843
930
  "lims", "xlim", "ylim", "expand_limits",
844
931
  # Annotations
845
932
  "annotate", "annotation_custom", "annotation_raster", "annotation_logticks",
@@ -861,10 +948,25 @@ __all__ = [
861
948
  "autoplot", "autolayer",
862
949
  # Utilities
863
950
  "resolution", "remove_missing",
951
+ "cut_interval", "cut_number", "cut_width",
952
+ "transform_position", "fill_alpha", "pattern_alpha",
953
+ "update_geom_defaults", "update_stat_defaults",
954
+ "reset_geom_defaults", "reset_stat_defaults",
955
+ "get_geom_defaults",
956
+ "make_constructor",
864
957
  "unit", "arrow", "alpha",
865
- "PT", "STROKE",
958
+ "PT", "STROKE", "zeroGrob",
959
+ # Submodule namespaces and remaining R-exported helpers already
960
+ # defined at top level (just need to surface for ``import *``).
961
+ "stat", "labeller",
962
+ "derive", "flip_data", "flipped_names", "has_flipped_aes",
866
963
  # Plugin discovery
867
964
  "discover_extensions", "list_extensions",
965
+ # Python-exclusive extension surface (README quickstart uses
966
+ # ``from ggplot2_py import *``; these would otherwise be invisible)
967
+ "ggplot_defaults", "BuildStage",
968
+ "GeomProtocol", "StatProtocol", "ScaleProtocol",
969
+ "CoordProtocol", "FacetProtocol", "PositionProtocol",
868
970
  ]
869
971
 
870
972
  # ---------------------------------------------------------------------------
ggplot2_py/_compat.py CHANGED
@@ -32,6 +32,8 @@ __all__ = [
32
32
  "Waiver",
33
33
  "is_waiver",
34
34
  "waiver",
35
+ "NA",
36
+ "is_na",
35
37
  "caller_arg",
36
38
  ]
37
39
 
@@ -103,22 +105,24 @@ def cli_inform(
103
105
  call: Optional[str] = None,
104
106
  **kwargs: Any,
105
107
  ) -> None:
106
- """Emit an informational message (no-op by default).
108
+ """Emit an informational message via Python's :mod:`warnings`.
107
109
 
108
- In interactive sessions this could print; in batch mode it stays silent.
109
- Override by monkey-patching if verbose output is desired.
110
+ Mirrors R ``cli::cli_inform`` / ``rlang::inform`` which always write
111
+ to stderr regardless of session type. Routing through ``warnings``
112
+ lets pytest, ``warnings.catch_warnings``, and user filters capture
113
+ or silence the message — matching the way R lets users wrap
114
+ ``suppressMessages({...})`` around an expression.
110
115
 
111
116
  Parameters
112
117
  ----------
113
118
  message : str
114
119
  Informational message.
115
120
  call : str, optional
116
- Name of the calling function.
121
+ Name of the calling function (unused; matches R signature).
117
122
  **kwargs : Any
118
123
  Substitution values for placeholders in *message*.
119
124
  """
120
- # Intentionally silent – mirrors rlang::inform() in non-interactive R.
121
- pass
125
+ warnings.warn(message, UserWarning, stacklevel=2)
122
126
 
123
127
 
124
128
  # ---------------------------------------------------------------------------
@@ -436,6 +440,49 @@ def waiver() -> Waiver:
436
440
  return Waiver()
437
441
 
438
442
 
443
+ # ---------------------------------------------------------------------------
444
+ # NA sentinel (R parity)
445
+ # ---------------------------------------------------------------------------
446
+ #
447
+ # R distinguishes ``NULL`` (unspecified, inherits from parent) from ``NA``
448
+ # (explicitly absent, must NOT inherit). Python collapses both onto
449
+ # ``None`` by default, which silently breaks theme inheritance — e.g.
450
+ # ``element_rect(fill="grey92", colour=NA)`` should keep the missing
451
+ # border on merge, but Python would replace ``None`` with the parent
452
+ # ``rect`` element's ``"black"``. This sentinel preserves R's NA
453
+ # semantics through ``combine_elements``.
454
+
455
+ class _NA:
456
+ """Sentinel for R's ``NA`` — explicitly absent, never inherited."""
457
+
458
+ _instance: Optional["_NA"] = None
459
+
460
+ def __new__(cls) -> "_NA":
461
+ if cls._instance is None:
462
+ cls._instance = super().__new__(cls)
463
+ return cls._instance
464
+
465
+ def __repr__(self) -> str:
466
+ return "NA"
467
+
468
+ def __bool__(self) -> bool:
469
+ return False
470
+
471
+ def __eq__(self, other: Any) -> bool:
472
+ return isinstance(other, _NA)
473
+
474
+ def __hash__(self) -> int:
475
+ return 0
476
+
477
+
478
+ NA = _NA()
479
+
480
+
481
+ def is_na(x: Any) -> bool:
482
+ """Return True iff *x* is the singleton :data:`NA` sentinel."""
483
+ return isinstance(x, _NA)
484
+
485
+
439
486
  def is_waiver(x: Any) -> bool:
440
487
  """Check whether *x* is a ``Waiver`` sentinel.
441
488
 
@@ -0,0 +1,169 @@
1
+ """Geom / stat default-aesthetic modifiers — port of R's
2
+ ``geom-update-defaults.R``.
3
+
4
+ Lets users override ``Geom*.default_aes`` (and the analogous Stat
5
+ defaults) globally for the lifetime of a Python session, with a cache
6
+ that lets the user roll back via ``reset_geom_defaults`` /
7
+ ``reset_stat_defaults``.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ from typing import Any, Dict, Optional
12
+
13
+ from ggplot2_py.aes import rename_aes
14
+ from ggplot2_py.geom import Geom
15
+ from ggplot2_py.stat import Stat
16
+
17
+ __all__ = [
18
+ "update_geom_defaults",
19
+ "update_stat_defaults",
20
+ "reset_geom_defaults",
21
+ "reset_stat_defaults",
22
+ "get_geom_defaults",
23
+ ]
24
+
25
+
26
+ # Cached pristine copies — populated lazily on first override so reset
27
+ # can restore the originals. R's equivalent: ``cache_defaults``.
28
+ _cache_geom: Dict[str, Any] = {}
29
+ _cache_stat: Dict[str, Any] = {}
30
+
31
+
32
+ def _resolve(name_or_cls: Any, registry: Dict[str, Any], kind: str) -> Any:
33
+ """Resolve a name or class to the registered Geom/Stat class.
34
+
35
+ Mirrors R ``validate_subclass``: accepts a string (snake-case
36
+ suffix), a Geom/Stat subclass, or a layer-constructor function.
37
+ """
38
+ if isinstance(name_or_cls, str):
39
+ cls = registry.get(name_or_cls) or registry.get(name_or_cls.lower())
40
+ if cls is None:
41
+ raise ValueError(f"No registered {kind} named {name_or_cls!r}.")
42
+ return cls
43
+ if isinstance(name_or_cls, type):
44
+ return name_or_cls
45
+ # A bound layer constructor returns a Layer; in R the function is
46
+ # passed to validate_subclass which sniffs the geom/stat off it.
47
+ raise TypeError(
48
+ f"Expected a string name or {kind} class; got {type(name_or_cls).__name__}."
49
+ )
50
+
51
+
52
+ def _snake_key(cls: Any, prefix: str) -> str:
53
+ """``GeomPoint -> "point"``, used as the cache key."""
54
+ name = cls.__name__
55
+ if name.startswith(prefix):
56
+ name = name[len(prefix):]
57
+ # CamelCase → snake_case
58
+ out = []
59
+ for i, c in enumerate(name):
60
+ if c.isupper() and i > 0:
61
+ out.append("_")
62
+ out.append(c.lower())
63
+ return "".join(out) or name
64
+
65
+
66
+ def _update(cls: Any, new: Any, cache: Dict[str, Any]) -> Any:
67
+ """Common update path for both geom and stat defaults."""
68
+ key = _snake_key(cls, "Geom" if cache is _cache_geom else "Stat")
69
+ old = dict(cls.default_aes) if hasattr(cls.default_aes, "items") else dict(cls.default_aes or {})
70
+
71
+ if new is None:
72
+ # Reset path — restore from cache, drop entry.
73
+ cached = cache.pop(key, None)
74
+ if cached is not None:
75
+ cls.default_aes = type(cls.default_aes)(**cached) if hasattr(type(cls.default_aes), "__init__") else dict(cached)
76
+ return old
77
+
78
+ # First override: snapshot the pristine defaults so we can roll back.
79
+ if key not in cache:
80
+ cache[key] = old
81
+
82
+ new_dict = rename_aes_dict(new)
83
+ merged = {**old}
84
+ for k, v in new_dict.items():
85
+ merged[k] = v
86
+ # Replace in the same container type the class originally used.
87
+ cls.default_aes = type(cls.default_aes)(**merged) if hasattr(type(cls.default_aes), "__init__") else merged
88
+ return old
89
+
90
+
91
+ def rename_aes_dict(new: Any) -> Dict[str, Any]:
92
+ """Apply :func:`rename_aes` to a Mapping or plain dict."""
93
+ if hasattr(new, "items"):
94
+ items = list(new.items())
95
+ else:
96
+ items = list(new)
97
+ keys = [k for k, _ in items]
98
+ canonical = rename_aes({k: None for k in keys})
99
+ name_map = {k: c for k, c in zip(keys, list(canonical.keys()))}
100
+ return {name_map[k]: v for k, v in items}
101
+
102
+
103
+ def update_geom_defaults(geom: Any, new: Any) -> Any:
104
+ """Modify a Geom's ``default_aes``. Pass ``None`` to roll back.
105
+
106
+ Mirrors R ``update_geom_defaults`` (geom-update-defaults.R:57-59).
107
+ """
108
+ cls = _resolve(geom, Geom._registry, "geom")
109
+ return _update(cls, new, _cache_geom)
110
+
111
+
112
+ def update_stat_defaults(stat: Any, new: Any) -> Any:
113
+ """Modify a Stat's ``default_aes``. Pass ``None`` to roll back.
114
+
115
+ Mirrors R ``update_stat_defaults`` (geom-update-defaults.R:63-65).
116
+ """
117
+ cls = _resolve(stat, Stat._registry, "stat")
118
+ return _update(cls, new, _cache_stat)
119
+
120
+
121
+ def reset_geom_defaults() -> None:
122
+ """Roll back every prior ``update_geom_defaults`` call.
123
+
124
+ Mirrors R ``reset_geom_defaults`` (geom-update-defaults.R:120).
125
+ """
126
+ for key in list(_cache_geom.keys()):
127
+ cls = Geom._registry.get(key) or Geom._registry.get(key.lower())
128
+ if cls is not None:
129
+ _update(cls, None, _cache_geom)
130
+
131
+
132
+ def reset_stat_defaults() -> None:
133
+ """Roll back every prior ``update_stat_defaults`` call.
134
+
135
+ Mirrors R ``reset_stat_defaults`` (geom-update-defaults.R:124).
136
+ """
137
+ for key in list(_cache_stat.keys()):
138
+ cls = Stat._registry.get(key) or Stat._registry.get(key.lower())
139
+ if cls is not None:
140
+ _update(cls, None, _cache_stat)
141
+
142
+
143
+ def get_geom_defaults(geom: Any, theme: Optional[Any] = None) -> Dict[str, Any]:
144
+ """Return the resolved default aesthetics for *geom*.
145
+
146
+ Mirrors R ``get_geom_defaults`` (geom-update-defaults.R:96-116).
147
+ Accepts a class, a registered name, or a layer-constructor
148
+ function. ``theme`` is honoured for ``FromTheme`` defaults.
149
+ """
150
+ if callable(geom) and not isinstance(geom, type):
151
+ # Layer constructor (geom_point) — call to obtain the Layer
152
+ layer = geom()
153
+ cls = type(getattr(layer, "geom", None) or geom)
154
+ if hasattr(layer, "aes_params"):
155
+ base = dict(cls.default_aes) if hasattr(cls.default_aes, "items") else dict(cls.default_aes or {})
156
+ base.update(layer.aes_params or {})
157
+ return base
158
+ if isinstance(geom, str):
159
+ cls = _resolve(geom, Geom._registry, "geom")
160
+ elif isinstance(geom, type):
161
+ cls = geom
162
+ else:
163
+ cls = type(geom)
164
+
165
+ base = dict(cls.default_aes) if hasattr(cls.default_aes, "items") else dict(cls.default_aes or {})
166
+ if theme is not None:
167
+ from ggplot2_py.geom import _eval_from_theme
168
+ base = dict(_eval_from_theme(cls.default_aes, theme))
169
+ return base