Skip to content

openavmkit.utilities.settings

Settings.json loader, preprocessor, and typed accessors.

This module is the single source of truth for reading settings.json. It performs four transformations on the user's file before any other module sees it:

  1. Comment stripping — keys prefixed with __ are removed.
  2. Variable resolution — string values prefixed with $$ are replaced by the value at the dotted path inside the same settings tree (recursive until stable).
  3. Template merging — the user's settings are merged with the built-in settings.template.json, so users only need to specify overrides.
  4. Flag handling!key overwrites the template instead of merging, +key extends template lists instead of replacing them.

After loading, a large collection of typed accessors (get_valuation_date, get_model_group_ids, get_fields_categorical, area_unit, etc.) provides a stable, well-typed interface to the resulting dict — prefer these over reaching into the dict directly.

See :doc:/advanced_settings for a user-facing reference of the preprocessor and high-impact settings.

area_unit

area_unit(settings)

Get the designated "small" area unit (square feet or square meters)

Parameters:

Name Type Description Default
settings dict

Settings dictionary

required

Returns:

Type Description
str | None

"sqft" if units are imperial and "sqm" if units are metric None otherwise

Source code in openavmkit/utilities/settings.py
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
def area_unit(settings: dict):
    """
    Get the designated "small" area unit (square feet or square meters)

    Parameters
    ----------
    settings : dict
        Settings dictionary

    Returns
    -------
    str|None
        "sqft" if units are imperial and "sqm" if units are metric
        None otherwise
    """
    base_units = settings.get("locality", {}).get("units", "imperial")
    if base_units == "imperial":
        return "sqft"
    elif base_units == "metric":
        return "sqm"

big_area_unit

big_area_unit(settings)

Get the designated "large" area unit (acre or hectare)

Parameters:

Name Type Description Default
settings dict

Settings dictionary

required

Returns:

Type Description
str | None

"acre" if units are imperial and "ha" if units are metric None otherwise

Source code in openavmkit/utilities/settings.py
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
def big_area_unit(settings: dict)-> str|None:
    """
    Get the designated "large" area unit (acre or hectare)

    Parameters
    ----------
    settings : dict
        Settings dictionary

    Returns
    -------
    str|None
        "acre" if units are imperial and "ha" if units are metric
        None otherwise
    """
    base_units = settings.get("locality", {}).get("units", "imperial")
    if base_units == "imperial":
        return "acre"
    elif base_units == "metric":
        return "ha"  # hectare

big_length_unit

big_length_unit(settings)

Get the designated "big" length unit (miles or kilometers)

Parameters:

Name Type Description Default
settings dict

Settings dictionary

required

Returns:

Type Description
str

"mi" if units are imperial and "km" if units are metric

Source code in openavmkit/utilities/settings.py
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
def big_length_unit(settings: dict):
    """
    Get the designated "big" length unit (miles or kilometers)

    Parameters
    ----------
    settings : dict
        Settings dictionary

    Returns
    -------
    str
        "mi" if units are imperial and "km" if units are metric
    """
    base_units = settings.get("locality", {}).get("units", "imperial")
    if base_units == "imperial":
        return "mi"
    elif base_units == "metric":
        return "km"

expand_area_stats_fields

expand_area_stats_fields(s, fields)

Expand a bare sale-price entry into the full per-area sale-rate family.

Listing sale_price or sale_price_time_adj (treated as aliases) auto-generates the price level plus the three area-normalized rates (_impr_<unit>, _vacant_land_<unit>, _impr_land_<unit>) — no suffixes needed. It uses the canonical sale field, matching :func:openavmkit.data.get_sale_field: the time-adjusted price when time adjustment is enabled, otherwise the raw sale price (one base, not both). All other fields pass through unchanged; the result is de-duplicated and order-preserving.

Parameters:

Name Type Description Default
s dict

Settings dictionary.

required
fields list

The configured area_stats.fields list.

required

Returns:

Type Description
list

The expanded field list.

Source code in openavmkit/utilities/settings.py
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
def expand_area_stats_fields(s: dict, fields: list) -> list:
    """Expand a bare sale-price entry into the full per-area sale-rate family.

    Listing ``sale_price`` or ``sale_price_time_adj`` (treated as aliases) auto-generates
    the price level plus the three area-normalized rates (``_impr_<unit>``,
    ``_vacant_land_<unit>``, ``_impr_land_<unit>``) — no suffixes needed. It uses the
    canonical sale field, matching :func:`openavmkit.data.get_sale_field`: the
    time-adjusted price when time adjustment is enabled, otherwise the raw sale price (one
    base, not both). All other fields pass through unchanged; the result is de-duplicated
    and order-preserving.

    Parameters
    ----------
    s : dict
        Settings dictionary.
    fields : list
        The configured ``area_stats.fields`` list.

    Returns
    -------
    list
        The expanded field list.
    """
    unit = area_unit(s)
    ta_on = bool(get_time_adjustment_instructions(s).get("use", True))
    base = "sale_price_time_adj" if ta_on else "sale_price"

    expanded: list = []
    for field in fields:
        if field in AREA_STATS_SALE_PRICE_TRIGGERS:
            expanded.append(base)  # price level
            if unit:
                expanded.append(f"{base}_impr_{unit}")
                expanded.append(f"{base}_vacant_land_{unit}")
                expanded.append(f"{base}_impr_land_{unit}")
        else:
            expanded.append(field)

    seen, out = set(), []
    for field in expanded:
        if field not in seen:
            seen.add(field)
            out.append(field)
    return out

get_area_stats_config

get_area_stats_config(s)

Return the data.process.enrich.area_stats config block (or {}).

Parameters:

Name Type Description Default
s dict

Settings dictionary.

required

Returns:

Type Description
dict

The area-stats configuration, or an empty dict if the feature is not configured for this locality.

Source code in openavmkit/utilities/settings.py
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
def get_area_stats_config(s: dict) -> dict:
    """Return the ``data.process.enrich.area_stats`` config block (or ``{}``).

    Parameters
    ----------
    s : dict
        Settings dictionary.

    Returns
    -------
    dict
        The area-stats configuration, or an empty dict if the feature is not
        configured for this locality.
    """
    return (
        s.get("data", {})
        .get("process", {})
        .get("enrich", {})
        .get("area_stats", {})
    ) or {}

get_area_stats_fields

get_area_stats_fields(s, df=None)

Enumerate the area-stat derived fields and their classifications.

For each configured location × field × stat combination this returns the generated column name mapped to metadata describing how it should be treated:

  • bucket: "land" / "impr" / "other" inherited from the base field
  • kind: "numeric" or "categorical" (the output kind of the stat)
  • location / base_field / stat: the components it was built from

A per-location count column (group size) is always included. Base fields that are unclassified in settings are skipped (so they don't pollute the model's feature lists with unknown buckets).

Parameters:

Name Type Description Default
s dict

Settings dictionary.

required
df DataFrame

If given, only columns actually present in df are returned.

None

Returns:

Type Description
dict

Mapping of derived column name to its metadata dict.

