mergeron 2024.738972.0__py3-none-any.whl → 2024.739079.9__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.

Potentially problematic release.


This version of mergeron might be problematic. Click here for more details.

Files changed (37) hide show
  1. mergeron/__init__.py +28 -3
  2. mergeron/core/__init__.py +2 -67
  3. mergeron/core/damodaran_margin_data.py +66 -52
  4. mergeron/core/excel_helper.py +32 -37
  5. mergeron/core/ftc_merger_investigations_data.py +66 -35
  6. mergeron/core/guidelines_boundaries.py +256 -1042
  7. mergeron/core/guidelines_boundary_functions.py +981 -0
  8. mergeron/core/{guidelines_boundaries_specialized_functions.py → guidelines_boundary_functions_extra.py} +53 -16
  9. mergeron/core/proportions_tests.py +2 -4
  10. mergeron/core/pseudorandom_numbers.py +6 -11
  11. mergeron/data/__init__.py +3 -0
  12. mergeron/data/damodaran_margin_data.xls +0 -0
  13. mergeron/data/damodaran_margin_data_dict.msgpack +0 -0
  14. mergeron/{jinja_LaTex_templates/setup_tikz_tables.tex.jinja2 → data/jinja2_LaTeX_templates/setup_tikz_tables.tex} +45 -50
  15. mergeron/demo/__init__.py +3 -0
  16. mergeron/demo/visualize_empirical_margin_distribution.py +88 -0
  17. mergeron/ext/__init__.py +2 -4
  18. mergeron/ext/tol_colors.py +3 -3
  19. mergeron/gen/__init__.py +53 -55
  20. mergeron/gen/_data_generation_functions.py +28 -93
  21. mergeron/gen/data_generation.py +20 -24
  22. mergeron/gen/{investigations_stats.py → enforcement_stats.py} +59 -57
  23. mergeron/gen/market_sample.py +6 -10
  24. mergeron/gen/upp_tests.py +29 -26
  25. mergeron-2024.739079.9.dist-info/METADATA +109 -0
  26. mergeron-2024.739079.9.dist-info/RECORD +36 -0
  27. mergeron/core/InCommon RSA Server CA cert chain.pem +0 -68
  28. mergeron-2024.738972.0.dist-info/METADATA +0 -108
  29. mergeron-2024.738972.0.dist-info/RECORD +0 -31
  30. /mergeron/{core → data}/ftc_invdata.msgpack +0 -0
  31. /mergeron/{jinja_LaTex_templates → data/jinja2_LaTeX_templates}/clrrate_cis_summary_table_template.tex.jinja2 +0 -0
  32. /mergeron/{jinja_LaTex_templates → data/jinja2_LaTeX_templates}/ftcinvdata_byhhianddelta_table_template.tex.jinja2 +0 -0
  33. /mergeron/{jinja_LaTex_templates → data/jinja2_LaTeX_templates}/ftcinvdata_summary_table_template.tex.jinja2 +0 -0
  34. /mergeron/{jinja_LaTex_templates → data/jinja2_LaTeX_templates}/ftcinvdata_summarypaired_table_template.tex.jinja2 +0 -0
  35. /mergeron/{jinja_LaTex_templates → data/jinja2_LaTeX_templates}/mergeron.cls +0 -0
  36. /mergeron/{jinja_LaTex_templates → data/jinja2_LaTeX_templates}/mergeron_table_collection_template.tex.jinja2 +0 -0
  37. {mergeron-2024.738972.0.dist-info → mergeron-2024.739079.9.dist-info}/WHEEL +0 -0
@@ -1,26 +1,30 @@
1
1
  """
2
2
  Specialized methods for defining and analyzing boundaries for Guidelines standards.
3
3
 
4
- These methods provide improved precision or demonstrate additional methods, but tend
5
- to have poor performance
4
+ These methods (functions) provide rely on scipy of sympy for core computations,
5
+ and may provide improved precision than core functions, but tend to have
6
+ poor performance
6
7
 
7
8
  """
8
9
 
9
10
  from collections.abc import Callable
10
11
  from dataclasses import dataclass
