plotnine 0.15.0a2__py3-none-any.whl → 0.15.0a3__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 (67) hide show
  1. plotnine/_mpl/layout_manager/_layout_tree.py +16 -6
  2. plotnine/_utils/__init__.py +4 -2
  3. plotnine/geoms/annotation_logticks.py +5 -8
  4. plotnine/geoms/annotation_stripes.py +4 -6
  5. plotnine/geoms/geom.py +20 -8
  6. plotnine/geoms/geom_abline.py +3 -2
  7. plotnine/geoms/geom_blank.py +0 -3
  8. plotnine/geoms/geom_boxplot.py +4 -4
  9. plotnine/geoms/geom_crossbar.py +3 -3
  10. plotnine/geoms/geom_dotplot.py +1 -1
  11. plotnine/geoms/geom_errorbar.py +2 -2
  12. plotnine/geoms/geom_errorbarh.py +2 -2
  13. plotnine/geoms/geom_hline.py +3 -2
  14. plotnine/geoms/geom_linerange.py +2 -2
  15. plotnine/geoms/geom_map.py +3 -3
  16. plotnine/geoms/geom_path.py +10 -11
  17. plotnine/geoms/geom_point.py +4 -5
  18. plotnine/geoms/geom_pointrange.py +3 -5
  19. plotnine/geoms/geom_polygon.py +2 -3
  20. plotnine/geoms/geom_raster.py +4 -5
  21. plotnine/geoms/geom_rect.py +3 -4
  22. plotnine/geoms/geom_ribbon.py +7 -7
  23. plotnine/geoms/geom_rug.py +1 -1
  24. plotnine/geoms/geom_segment.py +2 -2
  25. plotnine/geoms/geom_smooth.py +3 -3
  26. plotnine/geoms/geom_step.py +2 -2
  27. plotnine/geoms/geom_text.py +2 -3
  28. plotnine/geoms/geom_violin.py +4 -5
  29. plotnine/geoms/geom_vline.py +3 -2
  30. plotnine/guides/guides.py +1 -1
  31. plotnine/layer.py +18 -12
  32. plotnine/mapping/_eval_environment.py +1 -1
  33. plotnine/scales/scale_color.py +46 -14
  34. plotnine/scales/scale_continuous.py +5 -4
  35. plotnine/scales/scale_datetime.py +28 -14
  36. plotnine/scales/scale_discrete.py +2 -2
  37. plotnine/scales/scale_xy.py +2 -2
  38. plotnine/stats/smoothers.py +19 -19
  39. plotnine/stats/stat.py +15 -25
  40. plotnine/stats/stat_bin.py +2 -5
  41. plotnine/stats/stat_bin_2d.py +7 -9
  42. plotnine/stats/stat_bindot.py +5 -8
  43. plotnine/stats/stat_boxplot.py +5 -5
  44. plotnine/stats/stat_count.py +5 -9
  45. plotnine/stats/stat_density.py +5 -8
  46. plotnine/stats/stat_density_2d.py +12 -9
  47. plotnine/stats/stat_ecdf.py +6 -5
  48. plotnine/stats/stat_ellipse.py +5 -6
  49. plotnine/stats/stat_function.py +6 -8
  50. plotnine/stats/stat_hull.py +2 -3
  51. plotnine/stats/stat_identity.py +1 -2
  52. plotnine/stats/stat_pointdensity.py +4 -7
  53. plotnine/stats/stat_qq.py +45 -20
  54. plotnine/stats/stat_qq_line.py +15 -11
  55. plotnine/stats/stat_quantile.py +6 -7
  56. plotnine/stats/stat_sina.py +12 -14
  57. plotnine/stats/stat_smooth.py +7 -10
  58. plotnine/stats/stat_sum.py +1 -2
  59. plotnine/stats/stat_summary.py +6 -9
  60. plotnine/stats/stat_summary_bin.py +10 -13
  61. plotnine/stats/stat_unique.py +1 -2
  62. plotnine/stats/stat_ydensity.py +7 -10
  63. {plotnine-0.15.0a2.dist-info → plotnine-0.15.0a3.dist-info}/METADATA +3 -3
  64. {plotnine-0.15.0a2.dist-info → plotnine-0.15.0a3.dist-info}/RECORD +67 -67
  65. {plotnine-0.15.0a2.dist-info → plotnine-0.15.0a3.dist-info}/WHEEL +1 -1
  66. {plotnine-0.15.0a2.dist-info → plotnine-0.15.0a3.dist-info}/licenses/LICENSE +0 -0
  67. {plotnine-0.15.0a2.dist-info → plotnine-0.15.0a3.dist-info}/top_level.txt +0 -0
