mergeron 2025.739319.3__py3-none-any.whl → 2025.739341.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mergeron might be problematic. Click here for more details.

@@ -1,8 +1,13 @@
1
1
  """
2
2
  Functions for generating synthetic data under specified distributions.
3
3
 
4
- Uses multiple CPUs when available, with PCG64DXSM as the PRNG
5
- https://github.com/numpy/numpy/issues/16313.
4
+ Uses multiple CPUs when available, with PCG64DXSM as the PRNG. [#]_
5
+
6
+ References
7
+ ----------
8
+
9
+ .. [#] See,
10
+ https://numpy.org/doc/stable/reference/random/generated/numpy.random.PCG64DXSM.html
6
11
 
7
12
  """
8
13
 
@@ -14,7 +19,7 @@ from typing import Literal
14
19
 
15
20
  import numpy as np
16
21
  from attrs import Attribute, Converter, define, field
17
- from numpy.random import PCG64DXSM, Generator, SeedSequence
22
+ from numpy.random import Generator, SeedSequence
18
23
 
19
24
  from .. import ( # noqa: TID252
20
25
  NTHREADS,
@@ -24,6 +29,7 @@ from .. import ( # noqa: TID252
24
29
  this_yaml,
25
30
  yaml_rt_mapper,
26
31
  )
32
+ from . import DEFAULT_BITGENERATOR
27
33
 
28
34
  __version__ = VERSION
29
35
 
@@ -32,24 +38,24 @@ DEFAULT_BETA_DIST_PARMS: ArrayFloat = np.array([1.0, 1.0], float)
32
38
 
33
39
 
34
40
  def prng(_s: SeedSequence | None = None, /) -> np.random.Generator:
35
- """Adopt the PCG64DXSM bit-generator, the future default in numpy.default_rng().
41
+ """Return a psure-random number generator.
36
42
 
37
43
  Parameters
38
44
  ----------
39
45
  _s
40
- SeedSequence, for generating random numbers in repeatable fashion.
46
+ SeedSequence, for generating repeatable, non-overlapping random numbers.
41
47
 
42
48
  Returns
43
49
  -------
44
- A numpy random BitGenerator.
50
+ A numpy random generator.
45
51
 
46
52
  """
47
- return Generator(PCG64DXSM(_s))
53
+ return Generator(
54
+ DEFAULT_BITGENERATOR(SeedSequence(pool_size=8) if _s is None else _s)
55
+ )
48
56
 
49
57
 
50
58
  # Add yaml representer, constructor for SeedSequence
51
-
52
-
53
59
  (_, _) = (
54
60
  this_yaml.representer.add_representer(
55
61
  SeedSequence, lambda _r, _d: _r.represent_mapping("!SeedSequence", _d.state)
@@ -63,14 +69,14 @@ def prng(_s: SeedSequence | None = None, /) -> np.random.Generator:
63
69
  def gen_seed_seq_list_default(
64
70
  _len: int = 3, /, *, generated_entropy: Sequence[int] | None = None
65
71
  ) -> tuple[SeedSequence, ...]:
66
- """
67
- Return specified number of SeedSequences, for generating random variates
72
+ R"""
73
+ Return specified number of SeedSequences, for generating random variates.
68
74
 
69
75
  Initializes a specified number of SeedSequences based on a set of
70
76
  10 generated "seeds" in a hard-coded list. If the required number of
71
77
  random variates is larger than 10, the user must first generate
72
78
  a sufficient number of seeds to draw upon for initializing SeedSequences.
73
- The generated seeds can be reused in subsequent calls to this function.
79
+ The generated entropy can be reused in subsequent calls to this function.
74
80
 
75
81
  Parameters
76
82
  ----------
@@ -85,20 +91,18 @@ def gen_seed_seq_list_default(
85
91
  Returns
86
92
  -------
87
93
  A list of numpy SeedSequence objects, which can be used to seed prng() or to spawn
88
- seed sequences that can be used as seeds to generate non-overlapping streams in parallel.
94
+ seed sequences that can be used as seeds to generate non-overlapping streams in parallel. [#]_
89
95
 
90
96
  Raises
91
97
  ------
92
98
  ValueError
93
- When, :math:`\\_sseq\\_list\\_len > max(10, len(generated\\_entropy))`.
99
+ When, :math:`\_sseq\_list\_len > max(10, len(generated\_entropy))`.
94
100
 
95
101
  References
96
102
  ----------
97
- *See*, https://numpy.org/doc/stable/reference/random/parallel.html
98
-
103
+ .. [#] *See*, https://numpy.org/doc/stable/reference/random/parallel.html
99
104
 
100
105
  """
101
-
102
106
  generated_entropy = generated_entropy or [
103
107
  92156365243929466422624541055805800714117298857186959727264899187749727119124,
104
108
  45508962760932900824607908382088764294813063250106926349700153055300051503944,
@@ -214,7 +218,6 @@ class MultithreadedRNG:
214
218
 
215
219
  def fill(self) -> None:
216
220
  """Fill the provided output array with random number draws as specified."""
217
-
218
221
  if not len(self.dist_parms) or np.array_equal(
219
222
  self.dist_parms, DEFAULT_DIST_PARMS
220
223
  ):
mergeron/data/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
1
  """
2
- Data useful for empirical analysis of merger enforcement policy
2
+ Data useful for empirical analysis of merger enforcement policy.
3
3
 
4
4
  These data are processed for further analysis within relevant
5
5
  submodules of the parent package. Thus, direct access is
@@ -14,39 +14,41 @@ __version__ = VERSION
14
14
 
15
15
  data_resources = resources.files(f"{_PKG_NAME}.data")
16
16
 
17
- DAMODARAN_MARGIN_WORKBOOK = data_resources / "damodaran_margin_data.xls"
17
+ DAMODARAN_MARGIN_DATA = data_resources / "damodaran_margin_data_serialized.zip"
18
18
  """
19
- Python object pointing to included copy of Prof. Damodaran's margin data
19
+ Included copy of Prof. Damodaran's margin data.
20
20
 
21
- Only used as a fallback, in case direct download from source fails.
21
+ Use for replication/testing.
22
22
 
23
23
  NOTES
24
24
  -----
25
25
  Source data are from Prof. Aswath Damodaran, Stern School of Business, NYU; available online
26
- at https://pages.stern.nyu.edu/~adamodar/pc/datasets/margin.xls
26
+ at https://pages.stern.nyu.edu/~adamodar/pc/datasets/margin.xls
27
+ and https://pages.stern.nyu.edu/~adamodar/pc/archives/margin*.xls.
27
28
 
29
+ Gross margins are reported in 2017 data and later.
28
30
 
29
31
  Use as, for example:
30
32
 
31
33
  .. code-block:: python
32
34
 
33
- from mergeron.data import DAMODARAN_MARGIN_WORKBOOK
35
+ import mergeron.data as mdat
34
36
 
35
- shutil.copy2(DAMODARAN_MARGIN_WORKBOOK, Path.home() / f"{DAMODARAN_MARGIN_WORKBOOK.name}")
37
+ shutil.copy2(mdat.DAMODARAN_MARGIN_DATA, Path.home() / f"{DAMODARAN_MARGIN_DATA.name}")
36
38
  """
37
39
 
38
40
  FTC_MERGER_INVESTIGATIONS_DATA = data_resources / "ftc_merger_investigations_data.zip"
39
41
  """
40
- FTC merger investigtions data published in 2004, 2007, 2008, and 2013
42
+ FTC merger investigations data published in 2004, 2007, 2008, and 2013
41
43
 
42
44
  NOTES
43
45
  -----
44
- Raw data tables published by the FTC are loaded into a nested distionary, organized by
46
+ Raw data tables published by the FTC are loaded into a nested dictionary, organized by
45
47
  data period, table type, and table number. Each table is stored as a numerical array
46
- (:mod:`numpy` arrray), with additonal attrubutes for the industry group and additonal
48
+ (:mod:`numpy` array), with additional attributes for the industry group and additional
47
49
  evidence noted in the source data.
48
50
 
49
- Data for additonal data periods (time spans) not reported in the source data,
51
+ Data for additional data periods (time spans) not reported in the source data,
50
52
  e.g., 2004-2011, are constructed by subtracting counts in the base data from counts
51
53
  in the cumulative data, by table, for "enforced" mergers and "closed" mergers, when
52
54
  the cumulative data for the longer period are consistent with the base data for
mergeron/gen/__init__.py CHANGED
@@ -1,8 +1,4 @@
1
- """
2
- Defines constants, specifications (classes with attributes defining varous parameters) and
3
- containers for industry data generation and testing.
4
-
5
- """
1
+ """Defines constants, specifications and containers for industry data generation and testing."""
6
2
 
7
3
  from __future__ import annotations
8
4
 
@@ -45,6 +41,8 @@ DEFAULT_BETA_BND_DIST_PARMS = np.array([0.5, 1.0, 0.0, 1.0], float)
45
41
 
46
42
  @frozen
47
43
  class SeedSequenceData:
44
+ """Seed sequence values for shares, margins, and, optionally, firm-counts and prices."""
45
+
48
46
  share: SeedSequence = field(eq=attrgetter("state"))
49
47
  pcm: SeedSequence = field(eq=attrgetter("state"))
50
48
  fcounts: SeedSequence | None = field(eq=lambda x: x if x is None else x.state)
@@ -78,7 +76,7 @@ class SHRDistribution(str, Enameled):
78
76
  """Shape parameter for all merging-firm-shares is unity (1)"""
79
77
 
80
78
  DIR_FLAT_CONSTR = "Flat Dirichlet - Constrained"
81
- """Impose minimum probablility weight on each firm-count
79
+ """Impose minimum probability weight on each firm-count
82
80
 
83
81
  Only firm-counts with probability weight of 3% or more
84
82
  are included for data generation.
@@ -149,9 +147,9 @@ def _shr_dp_conv(_v: Sequence[float] | ArrayFloat | None, _i: ShareSpec) -> Arra
149
147
 
150
148
  @frozen
151
149
  class ShareSpec:
152
- """Market share specification
150
+ """Market share specification.
153
151
 
154
- A salientfeature of market-share specification in this package is that
152
+ A salient feature of market-share specification in this package is that
155
153
  the draws represent markets with multiple different firm-counts.
156
154
  Firm-counts are unspecified if the share distribution is
157
155
  :attr:`mergeron.SHRDistribution.UNI`, for Dirichlet-distributed market-shares,
@@ -165,7 +163,6 @@ class ShareSpec:
165
163
  In other words, recapture ratios cannot be estimated using
166
164
  outside-good choice probabilities if the distribution of markets over firm-counts
167
165
  is unspecified.
168
-
169
166
  """
170
167
 
171
168
  dist_type: SHRDistribution = field(kw_only=False)
@@ -181,10 +178,9 @@ class ShareSpec:
181
178
  Defaults to :attr:`DEFAULT_FCOUNT_WTS`, which specifies pre-merger
182
179
  firm-counts of 2 to 7 with weights in descending order from 6 to 1.
183
180
 
184
- ALERT: Firm-count weights are irrelevant when the mergering firmss shares are specified
181
+ ALERT: Firm-count weights are irrelevant when the merging firms' shares are specified
185
182
  to have uniform distribution; therefore this attribute is forced to None if
186
183
  :attr:`.dist_type` == :attr:`.SHRDistribution.UNI`.
187
-
188
184
  """
189
185
 
190
186
  @firm_counts_weights.default
@@ -210,7 +206,6 @@ class ShareSpec:
210
206
  for Dirichlet-type distributions, a vector of shape parameters of length
211
207
  equal to 1 plus the length of firm-count weights below; defaults depend on
212
208
  type of Dirichlet-distribution specified.
213
-
214
209
  """
215
210
 
216
211
  @dist_parms.default
@@ -227,7 +222,7 @@ class ShareSpec:
227
222
  and 0 < len(_v) < (1 + len(_i.firm_counts_weights))
228
223
  ):
229
224
  raise ValueError(
230
- "If specified, the number of distribution parameters must euqal or "
225
+ "If specified, the number of distribution parameters must equal or "
231
226
  "exceed the maximum firm-count premerger, which is "
232
227
  "1 plus the length of the vector specifying firm-count weights."
233
228
  )
@@ -252,7 +247,7 @@ class ShareSpec:
252
247
  outside good choice probabilities (:attr:`mergeron.RECForm.OUTIN`).
253
248
 
254
249
  The recapture ratio is usually calibrated to the numbers-equivalent of the
255
- HHI threshold for the presumtion of harm from unilateral competitive effects
250
+ HHI threshold for the presumption of harm from unilateral competitive effects
256
251
  in published merger guidelines. Accordingly, the recapture ratio rounded to
257
252
  the nearest 5% is:
258
253
 
@@ -267,7 +262,6 @@ class ShareSpec:
267
262
 
268
263
  ALERT: If diversion ratios are estimated by specifying a choice probability for the
269
264
  outside good, the recapture ratio is set to None, overriding any user-specified value.
270
-
271
265
  """
272
266
 
273
267
  @recapture_ratio.default
@@ -281,7 +275,7 @@ class ShareSpec:
281
275
  elif _v is None and _i.recapture_form != RECForm.OUTIN:
282
276
  raise ValueError(
283
277
  f"Recapture specification, {_i.recapture_form!r} requires that "
284
- "the market sample specification inclues a recapture ratio in the "
278
+ "the market sample specification includes a recapture ratio in the "
285
279
  "interval [0, 1)."
286
280
  )
287
281
 
@@ -300,10 +294,10 @@ class PCMDistribution(str, Enameled):
300
294
  @this_yaml.register_class
301
295
  @enum.unique
302
296
  class PCMRestriction(str, Enameled):
303
- """Firm 2 margins - derivation methods."""
297
+ """Restriction on generated Firm 2 margins."""
304
298
 
305
- IID = "i.i.d"
306
- MNL = "MNL-dep"
299
+ IID = "independent and identically distributed (IID)"
300
+ MNL = "Nash-Bertrand equilibrium with multinomial logit (MNL) demand"
307
301
  SYM = "symmetric"
308
302
 
309
303
 
@@ -320,7 +314,7 @@ def _pcm_dp_conv(_v: ArrayFloat | Sequence[float] | None, _i: PCMSpec) -> ArrayF
320
314
  return DEFAULT_DIST_PARMS
321
315
  elif _i.dist_type == PCMDistribution.EMPR and not isinstance(_v, np.ndarray):
322
316
  raise ValueError(
323
- "Invalid specification; use ..core.empriical_margin_distribution.margin_data_builider()[0]."
317
+ "Invalid specification; use ..core.empirical_margin_distribution.margin_data_builder()[0]."
324
318
  )
325
319
  elif isinstance(_v, Sequence | np.ndarray):
326
320
  return np.asarray(_v, float)
@@ -333,7 +327,7 @@ def _pcm_dp_conv(_v: ArrayFloat | Sequence[float] | None, _i: PCMSpec) -> ArrayF
333
327
 
334
328
  @frozen
335
329
  class PCMSpec:
336
- """Price-cost margin (PCM) specification
330
+ """Price-cost margin (PCM) specification.
337
331
 
338
332
  If price-cost margins are specified as having Beta distribution,
339
333
  `dist_parms` is specified as a pair of positive, non-zero shape parameters of
@@ -343,8 +337,6 @@ class PCMSpec:
343
337
  Bounded-Beta distribution, `dist_parms` is specified as
344
338
  the tuple, (`mean`, `std deviation`, `min`, `max`), where `min` and `max`
345
339
  are lower- and upper-bounds respectively within the interval :math:`[0, 1]`.
346
-
347
-
348
340
  """
349
341
 
350
342
  dist_type: PCMDistribution = field()
@@ -365,7 +357,6 @@ class PCMSpec:
365
357
  for Beta distribution, shape parameters, defaults to `(1, 1)`;
366
358
  for Bounded-Beta distribution, vector of (min, max, mean, std. deviation), non-optional;
367
359
  for empirical distribution based on Damodaran margin data, optional, ignored
368
-
369
360
  """
370
361
 
371
362
  @dist_parms.default
@@ -399,7 +390,7 @@ class PCMSpec:
399
390
 
400
391
  elif _i.dist_type == PCMDistribution.EMPR and not isinstance(_v, np.ndarray):
401
392
  raise ValueError(
402
- "Empirical distribution requires deserialzed margin data from Prof. Damodaran, NYU"
393
+ "Empirical distribution requires deserialized margin data from Prof. Damodaran, NYU"
403
394
  )
404
395
 
405
396
  pcm_restriction: PCMRestriction = field(kw_only=True, default=PCMRestriction.IID)
@@ -409,9 +400,9 @@ class PCMSpec:
409
400
  def __prv(_i: PCMSpec, _a: Attribute[PCMRestriction], _v: PCMRestriction) -> None:
410
401
  if _v == PCMRestriction.MNL and _i.dist_type == PCMDistribution.EMPR:
411
402
  print(
412
- "NOTE: Estimated Firm 2 parameters will not be consistent with "
413
- "the empirical distribution of margins in the source data. For "
414
- "consistency, respecify pcm_spec.pcm_restriction = PCMRestriction.IID."
403
+ "NOTE: For consistency of generated Firm 2 margins with source data,",
404
+ "respecify pcm_spec.pcm_restriction = PCMRestriction.IID.",
405
+ sep="\n",
415
406
  )
416
407
 
417
408
 
@@ -458,7 +449,7 @@ class SSZConstant(float, Enameled):
458
449
 
459
450
  @frozen
460
451
  class MarketSampleData:
461
- """Container for generated markets data sample."""
452
+ """Container for generated market sample dataset."""
462
453
 
463
454
  frmshr_array: ArrayDouble = field(eq=cmp_using(np.array_equal))
464
455
  """Merging-firm shares (with two merging firms)"""
@@ -509,7 +500,7 @@ class MarketSampleData:
509
500
  return retval
510
501
 
511
502
  hhi_post: ArrayDouble = field(eq=cmp_using(np.array_equal))
512
- """Post-merger change in Herfindahl-Hirschmann Index (HHI)"""
503
+ """Post-merger contribution to Herfindahl-Hirschman Index (HHI)"""
513
504
 
514
505
  @hhi_post.default
515
506
  def __hpd(_i: MarketSampleData) -> ArrayDouble:
@@ -538,7 +529,7 @@ class MarketSampleData:
538
529
 
539
530
 
540
531
  @frozen
541
- class ShareDataSample:
532
+ class ShareSampleData:
542
533
  """Container for generated market shares.
543
534
 
544
535
  Includes related measures of market structure
@@ -559,7 +550,7 @@ class ShareDataSample:
559
550
 
560
551
 
561
552
  @frozen
562
- class PriceDataSample:
553
+ class PriceSampleData:
563
554
  """Container for generated price array, and related."""
564
555
 
565
556
  price_array: ArrayDouble
@@ -570,7 +561,7 @@ class PriceDataSample:
570
561
 
571
562
 
572
563
  @frozen
573
- class MarginDataSample:
564
+ class MarginSampleData:
574
565
  """Container for generated margin array and related MNL test array."""
575
566
 
576
567
  pcm_array: ArrayDouble
@@ -581,7 +572,7 @@ class MarginDataSample:
581
572
 
582
573
  Applying restrictions from Bertrand-Nash oligopoly
583
574
  with MNL demand results in draws of Firm 2 PCM falling
584
- outside the feabile interval,:math:`[0, 1]`, depending
575
+ outside the feasible interval,:math:`[0, 1]`, depending
585
576
  on the configuration of merging firm shares. Such draws
586
577
  are are flagged as infeasible (False)in :code:`mnl_test_array` while
587
578
  draws with PCM values within the feasible range are
@@ -593,9 +584,11 @@ class MarginDataSample:
593
584
  @this_yaml.register_class
594
585
  @enum.unique
595
586
  class INVResolution(str, Enameled):
587
+ """Report investigations resulting in clearance; enforcement; or both, respectively."""
588
+
596
589
  CLRN = "clearance"
597
590
  ENFT = "enforcement"
598
- BOTH = "investigation"
591
+ BOTH = "clearance and enforcement, respectively"
599
592
 
600
593
 
601
594
  @frozen
@@ -639,24 +632,22 @@ class UPPTestRegime:
639
632
 
640
633
  @frozen
641
634
  class UPPTestsCounts:
642
- """Counts of markets resolved as specified
635
+ """Counts of markets resolved as specified.
643
636
 
644
637
  Resolution may be either :attr:`INVResolution.ENFT`,
645
638
  :attr:`INVResolution.CLRN`, or :attr:`INVResolution.BOTH`.
646
- In the case of :attr:`INVResolution.BOTH`, two colums of counts
639
+ In the case of :attr:`INVResolution.BOTH`, two columns of counts
647
640
  are returned: one for each resolution.
648
-
649
641
  """
650
642
 
651
643
  by_firm_count: ArrayBIGINT = field(eq=cmp_using(eq=np.array_equal))
652
644
  by_delta: ArrayBIGINT = field(eq=cmp_using(eq=np.array_equal))
653
645
  by_conczone: ArrayBIGINT = field(eq=cmp_using(eq=np.array_equal))
654
- """Zones are "unoncentrated", "moderately concentrated", and "highly concentrated",
655
- with futher detail by HHI and ΔHHI for mergers in the "unconcentrated" and
646
+ """Zones are "unconcentrated", "moderately concentrated", and "highly concentrated",
647
+ with further detail by HHI and ΔHHI for mergers in the "unconcentrated" and
656
648
  "moderately concentrated" zones. See
657
649
  :attr:`mergeron.gen.enforcement_stats.HMG_PRESUMPTION_ZONE_MAP` and
658
650
  :attr:`mergeron.gen.enforcement_stats.ZONE_VALS` for more detail.
659
-
660
651
  """
661
652
 
662
653
 
@@ -1,7 +1,4 @@
1
- """
2
- Methods to generate data for analyzing merger enforcement policy.
3
-
4
- """
1
+ """Methods to generate data for analyzing merger enforcement policy."""
5
2
 
6
3
  from __future__ import annotations
7
4
 
@@ -52,13 +49,13 @@ H5_CHUNK_SIZE = 10**6
52
49
 
53
50
 
54
51
  class SamplingFunctionKWArgs(TypedDict, total=False):
55
- "Keyword arguments of sampling methods defined below"
52
+ """Keyword arguments of sampling methods defined below."""
56
53
 
57
54
  sample_size: int
58
55
  """number of draws to generate"""
59
56
 
60
57
  seed_data: SeedSequenceData | None
61
- """seed data to ensure independedent and replicable draws"""
58
+ """seed data to ensure independent and replicable draws"""
62
59
 
63
60
  nthreads: int
64
61
  """number of parallel threads to use"""
@@ -116,7 +113,7 @@ class MarketSample:
116
113
  hsr_filing_test_type: SSZConstant = field(
117
114
  default=SSZConstant.ONE, validator=validators.instance_of(SSZConstant)
118
115
  )
119
- """Method for modeling HSR filing threholds, see :class:`SSZConstant`"""
116
+ """Method for modeling HSR filing thresholds, see :class:`SSZConstant`"""
120
117
 
121
118
  sample_size: int = field(default=10**6, validator=validators.instance_of(int))
122
119
  """number of draws to simulate"""
@@ -166,7 +163,7 @@ class MarketSample:
166
163
  """
167
164
  Generate share, diversion ratio, price, and margin data for MarketSpec.
168
165
 
169
- see :attr:`SamplingFunctionKWArgs` for description of keyord parameters
166
+ see :attr:`SamplingFunctionKWArgs` for description of keyword parameters
170
167
 
171
168
  Returns
172
169
  -------
@@ -174,7 +171,6 @@ class MarketSample:
174
171
  in the sample
175
172
 
176
173
  """
177
-
178
174
  # Scale up sample size to offset discards based on specified criteria
179
175
  shr_sample_size = sample_size * self.hsr_filing_test_type
180
176
  shr_sample_size *= (
@@ -253,14 +249,13 @@ class MarketSample:
253
249
  )
254
250
 
255
251
  def generate_sample(self, /) -> None:
256
- """Populate :attr:`data` with generated data
252
+ """Populate :attr:`data` with generated data.
257
253
 
258
254
  Returns
259
255
  -------
260
256
  None
261
257
 
262
258
  """
263
-
264
259
  self.dataset = self._gen_market_sample(
265
260
  seed_data=self.seed_data,
266
261
  sample_size=self.sample_size,
@@ -277,11 +272,10 @@ class MarketSample:
277
272
  sample_size: int = 10**6,
278
273
  nthreads: int = NTHREADS,
279
274
  ) -> UPPTestsCounts:
280
- """Generate market data and etstimate UPP test counts on same.
275
+ """Generate market data and compute UPP test counts on same.
281
276
 
282
277
  Parameters
283
278
  ----------
284
-
285
279
  _upp_test_parms
286
280
  Guidelines thresholds for testing UPP and related statistics
287
281
 
@@ -302,10 +296,9 @@ class MarketSample:
302
296
 
303
297
  Returns
304
298
  -------
305
- UPPTestCounts ojbect with of test counts by firm count, ΔHHI and concentration zone
299
+ UPPTestCounts object with of test counts by firm count, ΔHHI and concentration zone
306
300
 
307
301
  """
308
-
309
302
  market_data_sample = self._gen_market_sample(
310
303
  sample_size=sample_size, seed_data=seed_data, nthreads=nthreads
311
304
  )
@@ -319,7 +312,7 @@ class MarketSample:
319
312
  def __sim_enf_cnts_ll(
320
313
  self, _enf_parm_vec: gbl.HMGThresholds, _sim_test_regime: UPPTestRegime, /
321
314
  ) -> UPPTestsCounts:
322
- """A function to parallelize data-generation and testing
315
+ """Parallelize data-generation and testing.
323
316
 
324
317
  The parameters `_sim_enf_cnts_kwargs` are passed unaltered to
325
318
  the parent function, `sim_enf_cnts()`, except that, if provided,
@@ -331,7 +324,6 @@ class MarketSample:
331
324
 
332
325
  Parameters
333
326
  ----------
334
-
335
327
  _enf_parm_vec
336
328
  Guidelines thresholds to test against
337
329
 
@@ -346,7 +338,7 @@ class MarketSample:
346
338
  """
347
339
  sample_sz = self.sample_size
348
340
  subsample_sz = H5_CHUNK_SIZE
349
- iter_count = (sample_sz / subsample_sz).__ceil__() # noqa: PLC2801
341
+ iter_count = (sample_sz / subsample_sz).__ceil__()
350
342
  thread_count = self.nthreads or cpu_count()
351
343
 
352
344
  if (
@@ -382,7 +374,8 @@ class MarketSample:
382
374
  "nthreads": thread_count,
383
375
  })
384
376
 
385
- res_list = Parallel(n_jobs=min(thread_count, iter_count), prefer="threads")(
377
+ _threads = min(thread_count, iter_count)
378
+ res_list = Parallel(n_jobs=_threads, prefer="threads")(
386
379
  delayed(self.__sim_enf_cnts)(
387
380
  _enf_parm_vec,
388
381
  _sim_test_regime,
@@ -434,7 +427,6 @@ class MarketSample:
434
427
  None
435
428
 
436
429
  """
437
-
438
430
  if self.dataset is None:
439
431
  self.enf_counts = self.__sim_enf_cnts_ll(_enf_parm_vec, _upp_test_regime)
440
432
  else:
@@ -445,6 +437,7 @@ class MarketSample:
445
437
  def to_archive(
446
438
  self, zip_: zipfile.ZipFile, _subdir: str = "", /, *, save_dataset: bool = False
447
439
  ) -> None:
440
+ """Serialize market sample to Zip archive."""
448
441
  zpath = zipfile.Path(zip_, at=_subdir)
449
442
  name_root = f"{_PKG_NAME}_market_sample"
450
443
 
@@ -455,7 +448,7 @@ class MarketSample:
455
448
  if all((_ndt := self.dataset is None, _net := self.enf_counts is None)):
456
449
  raise ValueError(
457
450
  "No dataset and/or enforcement counts available for saving. "
458
- "Generate some data or set save_dataset to False to poceed."
451
+ "Generate some data or set save_dataset to False to proceed."
459
452
  )
460
453
 
461
454
  if not _ndt:
@@ -469,6 +462,7 @@ class MarketSample:
469
462
  def from_archive(
470
463
  zip_: zipfile.ZipFile, _subdir: str = "", /, *, restore_dataset: bool = False
471
464
  ) -> MarketSample:
465
+ """Deserialize market sample from Zip archive."""
472
466
  zpath = zipfile.Path(zip_, at=_subdir)
473
467
  name_root = f"{_PKG_NAME}_market_sample"
474
468
 
@@ -486,11 +480,11 @@ class MarketSample:
486
480
 
487
481
  if _dt:
488
482
  with _dp.open("rb") as _hfh:
489
- object.__setattr__( # noqa: PLC2801
483
+ object.__setattr__(
490
484
  market_sample_, "dataset", MarketSampleData.from_h5f(_hfh)
491
485
  )
492
486
  if _et:
493
- object.__setattr__( # noqa: PLC2801
487
+ object.__setattr__(
494
488
  market_sample_, "enf_counts", this_yaml.load(_ep.read_text())
495
489
  )
496
490
  return market_sample_
@@ -499,6 +493,7 @@ class MarketSample:
499
493
  def to_yaml(
500
494
  cls, _r: yaml.representer.RoundTripRepresenter, _d: MarketSample
501
495
  ) -> yaml.MappingNode:
496
+ """Serialize market sample to YAML representation."""
502
497
  retval: yaml.MappingNode = _r.represent_mapping(
503
498
  f"!{cls.__name__}",
504
499
  {
@@ -513,4 +508,5 @@ class MarketSample:
513
508
  def from_yaml(
514
509
  cls, _c: yaml.constructor.RoundTripConstructor, _n: yaml.MappingNode
515
510
  ) -> MarketSample:
511
+ """Deserialize market sample from YAML representation."""
516
512
  return cls(**yaml_rt_mapper(_c, _n))