plotnine 0.14.5__py3-none-any.whl → 0.15.0a2__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 (92) hide show
  1. plotnine/__init__.py +31 -37
  2. plotnine/_mpl/gridspec.py +265 -0
  3. plotnine/_mpl/layout_manager/__init__.py +6 -0
  4. plotnine/_mpl/layout_manager/_engine.py +87 -0
  5. plotnine/_mpl/layout_manager/_layout_items.py +957 -0
  6. plotnine/_mpl/layout_manager/_layout_tree.py +905 -0
  7. plotnine/_mpl/layout_manager/_spaces.py +1154 -0
  8. plotnine/_mpl/patches.py +70 -34
  9. plotnine/_mpl/text.py +159 -37
  10. plotnine/_mpl/utils.py +78 -10
  11. plotnine/_utils/__init__.py +35 -9
  12. plotnine/_utils/dev.py +45 -27
  13. plotnine/_utils/yippie.py +115 -0
  14. plotnine/animation.py +1 -1
  15. plotnine/coords/coord.py +3 -3
  16. plotnine/coords/coord_trans.py +1 -1
  17. plotnine/data/__init__.py +43 -8
  18. plotnine/data/anscombe-quartet.csv +45 -0
  19. plotnine/doctools.py +2 -2
  20. plotnine/facets/facet.py +34 -43
  21. plotnine/facets/facet_grid.py +14 -6
  22. plotnine/facets/facet_wrap.py +3 -5
  23. plotnine/facets/strips.py +20 -33
  24. plotnine/geoms/annotate.py +3 -3
  25. plotnine/geoms/annotation_logticks.py +2 -0
  26. plotnine/geoms/annotation_stripes.py +2 -0
  27. plotnine/geoms/geom.py +3 -3
  28. plotnine/geoms/geom_bar.py +10 -2
  29. plotnine/geoms/geom_col.py +6 -0
  30. plotnine/geoms/geom_crossbar.py +2 -3
  31. plotnine/geoms/geom_path.py +2 -2
  32. plotnine/geoms/geom_violin.py +24 -7
  33. plotnine/ggplot.py +95 -66
  34. plotnine/guides/guide.py +19 -20
  35. plotnine/guides/guide_colorbar.py +6 -6
  36. plotnine/guides/guide_legend.py +15 -16
  37. plotnine/guides/guides.py +8 -8
  38. plotnine/helpers.py +49 -0
  39. plotnine/iapi.py +33 -7
  40. plotnine/labels.py +8 -3
  41. plotnine/layer.py +4 -4
  42. plotnine/mapping/_env.py +2 -2
  43. plotnine/mapping/_eval_environment.py +85 -0
  44. plotnine/mapping/aes.py +14 -30
  45. plotnine/mapping/evaluation.py +7 -65
  46. plotnine/options.py +14 -7
  47. plotnine/plot_composition/__init__.py +10 -0
  48. plotnine/plot_composition/_compose.py +462 -0
  49. plotnine/plot_composition/_plotspec.py +50 -0
  50. plotnine/plot_composition/_spacer.py +32 -0
  51. plotnine/positions/position_dodge.py +1 -1
  52. plotnine/positions/position_dodge2.py +1 -1
  53. plotnine/positions/position_stack.py +1 -2
  54. plotnine/qplot.py +1 -2
  55. plotnine/scales/__init__.py +0 -6
  56. plotnine/scales/limits.py +7 -7
  57. plotnine/scales/scale.py +4 -4
  58. plotnine/scales/scale_continuous.py +2 -1
  59. plotnine/scales/scale_identity.py +10 -2
  60. plotnine/scales/scale_manual.py +6 -2
  61. plotnine/stats/binning.py +5 -2
  62. plotnine/stats/smoothers.py +3 -5
  63. plotnine/stats/stat.py +3 -3
  64. plotnine/stats/stat_bindot.py +1 -3
  65. plotnine/stats/stat_density.py +2 -2
  66. plotnine/stats/stat_qq_line.py +1 -1
  67. plotnine/stats/stat_sina.py +34 -1
  68. plotnine/themes/elements/__init__.py +3 -0
  69. plotnine/themes/elements/element_text.py +35 -24
  70. plotnine/themes/elements/margin.py +137 -61
  71. plotnine/themes/targets.py +3 -1
  72. plotnine/themes/theme.py +21 -7
  73. plotnine/themes/theme_538.py +0 -1
  74. plotnine/themes/theme_bw.py +0 -1
  75. plotnine/themes/theme_dark.py +0 -1
  76. plotnine/themes/theme_gray.py +32 -34
  77. plotnine/themes/theme_light.py +1 -1
  78. plotnine/themes/theme_matplotlib.py +28 -31
  79. plotnine/themes/theme_seaborn.py +36 -36
  80. plotnine/themes/theme_void.py +25 -27
  81. plotnine/themes/theme_xkcd.py +0 -1
  82. plotnine/themes/themeable.py +369 -169
  83. plotnine/typing.py +3 -3
  84. plotnine/watermark.py +3 -3
  85. {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.dist-info}/METADATA +8 -5
  86. {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.dist-info}/RECORD +89 -78
  87. {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.dist-info}/WHEEL +1 -1
  88. plotnine/_mpl/_plot_side_space.py +0 -888
  89. plotnine/_mpl/_plotnine_tight_layout.py +0 -293
  90. plotnine/_mpl/layout_engine.py +0 -110
  91. {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.dist-info/licenses}/LICENSE +0 -0
  92. {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,115 @@
1
+ """
2
+ Functions/class to quickly create plots for development and testing
3
+ """
4
+
5
+ import pandas as pd
6
+
7
+ from plotnine import (
8
+ aes,
9
+ element_blank,
10
+ element_rect,
11
+ element_text,
12
+ geom_col,
13
+ geom_point,
14
+ ggplot,
15
+ labs,
16
+ theme,
17
+ )
18
+
19
+ __all__ = ("geom", "legend", "plot", "rotate", "tag")
20
+
21
+
22
+ class _Plot:
23
+ """
24
+ Create a plot
25
+ """
26
+
27
+ def __getattr__(self, color: str):
28
+ """
29
+ Create a blank plot with given background color
30
+ """
31
+ return (
32
+ ggplot()
33
+ + labs(
34
+ x="x-axis",
35
+ y="y-axis",
36
+ title=color.title(),
37
+ )
38
+ + theme(
39
+ figure_size=(8, 6),
40
+ text=element_text(color="black", size=11),
41
+ panel_background=element_rect(fill=color, size=1),
42
+ plot_background=element_rect(fill=color, alpha=0.2),
43
+ panel_border=element_rect(color="black"),
44
+ strip_background=element_rect(color="black"),
45
+ panel_grid=element_blank(),
46
+ legend_key_size=12,
47
+ )
48
+ )
49
+
50
+
51
+ class _Geom:
52
+ """
53
+ Create some simple geoms
54
+ """
55
+
56
+ data = pd.DataFrame(
57
+ {
58
+ "cat": ["a", "b", "c", "d"],
59
+ "cat2": ["r", "r", "s", "s"],
60
+ "value": [1, 2, 3, 4],
61
+ }
62
+ )
63
+
64
+ @property
65
+ def points(self):
66
+ return geom_point(aes("cat", "value", color="cat"), self.data, size=2)
67
+
68
+ @property
69
+ def cols(self):
70
+ return geom_col(aes("cat", "value", fill="cat"), self.data)
71
+
72
+
73
+ class _Legend:
74
+ """
75
+ Position Legends
76
+ """
77
+
78
+ @property
79
+ def left(self):
80
+ return theme(legend_position="left")
81
+
82
+ @property
83
+ def bottom(self):
84
+ return theme(legend_position="bottom")
85
+
86
+ @property
87
+ def right(self):
88
+ return theme(legend_position="right")
89
+
90
+ @property
91
+ def top(self):
92
+ return theme(legend_position="top")
93
+
94
+
95
+ class _Rotate:
96
+ """
97
+ Rotate a text themeable
98
+ """
99
+
100
+ def __getattr__(self, name):
101
+ angle = 0 if name[-2:] == "_y" else 90
102
+ return theme(**{name: element_text(angle=angle)}) # pyright: ignore[reportArgumentType]
103
+
104
+
105
+ def tag(s, position="topleft"):
106
+ """
107
+ Create a tag at a position
108
+ """
109
+ return [labs(tag=s), theme(plot_tag_position=position)]
110
+
111
+
112
+ plot = _Plot()
113
+ geom = _Geom()
114
+ legend = _Legend()
115
+ rotate = _Rotate()
plotnine/animation.py CHANGED
@@ -233,6 +233,6 @@ class PlotnineAnimation(ArtistAnimation):
233
233
  plot.axs = axs
234
234
  with plot_context(plot):
235
235
  plot._build()
236
- plot.figure, plot.axs = plot.facet.setup(plot)
236
+ plot.axs = plot.facet.setup(plot)
237
237
  plot._draw_layers()
238
238
  return plot
plotnine/coords/coord.py CHANGED
@@ -35,12 +35,12 @@ class coord:
35
35
  # if the coordinate system needs them
36
36
  params: dict[str, Any]
37
37
 
38
- def __radd__(self, plot: ggplot) -> ggplot:
38
+ def __radd__(self, other: ggplot) -> ggplot:
39
39
  """
40
40
  Add coordinates to ggplot object
41
41
  """
42
- plot.coordinates = copy(self)
43
- return plot
42
+ other.coordinates = copy(self)
43
+ return other
44
44
 
45
45
  def setup_data(self, data: list[pd.DataFrame]) -> list[pd.DataFrame]:
46
46
  """
@@ -117,7 +117,7 @@ class coord_trans(coord):
117
117
  range=ranges.range,
118
118
  )
119
119
  sv.range = tuple(sorted(ranges.range_coord)) # type: ignore
120
- breaks = cast(tuple[float, float], sv.breaks)
120
+ breaks = cast("tuple[float, float]", sv.breaks)
121
121
  sv.breaks = transform_value(trans, breaks)
122
122
  sv.minor_breaks = transform_value(trans, sv.minor_breaks)
123
123
  return sv
plotnine/data/__init__.py CHANGED
@@ -8,6 +8,7 @@ import pandas as pd
8
8
  from pandas.api.types import CategoricalDtype
9
9
 
10
10
  __all__ = (
11
+ "anscombe_quartet",
11
12
  "diamonds",
12
13
  "economics",
13
14
  "economics_long",
@@ -42,6 +43,7 @@ penguins = pd.read_csv(DATA_DIR / "penguins.csv")
42
43
  luv_colours = pd.read_csv(DATA_DIR / "luv_colours.csv")
43
44
  faithfuld = pd.read_csv(DATA_DIR / "faithfuld.csv")
44
45
  faithful = pd.read_csv(DATA_DIR / "faithful.csv")
46
+ anscombe_quartet = pd.read_csv(DATA_DIR / "anscombe-quartet.csv")
45
47
 
46
48
  # For convenience to the user, we set some columns in these
47
49
  # dataframes to categoricals.
@@ -81,20 +83,24 @@ def _process_categories():
81
83
  """
82
84
  Set columns in some of the dataframes to categoricals
83
85
  """
84
- global diamonds, midwest, mpg, msleep, penguins
86
+ global diamonds, penguins
85
87
  diamonds = _ordered_categories(
86
88
  diamonds,
87
89
  {
88
- "cut": "Fair, Good, Very Good, Premium, Ideal".split(", "),
89
- "clarity": "I1 SI2 SI1 VS2 VS1 VVS2 VVS1 IF".split(),
90
+ "cut": ["Fair", "Good", "Very Good", "Premium", "Ideal"],
91
+ "clarity": [
92
+ "I1",
93
+ "SI2",
94
+ "SI1",
95
+ "VS2",
96
+ "VS1",
97
+ "VVS2",
98
+ "VVS1",
99
+ "IF",
100
+ ],
90
101
  "color": list("DEFGHIJ"),
91
102
  },
92
103
  )
93
- mpg = _unordered_categories(
94
- mpg, "manufacturer model trans fl drv class".split()
95
- )
96
- midwest = _unordered_categories(midwest, ["category"])
97
- msleep = _unordered_categories(msleep, ["vore", "conservation"])
98
104
  penguins = _unordered_categories(penguins, ["species", "island", "sex"])
99
105
 
100
106
 
@@ -614,3 +620,32 @@ A data frame with 83 rows and 11 variables
614
620
  Additional variables order, conservation status and
615
621
  vore were added from wikipedia.
616
622
  """
623
+
624
+ anscombe_quartet.__doc__ = """
625
+ Anscombe's Quartet
626
+
627
+ ## Description
628
+
629
+ A dataset by Statistician Francis Anscombe that challenged the commonly held
630
+ belief that "numerical calculations are exact, but graphs are rough"
631
+ (Anscombe, 1973).
632
+
633
+ It comprises of 4 (the quartet!) small sub-datasets, each with 11 points that
634
+ have different distributions but nearly identical descriptive statistics.
635
+ It is perhaps the best argument for visualising data.
636
+
637
+ ## Format
638
+
639
+ A dataframe with 44 rows and 3 variables
640
+
641
+ | Column | Description |
642
+ |--------------|---------------------------------------|
643
+ | dataset | The Dataset |
644
+ | x | x |
645
+ | y | y |
646
+
647
+ ## References
648
+
649
+ Anscombe, F. J. (1973). "Graphs in Statistical Analysis".
650
+ American Statistician. 27 (1): 17–21.
651
+ """
@@ -0,0 +1,45 @@
1
+ dataset,x,y
2
+ I,10,8.04
3
+ I,8,6.95
4
+ I,13,7.58
5
+ I,9,8.81
6
+ I,11,8.33
7
+ I,14,9.96
8
+ I,6,7.24
9
+ I,4,4.26
10
+ I,12,10.84
11
+ I,7,4.82
12
+ I,5,5.68
13
+ II,10,9.14
14
+ II,8,8.14
15
+ II,13,8.74
16
+ II,9,8.77
17
+ II,11,9.26
18
+ II,14,8.1
19
+ II,6,6.13
20
+ II,4,3.1
21
+ II,12,9.13
22
+ II,7,7.26
23
+ II,5,4.74
24
+ III,10,7.46
25
+ III,8,6.77
26
+ III,13,12.74
27
+ III,9,7.11
28
+ III,11,7.81
29
+ III,14,8.84
30
+ III,6,6.08
31
+ III,4,5.39
32
+ III,12,8.15
33
+ III,7,6.42
34
+ III,5,5.73
35
+ IV,8,6.58
36
+ IV,8,5.76
37
+ IV,8,7.71
38
+ IV,8,8.84
39
+ IV,8,8.47
40
+ IV,8,7.04
41
+ IV,8,5.25
42
+ IV,19,12.5
43
+ IV,8,5.56
44
+ IV,8,7.91
45
+ IV,8,6.89
plotnine/doctools.py CHANGED
@@ -188,7 +188,7 @@ def dict_to_table(header: tuple[str, str], contents: dict[str, str]) -> str:
188
188
  fill `None`
189
189
  """
190
190
  rows = [
191
- (name, value if value == "" else f"`{value!r}`" "{.py}")
191
+ (name, value if value == "" else f"`{value!r}`{{.py}}")
192
192
  for name, value in contents.items()
193
193
  ]
194
194
  return table_function(rows, headers=header, tablefmt="grid")
@@ -451,7 +451,7 @@ def document_geom(geom: type[geom]) -> type[geom]:
451
451
  table = dict_to_table(("Aesthetic", "Default value"), contents)
452
452
  aesthetics_table = AESTHETICS_TABLE_TPL.format(table=table)
453
453
  tpl = dedent(geom._aesthetics_doc).strip()
454
- aesthetics_doc = tpl.format(aesthetics_table=aesthetics_table)
454
+ aesthetics_doc = tpl.replace("{aesthetics_table}", aesthetics_table)
455
455
  aesthetics_doc = indent(aesthetics_doc, " " * 4)
456
456
 
457
457
  # common_parameters
plotnine/facets/facet.py CHANGED
@@ -20,9 +20,9 @@ if typing.TYPE_CHECKING:
20
20
  import numpy.typing as npt
21
21
  from matplotlib.axes import Axes
22
22
  from matplotlib.figure import Figure
23
- from matplotlib.gridspec import GridSpec
24
23
 
25
24
  from plotnine import ggplot, theme
25
+ from plotnine._mpl.gridspec import p9GridSpec
26
26
  from plotnine.coords.coord import coord
27
27
  from plotnine.facets.labelling import CanBeStripLabellingFunc
28
28
  from plotnine.facets.layout import Layout
@@ -93,6 +93,7 @@ class facet:
93
93
 
94
94
  # Axes
95
95
  axs: list[Axes]
96
+ _panels_gridspec: p9GridSpec
96
97
 
97
98
  # ggplot object that the facet belongs to
98
99
  plot: ggplot
@@ -100,8 +101,6 @@ class facet:
100
101
  # Facet strips
101
102
  strips: Strips
102
103
 
103
- grid_spec: GridSpec
104
-
105
104
  # The plot environment
106
105
  environment: Environment
107
106
 
@@ -121,33 +120,39 @@ class facet:
121
120
  self.as_table = as_table
122
121
  self.drop = drop
123
122
  self.dir = dir
123
+ allowed_scales = ["fixed", "free", "free_x", "free_y"]
124
+ if scales not in allowed_scales:
125
+ raise ValueError(
126
+ "Argument `scales` must be one of {allowed_scales}."
127
+ )
124
128
  self.free = {
125
129
  "x": scales in ("free_x", "free"),
126
130
  "y": scales in ("free_y", "free"),
127
131
  }
128
132
 
129
- def __radd__(self, plot: ggplot) -> ggplot:
133
+ def __radd__(self, other: ggplot) -> ggplot:
130
134
  """
131
135
  Add facet to ggplot object
132
136
  """
133
- plot.facet = copy(self)
134
- plot.facet.environment = plot.environment
135
- return plot
137
+ other.facet = copy(self)
138
+ other.facet.environment = other.environment
139
+ return other
136
140
 
137
141
  def setup(self, plot: ggplot):
138
142
  self.plot = plot
139
143
  self.layout = plot.layout
144
+ self.figure = plot.figure
140
145
 
141
- if hasattr(plot, "figure"):
142
- self.figure, self.axs = plot.figure, plot.axs
146
+ if hasattr(plot, "axs"):
147
+ self.axs = plot.axs
143
148
  else:
144
- self.figure, self.axs = self.make_figure()
149
+ self.axs = self._make_axes()
145
150
 
146
151
  self.coordinates = plot.coordinates
147
152
  self.theme = plot.theme
148
153
  self.layout.axs = self.axs
149
154
  self.strips = Strips.from_facet(self)
150
- return self.figure, self.axs
155
+ return self.axs
151
156
 
152
157
  def setup_data(self, data: list[pd.DataFrame]) -> list[pd.DataFrame]:
153
158
  """
@@ -346,17 +351,8 @@ class facet:
346
351
  ax.xaxis.set_major_formatter(MyFixedFormatter(panel_params.x.labels))
347
352
  ax.yaxis.set_major_formatter(MyFixedFormatter(panel_params.y.labels))
348
353
 
349
- pad_x = (
350
- margin.get_as("t", "pt")
351
- if (margin := theme.getp(("axis_text_x", "margin")))
352
- else 0
353
- )
354
-
355
- pad_y = (
356
- margin.get_as("r", "pt")
357
- if (margin := theme.getp(("axis_text_y", "margin")))
358
- else 0
359
- )
354
+ pad_x = theme.get_margin("axis_text_x").pt.t
355
+ pad_y = theme.get_margin("axis_text_y").pt.r
360
356
 
361
357
  ax.tick_params(axis="x", which="major", pad=pad_x)
362
358
  ax.tick_params(axis="y", which="major", pad=pad_y)
@@ -372,7 +368,7 @@ class facet:
372
368
  new = result.__dict__
373
369
 
374
370
  # don't make a deepcopy of the figure & the axes
375
- shallow = {"figure", "axs", "first_ax", "last_ax"}
371
+ shallow = {"axs", "first_ax", "last_ax"}
376
372
  for key, item in old.items():
377
373
  if key in shallow:
378
374
  new[key] = item
@@ -382,35 +378,31 @@ class facet:
382
378
 
383
379
  return result
384
380
 
385
- def _make_figure(self) -> tuple[Figure, GridSpec]:
381
+ def _get_panels_gridspec(self) -> p9GridSpec:
386
382
  """
387
- Create figure & gridspec
383
+ Create gridspec for the panels
388
384
  """
389
- import matplotlib.pyplot as plt
390
- from matplotlib.gridspec import GridSpec
385
+ from plotnine._mpl.gridspec import p9GridSpec
391
386
 
392
- return plt.figure(), GridSpec(self.nrow, self.ncol)
387
+ return p9GridSpec(
388
+ self.nrow, self.ncol, self.figure, nest_into=self.plot._gridspec[0]
389
+ )
393
390
 
394
- def make_figure(self) -> tuple[Figure, list[Axes]]:
391
+ def _make_axes(self) -> list[Axes]:
395
392
  """
396
- Create and return Matplotlib figure and subplot axes
393
+ Create and return subplot axes
397
394
  """
398
395
  num_panels = len(self.layout.layout)
399
396
  axsarr = np.empty((self.nrow, self.ncol), dtype=object)
400
397
 
401
- # Create figure & gridspec
402
- figure, gs = self._make_figure()
403
- self.grid_spec = gs
398
+ self._panels_gridspec = self._get_panels_gridspec()
404
399
 
405
400
  # Create axes
406
401
  it = itertools.product(range(self.nrow), range(self.ncol))
407
402
  for i, (row, col) in enumerate(it):
408
- axsarr[row, col] = figure.add_subplot(gs[i])
409
-
410
- # axsarr = np.array([
411
- # figure.add_subplot(gs[i])
412
- # for i in range(self.nrow * self.ncol)
413
- # ]).reshape((self.nrow, self.ncol))
403
+ axsarr[row, col] = self.figure.add_subplot(
404
+ self._panels_gridspec[i]
405
+ )
414
406
 
415
407
  # Rearrange axes
416
408
  # They are ordered to match the positions in the layout table
@@ -429,9 +421,9 @@ class facet:
429
421
 
430
422
  # Delete unused axes
431
423
  for ax in axs[num_panels:]:
432
- figure.delaxes(ax)
424
+ self.figure.delaxes(ax)
433
425
  axs = axs[:num_panels]
434
- return figure, list(axs)
426
+ return list(axs)
435
427
 
436
428
  def _aspect_ratio(self) -> Optional[float]:
437
429
  """
@@ -477,8 +469,7 @@ def combine_vars(
477
469
  has_all = [x.shape[1] == len(vars) for x in values]
478
470
  if not any(has_all):
479
471
  raise PlotnineError(
480
- "At least one layer must contain all variables "
481
- "used for facetting"
472
+ "At least one layer must contain all variables used for facetting"
482
473
  )
483
474
  base = pd.concat([x for i, x in enumerate(values) if has_all[i]], axis=0)
484
475
  base = base.drop_duplicates()
@@ -107,9 +107,11 @@ class facet_grid(facet):
107
107
  self.space = space
108
108
  self.margins = margins
109
109
 
110
- def _make_figure(self):
111
- import matplotlib.pyplot as plt
112
- from matplotlib.gridspec import GridSpec
110
+ def _get_panels_gridspec(self):
111
+ """
112
+ Create gridspec for the panels
113
+ """
114
+ from plotnine._mpl.gridspec import p9GridSpec
113
115
 
114
116
  layout = self.layout
115
117
  space = self.space
@@ -155,7 +157,13 @@ class facet_grid(facet):
155
157
  ratios["width_ratios"] = self.space.get("x")
156
158
  ratios["height_ratios"] = self.space.get("y")
157
159
 
158
- return plt.figure(), GridSpec(self.nrow, self.ncol, **ratios)
160
+ return p9GridSpec(
161
+ self.nrow,
162
+ self.ncol,
163
+ self.figure,
164
+ nest_into=self.plot._gridspec[0],
165
+ **ratios,
166
+ )
159
167
 
160
168
  def compute_layout(self, data: list[pd.DataFrame]) -> pd.DataFrame:
161
169
  if not self.rows and not self.cols:
@@ -302,7 +310,7 @@ def parse_grid_facets_old(
302
310
  "((var1, var2), (var3, var4))",
303
311
  ]
304
312
  error_msg_s = (
305
- "Valid sequences for specifying 'facets' look like" f" {valid_seqs}"
313
+ f"Valid sequences for specifying 'facets' look like {valid_seqs}"
306
314
  )
307
315
 
308
316
  valid_forms = [
@@ -314,7 +322,7 @@ def parse_grid_facets_old(
314
322
  ". ~ func(var1) + func(var2)",
315
323
  ". ~ func(var1+var3) + func(var2)",
316
324
  ] + valid_seqs
317
- error_msg_f = "Valid formula for 'facet_grid' look like" f" {valid_forms}"
325
+ error_msg_f = f"Valid formula for 'facet_grid' look like {valid_forms}"
318
326
 
319
327
  if not isinstance(facets, str):
320
328
  if len(facets) == 1:
@@ -198,8 +198,7 @@ def check_dimensions(
198
198
  if nrow is not None:
199
199
  if nrow < 1:
200
200
  warn(
201
- "'nrow' must be greater than 0. "
202
- "Your value has been ignored.",
201
+ "'nrow' must be greater than 0. Your value has been ignored.",
203
202
  PlotnineWarning,
204
203
  )
205
204
  nrow = None
@@ -209,8 +208,7 @@ def check_dimensions(
209
208
  if ncol is not None:
210
209
  if ncol < 1:
211
210
  warn(
212
- "'ncol' must be greater than 0. "
213
- "Your value has been ignored.",
211
+ "'ncol' must be greater than 0. Your value has been ignored.",
214
212
  PlotnineWarning,
215
213
  )
216
214
  ncol = None
@@ -241,7 +239,7 @@ def parse_wrap_facets_old(facets: str | Sequence[str]) -> Sequence[str]:
241
239
  This handles the old & silently deprecated r-style formulas
242
240
  """
243
241
  valid_forms = ["~ var1", "~ var1 + var2"]
244
- error_msg = "Valid formula for 'facet_wrap' look like" f" {valid_forms}"
242
+ error_msg = f"Valid formula for 'facet_wrap' look like {valid_forms}"
245
243
 
246
244
  if isinstance(facets, (list, tuple)):
247
245
  return facets
plotnine/facets/strips.py CHANGED
@@ -63,62 +63,49 @@ class strip:
63
63
  """
64
64
  theme = self.theme
65
65
  position = self.position
66
+
66
67
  if position == "top":
67
68
  # The x & y values are just starting locations
68
69
  # The final location is determined by the layout manager.
69
- y = 1
70
- ha, va = "center", "bottom"
70
+ bg_y = 1
71
+ ha = theme.getp(("strip_text_x", "ha"), "center")
72
+ va = theme.getp(("strip_text_x", "va"), "center")
71
73
  rotation = theme.getp(("strip_text_x", "rotation"))
72
- box_width = 1
73
- box_height = 0 # Determined by the text size
74
- # TODO: Allow two unique paddings for either side.
75
- # Requires implementing an mpl.patches.boxstyle that recognises
76
- # two padding values.
77
-
78
- strip_text_margin = (
79
- margin.get_as("b", "lines")
80
- if (margin := theme.getp(("strip_text_x", "margin")))
81
- else 0
82
- )
74
+ bg_height = 0 # Determined by the text size
75
+ margin = theme.getp(("strip_text_x", "margin")).to("lines")
83
76
  strip_align = theme.getp("strip_align_x")
84
77
 
85
78
  # x & width properties of the background slide and
86
79
  # shrink the strip horizontally.
87
- x = theme.getp(("strip_text_x", "x"), 0)
88
- box_width = theme.getp(("strip_background_x", "width"), 1)
80
+ bg_x = theme.getp(("strip_text_x", "x"), 0)
81
+ bg_width = theme.getp(("strip_background_x", "width"), 1)
89
82
 
90
83
  elif position == "right":
91
84
  # The x & y values are just starting locations
92
85
  # The final location is determined by the layout manager.
93
- x = 1
94
- ha, va = "left", "center"
86
+ bg_x = 1
87
+ ha = theme.getp(("strip_text_y", "ha"), "center")
88
+ va = theme.getp(("strip_text_y", "va"), "center")
95
89
  rotation = theme.getp(("strip_text_y", "rotation"))
96
- box_width = 0 # Determine by the text height
97
- # TODO: Allow two unique paddings for either side.
98
- # Requires implementing an mpl.patches.boxstyle that recognises
99
- # two padding values.
100
- strip_text_margin = (
101
- margin.get_as("r", "lines")
102
- if (margin := theme.getp(("strip_text_y", "margin")))
103
- else 0
104
- )
90
+ bg_width = 0 # Determine by the text height
91
+ margin = theme.getp(("strip_text_y", "margin")).to("lines")
105
92
  strip_align = theme.getp("strip_align_y")
106
93
 
107
94
  # y & height properties of the background slide and
108
95
  # shrink the strip vertically.
109
- y = theme.getp(("strip_text_y", "y"), 0)
110
- box_height = theme.getp(("strip_background_y", "height"), 1)
96
+ bg_y = theme.getp(("strip_text_y", "y"), 0)
97
+ bg_height = theme.getp(("strip_background_y", "height"), 1)
111
98
  else:
112
99
  raise ValueError(f"Unknown position for strip text: {position!r}")
113
100
 
114
101
  return strip_draw_info(
115
- x=x,
116
- y=y,
102
+ bg_x=bg_x,
103
+ bg_y=bg_y,
117
104
  ha=ha,
118
105
  va=va,
119
- box_width=box_width,
120
- box_height=box_height,
121
- strip_text_margin=strip_text_margin,
106
+ bg_width=bg_width,
107
+ bg_height=bg_height,
108
+ margin=margin,
122
109
  strip_align=strip_align,
123
110
  position=position,
124
111
  label=self.label_info.text(),
@@ -131,12 +131,12 @@ class annotate:
131
131
  **kwargs,
132
132
  )
133
133
 
134
- def __radd__(self, plot: ggplot) -> ggplot:
134
+ def __radd__(self, other: ggplot) -> ggplot:
135
135
  """
136
136
  Add to ggplot
137
137
  """
138
- plot += self.to_layer() # Add layer
139
- return plot
138
+ other += self.to_layer() # Add layer
139
+ return other
140
140
 
141
141
  def to_layer(self) -> layer:
142
142
  """
@@ -285,4 +285,6 @@ class annotation_logticks(annotate):
285
285
  linetype=linetype,
286
286
  lengths=lengths,
287
287
  base=base,
288
+ inherit_aes=False,
289
+ show_legend=False,
288
290
  )
@@ -72,6 +72,8 @@ class annotation_stripes(annotate):
72
72
  fill_range=fill_range,
73
73
  extend=extend,
74
74
  direction=direction,
75
+ inherit_aes=False,
76
+ show_legend=False,
75
77
  **kwargs,
76
78
  )
77
79