Source code in openavmkit/utilities/settings.py
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
def get_area_stats_fields(s: dict, df: pd.DataFrame = None) -> dict:
    """Enumerate the area-stat derived fields and their classifications.

    For each configured ``location × field × stat`` combination this returns the
    generated column name mapped to metadata describing how it should be treated:

    - ``bucket``: ``"land"`` / ``"impr"`` / ``"other"`` inherited from the base field
    - ``kind``: ``"numeric"`` or ``"categorical"`` (the *output* kind of the stat)
    - ``location`` / ``base_field`` / ``stat``: the components it was built from

    A per-location ``count`` column (group size) is always included. Base fields
    that are unclassified in settings are skipped (so they don't pollute the
    model's feature lists with unknown buckets).

    Parameters
    ----------
    s : dict
        Settings dictionary.
    df : pandas.DataFrame, optional
        If given, only columns actually present in ``df`` are returned.

    Returns
    -------
    dict
        Mapping of derived column name to its metadata dict.
    """
    cfg = get_area_stats_config(s)
    out: dict = {}
    if not cfg:
        return out

    locations = cfg.get("locations", []) or []
    fields = expand_area_stats_fields(s, cfg.get("fields", []) or [])
    num_stats = cfg.get("stats", AREA_STATS_NUMERIC_DEFAULT) or []
    cat_stats = cfg.get("categorical_stats", AREA_STATS_CATEGORICAL_DEFAULT) or []

    for location in locations:
        for count_kind in AREA_STAT_COUNT_KINDS:
            out[make_area_stat_count_field_name(location, count_kind)] = {
                "bucket": "other",
                "kind": "numeric",
                "location": location,
                "base_field": None,
                "stat": count_kind,
            }
        for field in fields:
            bucket, kind = _classify_base_field_raw(s, field)
            if kind is None:
                continue
            stats_list = num_stats if kind == "numeric" else cat_stats
            for stat in stats_list:
                name = make_area_stat_field_name(location, field, stat)
                out_kind = (
                    "categorical" if stat in AREA_STATS_CATEGORICAL_OUTPUT else "numeric"
                )
                out[name] = {
                    "bucket": bucket,
                    "kind": out_kind,
                    "location": location,
                    "base_field": field,
                    "stat": stat,
                }

    if df is not None:
        out = {k: v for k, v in out.items() if k in df.columns}
    return out

get_assessor_holdout_mode

get_assessor_holdout_mode(s)

Return how the assessor's values relate to the test holdout.

openavmkit cannot know whether a third party's values respect its randomly-drawn holdout, so by default it does not show the assessor head-to-head on that holdout. If you are the assessor (or otherwise know the holdout status), set analysis.ratio_study.assessor_holdout to declare it:

  • "unknown" (default): holdout status of the assessor's values is unknown, so the assessor is not shown on the pre-valuation random holdout.
  • "shared": the assessor's values were produced honoring this same test holdout (either openavmkit's generated keys, or your own keys supplied via modeling.instructions.test_keys_file), so the assessor is shown head-to-head on the holdout.

Parameters:

Name Type Description Default
s dict

Settings dictionary.

required

Returns:

Type Description
str

"unknown" or "shared".

Source code in openavmkit/utilities/settings.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
def get_assessor_holdout_mode(s: dict) -> str:
    """Return how the assessor's values relate to the test holdout.

    openavmkit cannot know whether a third party's values respect its randomly-drawn
    holdout, so by default it does not show the assessor head-to-head on that holdout. If
    *you* are the assessor (or otherwise know the holdout status), set
    ``analysis.ratio_study.assessor_holdout`` to declare it:

    - ``"unknown"`` (default): holdout status of the assessor's values is unknown, so the
      assessor is not shown on the pre-valuation random holdout.
    - ``"shared"``: the assessor's values were produced honoring this same test holdout
      (either openavmkit's generated keys, or your own keys supplied via
      ``modeling.instructions.test_keys_file``), so the assessor *is* shown head-to-head on
      the holdout.

    Parameters
    ----------
    s : dict
        Settings dictionary.

    Returns
    -------
    str
        ``"unknown"`` or ``"shared"``.
    """
    mode = (
        s.get("analysis", {})
        .get("ratio_study", {})
        .get("assessor_holdout", "unknown")
    )
    return str(mode).lower()

get_center

get_center(s, gdf=None)

Get the centroid of all the provided parcel geometry

Parameters:

Name Type Description Default
s dict

Settings dictionary

required
gdf GeoDataFrame

Parcel geometry

None
Return

tuple[float, float] Centroid of all the parcel geometry

Source code in openavmkit/utilities/settings.py
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
def get_center(s: dict, gdf: gpd.GeoDataFrame = None) -> tuple[float, float]:
    """
    Get the centroid of all the provided parcel geometry

    Parameters
    ----------
    s : dict
        Settings dictionary
    gdf : gpd.GeoDataFrame
        Parcel geometry

    Return
    ------
    tuple[float, float]
        Centroid of all the parcel geometry
    """
    center: dict | None = s.get("locality", {}).get("center", None)
    if center is not None:
        if "longitude" not in center or "latitude" not in center:
            raise ValueError(
                "Could not find both 'longitude' and 'latitude' in 'settings.locality.center'!"
            )
        latitude = center["latitude"]
        longitude = center["longitude"]
        return longitude, latitude
    elif gdf is not None:
        # calculate the center of the gdf
        centroid = gdf.geometry.unary_union.centroid
        return centroid.x, centroid.y
    else:
        raise ValueError("Could not find locality.center in settings!")

get_collapse_sparse_categories_config

get_collapse_sparse_categories_config(settings)

Get the data.process.collapse_sparse_categories config block.

Parameters:

Name Type Description Default
settings dict

Settings dictionary.

required

Returns:

Type Description
dict

Mapping from field name to its per-field collapse config (keys: sales_min, univ_min, optional replacement_value). Empty dict if the section is absent.

Source code in openavmkit/utilities/settings.py
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
def get_collapse_sparse_categories_config(settings: dict) -> dict:
    """Get the ``data.process.collapse_sparse_categories`` config block.

    Parameters
    ----------
    settings : dict
        Settings dictionary.

    Returns
    -------
    dict
        Mapping from field name to its per-field collapse config (keys:
        ``sales_min``, ``univ_min``, optional ``replacement_value``). Empty
        dict if the section is absent.
    """
    return (
        settings.get("data", {}).get("process", {}).get("collapse_sparse_categories", {})
    )

get_collapsed_fields

get_collapsed_fields(settings)

Return the set of columns that carry a collapsed ("Other") bucket.

For each entry in data.process.collapse_sparse_categories, the collapsed column is the entry's output_field if set, otherwise the source field (the dict key). This is the column downstream code should treat as cardinality-collapsed — when output_field is used the raw source field is left intact and is not considered collapsed. Reserved non-dict keys (e.g. strict) are skipped.

Parameters:

Name Type Description Default
settings dict

Settings dictionary.

required

Returns:

Type Description
set[str]

Column names that have been (or will be) cardinality-collapsed.

Source code in openavmkit/utilities/settings.py
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
def get_collapsed_fields(settings: dict) -> set[str]:
    """Return the set of columns that carry a collapsed (``"Other"``) bucket.

    For each entry in ``data.process.collapse_sparse_categories``, the collapsed
    column is the entry's ``output_field`` if set, otherwise the source field
    (the dict key). This is the column downstream code should treat as
    cardinality-collapsed — when ``output_field`` is used the raw source field is
    left intact and is *not* considered collapsed. Reserved non-dict keys (e.g.
    ``strict``) are skipped.

    Parameters
    ----------
    settings : dict
        Settings dictionary.

    Returns
    -------
    set[str]
        Column names that have been (or will be) cardinality-collapsed.
    """
    config = get_collapse_sparse_categories_config(settings)
    collapsed = set()
    for field, field_cfg in config.items():
        if not isinstance(field_cfg, dict):
            continue
        collapsed.add(field_cfg.get("output_field", field))
    return collapsed

get_data_dictionary

get_data_dictionary(settings)

Get the data dictionary object

Parameters:

Name Type Description Default
settings dict

Settings dictionary

required

Returns:

Type Description
dict

The data dictionary for this locality

Source code in openavmkit/utilities/settings.py
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
def get_data_dictionary(settings: dict) -> dict:
    """
    Get the data dictionary object

    Parameters
    ----------
    settings : dict
        Settings dictionary

    Returns
    -------
    dict
        The data dictionary for this locality
    """
    return settings.get("data_dictionary", {})