plotnine/stats/stat_qq.py CHANGED
@@ -1,3 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
1
5
  import numpy as np
2
6
  import pandas as pd
3
7
 
@@ -6,6 +10,11 @@ from ..exceptions import PlotnineError
6
10
  from ..mapping.evaluation import after_stat
7
11
  from .stat import stat
8
12
 
13
+ if TYPE_CHECKING:
14
+ from typing import Any, Sequence
15
+
16
+ from plotnine.typing import FloatArray
17
+
9
18
 
10
19
  # Note: distribution should be a name from scipy.stat.distribution
11
20
  @document
@@ -65,25 +74,41 @@ class stat_qq(stat):
65
74
  "alpha_beta": (3 / 8, 3 / 8),
66
75
  }
67
76
 
68
- @classmethod
69
- def compute_group(cls, data, scales, **params):
70
- from scipy.stats.mstats import plotting_positions
71
-
72
- from .distributions import get_continuous_distribution
73
-
77
+ def compute_group(self, data, scales):
74
78
  sample = data["sample"].sort_values().to_numpy()
75
- alpha, beta = params["alpha_beta"]
76
- quantiles = params["quantiles"]
77
-
78
- if quantiles is None:
79
- quantiles = plotting_positions(sample, alpha, beta)
80
- elif len(quantiles) != len(sample):
81
- raise PlotnineError(
82
- "The number of quantile values is not the same as "
83
- "the number of sample values."
84
- )
85
-
86
- quantiles = np.asarray(quantiles)
87
- cdist = get_continuous_distribution(params["distribution"])
88
- theoretical = cdist.ppf(quantiles, **params["dparams"])
79
+ theoretical = theoretical_qq(
80
+ sample,
81
+ self.params["distribution"],
82
+ alpha=self.params["alpha_beta"][0],
83
+ beta=self.params["alpha_beta"][1],
84
+ quantiles=self.params["quantiles"],
85
+ distribution_params=self.params["dparams"],
86
+ )
89
87
  return pd.DataFrame({"sample": sample, "theoretical": theoretical})
88
+
89
+
90
+ def theoretical_qq(
91
+ x: FloatArray,
92
+ distribution: str,
93
+ alpha: float,
94
+ beta: float,
95
+ quantiles: Sequence[float] | None,
96
+ distribution_params: dict[str, Any],
97
+ ) -> FloatArray:
98
+ """
99
+ Caculate theoretical qq distribution
100
+ """
101
+ from scipy.stats.mstats import plotting_positions
102
+
103
+ from .distributions import get_continuous_distribution
104
+
105
+ if quantiles is None:
106
+ quantiles = plotting_positions(x, alpha, beta)
107
+ elif len(quantiles) != len(x):
108
+ raise PlotnineError(
109
+ "The number of quantile values is not the same as "
110
+ "the number of sample values."
111
+ )
112
+
113
+ cdist = get_continuous_distribution(distribution)
114
+ return cdist.ppf(np.asarray(quantiles), **distribution_params)
@@ -4,7 +4,7 @@ import pandas as pd
4
4
  from ..doctools import document
5
5
  from ..exceptions import PlotnineError
6
6
  from .stat import stat
7
- from .stat_qq import stat_qq
7
+ from .stat_qq import theoretical_qq
8
8
 
9
9
 
10
10
  @document
@@ -64,31 +64,35 @@ class stat_qq_line(stat):
64
64
  raise PlotnineError(
65
65
  "Cannot fit line quantiles. 'line_p' must be of length 2"
66
66
  )
67
- return self.params
68
67
 
69
- @classmethod
70
- def compute_group(cls, data, scales, **params):
68
+ def compute_group(self, data, scales):
71
69
  from scipy.stats.mstats import mquantiles
72
70
 
73
71
  from .distributions import get_continuous_distribution
74
72
 
75
- line_p = params["line_p"]
76
- dparams = params["dparams"]
73
+ line_p = self.params["line_p"]
74
+ dparams = self.params["dparams"]
77
75
 
78
76
  # Compute theoretical values
