Update app.py
Browse files
app.py
CHANGED
|
@@ -696,110 +696,148 @@ def build_faktor_wilayah(df_filtered: pd.DataFrame, pop_kab: pd.DataFrame, pop_p
|
|
| 696 |
# 7) AGREGAT WILAYAH × JENIS (Final pakai faktor wilayah)
|
| 697 |
# ============================================================
|
| 698 |
|
| 699 |
-
def
|
| 700 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 701 |
return pd.DataFrame()
|
| 702 |
|
| 703 |
kew_norm = str(kew_value or "").upper()
|
| 704 |
-
|
| 705 |
|
| 706 |
-
|
| 707 |
-
key_col = "kab_key"
|
| 708 |
-
label_col = "KAB_DISP"
|
| 709 |
-
label_name = "Kab/Kota"
|
| 710 |
-
elif "PROV" in kew_norm:
|
| 711 |
-
key_col = "prov_key"
|
| 712 |
-
label_col = "PROV_DISP"
|
| 713 |
-
label_name = "Provinsi"
|
| 714 |
-
else:
|
| 715 |
-
key_col = "kab_key"
|
| 716 |
-
label_col = "KAB_DISP"
|
| 717 |
-
label_name = "Kab/Kota"
|
| 718 |
|
| 719 |
-
|
| 720 |
-
|
| 721 |
-
return pd.DataFrame()
|
| 722 |
|
| 723 |
-
|
| 724 |
-
Jumlah=("Indeks_Dasar_0_100", "size"),
|
| 725 |
-
Rata2_sub_koleksi=("sub_koleksi", "mean"),
|
| 726 |
-
Rata2_sub_sdm=("sub_sdm", "mean"),
|
| 727 |
-
Rata2_sub_pelayanan=("sub_pelayanan", "mean"),
|
| 728 |
-
Rata2_sub_pengelolaan=("sub_pengelolaan", "mean"),
|
| 729 |
-
Rata2_dim_kepatuhan=("dim_kepatuhan", "mean"),
|
| 730 |
-
Rata2_dim_kinerja=("dim_kinerja", "mean"),
|
| 731 |
-
Indeks_Dasar_Agregat_0_100=("Indeks_Dasar_0_100", "mean"),
|
| 732 |
-
).reset_index()
|
| 733 |
|
| 734 |
-
|
|
|
|
|
|
|
|
|
|
| 735 |
|
| 736 |
-
|
| 737 |
-
|
| 738 |
-
|
| 739 |
-
|
| 740 |
-
|
| 741 |
-
|
| 742 |
-
|
| 743 |
-
|
| 744 |
-
|
| 745 |
-
|
| 746 |
-
|
| 747 |
-
|
| 748 |
-
|
| 749 |
-
|
| 750 |
-
|
| 751 |
-
|
| 752 |
-
|
| 753 |
-
|
| 754 |
-
|
| 755 |
-
|
| 756 |
-
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
|
| 760 |
-
|
| 761 |
-
|
| 762 |
-
|
| 763 |
-
|
| 764 |
-
|
| 765 |
-
|
| 766 |
-
|
| 767 |
-
if pd.notna(t):
|
| 768 |
-
agg.at[i, "target_total_68_jenis"] = float(t)
|
| 769 |
-
if pd.notna(p):
|
| 770 |
-
agg.at[i, "pop_total_jenis"] = float(p)
|
| 771 |
|
| 772 |
-
|
| 773 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 774 |
|
| 775 |
-
|
| 776 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 777 |
|
| 778 |
-
|
| 779 |
-
|
| 780 |
|
| 781 |
-
# rounding (
|
| 782 |
for c in [
|
| 783 |
"Rata2_sub_koleksi","Rata2_sub_sdm","Rata2_sub_pelayanan","Rata2_sub_pengelolaan",
|
| 784 |
"Rata2_dim_kepatuhan","Rata2_dim_kinerja"
|
| 785 |
]:
|
| 786 |
-
if c in
|
| 787 |
-
|
| 788 |
-
|
| 789 |
-
for c in ["Indeks_Dasar_Agregat_0_100","Indeks_Final_Agregat_0_100"]:
|
| 790 |
-
if c in agg.columns:
|
| 791 |
-
agg[c] = pd.to_numeric(agg[c], errors="coerce").fillna(0.0).round(2)
|
| 792 |
|
| 793 |
-
for c in ["
|
| 794 |
-
if c in
|
| 795 |
-
|
| 796 |
|
| 797 |
-
|
| 798 |
-
|
| 799 |
-
agg[c] = pd.to_numeric(agg[c], errors="coerce").fillna(0.0)
|
| 800 |
|
| 801 |
-
|
|
|
|
|
|
|
| 802 |
|
|
|
|
| 803 |
|
| 804 |
# ============================================================
|
| 805 |
# 8) AGREGAT WILAYAH (KESELURUHAN) — RUMUS BARU: AVG3 dari 3 jenis
|
|
|
|
| 696 |
# 7) AGREGAT WILAYAH × JENIS (Final pakai faktor wilayah)
|
| 697 |
# ============================================================
|
| 698 |
|
| 699 |
+
def build_agg_wilayah_total_from_jenis(
|
| 700 |
+
agg_jenis: pd.DataFrame,
|
| 701 |
+
faktor_wilayah: pd.DataFrame,
|
| 702 |
+
pop_khusus: pd.DataFrame, # <-- PATCH: tambah
|
| 703 |
+
kew_value: str
|
| 704 |
+
):
|
| 705 |
+
if agg_jenis is None or agg_jenis.empty:
|
| 706 |
return pd.DataFrame()
|
| 707 |
|
| 708 |
kew_norm = str(kew_value or "").upper()
|
| 709 |
+
label_name = "Kab/Kota" if ("KAB" in kew_norm or "KOTA" in kew_norm) else ("Provinsi" if "PROV" in kew_norm else "Kab/Kota")
|
| 710 |
|
| 711 |
+
jenis_list = ["sekolah", "umum", "khusus"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 712 |
|
| 713 |
+
a = agg_jenis.copy()
|
| 714 |
+
a["Jenis"] = a["Jenis"].astype(str).str.lower().str.strip()
|
|
|
|
| 715 |
|
| 716 |
+
base_keys = a[["group_key", label_name]].drop_duplicates()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 717 |
|
| 718 |
+
full = base_keys.assign(_tmp=1).merge(
|
| 719 |
+
pd.DataFrame({"Jenis": jenis_list, "_tmp": 1}),
|
| 720 |
+
on="_tmp"
|
| 721 |
+
).drop(columns="_tmp")
|
| 722 |
|
| 723 |
+
cols_need = [
|
| 724 |
+
"Jumlah",
|
| 725 |
+
"Rata2_sub_koleksi","Rata2_sub_sdm","Rata2_sub_pelayanan","Rata2_sub_pengelolaan",
|
| 726 |
+
"Rata2_dim_kepatuhan","Rata2_dim_kinerja",
|
| 727 |
+
"Indeks_Dasar_Agregat_0_100",
|
| 728 |
+
"Indeks_Final_Agregat_0_100",
|
| 729 |
+
]
|
| 730 |
+
cols_present = [c for c in cols_need if c in a.columns]
|
| 731 |
+
|
| 732 |
+
full = full.merge(
|
| 733 |
+
a[["group_key", label_name, "Jenis"] + cols_present],
|
| 734 |
+
on=["group_key", label_name, "Jenis"],
|
| 735 |
+
how="left"
|
| 736 |
+
)
|
| 737 |
+
|
| 738 |
+
# missing=0 (kunci avg3 tetap ÷3)
|
| 739 |
+
for c in cols_present:
|
| 740 |
+
full[c] = pd.to_numeric(full[c], errors="coerce").fillna(0.0)
|
| 741 |
+
|
| 742 |
+
# keseluruhan wilayah = avg3 dari 3 jenis
|
| 743 |
+
out = full.groupby(["group_key", label_name], as_index=False).agg(
|
| 744 |
+
n_total=("Jumlah", "sum"),
|
| 745 |
+
Rata2_sub_koleksi=("Rata2_sub_koleksi", "mean"),
|
| 746 |
+
Rata2_sub_sdm=("Rata2_sub_sdm", "mean"),
|
| 747 |
+
Rata2_sub_pelayanan=("Rata2_sub_pelayanan", "mean"),
|
| 748 |
+
Rata2_sub_pengelolaan=("Rata2_sub_pengelolaan", "mean"),
|
| 749 |
+
Rata2_dim_kepatuhan=("Rata2_dim_kepatuhan", "mean"),
|
| 750 |
+
Rata2_dim_kinerja=("Rata2_dim_kinerja", "mean"),
|
| 751 |
+
Indeks_Dasar_Agregat_0_100=("Indeks_Dasar_Agregat_0_100", "mean"),
|
| 752 |
+
Indeks_Final_Wilayah_0_100=("Indeks_Final_Agregat_0_100", "mean"),
|
| 753 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 754 |
|
| 755 |
+
# tempel faktor/target/pop/coverage (informasi verifikasi)
|
| 756 |
+
if faktor_wilayah is not None and not faktor_wilayah.empty:
|
| 757 |
+
fw = faktor_wilayah.copy()
|
| 758 |
+
keep_fw = ["group_key", label_name, "target_total_68", "pop_total", "faktor_penyesuaian", "coverage_total_%"]
|
| 759 |
+
keep_fw = [c for c in keep_fw if c in fw.columns]
|
| 760 |
+
out = out.merge(fw[keep_fw], on=["group_key", label_name], how="left")
|
| 761 |
|
| 762 |
+
# =========================================================
|
| 763 |
+
# PATCH UTAMA:
|
| 764 |
+
# Tambahkan target_total_68 & pop_total dari POP_KHUSUS
|
| 765 |
+
# (tanpa kolom khusus tambahan)
|
| 766 |
+
# =========================================================
|
| 767 |
+
if (pop_khusus is not None) and (not pop_khusus.empty):
|
| 768 |
+
pk = pop_khusus.copy()
|
| 769 |
+
|
| 770 |
+
# pastikan numerik
|
| 771 |
+
for c in ["Target68_Total_Jenis", "Pop_Total_Jenis"]:
|
| 772 |
+
if c in pk.columns:
|
| 773 |
+
pk[c] = pd.to_numeric(pk[c], errors="coerce").fillna(0.0)
|
| 774 |
+
|
| 775 |
+
add_df = None
|
| 776 |
+
|
| 777 |
+
# KAB/KOTA: join langsung by kab_key == group_key
|
| 778 |
+
if ("KAB" in kew_norm or "KOTA" in kew_norm):
|
| 779 |
+
if "kab_key" in pk.columns:
|
| 780 |
+
add_df = pk.groupby("kab_key", as_index=False).agg(
|
| 781 |
+
add_target=("Target68_Total_Jenis", "sum"),
|
| 782 |
+
add_pop=("Pop_Total_Jenis", "sum"),
|
| 783 |
+
).rename(columns={"kab_key": "group_key"})
|
| 784 |
+
|
| 785 |
+
# PROVINSI: agregasi by prov_key == group_key
|
| 786 |
+
elif ("PROV" in kew_norm):
|
| 787 |
+
if "prov_key" in pk.columns:
|
| 788 |
+
add_df = pk.groupby("prov_key", as_index=False).agg(
|
| 789 |
+
add_target=("Target68_Total_Jenis", "sum"),
|
| 790 |
+
add_pop=("Pop_Total_Jenis", "sum"),
|
| 791 |
+
).rename(columns={"prov_key": "group_key"})
|
| 792 |
+
|
| 793 |
+
if add_df is not None and not add_df.empty:
|
| 794 |
+
out = out.merge(add_df, on="group_key", how="left")
|
| 795 |
+
out["add_target"] = pd.to_numeric(out.get("add_target", 0), errors="coerce").fillna(0.0)
|
| 796 |
+
out["add_pop"] = pd.to_numeric(out.get("add_pop", 0), errors="coerce").fillna(0.0)
|
| 797 |
+
|
| 798 |
+
# tambah ke total existing (kalau belum ada kolomnya -> dianggap 0)
|
| 799 |
+
out["target_total_68"] = pd.to_numeric(out.get("target_total_68", 0), errors="coerce").fillna(0.0) + out["add_target"]
|
| 800 |
+
out["pop_total"] = pd.to_numeric(out.get("pop_total", 0), errors="coerce").fillna(0.0) + out["add_pop"]
|
| 801 |
+
|
| 802 |
+
# opsional tapi konsisten: update coverage & faktor berdasarkan total baru (DI TABEL INI SAJA)
|
| 803 |
+
out["coverage_total_%"] = [
|
| 804 |
+
(safe_div(n, p) * 100) if (p is not None and not pd.isna(p) and float(p) > 0) else 0.0
|
| 805 |
+
for n, p in zip(
|
| 806 |
+
pd.to_numeric(out.get("n_total", 0), errors="coerce").fillna(0).astype(float).tolist(),
|
| 807 |
+
pd.to_numeric(out.get("pop_total", 0), errors="coerce").fillna(0).astype(float).tolist()
|
| 808 |
+
)
|
| 809 |
+
]
|
| 810 |
+
out["faktor_penyesuaian"] = [
|
| 811 |
+
faktor_penyesuaian_total(n, t)
|
| 812 |
+
for n, t in zip(
|
| 813 |
+
pd.to_numeric(out.get("n_total", 0), errors="coerce").fillna(0).astype(float).tolist(),
|
| 814 |
+
pd.to_numeric(out.get("target_total_68", 0), errors="coerce").fillna(0).astype(float).tolist()
|
| 815 |
+
)
|
| 816 |
+
]
|
| 817 |
|
| 818 |
+
# bersihkan kolom helper
|
| 819 |
+
out = out.drop(columns=[c for c in ["add_target", "add_pop"] if c in out.columns])
|
| 820 |
|
| 821 |
+
# rounding (yang sudah ada sebelumnya)
|
| 822 |
for c in [
|
| 823 |
"Rata2_sub_koleksi","Rata2_sub_sdm","Rata2_sub_pelayanan","Rata2_sub_pengelolaan",
|
| 824 |
"Rata2_dim_kepatuhan","Rata2_dim_kinerja"
|
| 825 |
]:
|
| 826 |
+
if c in out.columns:
|
| 827 |
+
out[c] = pd.to_numeric(out[c], errors="coerce").fillna(0.0).round(3)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 828 |
|
| 829 |
+
for c in ["Indeks_Dasar_Agregat_0_100","Indeks_Final_Wilayah_0_100"]:
|
| 830 |
+
if c in out.columns:
|
| 831 |
+
out[c] = pd.to_numeric(out[c], errors="coerce").fillna(0.0).round(2)
|
| 832 |
|
| 833 |
+
if "faktor_penyesuaian" in out.columns:
|
| 834 |
+
out["faktor_penyesuaian"] = pd.to_numeric(out["faktor_penyesuaian"], errors="coerce").fillna(1.0).round(3)
|
|
|
|
| 835 |
|
| 836 |
+
for c in ["target_total_68","pop_total","coverage_total_%"]:
|
| 837 |
+
if c in out.columns:
|
| 838 |
+
out[c] = pd.to_numeric(out[c], errors="coerce").fillna(0.0)
|
| 839 |
|
| 840 |
+
return out
|
| 841 |
|
| 842 |
# ============================================================
|
| 843 |
# 8) AGREGAT WILAYAH (KESELURUHAN) — RUMUS BARU: AVG3 dari 3 jenis
|