get_ensemble_instructions

get_ensemble_instructions(settings, mv)

Retrieves ensemble instructions for a particular modeling section

Parameters:

Name Type Description Default
settings dict

Settings dictionary.

required
mv string

Which section -- "main" or "vacant"

required

Returns:

Type Description
dict

Dictionary object containing ensemble settings

Source code in openavmkit/utilities/settings.py
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
def get_ensemble_instructions(settings: dict, mv: str) -> dict:
    """
    Retrieves ensemble instructions for a particular modeling section

    Parameters
    ----------
    settings : dict
        Settings dictionary.
    mv : string
        Which section -- "main" or "vacant"

    Returns
    -------
    dict
        Dictionary object containing ensemble settings
    """

    instructions = settings.get("modeling", {}).get("instructions", {}).get(mv, {})

    ensemble = instructions.get("ensemble", {})
    type = ensemble.get("type", "default")
    # "default" is just an alias for "median" (the historical default
    # aggregation); normalize it so downstream only ever sees "median".
    if type == "default":
        type = "median"
    if type in ("median", "mean"):
        models = ensemble.get("models", [])
        # "optimize" controls whether the greedy backward-elimination optimizer
        # runs. Its default depends on whether the user supplied an explicit
        # model list:
        #   - models given, optimize unspecified  -> False (use the list as-is;
        #     it is a whitelist of exactly which models to ensemble)
        #   - models given, optimize=True          -> optimize *from* the whitelist
        #   - models omitted, optimize unspecified -> True (optimize over all
        #     models -- the historical default)
        #   - models omitted, optimize=True        -> optimize over all models
        optimize = ensemble.get("optimize", len(models) == 0)
        return {
            "type": type,
            "models": models,
            "optimize": optimize,
        }
    elif type == "local":
        locations = ensemble.get("locations", None)
        if locations is None:
            locations = get_locations(settings)
        if locations is None:
            locations = []
        return {
            "type": "local",
            "locations": locations
        }

get_fields_boolean

get_fields_boolean(s, df=None, types=None)

Retrieve boolean field names based on settings and optional filters.

Parameters:

Name Type Description Default
s dict

Settings dictionary containing field configurations.

required
df DataFrame

DataFrame to filter fields by presence. Defaults to None.

None
types list[str]

List of field classification types to include (e.g., ["land", "impr", "other"]). Defaults to None, which includes all types.

None

Returns:

Type Description
list[str]

List of boolean field names matching the specified criteria.

Source code in openavmkit/utilities/settings.py
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
def get_fields_boolean(
    s: dict,
    df: pd.DataFrame = None,
    types: list[str] = None
) -> list[str]:
    """
    Retrieve boolean field names based on settings and optional filters.

    Parameters
    ----------
    s : dict
        Settings dictionary containing field configurations.
    df : pandas.DataFrame, optional
        DataFrame to filter fields by presence. Defaults to None.
    types : list[str], optional
        List of field classification types to include (e.g., ["land", "impr", "other"]).
        Defaults to None, which includes all types.

    Returns
    -------
    list[str]
        List of boolean field names matching the specified criteria.
    """
    if types is None:
        types = ["land", "impr", "other"]
    bools = []

    # Determine which boolean field to get based on na_handling
    field_type = "boolean"

    if "land" in types:
        bools += s.get("field_classification", {}).get("land", {}).get(field_type, [])
    if "impr" in types:
        bools += s.get("field_classification", {}).get("impr", {}).get(field_type, [])
    if "other" in types:
        bools += s.get("field_classification", {}).get("other", {}).get(field_type, [])

    if df is not None:
        bools = [bool for bool in bools if bool in df]
    return bools

get_fields_categorical

get_fields_categorical(s, df=None, include_boolean=False, types=None)

Retrieve categorical field names based on settings and optional filters.

Parameters:

Name Type Description Default
s dict

Settings dictionary containing field configurations.

required
df DataFrame

DataFrame to filter fields by presence. Defaults to None.

None
include_boolean bool

Whether to include boolean fields in the results or not. Defaults to False.

False
types list[str]

List of field classification types to include (e.g., ["land", "impr", "other"]). Defaults to None, which includes all types.

None

Returns:

Type Description
list[str]

List of categorical field names matching the specified criteria.

Source code in openavmkit/utilities/settings.py
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
def get_fields_categorical(
    s: dict,
    df: pd.DataFrame = None,
    include_boolean: bool = False,
    types: list[str] = None,
) -> list[str]:
    """
    Retrieve categorical field names based on settings and optional filters.

    Parameters
    ----------
    s : dict
        Settings dictionary containing field configurations.
    df : pandas.DataFrame, optional
        DataFrame to filter fields by presence. Defaults to None.
    include_boolean : bool, optional
        Whether to include boolean fields in the results or not. Defaults to False.
    types : list[str], optional
        List of field classification types to include (e.g., ["land", "impr", "other"]).
        Defaults to None, which includes all types.

    Returns
    -------
    list[str]
        List of categorical field names matching the specified criteria.
    """
    if types is None:
        types = ["land", "impr", "other"]
    cats = []
    if "land" in types:
        cats += s.get("field_classification", {}).get("land", {}).get("categorical", [])
    if "impr" in types:
        cats += s.get("field_classification", {}).get("impr", {}).get("categorical", [])
    if "other" in types:
        cats += (
            s.get("field_classification", {}).get("other", {}).get("categorical", [])
        )
    if include_boolean:
        if "land" in types:
            cats += s.get("field_classification", {}).get("land", {}).get("boolean", [])
        if "impr" in types:
            cats += s.get("field_classification", {}).get("impr", {}).get("boolean", [])
        if "other" in types:
            cats += (
                s.get("field_classification", {}).get("other", {}).get("boolean", [])
            )

    # collapse_sparse_categories `output_field` variants (e.g. "subdivision_collapsed")
    # are categorical by construction -- they inherit their source field's classification.
    # Derived from settings on every call so the inheritance persists across notebooks
    # (settings reload fresh each notebook). Without this, a collapsed variant reaches the
    # tree models as a raw string column and LightGBM/XGBoost reject it.
    collapse = (
        s.get("data", {}).get("process", {}).get("collapse_sparse_categories", {})
    )
    if isinstance(collapse, dict):
        for src_field, cfg in collapse.items():
            if not isinstance(cfg, dict):
                continue
            out_field = cfg.get("output_field")
            if out_field and src_field in cats and out_field not in cats:
                cats.append(out_field)

    # area-stat derived fields whose output is categorical (e.g. ``..._mode``)
    # inherit their base field's bucket; include those in the requested types.
    for name, meta in get_area_stats_fields(s, df).items():
        if meta["kind"] == "categorical" and meta["bucket"] in types and name not in cats:
            cats.append(name)

    if df is not None:
        cats = [cat for cat in cats if cat in df]
    return cats

get_fields_date

get_fields_date(s, df)

Get all fields pertaining to dates

Parameters:

Name Type Description Default
s dict

Settings dictionary

required
df DataFrame

Your dataset

required

Returns:

Type Description
list[str]

List of field names pertaining to dates

Source code in openavmkit/utilities/settings.py
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
def get_fields_date(s: dict, df: pd.DataFrame):
    """
    Get all fields pertaining to dates

    Parameters
    ----------
    s : dict
        Settings dictionary
    df : pd.DataFrame
        Your dataset

    Returns
    -------
    list[str]
        List of field names pertaining to dates
    """

    # TODO: add to this as necessary
    all_date_fields = ["sale_date", "date"]
    date_fields = [field for field in all_date_fields if field in df]
    for field in df:
        if "_date" in field and field not in date_fields:
            date_fields.append(field)

    return date_fields

get_fields_impr

get_fields_impr(s, df=None)