11
- from importlib.metadata import version
12
12
  from typing import Literal
13
13
 
14
14
  import numpy as np
15
15
  from mpmath import mp, mpf # type: ignore
16
16
  from numpy.typing import NDArray
17
17
  from scipy.spatial.distance import minkowski as distance_function # type: ignore
18
- from sympy import lambdify, simplify, solve, symbols
18
+ from sympy import lambdify, simplify, solve, symbols # type: ignore
19
19
 
20
- from .. import _PKG_NAME # noqa: TID252
21
- from .guidelines_boundaries import GuidelinesBoundary, _shrratio_boundary_intcpt, lerp
20
+ from .. import VERSION # noqa: TID252
21
+ from .guidelines_boundary_functions import (
22
+ GuidelinesBoundary,
23
+ _shrratio_boundary_intcpt,
24
+ lerp,
25
+ )
22
26
 
23
- __version__ = version(_PKG_NAME)
27
+ __version__ = VERSION
24
28
 
25
29
 
26
30
  mp.prec = 80
@@ -34,7 +38,39 @@ class GuidelinesBoundaryCallable:
34
38
  s_naught: float = 0
35
39
 
36
40
 
37
- def delta_hhi_boundary_qdtr(_dh_val: float = 0.01, /) -> GuidelinesBoundaryCallable:
41
+ def dh_area_quad(_dh_val: float = 0.01, /, *, prec: int = 9) -> float:
42
+ """
43
+ Area under the ΔHHI boundary.
44
+
45
+ When the given ΔHHI bound matches a Guidelines safeharbor,
46
+ the area under the boundary is half the intrinsic clearance rate
47
+ for the ΔHHI safeharbor.
48
+
49
+ Parameters
50
+ ----------
51
+ _dh_val
52
+ Merging-firms' ΔHHI bound.
53
+ prec
54
+ Specified precision in decimal places.
55
+
56
+ Returns
57
+ -------
58
+ Area under ΔHHI boundary.
59
+
60
+ """
61
+
62
+ _dh_val = mpf(f"{_dh_val}")
63
+ _s_naught = (1 - mp.sqrt(1 - 2 * _dh_val)) / 2
64
+
65
+ return round(
66
+ float(
67
+ _s_naught + mp.quad(lambda x: _dh_val / (2 * x), [_s_naught, 1 - _s_naught])
68
+ ),
69
+ prec,
70
+ )
71
+
72
+
73
+ def hhi_delta_boundary_qdtr(_dh_val: float = 0.01, /) -> GuidelinesBoundaryCallable:
38
74
  """
39
75
  Generate the list of share combination on the ΔHHI boundary.
40
76
 
@@ -70,7 +106,7 @@ def delta_hhi_boundary_qdtr(_dh_val: float = 0.01, /) -> GuidelinesBoundaryCalla
70
106
 
71
107
  def shrratio_boundary_qdtr_wtd_avg(
72
108
  _delta_star: float = 0.075,
73
- _r_val: float = 0.80,
109
+ _r_val: float = 0.85,
74
110
  /,
75
111
  *,
76
112
  weighting: Literal["own-share", "cross-product-share"] | None = "own-share",
@@ -189,10 +225,10 @@ def shrratio_boundary_qdtr_wtd_avg(
189
225
 
190
226
  def shrratio_boundary_distance(
191
227
  _delta_star: float = 0.075,
192
- _r_val: float = 0.80,
228
+ _r_val: float = 0.85,
193
229
  /,
194
230
  *,
195
- agg_method: Literal["arithmetic", "distance"] = "arithmetic",
231
+ agg_method: Literal["arithmetic mean", "distance"] = "arithmetic mean",
196
232
  weighting: Literal["own-share", "cross-product-share"] | None = "own-share",
197
233
  recapture_form: Literal["inside-out", "proportional"] = "inside-out",
198
234
  prec: int = 5,
@@ -202,7 +238,7 @@ def shrratio_boundary_distance(
202
238
  symmetric merging-firm margins.
203
239
 
204
240
  Reimplements the arithmetic-averages and distance estimations from function,
205
- `shrratio_boundary_wtd_avg`but uses the Minkowski-distance function,
241
+ `shrratio_boundary_wtd_avg` but uses the Minkowski-distance function,
206
242
  `scipy.spatial.distance.minkowski` for all aggregators. This reimplementation
207
243
  is useful for testing the output of `shrratio_boundary_wtd_avg`
208
244
  but runs considerably slower.
@@ -214,7 +250,7 @@ def shrratio_boundary_distance(
214
250
  _r_val
215
251
  recapture ratio
216
252
  agg_method
217
- Whether "arithmetic", "geometric", or "distance".
253
+ Whether "arithmetic mean" or "distance".
218
254
  weighting
219
255
  Whether "own-share" or "cross-product-share".
220
256
  recapture_form
@@ -272,7 +308,7 @@ def shrratio_boundary_distance(
272
308
  )
273
309
 
274
310
  match agg_method:
275
- case "arithmetic":
311
+ case "arithmetic mean":
276
312
  _delta_test = distance_function(
277
313
  (_de_1, _de_2), (0.0, 0.0), p=1, w=_weights_i
278
314
  )
@@ -334,11 +370,12 @@ def shrratio_boundary_distance(
334
370
  # Area under boundary
335
371
  _gbdry_area_total = 2 * _gbd_prtlarea - mp.power(_s_mid, "2")
336
372
 
337
- _gbdry_points = np.row_stack((_gbdry_points, (mpf("0.0"), _s_intcpt))).astype(
373
+ _gbdry_points = np.vstack((_gbdry_points, (mpf("0.0"), _s_intcpt))).astype(
338
374
  np.float64
339
375
  )
340
376
  # Points defining boundary to point-of-symmetry
341
377
  return GuidelinesBoundary(
342
- np.row_stack((np.flip(_gbdry_points, 0), np.flip(_gbdry_points[1:], 1))),
378
+ np.vstack((np.flip(_gbdry_points, 0), np.flip(_gbdry_points[1:], 1))),
343
379
  round(float(_gbdry_area_total), prec),
344
380
  )
381
+
@@ -9,7 +9,6 @@ from __future__ import annotations
9
9
 
10
10
  from collections.abc import Sequence
11
11
  from dataclasses import dataclass
12
- from importlib.metadata import version
13
12
  from typing import Literal, TypeVar
14
13
 
15
14
  import numpy as np
@@ -17,10 +16,9 @@ from numpy.typing import NBitBase, NDArray
17
16
  from scipy.optimize import OptimizeResult, root # type: ignore
18
17
  from scipy.stats import beta, chi2, norm # type: ignore
19
18
 
20
- from .. import _PKG_NAME # noqa: TID252
21
-
22
- __version__ = version(_PKG_NAME)
19
+ from .. import VERSION # noqa: TID252
23
20
 
21
+ __version__ = VERSION
24
22
 
25
23
  TI = TypeVar("TI", bound=NBitBase)
26
24
 
@@ -8,21 +8,16 @@ https://github.com/numpy/numpy/issues/16313.
8
8
 
9
9
  import concurrent.futures
10
10
  from collections.abc import Sequence
11
- from importlib.metadata import version
12
11
  from multiprocessing import cpu_count
13
- from typing import Literal, TypeVar
12
+ from typing import Literal
14
13
 
15
14
  import numpy as np
16
15
  from numpy.random import PCG64DXSM, Generator, SeedSequence
17
- from numpy.typing import NBitBase, NDArray
16
+ from numpy.typing import NDArray
18
17
 
19
- from .. import _PKG_NAME # noqa: TID252
18
+ from .. import VERSION # noqa: TID252
20
19
 
21
- __version__ = version(_PKG_NAME)
22
-
23
-
24
- TF = TypeVar("TF", bound=NBitBase)
25
- TI = TypeVar("TI", bound=NBitBase)
20
+ __version__ = VERSION
26
21
 
27
22
  NTHREADS = 2 * cpu_count()
28
23
  DIST_PARMS_DEFAULT = np.array([0.0, 1.0], np.float64)
@@ -145,7 +140,7 @@ class MultithreadedRNG:
145
140
  dist_type: Literal[
146
141
  "Beta", "Dirichlet", "Gaussian", "Normal", "Random", "Uniform"
147
142
  ] = "Uniform",
148
- dist_parms: NDArray[np.floating[TF]] | None = DIST_PARMS_DEFAULT, # type: ignore
143
+ dist_parms: NDArray[np.float64] | None = DIST_PARMS_DEFAULT,
149
144
  seed_sequence: SeedSequence | None = None,
150
145
  nthreads: int = NTHREADS,
151
146
  ):
@@ -211,7 +206,7 @@ class MultithreadedRNG:
211
206
  def _fill(
212
207
  _rng: np.random.Generator,
213
208
  _dist_type: str,
214
- _dist_parms: NDArray[np.floating[TF]],
209
+ _dist_parms: NDArray[np.float64],
215
210
  _out: NDArray[np.float64],
216
211
  _first: int,
217
212
  _last: int,
@@ -0,0 +1,3 @@
1
+ from .. import VERSION # noqa: TID252
2
+
3
+ __version__ = VERSION
Binary file
@@ -1,11 +1,7 @@
1
- ((# We force some color defintions here. These could be set in package code. #))
2
- ((* set obs_header_color = "0a6c97" *))
3
- ((* set sim_header_color = "646464" *))
4
- ((* set data_fill_color = "dfeadf" *))
5
1
  % Tables in tikz, but first we need to define some colors for nonwhite text backgrounds
6
- \definecolor{OBSHDRFill}{HTML}{\JINVAR{- obs_header_color -}}
7
- \definecolor{SIMHDRFill}{HTML}{\JINVAR{- sim_header_color -}}
8
- \definecolor{DataFill}{HTML}{\JINVAR{- data_fill_color -}}
2
+ \definecolor{OBSHDRFill}{HTML}{0a6c97}
3
+ \definecolor{SIMHDRFill}{HTML}{646464}
4
+ \definecolor{DataFill}{HTML}{dfeadf}
9
5
  % The below are definition's from Paul Tol's website, https://personal.sron.nl/~pault/
10
6
  \definecolor{VibrBlue}{HTML}{0077bb}
11
7
  \definecolor{BrightGreen}{HTML}{228833}
@@ -24,61 +20,60 @@
24
20
  \tikzset{
25
21
  % If you only have numbers, text depth = 0ex; if text, text depth = 0.25ex, (may need tweaking for alignment across cells)
26
22
  anytext/.style = {
27
- align = center,
28
- font = \sffamily\scriptsize,
29
- inner sep = 0pt,
30
- text depth = 0pt,
31
- }, %
23
+ align = center,
24
+ font = \sffamily\scriptsize,
25
+ inner sep = 0pt,
26
+ text depth = 0pt,
27
+ }, %
32
28
  hdrtext/.style = {
33
- anytext,
34
- text = white,
35
- fill = OBSHDRFill,
36
- }, %
29
+ anytext,
30
+ text = white,
31
+ fill = OBSHDRFill,
32
+ }, %
37
33
  notetext/.style = {
38
- anytext,
39
- font = \sffamily\tiny,
40
- align = left,
34
+ anytext,
35
+ font = \sffamily\tiny,
36
+ align = left,
41
37
  },
42
38
  anytable/.style = {
43
- matrix of nodes,
44
- nodes in empty cells,
45
- column sep = -1.0\pgflinewidth,
46
- row sep = -1.0\pgflinewidth,
47
- inner sep = -0.25\pgflinewidth,
48
- outer sep = -0.25\pgflinewidth,
49
- nodes = {anchor = center, minimum height = 12.5pt,}
39
+ matrix of nodes,
40
+ nodes in empty cells,
41
+ column sep = -1.0\pgflinewidth,
42
+ row sep = -1.0\pgflinewidth,
43
+ inner sep = -0.25\pgflinewidth,
44
+ outer sep = -0.25\pgflinewidth,
45
+ nodes = {anchor = center, minimum height = 12.5pt,}
50
46
  },
51
47
  datatable/.style = {
52
- anytable,
53
- nodes = {
54
- font = \sffamily\scriptsize\addfontfeatures{Numbers={Monospaced,Lining}},
55
- align = right,
56
- inner xsep = 3pt,
57
- inner ysep = 0pt,
58
- text depth = 0pt,
59
- draw = none,
60
- fill = none,
61
- },
62
- every even row/.style = {
63
- nodes = {fill = DataFill}
64
- },
48
+ anytable,
49
+ nodes = {
50
+ font = \sffamily\scriptsize\addfontfeatures{Numbers={Monospaced,Lining}},
51
+ align = right,
52
+ inner xsep = 3pt,
53
+ inner ysep = 0pt,
54
+ text depth = 0pt,
55
+ draw = none,
56
+ fill = none,
57
+ },
58
+ every even row/.style = {
59
+ nodes = {fill = DataFill}
60
+ },
65
61
  },
66
62
  hrow/.style = {
67
- anytable,
68
- nodes = {
69
- hdrtext,
70
- draw = none,
71
- fill = OBSHDRFill,
72
- text width = 40pt,
73
- },
63
+ anytable,
64
+ nodes = {
65
+ hdrtext,
66
+ draw = none,
67
+ fill = OBSHDRFill,
68
+ text width = 40pt,
69
+ },
74
70
  },
75
71
  hcol/.style = {
76
- hrow,
77
- nodes = {text width = 60pt,},
72
+ hrow,
73
+ nodes = {text width = 60pt,},
78
74
  },
79
- }
75
+ }
80
76
  % Define layers for later reference
81
77
  % https://tex.stackexchange.com/questions/40840/put-a-node-behind-another-in-a-tikz-diagram
82
78
  \pgfdeclarelayer{background}
83
79
  \pgfsetlayers{background,main}
84
-
@@ -0,0 +1,3 @@
1
+ from .. import VERSION # noqa: TID252
2
+
3
+ __version__ = VERSION
@@ -0,0 +1,88 @@
1
+ """
2
+ Plot the empirical distribution derived using the Gaussian KDE with
3
+ margin data downloaded from Prof. Damodaran's website at NYU.
4
+
5
+
6
+ """
7
+
8
+ import warnings
9
+ from pathlib import Path
10
+
11
+ import numpy as np
12
+ from matplotlib.ticker import StrMethodFormatter
13
+ from numpy.random import PCG64DXSM, Generator, SeedSequence
14
+ from scipy import stats # type: ignore
15
+
16
+ import mergeron.core.damodaran_margin_data as dmgn
17
+ from mergeron import DATA_DIR
18
+ from mergeron.core.guidelines_boundary_functions import boundary_plot
19
+
20
+ SAMPLE_SIZE = 10**6
21
+ BIN_COUNT = 25
22
+ mgn_data_obs, mgn_data_wts, mgn_data_stats = dmgn.mgn_data_builder()
23
+ print(repr(mgn_data_obs))
24
+ print(repr(mgn_data_stats))
25
+
26
+ plt, mgn_fig, mgn_ax, set_axis_def = boundary_plot(mktshares_plot_flag=False)
27
+ mgn_fig.set_figheight(6.5)
28
+ mgn_fig.set_figwidth(9.0)
29
+
30
+ _, mgn_bins, _ = mgn_ax.hist(
31
+ x=mgn_data_obs,
32
+ weights=mgn_data_wts,
33
+ bins=BIN_COUNT,
34
+ alpha=0.4,
35
+ density=True,
36
+ label="Downloaded data",
37
+ color="#004488", # Paul Tol's High Contrast Blue
38
+ )
39
+
40
+ with warnings.catch_warnings():
41
+ warnings.filterwarnings("ignore", category=UserWarning)
42
+ # Don't warn regarding the below; ticklabels have been fixed before this point
43
+ mgn_ax.set_yticklabels([
44
+ f"{float(_g.get_text()) * np.diff(mgn_bins)[-1]:.0%}"
45
+ for _g in mgn_ax.get_yticklabels()
46
+ ])
47
+
48
+ mgn_kde = stats.gaussian_kde(mgn_data_obs, weights=mgn_data_wts, bw_method="silverman")
49
+ mgn_kde.set_bandwidth(bw_method=mgn_kde.factor / 3.0)
50
+
51
+ mgn_xvec = np.linspace(0, BIN_COUNT, 10**5) / BIN_COUNT
52
+ mgn_ax.plot(
53
+ mgn_xvec,
54
+ mgn_kde(mgn_xvec),
55
+ color="#004488",
56
+ rasterized=True,
57
+ label="Estimated Density",
58
+ )
59
+
60
+ mgn_ax.hist(
61
+ x=mgn_kde.resample(
62
+ SAMPLE_SIZE, seed=Generator(PCG64DXSM(SeedSequence(pool_size=8)))
63
+ )[0],
64
+ color="#DDAA33", # Paul Tol's High Contrast Yellow
65
+ alpha=0.6,
66
+ bins=BIN_COUNT,
67
+ density=True,
68
+ label="Generated data",
69
+ )
70
+
71
+ mgn_ax.legend(
72
+ loc="best",
73
+ fancybox=False,
74
+ shadow=False,
75
+ frameon=True,
76
+ facecolor="white",
77
+ edgecolor="white",
78
+ framealpha=1,
79
+ fontsize="small",
80
+ )
81
+
82
+ mgn_ax.set_xlim(0.0, 1.0)
83
+ mgn_ax.xaxis.set_major_formatter(StrMethodFormatter("{x:>3.0%}"))
84
+ mgn_ax.set_xlabel("Price Cost Margin", fontsize=10)
85
+ mgn_ax.set_ylabel("Relative Frequency", fontsize=10)
86
+
87
+ mgn_fig.tight_layout()
88
+ plt.savefig(DATA_DIR / f"{Path(__file__).stem}.pdf")
mergeron/ext/__init__.py CHANGED
@@ -1,5 +1,3 @@
1
- from importlib.metadata import version
1
+ from .. import VERSION # noqa: TID252
2
2
 
3
- from .. import _PKG_NAME # noqa: TID252
4
-
5
- __version__ = version(_PKG_NAME)
3
+ __version__ = VERSION
@@ -796,7 +796,7 @@ def main() -> None:
796
796
  schemes: Sequence[str] = tol_cset()
797
797
  fig, axes = plt.subplots(ncols=len(schemes), figsize=(9, 3))
798
798
  fig.subplots_adjust(top=0.9, bottom=0.02, left=0.02, right=0.92)
799
- for ax, scheme in zip(axes, schemes):
799
+ for ax, scheme in zip(axes, schemes): # type: ignore
800
800
  cset = tol_cset(scheme)
801
801
  names = cset._fields # type: ignore
802
802
  colors = list(cset)
@@ -813,7 +813,7 @@ def main() -> None:
813
813
  gradient = np.vstack((gradient, gradient))
814
814
  fig, axes = plt.subplots(nrows=len(schemes))
815
815
  fig.subplots_adjust(top=0.98, bottom=0.02, left=0.2, right=0.99)
816
- for ax, scheme in zip(axes, schemes):
816
+ for ax, scheme in zip(axes, schemes): # type: ignore
817
817
  pos = list(ax.get_position().bounds)
818
818
  ax.set_axis_off()
819
819
  ax.imshow(gradient, aspect=4, cmap=tol_cmap(scheme))
@@ -832,7 +832,7 @@ def main() -> None:
832
832
  gradient = np.vstack((gradient, gradient))
833
833
  fig, axes = plt.subplots(nrows=23)
834
834
  fig.subplots_adjust(top=0.98, bottom=0.02, left=0.25, right=0.99)
835
- for lut, ax in enumerate(axes, start=1):
835
+ for lut, ax in enumerate(axes, start=1): # type: ignore
836
836
  pos = list(ax.get_position().bounds)
837
837
  ax.set_axis_off()
838
838
  ax.imshow(gradient, aspect=4, cmap=tol_cmap("rainbow_discrete", lut))