howso-engine 68.1.0__tar.gz → 68.2.0__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.
Files changed (126) hide show
  1. {howso_engine-68.1.0/howso_engine.egg-info → howso_engine-68.2.0}/PKG-INFO +1 -1
  2. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/feature_attributes/base.py +96 -61
  3. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/feature_attributes/infer_feature_attributes.py +73 -11
  4. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/feature_attributes/time_series.py +26 -14
  5. {howso_engine-68.1.0 → howso_engine-68.2.0/howso_engine.egg-info}/PKG-INFO +1 -1
  6. {howso_engine-68.1.0 → howso_engine-68.2.0}/.coveragerc +0 -0
  7. {howso_engine-68.1.0 → howso_engine-68.2.0}/.flake8 +0 -0
  8. {howso_engine-68.1.0 → howso_engine-68.2.0}/.github/CODEOWNERS +0 -0
  9. {howso_engine-68.1.0 → howso_engine-68.2.0}/.github/templates/version_summary.md +0 -0
  10. {howso_engine-68.1.0 → howso_engine-68.2.0}/.github/workflows/build-pr.yml +0 -0
  11. {howso_engine-68.1.0 → howso_engine-68.2.0}/.github/workflows/build-release.yml +0 -0
  12. {howso_engine-68.1.0 → howso_engine-68.2.0}/.github/workflows/build.yml +0 -0
  13. {howso_engine-68.1.0 → howso_engine-68.2.0}/.github/workflows/rebuild-requirements.yml +0 -0
  14. {howso_engine-68.1.0 → howso_engine-68.2.0}/.github/workflows/version-update.yml +0 -0
  15. {howso_engine-68.1.0 → howso_engine-68.2.0}/.gitignore +0 -0
  16. {howso_engine-68.1.0 → howso_engine-68.2.0}/.gitmodules +0 -0
  17. {howso_engine-68.1.0 → howso_engine-68.2.0}/.pylintrc +0 -0
  18. {howso_engine-68.1.0 → howso_engine-68.2.0}/CONTRIBUTING.md +0 -0
  19. {howso_engine-68.1.0 → howso_engine-68.2.0}/LICENSE-3RD-PARTY.txt +0 -0
  20. {howso_engine-68.1.0 → howso_engine-68.2.0}/LICENSE.txt +0 -0
  21. {howso_engine-68.1.0 → howso_engine-68.2.0}/MANIFEST.in +0 -0
  22. {howso_engine-68.1.0 → howso_engine-68.2.0}/Makefile +0 -0
  23. {howso_engine-68.1.0 → howso_engine-68.2.0}/README.md +0 -0
  24. {howso_engine-68.1.0 → howso_engine-68.2.0}/bin/build.sh +0 -0
  25. {howso_engine-68.1.0 → howso_engine-68.2.0}/config/latest-mt-debug-howso.yml +0 -0
  26. {howso_engine-68.1.0 → howso_engine-68.2.0}/config/latest-mt-howso.yml +0 -0
  27. {howso_engine-68.1.0 → howso_engine-68.2.0}/config/latest-mt-noavx-traces-howso.yml +0 -0
  28. {howso_engine-68.1.0 → howso_engine-68.2.0}/config/latest-mt-traces-howso.yml +0 -0
  29. {howso_engine-68.1.0 → howso_engine-68.2.0}/config/latest-st-debug-howso.yml +0 -0
  30. {howso_engine-68.1.0 → howso_engine-68.2.0}/config/latest-st-howso.yml +0 -0
  31. {howso_engine-68.1.0 → howso_engine-68.2.0}/config/latest-st-traces-howso.yml +0 -0
  32. {howso_engine-68.1.0 → howso_engine-68.2.0}/config/powershell/Download-Tzdata.ps1 +0 -0
  33. {howso_engine-68.1.0 → howso_engine-68.2.0}/config/powershell/Helper-Functions.ps1 +0 -0
  34. {howso_engine-68.1.0 → howso_engine-68.2.0}/hatch.toml +0 -0
  35. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/__main__.py +0 -0
  36. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/__init__.py +0 -0
  37. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/api.py +0 -0
  38. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/base.py +0 -0
  39. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/cache.py +0 -0
  40. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/client.py +0 -0
  41. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/configuration.py +0 -0
  42. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/exceptions.py +0 -0
  43. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/feature_flags.py +0 -0
  44. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/pandas/__init__.py +0 -0
  45. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/pandas/client.py +0 -0
  46. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/protocols.py +0 -0
  47. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/schemas/__init__.py +0 -0
  48. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/schemas/aggregate_reaction.py +0 -0
  49. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/schemas/base.py +0 -0
  50. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/schemas/group_reaction.py +0 -0
  51. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/schemas/project.py +0 -0
  52. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/schemas/reaction.py +0 -0
  53. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/schemas/session.py +0 -0
  54. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/schemas/trainee.py +0 -0
  55. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/schemas/version.py +0 -0
  56. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/client/typing.py +0 -0
  57. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/direct/__init__.py +0 -0
  58. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/direct/_utilities.py +0 -0
  59. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/direct/client.py +0 -0
  60. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/direct/schemas/__init__.py +0 -0
  61. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/direct/schemas/trainee.py +0 -0
  62. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/engine/__init__.py +0 -0
  63. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/engine/client.py +0 -0
  64. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/engine/project.py +0 -0
  65. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/engine/session.py +0 -0
  66. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/engine/trainee.py +0 -0
  67. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/howso-engine/howso.caml +0 -0
  68. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/howso-engine/migrations/migrations.caml +0 -0
  69. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/howso-engine/version.json +0 -0
  70. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/scikit/__init__.py +0 -0
  71. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/scikit/scikit.py +0 -0
  72. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/__init__.py +0 -0
  73. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/decorators.py +0 -0
  74. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/fanout_features.py +0 -0
  75. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/feature_attributes/__init__.py +0 -0
  76. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/feature_attributes/abstract_data.py +0 -0
  77. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/feature_attributes/pandas.py +0 -0
  78. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/feature_attributes/protocols.py +0 -0
  79. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/feature_attributes/relational.py +0 -0
  80. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/feature_attributes/serializers.py +0 -0
  81. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/feature_attributes/suggestions.py +0 -0
  82. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/feature_attributes/warnings.py +0 -0
  83. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/features.py +0 -0
  84. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/installation_verification/__init__.py +0 -0
  85. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/installation_verification/__main__.py +0 -0
  86. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/installation_verification/_constants.py +0 -0
  87. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/installation_verification/_helpers.py +0 -0
  88. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/installation_verification/_output.py +0 -0
  89. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/installation_verification/_probes.py +0 -0
  90. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/installation_verification/_types.py +0 -0
  91. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/installation_verification/checks_cpu.py +0 -0
  92. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/installation_verification/checks_engine.py +0 -0
  93. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/installation_verification/checks_environment.py +0 -0
  94. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/installation_verification/checks_optional.py +0 -0
  95. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/installation_verification/main.py +0 -0
  96. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/installation_verification/registry.py +0 -0
  97. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/internals.py +0 -0
  98. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/json_wrapper.py +0 -0
  99. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/locale.py +0 -0
  100. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/monitors.py +0 -0
  101. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/posix.py +0 -0
  102. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/progress.py +0 -0
  103. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/random.py +0 -0
  104. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/reaction.py +0 -0
  105. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/testing.py +0 -0
  106. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/tokenizing.py +0 -0
  107. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso/utilities/utilities.py +0 -0
  108. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso_engine.egg-info/SOURCES.txt +0 -0
  109. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso_engine.egg-info/dependency_links.txt +0 -0
  110. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso_engine.egg-info/entry_points.txt +0 -0
  111. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso_engine.egg-info/requires.txt +0 -0
  112. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso_engine.egg-info/scm_file_list.json +0 -0
  113. {howso_engine-68.1.0 → howso_engine-68.2.0}/howso_engine.egg-info/top_level.txt +0 -0
  114. {howso_engine-68.1.0 → howso_engine-68.2.0}/pyproject.toml +0 -0
  115. {howso_engine-68.1.0 → howso_engine-68.2.0}/pyrightconfig.json +0 -0
  116. {howso_engine-68.1.0 → howso_engine-68.2.0}/requirements-3.11-dev.txt +0 -0
  117. {howso_engine-68.1.0 → howso_engine-68.2.0}/requirements-3.11.txt +0 -0
  118. {howso_engine-68.1.0 → howso_engine-68.2.0}/requirements-3.12-dev.txt +0 -0
  119. {howso_engine-68.1.0 → howso_engine-68.2.0}/requirements-3.12.txt +0 -0
  120. {howso_engine-68.1.0 → howso_engine-68.2.0}/requirements-3.13-dev.txt +0 -0
  121. {howso_engine-68.1.0 → howso_engine-68.2.0}/requirements-3.13.txt +0 -0
  122. {howso_engine-68.1.0 → howso_engine-68.2.0}/requirements-3.14-dev.txt +0 -0
  123. {howso_engine-68.1.0 → howso_engine-68.2.0}/requirements-3.14.txt +0 -0
  124. {howso_engine-68.1.0 → howso_engine-68.2.0}/ruff.toml +0 -0
  125. {howso_engine-68.1.0 → howso_engine-68.2.0}/setup.cfg +0 -0
  126. {howso_engine-68.1.0 → howso_engine-68.2.0}/version.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: howso-engine
