← Files tochi-satei-kunARCHIVED FILE

skills/tochi-satei-kun/scripts/hijun_breakdown.py

6.41 KB · Oct 5, 2026 · 18:30 UTC

↓ Download file

# Copyright 2026 Koichi Matsuda / SignalYield Advisory
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""比準表「補修正率と地域格差率」詳細内訳の計算(v1.2.9 で correction.py から切り出し)。

鑑定実務での 4 区分(標準化補正:規模・画地、地域格差:街路・交通接近・環境・行政)
への分解と、各細目の % 表示を生成する。correction.py 本体を 20KB 未満に圧縮し
Cowork 配布層の truncate を回避するため独立モジュール化。
"""
import math
import pandas as pd

# correction.py から型情報・特徴量定数・ヘルパーをインポート
from correction import (CORRECTION_FEATURES, HIJUN_DETAIL_GROUP, HIJUN_DETAIL_LABEL,
                        _target_feature_value, _case_feature_value)


def hijun_breakdown_detail(row, hedonic_result, target):
    """事例1件について「補修正率と地域格差率」表用の詳細内訳を計算。

    Returns:
        {
          "事情補正": (label, percent),   # 例: ("正常", 0.0)
          "時点修正_pct": float,           # +%(年率×経過年数)
          "建付減価": (label, percent),   # ("更地", None) etc.
          "規模": [(label, %), ...],       # 標準化補正配下の細目
          "画地": [(label, %), ...],
          "化正相乗積": int,               # 100 + 規模%和 + 画地%和(または積×100)
          "街路": [(label, %), ...],       # 地域格差配下
          "交通接近": [(label, %), ...],
          "環境": [(label, %), ...],
          "行政": [(label, %), ...],       # 現状は空(β未取得)
          "街路_総和": float,
          "交通接近_総和": float,
          "環境_総和": float,
          "行政_総和": float,
          "相乗積": int,                   # 地域格差4区分の積×100
        }
    """
    out = {
        "事情補正": ("正常", 0.0),
        "建付減価": ("更地", None),
        "規模": [], "画地": [],
        "街路": [], "交通接近": [], "環境": [], "行政": [],
    }
    # 時点修正:adjusted/unit_price から %に変換
    base = float(row["unit_price"])
    if "adjusted_unit_price" in row and pd.notna(row["adjusted_unit_price"]):
        time_mult = float(row["adjusted_unit_price"]) / base if base > 0 else 1.0
    else:
        time_mult = 1.0
    out["時点修正_pct"] = (time_mult - 1.0) * 100

    if not hedonic_result.get("ok"):
        out["化正相乗積"] = 100
        out["街路_総和"] = 0.0
        out["交通接近_総和"] = 0.0
        out["環境_総和"] = 0.0
        out["行政_総和"] = 0.0
        out["相乗積"] = 100
        return out

    # v1.2.1: Style B — 事例側の値のみラベルに付記。査定対象側の値は個別格差シートに転記される設計。
    # 方位は事例の道路方位「方位(南)」「方位(東)」、地区は事例の地区「地区(赤堤)」等。
    case_road_dir = str(row.get("road_dir", "")).strip()
    case_district = str(row.get("district", "")).strip()
    def _label_for(feat):
        base = HIJUN_DETAIL_LABEL.get(feat, feat)
        if feat == "dir_score" and case_road_dir:
            return f"{base}({case_road_dir})"
        if feat == "ln_district_mean" and case_district:
            return f"{base}({case_district})"
        return base

    coef = hedonic_result["coefficients"]
    # 標準化補正の細目
    hyojunka_log_total = 0.0
    # 規模(ln_area + ln_area_sq)は1項目に統合して表示
    kibo_log = 0.0
    for feat in ("ln_area", "ln_area_sq"):
        if feat in coef and feat in HIJUN_DETAIL_GROUP:
            beta = coef[feat]["beta"]
            tx = _target_feature_value(target, feat)
            cx = _case_feature_value(row, feat)
            contrib = beta * (tx - cx)
            kibo_log += contrib
            hyojunka_log_total += contrib
    kibo_pct = (math.exp(kibo_log) - 1.0) * 100
    if abs(round(kibo_pct, 1)) >= 0.05:
        out["規模"].append(("規模", kibo_pct))

    # 画地(形状、袋地、不整形、方位)は個別表示
    for feat in CORRECTION_FEATURES:
        if feat not in coef or feat not in HIJUN_DETAIL_GROUP:
            continue
        if feat in ("ln_area", "ln_area_sq"):
            continue  # 既に処理済
        group = HIJUN_DETAIL_GROUP[feat]
        if group != "画地":
            continue
        beta = coef[feat]["beta"]
        tx = _target_feature_value(target, feat)
        cx = _case_feature_value(row, feat)
        contrib = beta * (tx - cx)  # 対数空間の補正
        pct = (math.exp(contrib) - 1.0) * 100
        out[group].append((_label_for(feat), pct))
        hyojunka_log_total += contrib
    # 標準化補正の総和(相乗積)
    out["標準化補正_総和"] = (math.exp(hyojunka_log_total) - 1.0) * 100
    # 化正相乗積 = exp(Σ log) × 100(後方互換のため残す)
    out["化正相乗積"] = int(round(math.exp(hyojunka_log_total) * 100))

    # 地域格差の細目(4区分)
    chiiki_subgroups = ("街路", "交通接近", "環境", "行政")
    subgroup_log = {g: 0.0 for g in chiiki_subgroups}
    for feat in CORRECTION_FEATURES:
        if feat not in coef or feat not in HIJUN_DETAIL_GROUP:
            continue
        group = HIJUN_DETAIL_GROUP[feat]
        if group not in chiiki_subgroups:
            continue
        beta = coef[feat]["beta"]
        tx = _target_feature_value(target, feat)
        cx = _case_feature_value(row, feat)
        contrib = beta * (tx - cx)
        pct = (math.exp(contrib) - 1.0) * 100
        out[group].append((_label_for(feat), pct))
        subgroup_log[group] += contrib

    for g in chiiki_subgroups:
        out[f"{g}_総和"] = (math.exp(subgroup_log[g]) - 1.0) * 100
    out["相乗積"] = int(round(math.exp(sum(subgroup_log.values())) * 100))
    return out

SHA-256: 939663cc0eafeb3c23c32d4c05ba659130e361d12d84c8804860299314904a58