mergeron 2024.739099.0__tar.gz → 2024.739099.2__tar.gz

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.

Files changed (32) hide show
  1. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/PKG-INFO +1 -1
  2. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/pyproject.toml +1 -1
  3. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/__init__.py +15 -1
  4. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/core/damodaran_margin_data.py +3 -4
  5. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/core/ftc_merger_investigations_data.py +8 -11
  6. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/core/guidelines_boundaries.py +3 -4
  7. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/core/guidelines_boundary_functions.py +8 -9
  8. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/core/guidelines_boundary_functions_extra.py +2 -3
  9. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/core/pseudorandom_numbers.py +5 -6
  10. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/gen/__init__.py +38 -32
  11. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/gen/_data_generation_functions.py +22 -28
  12. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/gen/data_generation.py +5 -6
  13. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/gen/enforcement_stats.py +125 -15
  14. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/gen/upp_tests.py +22 -20
  15. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/README.rst +0 -0
  16. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/License.txt +0 -0
  17. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/core/__init__.py +0 -0
  18. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/data/__init__.py +0 -0
  19. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/data/damodaran_margin_data.xls +0 -0
  20. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/data/damodaran_margin_data_dict.msgpack +0 -0
  21. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/data/ftc_invdata.msgpack +0 -0
  22. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/data/jinja2_LaTeX_templates/clrrate_cis_summary_table_template.tex.jinja2 +0 -0
  23. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/data/jinja2_LaTeX_templates/ftcinvdata_byhhianddelta_table_template.tex.jinja2 +0 -0
  24. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/data/jinja2_LaTeX_templates/ftcinvdata_summary_table_template.tex.jinja2 +0 -0
  25. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/data/jinja2_LaTeX_templates/ftcinvdata_summarypaired_table_template.tex.jinja2 +0 -0
  26. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/data/jinja2_LaTeX_templates/mergeron.cls +0 -0
  27. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/data/jinja2_LaTeX_templates/mergeron_table_collection_template.tex.jinja2 +0 -0
  28. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/data/jinja2_LaTeX_templates/setup_tikz_tables.tex +0 -0
  29. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/demo/__init__.py +0 -0
  30. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/demo/visualize_empirical_margin_distribution.py +0 -0
  31. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/gen/market_sample.py +0 -0
  32. {mergeron-2024.739099.0 → mergeron-2024.739099.2}/src/mergeron/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mergeron
3
- Version: 2024.739099.0
3
+ Version: 2024.739099.2
4
4
  Summary: Merger Policy Analysis using Python
5
5
  License: MIT
6
6
  Keywords: merger policy analysis,merger guidelines,merger screening,policy presumptions,concentration standards,upward pricing pressure,GUPPI
@@ -13,7 +13,7 @@ keywords = [
13
13
  "upward pricing pressure",
14
14
  "GUPPI",
15
15
  ]
16
- version = "2024.739099.0"
16
+ version = "2024.739099.2"
17
17
 
18
18
  # Classifiers list: https://pypi.org/classifiers/