3
- Version: 68.1.0
3
+ Version: 68.2.0
4
4
  Summary: The Howso Engine™ is a natively and fully explainable ML engine, serving as an alternative to black box AI neural networks.
5
5
  Author: Howso Incorporated
6
6
  Author-email: support@howso.com
@@ -77,8 +77,16 @@ INTEGER_MAX = int(math.pow(2, 53))
77
77
  LINUX_DT_MAX = "2262-04-11"
78
78
  WIN_DT_MAX = "6053-01-24"
79
79
 
80
- SIGNIFICANT_THRESHOLD_DEFAULT: int = 30
81
- """The default number of cases a preserved rare value is expected to keep after distillation."""
80
+ PRESERVE_RARE_VALUES_MIN_CASES_DEFAULT: int = 15
81
+ """The default number of cases a value needs in the data to be considered for rare value preservation."""
82
+
83
+ PRESERVE_RARE_VALUES_DISTILLED_MIN_CASES_DEFAULT: int = 3
84
+ """
85
+ The default number of cases a preserved rare value is expected to keep after distillation.
86
+
87
+ It is also the fewest cases a frequent value is expected to keep when its weight is reduced to fund
88
+ the rare values.
89
+ """
82
90
 
83
91
  DEFAULT_MAX_DISTILLED_CASES: int = 50_000
84
92
  """The distillation target assumed for rare value weighting when `max_distilled_cases` is not given."""
@@ -1208,7 +1216,8 @@ class InferFeatureAttributesBase(ABC):
1208
1216
  ordinal_feature_values: dict[str, list[Any]] | None = None,
1209
1217
  preserve_rare_values: PreserveRareValues | None = None,
1210
1218
  preserve_rare_values_caps: PreserveRareValuesCaps | None = None,
1211
- significance_threshold: int = SIGNIFICANT_THRESHOLD_DEFAULT,
1219
+ preserve_rare_values_distilled_min_cases: int = PRESERVE_RARE_VALUES_DISTILLED_MIN_CASES_DEFAULT,
1220
+ preserve_rare_values_min_cases: int = PRESERVE_RARE_VALUES_MIN_CASES_DEFAULT,
1212
1221
  tight_bounds: Iterable[str] | None = None,
1213
1222
  types: dict[str, str] | dict[str, MutableSequence[str]] | None = None,
1214
1223
  ) -> dict:
@@ -1554,8 +1563,10 @@ class InferFeatureAttributesBase(ABC):
1554
1563
  self.suggestions_collector.append(FanoutFeaturesSuggestion(candidate_fanout))
1555
1564
 
1556
1565
  # Compute or suggest `preserve_rare_values` configuration
1557
- self._process_rare_values(preserve_rare_values, max_distilled_cases, significance_threshold,
1558
- enable_suggestions, caps=_normalize_rare_value_caps(preserve_rare_values_caps))
1566
+ self._process_rare_values(preserve_rare_values, max_distilled_cases, enable_suggestions,
1567
+ min_cases=preserve_rare_values_min_cases,
1568
+ distilled_min_cases=preserve_rare_values_distilled_min_cases,
1569
+ caps=_normalize_rare_value_caps(preserve_rare_values_caps))
1559
1570
 