79
- qq_gdata = stat_qq.compute_group(data, scales, **params)
80
- sample = qq_gdata["sample"].to_numpy()
81
- theoretical = qq_gdata["theoretical"].to_numpy()
77
+ sample = data["sample"].sort_values().to_numpy()
78
+ theoretical = theoretical_qq(
79
+ sample,
80
+ self.params["distribution"],
81
+ alpha=self.params["alpha_beta"][0],
82
+ beta=self.params["alpha_beta"][1],
83
+ quantiles=self.params["quantiles"],
84
+ distribution_params=dparams,
85
+ )
82
86
 
83
87
  # Compute slope & intercept of the line through the quantiles
84
- cdist = get_continuous_distribution(params["distribution"])
88
+ cdist = get_continuous_distribution(self.params["distribution"])
85
89
  x_coords = cdist.ppf(line_p, **dparams)
86
90
  y_coords = mquantiles(sample, line_p)
87
91
  slope = (np.diff(y_coords) / np.diff(x_coords))[0]
88
92
  intercept = y_coords[0] - slope * x_coords[0]
89
93
 
90
94
  # Get x,y points that describe the line
91
- if params["fullrange"] and scales.x:
95
+ if self.params["fullrange"] and scales.x:
92
96
  x = scales.x.dimension()
93
97
  else:
94
98
  x = theoretical.min(), theoretical.max()
@@ -59,7 +59,7 @@ class stat_quantile(stat):
59
59
  CREATES = {"quantile", "group"}
60
60
 
61
61
  def setup_params(self, data):
62
- params = self.params.copy()
62
+ params = self.params
63
63
  if params["formula"] is None:
64
64
  params["formula"] = "y ~ x"
65
65
  warn("Formula not specified, using '{}'", PlotnineWarning)
@@ -68,15 +68,14 @@ class stat_quantile(stat):
68
68
  except TypeError:
69
69
  params["quantiles"] = (params["quantiles"],)
70
70
 
71
- return params
72
-
73
- @classmethod
74
- def compute_group(cls, data, scales, **params):
75
- res = [quant_pred(q, data, **params) for q in params["quantiles"]]
71
+ def compute_group(self, data, scales):
72
+ res = [
73
+ quant_pred(q, data, self.params) for q in self.params["quantiles"]
74
+ ]
76
75
  return pd.concat(res, axis=0, ignore_index=True)
77
76
 
78
77
 
79
- def quant_pred(q, data, **params):
78
+ def quant_pred(q, data, params):
80
79
  """
81
80
  Quantile precitions
82
81
  """
@@ -116,7 +116,7 @@ class stat_sina(stat):
116
116
  return data
117
117
 
118
118
  def setup_params(self, data):
119
- params = self.params.copy()
119
+ params = self.params
120
120
  random_state = params["random_state"]
121
121
 
122
122
  if params["maxwidth"] is None:
@@ -137,10 +137,9 @@ class stat_sina(stat):
137
137
  params["clip"] = (-np.inf, np.inf)
138
138
  params["bounds"] = (-np.inf, np.inf)
139
139
  params["n"] = 512
140
- return params
141
140
 
142
- @classmethod
143
- def compute_panel(cls, data, scales, **params):
141
+ def compute_panel(self, data, scales):
142
+ params = self.params
144
143
  maxwidth = params["maxwidth"]
145
144
  random_state = params["random_state"]
146
145
  fuzz = 1e-8
@@ -154,7 +153,7 @@ class stat_sina(stat):
154
153
  else:
155
154
  params["bins"] = breaks_from_bins(y_dim_fuzzed, params["bins"])
156
155
 
157
- data = super(cls, stat_sina).compute_panel(data, scales, **params)
156
+ data = super().compute_panel(data, scales)
158
157
 
159
158
  if not len(data):
160
159
  return data
@@ -198,11 +197,10 @@ class stat_sina(stat):
198
197
 
199
198
  return data
200
199
 
201
- @classmethod
202
- def compute_group(cls, data, scales, **params):
203
- maxwidth = params["maxwidth"]
204
- bins = params["bins"]
205
- bin_limit = params["bin_limit"]
200
+ def compute_group(self, data, scales):
201
+ maxwidth = self.params["maxwidth"]
202
+ bins = self.params["bins"]
203
+ bin_limit = self.params["bin_limit"]
206
204
  weight = None
207
205
  y = data["y"]
208
206
 
@@ -215,12 +213,12 @@ class stat_sina(stat):
215
213
  elif len(np.unique(y)) < 2:
216
214
  data["density"] = 1
217
215
  data["scaled"] = 1