19
19
  classifiers = [
@@ -2,12 +2,14 @@ from __future__ import annotations
2
2
 
3
3
  import enum
4
4
  from pathlib import Path
5
+ from typing import TypeAlias, TypeVar
5
6
 
6
7
  import numpy as np
8
+ from numpy.typing import NBitBase, NDArray
7
9
 
8
10
  _PKG_NAME: str = Path(__file__).parent.stem
9
11
 
10
- VERSION = "2024.739099.0"
12
+ VERSION = "2024.739099.2"
11
13
 
12
14
  __version__ = VERSION
13
15
 
@@ -24,6 +26,18 @@ if not DATA_DIR.is_dir():
24
26
  np.set_printoptions(precision=18)
25
27
 
26
28
 
29
+ TI = TypeVar("TI", bound=NBitBase)
30
+ ArrayINT = NDArray[np.integer[TI]]
31
+ TF = TypeVar("TF", bound=NBitBase)
32
+ ArrayFloat = NDArray[np.floating[TF]]
33
+
34
+
35
+ ArrayBoolean: TypeAlias = NDArray[np.bool_]
36
+
37
+ ArrayDouble: TypeAlias = NDArray[np.float64]
38
+ ArrayBIGINT: TypeAlias = NDArray[np.int64]
39
+
40
+
27
41
  @enum.unique
28
42
  class RECConstants(enum.StrEnum):
29
43
  """Recapture rate - derivation methods."""
@@ -43,11 +43,10 @@ import msgpack # type:ignore
43
43
  import numpy as np
44
44
  import urllib3
45
45
  from numpy.random import PCG64DXSM, Generator, SeedSequence
46
- from numpy.typing import NDArray
47
46
  from scipy import stats # type: ignore
48
47
  from xlrd import open_workbook # type: ignore
49
48
 
50
- from .. import _PKG_NAME, DATA_DIR, VERSION # noqa: TID252
49
+ from .. import _PKG_NAME, DATA_DIR, VERSION, ArrayDouble # noqa: TID252
51
50
 
52
51
  __version__ = VERSION
53
52
 
@@ -137,7 +136,7 @@ def mgn_data_getter( # noqa: PLR0912
137
136
 
138
137
  def mgn_data_builder(
139
138
  _mgn_tbl_dict: Mapping[str, Mapping[str, float | int]] | None = None, /
140
- ) -> tuple[NDArray[np.float64], NDArray[np.float64], NDArray[np.float64]]:
139
+ ) -> tuple[ArrayDouble, ArrayDouble, ArrayDouble]:
141
140
  if _mgn_tbl_dict is None:
142
141
  _mgn_tbl_dict = mgn_data_getter()
143
142
 
@@ -190,7 +189,7 @@ def mgn_data_resampler(
190
189
  /,
191
190
  *,
192
191
  seed_sequence: SeedSequence | None = None,
193
- ) -> NDArray[np.float64]:
192
+ ) -> ArrayDouble:
194
193
  """
195
194
  Generate draws from the empirical distribution bassed on Prof. Damodaran's margin data.
196
195
 
@@ -23,9 +23,8 @@ import re2 as re # type: ignore
23
23
  import urllib3
24
24
  from bs4 import BeautifulSoup
25
25
  from numpy.testing import assert_array_equal
26
- from numpy.typing import NDArray
27
26
 
28
- from .. import _PKG_NAME, DATA_DIR, VERSION # noqa: TID252
27
+ from .. import _PKG_NAME, DATA_DIR, VERSION, ArrayBIGINT # noqa: TID252
29
28
 
30
29
  __version__ = VERSION
31
30
 
@@ -93,7 +92,7 @@ CNT_FCOUNT_DICT = {
93
92
  class INVTableData(NamedTuple):
94
93
  industry_group: str
95
94
  additional_evidence: str
96
- data_array: NDArray[np.int64]
95
+ data_array: ArrayBIGINT
97
96
 
98
97
 
99
98
  INVData: TypeAlias = Mapping[str, dict[str, dict[str, INVTableData]]]
@@ -585,12 +584,12 @@ def _identify_table_type(_tnstr: str = CONC_TABLE_ALL, /) -> tuple[str, int, str
585
584
 
586
585
  def _process_table_blks_conc_type(
587
586
  _table_blocks: Sequence[Sequence[str]], /
588
- ) -> NDArray[np.int64]:
587
+ ) -> ArrayBIGINT:
589
588
  _conc_row_pat = re.compile(r"((?:0|\d,\d{3}) (?:- \d+,\d{3}|\+)|TOTAL)")
590
589
 
591
590
  _col_titles_array = tuple(CONC_DELTA_DICT.values())
592
- _col_totals: NDArray[np.int64] = np.zeros(len(_col_titles_array), np.int64)
593
- _invdata_array: NDArray[np.int64] = np.array(None)
591
+ _col_totals: ArrayBIGINT = np.zeros(len(_col_titles_array), np.int64)
592
+ _invdata_array: ArrayBIGINT = np.array(None)
594
593
 
595
594
  for _tbl_blk in _table_blocks:
596
595
  if _conc_row_pat.match(_blk_str := _tbl_blk[-3]):
@@ -642,13 +641,11 @@ def _process_table_blks_conc_type(
642
641
 
643
642
  def _process_table_blks_cnt_type(
644
643
  _table_blocks: Sequence[Sequence[str]], /
645
- ) -> NDArray[np.int64]:
644
+ ) -> ArrayBIGINT:
646
645
  _cnt_row_pat = re.compile(r"(\d+ (?:to \d+|\+)|TOTAL)")
647
646
 
648
- _invdata_array: NDArray[np.int64] = np.array(None)
649
- _col_totals: NDArray[np.int64] = np.zeros(
650
- 3, np.int64
651
- ) # "enforced", "closed", "total"
647
+ _invdata_array: ArrayBIGINT = np.array(None)
648
+ _col_totals: ArrayBIGINT = np.zeros(3, np.int64) # "enforced", "closed", "total"
652
649
 
653
650
  for _tbl_blk in _table_blocks:
654
651
  if _cnt_row_pat.match(_blk_str := _tbl_blk[-3]):
@@ -12,9 +12,8 @@ from typing import Literal, TypeAlias
12
12
  import numpy as np
13
13
  from attrs import Attribute, field, frozen, validators
14
14
  from mpmath import mp, mpf # type: ignore
15
- from numpy.typing import NDArray
16
15
 
17
- from .. import VERSION, RECConstants, UPPAggrSelector # noqa: TID252
16
+ from .. import VERSION, ArrayDouble, RECConstants, UPPAggrSelector # noqa: TID252
18
17
  from . import guidelines_boundary_functions as gbfn
19
18
 
20
19
  __version__ = VERSION
@@ -191,7 +190,7 @@ class ConcentrationBoundary:
191
190
  validator=(validators.instance_of(str), _concentration_measure_name_validator),
192
191
  )
193
192
 
194
- coordinates: NDArray[np.float64] = field(init=False, kw_only=True)
193
+ coordinates: ArrayDouble = field(init=False, kw_only=True)
195
194
  """Market-share pairs as Cartesian coordinates of points on the concentration boundary."""
196
195
 
197
196
  area: float = field(init=False, kw_only=True)
@@ -321,7 +320,7 @@ class DiversionRatioBoundary:
321
320
 
322
321
  """
323
322
 
324
- coordinates: NDArray[np.float64] = field(init=False, kw_only=True)
323
+ coordinates: ArrayDouble = field(init=False, kw_only=True)
325
324
  """Market-share pairs as Cartesian coordinates of points on the diversion ratio boundary."""
326
325
 
327
326
  area: float = field(init=False, kw_only=True)
@@ -4,9 +4,8 @@ from typing import Any, Literal, TypedDict
4
4
 
5
5
  import numpy as np
6
6
  from mpmath import mp, mpf # type: ignore
7
- from numpy.typing import NDArray
8
7
 
9
- from .. import VERSION # noqa: TID252
8
+ from .. import VERSION, ArrayBIGINT, ArrayDouble # noqa: TID252
10
9
 
11
10
  __version__ = VERSION
12
11
 
@@ -27,7 +26,7 @@ class ShareRatioBoundaryKeywords(TypedDict, total=False):
27
26
  class GuidelinesBoundary:
28
27
  """Output of a Guidelines boundary function."""
29
28
 
30
- coordinates: NDArray[np.float64]
29
+ coordinates: ArrayDouble
31
30
  """Market-share pairs as Cartesian coordinates of points on the boundary."""
32
31
 
33
32
  area: float
@@ -668,7 +667,7 @@ def shrratio_boundary_max(
668
667
  """
669
668
 
670
669
  # _r_val is not needed for max boundary, but is specified for consistency
671
- # of function call with other shrratio_mgnsym_boundary functions
670
+ # of function call with other share-ratio boundary functions
672
671
  del _r_val
673
672
  _delta_star = mpf(f"{_delta_star}")
674
673
  _s_intcpt = _delta_star
@@ -718,11 +717,11 @@ def _shrratio_boundary_intcpt(
718
717
 
719
718
 
720
719
  def lerp(
721
- _x1: int | float | mpf | NDArray[np.float64 | np.int64] = 3,
722
- _x2: int | float | mpf | NDArray[np.float64 | np.int64] = 1,
720
+ _x1: int | float | mpf | ArrayDouble | ArrayBIGINT = 3,
721
+ _x2: int | float | mpf | ArrayDouble | ArrayBIGINT = 1,
723
722
  _r: float | mpf = 0.25,
724
723
  /,
725
- ) -> float | mpf | NDArray[np.float64]:
724
+ ) -> float | mpf | ArrayDouble:
726
725
  """
727
726
  From the function of the same name in the C++ standard [2]_
728
727
 
@@ -884,9 +883,9 @@ def boundary_plot(*, mktshares_plot_flag: bool = True) -> tuple[Any, ...]:
884
883
  mktshares_plot_flag: bool = False,
885
884
  mktshares_axlbls_flag: bool = False,
886
885
  ) -> mpa.Axes:
887
- # Set the width of axis gridlines, and tick marks:
886
+ # Set the width of axis grid lines, and tick marks:
888
887
  # both axes, both major and minor ticks
889
- # Frame, grid, and facecolor
888
+ # Frame, grid, and face color
890
889
  for _spos0 in "left", "bottom":
891
890
  _ax1.spines[_spos0].set_linewidth(0.5)
892
891
  _ax1.spines[_spos0].set_zorder(5)
@@ -13,11 +13,10 @@ from typing import Literal
13
13
 
14
14
  import numpy as np
15
15
  from mpmath import mp, mpf # type: ignore
16
- from numpy.typing import NDArray
17
16
  from scipy.spatial.distance import minkowski as distance_function # type: ignore
18
17
  from sympy import lambdify, simplify, solve, symbols # type: ignore
19
18
 
20
- from .. import VERSION # noqa: TID252
19
+ from .. import VERSION, ArrayDouble # noqa: TID252
21
20
  from .guidelines_boundary_functions import (
22
21
  GuidelinesBoundary,
23
22
  _shrratio_boundary_intcpt,
@@ -33,7 +32,7 @@ mp.trap_complex = True
33
32
 
34
33
  @dataclass(slots=True, frozen=True)
35
34
  class GuidelinesBoundaryCallable:
36
- boundary_function: Callable[[NDArray[np.float64]], NDArray[np.float64]]
35
+ boundary_function: Callable[[ArrayDouble], ArrayDouble]
37
36
  area: float
38
37
  s_naught: float = 0
39
38
 
@@ -13,9 +13,8 @@ from typing import Literal
13
13
 
14
14
  import numpy as np
15
15
  from numpy.random import PCG64DXSM, Generator, SeedSequence
16
- from numpy.typing import NDArray
17
16
 
18
- from .. import VERSION # noqa: TID252
17
+ from .. import VERSION, ArrayDouble # noqa: TID252
19
18
 
20
19
  __version__ = VERSION
21
20
 
@@ -134,13 +133,13 @@ class MultithreadedRNG:
134
133
 
135
134
  def __init__(
136
135
  self,
137
- _out_array: NDArray[np.float64],
136
+ _out_array: ArrayDouble,
138
137
  /,
139
138
  *,
140
139
  dist_type: Literal[
141
140
  "Beta", "Dirichlet", "Gaussian", "Normal", "Random", "Uniform"
142
141
  ] = "Uniform",
143
- dist_parms: NDArray[np.float64] | None = DIST_PARMS_DEFAULT,
142
+ dist_parms: ArrayDouble | None = DIST_PARMS_DEFAULT,
144
143
  seed_sequence: SeedSequence | None = None,
145
144
  nthreads: int = NTHREADS,
146
145
  ):
@@ -206,8 +205,8 @@ class MultithreadedRNG:
206
205
  def _fill(
207
206
  _rng: np.random.Generator,
208
207
  _dist_type: str,
209
- _dist_parms: NDArray[np.float64],
210
- _out: NDArray[np.float64],
208
+ _dist_parms: ArrayDouble,
209
+ _out: ArrayDouble,
211
210
  _first: int,
212
211
  _last: int,
213
212
  /,
@@ -1,5 +1,5 @@
1
1
  """
2
- Defines constants and containers for industry data generation and testing
2
+ Defines constants and containers for industry data generation and testing.
3
3
 
4
4
  """
5
5
 
@@ -11,9 +11,15 @@ from typing import ClassVar, Protocol
11
11
 
12
12
  import numpy as np
13
13
  from attrs import Attribute, cmp_using, define, field, frozen, validators
14
- from numpy.typing import NDArray
15
14
 
16
- from .. import VERSION, RECConstants, UPPAggrSelector # noqa: TID252
15
+ from .. import ( # noqa: TID252
16
+ VERSION,
17
+ ArrayBIGINT,
18
+ ArrayBoolean,
19
+ ArrayDouble,
20
+ RECConstants,
21
+ UPPAggrSelector,
22
+ )
17
23
  from ..core.pseudorandom_numbers import DIST_PARMS_DEFAULT # noqa: TID252
18
24
 
19
25
  __version__ = VERSION
@@ -128,7 +134,7 @@ class ShareSpec:
128
134
  dist_type: SHRConstants
129
135
  """See :class:`SHRConstants`"""
130
136
 
131
- dist_parms: NDArray[np.float64] | None = field(
137
+ dist_parms: ArrayDouble | None = field(
132
138
  default=None, eq=cmp_using(eq=np.array_equal)
133
139
  )
134
140
  """Parameters for tailoring market-share distribution
@@ -139,9 +145,9 @@ class ShareSpec:
139
145
  type of Dirichlet-distribution specified.
140
146
 
141
147
  """
142
- firm_counts_weights: NDArray[np.float64 | np.int64] | None = field(
143
- default=None, eq=cmp_using(eq=np.array_equal)
144
- )
148
+ firm_counts_weights: (
149
+ ArrayDouble | ArrayBIGINT | ArrayDouble | ArrayBIGINT | None
150
+ ) = field(default=None, eq=cmp_using(eq=np.array_equal))
145
151
  """Relative or absolute frequencies of firm counts
146
152
 
147
153
 
@@ -193,7 +199,7 @@ class PCMSpec:
193
199
  dist_type: PCMConstants
194
200
  """See :class:`PCMConstants`"""
195
201
 
196
- dist_parms: NDArray[np.float64] | None
202
+ dist_parms: ArrayDouble | None
197
203
  """Parameter specification for tailoring PCM distribution
198
204
 
199
205
  For Uniform distribution, bounds of the distribution; defaults to `(0, 1)`;
@@ -356,39 +362,39 @@ class MarketSpec:
356
362
  class MarketDataSample:
357
363
  """Container for generated markets data sample."""
358
364
 
359
- frmshr_array: NDArray[np.float64]
365
+ frmshr_array: ArrayDouble
360
366
  """Merging-firm shares (with two merging firms)"""
361
367
 
362
- pcm_array: NDArray[np.float64]
368
+ pcm_array: ArrayDouble
363
369
  """Merging-firms' prices (normalized to 1, in default specification)"""
364
370
 
365
- price_array: NDArray[np.float64]
371
+ price_array: ArrayDouble
366
372
  """Merging-firms' price-cost margins (PCM)"""
367
373
 
368
- fcounts: NDArray[np.int64]
374
+ fcounts: ArrayBIGINT
369
375
  """Number of firms in market"""
370
376
 
371
- aggregate_purchase_prob: NDArray[np.float64]
377
+ aggregate_purchase_prob: ArrayDouble
372
378
  """
373
379
  One (1) minus probability that the outside good is chosen
374
380
 
375
381
  Converts market shares to choice probabilities by multiplication.
376
382
  """
377
383
 
378
- nth_firm_share: NDArray[np.float64]
384
+ nth_firm_share: ArrayDouble
379
385
  """Market-share of n-th firm
380
386
 
381
387
  Relevant for testing for draws the do or
382
388
  do not meet HSR filing thresholds.
383
389
  """
384
390
 
385
- divr_array: NDArray[np.float64]
391
+ divr_array: ArrayDouble
386
392
  """Diversion ratio between the merging firms"""
387
393
 
388
- hhi_post: NDArray[np.float64]
394
+ hhi_post: ArrayDouble
389
395
  """Post-merger change in Herfindahl-Hirschmann Index (HHI)"""
390
396
 
391
- hhi_delta: NDArray[np.float64]
397
+ hhi_delta: ArrayDouble
392
398
  """Change in HHI from combination of merging firms"""
393
399
 
394
400
 
@@ -400,16 +406,16 @@ class ShareDataSample:
400
406
  and aggregate purchase probability.
401
407
  """
402
408
 
403
- mktshr_array: NDArray[np.float64]
409
+ mktshr_array: ArrayDouble
404
410
  """All-firm shares (with two merging firms)"""
405
411
 
406
- fcounts: NDArray[np.int64]
412
+ fcounts: ArrayBIGINT
407
413
  """All-firm-count for each draw"""
408
414
 
409
- nth_firm_share: NDArray[np.float64]
415
+ nth_firm_share: ArrayDouble
410
416
  """Market-share of n-th firm"""
411
417
 
412
- aggregate_purchase_prob: NDArray[np.float64]
418
+ aggregate_purchase_prob: ArrayDouble
413
419
  """Converts market shares to choice probabilities by multiplication."""
414
420
 
415
421
 
@@ -417,10 +423,10 @@ class ShareDataSample:
417
423
  class PriceDataSample:
418
424
  """Container for generated price array, and related."""
419
425
 
420
- price_array: NDArray[np.float64]
426
+ price_array: ArrayDouble
421
427
  """Merging-firms' prices"""
422
428
 
423
- hsr_filing_test: NDArray[np.bool_]
429
+ hsr_filing_test: ArrayBoolean
424
430
  """Flags draws as meeting HSR filing thresholds or not"""
425
431
 
426
432
 
@@ -428,10 +434,10 @@ class PriceDataSample:
428
434
  class MarginDataSample:
429
435
  """Container for generated margin array and related MNL test array."""
430
436
 
431
- pcm_array: NDArray[np.float64]
437
+ pcm_array: ArrayDouble
432
438
  """Merging-firms' PCMs"""
433
439
 
434
- mnl_test_array: NDArray[np.bool_]
440
+ mnl_test_array: ArrayBoolean
435
441
  """Flags infeasible observations as False and rest as True
436
442
 
437
443
  Applying restrictions from Bertrand-Nash oligopoly
@@ -476,18 +482,18 @@ class UPPTestsRaw:
476
482
  :func:`enforcement_stats.gen_upp_arrays`.
477
483
  """
478
484
 
479
- guppi_test_simple: NDArray[np.bool_]
485
+ guppi_test_simple: ArrayBoolean
480
486
  """True if GUPPI estimate meets criterion"""
481
487
 
482
- guppi_test_compound: NDArray[np.bool_]
488
+ guppi_test_compound: ArrayBoolean
483
489
  """True if both GUPPI estimate and diversion ratio estimate
484
490
  meet criterion
485
491
  """
486
492
 
487
- cmcr_test: NDArray[np.bool_]
493
+ cmcr_test: ArrayBoolean
488
494
  """True if CMCR estimate meets criterion"""
489
495
 
490
- ipr_test: NDArray[np.bool_]
496
+ ipr_test: ArrayBoolean
491
497
  """True if IPR (partial price-simulation) estimate meets criterion"""
492
498
 
493
499
 
@@ -498,9 +504,9 @@ class UPPTestsCounts:
498
504
  Resolution may be either :attr:`INVResolution.ENFT` or :attr:`INVResolution.CLRN`.
499
505
  """
500
506
 
501
- by_firm_count: NDArray[np.int64]
502
- by_delta: NDArray[np.int64]
503
- by_conczone: NDArray[np.int64]
507
+ by_firm_count: ArrayBIGINT
508
+ by_delta: ArrayBIGINT
509
+ by_conczone: ArrayBIGINT
504
510
  """Zones are "unoncentrated", "moderately concentrated", and "highly concentrated"
505
511
  """
506
512
 
@@ -9,9 +9,8 @@ from typing import Literal
9
9
  import numpy as np
10
10
  from attrs import evolve
11
11
  from numpy.random import SeedSequence
12
- from numpy.typing import NDArray
13
12
 
14
- from .. import VERSION, RECConstants # noqa: TID252
13
+ from .. import VERSION, ArrayBIGINT, ArrayDouble, RECConstants # noqa: TID252
15
14
  from ..core.damodaran_margin_data import mgn_data_resampler # noqa: TID252
16
15
  from ..core.pseudorandom_numbers import ( # noqa: TID252
17
16
  DIST_PARMS_DEFAULT,
@@ -112,7 +111,7 @@ def _gen_share_data(
112
111
 
113
112
  def _gen_market_shares_uniform(
114
113
  _s_size: int = 10**6,
115
- _dist_parms_mktshr: NDArray[np.float64] | None = DIST_PARMS_DEFAULT,
114
+ _dist_parms_mktshr: ArrayDouble | None = DIST_PARMS_DEFAULT,
116
115
  _mktshr_rng_seed_seq: SeedSequence | None = None,
117
116
  _nthreads: int = 16,
118
117
  /,
@@ -137,7 +136,7 @@ def _gen_market_shares_uniform(
137
136
  """
138
137
 
139
138
  _frmshr_array = np.empty((_s_size, 2), dtype=np.float64)
140
- _dist_parms_mktshr: NDArray[np.float64] = (
139
+ _dist_parms_mktshr: ArrayDouble = (
141
140
  DIST_PARMS_DEFAULT if _dist_parms_mktshr is None else _dist_parms_mktshr
142
141
  )
143
142
  _mrng = MultithreadedRNG(
@@ -149,7 +148,7 @@ def _gen_market_shares_uniform(
149
148
  )
150
149
  _mrng.fill()
151
150
  # Convert draws on U[0, 1] to Uniformly-distributed draws on simplex, s_1 + s_2 <= 1
152
- _frmshr_array = np.sort(_frmshr_array, axis=1)
151
+ _frmshr_array.sort(axis=1)
153
152
  _frmshr_array = np.column_stack((
154
153
  _frmshr_array[:, 0],
155
154
  _frmshr_array[:, 1] - _frmshr_array[:, 0],
@@ -163,7 +162,7 @@ def _gen_market_shares_uniform(
163
162
  _frmshr_array, ((0, 0), (0, 1)), "constant", constant_values=np.nan
164
163
  )
165
164
 
166
- _fcounts: NDArray[np.int64] = np.ones((_s_size, 1), np.int64) * np.nan # type: ignore
165
+ _fcounts: ArrayBIGINT = np.ones((_s_size, 1), np.int64) * np.nan # type: ignore
167
166
  _nth_firm_share, _aggregate_purchase_prob = (
168
167
  np.nan * np.ones((_s_size, 1), np.float64) for _ in range(2)
169
168
  )
@@ -177,8 +176,8 @@ def _gen_market_shares_dirichlet_multisample(
177
176
  _s_size: int = 10**6,
178
177
  _recapture_form: RECConstants = RECConstants.INOUT,
179
178
  _dist_type_dir: SHRConstants = SHRConstants.DIR_FLAT,
180
- _dist_parms_dir: NDArray[np.float64] | None = None,
181
- _firm_count_wts: NDArray[np.float64] | None = None,
179
+ _dist_parms_dir: ArrayDouble | None = None,
180
+ _firm_count_wts: ArrayDouble | None = None,
182
181
  _fcount_rng_seed_seq: SeedSequence | None = None,
183
182
  _mktshr_rng_seed_seq: SeedSequence | None = None,
184
183
  _nthreads: int = 16,
@@ -216,7 +215,7 @@ def _gen_market_shares_dirichlet_multisample(
216
215
 
217
216
  """
218
217
 
219
- _firm_count_wts: NDArray[np.float64] = (
218
+ _firm_count_wts: ArrayDouble = (
220
219
  FCOUNT_WTS_DEFAULT if _firm_count_wts is None else _firm_count_wts
221
220
  )
222
221
 
@@ -243,7 +242,7 @@ def _gen_market_shares_dirichlet_multisample(
243
242
 
244
243
  if _dist_type_dir == SHRConstants.DIR_COND:
245
244
 
246
- def _gen_dir_alphas(_fcv: int) -> NDArray[np.float64]:
245
+ def _gen_dir_alphas(_fcv: int) -> ArrayDouble:
247
246
  _dat = [2.5] * 2
248
247
  if _fcv > len(_dat):
249
248
  _dat += [1.0 / (_fcv - 2)] * (_fcv - 2)
@@ -251,7 +250,7 @@ def _gen_market_shares_dirichlet_multisample(
251
250
 
252
251
  else:
253
252
 
254
- def _gen_dir_alphas(_fcv: int) -> NDArray[np.float64]:
253
+ def _gen_dir_alphas(_fcv: int) -> ArrayDouble:
255
254
  return np.array(_dir_alphas_full[:_fcv], dtype=np.float64) # type: ignore
256
255
 
257
256
  _fcounts = prng(_fcount_rng_seed_seq).choice(
@@ -312,7 +311,7 @@ def _gen_market_shares_dirichlet_multisample(
312
311
 
313
312
 
314
313
  def _gen_market_shares_dirichlet(
315
- _dir_alphas: NDArray[np.float64],
314
+ _dir_alphas: ArrayDouble,
316
315
  _s_size: int = 10**6,
317
316
  _recapture_form: RECConstants = RECConstants.INOUT,
318
317
  _mktshr_rng_seed_seq: SeedSequence | None = None,
@@ -394,9 +393,9 @@ def _gen_market_shares_dirichlet(
394
393
 
395
394
 
396
395
  def _gen_margin_price_data(
397
- _frmshr_array: NDArray[np.float64],
398
- _nth_firm_share: NDArray[np.float64],
399
- _aggregate_purchase_prob: NDArray[np.float64],
396
+ _frmshr_array: ArrayDouble,
397
+ _nth_firm_share: ArrayDouble,
398
+ _aggregate_purchase_prob: ArrayDouble,
400
399
  _pcm_spec: PCMSpec,
401
400
  _price_spec: PriceConstants,
402
401
  _hsr_filing_test_type: SSZConstants,
@@ -472,11 +471,6 @@ def _gen_margin_price_data(
472
471
  1 + _m1_nr,
473
472
  )
474
473
  _mnl_test_array = (_pcm_array[:, [1]] >= 0) & (_pcm_array[:, [1]] <= 1)
475
- else:
476
- # Generate i.i.d. PCMs
477
- # Construct price_array = 1/ (1 - pcm_array)
478
- # Rgenerate MNL test
479
- pass
480
474
 
481
475
  _margin_data = MarginDataSample(_pcm_array[:, :2], _mnl_test_array)
482
476
  del _price_array_here
@@ -543,8 +537,8 @@ def _gen_margin_price_data(
543
537
 
544
538
  # marked for deletion
545
539
  def _gen_price_data(
546
- _frmshr_array: NDArray[np.float64],
547
- _nth_firm_share: NDArray[np.float64],
540
+ _frmshr_array: ArrayDouble,
541
+ _nth_firm_share: ArrayDouble,
548
542
  _price_spec: PriceConstants,
549
543
  _hsr_filing_test_type: SSZConstants,
550
544
  _seed_seq: SeedSequence | None = None,
@@ -630,9 +624,9 @@ def _gen_price_data(
630
624
 
631
625
 
632
626
  def _gen_margin_data(
633
- _frmshr_array: NDArray[np.float64],
634
- _price_array: NDArray[np.float64],
635
- _aggregate_purchase_prob: NDArray[np.float64],
627
+ _frmshr_array: ArrayDouble,
628
+ _price_array: ArrayDouble,
629
+ _aggregate_purchase_prob: ArrayDouble,
636
630
  _pcm_spec: PCMSpec,
637
631
  _pcm_rng_seed_seq: SeedSequence,
638
632
  _nthreads: int = 16,
@@ -719,8 +713,8 @@ def _gen_margin_data(
719
713
 
720
714
 
721
715
  def _beta_located(
722
- _mu: float | NDArray[np.float64], _sigma: float | NDArray[np.float64], /
723
- ) -> NDArray[np.float64]:
716
+ _mu: float | ArrayDouble, _sigma: float | ArrayDouble, /
717
+ ) -> ArrayDouble:
724
718
  """
725
719
  Given mean and stddev, return shape parameters for corresponding Beta distribution
726
720
 
@@ -743,7 +737,7 @@ def _beta_located(
743
737
  return np.array([_mu * _mul, (1 - _mu) * _mul], dtype=np.float64)
744
738
 
745
739
 
746
- def beta_located_bound(_dist_parms: NDArray[np.float64], /) -> NDArray[np.float64]:
740
+ def beta_located_bound(_dist_parms: ArrayDouble, /) -> ArrayDouble:
747
741
  R"""
748
742
  Return shape parameters for a non-standard beta, given the mean, stddev, range
749
743
 
@@ -10,9 +10,8 @@ from typing import NamedTuple
10
10
 
11
11
  import numpy as np
12
12
  from numpy.random import SeedSequence
13
- from numpy.typing import NDArray
14
13
 
15
- from .. import VERSION, RECConstants # noqa: TID252
14
+ from .. import VERSION, ArrayDouble, RECConstants # noqa: TID252
16
15
  from . import (
17
16
  EMPTY_ARRAY_DEFAULT,
18
17
  FM2Constants,
@@ -227,10 +226,10 @@ def parse_seed_seq_list(
227
226
  def gen_divr_array(
228
227
  _recapture_form: RECConstants,
229
228
  _recapture_rate: float | None,
230
- _frmshr_array: NDArray[np.float64],
231
- _aggregate_purchase_prob: NDArray[np.float64] = EMPTY_ARRAY_DEFAULT,
229
+ _frmshr_array: ArrayDouble,
230
+ _aggregate_purchase_prob: ArrayDouble = EMPTY_ARRAY_DEFAULT,
232
231
  /,
233
- ) -> NDArray[np.float64]:
232
+ ) -> ArrayDouble:
234
233
  """
235
234
  Given merging-firm shares and related parameters, return diverion ratios.
236
235
 
@@ -259,7 +258,7 @@ def gen_divr_array(
259
258
 
260
259
  """
261
260
 
262
- _divr_array: NDArray[np.float64]
261
+ _divr_array: ArrayDouble
263
262
  if _recapture_form == RECConstants.FIXED:
264
263
  _divr_array = _recapture_rate * _frmshr_array[:, ::-1] / (1 - _frmshr_array) # type: ignore
265
264
 
@@ -15,12 +15,19 @@ from typing import Literal
15
15
  import numpy as np
16
16
  import re2 as re # type: ignore
17
17
  from jinja2 import Environment, FileSystemLoader, Template, select_autoescape
18
- from numpy.typing import NDArray
19
18
  from scipy.interpolate import interp1d # type: ignore
20
-
21
- from .. import _PKG_NAME, DATA_DIR, VERSION # noqa: TID252
19
+ from scipy.stats import beta, norm # type: ignore
20
+
21
+ from .. import ( # noqa: TID252
22
+ _PKG_NAME,
23
+ DATA_DIR,
24
+ TI,
25
+ VERSION,
26
+ ArrayBIGINT,
27
+ ArrayDouble,
28
+ ArrayINT,
29
+ )
22
30
  from ..core import ftc_merger_investigations_data as fid # noqa: TID252
23
- from ..core.proportions_tests import propn_ci # noqa: TID252
24
31
  from . import INVResolution
25
32
 
26
33
  __version__ = VERSION
@@ -114,7 +121,7 @@ if not (_out_path := DATA_DIR.joinpath(f"{_PKG_NAME}.cls")).is_file():
114
121
 
115
122
 
116
123
  if not (_DOTTEX := DATA_DIR / Rf"{_PKG_NAME}_TikZTableSettings.tex").is_file():
117
- # Write to dottex
124
+ # Write to LaTeX table settings file
118
125
  with resources.as_file(
119
126
  resources.files(f"{_PKG_NAME}.data.jinja2_LaTeX_templates").joinpath(
120
127
  "setup_tikz_tables.tex"
@@ -293,7 +300,7 @@ def enf_stats_listing_by_group(
293
300
  _stats_group: StatsGrpSelector,
294
301
  _enf_spec: INVResolution,
295
302
  /,
296
- ) -> NDArray[np.int64]:
303
+ ) -> ArrayBIGINT:
297
304
  if _stats_group == StatsGrpSelector.HD:
298
305
  raise ValueError(
299
306
  f"Clearance/enforcement statistics, '{_stats_group}' not valied here."
@@ -328,7 +335,7 @@ def enf_cnts_listing_byfirmcount(
328
335
  _table_evid_cond: EVIDENConstants = EVIDENConstants.UR,
329
336
  _enf_spec: INVResolution = INVResolution.CLRN,
330
337
  /,
331
- ) -> NDArray[np.int64]:
338
+ ) -> ArrayBIGINT:
332
339
  if _data_period not in _data_array_dict:
333
340
  raise ValueError(
334
341
  f"Invalid value of data period, {f'"{_data_period}"'}."
@@ -364,7 +371,7 @@ def enf_cnts_listing_byhhianddelta(
364
371
  _table_evid_cond: EVIDENConstants = EVIDENConstants.UR,
365
372
  _enf_spec: INVResolution = INVResolution.CLRN,
366
373
  /,
367
- ) -> NDArray[np.int64]:
374
+ ) -> ArrayBIGINT:
368
375
  if _data_period not in _data_array_dict:
369
376
  raise ValueError(
370
377
  f"Invalid value of data period, {f'"{_data_period}"'}."
@@ -419,7 +426,7 @@ def table_no_lku(
419
426
  return _tno
420
427
 
421
428
 
422
- def enf_cnts_byfirmcount(_cnts_array: NDArray[np.int64], /) -> NDArray[np.int64]:
429
+ def enf_cnts_byfirmcount(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
423
430
  _ndim_in = 1
424
431
  return np.vstack([
425
432
  np.concatenate([
@@ -430,7 +437,7 @@ def enf_cnts_byfirmcount(_cnts_array: NDArray[np.int64], /) -> NDArray[np.int64]
430
437
  ])
431
438
 
432
439
 
433
- def enf_cnts_bydelta(_cnts_array: NDArray[np.int64], /) -> NDArray[np.int64]:
440
+ def enf_cnts_bydelta(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
434
441
  _ndim_in = 2
435
442
  return np.vstack([
436
443
  np.concatenate([
@@ -441,7 +448,7 @@ def enf_cnts_bydelta(_cnts_array: NDArray[np.int64], /) -> NDArray[np.int64]:
441
448
  ])
442
449
 
443
450
 
444
- def enf_cnts_byconczone(_cnts_array: NDArray[np.int64], /) -> NDArray[np.int64]:
451
+ def enf_cnts_byconczone(_cnts_array: ArrayBIGINT, /) -> ArrayBIGINT:
445
452
  # Prepare to tag clearance stats by presumption zone
446
453
  _hhi_zone_post_ranged = hhi_zone_post_ranger(_cnts_array[:, 0] / 1e4)
447
454
  _hhi_delta_ranged = hhi_delta_ranger(_cnts_array[:, 1] / 1e4)
@@ -513,7 +520,7 @@ def enf_cnts_byconczone(_cnts_array: NDArray[np.int64], /) -> NDArray[np.int64]:
513
520
 
514
521
 
515
522
  def enf_stats_table_onedim(
516
- _inparr: NDArray[np.float64 | np.int64],
523
+ _inparr: ArrayDouble | ArrayBIGINT | ArrayDouble | ArrayBIGINT,
517
524
  _totals_row: int | None = None,
518
525
  /,
519
526
  *,
@@ -568,7 +575,7 @@ def enf_stats_table_onedim(
568
575
 
569
576
 
570
577
  def enf_stats_table_byzone(
571
- _inparr: NDArray[np.float64 | np.int64],
578
+ _inparr: ArrayDouble | ArrayBIGINT | ArrayDouble | ArrayBIGINT,
572
579
  _totals_row: int | None = None,
573
580
  /,
574
581
  *,
@@ -650,8 +657,8 @@ def enf_stats_table_byzone(
650
657
 
651
658
 
652
659
  def _stats_formatted_row(
653
- _stats_row_cnt: NDArray[np.int64],
654
- _stats_row_tot: NDArray[np.int64],
660
+ _stats_row_cnt: ArrayBIGINT,
661
+ _stats_row_tot: ArrayBIGINT,
655
662
  _return_type_sel: StatsReturnSelector,
656
663
  /,
657
664
  ) -> list[list[str]]:
@@ -715,6 +722,109 @@ def stats_print_rows(
715
722
  print()
716
723
 
717
724
 
725
+ def propn_ci(
726
+ _npos: ArrayINT[TI] | int = 4,
727
+ _nobs: ArrayINT[TI] | int = 10,
728
+ /,
729
+ *,
730
+ alpha: float = 0.05,
731
+ method: Literal[
732
+ "Agresti-Coull", "Clopper-Pearson", "Exact", "Wilson", "Score"
733
+ ] = "Wilson",
734
+ ) -> tuple[
735
+ ArrayDouble | float, ArrayDouble | float, ArrayDouble | float, ArrayDouble | float
736
+ ]:
737
+ """Returns point estimates and confidence interval for a proportion
738
+
739
+ Methods "Clopper-Pearson" and "Exact" are synoymous [3]_. Similarly,
740
+ "Wilson" and "Score" are synonyms here.
741
+
742
+ Parameters
743
+ ----------
744
+ _npos
745
+ Number of positives
746
+
747
+ _nobs
748
+ Number of observed values
749
+
750
+ alpha
751
+ Significance level
752
+
753
+ method
754
+ Method to use for estimating confidence interval
755
+
756
+ Returns
757
+ -------
758
+ Raw and estimated proportions, and bounds of the confidence interval
759
+
760
+
761
+ References
762
+ ----------
763
+
764
+ .. [3] Alan Agresti & Brent A. Coull (1998) Approximate is Better
765
+ than “Exact” for Interval Estimation of Binomial Proportions,
766
+ The American Statistician, 52:2, 119-126,
767
+ https://doi.org/10.1080/00031305.1998.10480550
768
+
769
+ """
770
+
771
+ for _f in _npos, _nobs:
772
+ if not isinstance(_f, int | np.integer):
773
+ raise ValueError(
774
+ f"Count, {_f!r} must have type that is a subtype of np.integer."
775
+ )
776
+
777
+ if not _nobs:
778
+ return (np.nan, np.nan, np.nan, np.nan)
779
+
780
+ _raw_phat: ArrayDouble | float = _npos / _nobs
781
+ _est_phat: ArrayDouble | float
782
+ _est_ci_l: ArrayDouble | float
783
+ _est_ci_u: ArrayDouble | float
784
+
785
+ match method:
786
+ case "Clopper-Pearson" | "Exact":
787
+ _est_ci_l, _est_ci_u = (
788
+ beta.ppf(*_f)
789
+ for _f in (
790
+ (alpha / 2, _npos, _nobs - _npos + 1),
791
+ (1 - alpha / 2, _npos + 1, _nobs - _npos),
792
+ )
793
+ )
794
+ _est_phat = 1 / 2 * (_est_ci_l + _est_ci_u)
795
+
796
+ case "Agresti-Coull":
797
+ _zsc = norm.ppf(1 - alpha / 2)
798
+ _zscsq = _zsc * _zsc
799
+ _adjmt = 4 if alpha == 0.05 else _zscsq
800
+ _est_phat = (_npos + _adjmt / 2) / (_nobs + _adjmt)
801
+ _est_ci_l, _est_ci_u = (
802
+ _est_phat + _g
803
+ for _g in [
804
+ _f * _zsc * np.sqrt(_est_phat * (1 - _est_phat) / (_nobs + _adjmt))
805
+ for _f in (-1, 1)
806
+ ]
807
+ )
808
+
809
+ case "Wilson" | "Score":
810
+ _zsc = norm.ppf(1 - alpha / 2)
811
+ _zscsq = _zsc * _zsc
812
+ _est_phat = (_npos + _zscsq / 2) / (_nobs + _zscsq)
813
+ _est_ci_l, _est_ci_u = (
814
+ _est_phat
815
+ + _f
816
+ * _zsc
817
+ * np.sqrt(_nobs * _raw_phat * (1 - _raw_phat) + _zscsq / 4)
818
+ / (_nobs + _zscsq)
819
+ for _f in (-1, 1)
820
+ )
821
+
822
+ case _:
823
+ raise ValueError(f"Method, {f'"{method}"'} not yet implemented.")
824
+
825
+ return _raw_phat, _est_phat, _est_ci_l, _est_ci_u
826
+
827
+
718
828
  def render_table_pdf(
719
829
  _table_dottex_pathlist: Sequence[str], _table_coll_path: str, /
720
830
  ) -> None:
@@ -13,9 +13,19 @@ import numpy as np
13
13
  import tables as ptb # type: ignore
14
14
  from joblib import Parallel, cpu_count, delayed # type: ignore
15
15
  from numpy.random import SeedSequence
16
- from numpy.typing import NDArray
17
16
 
18
- from .. import VERSION, RECConstants, UPPAggrSelector # noqa: TID252
17
+ from .. import ( # noqa: TID252
18
+ TF,
19
+ TI,
20
+ VERSION,
21
+ ArrayBIGINT,
22
+ ArrayBoolean,
23
+ ArrayDouble,
24
+ ArrayFloat,
25
+ ArrayINT,
26
+ RECConstants,
27
+ UPPAggrSelector,
28
+ )
19
29
  from ..core import guidelines_boundaries as gbl # noqa: TID252
20
30
  from . import (
21
31
  EMPTY_ARRAY_DEFAULT,
@@ -250,7 +260,7 @@ def enf_cnts(
250
260
  )
251
261
  _enf_cnts_sim_byfirmcount_array[0] = 2
252
262
 
253
- # Clearance/enfrocement counts --- by delta
263
+ # Clearance/enforcement counts --- by delta
254
264
  _hhi_delta_ranged = esl.hhi_delta_ranger(_hhi_delta)
255
265
  _enf_cnts_sim_bydelta_array = -1 * np.ones(_stats_rowlen, np.int64)
256
266
  for _hhi_delta_lim in esl.HHI_DELTA_KNOTS[:-1]:
@@ -272,7 +282,7 @@ def enf_cnts(
272
282
 
273
283
  _enf_cnts_sim_bydelta_array = _enf_cnts_sim_bydelta_array[1:]
274
284
 
275
- # Clearance/enfrocement counts --- by zone
285
+ # Clearance/enforcement counts --- by zone
276
286
  try:
277
287
  _hhi_zone_post_ranged = esl.hhi_zone_post_ranger(_hhi_post)
278
288
  except ValueError as _err:
@@ -357,23 +367,11 @@ def gen_upp_test_arrays(
357
367
  out=_guppi_array,
358
368
  )
359
369
 
360
- _cmcr_array = np.empty_like(_market_data.divr_array)
361
- np.divide(
362
- np.einsum("ij,ij->ij", _market_data.pcm_array, _market_data.divr_array),
363
- np.einsum("ij,ij->ij", 1 - _market_data.pcm_array, 1 - _market_data.divr_array),
364
- out=_cmcr_array,
365
- )
366
-
367
370
  _ipr_array = np.empty_like(_market_data.divr_array)
368
- np.divide(
369
- np.einsum("ij,ij->ij", _market_data.pcm_array, _market_data.divr_array),
370
- 1 - _market_data.divr_array,
371
- out=_ipr_array,
372
- )
371
+ np.divide(_guppi_array, (1 - _market_data.divr_array[:, ::-1]), out=_ipr_array)
373
372
 
374
- # This one needs further testing:
375
- # _ipr_array_alt = np.empty_like(_market_data.divr_array)
376
- # np.divide(_guppi_array, (1 - _market_data.divr_array[:, ::-1]), out=_ipr_array_alt)
373
+ _cmcr_array = np.empty_like(_market_data.divr_array)
374
+ np.divide(_ipr_array, 1 - _market_data.pcm_array, out=_cmcr_array)
377
375
 
378
376
  _test_measure_seq = (_market_data.divr_array, _guppi_array, _cmcr_array, _ipr_array)
379
377
 
@@ -496,7 +494,11 @@ def save_data_to_hdf5(
496
494
 
497
495
 
498
496
  def save_array_to_hdf5(
499
- _array_obj: NDArray[np.float64 | np.int64 | np.bool_],
497
+ _array_obj: ArrayFloat[TF]
498
+ | ArrayINT[TI]
499
+ | ArrayDouble
500
+ | ArrayBIGINT
501
+ | ArrayBoolean,
500
502
  _array_name: str,
501
503
  _h5_group: ptb.Group,
502
504
  _h5_file: ptb.File,