1560
1571
  # Re-order the keys like the original dataframe
1561
1572
  ordered_attributes = {}
@@ -2205,7 +2216,8 @@ class InferFeatureAttributesBase(ABC):
2205
2216
  def _find_protected_value_candidates(
2206
2217
  self,
2207
2218
  max_distilled_cases: int,
2208
- significance_threshold: int,
2219
+ min_cases: int,
2220
+ distilled_min_cases: int,
2209
2221
  *,
2210
2222
  features: Container[str] | None = None,
2211
2223
  ) -> tuple[PreserveRareValuesMap, list[dict]]:
@@ -2216,9 +2228,11 @@ class InferFeatureAttributesBase(ABC):
2216
2228
  ----------
2217
2229
  max_distilled_cases : int
2218
2230
  The maximum number of cases in the resultant data following distillation.
2219
- significance_threshold : int
2220
- The number of cases that are expected to result in a maintained signal for a particular
2221
- value post-distillation.
2231
+ min_cases : int
2232
+ The number of cases a value needs in the data to be a candidate.
2233
+ distilled_min_cases : int
2234
+ The number of cases a value should keep after distillation; a value expected to keep
2235
+ fewer is a candidate.
2222
2236
  features : Container of str, optional
2223
2237
  The features to search. Defaults to every nominal feature.
2224
2238
 
@@ -2238,7 +2252,6 @@ class InferFeatureAttributesBase(ABC):
2238
2252
  if self._get_unique_count(feature) == total_cases:
2239
2253
  # Don't make a suggestion for a completely unique feature
2240
2254
  continue
2241
- threshold = significance_threshold
2242
2255
  # Every null form (None, NaN, NA) is counted together, so it is one candidate, None
2243
2256
  null_seen = False
2244
2257
  for unique_value in self._get_unique_values(feature):
@@ -2254,10 +2267,10 @@ class InferFeatureAttributesBase(ABC):
2254
2267
  self.warnings_collector.triage(IFAWarningEmitterType.VALUE_COUNTS_PROCESSING, feature)
2255
2268
  continue
2256
2269
  # Don't include values that aren't significant to begin with
2257
- if count < threshold:
2270
+ if count < min_cases:
2258
2271
  continue
2259
2272
  expected_freq_at_target_size = (max_distilled_cases / total_cases) * count
2260
- if expected_freq_at_target_size < threshold:
2273
+ if expected_freq_at_target_size < distilled_min_cases:
2261
2274
  value_counts.append({"feature": feature, "value": value, "count": count})
2262
2275
  if feature not in pvm:
2263
2276
  pvm[feature] = [value]
@@ -2270,7 +2283,8 @@ class InferFeatureAttributesBase(ABC):
2270
2283
  self,
2271
2284
  feature: str,
2272
2285
  targets: Sequence[ProtectedValueMultiplier],
2273
- significance_threshold: int,
2286
+ min_cases: int,
2287
+ distilled_min_cases: int,
2274
2288
  floor: float,
2275
2289
  *,
2276
2290
  fit: Literal["largest_first", "scale"],
@@ -2280,12 +2294,12 @@ class InferFeatureAttributesBase(ABC):
2280
2294
  Compute the case weight multipliers of a feature that fund the preservation of its rare values.
2281
2295
 
2282
2296
  The `targets` are the rare values to weight up, each to its given multiplier. Values with
2283
- at least `floor` cases are significant: these are all scaled by one common factor, except
2284
- that none is scaled below `floor` cases, so a value that is significant before
2285
- distillation stays significant after it. With a `cap`, the factor is no smaller than
2286
- ``1 - cap``. The factor is chosen so the total case weight of the feature is unchanged.
2287
- Every other value, whether it has fewer than `significance_threshold` cases or falls
2288
- between that and the floor, keeps a multiplier of 1.
2297
+ at least `min_cases` and at least `floor` cases are significant: these are all scaled by one
2298
+ common factor, except that none is scaled below `floor` cases, so a value that is
2299
+ significant before distillation stays significant after it. With a `cap`, the factor is
2300
+ no smaller than ``1 - cap``. The factor is chosen so the total case weight of the feature is unchanged.
2301
+ Every other value, whether it has fewer than `min_cases` cases or falls below the floor,
2302
+ keeps a multiplier of 1.
2289
2303
 
2290
2304
  When the significant values cannot fund every target within those limits, `fit` decides
2291
2305
  what happens: ``"largest_first"`` keeps the targets with the most cases and leaves the
@@ -2300,11 +2314,13 @@ class InferFeatureAttributesBase(ABC):
2300
2314
  The rare values and the multipliers they should receive, each at least 1. Values must
2301
2315
  already be in stored form, as :func:`_as_feature_value` gives them: nulls as None and
2302
2316
  numpy scalars as Python types.
2303
- significance_threshold : int
2317
+ min_cases : int
2304
2318
  The number of cases below which a value is small.
2319
+ distilled_min_cases : int
2320
+ The number of cases each value should keep after distillation.
2305
2321
  floor : float
2306
- The number of cases a significant value must keep, ``significance_threshold`` divided
2307
- by the reduction ratio of distillation, or 0 when no distillation target is known.
2322
+ The number of cases a significant value must keep, ``distilled_min_cases`` divided by
2323
+ the reduction ratio of distillation, or 0 when no distillation target is known.
2308
2324
  fit : {"largest_first", "scale"}
2309
2325
  How to reconcile the targets with the available case weight when they do not fit.
2310
2326
  cap : float, optional
@@ -2318,7 +2334,7 @@ class InferFeatureAttributesBase(ABC):
2318
2334
  every significant value, whether scaled by the common factor or held at the floor. Values
2319
2335
  that keep a multiplier of 1 are not listed. Every listed value keeps at least `floor`
2320
2336
  cases of weight, so a configuration lists at most ``total_cases / floor`` values, which