Get all fields in the given dataframe that are classified in settings as pertaining to buildings/improvements.

Parameters:

Name Type Description Default
s dict

Settings dictionary

required
df DataFrame

Your dataset

None

Returns:

Type Description
dict

All fields pertaining to buildings/improvements, organized as a dictionary containing three keys:

  • "categorical": list of categorical fields
  • "numeric": list of numerical fields
  • "boolean": list of boolean fields
Source code in openavmkit/utilities/settings.py
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
def get_fields_impr(s: dict, df: pd.DataFrame = None) -> dict:
    """
    Get all fields in the given dataframe that are classified in settings as pertaining to buildings/improvements.

    Parameters
    ----------
    s : dict
        Settings dictionary
    df : pd.DataFrame
        Your dataset

    Returns
    -------
    dict
        All fields pertaining to buildings/improvements, organized as a dictionary containing three keys:

          - "categorical": list of categorical fields
          - "numeric": list of numerical fields
          - "boolean": list of boolean fields
    """
    return _get_fields(s, "impr", df)

get_fields_impr_as_list

get_fields_impr_as_list(s, df=None)

Get all fields in the given dataframe that are classified in settings as pertaining to buildings/improvements.

Parameters:

Name Type Description Default
s dict

Settings dictionary

required
df DataFrame

Your dataset

None

Returns:

Type Description
list

A list of all field names pertaining to buildings/improvements

Source code in openavmkit/utilities/settings.py
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
def get_fields_impr_as_list(s: dict, df: pd.DataFrame = None) -> list[str]:
    """
    Get all fields in the given dataframe that are classified in settings as pertaining to buildings/improvements.

    Parameters
    ----------
    s : dict
        Settings dictionary
    df : pd.DataFrame
        Your dataset

    Returns
    -------
    list
        A list of all field names pertaining to buildings/improvements
    """
    fields = get_fields_impr(s, df)
    return (
        fields.get("categorical", [])
        + fields.get("numeric", [])
        + fields.get("boolean", [])
    )

get_fields_land

get_fields_land(s, df=None)

Get all fields in the given dataframe that are classified in settings as pertaining to land.

Parameters:

Name Type Description Default
s dict

Settings dictionary

required
df DataFrame

Your dataset

None

Returns:

Type Description
dict

All fields pertaining to land, organized as a dictionary containing three keys:

  • "categorical": list of categorical fields
  • "numeric": list of numerical fields
  • "boolean": list of boolean fields
Source code in openavmkit/utilities/settings.py
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
def get_fields_land(s: dict, df: pd.DataFrame = None) -> dict:
    """
    Get all fields in the given dataframe that are classified in settings as pertaining to land.

    Parameters
    ----------
    s : dict
        Settings dictionary
    df : pd.DataFrame
        Your dataset

    Returns
    -------
    dict
        All fields pertaining to land, organized as a dictionary containing three keys:

          - "categorical": list of categorical fields
          - "numeric": list of numerical fields
          - "boolean": list of boolean fields
    """
    fields_land = _get_fields(s, "land", df)
    fields_unclassified = _get_unclassified_fields(s, df)

    for field in fields_unclassified:
        if field.startswith("dist_to_") or field.startswith("within_") or field.startswith("proximity_to_") or field.startswith("spatial_lag_"):
            fields_land["numeric"].append(field)
        # Defensive net for area-stat columns that reach the getter without their
        # config context (e.g. loaded from cache). Numeric stats only -- the
        # categorical ``_mode`` variant is handled by get_fields_categorical.
        elif field.startswith(AREA_STAT_PREFIX) and not field.endswith("_mode"):
            fields_land["numeric"].append(field)

    for key in fields_land:
        # remove duplicates:
        fields_land[key] = list(set(fields_land[key]))

    return fields_land

get_fields_land_as_list

get_fields_land_as_list(s, df=None)

Get all fields in the given dataframe that are classified in settings as pertaining to land.

Parameters:

Name Type Description Default
s dict

Settings dictionary

required
df DataFrame

Your dataset

None

Returns:

Type Description
list

A list of all field names pertaining to land

Source code in openavmkit/utilities/settings.py
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
def get_fields_land_as_list(s: dict, df: pd.DataFrame = None) -> list[str]:
    """
    Get all fields in the given dataframe that are classified in settings as pertaining to land.

    Parameters
    ----------
    s : dict
        Settings dictionary
    df : pd.DataFrame
        Your dataset

    Returns
    -------
    list
        A list of all field names pertaining to land
    """
    fields = get_fields_land(s, df)
    return (
        fields.get("categorical", [])
        + fields.get("numeric", [])
        + fields.get("boolean", [])
    )

get_fields_numeric

get_fields_numeric(s, df=None, include_boolean=False, types=None)

Retrieve numeric field names based on settings and optional filters.

Parameters:

Name Type Description Default
s dict

Settings dictionary containing field configurations.

required
df DataFrame

DataFrame to filter fields by presence. Defaults to None.

None
include_boolean bool

Whether to include boolean fields in the results or not. Defaults to False.

False
types list[str]

List of field classification types to include (e.g., ["land", "impr", "other"]). Defaults to None, which includes all types.

None

Returns:

Type Description
list[str]

List of numeric field names matching the specified criteria.

Source code in openavmkit/utilities/settings.py
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
def get_fields_numeric(
    s: dict,
    df: pd.DataFrame = None,
    include_boolean: bool = False,
    types: list[str] = None,
) -> list[str]:
    """
     Retrieve numeric field names based on settings and optional filters.

     Parameters
     ----------
     s : dict
         Settings dictionary containing field configurations.
     df : pandas.DataFrame, optional
         DataFrame to filter fields by presence. Defaults to None.
     include_boolean : bool, optional
         Whether to include boolean fields in the results or not. Defaults to False.
     types : list[str], optional
         List of field classification types to include (e.g., ["land", "impr", "other"]).
         Defaults to None, which includes all types.

     Returns
     -------
     list[str]
         List of numeric field names matching the specified criteria.
     """
    if types is None:
        types = ["land", "impr", "other"]
    nums = []
    if "land" in types:
        nums += s.get("field_classification", {}).get("land", {}).get("numeric", [])
    if "impr" in types:
        nums += s.get("field_classification", {}).get("impr", {}).get("numeric", [])
    if "other" in types:
        nums += s.get("field_classification", {}).get("other", {}).get("numeric", [])
    if include_boolean:
        if "land" in types:
            nums += s.get("field_classification", {}).get("land", {}).get("boolean", [])
        if "impr" in types:
            nums += s.get("field_classification", {}).get("impr", {}).get("boolean", [])
        if "other" in types:
            nums += (
                s.get("field_classification", {}).get("other", {}).get("boolean", [])
            )

    # area-stat derived fields with numeric output inherit their base field's
    # bucket; include those in the requested types so models auto-discover them.
    for name, meta in get_area_stats_fields(s, df).items():
        if meta["kind"] == "numeric" and meta["bucket"] in types and name not in nums:
            nums.append(name)

    if df is not None:
        nums = [num for num in nums if num in df]
    return nums

get_fields_other

get_fields_other(s, df=None)

Get all fields in the given dataframe that are classified in settings as pertaining to neither land nor buildings/improvements.

Parameters:

Name Type Description Default
s dict

Settings dictionary

required
df DataFrame

Your dataset

None

Returns:

Type Description
dict

All fields pertaining neither to land nor to buildings/improvements, organized as a dictionary containing three keys:

  • "categorical": list of categorical fields
  • "numeric": list of numerical fields
  • "boolean": list of boolean fields
