mergeron 2025.739439.11__py3-none-any.whl → 2025.739439.13__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 +6 -6
- mergeron/core/guidelines_boundaries.py +32 -48
- mergeron/core/guidelines_boundary_functions.py +24 -32
- mergeron/core/pseudorandom_numbers.py +1 -1
- mergeron/gen/__init__.py +38 -36
- mergeron/gen/data_generation.py +25 -25
- mergeron/gen/data_generation_functions.py +82 -76
- mergeron/gen/enforcement_stats.py +17 -17
- mergeron/gen/upp_tests.py +39 -73
- mergeron/perks/guidelines_boundary_functions_extra.py +26 -29
- {mergeron-2025.739439.11.dist-info → mergeron-2025.739439.13.dist-info}/METADATA +1 -2
- mergeron-2025.739439.13.dist-info/RECORD +21 -0
- mergeron-2025.739439.11.dist-info/RECORD +0 -21
- {mergeron-2025.739439.11.dist-info → mergeron-2025.739439.13.dist-info}/WHEEL +0 -0
|
@@ -4,13 +4,12 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from typing import Literal
|
|
6
6
|
|
|
7
|
-
import numexpr as ne # type: ignore
|
|
8
7
|
import numpy as np
|
|
9
8
|
from attrs import evolve
|
|
10
9
|
from numpy.random import SeedSequence
|
|
11
10
|
|
|
12
11
|
from .. import ( # noqa: TID252
|
|
13
|
-
|
|
12
|
+
DEFAULT_REC,
|
|
14
13
|
NTHREADS,
|
|
15
14
|
VERSION,
|
|
16
15
|
ArrayDouble,
|
|
@@ -27,14 +26,14 @@ from ..core.pseudorandom_numbers import ( # noqa: TID252
|
|
|
27
26
|
from . import (
|
|
28
27
|
DEFAULT_BETA_BND_DIST_PARMS,
|
|
29
28
|
DEFAULT_FCOUNT_WTS,
|
|
30
|
-
|
|
29
|
+
MarginsData,
|
|
30
|
+
MarketSharesData,
|
|
31
|
+
MarketShareSpec,
|
|
31
32
|
PCMDistribution,
|
|
32
33
|
PCMRestriction,
|
|
33
34
|
PCMSpec,
|
|
34
|
-
|
|
35
|
+
PricesData,
|
|
35
36
|
PriceSpec,
|
|
36
|
-
ShareSampleData,
|
|
37
|
-
ShareSpec,
|
|
38
37
|
SHRDistribution,
|
|
39
38
|
SSZConstant,
|
|
40
39
|
)
|
|
@@ -42,14 +41,14 @@ from . import (
|
|
|
42
41
|
__version__ = VERSION
|
|
43
42
|
|
|
44
43
|
|
|
45
|
-
def
|
|
44
|
+
def market_share_sampler(
|
|
46
45
|
_sample_size: int,
|
|
47
|
-
_share_spec:
|
|
46
|
+
_share_spec: MarketShareSpec,
|
|
48
47
|
_fcount_rng_seed_seq: SeedSequence | None,
|
|
49
48
|
_mktshr_rng_seed_seq: SeedSequence,
|
|
50
49
|
_nthreads: int,
|
|
51
50
|
/,
|
|
52
|
-
) ->
|
|
51
|
+
) -> MarketSharesData:
|
|
53
52
|
"""Generate share data.
|
|
54
53
|
|
|
55
54
|
Parameters
|
|
@@ -65,7 +64,7 @@ def gen_share_data(
|
|
|
65
64
|
|
|
66
65
|
Returns
|
|
67
66
|
-------
|
|
68
|
-
Arrays representing shares, diversion ratios, etc. structured as a :
|
|
67
|
+
Arrays representing shares, diversion ratios, etc. structured as a :MarketSharesData:
|
|
69
68
|
|
|
70
69
|
"""
|
|
71
70
|
dist_type_mktshr, firm_count_prob_wts, dist_parms_mktshr, recapture_form = (
|
|
@@ -74,12 +73,12 @@ def gen_share_data(
|
|
|
74
73
|
)
|
|
75
74
|
|
|
76
75
|
if dist_type_mktshr == SHRDistribution.UNI:
|
|
77
|
-
mkt_share_sample =
|
|
76
|
+
mkt_share_sample = market_share_sampler_uniform(
|
|
78
77
|
_sample_size, dist_parms_mktshr, _mktshr_rng_seed_seq, _nthreads
|
|
79
78
|
)
|
|
80
79
|
|
|
81
80
|
elif dist_type_mktshr.name.startswith("DIR_"):
|
|
82
|
-
mkt_share_sample =
|
|
81
|
+
mkt_share_sample = _market_share_sampler_dirichlet_multimarket(
|
|
83
82
|
_sample_size,
|
|
84
83
|
recapture_form,
|
|
85
84
|
dist_type_mktshr,
|
|
@@ -97,9 +96,9 @@ def gen_share_data(
|
|
|
97
96
|
|
|
98
97
|
# If recapture_form == "inside-out", recalculate _aggregate_purchase_prob
|
|
99
98
|
frmshr_array = mkt_share_sample.mktshr_array[:, :2]
|
|
100
|
-
r_bar_ = _share_spec.
|
|
99
|
+
r_bar_ = _share_spec.recapture_rate or DEFAULT_REC
|
|
101
100
|
if recapture_form == RECForm.INOUT:
|
|
102
|
-
mkt_share_sample =
|
|
101
|
+
mkt_share_sample = MarketSharesData(
|
|
103
102
|
mkt_share_sample.mktshr_array,
|
|
104
103
|
mkt_share_sample.fcounts,
|
|
105
104
|
mkt_share_sample.nth_firm_share,
|
|
@@ -109,13 +108,13 @@ def gen_share_data(
|
|
|
109
108
|
return mkt_share_sample
|
|
110
109
|
|
|
111
110
|
|
|
112
|
-
def
|
|
111
|
+
def market_share_sampler_uniform(
|
|
113
112
|
_s_size: int,
|
|
114
113
|
_dist_parms_mktshr: ArrayDouble,
|
|
115
114
|
_mktshr_rng_seed_seq: SeedSequence,
|
|
116
115
|
_nthreads: int,
|
|
117
116
|
/,
|
|
118
|
-
) ->
|
|
117
|
+
) -> MarketSharesData:
|
|
119
118
|
"""Generate merging-firm shares from Uniform distribution on the 3-D simplex.
|
|
120
119
|
|
|
121
120
|
Parameters
|
|
@@ -167,12 +166,12 @@ def gen_market_shares_uniform(
|
|
|
167
166
|
nth_firm_share_.fill(np.nan)
|
|
168
167
|
aggregate_purchase_prob_.fill(np.nan)
|
|
169
168
|
|
|
170
|
-
return
|
|
169
|
+
return MarketSharesData(
|
|
171
170
|
mktshr_array_, fcounts_, nth_firm_share_, aggregate_purchase_prob_
|
|
172
171
|
)
|
|
173
172
|
|
|
174
173
|
|
|
175
|
-
def
|
|
174
|
+
def _market_share_sampler_dirichlet_multimarket(
|
|
176
175
|
_s_size: int,
|
|
177
176
|
_recapture_form: RECForm,
|
|
178
177
|
_dist_type_dir: SHRDistribution,
|
|
@@ -182,7 +181,7 @@ def _gen_market_shares_dirichlet_multimarket(
|
|
|
182
181
|
_mktshr_rng_seed_seq: SeedSequence,
|
|
183
182
|
_nthreads: int = NTHREADS,
|
|
184
183
|
/,
|
|
185
|
-
) ->
|
|
184
|
+
) -> MarketSharesData:
|
|
186
185
|
"""Dirichlet-distributed shares with multiple firm-counts.
|
|
187
186
|
|
|
188
187
|
Firm-counts may be specified as having Uniform distribution over the range
|
|
@@ -202,7 +201,7 @@ def _gen_market_shares_dirichlet_multimarket(
|
|
|
202
201
|
Whether Dirichlet is Flat or Asymmetric
|
|
203
202
|
|
|
204
203
|
_recapture_form
|
|
205
|
-
r_1 = r_2 if
|
|
204
|
+
r_1 = r_2 if RECForm.FIXED, otherwise share-proportional
|
|
206
205
|
|
|
207
206
|
_fcount_rng_seed_seq
|
|
208
207
|
seed firm count rng, for replicable results
|
|
@@ -247,7 +246,7 @@ def _gen_market_shares_dirichlet_multimarket(
|
|
|
247
246
|
|
|
248
247
|
if _dist_type_dir == SHRDistribution.DIR_COND:
|
|
249
248
|
|
|
250
|
-
def
|
|
249
|
+
def _alphas_builder(_fcv: int) -> ArrayDouble:
|
|
251
250
|
dat_ = [2.5] * 2
|
|
252
251
|
if _fcv > len(dat_):
|
|
253
252
|
dat_ += [1.0 / (_fcv - 2)] * (_fcv - 2)
|
|
@@ -255,7 +254,7 @@ def _gen_market_shares_dirichlet_multimarket(
|
|
|
255
254
|
|
|
256
255
|
else:
|
|
257
256
|
|
|
258
|
-
def
|
|
257
|
+
def _alphas_builder(_fcv: int) -> ArrayDouble:
|
|
259
258
|
return np.array(dir_alphas_full[:_fcv], float)
|
|
260
259
|
|
|
261
260
|
fcounts_ = prng(_fcount_rng_seed_seq).choice(
|
|
@@ -270,10 +269,10 @@ def _gen_market_shares_dirichlet_multimarket(
|
|
|
270
269
|
mktshr_array_ = np.empty((_s_size, fc_max), float)
|
|
271
270
|
for f_val, f_sseq in zip(fcount_keys, mktshr_seed_seq_ch, strict=True):
|
|
272
271
|
fcounts_match_rows = np.where(fcounts_ == f_val)[0]
|
|
273
|
-
dir_alphas_test =
|
|
272
|
+
dir_alphas_test = _alphas_builder(f_val)
|
|
274
273
|
|
|
275
274
|
try:
|
|
276
|
-
mktshr_sample_f =
|
|
275
|
+
mktshr_sample_f = market_share_sampler_dirichlet(
|
|
277
276
|
dir_alphas_test,
|
|
278
277
|
len(fcounts_match_rows),
|
|
279
278
|
_recapture_form,
|
|
@@ -306,19 +305,19 @@ def _gen_market_shares_dirichlet_multimarket(
|
|
|
306
305
|
)
|
|
307
306
|
)
|
|
308
307
|
|
|
309
|
-
return
|
|
308
|
+
return MarketSharesData(
|
|
310
309
|
mktshr_array_, fcounts_, nth_firm_share_, aggregate_purchase_prob_
|
|
311
310
|
)
|
|
312
311
|
|
|
313
312
|
|
|
314
|
-
def
|
|
313
|
+
def market_share_sampler_dirichlet(
|
|
315
314
|
_dir_alphas: ArrayDouble,
|
|
316
315
|
_s_size: int,
|
|
317
316
|
_recapture_form: RECForm,
|
|
318
317
|
_mktshr_rng_seed_seq: SeedSequence,
|
|
319
318
|
_nthreads: int,
|
|
320
319
|
/,
|
|
321
|
-
) ->
|
|
320
|
+
) -> MarketSharesData:
|
|
322
321
|
"""Dirichlet-distributed shares with fixed firm-count.
|
|
323
322
|
|
|
324
323
|
Parameters
|
|
@@ -389,7 +388,7 @@ def gen_market_shares_dirichlet(
|
|
|
389
388
|
aggregate_purchase_prob_ = 1 - mktshr_array[:, [-1]]
|
|
390
389
|
mktshr_array = mktshr_array[:, :-1] / aggregate_purchase_prob_
|
|
391
390
|
|
|
392
|
-
return
|
|
391
|
+
return MarketSharesData(
|
|
393
392
|
mktshr_array,
|
|
394
393
|
(mktshr_array.shape[-1] * np.ones((_s_size, 1))).astype(np.int64),
|
|
395
394
|
mktshr_array[:, [-1]],
|
|
@@ -397,9 +396,9 @@ def gen_market_shares_dirichlet(
|
|
|
397
396
|
)
|
|
398
397
|
|
|
399
398
|
|
|
400
|
-
def
|
|
399
|
+
def diversion_ratios_builder(
|
|
401
400
|
_recapture_form: RECForm,
|
|
402
|
-
|
|
401
|
+
_recapture_rate: float | None,
|
|
403
402
|
_frmshr_array: ArrayDouble,
|
|
404
403
|
_aggregate_purchase_prob: ArrayDouble,
|
|
405
404
|
/,
|
|
@@ -415,8 +414,8 @@ def gen_divr_array(
|
|
|
415
414
|
_recapture_form
|
|
416
415
|
Enum specifying Fixed (proportional), Inside-out, or Outside-in
|
|
417
416
|
|
|
418
|
-
|
|
419
|
-
If recapture is proportional or inside-out, the recapture
|
|
417
|
+
_recapture_rate
|
|
418
|
+
If recapture is proportional or inside-out, the recapture rate
|
|
420
419
|
for the firm with the smaller share.
|
|
421
420
|
|
|
422
421
|
_frmshr_array
|
|
@@ -437,23 +436,25 @@ def gen_divr_array(
|
|
|
437
436
|
Merging-firm diversion ratios for mergers in the sample.
|
|
438
437
|
|
|
439
438
|
"""
|
|
440
|
-
|
|
439
|
+
divratio_array: ArrayDouble
|
|
440
|
+
|
|
441
441
|
if _recapture_form == RECForm.FIXED:
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
442
|
+
divratio_array = (
|
|
443
|
+
(DEFAULT_REC if _recapture_rate is None else _recapture_rate)
|
|
444
|
+
* _frmshr_array[:, ::-1]
|
|
445
|
+
/ (1 - _frmshr_array)
|
|
445
446
|
)
|
|
446
447
|
|
|
447
448
|
else:
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
divr_array = ne.evaluate("purchprob_array_rev / (1 - purchprob_array)")
|
|
449
|
+
_purchase_prob = np.einsum("ij,ij->ij", _aggregate_purchase_prob, _frmshr_array)
|
|
450
|
+
divratio_array = _purchase_prob[:, ::-1] / (1 - _purchase_prob)
|
|
451
451
|
|
|
452
452
|
divr_assert_test = (
|
|
453
453
|
(np.round(np.einsum("ij->i", _frmshr_array), 15) == 1)
|
|
454
|
-
| (np.argmin(_frmshr_array, axis=1) == np.argmax(
|
|
454
|
+
| (np.argmin(_frmshr_array, axis=1) == np.argmax(divratio_array, axis=1))
|
|
455
455
|
)[:, None]
|
|
456
456
|
if not all(divr_assert_test):
|
|
457
|
+
print(_frmshr_array, divratio_array)
|
|
457
458
|
raise ValueError(
|
|
458
459
|
"{} {} {} {}".format(
|
|
459
460
|
"Data construction fails tests:",
|
|
@@ -463,10 +464,10 @@ def gen_divr_array(
|
|
|
463
464
|
)
|
|
464
465
|
)
|
|
465
466
|
|
|
466
|
-
return
|
|
467
|
+
return divratio_array
|
|
467
468
|
|
|
468
469
|
|
|
469
|
-
def
|
|
470
|
+
def prices_sampler(
|
|
470
471
|
_frmshr_array: ArrayDouble,
|
|
471
472
|
_nth_firm_share: ArrayDouble,
|
|
472
473
|
_aggregate_purchase_prob: ArrayDouble,
|
|
@@ -477,13 +478,13 @@ def gen_margin_price_data(
|
|
|
477
478
|
_pr_rng_seed_seq: SeedSequence | None,
|
|
478
479
|
_nthreads: int,
|
|
479
480
|
/,
|
|
480
|
-
) -> tuple[
|
|
481
|
+
) -> tuple[MarginsData, PricesData]:
|
|
481
482
|
"""Generate margin and price data for mergers in the sample.
|
|
482
483
|
|
|
483
484
|
Parameters
|
|
484
485
|
----------
|
|
485
486
|
_frmshr_array
|
|
486
|
-
Merging-firm shares; see :class:`mergeron.gen.
|
|
487
|
+
Merging-firm shares; see :class:`mergeron.gen.MarketShareSpec`.
|
|
487
488
|
|
|
488
489
|
_nth_firm_share
|
|
489
490
|
Share of the nth firm in the sample.
|
|
@@ -517,7 +518,7 @@ def gen_margin_price_data(
|
|
|
517
518
|
-------
|
|
518
519
|
Simulated margin- and price-data arrays for mergers in the sample.
|
|
519
520
|
"""
|
|
520
|
-
margin_data =
|
|
521
|
+
margin_data = MarginsData(
|
|
521
522
|
np.empty_like(_frmshr_array), np.ones(len(_frmshr_array)) == 0
|
|
522
523
|
)
|
|
523
524
|
|
|
@@ -551,7 +552,7 @@ def gen_margin_price_data(
|
|
|
551
552
|
# generate price and margin data
|
|
552
553
|
frmshr_array_plus = np.hstack((_frmshr_array, _nth_firm_share))
|
|
553
554
|
pcm_spec_here = evolve(_pcm_spec, pcm_restriction=PCMRestriction.IID)
|
|
554
|
-
margin_data =
|
|
555
|
+
margin_data = _margins_sampler(
|
|
555
556
|
frmshr_array_plus,
|
|
556
557
|
np.ones_like(frmshr_array_plus, np.float64),
|
|
557
558
|
_aggregate_purchase_prob,
|
|
@@ -560,37 +561,41 @@ def gen_margin_price_data(
|
|
|
560
561
|
_nthreads,
|
|
561
562
|
)
|
|
562
563
|
|
|
563
|
-
pcm_array,
|
|
564
|
-
getattr(margin_data, _f) for _f in ("pcm_array", "
|
|
564
|
+
pcm_array, mnl_test = (
|
|
565
|
+
getattr(margin_data, _f) for _f in ("pcm_array", "mnl_test")
|
|
565
566
|
)
|
|
566
567
|
|
|
567
|
-
price_array_here =
|
|
568
|
+
price_array_here = np.divide(1, 1 - pcm_array)
|
|
568
569
|
price_array = price_array_here[:, :2]
|
|
569
|
-
nth_firm_price = price_array_here[:, [-1]]
|
|
570
|
+
nth_firm_price = price_array_here[:, [-1]]
|
|
570
571
|
if _pcm_spec.pcm_restriction == PCMRestriction.MNL:
|
|
571
572
|
# Generate i.i.d. PCMs then take PCM0 and construct PCM1
|
|
572
573
|
# Regenerate MNL test
|
|
573
|
-
purchase_prob_array =
|
|
574
|
-
"_aggregate_purchase_prob
|
|
574
|
+
purchase_prob_array = np.einsum(
|
|
575
|
+
"ij,ij->ij", _aggregate_purchase_prob, _frmshr_array
|
|
575
576
|
)
|
|
576
577
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
578
|
+
pcm_array[:, 1] = np.divide(
|
|
579
|
+
np.einsum("i,i->i", pcm_array[:, 0], 1 - purchase_prob_array[:, 0]),
|
|
580
|
+
1
|
|
581
|
+
- purchase_prob_array[:, 1]
|
|
582
|
+
+ np.einsum(
|
|
583
|
+
"i,i->i",
|
|
584
|
+
pcm_array[:, 0],
|
|
585
|
+
purchase_prob_array[:, 1] - purchase_prob_array[:, 0],
|
|
586
|
+
),
|
|
583
587
|
)
|
|
584
|
-
mnl_test_array = (pcm_array[:, [1]] >= 0) & (pcm_array[:, [1]] <= 1)
|
|
585
588
|
|
|
586
|
-
|
|
589
|
+
mnl_test = (pcm_array[:, [1]] >= 0) & (pcm_array[:, [1]] <= 1)
|
|
590
|
+
|
|
591
|
+
margin_data = MarginsData(pcm_array[:, :2], mnl_test)
|
|
587
592
|
del price_array_here
|
|
588
593
|
case _:
|
|
589
594
|
raise ValueError(
|
|
590
595
|
f'Specification of price distribution, "{_price_spec.value}" is invalid.'
|
|
591
596
|
)
|
|
592
597
|
if _price_spec != PriceSpec.CSY:
|
|
593
|
-
margin_data =
|
|
598
|
+
margin_data = _margins_sampler(
|
|
594
599
|
_frmshr_array,
|
|
595
600
|
price_array,
|
|
596
601
|
_aggregate_purchase_prob,
|
|
@@ -600,8 +605,8 @@ def gen_margin_price_data(
|
|
|
600
605
|
)
|
|
601
606
|
|
|
602
607
|
# _price_array = _price_array.astype(np.float64)
|
|
603
|
-
rev_array =
|
|
604
|
-
nth_firm_rev =
|
|
608
|
+
rev_array = np.einsum("ij,ij->ij", price_array, _frmshr_array)
|
|
609
|
+
nth_firm_rev = np.einsum("ij,ij->ij", nth_firm_price, _nth_firm_share)
|
|
605
610
|
|
|
606
611
|
# Although `_test_rev_ratio_inv` is not fixed at 10%,
|
|
607
612
|
# the ratio has not changed since inception of the HSR filing test,
|
|
@@ -646,10 +651,10 @@ def gen_margin_price_data(
|
|
|
646
651
|
# under value-of-transactions test or merger triggers post-consummation review
|
|
647
652
|
hsr_filing_test |= _frmshr_array.min(axis=1) >= test_rev_ratio_inv
|
|
648
653
|
|
|
649
|
-
return margin_data,
|
|
654
|
+
return margin_data, PricesData(price_array, hsr_filing_test)
|
|
650
655
|
|
|
651
656
|
|
|
652
|
-
def
|
|
657
|
+
def _margins_sampler(
|
|
653
658
|
_frmshr_array: ArrayDouble,
|
|
654
659
|
_price_array: ArrayDouble,
|
|
655
660
|
_aggregate_purchase_prob: ArrayDouble,
|
|
@@ -657,7 +662,7 @@ def _gen_margin_data(
|
|
|
657
662
|
_pcm_rng_seed_seq: SeedSequence,
|
|
658
663
|
_nthreads: int,
|
|
659
664
|
/,
|
|
660
|
-
) ->
|
|
665
|
+
) -> MarginsData:
|
|
661
666
|
dist_type_pcm, dist_parms_pcm, pcm_restriction_ = (
|
|
662
667
|
getattr(_pcm_spec, _f) for _f in ("dist_type", "dist_parms", "pcm_restriction")
|
|
663
668
|
)
|
|
@@ -722,19 +727,20 @@ def _gen_margin_data(
|
|
|
722
727
|
# Impose FOCs from profit-maximization with MNL demand
|
|
723
728
|
purchase_prob_array = _aggregate_purchase_prob * _frmshr_array
|
|
724
729
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
730
|
+
pcm_array[:, 1] = np.divide(
|
|
731
|
+
np.einsum(
|
|
732
|
+
"i,i,i->i",
|
|
733
|
+
_price_array[:, 0],
|
|
734
|
+
pcm_array[:, 0],
|
|
735
|
+
1 - purchase_prob_array[:, 0],
|
|
736
|
+
),
|
|
737
|
+
np.einsum("i,i->i", _price_array[:, 1], 1 - purchase_prob_array[:, 1]),
|
|
731
738
|
)
|
|
732
|
-
|
|
733
|
-
mnl_test_array = (pcm_array[:, 1] >= 0) & (pcm_array[:, 1] <= 1)
|
|
739
|
+
mnl_test = (pcm_array[:, 1] >= 0) & (pcm_array[:, 1] <= 1)
|
|
734
740
|
else:
|
|
735
|
-
|
|
741
|
+
mnl_test = np.ones(len(pcm_array), dtype=bool)
|
|
736
742
|
|
|
737
|
-
return
|
|
743
|
+
return MarginsData(pcm_array, mnl_test)
|
|
738
744
|
|
|
739
745
|
|
|
740
746
|
def _beta_located(
|
|
@@ -335,12 +335,12 @@ def table_no_lku(
|
|
|
335
335
|
return tno_
|
|
336
336
|
|
|
337
337
|
|
|
338
|
-
def enf_cnts_byfirmcount(
|
|
338
|
+
def enf_cnts_byfirmcount(_raw_counts: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
339
339
|
"""Summarize investigations data by firm count.
|
|
340
340
|
|
|
341
341
|
Parameters
|
|
342
342
|
----------
|
|
343
|
-
|
|
343
|
+
_raw_counts
|
|
344
344
|
raw investigations data array
|
|
345
345
|
|
|
346
346
|
Returns
|
|
@@ -348,7 +348,7 @@ def enf_cnts_byfirmcount(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
|
348
348
|
ArrayBIGINT
|
|
349
349
|
Subtotals for columns other than the first, grouped by the first column.
|
|
350
350
|
"""
|
|
351
|
-
if not
|
|
351
|
+
if not _raw_counts[:, 0].any():
|
|
352
352
|
return np.array([], int)
|
|
353
353
|
|
|
354
354
|
ndim_in = 1
|
|
@@ -356,19 +356,19 @@ def enf_cnts_byfirmcount(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
|
356
356
|
np.concatenate([
|
|
357
357
|
(_i,),
|
|
358
358
|
np.einsum(
|
|
359
|
-
"ij->j",
|
|
359
|
+
"ij->j", _raw_counts[_raw_counts[:, 0] == _i][:, ndim_in:], dtype=int
|
|
360
360
|
),
|
|
361
361
|
])
|
|
362
|
-
for _i in np.unique(
|
|
362
|
+
for _i in np.unique(_raw_counts[:, 0])
|
|
363
363
|
])
|
|
364
364
|
|
|
365
365
|
|
|
366
|
-
def enf_cnts_bydelta(
|
|
366
|
+
def enf_cnts_bydelta(_raw_counts: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
367
367
|
"""Summarize investigations data by ΔHHI.
|
|
368
368
|
|
|
369
369
|
Parameters
|
|
370
370
|
----------
|
|
371
|
-
|
|
371
|
+
_raw_counts
|
|
372
372
|
raw investigations data array
|
|
373
373
|
|
|
374
374
|
Returns
|
|
@@ -376,28 +376,28 @@ def enf_cnts_bydelta(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
|
376
376
|
ArrayBIGINT
|
|
377
377
|
Subtotals for columns higher than the second, grouped by the second column.
|
|
378
378
|
"""
|
|
379
|
-
if not
|
|
379
|
+
if not _raw_counts[:, 1].any():
|
|
380
380
|
return np.array([], int)
|
|
381
381
|
ndim_in = 2
|
|
382
382
|
return np.vstack([
|
|
383
383
|
np.concatenate([
|
|
384
384
|
(_k,),
|
|
385
385
|
np.einsum(
|
|
386
|
-
"ij->j",
|
|
386
|
+
"ij->j", _raw_counts[_raw_counts[:, 1] == _k][:, ndim_in:], dtype=int
|
|
387
387
|
),
|
|
388
388
|
])
|
|
389
389
|
for _k in HHI_DELTA_KNOTS[:-1]
|
|
390
390
|
])
|
|
391
391
|
|
|
392
392
|
|
|
393
|
-
def enf_cnts_byconczone(
|
|
393
|
+
def enf_cnts_byconczone(_raw_counts: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
394
394
|
"""Summarize investigations data by concentration zone, as defined in the Guidelines.
|
|
395
395
|
|
|
396
396
|
Includes sub-total detail for "Moderately Concentrated" and "Unconcentrated" markets.
|
|
397
397
|
|
|
398
398
|
Parameters
|
|
399
399
|
----------
|
|
400
|
-
|
|
400
|
+
_raw_counts
|
|
401
401
|
raw investigations data array
|
|
402
402
|
|
|
403
403
|
Returns
|
|
@@ -405,7 +405,7 @@ def enf_cnts_byconczone(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
|
405
405
|
ArrayBIGINT
|
|
406
406
|
Subtotals range of HHI and ΔHHI, with detail
|
|
407
407
|
"""
|
|
408
|
-
if not
|
|
408
|
+
if not _raw_counts[:, 0].any() or np.isnan(_raw_counts[:, 0]).all():
|
|
409
409
|
return np.array([], int)
|
|
410
410
|
# Step 1: Tag and agg. from HHI-post and ΔHHI to zone triple
|
|
411
411
|
# NOTE: Although you could just map and not (partially) aggregate in this step,
|
|
@@ -416,15 +416,15 @@ def enf_cnts_byconczone(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
|
416
416
|
_ndim_in = 2
|
|
417
417
|
_nkeys = 3
|
|
418
418
|
cnts_byhhipostanddelta: ArrayBIGINT = np.zeros(
|
|
419
|
-
(1, _nkeys +
|
|
419
|
+
(1, _nkeys + _raw_counts.shape[1] - _ndim_in), dtype=int
|
|
420
420
|
)
|
|
421
421
|
cnts_byconczone: ArrayBIGINT = np.zeros(
|
|
422
|
-
(1, _nkeys +
|
|
422
|
+
(1, _nkeys + _raw_counts.shape[1] - _ndim_in), dtype=int
|
|
423
423
|
)
|
|
424
424
|
|
|
425
425
|
# Prepare to tag clearance stats by presumption zone
|
|
426
|
-
hhi_zone_post_ranged = hhi_zone_post_ranger(
|
|
427
|
-
hhi_delta_ranged = hhi_delta_ranger(
|
|
426
|
+
hhi_zone_post_ranged = hhi_zone_post_ranger(_raw_counts[:, 0] / 1e4)
|
|
427
|
+
hhi_delta_ranged = hhi_delta_ranger(_raw_counts[:, 1] / 1e4)
|
|
428
428
|
for _hhi_zone_post_lim in HHI_POST_ZONE_KNOTS[:-1]:
|
|
429
429
|
zone_test = hhi_zone_post_ranged == _hhi_zone_post_lim
|
|
430
430
|
|
|
@@ -445,7 +445,7 @@ def enf_cnts_byconczone(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
|
445
445
|
(
|
|
446
446
|
*zone_val,
|
|
447
447
|
*np.einsum(
|
|
448
|
-
"ij->j",
|
|
448
|
+
"ij->j", _raw_counts[:, _ndim_in:][conc_test], dtype=int
|
|
449
449
|
),
|
|
450
450
|
),
|
|
451
451
|
dtype=int,
|