218
- elif params["method"] == "density":
216
+ elif self.params["method"] == "density":
219
217
  from scipy.interpolate import interp1d
220
218
 
221
219
  # density kernel estimation
222
220
  range_y = y.min(), y.max()
223
- dens = compute_density(y, weight, range_y, **params)
221
+ dens = compute_density(y, weight, range_y, self.params)
224
222
  densf = interp1d(
225
223
  dens["x"],
226
224
  dens["density"],
@@ -253,9 +251,9 @@ class stat_sina(stat):
253
251
 
254
252
  return data
255
253
 
256
- def finish_layer(self, data, params):
254
+ def finish_layer(self, data):
257
255
  # Rescale x in case positions have been adjusted
258
- style = params["style"]
256
+ style = self.params["style"]
259
257
  x_mean = data["x"].to_numpy()
260
258
  x_mod = (data["xmax"] - data["xmin"]) / data["width"]
261
259
  data["x"] = data["x"] + data["x_diff"] * x_mod
@@ -37,7 +37,7 @@ class stat_smooth(stat):
37
37
  If a `callable` is passed, it must have the signature:
38
38
 
39
39
  ```python
40
- def my_smoother(data, xseq, **params):
40
+ def my_smoother(data, xseq, params):
41
41
  # * data - has the x and y values for the model
42
42
  # * xseq - x values to be predicted
43
43
  # * params - stat parameters
@@ -163,7 +163,7 @@ class stat_smooth(stat):
163
163
  return data
164
164
 
165
165
  def setup_params(self, data):
166
- params = self.params.copy()
166
+ params = self.params
167
167
  # Use loess/lowess for small datasets
168
168
  # and glm for large
169
169
  if params["method"] == "auto":
@@ -202,12 +202,9 @@ class stat_smooth(stat):
202
202
  )
203
203
  params["environment"] = self.environment
204
204
 
205
- return params
206
-
207
- @classmethod
208
- def compute_group(cls, data, scales, **params):
205
+ def compute_group(self, data, scales):
209
206
  data = data.sort_values("x")
210
- n = params["n"]
207
+ n = self.params["n"]
211
208
 
212
209
  x_unique = data["x"].unique()
213
210
 
@@ -223,15 +220,15 @@ class stat_smooth(stat):
223
220
  return pd.DataFrame()
224
221
 
225
222
  if data["x"].dtype.kind == "i":
226
- if params["fullrange"]:
223
+ if self.params["fullrange"]:
227
224
  xseq = scales.x.dimension()
228
225
  else:
229
226
  xseq = np.sort(x_unique)
230
227
  else:
231
- if params["fullrange"]:
228
+ if self.params["fullrange"]:
232
229
  rangee = scales.x.dimension()
233
230
  else:
234
231
  rangee = [data["x"].min(), data["x"].max()]
235
232
  xseq = np.linspace(rangee[0], rangee[1], n)
236
233
 
237
- return predictdf(data, xseq, **params)
234
+ return predictdf(data, xseq, self.params)
@@ -35,8 +35,7 @@ class stat_sum(stat):
35
35
  DEFAULT_AES = {"size": after_stat("n"), "weight": 1}
36
36
  CREATES = {"n", "prop"}
37
37
 
38
- @classmethod
39
- def compute_panel(cls, data, scales, **params):
38
+ def compute_panel(self, data, scales):
40
39
  if "weight" not in data:
41
40
  data["weight"] = 1
42
41
 
@@ -299,16 +299,13 @@ class stat_summary(stat):
299
299
 
300
300
  self.params["fun_args"]["random_state"] = random_state
301
301
 
302
- return self.params
303
-
304
- @classmethod
305
- def compute_panel(cls, data, scales, **params):
302
+ def compute_panel(self, data, scales):
306
303
  func = make_summary_fun(
307
- params["fun_data"],
308
- params["fun_y"],
309
- params["fun_ymin"],
310
- params["fun_ymax"],
311
- params["fun_args"],
304
+ self.params["fun_data"],
305
+ self.params["fun_y"],
306
+ self.params["fun_ymin"],
307
+ self.params["fun_ymax"],
308
+ self.params["fun_args"],
312
309
  )
313
310
 
314
311
  # break a dataframe into pieces, summarise each piece,
@@ -123,21 +123,18 @@ class stat_summary_bin(stat):
123
123
 
124
124
  self.params["fun_args"]["random_state"] = random_state
125
125
 
126
- return self.params
127
-
128
- @classmethod
129
- def compute_group(cls, data, scales, **params):
130
- bins = params["bins"]
131
- breaks = params["breaks"]
132
- binwidth = params["binwidth"]
133
- boundary = params["boundary"]
126
+ def compute_group(self, data, scales):
127
+ bins = self.params["bins"]
128
+ breaks = self.params["breaks"]
129
+ binwidth = self.params["binwidth"]
130
+ boundary = self.params["boundary"]
134
131
 
135
132
  func = make_summary_fun(
136
- params["fun_data"],
137
- params["fun_y"],
138
- params["fun_ymin"],
139
- params["fun_ymax"],
140
- params["fun_args"],
133
+ self.params["fun_data"],
134
+ self.params["fun_y"],
135
+ self.params["fun_ymin"],
136
+ self.params["fun_ymax"],
137
+ self.params["fun_args"],
141
138
  )
142
139
 
143
140
  breaks = fuzzybreaks(scales.x, breaks, boundary, binwidth, bins)
@@ -16,6 +16,5 @@ class stat_unique(stat):
16
16
 
17
17
  DEFAULT_PARAMS = {"geom": "point", "position": "identity", "na_rm": False}
18
18
 
19
- @classmethod
20
- def compute_panel(cls, data, scales, **params):
19
+ def compute_panel(self, data, scales):
21
20
  return data.drop_duplicates()
@@ -109,7 +109,7 @@ class stat_ydensity(stat):
109
109
  return data
110
110
 
111
111
  def setup_params(self, data):
112
- params = self.params.copy()
112
+ params = self.params
113
113
 
114
114
  valid_scale = ("area", "count", "width")
115
115
  if params["scale"] not in valid_scale:
@@ -141,11 +141,9 @@ class stat_ydensity(stat):
141
141
  for key in missing_params:
142
142
  params[key] = stat_density.DEFAULT_PARAMS[key]
143
143
 
144
- return params
145
-
146
- @classmethod
147
- def compute_panel(cls, data, scales, **params):
148
- data = super(cls, cls).compute_panel(data, scales, **params)
144
+ def compute_panel(self, data, scales):
145
+ params = self.params
146
+ data = super().compute_panel(data, scales)
149
147
 
150
148
  if not len(data):
151
149
  return data
@@ -167,20 +165,19 @@ class stat_ydensity(stat):
167
165
 
168
166
  return data
169
167
 
170
- @classmethod
171
- def compute_group(cls, data, scales, **params):
168
+ def compute_group(self, data, scales):
172
169
  n = len(data)
173
170
  if n == 0:
174
171
  return pd.DataFrame()
175
172
 
176
173
  weight = data.get("weight")
177
174
 
178
- if params["trim"]:
175
+ if self.params["trim"]:
179
176
  range_y = data["y"].min(), data["y"].max()
180
177
  else:
181
178
  range_y = scales.y.dimension()
182
179
 
183
- dens = compute_density(data["y"], weight, range_y, **params)
180
+ dens = compute_density(data["y"], weight, range_y, self.params)
184
181
 
185
182
  if not len(dens):
186
183
  return dens
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plotnine
3
- Version: 0.15.0a2
3
+ Version: 0.15.0a3
4
4
  Summary: A Grammar of Graphics for Python
5
5
  Author-email: Hassan Kibirige <has2k1@gmail.com>
6
6
  License: The MIT License (MIT)
@@ -46,7 +46,7 @@ Description-Content-Type: text/markdown
46
46
  License-File: LICENSE
47
47
  Requires-Dist: matplotlib>=3.8.0
48
48
  Requires-Dist: pandas>=2.2.0
49
- Requires-Dist: mizani~=0.13.0
49
+ Requires-Dist: mizani~=0.14.0
50
50
  Requires-Dist: numpy>=1.23.5
51
51
  Requires-Dist: scipy>=1.8.0
52
52
  Requires-Dist: statsmodels>=0.14.0
@@ -82,7 +82,7 @@ Requires-Dist: twine; extra == "dev"
82
82
  Requires-Dist: plotnine[typing]; extra == "dev"
83
83
  Requires-Dist: pre-commit; extra == "dev"
84
84
  Provides-Extra: typing
85
- Requires-Dist: pyright==1.1.400; extra == "typing"
85
+ Requires-Dist: pyright==1.1.402; extra == "typing"
86
86
  Requires-Dist: ipython; extra == "typing"
87
87
  Requires-Dist: pandas-stubs; extra == "typing"
88
88
  Dynamic: license-file