2321
- is the distillation target divided by the significance threshold. None when the
2337
+ is the distillation target divided by `distilled_min_cases`. None when the
2322
2338
  feature has no significant values to take case weight from, or no target could be
2323
2339
  funded.
2324
2340
  list of ProtectedValueMultiplier
@@ -2339,8 +2355,8 @@ class InferFeatureAttributesBase(ABC):
2339
2355
 
2340
2356
  # The algorithm, in four steps:
2341
2357
  # 1. Sort every non-target value into "unchanged" (keeps weight 1) or "significant" (can give
2342
- # weight up). A significant value has at least `floor` cases, so it can lose some and still
2343
- # keep `significance_threshold` after distillation.
2358
+ # weight up). A significant value has at least `min_cases` and at least `floor` cases, so it
2359
+ # can lose some and still keep `distilled_min_cases` after distillation.
2344
2360
  # 2. Compare what the targets need ("deficits") with what the significant values can give
2345
2361
  # ("budgets"), and decide which targets are funded.
2346
2362
  # 3. Find the one factor that scales the significant values so their total loss equals the
@@ -2349,7 +2365,7 @@ class InferFeatureAttributesBase(ABC):
2349
2365
  # values, each at the factor or held at the floor.
2350
2366
 
2351
2367
  # Step 1: classify the other values. A value is unchanged when it is small (fewer than
2352
- # `significance_threshold` cases) or already below the floor, since it has no weight to spare and
2368
+ # `min_cases` cases) or already below the floor, since it has no weight to spare and
2353
2369
  # is not a target to lift. Nulls are counted together as the single value None.
2354
2370
  null_seen = False
2355
2371
  unchanged_counts: list[int] = []
@@ -2373,7 +2389,7 @@ class InferFeatureAttributesBase(ABC):
2373
2389
  except (TypeError, ValueError):
2374
2390
  self.warnings_collector.triage(IFAWarningEmitterType.VALUE_COUNTS_PROCESSING, feature)
2375
2391
  continue
2376
- if count < significance_threshold or count < floor:
2392
+ if count < min_cases or count < floor:
2377
2393
  unchanged_counts.append(count)
2378
2394
  else:
2379
2395
  significant.append((value, count))
@@ -2391,7 +2407,8 @@ class InferFeatureAttributesBase(ABC):
2391
2407
  target_counts=lifted_counts,
2392
2408
  other_counts=all_other_counts,
2393
2409
  total_cases=total_cases,
2394
- significance_threshold=significance_threshold,
2410
+ min_cases=min_cases,
2411
+ distilled_min_cases=distilled_min_cases,
2395
2412
  min_multiplier=min_multiplier,
2396
2413
  ),
2397
2414
  "multiplier_scale": None,
@@ -2447,7 +2464,8 @@ class InferFeatureAttributesBase(ABC):
2447
2464
  target_counts=lifted_counts,
2448
2465
  other_counts=all_other_counts,
2449
2466
  total_cases=total_cases,
2450
- significance_threshold=significance_threshold,
2467
+ min_cases=min_cases,
2468
+ distilled_min_cases=distilled_min_cases,
2451
2469
  min_multiplier=min_multiplier,
2452
2470
  ),
2453
2471
  "multiplier_scale": None,
@@ -2524,7 +2542,7 @@ class InferFeatureAttributesBase(ABC):
2524
2542
 
2525
2543
  @staticmethod
2526
2544
  def _min_distilled_cases_to_fit(target_counts: np.ndarray, other_counts: np.ndarray, total_cases: int,
2527
- significance_threshold: int, min_multiplier: float) -> int | None:
2545
+ min_cases: int, distilled_min_cases: int, min_multiplier: float) -> int | None:
2528
2546
  """
2529
2547
  Find the smallest `max_distilled_cases` at which every rare value of a feature can be preserved.
2530
2548
 
@@ -2537,7 +2555,9 @@ class InferFeatureAttributesBase(ABC):
2537
2555
  at each searched target, since the floor falls as the target grows.
2538
2556
  total_cases : int
2539
2557
  The number of cases in the data.
2540
- significance_threshold : int
2558
+ min_cases : int
2559
+ The number of cases below which a value is small and cannot donate.
2560
+ distilled_min_cases : int
2541
2561
  The number of cases each value should keep after distillation.
2542
2562
  min_multiplier : float
2543
2563
  The smallest multiplier the feature's significant values may receive.
@@ -2555,10 +2575,10 @@ class InferFeatureAttributesBase(ABC):
2555
2575
  def fits(max_distilled_cases: int) -> bool:
2556
2576
  max_distilled_cases, _ = get_optimized_partition_size(row_count=total_cases,
2557
2577
  max_partition_size=max_distilled_cases)
2558
- floor = significance_threshold * total_cases / max_distilled_cases
2559
- # Targets below the floor need funding; any value at or above it can give some up
2578
+ floor = distilled_min_cases * total_cases / max_distilled_cases
2579
+ # Targets below the floor need funding; any value at or above it that is not small can give some up
2560
2580
  needed = float((floor - target_counts[target_counts < floor]).sum())
2561
- significant = counts[counts >= floor]
2581
+ significant = counts[(counts >= floor) & (counts >= min_cases)]
2562
2582
  budget = float((significant - np.maximum(floor, min_multiplier * significant)).sum())
2563
2583
  return needed <= budget
2564
2584
 
@@ -2577,14 +2597,15 @@ class InferFeatureAttributesBase(ABC):
2577
2597
  self,
2578
2598
  max_distilled_cases: int,
2579
2599
  values_map: PreserveRareValuesMap,
2580
- significance_threshold: int,
2600
+ min_cases: int,
2601
+ distilled_min_cases: int,
2581
2602
  caps: Mapping[str, float] | None = None,
2582
2603
  ) -> tuple[FullPreserveRareValuesConfig, PreserveRareValuesMap, list[RareValuePreservationLimit],
2583
2604
  dict[str, list[tuple[Any, int]]]]:
2584
2605
  """
2585
2606
  Determine the case weight multipliers for the provided rare values and the other values of their features.
2586
2607
 
2587
- Each rare value is weighted so that it keeps `significance_threshold` cases after
2608
+ Each rare value is weighted so that it keeps `distilled_min_cases` cases after
2588
2609
  distillation, funded by the feature's significant values as described in
