mergeron 2025.739319.2__py3-none-any.whl → 2025.739341.8__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 +213 -158
- 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 +38 -38
- mergeron/gen/data_generation.py +20 -24
- mergeron/gen/data_generation_functions.py +36 -51
- mergeron/gen/enforcement_stats.py +151 -27
- mergeron/gen/upp_tests.py +4 -9
- mergeron-2025.739341.8.dist-info/METADATA +94 -0
- mergeron-2025.739341.8.dist-info/RECORD +20 -0
- {mergeron-2025.739319.2.dist-info → mergeron-2025.739341.8.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.2.dist-info/METADATA +0 -174
- mergeron-2025.739319.2.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
|
-
|
|
32
|
-
MarginDataSample,
|
|
29
|
+
MarginSampleData,
|
|
33
30
|
PCMDistribution,
|
|
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,10 +656,9 @@ def _gen_margin_data(
|
|
|
664
656
|
_pcm_rng_seed_seq: SeedSequence,
|
|
665
657
|
_nthreads: int,
|
|
666
658
|
/,
|
|
667
|
-
) ->
|
|
668
|
-
dist_type_pcm, dist_parms_pcm,
|
|
669
|
-
getattr(_pcm_spec, _f)
|
|
670
|
-
for _f in ("dist_type", "dist_parms", "pcm_restriction")
|
|
659
|
+
) -> MarginSampleData:
|
|
660
|
+
dist_type_pcm, dist_parms_pcm, pcm_restriction_ = (
|
|
661
|
+
getattr(_pcm_spec, _f) for _f in ("dist_type", "dist_parms", "pcm_restriction")
|
|
671
662
|
)
|
|
672
663
|
|
|
673
664
|
pcm_array = (
|
|
@@ -680,7 +671,10 @@ def _gen_margin_data(
|
|
|
680
671
|
beta_min, beta_max = [0.0] * 2 # placeholder
|
|
681
672
|
if dist_type_pcm == PCMDistribution.EMPR:
|
|
682
673
|
pcm_array = margin_data_resampler(
|
|
683
|
-
dist_parms_pcm,
|
|
674
|
+
dist_parms_pcm,
|
|
675
|
+
sample_size=pcm_array.shape,
|
|
676
|
+
seed_sequence=_pcm_rng_seed_seq,
|
|
677
|
+
nthreads=_nthreads,
|
|
684
678
|
)
|
|
685
679
|
else:
|
|
686
680
|
dist_type_: Literal["Beta", "Uniform"]
|
|
@@ -721,16 +715,10 @@ def _gen_margin_data(
|
|
|
721
715
|
pcm_array = (beta_max - beta_min) * pcm_array + beta_min
|
|
722
716
|
del beta_min, beta_max
|
|
723
717
|
|
|
724
|
-
if
|
|
718
|
+
if pcm_restriction_ == PCMRestriction.SYM:
|
|
725
719
|
pcm_array = np.hstack((pcm_array,) * _frmshr_array.shape[1])
|
|
726
|
-
if
|
|
720
|
+
if pcm_restriction_ == PCMRestriction.MNL:
|
|
727
721
|
# Impose FOCs from profit-maximization with MNL demand
|
|
728
|
-
if dist_type_pcm == PCMDistribution.EMPR:
|
|
729
|
-
print(
|
|
730
|
-
"NOTE: Estimated Firm 2 parameters will not be consistent with "
|
|
731
|
-
"the empirical distribution of margins in the source data. For "
|
|
732
|
-
"consistency, respecify pcm_spec.pcm_restriction = PCMRestriction.IID."
|
|
733
|
-
)
|
|
734
722
|
purchase_prob_array = _aggregate_purchase_prob * _frmshr_array
|
|
735
723
|
|
|
736
724
|
pcm_array[:, [1]] = np.divide(
|
|
@@ -749,14 +737,14 @@ def _gen_margin_data(
|
|
|
749
737
|
else:
|
|
750
738
|
mnl_test_array = np.ones(len(pcm_array), dtype=bool)
|
|
751
739
|
|
|
752
|
-
return
|
|
740
|
+
return MarginSampleData(pcm_array, mnl_test_array)
|
|
753
741
|
|
|
754
742
|
|
|
755
743
|
def _beta_located(
|
|
756
744
|
_mu: float | ArrayDouble, _sigma: float | ArrayDouble, /
|
|
757
745
|
) -> ArrayDouble:
|
|
758
746
|
"""
|
|
759
|
-
Given mean and stddev, return shape parameters for corresponding Beta distribution
|
|
747
|
+
Given mean and stddev, return shape parameters for corresponding Beta distribution.
|
|
760
748
|
|
|
761
749
|
Solve the first two moments of the standard Beta to get the shape parameters.
|
|
762
750
|
|
|
@@ -772,20 +760,18 @@ def _beta_located(
|
|
|
772
760
|
shape parameters for Beta distribution
|
|
773
761
|
|
|
774
762
|
"""
|
|
775
|
-
|
|
776
763
|
mul = -1 + _mu * (1 - _mu) / _sigma**2
|
|
777
764
|
return np.array([_mu * mul, (1 - _mu) * mul], float)
|
|
778
765
|
|
|
779
766
|
|
|
780
767
|
def beta_located_bound(_dist_parms: ArrayDouble, /) -> ArrayDouble:
|
|
781
768
|
R"""
|
|
782
|
-
Return shape parameters for a non-standard beta, given
|
|
783
|
-
|
|
769
|
+
Return shape parameters for a non-standard beta, given mean, stddev, and range.
|
|
784
770
|
|
|
785
771
|
Recover the r.v.s as
|
|
786
772
|
:math:`\min + (\max - \min) \cdot \symup{Β}(a, b)`,
|
|
787
773
|
with `a` and `b` calculated from the specified mean (:math:`\mu`) and
|
|
788
|
-
variance (:math:`\sigma`). [
|
|
774
|
+
variance (:math:`\sigma`). [#]_
|
|
789
775
|
|
|
790
776
|
Parameters
|
|
791
777
|
----------
|
|
@@ -802,8 +788,7 @@ def beta_located_bound(_dist_parms: ArrayDouble, /) -> ArrayDouble:
|
|
|
802
788
|
|
|
803
789
|
References
|
|
804
790
|
----------
|
|
805
|
-
.. [
|
|
791
|
+
.. [#] NIST, Beta Distribution. https://www.itl.nist.gov/div898/handbook/eda/section3/eda366h.htm
|
|
806
792
|
""" # noqa: RUF002
|
|
807
|
-
|
|
808
793
|
bmu, bsigma, bmin, bmax = _dist_parms
|
|
809
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
|
|
@@ -19,6 +16,8 @@ __version__ = VERSION
|
|
|
19
16
|
@this_yaml.register_class
|
|
20
17
|
@enum.unique
|
|
21
18
|
class IndustryGroup(str, Enameled):
|
|
19
|
+
"""Industry group of reported markets."""
|
|
20
|
+
|
|
22
21
|
ALL = "All Markets"
|
|
23
22
|
GRO = "Grocery Markets"
|
|
24
23
|
OIL = "Oil Markets"
|
|
@@ -34,21 +33,25 @@ class IndustryGroup(str, Enameled):
|
|
|
34
33
|
@this_yaml.register_class
|
|
35
34
|
@enum.unique
|
|
36
35
|
class OtherEvidence(str, Enameled):
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
36
|
+
"""Additional evidence available, if any, for reported markets."""
|
|
37
|
+
|
|
38
|
+
HOT = "Hot Documents Identified"
|
|
39
|
+
NHT = "No Hot Documents Identified"
|
|
40
|
+
HTU = "No Evidence on Hot Documents"
|
|
41
|
+
NCC = "No Strong Customer Complaints"
|
|
42
|
+
SCC = "Strong Customer Complaints"
|
|
43
|
+
CCU = "No Evidence on Customer Complaints"
|
|
44
|
+
END = "Entry Difficult"
|
|
45
|
+
EEY = "Entry Easy"
|
|
46
|
+
EEU = "No Entry Evidence"
|
|
47
|
+
UNR = "Unrestricted on additional evidence"
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
@this_yaml.register_class
|
|
50
51
|
@enum.unique
|
|
51
52
|
class StatsGrpSelector(str, Enameled):
|
|
53
|
+
"""Measure used to summarize investigations data."""
|
|
54
|
+
|
|
52
55
|
FC = "ByFirmCount"
|
|
53
56
|
HD = "ByHHIandDelta"
|
|
54
57
|
DL = "ByDelta"
|
|
@@ -58,6 +61,8 @@ class StatsGrpSelector(str, Enameled):
|
|
|
58
61
|
@this_yaml.register_class
|
|
59
62
|
@enum.unique
|
|
60
63
|
class StatsReturnSelector(str, Enameled):
|
|
64
|
+
"""Statistics to report on investigations data."""
|
|
65
|
+
|
|
61
66
|
CNT = "count"
|
|
62
67
|
RPT = "rate, point"
|
|
63
68
|
RIN = "rate, interval"
|
|
@@ -66,6 +71,8 @@ class StatsReturnSelector(str, Enameled):
|
|
|
66
71
|
@this_yaml.register_class
|
|
67
72
|
@enum.unique
|
|
68
73
|
class SortSelector(str, Enameled):
|
|
74
|
+
"""Sort order for reporting investigations data."""
|
|
75
|
+
|
|
69
76
|
UCH = "unchanged"
|
|
70
77
|
REV = "reversed"
|
|
71
78
|
|
|
@@ -128,7 +135,7 @@ ZONE_DETAIL_STRINGS_DELTA = {
|
|
|
128
135
|
|
|
129
136
|
|
|
130
137
|
def enf_cnts_obs_by_group(
|
|
131
|
-
_invdata_array_dict:
|
|
138
|
+
_invdata_array_dict: fid.INVData,
|
|
132
139
|
_study_period: str,
|
|
133
140
|
_table_ind_grp: IndustryGroup,
|
|
134
141
|
_table_evid_cond: OtherEvidence,
|
|
@@ -136,6 +143,28 @@ def enf_cnts_obs_by_group(
|
|
|
136
143
|
_enf_spec: INVResolution,
|
|
137
144
|
/,
|
|
138
145
|
) -> ArrayBIGINT:
|
|
146
|
+
"""Summarize investigations data by reporting group.
|
|
147
|
+
|
|
148
|
+
Parameters
|
|
149
|
+
----------
|
|
150
|
+
_invdata_array_dict
|
|
151
|
+
raw investigations data
|
|
152
|
+
_study_period
|
|
153
|
+
study period
|
|
154
|
+
_table_ind_grp
|
|
155
|
+
industry group
|
|
156
|
+
_table_evid_cond
|
|
157
|
+
additional evidence
|
|
158
|
+
_stats_group
|
|
159
|
+
grouping measure
|
|
160
|
+
_enf_spec
|
|
161
|
+
enforcement specification (see, :class:`mergeron.gen.INVResolution`)
|
|
162
|
+
|
|
163
|
+
Returns
|
|
164
|
+
-------
|
|
165
|
+
ArrayBIGINT
|
|
166
|
+
Counts of markets resolved as enforced, cleared, or both, respectively.
|
|
167
|
+
"""
|
|
139
168
|
if _stats_group == StatsGrpSelector.HD:
|
|
140
169
|
raise ValueError(
|
|
141
170
|
f"Clearance/enforcement statistics, '{_stats_group}' not valied here."
|
|
@@ -164,13 +193,34 @@ def enf_cnts_obs_by_group(
|
|
|
164
193
|
|
|
165
194
|
|
|
166
195
|
def enf_cnts_obs_byfirmcount(
|
|
167
|
-
_data_array_dict:
|
|
196
|
+
_data_array_dict: fid.INVData,
|
|
168
197
|
_data_period: str = "1996-2003",
|
|
169
198
|
_table_ind_group: IndustryGroup = IndustryGroup.ALL,
|
|
170
|
-
_table_evid_cond: OtherEvidence = OtherEvidence.
|
|
171
|
-
_enf_spec: INVResolution = INVResolution.
|
|
199
|
+
_table_evid_cond: OtherEvidence = OtherEvidence.UNR,
|
|
200
|
+
_enf_spec: INVResolution = INVResolution.ENFT,
|
|
172
201
|
/,
|
|
173
202
|
) -> ArrayBIGINT:
|
|
203
|
+
"""Summarize investigations data by firm count.
|
|
204
|
+
|
|
205
|
+
Parameters
|
|
206
|
+
----------
|
|
207
|
+
_data_array_dict
|
|
208
|
+
raw investigations data
|
|
209
|
+
_data_period
|
|
210
|
+
data period
|
|
211
|
+
_table_ind_group
|
|
212
|
+
industry group
|
|
213
|
+
_table_evid_cond
|
|
214
|
+
additional evidence
|
|
215
|
+
_enf_spec
|
|
216
|
+
enforcement specification (see, :class:`mergeron.gen.INVResolution`)
|
|
217
|
+
|
|
218
|
+
Returns
|
|
219
|
+
-------
|
|
220
|
+
ArrayBIGINT
|
|
221
|
+
Counts of markets resolved as enforced, cleared, or both, respectively,
|
|
222
|
+
reported by number of pre-merger firms.
|
|
223
|
+
"""
|
|
174
224
|
if _data_period not in _data_array_dict:
|
|
175
225
|
raise ValueError(
|
|
176
226
|
f"Invalid value of data period, {f'"{_data_period}"'}."
|
|
@@ -197,13 +247,34 @@ def enf_cnts_obs_byfirmcount(
|
|
|
197
247
|
|
|
198
248
|
|
|
199
249
|
def enf_cnts_obs_byhhianddelta(
|
|
200
|
-
_data_array_dict:
|
|
250
|
+
_data_array_dict: fid.INVData,
|
|
201
251
|
_data_period: str = "1996-2003",
|
|
202
252
|
_table_ind_group: IndustryGroup = IndustryGroup.ALL,
|
|
203
|
-
_table_evid_cond: OtherEvidence = OtherEvidence.
|
|
204
|
-
_enf_spec: INVResolution = INVResolution.
|
|
253
|
+
_table_evid_cond: OtherEvidence = OtherEvidence.UNR,
|
|
254
|
+
_enf_spec: INVResolution = INVResolution.ENFT,
|
|
205
255
|
/,
|
|
206
256
|
) -> ArrayBIGINT:
|
|
257
|
+
"""Summarize investigations data by HHI and ΔHHI.
|
|
258
|
+
|
|
259
|
+
Parameters
|
|
260
|
+
----------
|
|
261
|
+
_data_array_dict
|
|
262
|
+
raw investigations data
|
|
263
|
+
_data_period
|
|
264
|
+
data period
|
|
265
|
+
_table_ind_group
|
|
266
|
+
industry group
|
|
267
|
+
_table_evid_cond
|
|
268
|
+
additional evidence
|
|
269
|
+
_enf_spec
|
|
270
|
+
enforcement specification (see, :class:`mergeron.gen.INVResolution`)
|
|
271
|
+
|
|
272
|
+
Returns
|
|
273
|
+
-------
|
|
274
|
+
ArrayBIGINT
|
|
275
|
+
Counts of markets resolved as enforced, cleared, or both, respectively,
|
|
276
|
+
reported by HHI and ΔHHI.
|
|
277
|
+
"""
|
|
207
278
|
if _data_period not in _data_array_dict:
|
|
208
279
|
raise ValueError(
|
|
209
280
|
f"Invalid value of data period, {f'"{_data_period}"'}."
|
|
@@ -232,9 +303,19 @@ def enf_cnts_obs_byhhianddelta(
|
|
|
232
303
|
def table_no_lku(
|
|
233
304
|
_data_array_dict_sub: Mapping[str, fid.INVTableData],
|
|
234
305
|
_table_ind_group: IndustryGroup = IndustryGroup.ALL,
|
|
235
|
-
_table_evid_cond: OtherEvidence = OtherEvidence.
|
|
306
|
+
_table_evid_cond: OtherEvidence = OtherEvidence.UNR,
|
|
236
307
|
/,
|
|
237
308
|
) -> str:
|
|
309
|
+
"""Lookup table number based on industry group and additional evidence."""
|
|
310
|
+
if _table_evid_cond not in (
|
|
311
|
+
_egl := [
|
|
312
|
+
_data_array_dict_sub[_v].additional_evidence for _v in _data_array_dict_sub
|
|
313
|
+
]
|
|
314
|
+
):
|
|
315
|
+
raise ValueError(
|
|
316
|
+
f"Invalid value for additional evidence, {f'"{_table_evid_cond}"'}."
|
|
317
|
+
f"Must be one of {_egl!r}"
|
|
318
|
+
)
|
|
238
319
|
if _table_ind_group not in (
|
|
239
320
|
_igl := [_data_array_dict_sub[_v].industry_group for _v in _data_array_dict_sub]
|
|
240
321
|
):
|
|
@@ -256,6 +337,18 @@ def table_no_lku(
|
|
|
256
337
|
|
|
257
338
|
|
|
258
339
|
def enf_cnts_byfirmcount(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
340
|
+
"""Summarize investigations data by firm count.
|
|
341
|
+
|
|
342
|
+
Parameters
|
|
343
|
+
----------
|
|
344
|
+
_cnts_array
|
|
345
|
+
raw investigations data array
|
|
346
|
+
|
|
347
|
+
Returns
|
|
348
|
+
-------
|
|
349
|
+
ArrayBIGINT
|
|
350
|
+
Subtotals for columns other than the first, grouped by the first column.
|
|
351
|
+
"""
|
|
259
352
|
if not _cnts_array[:, 0].any():
|
|
260
353
|
return np.array([], int)
|
|
261
354
|
|
|
@@ -272,6 +365,20 @@ def enf_cnts_byfirmcount(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
|
272
365
|
|
|
273
366
|
|
|
274
367
|
def enf_cnts_bydelta(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
368
|
+
"""Summarize investigations data by ΔHHI.
|
|
369
|
+
|
|
370
|
+
Parameters
|
|
371
|
+
----------
|
|
372
|
+
_cnts_array
|
|
373
|
+
raw investigations data array
|
|
374
|
+
|
|
375
|
+
Returns
|
|
376
|
+
-------
|
|
377
|
+
ArrayBIGINT
|
|
378
|
+
Subtotals for columns higher than the second, grouped by the second column.
|
|
379
|
+
"""
|
|
380
|
+
if not _cnts_array[:, 1].any():
|
|
381
|
+
return np.array([], int)
|
|
275
382
|
ndim_in = 2
|
|
276
383
|
return np.vstack([
|
|
277
384
|
np.concatenate([
|
|
@@ -285,9 +392,23 @@ def enf_cnts_bydelta(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
|
285
392
|
|
|
286
393
|
|
|
287
394
|
def enf_cnts_byconczone(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
395
|
+
"""Summarize investigations data by concentration zone, as defined in the Guidelines.
|
|
396
|
+
|
|
397
|
+
Includes sub-total detail for "Moderately Concentrated" and "Unconcentrated" markets.
|
|
398
|
+
|
|
399
|
+
Parameters
|
|
400
|
+
----------
|
|
401
|
+
_cnts_array
|
|
402
|
+
raw investigations data array
|
|
403
|
+
|
|
404
|
+
Returns
|
|
405
|
+
-------
|
|
406
|
+
ArrayBIGINT
|
|
407
|
+
Subtotals range of HHI and ΔHHI, with detail
|
|
408
|
+
"""
|
|
288
409
|
if not _cnts_array[:, 0].any() or np.isnan(_cnts_array[:, 0]).all():
|
|
289
410
|
return np.array([], int)
|
|
290
|
-
# Step 1: Tag and agg. from HHI-post and
|
|
411
|
+
# Step 1: Tag and agg. from HHI-post and ΔHHI to zone triple
|
|
291
412
|
# NOTE: Although you could just map and not (partially) aggregate in this step,
|
|
292
413
|
# the mapped array is a copy, and is larger without partial aggregation, so
|
|
293
414
|
# aggregation reduces the footprint of this step in memory. Although this point
|
|
@@ -335,10 +456,13 @@ def enf_cnts_byconczone(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
|
335
456
|
# Logical-and of multiple vectors:
|
|
336
457
|
hhi_zone_test = (
|
|
337
458
|
1
|
|
338
|
-
* np.stack(
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
459
|
+
* np.stack(
|
|
460
|
+
[
|
|
461
|
+
cnts_byhhipostanddelta[:, _idx] == _val
|
|
462
|
+
for _idx, _val in enumerate(zone_val)
|
|
463
|
+
],
|
|
464
|
+
axis=1,
|
|
465
|
+
)
|
|
342
466
|
).prod(axis=1) == 1
|
|
343
467
|
|
|
344
468
|
cnts_byconczone = np.vstack((
|
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
|
)
|