mergeron 2025.739290.3__py3-none-any.whl → 2025.739290.4__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 +74 -48
- mergeron/core/__init__.py +105 -4
- mergeron/core/empirical_margin_distribution.py +100 -78
- mergeron/core/ftc_merger_investigations_data.py +309 -316
- mergeron/core/guidelines_boundaries.py +62 -121
- mergeron/core/guidelines_boundary_functions.py +207 -384
- mergeron/core/guidelines_boundary_functions_extra.py +264 -104
- mergeron/core/pseudorandom_numbers.py +76 -67
- mergeron/data/damodaran_margin_data_serialized.zip +0 -0
- mergeron/data/ftc_invdata.zip +0 -0
- mergeron/demo/visualize_empirical_margin_distribution.py +9 -7
- mergeron/gen/__init__.py +123 -161
- mergeron/gen/data_generation.py +183 -149
- mergeron/gen/data_generation_functions.py +220 -237
- mergeron/gen/enforcement_stats.py +83 -115
- mergeron/gen/upp_tests.py +118 -193
- {mergeron-2025.739290.3.dist-info → mergeron-2025.739290.4.dist-info}/METADATA +2 -3
- mergeron-2025.739290.4.dist-info/RECORD +24 -0
- {mergeron-2025.739290.3.dist-info → mergeron-2025.739290.4.dist-info}/WHEEL +1 -1
- mergeron/data/damodaran_margin_data_dict.msgpack +0 -0
- mergeron-2025.739290.3.dist-info/RECORD +0 -23
|
@@ -9,15 +9,16 @@ from collections.abc import Mapping
|
|
|
9
9
|
import numpy as np
|
|
10
10
|
from scipy.interpolate import interp1d # type: ignore
|
|
11
11
|
|
|
12
|
-
from .. import VERSION, ArrayBIGINT, this_yaml # noqa: TID252
|
|
12
|
+
from .. import VERSION, ArrayBIGINT, EnumYAMLized, this_yaml # noqa: TID252
|
|
13
13
|
from ..core import ftc_merger_investigations_data as fid # noqa: TID252
|
|
14
14
|
from . import INVResolution
|
|
15
15
|
|
|
16
16
|
__version__ = VERSION
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
@this_yaml.register_class
|
|
19
20
|
@enum.unique
|
|
20
|
-
class IndustryGroup(
|
|
21
|
+
class IndustryGroup(str, EnumYAMLized):
|
|
21
22
|
ALL = "All Markets"
|
|
22
23
|
GRO = "Grocery Markets"
|
|
23
24
|
OIL = "Oil Markets"
|
|
@@ -30,8 +31,9 @@ class IndustryGroup(enum.StrEnum):
|
|
|
30
31
|
IIC = "Industries in Common"
|
|
31
32
|
|
|
32
33
|
|
|
34
|
+
@this_yaml.register_class
|
|
33
35
|
@enum.unique
|
|
34
|
-
class OtherEvidence(
|
|
36
|
+
class OtherEvidence(str, EnumYAMLized):
|
|
35
37
|
UR = "Unrestricted on additional evidence"
|
|
36
38
|
HD = "Hot Documents Identified"
|
|
37
39
|
HN = "No Hot Documents Identified"
|
|
@@ -44,23 +46,26 @@ class OtherEvidence(enum.StrEnum):
|
|
|
44
46
|
NE = "No Entry Evidence"
|
|
45
47
|
|
|
46
48
|
|
|
49
|
+
@this_yaml.register_class
|
|
47
50
|
@enum.unique
|
|
48
|
-
class StatsGrpSelector(
|
|
51
|
+
class StatsGrpSelector(str, EnumYAMLized):
|
|
49
52
|
FC = "ByFirmCount"
|
|
50
53
|
HD = "ByHHIandDelta"
|
|
51
54
|
DL = "ByDelta"
|
|
52
55
|
ZN = "ByConcZone"
|
|
53
56
|
|
|
54
57
|
|
|
58
|
+
@this_yaml.register_class
|
|
55
59
|
@enum.unique
|
|
56
|
-
class StatsReturnSelector(
|
|
60
|
+
class StatsReturnSelector(str, EnumYAMLized):
|
|
57
61
|
CNT = "count"
|
|
58
62
|
RPT = "rate, point"
|
|
59
63
|
RIN = "rate, interval"
|
|
60
64
|
|
|
61
65
|
|
|
66
|
+
@this_yaml.register_class
|
|
62
67
|
@enum.unique
|
|
63
|
-
class SortSelector(
|
|
68
|
+
class SortSelector(str, EnumYAMLized):
|
|
64
69
|
UCH = "unchanged"
|
|
65
70
|
REV = "reversed"
|
|
66
71
|
|
|
@@ -138,17 +143,17 @@ def enf_cnts_obs_by_group(
|
|
|
138
143
|
|
|
139
144
|
match _stats_group:
|
|
140
145
|
case StatsGrpSelector.FC:
|
|
141
|
-
|
|
142
|
-
|
|
146
|
+
cnts_func = enf_cnts_byfirmcount
|
|
147
|
+
cnts_listing_func = enf_cnts_obs_byfirmcount
|
|
143
148
|
case StatsGrpSelector.DL:
|
|
144
|
-
|
|
145
|
-
|
|
149
|
+
cnts_func = enf_cnts_bydelta
|
|
150
|
+
cnts_listing_func = enf_cnts_obs_byhhianddelta
|
|
146
151
|
case StatsGrpSelector.ZN:
|
|
147
|
-
|
|
148
|
-
|
|
152
|
+
cnts_func = enf_cnts_byconczone
|
|
153
|
+
cnts_listing_func = enf_cnts_obs_byhhianddelta
|
|
149
154
|
|
|
150
|
-
return
|
|
151
|
-
|
|
155
|
+
return cnts_func(
|
|
156
|
+
cnts_listing_func(
|
|
152
157
|
_invdata_array_dict,
|
|
153
158
|
_study_period,
|
|
154
159
|
_table_ind_grp,
|
|
@@ -172,26 +177,23 @@ def enf_cnts_obs_byfirmcount(
|
|
|
172
177
|
f"Must be one of, {tuple(_data_array_dict.keys())!r}."
|
|
173
178
|
)
|
|
174
179
|
|
|
175
|
-
|
|
180
|
+
data_array_dict_sub = _data_array_dict[_data_period][fid.TABLE_TYPES[1]]
|
|
176
181
|
|
|
177
|
-
|
|
182
|
+
table_no_ = table_no_lku(data_array_dict_sub, _table_ind_group, _table_evid_cond)
|
|
178
183
|
|
|
179
|
-
|
|
184
|
+
cnts_array = data_array_dict_sub[table_no_].data_array
|
|
180
185
|
|
|
181
|
-
|
|
182
|
-
|
|
186
|
+
ndim_in = 1
|
|
187
|
+
stats_kept_indxs = []
|
|
183
188
|
match _enf_spec:
|
|
184
189
|
case INVResolution.CLRN:
|
|
185
|
-
|
|
190
|
+
stats_kept_indxs = [-1, -2]
|
|
186
191
|
case INVResolution.ENFT:
|
|
187
|
-
|
|
192
|
+
stats_kept_indxs = [-1, -3]
|
|
188
193
|
case INVResolution.BOTH:
|
|
189
|
-
|
|
194
|
+
stats_kept_indxs = [-1, -3, -2]
|
|
190
195
|
|
|
191
|
-
return np.column_stack([
|
|
192
|
-
_cnts_array[:, :_ndim_in],
|
|
193
|
-
_cnts_array[:, _stats_kept_indxs],
|
|
194
|
-
])
|
|
196
|
+
return np.column_stack([cnts_array[:, :ndim_in], cnts_array[:, stats_kept_indxs]])
|
|
195
197
|
|
|
196
198
|
|
|
197
199
|
def enf_cnts_obs_byhhianddelta(
|
|
@@ -208,26 +210,23 @@ def enf_cnts_obs_byhhianddelta(
|
|
|
208
210
|
f"Must be one of, {tuple(_data_array_dict.keys())!r}."
|
|
209
211
|
)
|
|
210
212
|
|
|
211
|
-
|
|
213
|
+
data_array_dict_sub = _data_array_dict[_data_period][fid.TABLE_TYPES[0]]
|
|
212
214
|
|
|
213
|
-
|
|
215
|
+
table_no_ = table_no_lku(data_array_dict_sub, _table_ind_group, _table_evid_cond)
|
|
214
216
|
|
|
215
|
-
|
|
217
|
+
cnts_array = data_array_dict_sub[table_no_].data_array
|
|
216
218
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
+
ndim_in = 2
|
|
220
|
+
stats_kept_indxs = []
|
|
219
221
|
match _enf_spec:
|
|
220
222
|
case INVResolution.CLRN:
|
|
221
|
-
|
|
223
|
+
stats_kept_indxs = [-1, -2]
|
|
222
224
|
case INVResolution.ENFT:
|
|
223
|
-
|
|
225
|
+
stats_kept_indxs = [-1, -3]
|
|
224
226
|
case INVResolution.BOTH:
|
|
225
|
-
|
|
227
|
+
stats_kept_indxs = [-1, -3, -2]
|
|
226
228
|
|
|
227
|
-
return np.column_stack([
|
|
228
|
-
_cnts_array[:, :_ndim_in],
|
|
229
|
-
_cnts_array[:, _stats_kept_indxs],
|
|
230
|
-
])
|
|
229
|
+
return np.column_stack([cnts_array[:, :ndim_in], cnts_array[:, stats_kept_indxs]])
|
|
231
230
|
|
|
232
231
|
|
|
233
232
|
def table_no_lku(
|
|
@@ -237,143 +236,112 @@ def table_no_lku(
|
|
|
237
236
|
/,
|
|
238
237
|
) -> str:
|
|
239
238
|
if _table_ind_group not in (
|
|
240
|
-
|
|
239
|
+
igl_ := [_data_array_dict_sub[_v].industry_group for _v in _data_array_dict_sub]
|
|
241
240
|
):
|
|
242
241
|
raise ValueError(
|
|
243
242
|
f"Invalid value for industry group, {f'"{_table_ind_group}"'}."
|
|
244
|
-
f"Must be one of {
|
|
243
|
+
f"Must be one of {igl_!r}"
|
|
245
244
|
)
|
|
246
245
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
for
|
|
246
|
+
tno_ = next(
|
|
247
|
+
t_
|
|
248
|
+
for t_ in _data_array_dict_sub
|
|
250
249
|
if all((
|
|
251
|
-
_data_array_dict_sub[
|
|
252
|
-
_data_array_dict_sub[
|
|
250
|
+
_data_array_dict_sub[t_].industry_group == _table_ind_group,
|
|
251
|
+
_data_array_dict_sub[t_].additional_evidence == _table_evid_cond,
|
|
253
252
|
))
|
|
254
253
|
)
|
|
255
254
|
|
|
256
|
-
return
|
|
255
|
+
return tno_
|
|
257
256
|
|
|
258
257
|
|
|
259
258
|
def enf_cnts_byfirmcount(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
260
|
-
|
|
259
|
+
ndim_in = 1
|
|
261
260
|
return np.vstack([
|
|
262
261
|
np.concatenate([
|
|
263
262
|
(f,),
|
|
264
|
-
np.einsum("ij->j", _cnts_array[_cnts_array[:, 0] == f][:,
|
|
263
|
+
np.einsum("ij->j", _cnts_array[_cnts_array[:, 0] == f][:, ndim_in:]),
|
|
265
264
|
])
|
|
266
265
|
for f in np.unique(_cnts_array[:, 0])
|
|
267
266
|
])
|
|
268
267
|
|
|
269
268
|
|
|
270
269
|
def enf_cnts_bydelta(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
271
|
-
|
|
270
|
+
ndim_in = 2
|
|
272
271
|
return np.vstack([
|
|
273
272
|
np.concatenate([
|
|
274
|
-
(
|
|
275
|
-
np.einsum("ij->j", _cnts_array[_cnts_array[:, 1] ==
|
|
273
|
+
(f_,),
|
|
274
|
+
np.einsum("ij->j", _cnts_array[_cnts_array[:, 1] == f_][:, ndim_in:]),
|
|
276
275
|
])
|
|
277
|
-
for
|
|
276
|
+
for f_ in HHI_DELTA_KNOTS[:-1]
|
|
278
277
|
])
|
|
279
278
|
|
|
280
279
|
|
|
281
280
|
def enf_cnts_byconczone(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
_hhi_delta_ranged = hhi_delta_ranger(_cnts_array[:, 1] / 1e4)
|
|
285
|
-
|
|
281
|
+
if not _cnts_array.any():
|
|
282
|
+
return np.array([], int)
|
|
286
283
|
# Step 1: Tag and agg. from HHI-post and Delta to zone triple
|
|
287
284
|
# NOTE: Although you could just map and not (partially) aggregate in this step,
|
|
288
285
|
# the mapped array is a copy, and is larger without partial aggregation, so
|
|
289
286
|
# aggregation reduces the footprint of this step in memory. Although this point
|
|
290
287
|
# is more relevant for generated than observed data, using the same coding pattern
|
|
291
288
|
# in both cases does make life easier
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
289
|
+
ndim_in = 2
|
|
290
|
+
nkeys_ = 3
|
|
291
|
+
cnts_byhhipostanddelta, cnts_byconczone = (
|
|
292
|
+
np.zeros(nkeys_ + _cnts_array.shape[1] - ndim_in, dtype=int) for _ in range(2)
|
|
296
293
|
)
|
|
297
|
-
|
|
294
|
+
|
|
295
|
+
# Prepare to tag clearance stats by presumption zone
|
|
296
|
+
hhi_zone_post_ranged = hhi_zone_post_ranger(_cnts_array[:, 0] / 1e4)
|
|
297
|
+
hhi_delta_ranged = hhi_delta_ranger(_cnts_array[:, 1] / 1e4)
|
|
298
298
|
for _hhi_zone_post_lim in HHI_POST_ZONE_KNOTS[:-1]:
|
|
299
|
-
|
|
299
|
+
zone_test = hhi_zone_post_ranged == _hhi_zone_post_lim
|
|
300
300
|
|
|
301
|
-
for
|
|
302
|
-
|
|
303
|
-
(
|
|
304
|
-
if
|
|
305
|
-
else (
|
|
301
|
+
for hhi_zone_delta_lim in HHI_DELTA_KNOTS[:3]:
|
|
302
|
+
delta_test = (
|
|
303
|
+
(hhi_delta_ranged >= hhi_zone_delta_lim)
|
|
304
|
+
if hhi_zone_delta_lim == HHI_DELTA_KNOTS[2]
|
|
305
|
+
else (hhi_delta_ranged == hhi_zone_delta_lim)
|
|
306
306
|
)
|
|
307
307
|
|
|
308
|
-
|
|
309
|
-
_hhi_zone_delta_lim
|
|
310
|
-
]
|
|
308
|
+
zone_val = HMG_PRESUMPTION_ZONE_MAP[_hhi_zone_post_lim][hhi_zone_delta_lim]
|
|
311
309
|
|
|
312
|
-
|
|
310
|
+
conc_test = zone_test & delta_test
|
|
313
311
|
|
|
314
|
-
|
|
315
|
-
|
|
312
|
+
cnts_byhhipostanddelta = np.vstack((
|
|
313
|
+
cnts_byhhipostanddelta,
|
|
316
314
|
np.array(
|
|
317
315
|
(
|
|
318
|
-
*
|
|
319
|
-
*np.einsum("ij->j", _cnts_array[:,
|
|
316
|
+
*zone_val,
|
|
317
|
+
*np.einsum("ij->j", _cnts_array[:, ndim_in:][conc_test]),
|
|
320
318
|
),
|
|
321
319
|
dtype=int,
|
|
322
320
|
),
|
|
323
321
|
))
|
|
324
|
-
|
|
322
|
+
cnts_byhhipostanddelta = cnts_byhhipostanddelta[1:]
|
|
325
323
|
|
|
326
|
-
for
|
|
324
|
+
for zone_val in ZONE_VALS:
|
|
327
325
|
# Logical-and of multiple vectors:
|
|
328
|
-
|
|
326
|
+
hhi_zone_test = (
|
|
329
327
|
1
|
|
330
328
|
* np.column_stack([
|
|
331
|
-
|
|
332
|
-
for _idx, _val in enumerate(
|
|
329
|
+
cnts_byhhipostanddelta[:, _idx] == _val
|
|
330
|
+
for _idx, _val in enumerate(zone_val)
|
|
333
331
|
])
|
|
334
332
|
).prod(axis=1) == 1
|
|
335
333
|
|
|
336
|
-
|
|
337
|
-
|
|
334
|
+
cnts_byconczone = np.vstack((
|
|
335
|
+
cnts_byconczone,
|
|
338
336
|
np.concatenate(
|
|
339
337
|
(
|
|
340
|
-
|
|
338
|
+
zone_val,
|
|
341
339
|
np.einsum(
|
|
342
|
-
"ij->j",
|
|
340
|
+
"ij->j", cnts_byhhipostanddelta[hhi_zone_test][:, nkeys_:]
|
|
343
341
|
),
|
|
344
342
|
),
|
|
345
343
|
dtype=int,
|
|
346
344
|
),
|
|
347
345
|
))
|
|
348
346
|
|
|
349
|
-
return
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
for _typ in (
|
|
353
|
-
IndustryGroup,
|
|
354
|
-
OtherEvidence,
|
|
355
|
-
StatsGrpSelector,
|
|
356
|
-
StatsReturnSelector,
|
|
357
|
-
SortSelector,
|
|
358
|
-
):
|
|
359
|
-
# NOTE: If additional enums are defined in this module,
|
|
360
|
-
# add themn to the list above
|
|
361
|
-
|
|
362
|
-
_, _ = (
|
|
363
|
-
this_yaml.representer.add_representer(
|
|
364
|
-
_typ,
|
|
365
|
-
lambda _r, _d: _r.represent_scalar(f"!{_d.__class__.__name__}", _d.name),
|
|
366
|
-
),
|
|
367
|
-
this_yaml.constructor.add_constructor(
|
|
368
|
-
f"!{_typ.__name__}",
|
|
369
|
-
lambda _c, _n, /: getattr(
|
|
370
|
-
globals().get(_n.tag.lstrip("!")), _c.construct_scalar(_n)
|
|
371
|
-
),
|
|
372
|
-
),
|
|
373
|
-
)
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if __name__ == "__main__":
|
|
377
|
-
print(
|
|
378
|
-
"This module provides methods to aggregate statistics on merger enforcement patterns for reporting."
|
|
379
|
-
)
|
|
347
|
+
return cnts_byconczone[1:]
|