2589
2610
  :meth:`_reweight_rare_values`. When not every rare value fits, those with the most cases
2590
2611
  are preserved.
@@ -2595,9 +2616,10 @@ class InferFeatureAttributesBase(ABC):
2595
2616
  The maximum number of cases in the resultant data following distillation.
2596
2617
  values_map : PreserveRareValuesMap
2597
2618
  A mapping of feature name to list of rare values to compute multipliers for.
2598
- significance_threshold : int
2599
- The number of cases that are expected to result in a maintained signal for a
2600
- particular value post-distillation.
2619
+ min_cases : int
2620
+ The number of cases below which a value is small and does not fund the rare values.
2621
+ distilled_min_cases : int
2622
+ The number of cases each rare value should keep after distillation.
2601
2623
  caps : Mapping of str to float, optional
2602
2624
  The largest share of its weight a significant value of each listed feature may give up.
2603
2625
 
@@ -2612,7 +2634,7 @@ class InferFeatureAttributesBase(ABC):
2612
2634
  The features whose rare values could not all be preserved.
2613
2635
  dict of str to list of tuple
2614
2636
  The values of each feature that were not lifted, with their case counts, because each
2615
- keeps the significance threshold on its own at this target.
2637
+ keeps `distilled_min_cases` cases after distillation on its own at this target.
2616
2638
  """
2617
2639
  prvc: FullPreserveRareValuesConfig = {}
2618
2640
  protected_map: PreserveRareValuesMap = {}
@@ -2624,7 +2646,7 @@ class InferFeatureAttributesBase(ABC):
2624
2646
  if feature not in self.attributes:
2625
2647
  # Multiprocessing is enabled, and this feature will be handled in another process
2626
2648
  continue
2627
- floor = significance_threshold * total_cases / max_distilled_cases
2649
+ floor = distilled_min_cases * total_cases / max_distilled_cases
2628
2650
  targets: list[ProtectedValueMultiplier] = []
2629
2651
  for value in _dedupe_values(values):
2630
2652
  count = self._get_value_count(feature, value)
@@ -2632,7 +2654,7 @@ class InferFeatureAttributesBase(ABC):
2632
2654
  raise ValueError(f"Specified protected value `{value}` not found in column `{feature}`. "
2633
2655
  "Please verify the value and type.")
2634
2656
  multiplier = floor / count
2635
- # A value that keeps the threshold on its own does not need signal preservation
2657
+ # A value that keeps `distilled_min_cases` on its own does not need signal preservation
2636
2658
  if multiplier <= 1:
2637
2659
  skipped.setdefault(feature, []).append((_as_feature_value(value), count))
2638
2660
  continue
@@ -2642,7 +2664,8 @@ class InferFeatureAttributesBase(ABC):
2642
2664
  config, kept, limit = self._reweight_rare_values(
2643
2665
  feature=feature,
2644
2666
  targets=targets,
2645
- significance_threshold=significance_threshold,
2667
+ min_cases=min_cases,
2668
+ distilled_min_cases=distilled_min_cases,
2646
2669
  floor=floor,
2647
2670
  fit="largest_first",
2648
2671
  cap=caps.get(feature),
@@ -2658,9 +2681,10 @@ class InferFeatureAttributesBase(ABC):
2658
2681
  self,
2659
2682
  preserve_rare_values: PreserveRareValues | None,
2660
2683
  max_distilled_cases: int | None,
2661
- significance_threshold: int,
2662
2684
  enable_suggestions: bool = True,
2663
2685
  *,
2686
+ min_cases: int,
2687
+ distilled_min_cases: int,
2664
2688
  caps: Mapping[str, float] | None = None,
2665
2689
  ) -> None:
2666
2690
  """
@@ -2708,12 +2732,14 @@ class InferFeatureAttributesBase(ABC):
2708
2732
  if not enable_suggestions or self._get_row_count() < requested_max_distilled_cases:
2709
2733
  return
2710
2734
  candidates, values_ranking = self._find_protected_value_candidates(
2711
- max_distilled_cases=max_distilled_cases, significance_threshold=significance_threshold)
2735
+ max_distilled_cases=max_distilled_cases, min_cases=min_cases,
2736
+ distilled_min_cases=distilled_min_cases)
2712
2737
  if candidates:
2713
2738
  candidate_prvc, protected_map, limits, _ = self._compute_preserve_rare_values_config(
2714
2739
  max_distilled_cases=max_distilled_cases,
2715
2740
  values_map=candidates,
2716
- significance_threshold=significance_threshold,
2741
+ min_cases=min_cases,
2742
+ distilled_min_cases=distilled_min_cases,
2717
2743
  caps=caps,
2718
2744
  )
2719
2745
  if candidate_prvc or limits:
@@ -2743,7 +2769,8 @@ class InferFeatureAttributesBase(ABC):
2743
2769
  "inference with the `types` parameter.")
2744
2770
  values_map, _ = self._find_protected_value_candidates(
2745
2771
  max_distilled_cases=max_distilled_cases,
2746
- significance_threshold=significance_threshold,
2772
+ min_cases=min_cases,
2773
+ distilled_min_cases=distilled_min_cases,
2747
2774
  features=search_features,
2748
2775
  )
2749
2776
  named_features = list(search_features or [])
@@ -2758,14 +2785,16 @@ class InferFeatureAttributesBase(ABC):
2758
2785
  prvc.update(self._fund_given_multipliers(
2759
2786
  given=given,
2760
2787
  max_distilled_cases=max_distilled_cases,
2761
- significance_threshold=significance_threshold,
2788
+ min_cases=min_cases,
2789
+ distilled_min_cases=distilled_min_cases,
2762
2790
  caps=caps,
2763
2791
  ))
2764
2792
  # Plain values get their multipliers computed
2765
2793
  computed, _, limits, skipped = self._compute_preserve_rare_values_config(
2766
2794
  max_distilled_cases=max_distilled_cases,
2767
2795
  values_map=values_map,
2768
- significance_threshold=significance_threshold,
2796
+ min_cases=min_cases,
2797
+ distilled_min_cases=distilled_min_cases,
2769
2798
  caps=caps,
2770
2799
  )
