ggplot2-python 4.0.2.9000.post1__py3-none-any.whl → 4.0.2.9000.post3__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 +146 -14
- ggplot2_py/_compat.py +53 -6
- ggplot2_py/_defaults.py +169 -0
- ggplot2_py/_make_constructor.py +440 -0
- ggplot2_py/_utils.py +243 -16
- ggplot2_py/aes.py +88 -18
- ggplot2_py/annotation.py +21 -30
- ggplot2_py/autoplot.py +79 -0
- ggplot2_py/coord.py +1265 -0
- ggplot2_py/coords/__init__.py +10 -0
- ggplot2_py/facet.py +927 -193
- ggplot2_py/fortify.py +179 -1
- ggplot2_py/geom.py +259 -62
- ggplot2_py/ggproto.py +50 -0
- ggplot2_py/guide.py +1790 -21
- ggplot2_py/guide_axis.py +62 -47
- ggplot2_py/guide_colourbar.py +53 -7
- ggplot2_py/guide_legend.py +41 -33
- ggplot2_py/labeller.py +99 -0
- ggplot2_py/layer.py +93 -15
- ggplot2_py/plot.py +39 -22
- ggplot2_py/plot_render.py +739 -101
- ggplot2_py/position.py +14 -9
- ggplot2_py/scale.py +149 -6
- ggplot2_py/stat.py +1472 -402
- ggplot2_py/theme.py +14 -0
- ggplot2_py/theme_defaults.py +46 -23
- ggplot2_py/theme_elements.py +48 -8
- {ggplot2_python-4.0.2.9000.post1.dist-info → ggplot2_python-4.0.2.9000.post3.dist-info}/METADATA +1 -1
- ggplot2_python-4.0.2.9000.post3.dist-info/RECORD +57 -0
- ggplot2_python-4.0.2.9000.post1.dist-info/RECORD +0 -54
- {ggplot2_python-4.0.2.9000.post1.dist-info → ggplot2_python-4.0.2.9000.post3.dist-info}/WHEEL +0 -0
- {ggplot2_python-4.0.2.9000.post1.dist-info → ggplot2_python-4.0.2.9000.post3.dist-info}/licenses/LICENSE +0 -0
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.
|
|
10
|
+
__version__ = "4.0.2.9000.post3"
|
|
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,
|
|
@@ -460,7 +466,9 @@ from ggplot2_py.coord import (
|
|
|
460
466
|
CoordFixed,
|
|
461
467
|
CoordFlip,
|
|
462
468
|
CoordPolar,
|
|
469
|
+
CoordQuickmap,
|
|
463
470
|
CoordRadial,
|
|
471
|
+
CoordSf,
|
|
464
472
|
CoordTrans,
|
|
465
473
|
CoordTransform,
|
|
466
474
|
coord_cartesian,
|
|
@@ -468,11 +476,14 @@ from ggplot2_py.coord import (
|
|
|
468
476
|
coord_fixed,
|
|
469
477
|
coord_flip,
|
|
470
478
|
coord_polar,
|
|
479
|
+
coord_quickmap,
|
|
471
480
|
coord_radial,
|
|
481
|
+
coord_sf,
|
|
472
482
|
coord_trans,
|
|
473
483
|
coord_transform,
|
|
474
484
|
coord_munch,
|
|
475
485
|
is_coord,
|
|
486
|
+
sf_transform_xy,
|
|
476
487
|
)
|
|
477
488
|
|
|
478
489
|
# ---------------------------------------------------------------------------
|
|
@@ -487,7 +498,11 @@ from ggplot2_py.facet import (
|
|
|
487
498
|
facet_grid,
|
|
488
499
|
facet_wrap,
|
|
489
500
|
is_facet,
|
|
501
|
+
wrap_dims,
|
|
502
|
+
max_height,
|
|
503
|
+
max_width,
|
|
490
504
|
)
|
|
505
|
+
from ggplot2_py.layout import Layout
|
|
491
506
|
|
|
492
507
|
# ---------------------------------------------------------------------------
|
|
493
508
|
# Position adjustments
|
|
@@ -535,6 +550,8 @@ from ggplot2_py.guide import (
|
|
|
535
550
|
guide_bins,
|
|
536
551
|
guide_colourbar,
|
|
537
552
|
guide_colorbar,
|
|
553
|
+
guide_old_colourbar,
|
|
554
|
+
guide_old_colorbar,
|
|
538
555
|
guide_coloursteps,
|
|
539
556
|
guide_colorsteps,
|
|
540
557
|
guide_custom,
|
|
@@ -542,6 +559,14 @@ from ggplot2_py.guide import (
|
|
|
542
559
|
guide_none,
|
|
543
560
|
guides,
|
|
544
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,
|
|
545
570
|
)
|
|
546
571
|
|
|
547
572
|
# ---------------------------------------------------------------------------
|
|
@@ -560,6 +585,7 @@ from ggplot2_py.theme import (
|
|
|
560
585
|
reset_theme_settings,
|
|
561
586
|
update_theme,
|
|
562
587
|
replace_theme,
|
|
588
|
+
from_theme,
|
|
563
589
|
)
|
|
564
590
|
from ggplot2_py.theme_elements import (
|
|
565
591
|
Element,
|
|
@@ -575,6 +601,7 @@ from ggplot2_py.theme_elements import (
|
|
|
575
601
|
el_def,
|
|
576
602
|
merge_element,
|
|
577
603
|
is_theme_element,
|
|
604
|
+
is_margin,
|
|
578
605
|
Margin,
|
|
579
606
|
margin,
|
|
580
607
|
margin_auto,
|
|
@@ -596,6 +623,17 @@ from ggplot2_py.theme_defaults import (
|
|
|
596
623
|
theme_classic,
|
|
597
624
|
theme_void,
|
|
598
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,
|
|
599
637
|
)
|
|
600
638
|
|
|
601
639
|
# ---------------------------------------------------------------------------
|
|
@@ -637,13 +675,43 @@ from ggplot2_py.draw_key import (
|
|
|
637
675
|
# Save, fortify, qplot
|
|
638
676
|
# ---------------------------------------------------------------------------
|
|
639
677
|
from ggplot2_py.save import ggsave, check_device
|
|
640
|
-
from ggplot2_py.fortify import
|
|
678
|
+
from ggplot2_py.fortify import (
|
|
679
|
+
fortify,
|
|
680
|
+
fortify_lm,
|
|
681
|
+
fortify_dispatch,
|
|
682
|
+
fortify_glht,
|
|
683
|
+
fortify_confint_glht,
|
|
684
|
+
fortify_summary_glht,
|
|
685
|
+
fortify_cld,
|
|
686
|
+
)
|
|
641
687
|
from ggplot2_py.qplot import qplot, quickplot
|
|
688
|
+
from ggplot2_py.labeller import (
|
|
689
|
+
label_value,
|
|
690
|
+
label_both,
|
|
691
|
+
label_context,
|
|
692
|
+
label_parsed,
|
|
693
|
+
label_bquote,
|
|
694
|
+
label_wrap_gen,
|
|
695
|
+
as_labeller,
|
|
696
|
+
)
|
|
697
|
+
from ggplot2_py.autoplot import autoplot, autolayer
|
|
642
698
|
|
|
643
699
|
# ---------------------------------------------------------------------------
|
|
644
700
|
# Utility re-exports (matching R namespace)
|
|
645
701
|
# ---------------------------------------------------------------------------
|
|
646
|
-
from ggplot2_py._utils import
|
|
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
|
|
647
715
|
|
|
648
716
|
# grid re-exports (matching R: importFrom(grid, unit, arrow))
|
|
649
717
|
from grid_py import Unit as unit, arrow
|
|
@@ -714,6 +782,9 @@ __all__ = [
|
|
|
714
782
|
"geom_sf", "geom_sf_label", "geom_sf_text",
|
|
715
783
|
"geom_qq", "geom_qq_line",
|
|
716
784
|
"is_geom",
|
|
785
|
+
"GeomMap", "GeomQuantile",
|
|
786
|
+
"GeomCustomAnn", "GeomLogticks", "GeomRasterAnn",
|
|
787
|
+
"translate_shape_string",
|
|
717
788
|
# Stat classes
|
|
718
789
|
"Stat", "StatIdentity", "StatBin", "StatCount", "StatDensity",
|
|
719
790
|
"StatSmooth", "StatBoxplot", "StatSummary", "StatSummaryBin",
|
|
@@ -771,20 +842,51 @@ __all__ = [
|
|
|
771
842
|
"scale_color_viridis_c", "scale_color_viridis_d",
|
|
772
843
|
"scale_color_grey", "scale_color_identity", "scale_color_manual",
|
|
773
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",
|
|
774
847
|
"scale_size", "scale_size_continuous", "scale_size_area",
|
|
775
|
-
"
|
|
776
|
-
"
|
|
777
|
-
"
|
|
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",
|
|
778
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",
|
|
779
878
|
# Coords
|
|
780
879
|
"Coord", "CoordCartesian", "CoordFixed", "CoordFlip",
|
|
781
|
-
"CoordPolar", "CoordRadial", "CoordTrans", "CoordTransform",
|
|
880
|
+
"CoordPolar", "CoordQuickmap", "CoordRadial", "CoordTrans", "CoordTransform",
|
|
782
881
|
"coord_cartesian", "coord_equal", "coord_fixed", "coord_flip",
|
|
783
|
-
"coord_polar", "coord_radial", "coord_trans", "coord_transform",
|
|
784
|
-
"
|
|
882
|
+
"coord_polar", "coord_quickmap", "coord_radial", "coord_sf", "coord_trans", "coord_transform",
|
|
883
|
+
"CoordSf",
|
|
884
|
+
"coord_munch", "is_coord", "sf_transform_xy",
|
|
785
885
|
# Facets
|
|
786
886
|
"Facet", "FacetNull", "FacetGrid", "FacetWrap",
|
|
787
|
-
"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",
|
|
788
890
|
# Positions
|
|
789
891
|
"Position", "PositionIdentity", "PositionDodge", "PositionDodge2",
|
|
790
892
|
"PositionJitter", "PositionJitterdodge", "PositionNudge",
|
|
@@ -797,22 +899,34 @@ __all__ = [
|
|
|
797
899
|
"GuideAxisTheta", "GuideBins", "GuideColourbar", "GuideColoursteps",
|
|
798
900
|
"GuideCustom", "GuideLegend", "GuideNone",
|
|
799
901
|
"guide_axis", "guide_legend", "guide_colourbar", "guide_colorbar",
|
|
902
|
+
"guide_old_colourbar", "guide_old_colorbar",
|
|
800
903
|
"guide_coloursteps", "guide_colorsteps", "guide_bins",
|
|
801
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",
|
|
802
909
|
# Themes
|
|
803
910
|
"theme", "is_theme", "complete_theme",
|
|
804
911
|
"theme_get", "theme_set", "theme_update", "theme_replace",
|
|
805
912
|
"set_theme", "get_theme", "reset_theme_settings",
|
|
913
|
+
"update_theme", "replace_theme", "from_theme",
|
|
806
914
|
"Element", "element_blank", "element_line", "element_rect",
|
|
807
915
|
"element_text", "element_point", "element_polygon", "element_geom",
|
|
808
916
|
"element_grob", "element_render", "merge_element",
|
|
809
|
-
"
|
|
917
|
+
"el_def", "is_theme_element", "is_margin",
|
|
918
|
+
"Margin", "margin", "margin_auto", "margin_part", "Rel", "rel",
|
|
810
919
|
"calc_element", "get_element_tree", "register_theme_elements",
|
|
811
920
|
"theme_grey", "theme_gray", "theme_bw", "theme_linedraw",
|
|
812
921
|
"theme_light", "theme_dark", "theme_minimal", "theme_classic",
|
|
813
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",
|
|
814
928
|
# Labels, limits
|
|
815
|
-
"labs", "xlab", "ylab", "ggtitle",
|
|
929
|
+
"labs", "xlab", "ylab", "ggtitle", "update_labels",
|
|
816
930
|
"lims", "xlim", "ylim", "expand_limits",
|
|
817
931
|
# Annotations
|
|
818
932
|
"annotate", "annotation_custom", "annotation_raster", "annotation_logticks",
|
|
@@ -823,11 +937,29 @@ __all__ = [
|
|
|
823
937
|
"draw_key_pointrange", "draw_key_smooth", "draw_key_text",
|
|
824
938
|
"draw_key_abline", "draw_key_vline", "draw_key_timeseries", "draw_key_vpath",
|
|
825
939
|
# Save, fortify, qplot
|
|
826
|
-
"ggsave", "check_device",
|
|
940
|
+
"ggsave", "check_device",
|
|
941
|
+
"fortify", "fortify_lm", "fortify_dispatch",
|
|
942
|
+
"fortify_glht", "fortify_confint_glht", "fortify_summary_glht", "fortify_cld",
|
|
943
|
+
"qplot", "quickplot",
|
|
944
|
+
# Labellers
|
|
945
|
+
"label_value", "label_both", "label_context", "label_parsed",
|
|
946
|
+
"label_bquote", "label_wrap_gen", "as_labeller",
|
|
947
|
+
# autoplot / autolayer generics
|
|
948
|
+
"autoplot", "autolayer",
|
|
827
949
|
# Utilities
|
|
828
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",
|
|
829
957
|
"unit", "arrow", "alpha",
|
|
830
|
-
"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",
|
|
831
963
|
# Plugin discovery
|
|
832
964
|
"discover_extensions", "list_extensions",
|
|
833
965
|
]
|
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
|
|
108
|
+
"""Emit an informational message via Python's :mod:`warnings`.
|
|
107
109
|
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
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
|
|
ggplot2_py/_defaults.py
ADDED
|
@@ -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
|