Source code in openavmkit/utilities/settings.py
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
def get_fields_other(s: dict, df: pd.DataFrame = None) -> dict:
    """
    Get all fields in the given dataframe that are classified in settings as pertaining to neither land nor
    buildings/improvements.

    Parameters
    ----------
    s : dict
        Settings dictionary
    df : pd.DataFrame
        Your dataset

    Returns
    -------
    dict
        All fields pertaining neither to land nor to buildings/improvements,
        organized as a dictionary containing three keys:

          - "categorical": list of categorical fields
          - "numeric": list of numerical fields
          - "boolean": list of boolean fields
    """
    return _get_fields(s, "other", df)

get_fields_other_as_list

get_fields_other_as_list(s, df=None)

Get all fields in the given dataframe that are classified in settings as pertaining to neither land nor to buildings/improvements.

Parameters:

Name Type Description Default
s dict

Settings dictionary

required
df DataFrame

Your dataset

None

Returns:

Type Description
list

A list of all field names pertaining neither to land nor to buildings/improvements

Source code in openavmkit/utilities/settings.py
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
def get_fields_other_as_list(s: dict, df: pd.DataFrame = None) -> list[str]:
    """
    Get all fields in the given dataframe that are classified in settings as pertaining to neither land nor to
    buildings/improvements.

    Parameters
    ----------
    s : dict
        Settings dictionary
    df : pd.DataFrame
        Your dataset

    Returns
    -------
    list
        A list of all field names pertaining neither to land nor to buildings/improvements
    """
    fields = get_fields_other(s, df)
    return (
        fields.get("categorical", [])
        + fields.get("numeric", [])
        + fields.get("boolean", [])
    )

get_grouped_fields_from_data_dictionary

get_grouped_fields_from_data_dictionary(dd, group, types=None)

Get all field names from the data dictionary of the named group and, optionally, of the designated types.

Parameters:

Name Type Description Default
dd dict

The data dictionary

required
group str

Name of a particular group in the data dictionary

required
types list

If None, returns all field names in the group. If not, targets only those fields that match the listed types. Legal values are: "boolean", "str", "number", "percent", "date"

None

Returns:

Type Description
list[str]

A list of field names belonging to the specified group

Source code in openavmkit/utilities/settings.py
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
def get_grouped_fields_from_data_dictionary(
    dd: dict, group: str, types: list[str] = None
) -> list[str]:
    """
    Get all field names from the data dictionary of the named group and, optionally, of the designated types.

    Parameters
    ----------
    dd : dict
        The data dictionary
    group : str
        Name of a particular group in the data dictionary
    types : list, optional
        If None, returns all field names in the group. If not, targets only those fields that match the
        listed types. Legal values are: "boolean", "str", "number", "percent", "date"

    Returns
    -------
    list[str]
        A list of field names belonging to the specified group
    """
    result = []
    for key in dd:
        entry = dd[key]
        if group in entry.get("groups", []):
            if types is None or entry.get("type") in types:
                result.append(key)
    return result

get_location_fields

get_location_fields(settings, df=None)

Return every field the settings treat as a coherent geographic location.

Collapsing any of these in place via collapse_sparse_categories is a footgun: downstream grouping / clustering / breakdowns assume each location value is a geographically coherent zone, and a collapsed location merges unrelated zones into one "Other" bucket. This aggregates, from settings:

  • field_classification.important.locations
  • field_classification.important.fields.loc_* (the mapped field names)
  • analysis.{sales_scrutiny,horizontal_equity,land_equity,impr_equity}.location
  • analysis.ratio_study.breakdowns[].by entries written as <loc_*> (resolved through important.fields)
  • modeling.instructions.{main,vacant}.ensemble.locations and any per-model modeling.models.*.*.locations
  • land.lycd.*.location

field_classification.important.report_locations is intentionally excluded — those are used only as report output columns (benign if collapsed).

Parameters:

Name Type Description Default
settings dict

Settings dictionary.

required
df DataFrame

If given, the result is filtered to columns present in df.

None

Returns:

Type Description
set[str]

Field names used as coherent geographic locations.

Source code in openavmkit/utilities/settings.py
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
def get_location_fields(settings: dict, df: pd.DataFrame = None) -> set[str]:
    """Return every field the settings treat as a coherent geographic location.

    Collapsing any of these *in place* via ``collapse_sparse_categories`` is a
    footgun: downstream grouping / clustering / breakdowns assume each location
    value is a geographically coherent zone, and a collapsed location merges
    unrelated zones into one ``"Other"`` bucket. This aggregates, from
    ``settings``:

    - ``field_classification.important.locations``
    - ``field_classification.important.fields.loc_*`` (the mapped field names)
    - ``analysis.{sales_scrutiny,horizontal_equity,land_equity,impr_equity}.location``
    - ``analysis.ratio_study.breakdowns[].by`` entries written as ``<loc_*>``
      (resolved through ``important.fields``)
    - ``modeling.instructions.{main,vacant}.ensemble.locations`` and any
      per-model ``modeling.models.*.*.locations``
    - ``land.lycd.*.location``

    ``field_classification.important.report_locations`` is intentionally excluded
    — those are used only as report output columns (benign if collapsed).

    Parameters
    ----------
    settings : dict
        Settings dictionary.
    df : pandas.DataFrame, optional
        If given, the result is filtered to columns present in ``df``.

    Returns
    -------
    set[str]
        Field names used as coherent geographic locations.
    """
    fc = settings.get("field_classification", {})
    important = fc.get("important", {})
    fields_map = important.get("fields", {}) or {}

    out: set[str] = set()
    out.update(important.get("locations", []) or [])
    for alias, actual in fields_map.items():
        if isinstance(alias, str) and alias.startswith("loc_") and actual:
            out.add(actual)

    analysis = settings.get("analysis", {})
    for sect in ("sales_scrutiny", "horizontal_equity", "land_equity", "impr_equity"):
        loc = analysis.get(sect, {}).get("location")
        if loc:
            out.add(loc)

    for bd in analysis.get("ratio_study", {}).get("breakdowns", []) or []:
        by = bd.get("by") if isinstance(bd, dict) else None
        if isinstance(by, str) and by.startswith("<") and by.endswith(">"):
            actual = fields_map.get(by[1:-1])
            if actual:
                out.add(actual)

    modeling = settings.get("modeling", {})
    instr = modeling.get("instructions", {})
    for mv in ("main", "vacant"):
        ens = instr.get(mv, {}).get("ensemble", {})
        out.update(ens.get("locations", []) or [])
    for mv_block in modeling.get("models", {}).values():
        if not isinstance(mv_block, dict):
            continue
        for model_cfg in mv_block.values():
            if isinstance(model_cfg, dict):
                out.update(model_cfg.get("locations", []) or [])

    for cfg in settings.get("land", {}).get("lycd", {}).values():
        if isinstance(cfg, dict) and cfg.get("location"):
            out.add(cfg["location"])

    out.discard(None)
    if df is not None:
        out = {f for f in out if f in df.columns}
    return out

get_locations

get_locations(settings, df=None)

Retrieve location fields from settings. These are all the fields that are considered locations.

Parameters:

Name Type Description Default
settings dict

Settings dictionary.

required
df DataFrame

Optional DataFrame to filter available locations.

None

Returns:

Type Description
list[str]

List of location field names.

Source code in openavmkit/utilities/settings.py
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
def get_locations(settings: dict, df: pd.DataFrame = None) -> list[str]:
    """
    Retrieve location fields from settings. These are all the fields that are considered locations.

    Parameters
    ----------
    settings : dict
        Settings dictionary.
    df : pandas.DataFrame, optional
        Optional DataFrame to filter available locations.

    Returns
    -------
    list[str]
        List of location field names.
    """

    locations = (
        settings.get("field_classification", {})
        .get("important", {})
        .get("locations", [])
    )
    if df is not None:
        locations = [loc for loc in locations if loc in df]
    return locations

get_long_distance_unit

get_long_distance_unit(settings)

Get the designated "long" distance unit (mile or kilometer)