2771
2800
  prvc.update(computed)
@@ -2781,13 +2810,13 @@ class InferFeatureAttributesBase(ABC):
2781
2810
  self.warnings_collector.triage(
2782
2811
  IFAWarningEmitterType.SIMPLE,
2783
2812
  f"No rare value candidates were found in {names} at a `max_distilled_cases` of "
2784
- f"{requested_max_distilled_cases:,}: every value either has fewer than {significance_threshold} "
2785
- "cases or keeps that many after distillation on its own.\n"
2813
+ f"{requested_max_distilled_cases:,}: every value either has fewer than {min_cases} cases or "
2814
+ f"keeps {distilled_min_cases} after distillation on its own.\n"
2786
2815
  "These features need no rare value preservation at this target."
2787
2816
  )
2788
- # A value the user named that was not lifted keeps the threshold on its own; it is reported with its
2817
+ # A value the user named that was not lifted keeps `distilled_min_cases` on its own; it is reported with its
2789
2818
  # count against the floor, whether or not the other values named for the feature were lifted
2790
- floor = significance_threshold * self._get_row_count() / max_distilled_cases
2819
+ floor = distilled_min_cases * self._get_row_count() / max_distilled_cases
2791
2820
  floor_text = f"{floor:,.0f}" if float(floor).is_integer() else f"{floor:,.1f}"
2792
2821
  for feature, values in skipped.items():
2793
2822
  if feature not in self.attributes:
@@ -2799,9 +2828,10 @@ class InferFeatureAttributesBase(ABC):
2799
2828
  else f"No value weight multipliers were written for `{feature}`.")
2800
2829
  self.warnings_collector.triage(
2801
2830
  IFAWarningEmitterType.SIMPLE,
2802
- f"The values {listed} of feature `{feature}` were not lifted: each keeps the significance "
2803
- f"threshold on its own at a `max_distilled_cases` of {requested_max_distilled_cases:,}, where a "
2804
- f"value needs at least {floor_text} cases to keep {significance_threshold} after distillation.\n"
2831
+ f"The values {listed} of feature `{feature}` were not lifted: each keeps {distilled_min_cases} "
2832
+ f"cases after distillation on its own at a `max_distilled_cases` of "
2833
+ f"{requested_max_distilled_cases:,}, where a value needs at least {floor_text} cases to keep "
2834
+ f"{distilled_min_cases}.\n"
2805
2835
  f"{outcome}"
2806
2836
  )
2807
2837
  if not user_set_mdc:
@@ -2829,7 +2859,8 @@ class InferFeatureAttributesBase(ABC):
2829
2859
  self,
2830
2860
  given: PreserveRareValuesConfig,
2831
2861
  max_distilled_cases: int,
2832
- significance_threshold: int,
2862
+ min_cases: int,
2863
+ distilled_min_cases: int,
2833
2864
  caps: Mapping[str, float],
2834
2865
  ) -> FullPreserveRareValuesConfig:
