mergeron 2024.738930.0__py3-none-any.whl → 2024.738936.2__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.
- mergeron/core/__init__.py +1 -1
- mergeron/core/guidelines_standards.py +247 -57
- mergeron/core/proportions_tests.py +19 -19
- mergeron/core/pseudorandom_numbers.py +30 -29
- mergeron/examples/__init__.py +1 -1
- mergeron/examples/concentration_as_diversion.py +75 -88
- mergeron/examples/investigations_stats_obs_tables.py +119 -111
- mergeron/examples/investigations_stats_sim_tables.py +108 -87
- mergeron/examples/plotSafeHarbs_symbolically.py +2 -2
- mergeron/examples/safeharbor_boundaries_for_mergers_with_asymmetric_shares.py +35 -28
- mergeron/examples/safeharbor_boundaries_for_symmetric_firm_mergers.py +6 -13
- mergeron/examples/sound_guppi_safeharbor.py +23 -18
- mergeron/examples/testIntrinsicClearanceRates.py +5 -5
- mergeron/examples/visualize_empirical_margin_distribution.py +1 -1
- mergeron/examples/{visualize_guidelines_tests_scatterplots.py → visualize_guidelines_tests.py} +42 -48
- mergeron/ext/__init__.py +1 -1
- mergeron/gen/__init__.py +1 -1
- mergeron/gen/data_generation.py +25 -24
- mergeron/gen/guidelines_tests.py +47 -47
- mergeron/gen/investigations_stats.py +98 -46
- mergeron/jinja_LaTex_templates/clrrate_cis_summary_table_template.tex.jinja2 +2 -3
- mergeron/jinja_LaTex_templates/ftcinvdata_byhhianddelta_table_template.tex.jinja2 +2 -3
- mergeron/jinja_LaTex_templates/ftcinvdata_summary_table_template.tex.jinja2 +1 -2
- mergeron/jinja_LaTex_templates/ftcinvdata_summarypaired_table_template.tex.jinja2 +13 -14
- mergeron/jinja_LaTex_templates/mergeron.cls +161 -0
- mergeron/jinja_LaTex_templates/mergeron_table_collection_template.tex.jinja2 +90 -0
- {mergeron-2024.738930.0.dist-info → mergeron-2024.738936.2.dist-info}/METADATA +15 -18
- mergeron-2024.738936.2.dist-info/RECORD +41 -0
- mergeron-2024.738930.0.dist-info/RECORD +0 -39
- {mergeron-2024.738930.0.dist-info → mergeron-2024.738936.2.dist-info}/WHEEL +0 -0
mergeron/core/__init__.py
CHANGED
|
@@ -12,12 +12,12 @@ from .. import _PKG_NAME # noqa: TID252
|
|
|
12
12
|
__version__ = version(_PKG_NAME)
|
|
13
13
|
|
|
14
14
|
import decimal
|
|
15
|
-
from dataclasses import dataclass
|
|
16
15
|
from typing import Any, Literal, TypeAlias
|
|
17
16
|
|
|
18
17
|
import numpy as np
|
|
19
18
|
from mpmath import mp, mpf # type: ignore
|
|
20
19
|
from numpy.typing import NDArray
|
|
20
|
+
from scipy.spatial.distance import minkowski as distance_function
|
|
21
21
|
|
|
22
22
|
mp.prec = 80
|
|
23
23
|
mp.trap_complex = True
|
|
@@ -26,7 +26,6 @@ HMGPubYear: TypeAlias = Literal[1992, 2010, 2023]
|
|
|
26
26
|
GuidelinesSTD = namedtuple("GuidelinesSTD", "delta rec guppi divr cmcr ipr")
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
@dataclass
|
|
30
29
|
class GuidelinesStandards:
|
|
31
30
|
"""
|
|
32
31
|
Guidelines standards by Guidelines publication year
|
|
@@ -170,7 +169,7 @@ def lerp(
|
|
|
170
169
|
/,
|
|
171
170
|
) -> float | mpf | NDArray[np.float64]:
|
|
172
171
|
"""
|
|
173
|
-
From the
|
|
172
|
+
From the function of the same name in the C++ standard [2]_
|
|
174
173
|
|
|
175
174
|
Constructs the weighted average, :math:`w_1 x_1 + w_2 x_2`, where
|
|
176
175
|
:math:`w_1 = 1 - r` and :math:`w_2 = r`.
|
|
@@ -180,21 +179,35 @@ def lerp(
|
|
|
180
179
|
_x1, _x2
|
|
181
180
|
bounds :math:`x_1, x_2` to interpolate between.
|
|
182
181
|
_r
|
|
183
|
-
interpolation weight :math:`r` assigned to :math:`x_2
|
|
182
|
+
interpolation weight :math:`r` assigned to :math:`x_2`
|
|
184
183
|
|
|
185
184
|
Returns
|
|
186
185
|
-------
|
|
187
|
-
The linear interpolation, or weighted average,
|
|
186
|
+
The linear interpolation, or weighted average,
|
|
187
|
+
:math:`x_1 + r \\cdot (x_1 - x_2) \\equiv (1 - r) \\cdot x_1 + r \\cdot x_2`.
|
|
188
188
|
|
|
189
189
|
Raises
|
|
190
190
|
------
|
|
191
191
|
ValueError
|
|
192
192
|
If the interpolation weight is not in the interval, :math:`[0, 1]`.
|
|
193
193
|
|
|
194
|
+
References
|
|
195
|
+
----------
|
|
196
|
+
|
|
197
|
+
.. [2] C++ Reference, https://en.cppreference.com/w/cpp/numeric/lerp
|
|
198
|
+
|
|
194
199
|
"""
|
|
200
|
+
|
|
195
201
|
if not 0 <= _r <= 1:
|
|
196
202
|
raise ValueError("Specified interpolation weight must lie in [0, 1].")
|
|
197
|
-
|
|
203
|
+
elif _r == 0:
|
|
204
|
+
return _x1
|
|
205
|
+
elif _r == 1:
|
|
206
|
+
return _x2
|
|
207
|
+
elif _r == 0.5:
|
|
208
|
+
return 1 / 2 * (_x1 + _x2)
|
|
209
|
+
else:
|
|
210
|
+
return _r * _x2 + (1 - _r) * _x1
|
|
198
211
|
|
|
199
212
|
|
|
200
213
|
def gbd_from_dsf(
|
|
@@ -337,9 +350,7 @@ def boundary_plot(*, mktshares_plot_flag: bool = True) -> tuple[Any, ...]:
|
|
|
337
350
|
_fig = plt.figure(figsize=(5, 5), dpi=600)
|
|
338
351
|
_ax_out = _fig.add_subplot()
|
|
339
352
|
|
|
340
|
-
def _set_axis_def(
|
|
341
|
-
_ax1: mpa._axes.Axes, /, mktshares_plot_flag: bool = False
|
|
342
|
-
) -> mpa.Axes:
|
|
353
|
+
def _set_axis_def(_ax1: mpa.Axes, /, mktshares_plot_flag: bool = False) -> mpa.Axes:
|
|
343
354
|
# Set the width of axis gridlines, and tick marks:
|
|
344
355
|
# both axes, both major and minor ticks
|
|
345
356
|
# Frame, grid, and facecolor
|
|
@@ -556,7 +567,7 @@ def delta_hhi_boundary(
|
|
|
556
567
|
|
|
557
568
|
|
|
558
569
|
def combined_share_boundary(
|
|
559
|
-
|
|
570
|
+
_s_intcpt: float = 0.0625, /, *, bdry_dps: int = 10
|
|
560
571
|
) -> tuple[NDArray[np.float64], float]:
|
|
561
572
|
"""
|
|
562
573
|
Share combinations on the merging-firms' combined share boundary.
|
|
@@ -567,7 +578,7 @@ def combined_share_boundary(
|
|
|
567
578
|
|
|
568
579
|
Parameters
|
|
569
580
|
----------
|
|
570
|
-
|
|
581
|
+
_s_intcpt:
|
|
571
582
|
Merging-firms' combined share.
|
|
572
583
|
bdry_dps
|
|
573
584
|
Number of decimal places for rounding reported shares.
|
|
@@ -577,16 +588,16 @@ def combined_share_boundary(
|
|
|
577
588
|
Array of share-pairs, area under boundary.
|
|
578
589
|
|
|
579
590
|
"""
|
|
580
|
-
|
|
581
|
-
_s_mid =
|
|
591
|
+
_s_intcpt = mpf(f"{_s_intcpt}")
|
|
592
|
+
_s_mid = _s_intcpt / 2
|
|
582
593
|
|
|
583
|
-
_s1_pts = (0, _s_mid,
|
|
594
|
+
_s1_pts = (0, _s_mid, _s_intcpt)
|
|
584
595
|
return (
|
|
585
596
|
np.column_stack((
|
|
586
597
|
np.array(_s1_pts, np.float64),
|
|
587
598
|
np.array(_s1_pts[::-1], np.float64),
|
|
588
599
|
)),
|
|
589
|
-
round(float(
|
|
600
|
+
round(float(_s_intcpt * _s_mid), bdry_dps),
|
|
590
601
|
)
|
|
591
602
|
|
|
592
603
|
|
|
@@ -654,17 +665,17 @@ def shrratio_mgnsym_boundary_max(
|
|
|
654
665
|
# of function call with other shrratio_mgnsym_boundary functions
|
|
655
666
|
del _r_val
|
|
656
667
|
_delta_star = mpf(f"{_delta_star}")
|
|
657
|
-
|
|
668
|
+
_s_intcpt = _delta_star
|
|
658
669
|
_s_mid = _delta_star / (1 + _delta_star)
|
|
659
670
|
|
|
660
|
-
_s1_pts = (0, _s_mid,
|
|
671
|
+
_s1_pts = (0, _s_mid, _s_intcpt)
|
|
661
672
|
|
|
662
673
|
return (
|
|
663
674
|
np.column_stack((
|
|
664
675
|
np.array(_s1_pts, np.float64),
|
|
665
676
|
np.array(_s1_pts[::-1], np.float64),
|
|
666
677
|
)),
|
|
667
|
-
round(float(
|
|
678
|
+
round(float(_s_intcpt * _s_mid), gbd_dps), # simplified calculation
|
|
668
679
|
)
|
|
669
680
|
|
|
670
681
|
|
|
@@ -714,7 +725,7 @@ def shrratio_mgnsym_boundary_min(
|
|
|
714
725
|
)
|
|
715
726
|
|
|
716
727
|
_delta_star = mpf(f"{_delta_star}")
|
|
717
|
-
|
|
728
|
+
_s_intcpt = mpf("1.00")
|
|
718
729
|
_s_mid = _delta_star / (1 + _delta_star)
|
|
719
730
|
|
|
720
731
|
if recapture_spec == "inside-out":
|
|
@@ -729,14 +740,14 @@ def shrratio_mgnsym_boundary_min(
|
|
|
729
740
|
_smin_nr / _guppi_bdry_env_dr,
|
|
730
741
|
_s_mid,
|
|
731
742
|
_smax_nr / _guppi_bdry_env_dr,
|
|
732
|
-
|
|
743
|
+
_s_intcpt,
|
|
733
744
|
),
|
|
734
745
|
np.float64,
|
|
735
746
|
)
|
|
736
747
|
|
|
737
748
|
_gbd_area = (_smin_nr + (_smax_nr - _smin_nr) * _s_mid) / _guppi_bdry_env_dr
|
|
738
749
|
else:
|
|
739
|
-
_s1_pts, _gbd_area = np.array((0, _s_mid,
|
|
750
|
+
_s1_pts, _gbd_area = np.array((0, _s_mid, _s_intcpt), np.float64), _s_mid
|
|
740
751
|
|
|
741
752
|
return np.column_stack((_s1_pts, _s1_pts[::-1])), round(float(_gbd_area), gbd_dps)
|
|
742
753
|
|
|
@@ -747,7 +758,7 @@ def shrratio_mgnsym_boundary_wtd_avg(
|
|
|
747
758
|
/,
|
|
748
759
|
*,
|
|
749
760
|
avg_method: Literal["arithmetic", "geometric", "distance"] = "arithmetic",
|
|
750
|
-
wgtng_policy: Literal["own-share", "cross-product-share"] = "own-share",
|
|
761
|
+
wgtng_policy: Literal["own-share", "cross-product-share"] | None = "own-share",
|
|
751
762
|
recapture_spec: Literal["inside-out", "proportional"] = "inside-out",
|
|
752
763
|
gbd_dps: int = 5,
|
|
753
764
|
) -> tuple[NDArray[np.float64], float]:
|
|
@@ -819,9 +830,9 @@ def shrratio_mgnsym_boundary_wtd_avg(
|
|
|
819
830
|
Parameters
|
|
820
831
|
----------
|
|
821
832
|
_delta_star
|
|
822
|
-
GUPPI bound
|
|
833
|
+
corollary to GUPPI bound (:math:`\\overline{g} / (m^* \\cdot \\overline{r})`)
|
|
823
834
|
_r_val
|
|
824
|
-
|
|
835
|
+
recapture ratio
|
|
825
836
|
avg_method
|
|
826
837
|
Whether "arithmetic", "geometric", or "distance".
|
|
827
838
|
wgtng_policy
|
|
@@ -840,17 +851,18 @@ def shrratio_mgnsym_boundary_wtd_avg(
|
|
|
840
851
|
|
|
841
852
|
if _delta_star > 1:
|
|
842
853
|
raise ValueError(
|
|
843
|
-
"
|
|
844
|
-
"Margin-adjusted benchmark share ratio cannot exceed 1."
|
|
854
|
+
"Margin-adjusted benchmark share ratio, `_delta_star` cannot exceed 1."
|
|
845
855
|
)
|
|
846
856
|
|
|
847
857
|
_delta_star = mpf(f"{_delta_star}")
|
|
848
858
|
_s_mid = _delta_star / (1 + _delta_star)
|
|
849
859
|
|
|
860
|
+
# initial conditions
|
|
850
861
|
_gbdry_points = [(_s_mid, _s_mid)]
|
|
851
862
|
_s_1_pre, _s_2_pre = _s_mid, _s_mid
|
|
852
863
|
_s_2_oddval, _s_2_oddsum, _s_2_evnsum = True, 0, 0
|
|
853
864
|
|
|
865
|
+
# parameters for iteration
|
|
854
866
|
_gbd_step_sz = mp.power(10, -gbd_dps)
|
|
855
867
|
_theta = _gbd_step_sz * (10 if wgtng_policy == "cross-product-share" else 1)
|
|
856
868
|
for _s_1 in mp.arange(_s_mid - _gbd_step_sz, 0, -_gbd_step_sz):
|
|
@@ -871,8 +883,12 @@ def shrratio_mgnsym_boundary_wtd_avg(
|
|
|
871
883
|
else _s_1 / (1 - _s_2)
|
|
872
884
|
)
|
|
873
885
|
|
|
874
|
-
_r =
|
|
875
|
-
|
|
886
|
+
_r = (
|
|
887
|
+
mp.fdiv(
|
|
888
|
+
_s_1 if wgtng_policy == "cross-product-share" else _s_2, _s_1 + _s_2
|
|
889
|
+
)
|
|
890
|
+
if wgtng_policy
|
|
891
|
+
else 0.5
|
|
876
892
|
)
|
|
877
893
|
|
|
878
894
|
match avg_method:
|
|
@@ -884,12 +900,12 @@ def shrratio_mgnsym_boundary_wtd_avg(
|
|
|
884
900
|
_delta_test = lerp(_de_1, _de_2, _r)
|
|
885
901
|
|
|
886
902
|
if wgtng_policy == "cross-product-share":
|
|
887
|
-
|
|
903
|
+
_test_flag, _incr_decr = (_delta_test > _delta_star, -1)
|
|
888
904
|
else:
|
|
889
|
-
|
|
905
|
+
_test_flag, _incr_decr = (_delta_test < _delta_star, 1)
|
|
890
906
|
|
|
891
|
-
if
|
|
892
|
-
_s_2 +=
|
|
907
|
+
if _test_flag:
|
|
908
|
+
_s_2 += _incr_decr * _gbd_step_sz
|
|
893
909
|
else:
|
|
894
910
|
break
|
|
895
911
|
|
|
@@ -917,13 +933,19 @@ def shrratio_mgnsym_boundary_wtd_avg(
|
|
|
917
933
|
# Area under boundary
|
|
918
934
|
_gbdry_area_total = 2 * _gbd_prtlarea - mp.power(_s_mid, 2)
|
|
919
935
|
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
936
|
+
match wgtng_policy:
|
|
937
|
+
case "cross-product-share":
|
|
938
|
+
_s_intcpt = _delta_star
|
|
939
|
+
case "own-product-share":
|
|
940
|
+
_s_intcpt = mpf("1.0")
|
|
941
|
+
case None if avg_method == "distance":
|
|
942
|
+
_s_intcpt = _delta_star * mp.sqrt("2")
|
|
943
|
+
case _:
|
|
944
|
+
_s_intcpt = _s_2_pre
|
|
945
|
+
|
|
946
|
+
_gbdry_points = np.row_stack((_gbdry_points, (mpf("0.0"), _s_intcpt))).astype(
|
|
947
|
+
np.float64
|
|
948
|
+
)
|
|
927
949
|
# Points defining boundary to point-of-symmetry
|
|
928
950
|
return (
|
|
929
951
|
np.row_stack((np.flip(_gbdry_points, 0), np.flip(_gbdry_points[1:], 1))),
|
|
@@ -1010,13 +1032,13 @@ def shrratio_mgnsym_boundary_xact_avg(
|
|
|
1010
1032
|
_gbdry_points_start = np.array([(_s_mid, _s_mid)])
|
|
1011
1033
|
_s_1 = np.array(mp.arange(_s_mid - _gbd_step_sz, 0, -_gbd_step_sz), np.float64)
|
|
1012
1034
|
if recapture_spec == "inside-out":
|
|
1013
|
-
|
|
1035
|
+
_s_intcpt = mp.fdiv(
|
|
1014
1036
|
mp.fsub(
|
|
1015
1037
|
2 * _delta_star * _r_val + 1, mp.fabs(2 * _delta_star * _r_val - 1)
|
|
1016
1038
|
),
|
|
1017
1039
|
2 * mpf(f"{_r_val}"),
|
|
1018
1040
|
)
|
|
1019
|
-
_nr_t1 = 1 + 2 * _delta_star * _r_val * (1 - _s_1) - _s_1 * (1 - _r_val)
|
|
1041
|
+
_nr_t1 = 1 + 2 * _delta_star * _r_val * (1 - _s_1) - _s_1 * (1 - _r_val) # type: ignore
|
|
1020
1042
|
|
|
1021
1043
|
_nr_sqrt_mdr = 4 * _delta_star * _r_val
|
|
1022
1044
|
_nr_sqrt_mdr2 = _nr_sqrt_mdr * _r_val
|
|
@@ -1041,9 +1063,9 @@ def shrratio_mgnsym_boundary_xact_avg(
|
|
|
1041
1063
|
|
|
1042
1064
|
_nr_t2_s1 = _nr_sqrt_s1sq + _nr_sqrt_s1 + _nr_sqrt_nos1
|
|
1043
1065
|
|
|
1044
|
-
if not np.isclose(
|
|
1045
|
-
np.einsum("i->", _nr_t2_mdr.astype(np.float64)), # from mpf to float64
|
|
1046
|
-
np.einsum("i->", _nr_t2_s1.astype(np.float64)),
|
|
1066
|
+
if not np.isclose( # type: ignore
|
|
1067
|
+
np.einsum("i->", _nr_t2_mdr.astype(np.float64)), # type: ignore from mpf to float64
|
|
1068
|
+
np.einsum("i->", _nr_t2_s1.astype(np.float64)), # type: ignore from mpf to float64
|
|
1047
1069
|
rtol=0,
|
|
1048
1070
|
atol=0.5 * gbd_dps,
|
|
1049
1071
|
):
|
|
@@ -1055,7 +1077,7 @@ def shrratio_mgnsym_boundary_xact_avg(
|
|
|
1055
1077
|
_s_2 = (_nr_t1 - np.sqrt(_nr_t2_s1)) / (2 * _r_val)
|
|
1056
1078
|
|
|
1057
1079
|
else:
|
|
1058
|
-
|
|
1080
|
+
_s_intcpt = mp.fsub(_delta_star + 1 / 2, mp.fabs(_delta_star - 1 / 2))
|
|
1059
1081
|
_s_2 = (
|
|
1060
1082
|
(1 / 2)
|
|
1061
1083
|
+ _delta_star
|
|
@@ -1070,7 +1092,7 @@ def shrratio_mgnsym_boundary_xact_avg(
|
|
|
1070
1092
|
)
|
|
1071
1093
|
|
|
1072
1094
|
_gbdry_points_inner = np.column_stack((_s_1, _s_2))
|
|
1073
|
-
_gbdry_points_end = np.array([(mpf("0.0"),
|
|
1095
|
+
_gbdry_points_end = np.array([(mpf("0.0"), _s_intcpt)], np.float64)
|
|
1074
1096
|
|
|
1075
1097
|
_gbdry_points = np.row_stack((
|
|
1076
1098
|
_gbdry_points_end,
|
|
@@ -1112,6 +1134,8 @@ def shrratio_mgnsym_boundary_avg(
|
|
|
1112
1134
|
Share combinations along the average GUPPI boundary, with
|
|
1113
1135
|
symmetric merging-firm margins.
|
|
1114
1136
|
|
|
1137
|
+
Faster than calling shrratio_mgnsym_boundary_avg(wgtng_policy=None).
|
|
1138
|
+
|
|
1115
1139
|
Parameters
|
|
1116
1140
|
----------
|
|
1117
1141
|
_delta_star
|
|
@@ -1119,7 +1143,7 @@ def shrratio_mgnsym_boundary_avg(
|
|
|
1119
1143
|
_r_val
|
|
1120
1144
|
Recapture ratio.
|
|
1121
1145
|
avg_method
|
|
1122
|
-
Whether "arithmetic", "geometric", or "
|
|
1146
|
+
Whether "arithmetic", "geometric", or "distance".
|
|
1123
1147
|
recapture_spec
|
|
1124
1148
|
Whether recapture-ratio is MNL-consistent ("inside-out") or has fixed
|
|
1125
1149
|
value for both merging firms ("proportional").
|
|
@@ -1138,7 +1162,7 @@ def shrratio_mgnsym_boundary_avg(
|
|
|
1138
1162
|
"Margin-adjusted benchmark share ratio cannot exceed 1."
|
|
1139
1163
|
)
|
|
1140
1164
|
|
|
1141
|
-
if avg_method not in (_avgmthds := ("arithmetic", "geometric", "
|
|
1165
|
+
if avg_method not in (_avgmthds := ("arithmetic", "geometric", "distance")):
|
|
1142
1166
|
raise ValueError(
|
|
1143
1167
|
f"Averarging method, {f'"{avg_method}"'} is invalid. "
|
|
1144
1168
|
f"Must be one of, {_avgmthds!r}."
|
|
@@ -1152,18 +1176,21 @@ def shrratio_mgnsym_boundary_avg(
|
|
|
1152
1176
|
|
|
1153
1177
|
_delta_star = mpf(f"{_delta_star}")
|
|
1154
1178
|
_s_mid = _delta_star / (1 + _delta_star)
|
|
1155
|
-
_gbd_step_sz = mp.power(10, -gbd_dps)
|
|
1156
1179
|
|
|
1180
|
+
# initial conditions
|
|
1157
1181
|
_s_2 = _s_mid
|
|
1158
1182
|
_s_2_oddval = True
|
|
1159
1183
|
_s_2_oddsum = 0
|
|
1160
1184
|
_s_2_evnsum = 0
|
|
1161
1185
|
_gbdry_points = [(_s_mid, _s_mid)]
|
|
1186
|
+
|
|
1187
|
+
# parameters for iteration
|
|
1188
|
+
_gbd_step_sz = mp.power(10, -gbd_dps)
|
|
1162
1189
|
for _s_1 in mp.arange(_s_mid, 0, -_gbd_step_sz):
|
|
1163
1190
|
_s_1 -= _gbd_step_sz
|
|
1164
1191
|
while True:
|
|
1165
|
-
|
|
1166
|
-
|
|
1192
|
+
_delta_12 = _s_2 / (1 - _s_1)
|
|
1193
|
+
_delta_21 = (
|
|
1167
1194
|
_s_1 / (1 - _s_2)
|
|
1168
1195
|
if recapture_spec == "proportional"
|
|
1169
1196
|
else _s_1 / (1 - lerp(_s_1, _s_2, _r_val))
|
|
@@ -1171,13 +1198,21 @@ def shrratio_mgnsym_boundary_avg(
|
|
|
1171
1198
|
|
|
1172
1199
|
match avg_method:
|
|
1173
1200
|
case "geometric":
|
|
1174
|
-
|
|
1201
|
+
_delta_test = mp.sqrt(_delta_12 * _delta_21)
|
|
1175
1202
|
case "distance":
|
|
1176
|
-
|
|
1203
|
+
# _delta_test = mp.sqrt(mp.fdiv((_delta_12**2 + _delta_21**2), "2"))
|
|
1204
|
+
_delta_test = mp.sqrt(
|
|
1205
|
+
mp.fdiv(
|
|
1206
|
+
mp.fsum(
|
|
1207
|
+
mp.power(f"{_g}", "2") for _g in (_delta_12, _delta_21)
|
|
1208
|
+
),
|
|
1209
|
+
"2",
|
|
1210
|
+
)
|
|
1211
|
+
)
|
|
1177
1212
|
case _:
|
|
1178
|
-
|
|
1213
|
+
_delta_test = mp.fdiv(_delta_12 + _delta_21, "2")
|
|
1179
1214
|
|
|
1180
|
-
if
|
|
1215
|
+
if _delta_test < _delta_star:
|
|
1181
1216
|
_s_2 += _gbd_step_sz
|
|
1182
1217
|
else:
|
|
1183
1218
|
break
|
|
@@ -1190,12 +1225,12 @@ def shrratio_mgnsym_boundary_avg(
|
|
|
1190
1225
|
|
|
1191
1226
|
# Starting at _s_id - _gbd_step_sz means _s_1 is not always
|
|
1192
1227
|
# an even multiple of _gbd_step_sz
|
|
1193
|
-
|
|
1228
|
+
_s_intcpt = _s_2
|
|
1194
1229
|
|
|
1195
1230
|
_gbd_prtlarea = 2 * _gbd_step_sz * (
|
|
1196
1231
|
mp.fmul(4 / 3, _s_2_oddsum)
|
|
1197
1232
|
+ mp.fmul(2 / 3, _s_2_evnsum)
|
|
1198
|
-
+ mp.fmul(1 / 3, _s_mid +
|
|
1233
|
+
+ mp.fmul(1 / 3, _s_mid + _s_intcpt)
|
|
1199
1234
|
) - mp.power(_s_mid, 2)
|
|
1200
1235
|
|
|
1201
1236
|
_gbdry_points = np.array(_gbdry_points, np.float64)
|
|
@@ -1203,3 +1238,158 @@ def shrratio_mgnsym_boundary_avg(
|
|
|
1203
1238
|
np.row_stack((np.flip(_gbdry_points, 0), np.flip(_gbdry_points[1:], 1))),
|
|
1204
1239
|
round(float(_gbd_prtlarea), gbd_dps),
|
|
1205
1240
|
)
|
|
1241
|
+
|
|
1242
|
+
|
|
1243
|
+
def shrratio_mgnsym_boundary_distance(
|
|
1244
|
+
_delta_star: float = 0.075,
|
|
1245
|
+
_r_val: float = 0.80,
|
|
1246
|
+
/,
|
|
1247
|
+
*,
|
|
1248
|
+
avg_method: Literal["arithmetic", "distance"] = "arithmetic",
|
|
1249
|
+
wgtng_policy: Literal["own-share", "cross-product-share"] | None = "own-share",
|
|
1250
|
+
recapture_spec: Literal["inside-out", "proportional"] = "inside-out",
|
|
1251
|
+
gbd_dps: int = 5,
|
|
1252
|
+
) -> tuple[NDArray[np.float64], float]:
|
|
1253
|
+
"""
|
|
1254
|
+
Share combinations for the GUPPI boundaries using various aggregators with symmetric merging-firm margins.
|
|
1255
|
+
|
|
1256
|
+
Parameters
|
|
1257
|
+
----------
|
|
1258
|
+
_delta_star
|
|
1259
|
+
corollary to GUPPI bound (:math:`\\overline{g} / (m^* \\cdot \\overline{r})`)
|
|
1260
|
+
_r_val
|
|
1261
|
+
recapture ratio
|
|
1262
|
+
avg_method
|
|
1263
|
+
Whether "arithmetic", "geometric", or "distance".
|
|
1264
|
+
wgtng_policy
|
|
1265
|
+
Whether "own-share" or "cross-product-share".
|
|
1266
|
+
recapture_spec
|
|
1267
|
+
Whether recapture-ratio is MNL-consistent ("inside-out") or has fixed
|
|
1268
|
+
value for both merging firms ("proportional").
|
|
1269
|
+
gbd_dps
|
|
1270
|
+
Number of decimal places for rounding returned shares and area.
|
|
1271
|
+
|
|
1272
|
+
Returns
|
|
1273
|
+
-------
|
|
1274
|
+
Array of share-pairs, area under boundary.
|
|
1275
|
+
|
|
1276
|
+
"""
|
|
1277
|
+
|
|
1278
|
+
if _delta_star > 1:
|
|
1279
|
+
raise ValueError(
|
|
1280
|
+
"Margin-adjusted benchmark share ratio, `_delta_star` cannot exceed 1."
|
|
1281
|
+
)
|
|
1282
|
+
|
|
1283
|
+
_delta_star = mpf(f"{_delta_star}")
|
|
1284
|
+
_s_mid = _delta_star / (1 + _delta_star)
|
|
1285
|
+
|
|
1286
|
+
# initial conditions
|
|
1287
|
+
_gbdry_points = [(_s_mid, _s_mid)]
|
|
1288
|
+
_s_1_pre, _s_2_pre = _s_mid, _s_mid
|
|
1289
|
+
_s_2_oddval, _s_2_oddsum, _s_2_evnsum = True, 0, 0
|
|
1290
|
+
|
|
1291
|
+
# parameters for iteration
|
|
1292
|
+
_weights_base = (mpf("0.5"),) * 2
|
|
1293
|
+
_gbd_step_sz = mp.power(10, -gbd_dps)
|
|
1294
|
+
_theta = _gbd_step_sz * (10 if wgtng_policy == "cross-product-share" else 1)
|
|
1295
|
+
for _s_1 in mp.arange(_s_mid - _gbd_step_sz, 0, -_gbd_step_sz):
|
|
1296
|
+
# The wtd. avg. GUPPI is not always convex to the origin, so we
|
|
1297
|
+
# increment _s_2 after each iteration in which our algorithm
|
|
1298
|
+
# finds (s1, s2) on the boundary
|
|
1299
|
+
_s_2 = _s_2_pre * (1 + _theta)
|
|
1300
|
+
|
|
1301
|
+
if (_s_1 + _s_2) > mpf("0.99875"):
|
|
1302
|
+
# 1: # We lose accuracy at 3-9s and up
|
|
1303
|
+
break
|
|
1304
|
+
|
|
1305
|
+
while True:
|
|
1306
|
+
_de_1 = _s_2 / (1 - _s_1)
|
|
1307
|
+
_de_2 = (
|
|
1308
|
+
_s_1 / (1 - lerp(_s_1, _s_2, _r_val))
|
|
1309
|
+
if recapture_spec == "inside-out"
|
|
1310
|
+
else _s_1 / (1 - _s_2)
|
|
1311
|
+
)
|
|
1312
|
+
|
|
1313
|
+
_weights_i = (
|
|
1314
|
+
(
|
|
1315
|
+
_w1 := mp.fdiv(
|
|
1316
|
+
_s_2 if wgtng_policy == "cross-product-share" else _s_1,
|
|
1317
|
+
_s_1 + _s_2,
|
|
1318
|
+
),
|
|
1319
|
+
1 - _w1,
|
|
1320
|
+
)
|
|
1321
|
+
if wgtng_policy
|
|
1322
|
+
else _weights_base
|
|
1323
|
+
)
|
|
1324
|
+
|
|
1325
|
+
match avg_method:
|
|
1326
|
+
case "arithmetic":
|
|
1327
|
+
_delta_test = distance_function(
|
|
1328
|
+
(_de_1, _de_2), (0.0, 0.0), p=1, w=_weights_i
|
|
1329
|
+
)
|
|
1330
|
+
case "distance":
|
|
1331
|
+
_delta_test = distance_function(
|
|
1332
|
+
(_de_1, _de_2), (0.0, 0.0), p=2, w=_weights_i
|
|
1333
|
+
)
|
|
1334
|
+
|
|
1335
|
+
if wgtng_policy == "cross-product-share":
|
|
1336
|
+
_test_flag, _incr_decr = (_delta_test > _delta_star, -1)
|
|
1337
|
+
else:
|
|
1338
|
+
_test_flag, _incr_decr = (_delta_test < _delta_star, 1)
|
|
1339
|
+
|
|
1340
|
+
if _test_flag:
|
|
1341
|
+
_s_2 += _incr_decr * _gbd_step_sz
|
|
1342
|
+
else:
|
|
1343
|
+
break
|
|
1344
|
+
|
|
1345
|
+
# Build-up boundary points
|
|
1346
|
+
_gbdry_points.append((_s_1, _s_2))
|
|
1347
|
+
|
|
1348
|
+
# Build up area terms
|
|
1349
|
+
_s_2_oddsum += _s_2 if _s_2_oddval else 0
|
|
1350
|
+
_s_2_evnsum += _s_2 if not _s_2_oddval else 0
|
|
1351
|
+
_s_2_oddval = not _s_2_oddval
|
|
1352
|
+
|
|
1353
|
+
# Hold share points
|
|
1354
|
+
_s_2_pre = _s_2
|
|
1355
|
+
_s_1_pre = _s_1
|
|
1356
|
+
|
|
1357
|
+
_gbd_prtlarea = _gbd_step_sz * (
|
|
1358
|
+
(4 * _s_2_oddsum + 2 * _s_2_evnsum + _s_mid + _delta_star) / 3
|
|
1359
|
+
if wgtng_policy == "cross-product-share"
|
|
1360
|
+
else (
|
|
1361
|
+
(4 * _s_2_oddsum + 2 * _s_2_evnsum + _s_mid + _s_2_pre) / 3
|
|
1362
|
+
+ _s_1_pre * (1 + _s_2_pre) / 2
|
|
1363
|
+
)
|
|
1364
|
+
)
|
|
1365
|
+
|
|
1366
|
+
# Area under boundary
|
|
1367
|
+
_gbdry_area_total = 2 * _gbd_prtlarea - mp.power(_s_mid, 2)
|
|
1368
|
+
|
|
1369
|
+
match wgtng_policy:
|
|
1370
|
+
case "cross-product-share":
|
|
1371
|
+
_s_intcpt = _delta_star
|
|
1372
|
+
case "own-product-share":
|
|
1373
|
+
_s_intcpt = mpf("1.0")
|
|
1374
|
+
case None if avg_method == "distance":
|
|
1375
|
+
_s_intcpt = _delta_star * mp.sqrt("2")
|
|
1376
|
+
case None if avg_method == "arithmetic" and recapture_spec == "inside-out":
|
|
1377
|
+
_s_intcpt = mp.fdiv(
|
|
1378
|
+
mp.fsub(
|
|
1379
|
+
2 * _delta_star * _r_val + 1, mp.fabs(2 * _delta_star * _r_val - 1)
|
|
1380
|
+
),
|
|
1381
|
+
2 * mpf(f"{_r_val}"),
|
|
1382
|
+
)
|
|
1383
|
+
case None if avg_method == "arithmetic":
|
|
1384
|
+
_s_intcpt = mp.fsub(_delta_star + 1 / 2, mp.fabs(_delta_star - 1 / 2))
|
|
1385
|
+
case _:
|
|
1386
|
+
_s_intcpt = _s_2_pre
|
|
1387
|
+
|
|
1388
|
+
_gbdry_points = np.row_stack((_gbdry_points, (mpf("0.0"), _s_intcpt))).astype(
|
|
1389
|
+
np.float64
|
|
1390
|
+
)
|
|
1391
|
+
# Points defining boundary to point-of-symmetry
|
|
1392
|
+
return (
|
|
1393
|
+
np.row_stack((np.flip(_gbdry_points, 0), np.flip(_gbdry_points[1:], 1))),
|
|
1394
|
+
round(float(_gbdry_area_total), gbd_dps),
|
|
1395
|
+
)
|
|
@@ -21,12 +21,12 @@ from numpy.typing import NBitBase, NDArray
|
|
|
21
21
|
from scipy.optimize import OptimizeResult, root # type: ignore
|
|
22
22
|
from scipy.stats import beta, chi2, norm # type: ignore
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
TI = TypeVar("TI", bound=NBitBase)
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def propn_ci(
|
|
28
|
-
_npos: NDArray[np.integer[
|
|
29
|
-
_nobs: NDArray[np.integer[
|
|
28
|
+
_npos: NDArray[np.integer[TI]] | int = 4,
|
|
29
|
+
_nobs: NDArray[np.integer[TI]] | int = 10,
|
|
30
30
|
/,
|
|
31
31
|
*,
|
|
32
32
|
alpha: float = 0.05,
|
|
@@ -41,7 +41,7 @@ def propn_ci(
|
|
|
41
41
|
]:
|
|
42
42
|
"""Returns point estimates and confidence interval for a proportion
|
|
43
43
|
|
|
44
|
-
Methods "Clopper-Pearson" and "Exact" are synoymous [
|
|
44
|
+
Methods "Clopper-Pearson" and "Exact" are synoymous [3]_. Similarly,
|
|
45
45
|
"Wilson" and "Score" are synonyms here.
|
|
46
46
|
|
|
47
47
|
Parameters
|
|
@@ -66,7 +66,7 @@ def propn_ci(
|
|
|
66
66
|
References
|
|
67
67
|
----------
|
|
68
68
|
|
|
69
|
-
.. [
|
|
69
|
+
.. [3] Alan Agresti & Brent A. Coull (1998) Approximate is Better
|
|
70
70
|
than “Exact” for Interval Estimation of Binomial Proportions,
|
|
71
71
|
The American Statistician, 52:2, 119-126,
|
|
72
72
|
https://doi.org/10.1080/00031305.1998.10480550
|
|
@@ -74,9 +74,9 @@ def propn_ci(
|
|
|
74
74
|
"""
|
|
75
75
|
|
|
76
76
|
for _f in _npos, _nobs:
|
|
77
|
-
if not isinstance(_f,
|
|
77
|
+
if not isinstance(_f, int | np.integer):
|
|
78
78
|
raise ValueError(
|
|
79
|
-
f"Count, {_f!r} must have type that is a subtype of np.
|
|
79
|
+
f"Count, {_f!r} must have type that is a subtype of np.integer."
|
|
80
80
|
)
|
|
81
81
|
|
|
82
82
|
if not _nobs:
|
|
@@ -131,7 +131,7 @@ def propn_ci(
|
|
|
131
131
|
|
|
132
132
|
|
|
133
133
|
def propn_ci_multinomial(
|
|
134
|
-
_counts: NDArray[np.integer[
|
|
134
|
+
_counts: NDArray[np.integer[TI]],
|
|
135
135
|
/,
|
|
136
136
|
*,
|
|
137
137
|
alpha: float = 0.05,
|
|
@@ -200,9 +200,9 @@ def propn_diff_ci(
|
|
|
200
200
|
) -> tuple[float, float, float, float]:
|
|
201
201
|
R"""Confidence intervals for differences in binomial proportions.
|
|
202
202
|
|
|
203
|
-
Methods available are Agresti-Caffo [
|
|
204
|
-
and Newcombe (aka, Score method) [
|
|
205
|
-
R-language function BinomDiffCI, in the module StatsAndCIs [
|
|
203
|
+
Methods available are Agresti-Caffo [4]_, Mee [5]_, Meitinen-Nurminen [5]_ [6]_
|
|
204
|
+
and Newcombe (aka, Score method) [5]_. See also, source code for the
|
|
205
|
+
R-language function BinomDiffCI, in the module StatsAndCIs [7]_.
|
|
206
206
|
|
|
207
207
|
Parameters
|
|
208
208
|
----------
|
|
@@ -222,19 +222,19 @@ def propn_diff_ci(
|
|
|
222
222
|
References
|
|
223
223
|
----------
|
|
224
224
|
|
|
225
|
-
.. [
|
|
225
|
+
.. [4] Agresti, A., & Caffo, T. (2000). Simple and Effective
|
|
226
226
|
Confidence Intervals for Proportions and Differences of Proportions
|
|
227
227
|
Result from Adding Two Successes and Two Failures.
|
|
228
228
|
The American Statistician, 54(4), 280--288. https://doi.org/10.2307/2685779
|
|
229
229
|
|
|
230
|
-
.. [
|
|
230
|
+
.. [5] Newcombe, R.G. (1998). Two-sided confidence intervals for
|
|
231
231
|
the single proportion: comparison of seven methods. Statist. Med., 17: 857-872.
|
|
232
232
|
https://doi.org/10.1002/(SICI)1097-0258(19980430)17:8%3C857::AID-SIM777%3E3.0.CO;2-E
|
|
233
233
|
|
|
234
|
-
.. [
|
|
234
|
+
.. [6] Miettinen, O. and Nurminen, M. (1985). Comparative analysis of two rates.
|
|
235
235
|
Statist. Med., 4: 213-226. https://doi.org/10.1002/sim.4780040211; Appendix I
|
|
236
236
|
|
|
237
|
-
.. [
|
|
237
|
+
.. [7] StatsAndCIs.r, function BinomDiffCI, method, "mn"
|
|
238
238
|
https://github.com/cran/DescTools/blob/master/R/StatsAndCIs.r
|
|
239
239
|
(R source code is distributed under the CC-BY license.)
|
|
240
240
|
|
|
@@ -349,9 +349,9 @@ def _propn_diff_ci_mn(
|
|
|
349
349
|
|
|
350
350
|
"""
|
|
351
351
|
for _f in _npos1, _nobs1, _npos1, _nobs2:
|
|
352
|
-
if not isinstance(_f,
|
|
352
|
+
if not isinstance(_f, int | np.integer):
|
|
353
353
|
raise ValueError(
|
|
354
|
-
f"Count, {_f!r} must have type that is a subtype of np.
|
|
354
|
+
f"Count, {_f!r} must have type that is a subtype of np.integer."
|
|
355
355
|
)
|
|
356
356
|
|
|
357
357
|
_chi_sq_cr = chi2.ppf(1 - alpha, 1)
|
|
@@ -442,7 +442,7 @@ def _propn_diff_chisq_mn(
|
|
|
442
442
|
|
|
443
443
|
|
|
444
444
|
def propn_ci_diff_multinomial(
|
|
445
|
-
_counts: NDArray[np.integer[
|
|
445
|
+
_counts: NDArray[np.integer[TI]], /, *, alpha: float = 0.05
|
|
446
446
|
) -> NDArray[np.float64]:
|
|
447
447
|
"""Estimate confidence intervals of pair-wise differences in multinomial proportions
|
|
448
448
|
|
|
@@ -483,7 +483,7 @@ class MultinomialDiffTest(NamedTuple):
|
|
|
483
483
|
|
|
484
484
|
|
|
485
485
|
def propn_diff_multinomial_chisq(
|
|
486
|
-
_counts: NDArray[np.integer[
|
|
486
|
+
_counts: NDArray[np.integer[TI]], /, *, alpha: float = 0.05
|
|
487
487
|
) -> MultinomialDiffTest:
|
|
488
488
|
"""Chi-square test for homogeneity of differences in multinomial proportions.
|
|
489
489
|
|