Parameters:

Name Type Description Default
settings dict

Settings dictionary

required

Returns:

Type Description
str | None

"mile" if units are imperial and "km" if units are metric None otherwise

Source code in openavmkit/utilities/settings.py
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
def get_long_distance_unit(settings: dict) -> str|None:
    """
    Get the designated "long" distance unit (mile or kilometer)

    Parameters
    ----------
    settings : dict
        Settings dictionary

    Returns
    -------
    str|None
        "mile" if units are imperial and "km" if units are metric
        None otherwise
    """
    base_units = settings.get("locality", {}).get("units", "imperial")
    if base_units == "imperial":
        return "mile"
    elif base_units == "metric":
        return "km"

get_model_group

get_model_group(s, key)

Get a model group definition object from the settings dictionary

Parameters:

Name Type Description Default
s dict

Settings object

required
key str

The name of the model group

required

Returns:

Type Description
dict

Model group definition

Source code in openavmkit/utilities/settings.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
def get_model_group(s: dict, key: str) -> dict:
    """
    Get a model group definition object from the settings dictionary

    Parameters
    ----------
    s : dict
        Settings object
    key : str
        The name of the model group

    Returns
    -------
    dict
        Model group definition
    """
    return s.get("modeling", {}).get("model_groups", {}).get(key, {})

get_model_group_ids

get_model_group_ids(settings, df=None)

Get all model group ids specified in settings, in the preferred order specified by the user

Parameters:

Name Type Description Default
settings dict

Settings dictionary

required
df DataFrame

Your dataset

None

Returns:

Type Description
list[str]

Ordered list of model group ids

Source code in openavmkit/utilities/settings.py
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
def get_model_group_ids(settings: dict, df: pd.DataFrame = None) -> list[str]:
    """
    Get all model group ids specified in settings, in the preferred order specified by the user

    Parameters
    ----------
    settings : dict
        Settings dictionary
    df : pd.DataFrame
        Your dataset

    Returns
    -------
    list[str]
        Ordered list of model group ids
    """
    modeling = settings.get("modeling", {})

    # Get the model groups defined in the settings
    model_groups = modeling.get("model_groups", {})

    # Get the preferred order, if any
    order = modeling.get("instructions", {}).get("model_group_order", [])

    if df is not None:
        # If a dataframe is provided, filter out model groups that are not present in the DataFrame
        model_groups_in_df = df["model_group"].unique()
        model_group_ids = [key for key in model_groups if key in model_groups_in_df]
    else:
        model_group_ids = [key for key in model_groups]

    # Order the model groups according to the preferred order
    ordered_ids = [key for key in order if key in model_group_ids]
    unordered_ids = [key for key in model_group_ids if key not in ordered_ids]
    ordered_ids += unordered_ids

    return ordered_ids

get_model_seed

get_model_seed(s)

Return the random seed used for model tuning and fitting.

Read from modeling.metadata.seed (default 42). This is the single source of truth for reproducibility of the (otherwise nondeterministic) tree-based models: it seeds the Optuna hyperparameter sampler, the cross-validation folds, and the final model fits.

Modeling is always deterministic — there is no nondeterministic mode. The XGBoost/LightGBM tuners stay parallel and reproducible via batched ask-and-tell (see _run_batched in :mod:openavmkit.tuning), so determinism costs no parallelism. Provide your own integer to vary the seed; an absent or null value falls back to 42.

Parameters:

Name Type Description Default
s dict

Settings dictionary.

required

Returns:

Type Description
int

The model seed (always an integer).

Source code in openavmkit/utilities/settings.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def get_model_seed(s: dict) -> int:
    """Return the random seed used for model tuning and fitting.

    Read from ``modeling.metadata.seed`` (default ``42``). This is the single source of
    truth for reproducibility of the (otherwise nondeterministic) tree-based models: it
    seeds the Optuna hyperparameter sampler, the cross-validation folds, and the final
    model fits.

    Modeling is **always deterministic** — there is no nondeterministic mode. The
    XGBoost/LightGBM tuners stay parallel *and* reproducible via batched ask-and-tell
    (see ``_run_batched`` in :mod:`openavmkit.tuning`), so determinism costs no
    parallelism. Provide your own integer to vary the seed; an absent or ``null`` value
    falls back to ``42``.

    Parameters
    ----------
    s : dict
        Settings dictionary.

    Returns
    -------
    int
        The model seed (always an integer).
    """
    seed = s.get("modeling", {}).get("metadata", {}).get("seed", 42)
    return 42 if seed is None else int(seed)

get_short_distance_unit

get_short_distance_unit(settings)

Get the designated "short" distance unit (foot or meter)

Parameters:

Name Type Description Default
settings dict

Settings dictionary

required

Returns:

Type Description
str | None

"ft" if units are imperial and "m" if units are metric None otherwise

Source code in openavmkit/utilities/settings.py
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
def get_short_distance_unit(settings: dict) -> str|None:
    """
    Get the designated "short" distance unit (foot or meter)

    Parameters
    ----------
    settings : dict
        Settings dictionary

    Returns
    -------
    str|None
        "ft" if units are imperial and "m" if units are metric
        None otherwise
    """
    base_units = settings.get("locality", {}).get("units", "imperial")
    if base_units == "imperial":
        return "ft"
    elif base_units == "metric":
        return "m"

get_valuation_date

get_valuation_date(s)

Get the valuation date from the settings dictionary

Parameters:

Name Type Description Default
s dict

Settings dictionary

required

Returns:

Type Description
datetime

The valuation date

Source code in openavmkit/utilities/settings.py
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def get_valuation_date(s: dict) -> datetime:
    """
    Get the valuation date from the settings dictionary

    Parameters
    ----------
    s : dict
        Settings dictionary

    Returns
    -------
    datetime
        The valuation date
    """
    val_date_str: str | None = (
        s.get("modeling", {}).get("metadata", {}).get("valuation_date", None)
    )

    if val_date_str is None:
        # return January 1 of this year:
        return datetime(datetime.now().year, 1, 1)

    # process the date from string to datetime using format YYYY-MM-DD:
    val_date = datetime.strptime(val_date_str, "%Y-%m-%d")
    return val_date

get_variable_interactions

get_variable_interactions(entry, settings, df=None)

Get variable interaction information from a dictionary object

Parameters:

Name Type Description Default
entry dict

The dictionary object that may contain variable interactions

required
settings dict

Global settings dictionary

required
df DataFrame

Your dataset

None

Returns:

Type Description
dict

Interactions dictionary which maps field names to other field names, indicating variable interactions.

Example: Interacting a categorical field like "neighborhood" with a numeric field like "land_area_{unit}" means that every one-hot-encoded descendant like "neighborhood=River Heights" will be multiplied against the numeric value of "land_area_{unit}", so this is a way to interact neighborhood dummies with land size.

Source code in openavmkit/utilities/settings.py
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
def get_variable_interactions(entry: dict, settings: dict, df: pd.DataFrame = None) -> dict:
    """
    Get variable interaction information from a dictionary object

    Parameters
    ----------
    entry : dict
        The dictionary object that may contain variable interactions
    settings : dict
        Global settings dictionary
    df : pd.DataFrame
        Your dataset

    Returns
    -------
    dict
        Interactions dictionary which maps field names to other field names, indicating variable interactions.

        Example:
        Interacting a categorical field like "neighborhood" with a numeric field like "land_area_{unit}" means that
        every one-hot-encoded descendant like "neighborhood=River Heights" will be multiplied against the numeric
        value of "land_area_{unit}", so this is a way to interact neighborhood dummies with land size.
    """
    unit = area_unit(settings)
    interactions: dict | None = entry.get("interactions", None)
    if interactions is None:
        return {}
    is_default = interactions.get("default", False)
    if is_default:
        result = {}
        fields_land = get_fields_categorical(
            settings, df, include_boolean=True, types=["land"]
        )
        fields_impr = get_fields_categorical(
            settings, df, include_boolean=True, types=["impr"]
        )
        for field in fields_land:
            result[field] = f"land_area_{unit}"
        for field in fields_impr:
            result[field] = f"bldg_area_finished_{unit}"
        return result
    else:
        return interactions.get("fields", {})