2835
2866
  """
@@ -2842,8 +2873,11 @@ class InferFeatureAttributesBase(ABC):
2842
2873
  :func:`_split_rare_values` validates and deduplicates them.
2843
2874
  max_distilled_cases : int
2844
2875
  The distillation target, as :func:`get_optimized_partition_size` rounds it.
2845
- significance_threshold : int
2846
- The number of cases below which a value is small.
2876
+ min_cases : int
2877
+ The number of cases below which a value is small and does not fund the rare values.
2878
+ distilled_min_cases : int
2879
+ The number of cases each value should keep after distillation, which sets the floor
2880
+ no funding value is reduced below.
2847
2881
  caps : Mapping of str to float
2848
2882
  The largest share of its weight a significant value of each listed feature may give up.
2849
2883
 
@@ -2874,11 +2908,12 @@ class InferFeatureAttributesBase(ABC):
2874
2908
  # Every pair asked to hold its weight, so there is nothing to fund and no value to reweight;
2875
2909
  # an unlisted value holds its weight already
2876
2910
  continue
2877
- floor = significance_threshold * total_cases / max_distilled_cases
2911
+ floor = distilled_min_cases * total_cases / max_distilled_cases
2878
2912
  config, _, limit = self._reweight_rare_values(
2879
2913
  feature=feature,
2880
2914
  targets=targets,
2881
- significance_threshold=significance_threshold,
2915
+ min_cases=min_cases,
2916
+ distilled_min_cases=distilled_min_cases,
2882
2917
  floor=floor,
2883
2918
  fit="scale",
2884
2919
  cap=caps.get(feature),
@@ -2,11 +2,16 @@ from __future__ import annotations
2
2
 
3
3
  from collections.abc import Iterable, Sequence
4
4
  from typing import Any, Literal, overload, TYPE_CHECKING, TypeAlias, TypedDict, Unpack
5
+ import warnings
5
6
 
6
7
  import pandas as pd
7
8
 
8
9
  from howso.utilities.feature_attributes.abstract_data import InferFeatureAttributesAbstractData
9
- from howso.utilities.feature_attributes.base import FeatureAttributesBase
10
+ from howso.utilities.feature_attributes.base import (
11
+ FeatureAttributesBase,
12
+ PRESERVE_RARE_VALUES_DISTILLED_MIN_CASES_DEFAULT,
13
+ PRESERVE_RARE_VALUES_MIN_CASES_DEFAULT,
14
+ )
10
15
  from howso.utilities.feature_attributes.pandas import InferFeatureAttributesDataFrame
11
16
  from howso.utilities.feature_attributes.protocols import (
12
17
  IFACompatibleADCProtocol,
@@ -46,6 +51,8 @@ class InferOptions(TypedDict, total=False):
46
51
  ordinal_feature_values: dict[str, list[Any] | tuple[str]]
47
52
  preserve_rare_values: PreserveRareValues | None
48
53
  preserve_rare_values_caps: PreserveRareValuesCaps | None
54
+ preserve_rare_values_distilled_min_cases: int
55
+ preserve_rare_values_min_cases: int
49
56
  significance_threshold: int
50
57
  tight_bounds: Iterable[str]
51
58
  types: dict[str, FeatureType] | dict[FeatureType, list[str]]
@@ -319,14 +326,16 @@ def infer_feature_attributes(
319
326
  A dict maps each feature name to one of three forms:
320
327
 
321
328
  - A list of values to protect. Each is weighted to keep approximately
322
- `significance_threshold` cases after distillation, funded by the feature's other values.
329
+ `preserve_rare_values_distilled_min_cases` cases after distillation, funded by the
330
+ feature's other values.
323
331
  When not every value fits, those with the most cases are given priority. Without
324
332
  `max_distilled_cases`, the multipliers are computed for a default target of 50,000 cases.
325
333
  - A list of dicts of "value" and "multiplier", giving the multiplier each value should
326
334
  receive, at least 1. The feature's other values are reweighted to fund them so the total
327
- case weight is unchanged: values with fewer cases than the significance threshold keep a
328
- weight of 1, and every other value is scaled by one common factor, except that no value is
329
- scaled below the number of cases that keeps the threshold after distillation. See
335
+ case weight is unchanged: values with fewer than `preserve_rare_values_min_cases` cases
336
+ keep a weight of 1, and every other value is scaled by one common factor, except that no
337
+ value is scaled below the number of cases that keeps
338
+ `preserve_rare_values_distilled_min_cases` after distillation. See
330
339
  `preserve_rare_values_caps` to limit how much weight those values give up. Without
331
340
  `max_distilled_cases`, the floor is computed for a target of 50,000 cases. If the other
332
341
  values cannot fund the multipliers, each multiplier's increase over 1 is scaled down and a
@@ -355,9 +364,20 @@ def infer_feature_attributes(
355
364
  of its weight; given as a dict, the value for each feature is the largest share of its
356
365
  weight a significant value of that feature may give up, greater than 0 and at most 1.
357
366
  Features not listed give up as much as preserving every rare value requires, which draws
358
- the weights of their significant values toward the floor that keeps the significance
359
- threshold after distillation. When a cap leaves too little weight, the rare values with
360
- the most cases are preserved and a warning reports how many.
367
+ the weights of their significant values toward the floor that keeps
368
+ `preserve_rare_values_distilled_min_cases` cases after distillation. When a cap leaves too
369
+ little weight, the rare values with the most cases are preserved and a warning reports how
370
+ many.
371
+
372
+ preserve_rare_values_distilled_min_cases : int, default 3
373
+ (Optional) The number of cases each preserved rare value is expected to keep after
374
+ distillation. It is also the fewest cases a frequent value is expected to keep when its
375
+ case weight is reduced to fund the rare values.
376
+
377
+ preserve_rare_values_min_cases : int, default 15
378
+ (Optional) The number of cases a value needs in the data to be considered for rare value
379
+ preservation. Values with fewer cases keep their case weight and do not fund rare values.
380
+ Must be at least `preserve_rare_values_distilled_min_cases`.
361
381
 
362
382
  rate_boundaries : dict, optional
363
383
  (Optional) For time series, specify the rate boundaries in the form
@@ -377,9 +397,10 @@ def infer_feature_attributes(
377
397
  }
378
398
  }
379
399
 
380
- significance_threshold : int, default 30
381
- (Optional) After data distillation, the number of cases that are expected to result in a
382
- signal for preserved rare values.
400
+ significance_threshold : int, optional
401
+ (Optional) Deprecated; use `preserve_rare_values_min_cases`, which receives this value.
402
+ The number of cases each rare value keeps after distillation is set only by
403
+ `preserve_rare_values_distilled_min_cases`.
383
404
 
384
405
  tables : Iterable of TableNameProtocol
385
406
  (Optional, required for datastores) An Iterable of table names to
@@ -524,4 +545,45 @@ def infer_feature_attributes(
524
545
  else:
525
546
  raise NotImplementedError("Data not recognized as a DataFrame, AbstractData class, or compatible datastore.")
526
547
 
548
+ _resolve_rare_value_thresholds(kwargs)
527
549
  return infer(**kwargs)
550
+
551
+
552
+ def _resolve_rare_value_thresholds(options: dict[str, Any]) -> None:
553
+ """
554
+ Check the rare value thresholds in `options`, replacing a deprecated `significance_threshold`.
555
+
556
+ A `significance_threshold` is moved to `preserve_rare_values_min_cases` with a deprecation
557
+ warning. The number of cases kept after distillation is set only by
558
+ `preserve_rare_values_distilled_min_cases`.
559
+
560
+ Parameters
561
+ ----------
562
+ options : dict
563
+ The keyword arguments given to :func:`infer_feature_attributes`, updated in place.
564
+
565
+ Raises
566
+ ------
567
+ ValueError
568
+ If both `significance_threshold` and `preserve_rare_values_min_cases` are given, or
569
+ `preserve_rare_values_min_cases` is less than `preserve_rare_values_distilled_min_cases`.
570
+ """
571
+ significance_threshold = options.pop("significance_threshold", None)
572
+ if significance_threshold is not None:
573
+ if options.get("preserve_rare_values_min_cases") is not None:
574
+ raise ValueError("`significance_threshold` is deprecated and replaced by "
575
+ "`preserve_rare_values_min_cases`; pass only `preserve_rare_values_min_cases`.")
576
+ warnings.warn(
577
+ "`significance_threshold` is deprecated; use `preserve_rare_values_min_cases`, which receives its value. "
578
+ "The number of cases each rare value keeps after distillation is set by "
579
+ "`preserve_rare_values_distilled_min_cases`.",
580
+ DeprecationWarning, stacklevel=3,
581
+ )
582
+ options["preserve_rare_values_min_cases"] = significance_threshold
583
+ min_cases = options.get("preserve_rare_values_min_cases", PRESERVE_RARE_VALUES_MIN_CASES_DEFAULT)
584
+ distilled_min_cases = options.get("preserve_rare_values_distilled_min_cases",
585
+ PRESERVE_RARE_VALUES_DISTILLED_MIN_CASES_DEFAULT)
586
+ if min_cases < distilled_min_cases:
587
+ raise ValueError(f"`preserve_rare_values_min_cases` ({min_cases}) must be at least "
588
+ f"`preserve_rare_values_distilled_min_cases` ({distilled_min_cases}): a value with fewer "
589
+ "cases in the data than it should keep after distillation cannot keep them.")
@@ -18,7 +18,8 @@ import psutil
18
18
  from howso.utilities.feature_attributes.abstract_data import InferFeatureAttributesAbstractData
19
19
  from howso.utilities.feature_attributes.base import (
20
20
  InferFeatureAttributesBase,
21
- SIGNIFICANT_THRESHOLD_DEFAULT,
21
+ PRESERVE_RARE_VALUES_DISTILLED_MIN_CASES_DEFAULT,
22
+ PRESERVE_RARE_VALUES_MIN_CASES_DEFAULT,
22
23
  SingleTableFeatureAttributes,
23
24
  )
24
25
  from howso.utilities.feature_attributes.pandas import InferFeatureAttributesDataFrame
@@ -386,8 +387,9 @@ class InferFeatureAttributesTimeSeries(ABC):
386
387
  ordinal_feature_values: t.Optional[dict[str, list[t.Any]]] = None,
387
388
  preserve_rare_values: t.Optional[PreserveRareValues] = None,
388
389
  preserve_rare_values_caps: t.Optional[PreserveRareValuesCaps] = None,
390
+ preserve_rare_values_distilled_min_cases: int = PRESERVE_RARE_VALUES_DISTILLED_MIN_CASES_DEFAULT,
391
+ preserve_rare_values_min_cases: int = PRESERVE_RARE_VALUES_MIN_CASES_DEFAULT,
389
392
  rate_boundaries: t.Optional[dict] = None,
390
- significance_threshold: int = SIGNIFICANT_THRESHOLD_DEFAULT,
391
393
  time_invariant_features: t.Optional[Iterable[str]] = None,
392
394
  tight_bounds: t.Optional[Iterable[str]] = None,
393
395
  time_feature_is_universal: t.Optional[bool] = None,
@@ -613,14 +615,16 @@ class InferFeatureAttributesTimeSeries(ABC):
613
615
  A dict maps each feature name to one of three forms:
614
616
 
615
617
  - A list of values to protect. Each is weighted to keep approximately
616
- `significance_threshold` cases after distillation, funded by the feature's other values.
618
+ `preserve_rare_values_distilled_min_cases` cases after distillation, funded by the
619
+ feature's other values.
617
620
  When not every value fits, those with the most cases are given priority. Without
618
621
  `max_distilled_cases`, the multipliers are computed for a default target of 50,000 cases.
619
622
  - A list of dicts of "value" and "multiplier", giving the multiplier each value should
620
623
  receive, at least 1. The feature's other values are reweighted to fund them so the total
621
- case weight is unchanged: values with fewer cases than the significance threshold keep a
622
- weight of 1, and every other value is scaled by one common factor, except that no value is
623
- scaled below the number of cases that keeps the threshold after distillation. See
624
+ case weight is unchanged: values with fewer than `preserve_rare_values_min_cases` cases
625
+ keep a weight of 1, and every other value is scaled by one common factor, except that no
626
+ value is scaled below the number of cases that keeps
627
+ `preserve_rare_values_distilled_min_cases` after distillation. See
624
628
  `preserve_rare_values_caps` to limit how much weight those values give up. Without
625
629
  `max_distilled_cases`, the floor is computed for a target of 50,000 cases. If the other
626
630
  values cannot fund the multipliers, each multiplier's increase over 1 is scaled down and a
@@ -649,9 +653,20 @@ class InferFeatureAttributesTimeSeries(ABC):
649
653
  of its weight; given as a dict, the value for each feature is the largest share of its
650
654
  weight a significant value of that feature may give up, greater than 0 and at most 1.
651
655
  Features not listed give up as much as preserving every rare value requires, which draws
652
- the weights of their significant values toward the floor that keeps the significance
653
- threshold after distillation. When a cap leaves too little weight, the rare values with
654
- the most cases are preserved and a warning reports how many.
656
+ the weights of their significant values toward the floor that keeps
657
+ `preserve_rare_values_distilled_min_cases` cases after distillation. When a cap leaves
658
+ too little weight, the rare values with the most cases are preserved and a warning
659
+ reports how many.
660
+
661
+ preserve_rare_values_distilled_min_cases : int, default 3
662
+ (Optional) The number of cases each preserved rare value is expected to keep after
663
+ distillation. It is also the fewest cases a frequent value is expected to keep when its
664
+ case weight is reduced to fund the rare values.
665
+
666
+ preserve_rare_values_min_cases : int, default 15
667
+ (Optional) The number of cases a value needs in the data to be considered for rare value
668
+ preservation. Values with fewer cases keep their case weight and do not fund rare values.
669
+ Must be at least `preserve_rare_values_distilled_min_cases`.
655
670
 
656
671
  rate_boundaries : dict, default None
657
672
  (Optional) For time series, specify the rate boundaries in the form
@@ -671,10 +686,6 @@ class InferFeatureAttributesTimeSeries(ABC):
671
686
  }
672
687
  }
673
688
 
674
- significance_threshold : int, default 30
675
- (Optional) After data distillation, the number of cases that are expected to result in a
676
- signal for preserved rare values.
677
-
678
689
  tight_bounds: Iterable of str, default None
679
690
  (Optional) Set tight min and max bounds for the features
680
691
  specified in the Iterable.
@@ -810,7 +821,8 @@ class InferFeatureAttributesTimeSeries(ABC):
810
821
  ordinal_feature_values=ordinal_feature_values,
811
822
  preserve_rare_values=preserve_rare_values,
812
823
  preserve_rare_values_caps=preserve_rare_values_caps,
813
- significance_threshold=significance_threshold,
824
+ preserve_rare_values_distilled_min_cases=preserve_rare_values_distilled_min_cases,
825
+ preserve_rare_values_min_cases=preserve_rare_values_min_cases,
814
826
  tight_bounds=set(tight_bounds) if tight_bounds else None,
815
827
  types=types,
816
828
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: howso-engine
3
- Version: 68.1.0
3
+ Version: 68.2.0
4
4
  Summary: The Howso Engine™ is a natively and fully explainable ML engine, serving as an alternative to black box AI neural networks.
5
5
  Author: Howso Incorporated
6
6
  Author-email: support@howso.com
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes