mergeron 2025.739319.3__py3-none-any.whl → 2025.739341.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.
- mergeron/__init__.py +21 -23
- mergeron/core/__init__.py +21 -5
- mergeron/core/empirical_margin_distribution.py +216 -160
- mergeron/core/ftc_merger_investigations_data.py +31 -35
- mergeron/core/guidelines_boundaries.py +27 -20
- mergeron/core/guidelines_boundary_functions.py +22 -32
- mergeron/core/guidelines_boundary_functions_extra.py +15 -30
- mergeron/core/pseudorandom_numbers.py +21 -18
- mergeron/data/__init__.py +13 -11
- mergeron/data/damodaran_margin_data_serialized.zip +0 -0
- mergeron/gen/__init__.py +32 -41
- mergeron/gen/data_generation.py +19 -23
- mergeron/gen/data_generation_functions.py +27 -38
- mergeron/gen/enforcement_stats.py +154 -32
- mergeron/gen/upp_tests.py +4 -9
- mergeron-2025.739341.9.dist-info/METADATA +94 -0
- mergeron-2025.739341.9.dist-info/RECORD +20 -0
- {mergeron-2025.739319.3.dist-info → mergeron-2025.739341.9.dist-info}/WHEEL +1 -1
- mergeron/data/damodaran_margin_data.xls +0 -0
- mergeron/demo/__init__.py +0 -3
- mergeron/demo/visualize_empirical_margin_distribution.py +0 -94
- mergeron-2025.739319.3.dist-info/METADATA +0 -174
- mergeron-2025.739319.3.dist-info/RECORD +0 -22
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Non-public functions called in data_generation.py
|
|
3
|
-
"""
|
|
1
|
+
"""Non-public functions called in data_generation.py."""
|
|
4
2
|
|
|
5
3
|
from __future__ import annotations
|
|
6
4
|
|
|
@@ -28,13 +26,13 @@ from ..core.pseudorandom_numbers import ( # noqa: TID252
|
|
|
28
26
|
from . import (
|
|
29
27
|
DEFAULT_BETA_BND_DIST_PARMS,
|
|
30
28
|
DEFAULT_FCOUNT_WTS,
|
|
31
|
-
|
|
29
|
+
MarginSampleData,
|
|
32
30
|
PCMDistribution,
|
|
33
31
|
PCMRestriction,
|
|
34
32
|
PCMSpec,
|
|
35
|
-
|
|
33
|
+
PriceSampleData,
|
|
36
34
|
PriceSpec,
|
|
37
|
-
|
|
35
|
+
ShareSampleData,
|
|
38
36
|
ShareSpec,
|
|
39
37
|
SHRDistribution,
|
|
40
38
|
SSZConstant,
|
|
@@ -50,8 +48,8 @@ def gen_share_data(
|
|
|
50
48
|
_mktshr_rng_seed_seq: SeedSequence,
|
|
51
49
|
_nthreads: int,
|
|
52
50
|
/,
|
|
53
|
-
) ->
|
|
54
|
-
"""
|
|
51
|
+
) -> ShareSampleData:
|
|
52
|
+
"""Generate share data.
|
|
55
53
|
|
|
56
54
|
Parameters
|
|
57
55
|
----------
|
|
@@ -66,10 +64,9 @@ def gen_share_data(
|
|
|
66
64
|
|
|
67
65
|
Returns
|
|
68
66
|
-------
|
|
69
|
-
Arrays representing shares, diversion ratios, etc. structured as a :
|
|
67
|
+
Arrays representing shares, diversion ratios, etc. structured as a :ShareSampleData:
|
|
70
68
|
|
|
71
69
|
"""
|
|
72
|
-
|
|
73
70
|
dist_type_mktshr, firm_count_prob_wts, dist_parms_mktshr, recapture_form = (
|
|
74
71
|
getattr(_share_spec, _f)
|
|
75
72
|
for _f in ("dist_type", "firm_counts_weights", "dist_parms", "recapture_form")
|
|
@@ -101,7 +98,7 @@ def gen_share_data(
|
|
|
101
98
|
frmshr_array = mkt_share_sample.mktshr_array[:, :2]
|
|
102
99
|
r_bar_ = _share_spec.recapture_ratio or DEFAULT_REC_RATIO
|
|
103
100
|
if recapture_form == RECForm.INOUT:
|
|
104
|
-
mkt_share_sample =
|
|
101
|
+
mkt_share_sample = ShareSampleData(
|
|
105
102
|
mkt_share_sample.mktshr_array,
|
|
106
103
|
mkt_share_sample.fcounts,
|
|
107
104
|
mkt_share_sample.nth_firm_share,
|
|
@@ -117,7 +114,7 @@ def gen_market_shares_uniform(
|
|
|
117
114
|
_mktshr_rng_seed_seq: SeedSequence,
|
|
118
115
|
_nthreads: int,
|
|
119
116
|
/,
|
|
120
|
-
) ->
|
|
117
|
+
) -> ShareSampleData:
|
|
121
118
|
"""Generate merging-firm shares from Uniform distribution on the 3-D simplex.
|
|
122
119
|
|
|
123
120
|
Parameters
|
|
@@ -136,7 +133,6 @@ def gen_market_shares_uniform(
|
|
|
136
133
|
market shares and other market statistics for each draw (market)
|
|
137
134
|
|
|
138
135
|
"""
|
|
139
|
-
|
|
140
136
|
frmshr_array: ArrayDouble = np.empty((_s_size, 2), float)
|
|
141
137
|
|
|
142
138
|
MultithreadedRNG(
|
|
@@ -170,7 +166,7 @@ def gen_market_shares_uniform(
|
|
|
170
166
|
nth_firm_share_.fill(np.nan)
|
|
171
167
|
aggregate_purchase_prob_.fill(np.nan)
|
|
172
168
|
|
|
173
|
-
return
|
|
169
|
+
return ShareSampleData(
|
|
174
170
|
mktshr_array_, fcounts_, nth_firm_share_, aggregate_purchase_prob_
|
|
175
171
|
)
|
|
176
172
|
|
|
@@ -185,7 +181,7 @@ def _gen_market_shares_dirichlet_multimarket(
|
|
|
185
181
|
_mktshr_rng_seed_seq: SeedSequence,
|
|
186
182
|
_nthreads: int = NTHREADS,
|
|
187
183
|
/,
|
|
188
|
-
) ->
|
|
184
|
+
) -> ShareSampleData:
|
|
189
185
|
"""Dirichlet-distributed shares with multiple firm-counts.
|
|
190
186
|
|
|
191
187
|
Firm-counts may be specified as having Uniform distribution over the range
|
|
@@ -221,7 +217,6 @@ def _gen_market_shares_dirichlet_multimarket(
|
|
|
221
217
|
array of market shares and other market statistics
|
|
222
218
|
|
|
223
219
|
"""
|
|
224
|
-
|
|
225
220
|
_firm_count_wts = (
|
|
226
221
|
DEFAULT_FCOUNT_WTS
|
|
227
222
|
if _firm_count_wts is None or not len(_firm_count_wts)
|
|
@@ -310,7 +305,7 @@ def _gen_market_shares_dirichlet_multimarket(
|
|
|
310
305
|
)
|
|
311
306
|
)
|
|
312
307
|
|
|
313
|
-
return
|
|
308
|
+
return ShareSampleData(
|
|
314
309
|
mktshr_array_, fcounts_, nth_firm_share_, aggregate_purchase_prob_
|
|
315
310
|
)
|
|
316
311
|
|
|
@@ -322,7 +317,7 @@ def gen_market_shares_dirichlet(
|
|
|
322
317
|
_mktshr_rng_seed_seq: SeedSequence,
|
|
323
318
|
_nthreads: int,
|
|
324
319
|
/,
|
|
325
|
-
) ->
|
|
320
|
+
) -> ShareSampleData:
|
|
326
321
|
"""Dirichlet-distributed shares with fixed firm-count.
|
|
327
322
|
|
|
328
323
|
Parameters
|
|
@@ -349,7 +344,6 @@ def gen_market_shares_dirichlet(
|
|
|
349
344
|
array of market shares and other market statistics
|
|
350
345
|
|
|
351
346
|
"""
|
|
352
|
-
|
|
353
347
|
if not isinstance(_dir_alphas, np.ndarray):
|
|
354
348
|
_dir_alphas = np.array(_dir_alphas)
|
|
355
349
|
|
|
@@ -394,7 +388,7 @@ def gen_market_shares_dirichlet(
|
|
|
394
388
|
aggregate_purchase_prob_ = 1 - mktshr_array[:, [-1]] # type: ignore
|
|
395
389
|
mktshr_array = mktshr_array[:, :-1] / aggregate_purchase_prob_
|
|
396
390
|
|
|
397
|
-
return
|
|
391
|
+
return ShareSampleData(
|
|
398
392
|
mktshr_array,
|
|
399
393
|
(mktshr_array.shape[-1] * np.ones((_s_size, 1))).astype(np.int64),
|
|
400
394
|
mktshr_array[:, [-1]],
|
|
@@ -410,7 +404,7 @@ def gen_divr_array(
|
|
|
410
404
|
/,
|
|
411
405
|
) -> ArrayDouble:
|
|
412
406
|
"""
|
|
413
|
-
Given merging-firm shares and related parameters, return
|
|
407
|
+
Given merging-firm shares and related parameters, return diversion ratios.
|
|
414
408
|
|
|
415
409
|
If recapture is specified as :attr:`mergeron.RECForm.OUTIN`, then the
|
|
416
410
|
choice-probability for the outside good must be supplied.
|
|
@@ -442,7 +436,6 @@ def gen_divr_array(
|
|
|
442
436
|
Merging-firm diversion ratios for mergers in the sample.
|
|
443
437
|
|
|
444
438
|
"""
|
|
445
|
-
|
|
446
439
|
divr_array: ArrayDouble
|
|
447
440
|
if _recapture_form == RECForm.FIXED:
|
|
448
441
|
divr_array = _recapture_ratio * _frmshr_array[:, ::-1] / (1 - _frmshr_array) # type: ignore
|
|
@@ -468,7 +461,7 @@ def gen_divr_array(
|
|
|
468
461
|
return divr_array
|
|
469
462
|
|
|
470
463
|
|
|
471
|
-
def gen_margin_price_data(
|
|
464
|
+
def gen_margin_price_data(
|
|
472
465
|
_frmshr_array: ArrayDouble,
|
|
473
466
|
_nth_firm_share: ArrayDouble,
|
|
474
467
|
_aggregate_purchase_prob: ArrayDouble,
|
|
@@ -479,7 +472,7 @@ def gen_margin_price_data( # noqa: PLR0914
|
|
|
479
472
|
_pr_rng_seed_seq: SeedSequence | None,
|
|
480
473
|
_nthreads: int,
|
|
481
474
|
/,
|
|
482
|
-
) -> tuple[
|
|
475
|
+
) -> tuple[MarginSampleData, PriceSampleData]:
|
|
483
476
|
"""Generate margin and price data for mergers in the sample.
|
|
484
477
|
|
|
485
478
|
Parameters
|
|
@@ -519,8 +512,7 @@ def gen_margin_price_data( # noqa: PLR0914
|
|
|
519
512
|
-------
|
|
520
513
|
Simulated margin- and price-data arrays for mergers in the sample.
|
|
521
514
|
"""
|
|
522
|
-
|
|
523
|
-
margin_data = MarginDataSample(
|
|
515
|
+
margin_data = MarginSampleData(
|
|
524
516
|
np.empty_like(_frmshr_array), np.ones(len(_frmshr_array)) == 0
|
|
525
517
|
)
|
|
526
518
|
|
|
@@ -590,7 +582,7 @@ def gen_margin_price_data( # noqa: PLR0914
|
|
|
590
582
|
)
|
|
591
583
|
mnl_test_array = (pcm_array[:, [1]] >= 0) & (pcm_array[:, [1]] <= 1)
|
|
592
584
|
|
|
593
|
-
margin_data =
|
|
585
|
+
margin_data = MarginSampleData(pcm_array[:, :2], mnl_test_array)
|
|
594
586
|
del price_array_here
|
|
595
587
|
case _:
|
|
596
588
|
raise ValueError(
|
|
@@ -649,11 +641,11 @@ def gen_margin_price_data( # noqa: PLR0914
|
|
|
649
641
|
# Otherwise, all draws meet the filing test
|
|
650
642
|
hsr_filing_test = np.ones(len(_frmshr_array), dtype=bool)
|
|
651
643
|
|
|
652
|
-
# Assume that if minimum
|
|
644
|
+
# Assume that if minimum merging-firm share is 10%, merger filing required
|
|
653
645
|
# under value-of-transactions test or merger triggers post-consummation review
|
|
654
646
|
hsr_filing_test |= _frmshr_array.min(axis=1) >= test_rev_ratio_inv
|
|
655
647
|
|
|
656
|
-
return margin_data,
|
|
648
|
+
return margin_data, PriceSampleData(price_array, hsr_filing_test)
|
|
657
649
|
|
|
658
650
|
|
|
659
651
|
def _gen_margin_data(
|
|
@@ -664,7 +656,7 @@ def _gen_margin_data(
|
|
|
664
656
|
_pcm_rng_seed_seq: SeedSequence,
|
|
665
657
|
_nthreads: int,
|
|
666
658
|
/,
|
|
667
|
-
) ->
|
|
659
|
+
) -> MarginSampleData:
|
|
668
660
|
dist_type_pcm, dist_parms_pcm, pcm_restriction_ = (
|
|
669
661
|
getattr(_pcm_spec, _f) for _f in ("dist_type", "dist_parms", "pcm_restriction")
|
|
670
662
|
)
|
|
@@ -745,14 +737,14 @@ def _gen_margin_data(
|
|
|
745
737
|
else:
|
|
746
738
|
mnl_test_array = np.ones(len(pcm_array), dtype=bool)
|
|
747
739
|
|
|
748
|
-
return
|
|
740
|
+
return MarginSampleData(pcm_array, mnl_test_array)
|
|
749
741
|
|
|
750
742
|
|
|
751
743
|
def _beta_located(
|
|
752
744
|
_mu: float | ArrayDouble, _sigma: float | ArrayDouble, /
|
|
753
745
|
) -> ArrayDouble:
|
|
754
746
|
"""
|
|
755
|
-
Given mean and stddev, return shape parameters for corresponding Beta distribution
|
|
747
|
+
Given mean and stddev, return shape parameters for corresponding Beta distribution.
|
|
756
748
|
|
|
757
749
|
Solve the first two moments of the standard Beta to get the shape parameters.
|
|
758
750
|
|
|
@@ -768,20 +760,18 @@ def _beta_located(
|
|
|
768
760
|
shape parameters for Beta distribution
|
|
769
761
|
|
|
770
762
|
"""
|
|
771
|
-
|
|
772
763
|
mul = -1 + _mu * (1 - _mu) / _sigma**2
|
|
773
764
|
return np.array([_mu * mul, (1 - _mu) * mul], float)
|
|
774
765
|
|
|
775
766
|
|
|
776
767
|
def beta_located_bound(_dist_parms: ArrayDouble, /) -> ArrayDouble:
|
|
777
768
|
R"""
|
|
778
|
-
Return shape parameters for a non-standard beta, given
|
|
779
|
-
|
|
769
|
+
Return shape parameters for a non-standard beta, given mean, stddev, and range.
|
|
780
770
|
|
|
781
771
|
Recover the r.v.s as
|
|
782
772
|
:math:`\min + (\max - \min) \cdot \symup{Β}(a, b)`,
|
|
783
773
|
with `a` and `b` calculated from the specified mean (:math:`\mu`) and
|
|
784
|
-
variance (:math:`\sigma`). [
|
|
774
|
+
variance (:math:`\sigma`). [#]_
|
|
785
775
|
|
|
786
776
|
Parameters
|
|
787
777
|
----------
|
|
@@ -798,8 +788,7 @@ def beta_located_bound(_dist_parms: ArrayDouble, /) -> ArrayDouble:
|
|
|
798
788
|
|
|
799
789
|
References
|
|
800
790
|
----------
|
|
801
|
-
.. [
|
|
791
|
+
.. [#] NIST, Beta Distribution. https://www.itl.nist.gov/div898/handbook/eda/section3/eda366h.htm
|
|
802
792
|
""" # noqa: RUF002
|
|
803
|
-
|
|
804
793
|
bmu, bsigma, bmin, bmax = _dist_parms
|
|
805
794
|
return _beta_located((bmu - bmin) / (bmax - bmin), bsigma / (bmax - bmin))
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Methods to format and print summary statistics on merger enforcement patterns.
|
|
3
|
-
|
|
4
|
-
"""
|
|
1
|
+
"""Methods to format and print summary statistics on merger enforcement patterns."""
|
|
5
2
|
|
|
6
3
|
import enum
|
|
7
4
|
from collections.abc import Mapping
|
|
@@ -10,6 +7,7 @@ import numpy as np
|
|
|
10
7
|
from scipy.interpolate import make_interp_spline # type: ignore
|
|
11
8
|
|
|
12
9
|
from .. import VERSION, ArrayBIGINT, Enameled, this_yaml # noqa: TID252
|
|
10
|
+
from ..core import INVData, INVTableData # noqa: TID252
|
|
13
11
|
from ..core import ftc_merger_investigations_data as fid # noqa: TID252
|
|
14
12
|
from . import INVResolution
|
|
15
13
|
|
|
@@ -19,6 +17,8 @@ __version__ = VERSION
|
|
|
19
17
|
@this_yaml.register_class
|
|
20
18
|
@enum.unique
|
|
21
19
|
class IndustryGroup(str, Enameled):
|
|
20
|
+
"""Industry group of reported markets."""
|
|
21
|
+
|
|
22
22
|
ALL = "All Markets"
|
|
23
23
|
GRO = "Grocery Markets"
|
|
24
24
|
OIL = "Oil Markets"
|
|
@@ -34,21 +34,25 @@ class IndustryGroup(str, Enameled):
|
|
|
34
34
|
@this_yaml.register_class
|
|
35
35
|
@enum.unique
|
|
36
36
|
class OtherEvidence(str, Enameled):
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
37
|
+
"""Additional evidence available, if any, for reported markets."""
|
|
38
|
+
|
|
39
|
+
HOT = "Hot Documents Identified"
|
|
40
|
+
NHT = "No Hot Documents Identified"
|
|
41
|
+
HTU = "No Evidence on Hot Documents"
|
|
42
|
+
NCC = "No Strong Customer Complaints"
|
|
43
|
+
SCC = "Strong Customer Complaints"
|
|
44
|
+
CCU = "No Evidence on Customer Complaints"
|
|
45
|
+
END = "Entry Difficult"
|
|
46
|
+
EEY = "Entry Easy"
|
|
47
|
+
EEU = "No Entry Evidence"
|
|
48
|
+
UNR = "Unrestricted on additional evidence"
|
|
47
49
|
|
|
48
50
|
|
|
49
51
|
@this_yaml.register_class
|
|
50
52
|
@enum.unique
|
|
51
53
|
class StatsGrpSelector(str, Enameled):
|
|
54
|
+
"""Measure used to summarize investigations data."""
|
|
55
|
+
|
|
52
56
|
FC = "ByFirmCount"
|
|
53
57
|
HD = "ByHHIandDelta"
|
|
54
58
|
DL = "ByDelta"
|
|
@@ -58,6 +62,8 @@ class StatsGrpSelector(str, Enameled):
|
|
|
58
62
|
@this_yaml.register_class
|
|
59
63
|
@enum.unique
|
|
60
64
|
class StatsReturnSelector(str, Enameled):
|
|
65
|
+
"""Statistics to report on investigations data."""
|
|
66
|
+
|
|
61
67
|
CNT = "count"
|
|
62
68
|
RPT = "rate, point"
|
|
63
69
|
RIN = "rate, interval"
|
|
@@ -66,23 +72,23 @@ class StatsReturnSelector(str, Enameled):
|
|
|
66
72
|
@this_yaml.register_class
|
|
67
73
|
@enum.unique
|
|
68
74
|
class SortSelector(str, Enameled):
|
|
75
|
+
"""Sort order for reporting investigations data."""
|
|
76
|
+
|
|
69
77
|
UCH = "unchanged"
|
|
70
78
|
REV = "reversed"
|
|
71
79
|
|
|
72
80
|
|
|
73
81
|
# Parameters and functions to interpolate selected HHI and ΔHHI values
|
|
74
82
|
# recorded in fractions to ranges of values in points on the HHI scale
|
|
75
|
-
HHI_DELTA_KNOTS = np.array(
|
|
76
|
-
|
|
77
|
-
)
|
|
78
|
-
HHI_POST_ZONE_KNOTS = np.array([0, 1800, 2400, 10001], dtype=np.int64)
|
|
83
|
+
HHI_DELTA_KNOTS = np.array([0, 100, 200, 300, 500, 800, 1200, 2500, 5001], int)
|
|
84
|
+
HHI_POST_ZONE_KNOTS = np.array([0, 1800, 2400, 10001], int)
|
|
79
85
|
hhi_delta_ranger, hhi_zone_post_ranger = (
|
|
80
86
|
make_interp_spline(_f / 1e4, _f, k=0)
|
|
81
87
|
for _f in (HHI_DELTA_KNOTS, HHI_POST_ZONE_KNOTS)
|
|
82
88
|
)
|
|
83
89
|
|
|
84
90
|
|
|
85
|
-
HMG_PRESUMPTION_ZONE_MAP = {
|
|
91
|
+
HMG_PRESUMPTION_ZONE_MAP: dict[int, dict[int, tuple[int, int, int]]] = {
|
|
86
92
|
HHI_POST_ZONE_KNOTS[0]: {
|
|
87
93
|
HHI_DELTA_KNOTS[0]: (0, 0, 0),
|
|
88
94
|
HHI_DELTA_KNOTS[1]: (0, 0, 0),
|
|
@@ -128,7 +134,7 @@ ZONE_DETAIL_STRINGS_DELTA = {
|
|
|
128
134
|
|
|
129
135
|
|
|
130
136
|
def enf_cnts_obs_by_group(
|
|
131
|
-
_invdata_array_dict:
|
|
137
|
+
_invdata_array_dict: INVData,
|
|
132
138
|
_study_period: str,
|
|
133
139
|
_table_ind_grp: IndustryGroup,
|
|
134
140
|
_table_evid_cond: OtherEvidence,
|
|
@@ -136,6 +142,28 @@ def enf_cnts_obs_by_group(
|
|
|
136
142
|
_enf_spec: INVResolution,
|
|
137
143
|
/,
|
|
138
144
|
) -> ArrayBIGINT:
|
|
145
|
+
"""Summarize investigations data by reporting group.
|
|
146
|
+
|
|
147
|
+
Parameters
|
|
148
|
+
----------
|
|
149
|
+
_invdata_array_dict
|
|
150
|
+
raw investigations data
|
|
151
|
+
_study_period
|
|
152
|
+
study period
|
|
153
|
+
_table_ind_grp
|
|
154
|
+
industry group
|
|
155
|
+
_table_evid_cond
|
|
156
|
+
additional evidence
|
|
157
|
+
_stats_group
|
|
158
|
+
grouping measure
|
|
159
|
+
_enf_spec
|
|
160
|
+
enforcement specification (see, :class:`mergeron.gen.INVResolution`)
|
|
161
|
+
|
|
162
|
+
Returns
|
|
163
|
+
-------
|
|
164
|
+
ArrayBIGINT
|
|
165
|
+
Counts of markets resolved as enforced, cleared, or both, respectively.
|
|
166
|
+
"""
|
|
139
167
|
if _stats_group == StatsGrpSelector.HD:
|
|
140
168
|
raise ValueError(
|
|
141
169
|
f"Clearance/enforcement statistics, '{_stats_group}' not valied here."
|
|
@@ -164,13 +192,34 @@ def enf_cnts_obs_by_group(
|
|
|
164
192
|
|
|
165
193
|
|
|
166
194
|
def enf_cnts_obs_byfirmcount(
|
|
167
|
-
_data_array_dict:
|
|
195
|
+
_data_array_dict: INVData,
|
|
168
196
|
_data_period: str = "1996-2003",
|
|
169
197
|
_table_ind_group: IndustryGroup = IndustryGroup.ALL,
|
|
170
|
-
_table_evid_cond: OtherEvidence = OtherEvidence.
|
|
171
|
-
_enf_spec: INVResolution = INVResolution.
|
|
198
|
+
_table_evid_cond: OtherEvidence = OtherEvidence.UNR,
|
|
199
|
+
_enf_spec: INVResolution = INVResolution.ENFT,
|
|
172
200
|
/,
|
|
173
201
|
) -> ArrayBIGINT:
|
|
202
|
+
"""Summarize investigations data by firm count.
|
|
203
|
+
|
|
204
|
+
Parameters
|
|
205
|
+
----------
|
|
206
|
+
_data_array_dict
|
|
207
|
+
raw investigations data
|
|
208
|
+
_data_period
|
|
209
|
+
data period
|
|
210
|
+
_table_ind_group
|
|
211
|
+
industry group
|
|
212
|
+
_table_evid_cond
|
|
213
|
+
additional evidence
|
|
214
|
+
_enf_spec
|
|
215
|
+
enforcement specification (see, :class:`mergeron.gen.INVResolution`)
|
|
216
|
+
|
|
217
|
+
Returns
|
|
218
|
+
-------
|
|
219
|
+
ArrayBIGINT
|
|
220
|
+
Counts of markets resolved as enforced, cleared, or both, respectively,
|
|
221
|
+
reported by number of pre-merger firms.
|
|
222
|
+
"""
|
|
174
223
|
if _data_period not in _data_array_dict:
|
|
175
224
|
raise ValueError(
|
|
176
225
|
f"Invalid value of data period, {f'"{_data_period}"'}."
|
|
@@ -197,13 +246,34 @@ def enf_cnts_obs_byfirmcount(
|
|
|
197
246
|
|
|
198
247
|
|
|
199
248
|
def enf_cnts_obs_byhhianddelta(
|
|
200
|
-
_data_array_dict:
|
|
249
|
+
_data_array_dict: INVData,
|
|
201
250
|
_data_period: str = "1996-2003",
|
|
202
251
|
_table_ind_group: IndustryGroup = IndustryGroup.ALL,
|
|
203
|
-
_table_evid_cond: OtherEvidence = OtherEvidence.
|
|
204
|
-
_enf_spec: INVResolution = INVResolution.
|
|
252
|
+
_table_evid_cond: OtherEvidence = OtherEvidence.UNR,
|
|
253
|
+
_enf_spec: INVResolution = INVResolution.ENFT,
|
|
205
254
|
/,
|
|
206
255
|
) -> ArrayBIGINT:
|
|
256
|
+
"""Summarize investigations data by HHI and ΔHHI.
|
|
257
|
+
|
|
258
|
+
Parameters
|
|
259
|
+
----------
|
|
260
|
+
_data_array_dict
|
|
261
|
+
raw investigations data
|
|
262
|
+
_data_period
|
|
263
|
+
data period
|
|
264
|
+
_table_ind_group
|
|
265
|
+
industry group
|
|
266
|
+
_table_evid_cond
|
|
267
|
+
additional evidence
|
|
268
|
+
_enf_spec
|
|
269
|
+
enforcement specification (see, :class:`mergeron.gen.INVResolution`)
|
|
270
|
+
|
|
271
|
+
Returns
|
|
272
|
+
-------
|
|
273
|
+
ArrayBIGINT
|
|
274
|
+
Counts of markets resolved as enforced, cleared, or both, respectively,
|
|
275
|
+
reported by HHI and ΔHHI.
|
|
276
|
+
"""
|
|
207
277
|
if _data_period not in _data_array_dict:
|
|
208
278
|
raise ValueError(
|
|
209
279
|
f"Invalid value of data period, {f'"{_data_period}"'}."
|
|
@@ -230,11 +300,21 @@ def enf_cnts_obs_byhhianddelta(
|
|
|
230
300
|
|
|
231
301
|
|
|
232
302
|
def table_no_lku(
|
|
233
|
-
_data_array_dict_sub: Mapping[str,
|
|
303
|
+
_data_array_dict_sub: Mapping[str, INVTableData],
|
|
234
304
|
_table_ind_group: IndustryGroup = IndustryGroup.ALL,
|
|
235
|
-
_table_evid_cond: OtherEvidence = OtherEvidence.
|
|
305
|
+
_table_evid_cond: OtherEvidence = OtherEvidence.UNR,
|
|
236
306
|
/,
|
|
237
307
|
) -> str:
|
|
308
|
+
"""Lookup table number based on industry group and additional evidence."""
|
|
309
|
+
if _table_evid_cond not in (
|
|
310
|
+
_egl := [
|
|
311
|
+
_data_array_dict_sub[_v].additional_evidence for _v in _data_array_dict_sub
|
|
312
|
+
]
|
|
313
|
+
):
|
|
314
|
+
raise ValueError(
|
|
315
|
+
f"Invalid value for additional evidence, {f'"{_table_evid_cond}"'}."
|
|
316
|
+
f"Must be one of {_egl!r}"
|
|
317
|
+
)
|
|
238
318
|
if _table_ind_group not in (
|
|
239
319
|
_igl := [_data_array_dict_sub[_v].industry_group for _v in _data_array_dict_sub]
|
|
240
320
|
):
|
|
@@ -256,6 +336,18 @@ def table_no_lku(
|
|
|
256
336
|
|
|
257
337
|
|
|
258
338
|
def enf_cnts_byfirmcount(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
339
|
+
"""Summarize investigations data by firm count.
|
|
340
|
+
|
|
341
|
+
Parameters
|
|
342
|
+
----------
|
|
343
|
+
_cnts_array
|
|
344
|
+
raw investigations data array
|
|
345
|
+
|
|
346
|
+
Returns
|
|
347
|
+
-------
|
|
348
|
+
ArrayBIGINT
|
|
349
|
+
Subtotals for columns other than the first, grouped by the first column.
|
|
350
|
+
"""
|
|
259
351
|
if not _cnts_array[:, 0].any():
|
|
260
352
|
return np.array([], int)
|
|
261
353
|
|
|
@@ -272,6 +364,20 @@ def enf_cnts_byfirmcount(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
|
272
364
|
|
|
273
365
|
|
|
274
366
|
def enf_cnts_bydelta(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
367
|
+
"""Summarize investigations data by ΔHHI.
|
|
368
|
+
|
|
369
|
+
Parameters
|
|
370
|
+
----------
|
|
371
|
+
_cnts_array
|
|
372
|
+
raw investigations data array
|
|
373
|
+
|
|
374
|
+
Returns
|
|
375
|
+
-------
|
|
376
|
+
ArrayBIGINT
|
|
377
|
+
Subtotals for columns higher than the second, grouped by the second column.
|
|
378
|
+
"""
|
|
379
|
+
if not _cnts_array[:, 1].any():
|
|
380
|
+
return np.array([], int)
|
|
275
381
|
ndim_in = 2
|
|
276
382
|
return np.vstack([
|
|
277
383
|
np.concatenate([
|
|
@@ -285,9 +391,23 @@ def enf_cnts_bydelta(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
|
285
391
|
|
|
286
392
|
|
|
287
393
|
def enf_cnts_byconczone(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
394
|
+
"""Summarize investigations data by concentration zone, as defined in the Guidelines.
|
|
395
|
+
|
|
396
|
+
Includes sub-total detail for "Moderately Concentrated" and "Unconcentrated" markets.
|
|
397
|
+
|
|
398
|
+
Parameters
|
|
399
|
+
----------
|
|
400
|
+
_cnts_array
|
|
401
|
+
raw investigations data array
|
|
402
|
+
|
|
403
|
+
Returns
|
|
404
|
+
-------
|
|
405
|
+
ArrayBIGINT
|
|
406
|
+
Subtotals range of HHI and ΔHHI, with detail
|
|
407
|
+
"""
|
|
288
408
|
if not _cnts_array[:, 0].any() or np.isnan(_cnts_array[:, 0]).all():
|
|
289
409
|
return np.array([], int)
|
|
290
|
-
# Step 1: Tag and agg. from HHI-post and
|
|
410
|
+
# Step 1: Tag and agg. from HHI-post and ΔHHI to zone triple
|
|
291
411
|
# NOTE: Although you could just map and not (partially) aggregate in this step,
|
|
292
412
|
# the mapped array is a copy, and is larger without partial aggregation, so
|
|
293
413
|
# aggregation reduces the footprint of this step in memory. Although this point
|
|
@@ -295,9 +415,11 @@ def enf_cnts_byconczone(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
|
295
415
|
# in both cases does make life easier
|
|
296
416
|
_ndim_in = 2
|
|
297
417
|
_nkeys = 3
|
|
298
|
-
cnts_byhhipostanddelta
|
|
299
|
-
|
|
300
|
-
|
|
418
|
+
cnts_byhhipostanddelta: ArrayBIGINT = np.zeros(
|
|
419
|
+
(1, _nkeys + _cnts_array.shape[1] - _ndim_in), dtype=int
|
|
420
|
+
)
|
|
421
|
+
cnts_byconczone: ArrayBIGINT = np.zeros(
|
|
422
|
+
(1, _nkeys + _cnts_array.shape[1] - _ndim_in), dtype=int
|
|
301
423
|
)
|
|
302
424
|
|
|
303
425
|
# Prepare to tag clearance stats by presumption zone
|
mergeron/gen/upp_tests.py
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Methods to compute intrinsic clearance rates and intrinsic enforcement rates
|
|
3
|
-
from generated market data.
|
|
4
|
-
|
|
5
|
-
"""
|
|
1
|
+
"""Methods to compute intrinsic enforcement/clearance rates from generated market data."""
|
|
6
2
|
|
|
7
3
|
from collections.abc import Sequence
|
|
8
4
|
from typing import TypedDict
|
|
@@ -28,20 +24,20 @@ __version__ = VERSION
|
|
|
28
24
|
|
|
29
25
|
|
|
30
26
|
class INVRESCntsArgs(TypedDict, total=False):
|
|
31
|
-
"Keyword arguments of function, :code:`sim_enf_cnts
|
|
27
|
+
"""Keyword arguments of function, :code:`sim_enf_cnts`."""
|
|
32
28
|
|
|
33
29
|
sample_size: int
|
|
34
30
|
seed_seq_list: Sequence[SeedSequence] | None
|
|
35
31
|
nthreads: int
|
|
36
32
|
|
|
37
33
|
|
|
38
|
-
def compute_upp_test_counts(
|
|
34
|
+
def compute_upp_test_counts(
|
|
39
35
|
_market_data_sample: MarketSampleData,
|
|
40
36
|
_upp_test_parms: gbl.HMGThresholds,
|
|
41
37
|
_upp_test_regime: UPPTestRegime,
|
|
42
38
|
/,
|
|
43
39
|
) -> UPPTestsCounts:
|
|
44
|
-
"""Estimate enforcement and clearance counts from market data sample
|
|
40
|
+
"""Estimate enforcement and clearance counts from market data sample.
|
|
45
41
|
|
|
46
42
|
Parameters
|
|
47
43
|
----------
|
|
@@ -64,7 +60,6 @@ def compute_upp_test_counts( # noqa: PLR0914
|
|
|
64
60
|
Enforced and cleared counts
|
|
65
61
|
|
|
66
62
|
"""
|
|
67
|
-
|
|
68
63
|
g_bar_, divr_bar_, cmcr_bar_, ipr_bar_ = (
|
|
69
64
|
getattr(_upp_test_parms, _f) for _f in ("guppi", "divr", "cmcr", "ipr")
|
|
70
65
|
)
|