is_collapse_strict

is_collapse_strict(settings)

Whether cardinality-collapse location guards should raise instead of warn.

Opt in by setting the reserved boolean key data.process.collapse_sparse_categories.strict to true. (Note: a __strict key would be stripped as a comment, so the flag is a single reserved strict key alongside the per-field entries.)

Parameters:

Name Type Description Default
settings dict

Settings dictionary.

required

Returns:

Type Description
bool

True if guards should raise ValueError; False (default) to warn.

Source code in openavmkit/utilities/settings.py
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
def is_collapse_strict(settings: dict) -> bool:
    """Whether cardinality-collapse location guards should raise instead of warn.

    Opt in by setting the reserved boolean key
    ``data.process.collapse_sparse_categories.strict`` to ``true``. (Note: a
    ``__strict`` key would be stripped as a comment, so the flag is a single
    reserved ``strict`` key alongside the per-field entries.)

    Parameters
    ----------
    settings : dict
        Settings dictionary.

    Returns
    -------
    bool
        True if guards should raise ``ValueError``; False (default) to warn.
    """
    return bool(get_collapse_sparse_categories_config(settings).get("strict", False))

is_field_collapsed

is_field_collapsed(settings, field)

Return True if field is a cardinality-collapsed output column.

See :func:get_collapsed_fields.

Source code in openavmkit/utilities/settings.py
1343
1344
1345
1346
1347
1348
def is_field_collapsed(settings: dict, field: str) -> bool:
    """Return True if ``field`` is a cardinality-collapsed output column.

    See :func:`get_collapsed_fields`.
    """
    return field in get_collapsed_fields(settings)

is_sale_derived_field

is_sale_derived_field(s, field)

Whether field is the sale price (or a sale-price variant).

Sale-derived fields must be aggregated over training valid sales only to avoid leaking the target; everything else aggregates over the universe. The sale field is always sale_price or sale_price_time_adj (see openavmkit.data.get_sale_field), so a prefix check covers both without a circular import back into data.

Source code in openavmkit/utilities/settings.py
415
416
417
418
419
420
421
422
423
424
def is_sale_derived_field(s: dict, field: str) -> bool:
    """Whether ``field`` is the sale price (or a sale-price variant).

    Sale-derived fields must be aggregated over training valid sales only to
    avoid leaking the target; everything else aggregates over the universe. The
    sale field is always ``sale_price`` or ``sale_price_time_adj`` (see
    ``openavmkit.data.get_sale_field``), so a prefix check covers both without a
    circular import back into ``data``.
    """
    return str(field).startswith("sale_price")

length_unit

length_unit(settings)

Get the designated "small" length unit (feet or meters)

Parameters:

Name Type Description Default
settings dict

Settings dictionary

required

Returns:

Type Description
str

"ft" if units are imperial and "m" if units are metric

Source code in openavmkit/utilities/settings.py
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
def length_unit(settings: dict)-> str|None:
    """
    Get the designated "small" length unit (feet or meters)

    Parameters
    ----------
    settings : dict
        Settings dictionary

    Returns
    -------
    str
        "ft" if units are imperial and "m" if units are metric
    """
    base_units = settings.get("locality", {}).get("units", "imperial")
    if base_units == "imperial":
        return "ft"
    elif base_units == "metric":
        return "m"

load_settings

load_settings(settings_file='in/settings.json', settings_object=None, error=True, warning=True)

Load settings file from disk

Parameters:

Name Type Description Default
settings_file str

Path to the settings file

'in/settings.json'
settings_object dict

Already loaded settings object

None
error bool

Whether to raise errors or simply emit warnings if something is wrong

True
warning bool

Whether to emit warnings if something is wrong

True

Returns:

Type Description
dict

The settings object

Source code in openavmkit/utilities/settings.py
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
def load_settings(
    settings_file: str = "in/settings.json", settings_object: dict = None, error:bool=True, warning:bool=True
) -> dict | None:
    """
    Load settings file from disk

    Parameters
    ----------
    settings_file : str
        Path to the settings file
    settings_object : dict, optional
        Already loaded settings object
    error : bool, optional
        Whether to raise errors or simply emit warnings if something is wrong
    warning : bool, optional
        Whether to emit warnings if something is wrong

    Returns
    -------
    dict
        The settings object
    """
    settings : dict | None = None

    if settings_object is None:
        try:
            with open(settings_file, "r") as f:
                settings = json.load(f)
        except FileNotFoundError:
            cwd = os.getcwd()
            full_path = os.path.join(cwd, settings_file)
            exists = os.path.exists(full_path)
            msg = f"Could not find settings file: {settings_file}. Go to '{cwd}' and create a settings.json file there! {full_path} exists? {exists}"
            if error:
                raise FileNotFoundError(msg)
            else:
                if warning:
                    warnings.warn(msg)

    else:
        settings = settings_object

    if settings is None:
        return None

    template = _load_settings_template()
    # merge settings with template; settings will overwrite template values
    settings = _merge_settings(template, settings)
    base_dd = {"data_dictionary": _load_data_dictionary_template()}
    settings = _merge_settings(base_dd, settings)
    settings = _remove_comments_from_settings(settings)
    settings = _replace_variables(settings)
    return settings

make_area_stat_count_field_name

make_area_stat_count_field_name(location, kind='count')

Build a per-location count column name (e.g. area_stat_neighborhood_sales_count).

kind is one of :data:AREA_STAT_COUNT_KINDS; defaults to the universe parcel count.

Source code in openavmkit/utilities/settings.py
407
408
409
410
411
412
def make_area_stat_count_field_name(location: str, kind: str = "count") -> str:
    """Build a per-location count column name (e.g. ``area_stat_neighborhood_sales_count``).

    ``kind`` is one of :data:`AREA_STAT_COUNT_KINDS`; defaults to the universe parcel count.
    """
    return f"{AREA_STAT_PREFIX}{location}_{kind}"

make_area_stat_field_name

make_area_stat_field_name(location, field, stat)

Build the derived column name for one (location, field, stat) combination.

Examples:

make_area_stat_field_name("neighborhood", "bldg_area_finished_sqft", "mean") returns "area_stat_neighborhood_bldg_area_finished_sqft_mean".

Source code in openavmkit/utilities/settings.py
388
389
390
391
392
393
394
395
396
def make_area_stat_field_name(location: str, field: str, stat: str) -> str:
    """Build the derived column name for one (location, field, stat) combination.

    Examples
    --------
    ``make_area_stat_field_name("neighborhood", "bldg_area_finished_sqft", "mean")``
    returns ``"area_stat_neighborhood_bldg_area_finished_sqft_mean"``.
    """
    return f"{AREA_STAT_PREFIX}{location}_{field}_{stat}"

resolve_use_sales_from

resolve_use_sales_from(s, model_group=None)

Resolve modeling.metadata.use_sales_from into per-type year thresholds.

The setting can take four forms:

  • None (missing) — returns (None, None); callers should treat as "no threshold".
  • int — single cutoff applied to both improved and vacant sales.
  • dict {"improved": YYYY, "vacant": YYYY} — per-type cutoffs (missing keys fall back to val_year - 5).
  • dict {"default": <entry>, "by_model_group": {<group>: <entry>}}per-model-group cutoffs, where each <entry> is itself an int or a {improved, vacant} dict. A group listed in by_model_group uses its own window; any other group (or model_group=None) uses default.

Returns (improved_year, vacant_year) for the requested model_group (or the default when no group is given). Always use this helper instead of parsing use_sales_from inline — the dict forms need careful branching, and naïve scalar comparisons against a Series crash with "TypeError: len() of unsized object".

See also :func:use_sales_from_floor, which the cleaning/clipping stages use to keep the widest window any group needs (they hard-drop, and run before the per-group split).

Source code in openavmkit/utilities/settings.py
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
def resolve_use_sales_from(
    s: dict, model_group: str | None = None
) -> tuple[int | None, int | None]:
    """Resolve ``modeling.metadata.use_sales_from`` into per-type year thresholds.

    The setting can take four forms:

      * ``None`` (missing) — returns ``(None, None)``; callers should treat as
        "no threshold".
      * ``int`` — single cutoff applied to both improved and vacant sales.
      * ``dict`` ``{"improved": YYYY, "vacant": YYYY}`` — per-type cutoffs (missing
        keys fall back to ``val_year - 5``).
      * ``dict`` ``{"default": <entry>, "by_model_group": {<group>: <entry>}}`` —
        **per-model-group** cutoffs, where each ``<entry>`` is itself an ``int`` or a
        ``{improved, vacant}`` dict. A group listed in ``by_model_group`` uses its own
        window; any other group (or ``model_group=None``) uses ``default``.

    Returns ``(improved_year, vacant_year)`` for the requested ``model_group`` (or the
    default when no group is given). Always use this helper instead of parsing
    ``use_sales_from`` inline — the dict forms need careful branching, and naïve scalar
    comparisons against a ``Series`` crash with "TypeError: len() of unsized object".

    See also :func:`use_sales_from_floor`, which the cleaning/clipping stages use to keep
    the widest window any group needs (they hard-drop, and run before the per-group split).
    """
    md = s.get("modeling", {}).get("metadata", {})
    if "use_sales_from" not in md:
        return None, None
    usf = md["use_sales_from"]
    if usf is None:
        return None, None
    if isinstance(usf, int):
        return usf, usf
    if isinstance(usf, dict):
        val_year = get_valuation_date(s).year
        if _is_per_group_use_sales_from(usf):
            by_group = usf.get("by_model_group", {}) or {}
            if model_group is not None and model_group in by_group:
                return _parse_use_sales_from_entry(by_group[model_group], val_year)
            return _parse_use_sales_from_entry(usf.get("default"), val_year)
        # legacy per-type {improved, vacant}
        return usf.get("improved", val_year - 5), usf.get("vacant", val_year - 5)
    # Fall through: malformed value — return None/None and let callers no-op.
    return None, None

use_sales_from_floor

use_sales_from_floor(s)

The most-permissive (oldest) use_sales_from across all groups.

The cleaning / clipping stages permanently drop too-old sales before the per-group train/test split runs, so they must keep down to the widest window any model group needs — otherwise a group with a longer reach (e.g. data-starved commercial) would have its older sales deleted before it is ever modeled. This returns that floor; the per-group narrowing then happens in get_data_split_for via :func:resolve_use_sales_from with a model_group.

Floor semantics, per sale type: None (unbounded) if any relevant window is None; otherwise the minimum year. For scalar / legacy {improved, vacant} configs this is identical to :func:resolve_use_sales_from.

Source code in openavmkit/utilities/settings.py
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
def use_sales_from_floor(s: dict) -> tuple[int | None, int | None]:
    """The most-permissive (oldest) ``use_sales_from`` across all groups.

    The cleaning / clipping stages permanently drop too-old sales *before* the per-group
    train/test split runs, so they must keep down to the widest window any model group
    needs — otherwise a group with a longer reach (e.g. data-starved commercial) would
    have its older sales deleted before it is ever modeled. This returns that floor; the
    per-group narrowing then happens in ``get_data_split_for`` via
    :func:`resolve_use_sales_from` with a ``model_group``.

    Floor semantics, per sale type: ``None`` (unbounded) if *any* relevant window is
    ``None``; otherwise the minimum year. For scalar / legacy ``{improved, vacant}``
    configs this is identical to :func:`resolve_use_sales_from`.
    """
    md = s.get("modeling", {}).get("metadata", {})
    if "use_sales_from" not in md or md["use_sales_from"] is None:
        return None, None
    usf = md["use_sales_from"]
    if isinstance(usf, int):
        return usf, usf
    if isinstance(usf, dict):
        val_year = get_valuation_date(s).year
        if _is_per_group_use_sales_from(usf):
            entries = [usf.get("default")] + list((usf.get("by_model_group", {}) or {}).values())
            pairs = [_parse_use_sales_from_entry(e, val_year) for e in entries]
            imprs = [p[0] for p in pairs]
            vacs = [p[1] for p in pairs]
            floor_impr = None if any(x is None for x in imprs) else min(imprs)
            floor_vac = None if any(x is None for x in vacs) else min(vacs)
            return floor_impr, floor_vac
        return usf.get("improved", val_year - 5), usf.get("vacant", val_year - 5)
    return None, None

warn_if_location_collapsed

warn_if_location_collapsed(settings, fields, context, df=None)

Warn (or raise, if strict) when a location field used here was collapsed.

Call this at sites that consume a field as a coherent geographic grouping key (equity clustering, ratio-study breakdowns, local-ensemble selection, etc.). If the field is in :func:get_collapsed_fields, collapsing has merged unrelated zones into the replacement bucket, so grouping by it is wrong.

Deduplicates on (field, context) so repeated per-model-group calls warn only once per run.

Parameters:

Name Type Description Default
settings dict

Settings dictionary.

required
fields str or Iterable[str]

The location field(s) about to be used at this site.

required
context str

Short description of the consuming site, e.g. "horizontal equity clustering" — used in the message.

required
df DataFrame

Unused; accepted so callers can pass it uniformly.

None
Source code in openavmkit/utilities/settings.py
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
def warn_if_location_collapsed(
    settings: dict, fields, context: str, df: pd.DataFrame = None
) -> None:
    """Warn (or raise, if strict) when a location field used here was collapsed.

    Call this at sites that consume a field as a coherent geographic grouping key
    (equity clustering, ratio-study breakdowns, local-ensemble selection, etc.).
    If the field is in :func:`get_collapsed_fields`, collapsing has merged
    unrelated zones into the replacement bucket, so grouping by it is wrong.

    Deduplicates on ``(field, context)`` so repeated per-model-group calls warn
    only once per run.

    Parameters
    ----------
    settings : dict
        Settings dictionary.
    fields : str or Iterable[str]
        The location field(s) about to be used at this site.
    context : str
        Short description of the consuming site, e.g. ``"horizontal equity
        clustering"`` — used in the message.
    df : pandas.DataFrame, optional
        Unused; accepted so callers can pass it uniformly.
    """
    if fields is None:
        return
    if isinstance(fields, str):
        fields = [fields]
    collapsed = get_collapsed_fields(settings)
    strict = is_collapse_strict(settings)
    for field in fields:
        if not field or field not in collapsed:
            continue
        key = (field, context)
        if key in _LOCATION_COLLAPSE_WARNED:
            continue
        _LOCATION_COLLAPSE_WARNED.add(key)
        msg = (
            f"Location field '{field}' is being used as a geographic grouping key "
            f"for {context}, but it was cardinality-collapsed via "
            f"data.process.collapse_sparse_categories. Collapsing a location merges "
            f"unrelated zones into one replacement bucket (e.g. 'Other'), which "
            f"corrupts {context}. Best practice: collapse into a separate "
            f"'{field}_collapsed' modeling variant (set 'output_field' on the collapse "
            f"config) and use that ONLY as a model feature, leaving '{field}' intact "
            f"as the location."
        )
        if strict:
            raise ValueError(msg)
        warnings.warn(msg)