platform = "gcolab"
# platform = "kaggle"
compute_type = "cpu" # 'cpu' atau 'gpu'FINANCIAL DISTRESS PREDICTION
TOGGLE GColab / Kaggle
import os
if platform == "gcolab":
# ----- Google Colab ----- #
import importlib.util
if importlib.util.find_spec("catboost") is None:
%pip install catboost -q
if importlib.util.find_spec("optuna") is None:
%pip install optuna -q
from google.colab import drive, output
MOUNT_POINT = "/content/drive"
prefix_path = f"{MOUNT_POINT}/MyDrive/SKRIPSI/Dataset/"
save_path = prefix_path
if not os.path.isdir(f"{MOUNT_POINT}/MyDrive"):
drive.mount(MOUNT_POINT)
if not os.path.isdir(prefix_path):
raise FileNotFoundError(f"Folder tidak ditemukan: {prefix_path}")
if not os.path.isdir(save_path):
raise FileNotFoundError(f"Folder tidak ditemukan: {save_path}")
elif platform == "kaggle":
# ----- Kaggle ----- #
prefix_path = "/kaggle/input/idx-financial-data/"
save_path = "/kaggle/working/"
os.makedirs(save_path + 'pickle', exist_ok=True)
if compute_type == 'cpu':
prefix_path = "/kaggle/input/datasets/daffarestupratama/idx-financial-data/"
else:
raise ValueError("Invalid platform choice")Libraries
Install
%pip install -U kaleido==0.2.1 -qImport
import warningsimport pandas as pd
import numpy as np
import sys
import re
from IPython.display import clear_output, display
import math
import textwrap
import pickleimport matplotlib.pyplot as plt
from matplotlib.colors import Normalize, LinearSegmentedColormap
from matplotlib.collections import LineCollection
from matplotlib.patches import Patch
from matplotlib.lines import Line2D
import plotly.graph_objects as go
import seaborn as snsfrom statsmodels.graphics.mosaicplot import mosaicfrom scipy.stats import chi2_contingency
from scipy.stats import fisher_exactfrom sklearn.experimental import enable_iterative_imputer
from sklearn.model_selection import train_test_split, GridSearchCV, cross_val_predict
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import AdaBoostClassifier
from sklearn.preprocessing import StandardScaler, MinMaxScaler, OrdinalEncoder
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
from sklearn.model_selection import train_test_split, RandomizedSearchCV, StratifiedKFold, GridSearchCV
from sklearn.base import BaseEstimator, ClassifierMixin, clone, TransformerMixin
from sklearn.utils.validation import check_is_fitted
from sklearn.impute import KNNImputer, SimpleImputer, IterativeImputer
from sklearn.metrics import roc_auc_score, average_precision_score, classification_report, confusion_matrix, precision_recall_curve, accuracy_score, precision_score, recall_score, f1_score, classification_report, roc_curve, matthews_corrcoef
from sklearn.pipeline import Pipeline
from sklearn.feature_selection import mutual_info_classiffrom imblearn.pipeline import Pipeline as ImbPipeline
from imblearn.over_sampling import SMOTE, RandomOverSampler
import optunaimport statsmodels.api as sm
from statsmodels.stats.outliers_influence import variance_inflation_factor
from statsmodels.tools.tools import add_constantfrom sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier
from xgboost import XGBClassifier
from lightgbm import LGBMClassifier
from catboost import CatBoostClassifierimport shapSet Options & Constants
pd.set_option('display.max_columns', None)pd.set_option('display.float_format', lambda x: '%.6f' % x)RANDOM_STATE = 42Helper Functions
# Untuk ubah df ndarray jadi numpy dan contiguous
# Dipakai saat pindah dari gpu ke cpu
def to_float32_contiguous(X):
# X bisa DataFrame atau ndarray
arr = X.to_numpy() if hasattr(X, "to_numpy") else np.asarray(X)
return np.asarray(arr, dtype=np.float32, order="C")from IPython.display import display, Audio
# Membuat custom exception agar error log tidak terlalu panjang dan berantakan
class StopExecution(Exception):
def _render_traceback_(self):
pass
def limit():
# Menggunakan modul standar IPython untuk memutar audio
for _ in range(2):
display(Audio(url="https://upload.wikimedia.org/wikipedia/commons/0/05/Beep-09.ogg", autoplay=True))
print("Exiting program to limit code run.")
# Menghentikan eksekusi cell selanjutnya tanpa mematikan kernel
raise StopExecution("Eksekusi dihentikan secara sengaja.")
def end():
for _ in range(5):
display(Audio(url="https://upload.wikimedia.org/wikipedia/commons/0/05/Beep-09.ogg", autoplay=True))
print("End of program.")
raise StopExecution("Program selesai.")Business Understanding
Tujuan Bisnis
Kriteria Sukses Bisnis
Menerjemahkan ke Tujuan Data Mining
Daftar Perusahaan
Load Daftar Nama dan Kode Saham
df_overview = pd.read_excel(prefix_path + 'Daftar_Saham.xlsx')
df_overview| No | Kode | Nama Perusahaan | Tanggal Pencatatan | Saham | Papan Pencatatan | |
|---|---|---|---|---|---|---|
| 0 | 1 | AALI | Astra Agro Lestari Tbk. | 09 Des 1997 | 1.924.688.333 | Utama |
| 1 | 2 | ABBA | Mahaka Media Tbk. | 03 Apr 2002 | 3.935.892.857 | Pemantauan Khusus |
| 2 | 3 | ABDA | Asuransi Bina Dana Arta Tbk. | 06 Jul 1989 | 620.806.680 | Pemantauan Khusus |
| 3 | 4 | ABMM | ABM Investama Tbk. | 06 Des 2011 | 2.753.165.000 | Utama |
| 4 | 5 | ACES | Aspirasi Hidup Indonesia Tbk. | 06 Nov 2007 | 17.120.389.700 | Utama |
| ... | ... | ... | ... | ... | ... | ... |
| 950 | 951 | ASPI | Andalan Sakti Primaindo Tbk. | 17 Feb 2020 | 681.823.317 | Pemantauan Khusus |
| 951 | 952 | ESTA | Esta Multi Usaha Tbk. | 09 Mar 2020 | 2.425.354.179 | Pengembangan |
| 952 | 953 | BESS | Batulicin Nusantara Maritim Tb | 09 Mar 2020 | 3.440.455.528 | Pengembangan |
| 953 | 954 | AMAN | Makmur Berkah Amanda Tbk. | 13 Mar 2020 | 3.873.500.000 | Pengembangan |
| 954 | 955 | CARE | Metro Healthcare Indonesia Tbk | 13 Mar 2020 | 33.250.000.000 | Pengembangan |
955 rows × 6 columns
Load Papan Pemantauan Khusus
df_ppk = pd.read_excel(prefix_path + 'Data_PPK.xlsx')
df_ppk| Kode Saham | Nama Perusahaan | Tanggal Masuk | Tanggal Keluar | Kriteria | |
|---|---|---|---|---|---|
| 0 | OCAP | ONIX CAPITAL Tbk | 19 Jul 2021 | NaN | 2, 3, 5, 7 |
| 1 | PICO | Pelangi Indah Canindo Tbk | 19 Jul 2021 | 13 Jun 2022 | 8 |
| 2 | BINA | PT Bank Ina Perdana Tbk. | 21 Jul 2021 | 20 Agt 2021 | 10 |
| 3 | GMFI | PT Garuda Maintenance Facility Aero Asia Tbk. | 29 Jul 2021 | NaN | 5 |
| 4 | BOLA | PT Bali Bintang Sejahtera Tbk. | 20 Agt 2021 | 20 Sep 2021 | 10 |
| ... | ... | ... | ... | ... | ... |
| 401 | PGJO | PT Tourindo Guide Indonesia Tbk. | 17 Sep 2025 | NaN | 10 |
| 402 | IRSX | PT Aviana Sinar Abadi Tbk | 18 Sep 2025 | NaN | 1 |
| 403 | RONY | PT Aracord Nusantara Group Tbk | 18 Sep 2025 | NaN | 7, 10 |
| 404 | LAPD | Leyand International Tbk | 19 Sep 2025 | NaN | 1 |
| 405 | PIPA | PT Multi Makmur Lemindo Tbk. | 22 Sep 2025 | NaN | 10 |
406 rows × 5 columns
Merge Data Daftar Saham
df_emiten_idx = pd.merge(df_overview, df_ppk, left_on='Kode', right_on='Kode Saham', how='left')
df_emiten_idx| No | Kode | Nama Perusahaan_x | Tanggal Pencatatan | Saham | Papan Pencatatan | Kode Saham | Nama Perusahaan_y | Tanggal Masuk | Tanggal Keluar | Kriteria | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | AALI | Astra Agro Lestari Tbk. | 09 Des 1997 | 1.924.688.333 | Utama | NaN | NaN | NaN | NaN | NaN |
| 1 | 2 | ABBA | Mahaka Media Tbk. | 03 Apr 2002 | 3.935.892.857 | Pemantauan Khusus | ABBA | Mahaka Media Tbk | 31 Mei 2024 | NaN | 1, 5 |
| 2 | 3 | ABDA | Asuransi Bina Dana Arta Tbk. | 06 Jul 1989 | 620.806.680 | Pemantauan Khusus | ABDA | Asuransi Bina Dana Arta Tbk | 09 Apr 2025 | NaN | 7 |
| 3 | 4 | ABMM | ABM Investama Tbk. | 06 Des 2011 | 2.753.165.000 | Utama | NaN | NaN | NaN | NaN | NaN |
| 4 | 5 | ACES | Aspirasi Hidup Indonesia Tbk. | 06 Nov 2007 | 17.120.389.700 | Utama | NaN | NaN | NaN | NaN | NaN |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 950 | 951 | ASPI | Andalan Sakti Primaindo Tbk. | 17 Feb 2020 | 681.823.317 | Pemantauan Khusus | ASPI | PT Andalan Sakti Primaindo Tbk. | 24 Jul 2025 | 04 Agt 2025 | 10 |
| 951 | 952 | ESTA | Esta Multi Usaha Tbk. | 09 Mar 2020 | 2.425.354.179 | Pengembangan | ESTA | PT Esta Multi Usaha Tbk. | 30 Apr 2025 | 04 Jun 2025 | 6 |
| 952 | 953 | BESS | Batulicin Nusantara Maritim Tb | 09 Mar 2020 | 3.440.455.528 | Pengembangan | BESS | PT Batulicin Nusantara Maritim Tbk. | 10 Mar 2025 | 19 Mar 2025 | 10 |
| 953 | 954 | AMAN | Makmur Berkah Amanda Tbk. | 13 Mar 2020 | 3.873.500.000 | Pengembangan | NaN | NaN | NaN | NaN | NaN |
| 954 | 955 | CARE | Metro Healthcare Indonesia Tbk | 13 Mar 2020 | 33.250.000.000 | Pengembangan | NaN | NaN | NaN | NaN | NaN |
955 rows × 11 columns
Hapus kolom yang redundant
# Drop redundant columns
df_emiten_idx = df_emiten_idx.drop(columns=['Kode Saham', 'Nama Perusahaan_y', 'No'])
# Rename 'Nama Perusahaan_x' column
df_emiten_idx = df_emiten_idx.rename(columns={'Nama Perusahaan_x': 'Nama Perusahaan'})
# Display the updated DataFrame
df_emiten_idx| Kode | Nama Perusahaan | Tanggal Pencatatan | Saham | Papan Pencatatan | Tanggal Masuk | Tanggal Keluar | Kriteria | |
|---|---|---|---|---|---|---|---|---|
| 0 | AALI | Astra Agro Lestari Tbk. | 09 Des 1997 | 1.924.688.333 | Utama | NaN | NaN | NaN |
| 1 | ABBA | Mahaka Media Tbk. | 03 Apr 2002 | 3.935.892.857 | Pemantauan Khusus | 31 Mei 2024 | NaN | 1, 5 |
| 2 | ABDA | Asuransi Bina Dana Arta Tbk. | 06 Jul 1989 | 620.806.680 | Pemantauan Khusus | 09 Apr 2025 | NaN | 7 |
| 3 | ABMM | ABM Investama Tbk. | 06 Des 2011 | 2.753.165.000 | Utama | NaN | NaN | NaN |
| 4 | ACES | Aspirasi Hidup Indonesia Tbk. | 06 Nov 2007 | 17.120.389.700 | Utama | NaN | NaN | NaN |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 950 | ASPI | Andalan Sakti Primaindo Tbk. | 17 Feb 2020 | 681.823.317 | Pemantauan Khusus | 24 Jul 2025 | 04 Agt 2025 | 10 |
| 951 | ESTA | Esta Multi Usaha Tbk. | 09 Mar 2020 | 2.425.354.179 | Pengembangan | 30 Apr 2025 | 04 Jun 2025 | 6 |
| 952 | BESS | Batulicin Nusantara Maritim Tb | 09 Mar 2020 | 3.440.455.528 | Pengembangan | 10 Mar 2025 | 19 Mar 2025 | 10 |
| 953 | AMAN | Makmur Berkah Amanda Tbk. | 13 Mar 2020 | 3.873.500.000 | Pengembangan | NaN | NaN | NaN |
| 954 | CARE | Metro Healthcare Indonesia Tbk | 13 Mar 2020 | 33.250.000.000 | Pengembangan | NaN | NaN | NaN |
955 rows × 8 columns
Convert Tanggal
# Map Indonesian month abbreviations to English ones
indo_to_eng_month = {
'Jan': 'Jan', 'Feb': 'Feb', 'Mar': 'Mar', 'Apr': 'Apr', 'Mei': 'May', 'Jun': 'Jun',
'Jul': 'Jul', 'Agt': 'Aug', 'Sep': 'Sep', 'Okt': 'Oct', 'Nov': 'Nov', 'Des': 'Dec'
}
# Convert date columns to datetime objects
date_cols = ['Tanggal Pencatatan', 'Tanggal Masuk', 'Tanggal Keluar']
for col in date_cols:
# Replace Indonesian month abbreviations with English ones
df_emiten_idx[col] = df_emiten_idx[col].astype(str).replace(indo_to_eng_month, regex=True)
df_emiten_idx[col] = pd.to_datetime(df_emiten_idx[col], format='%d %b %Y', errors='coerce')
df_emiten_idx| Kode | Nama Perusahaan | Tanggal Pencatatan | Saham | Papan Pencatatan | Tanggal Masuk | Tanggal Keluar | Kriteria | |
|---|---|---|---|---|---|---|---|---|
| 0 | AALI | Astra Agro Lestari Tbk. | 1997-12-09 | 1.924.688.333 | Utama | NaT | NaT | NaN |
| 1 | ABBA | Mahaka Media Tbk. | 2002-04-03 | 3.935.892.857 | Pemantauan Khusus | 2024-05-31 | NaT | 1, 5 |
| 2 | ABDA | Asuransi Bina Dana Arta Tbk. | 1989-07-06 | 620.806.680 | Pemantauan Khusus | 2025-04-09 | NaT | 7 |
| 3 | ABMM | ABM Investama Tbk. | 2011-12-06 | 2.753.165.000 | Utama | NaT | NaT | NaN |
| 4 | ACES | Aspirasi Hidup Indonesia Tbk. | 2007-11-06 | 17.120.389.700 | Utama | NaT | NaT | NaN |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 950 | ASPI | Andalan Sakti Primaindo Tbk. | 2020-02-17 | 681.823.317 | Pemantauan Khusus | 2025-07-24 | 2025-08-04 | 10 |
| 951 | ESTA | Esta Multi Usaha Tbk. | 2020-03-09 | 2.425.354.179 | Pengembangan | 2025-04-30 | 2025-06-04 | 6 |
| 952 | BESS | Batulicin Nusantara Maritim Tb | 2020-03-09 | 3.440.455.528 | Pengembangan | 2025-03-10 | 2025-03-19 | 10 |
| 953 | AMAN | Makmur Berkah Amanda Tbk. | 2020-03-13 | 3.873.500.000 | Pengembangan | NaT | NaT | NaN |
| 954 | CARE | Metro Healthcare Indonesia Tbk | 2020-03-13 | 33.250.000.000 | Pengembangan | NaT | NaT | NaN |
955 rows × 8 columns
Tambah Kolom PPK (Yes/No)
# Kolom Identifikasi PPK
df_emiten_idx['PPK'] = df_emiten_idx['Tanggal Masuk'].apply(lambda x: 'Yes' if pd.notna(x) else 'No')
df_emiten_idx| Kode | Nama Perusahaan | Tanggal Pencatatan | Saham | Papan Pencatatan | Tanggal Masuk | Tanggal Keluar | Kriteria | PPK | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | AALI | Astra Agro Lestari Tbk. | 1997-12-09 | 1.924.688.333 | Utama | NaT | NaT | NaN | No |
| 1 | ABBA | Mahaka Media Tbk. | 2002-04-03 | 3.935.892.857 | Pemantauan Khusus | 2024-05-31 | NaT | 1, 5 | Yes |
| 2 | ABDA | Asuransi Bina Dana Arta Tbk. | 1989-07-06 | 620.806.680 | Pemantauan Khusus | 2025-04-09 | NaT | 7 | Yes |
| 3 | ABMM | ABM Investama Tbk. | 2011-12-06 | 2.753.165.000 | Utama | NaT | NaT | NaN | No |
| 4 | ACES | Aspirasi Hidup Indonesia Tbk. | 2007-11-06 | 17.120.389.700 | Utama | NaT | NaT | NaN | No |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 950 | ASPI | Andalan Sakti Primaindo Tbk. | 2020-02-17 | 681.823.317 | Pemantauan Khusus | 2025-07-24 | 2025-08-04 | 10 | Yes |
| 951 | ESTA | Esta Multi Usaha Tbk. | 2020-03-09 | 2.425.354.179 | Pengembangan | 2025-04-30 | 2025-06-04 | 6 | Yes |
| 952 | BESS | Batulicin Nusantara Maritim Tb | 2020-03-09 | 3.440.455.528 | Pengembangan | 2025-03-10 | 2025-03-19 | 10 | Yes |
| 953 | AMAN | Makmur Berkah Amanda Tbk. | 2020-03-13 | 3.873.500.000 | Pengembangan | NaT | NaT | NaN | No |
| 954 | CARE | Metro Healthcare Indonesia Tbk | 2020-03-13 | 33.250.000.000 | Pengembangan | NaT | NaT | NaN | No |
955 rows × 9 columns
Kriteria Distress PPK dan Tambah Kolom Distress (Yes/No)
# Define the criteria values for distress
distress_criteria = ['2', '3', '5', '8', '9'] + ['1', '7'] + ['4', '6', '10', '11']
# Function to determine 'Distress' status
def is_distress(row):
if row['PPK'] == 'Yes':
kriteria_values = str(row['Kriteria']).split(', ')
if any(item in distress_criteria for item in kriteria_values):
return 'Yes'
return 'No'
# Apply the function to create the 'Distress' column
df_emiten_idx['Distress_PPK'] = df_emiten_idx.apply(is_distress, axis=1)
# Display the updated DataFrame
df_emiten_idx| Kode | Nama Perusahaan | Tanggal Pencatatan | Saham | Papan Pencatatan | Tanggal Masuk | Tanggal Keluar | Kriteria | PPK | Distress_PPK | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | AALI | Astra Agro Lestari Tbk. | 1997-12-09 | 1.924.688.333 | Utama | NaT | NaT | NaN | No | No |
| 1 | ABBA | Mahaka Media Tbk. | 2002-04-03 | 3.935.892.857 | Pemantauan Khusus | 2024-05-31 | NaT | 1, 5 | Yes | Yes |
| 2 | ABDA | Asuransi Bina Dana Arta Tbk. | 1989-07-06 | 620.806.680 | Pemantauan Khusus | 2025-04-09 | NaT | 7 | Yes | Yes |
| 3 | ABMM | ABM Investama Tbk. | 2011-12-06 | 2.753.165.000 | Utama | NaT | NaT | NaN | No | No |
| 4 | ACES | Aspirasi Hidup Indonesia Tbk. | 2007-11-06 | 17.120.389.700 | Utama | NaT | NaT | NaN | No | No |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 950 | ASPI | Andalan Sakti Primaindo Tbk. | 2020-02-17 | 681.823.317 | Pemantauan Khusus | 2025-07-24 | 2025-08-04 | 10 | Yes | Yes |
| 951 | ESTA | Esta Multi Usaha Tbk. | 2020-03-09 | 2.425.354.179 | Pengembangan | 2025-04-30 | 2025-06-04 | 6 | Yes | Yes |
| 952 | BESS | Batulicin Nusantara Maritim Tb | 2020-03-09 | 3.440.455.528 | Pengembangan | 2025-03-10 | 2025-03-19 | 10 | Yes | Yes |
| 953 | AMAN | Makmur Berkah Amanda Tbk. | 2020-03-13 | 3.873.500.000 | Pengembangan | NaT | NaT | NaN | No | No |
| 954 | CARE | Metro Healthcare Indonesia Tbk | 2020-03-13 | 33.250.000.000 | Pengembangan | NaT | NaT | NaN | No | No |
955 rows × 10 columns
Convert String Kriteria Jadi List
df_emiten_idx["Kriteria"] = (
df_emiten_idx["Kriteria"]
.astype("string")
.str.strip()
.str.split(r"\s*,\s*", regex=True)
)
df_emiten_idx["Kriteria"] = df_emiten_idx["Kriteria"].apply(
lambda x: [int(i) for i in x if str(i).strip() != ""] if isinstance(x, list) else pd.NA
)df_emiten_idx["Kriteria"].head()| Kriteria | |
|---|---|
| 0 | <NA> |
| 1 | [1, 5] |
| 2 | [7] |
| 3 | <NA> |
| 4 | <NA> |
df_emiten_idx["Kriteria"].iloc[1][1]5
type(df_emiten_idx["Kriteria"].iloc[1][1])int
Cek Null & Nol
# Check null
null_counts_emiten = df_emiten_idx.isnull().sum()
print("Jumlah nilai null per kolom di df_emiten:")
print(null_counts_emiten)
# Check for zero val
numerical_cols_emiten = df_emiten_idx.select_dtypes(include=['number']).columns
zero_counts_emiten = (df_emiten_idx[numerical_cols_emiten] == 0).sum()
print("\nJumlah nilai 0 per kolom numerik di df_emiten:")
print(zero_counts_emiten)Jumlah nilai null per kolom di df_emiten:
Kode 0
Nama Perusahaan 0
Tanggal Pencatatan 0
Saham 0
Papan Pencatatan 0
Tanggal Masuk 549
Tanggal Keluar 743
Kriteria 549
PPK 0
Distress_PPK 0
dtype: int64
Jumlah nilai 0 per kolom numerik di df_emiten:
Series([], dtype: float64)
# Print the result
print(f"Jumlah emiten yang distress berdasarkan PPK Kriteria tertentu:\n{len(df_emiten_idx[df_emiten_idx['Distress_PPK'] == 'Yes'])}")Jumlah emiten yang distress berdasarkan PPK Kriteria tertentu:
406
Hasil Dataset Daftar Emiten IDX
df_emiten_idx| Kode | Nama Perusahaan | Tanggal Pencatatan | Saham | Papan Pencatatan | Tanggal Masuk | Tanggal Keluar | Kriteria | PPK | Distress_PPK | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | AALI | Astra Agro Lestari Tbk. | 1997-12-09 | 1.924.688.333 | Utama | NaT | NaT | <NA> | No | No |
| 1 | ABBA | Mahaka Media Tbk. | 2002-04-03 | 3.935.892.857 | Pemantauan Khusus | 2024-05-31 | NaT | [1, 5] | Yes | Yes |
| 2 | ABDA | Asuransi Bina Dana Arta Tbk. | 1989-07-06 | 620.806.680 | Pemantauan Khusus | 2025-04-09 | NaT | [7] | Yes | Yes |
| 3 | ABMM | ABM Investama Tbk. | 2011-12-06 | 2.753.165.000 | Utama | NaT | NaT | <NA> | No | No |
| 4 | ACES | Aspirasi Hidup Indonesia Tbk. | 2007-11-06 | 17.120.389.700 | Utama | NaT | NaT | <NA> | No | No |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 950 | ASPI | Andalan Sakti Primaindo Tbk. | 2020-02-17 | 681.823.317 | Pemantauan Khusus | 2025-07-24 | 2025-08-04 | [10] | Yes | Yes |
| 951 | ESTA | Esta Multi Usaha Tbk. | 2020-03-09 | 2.425.354.179 | Pengembangan | 2025-04-30 | 2025-06-04 | [6] | Yes | Yes |
| 952 | BESS | Batulicin Nusantara Maritim Tb | 2020-03-09 | 3.440.455.528 | Pengembangan | 2025-03-10 | 2025-03-19 | [10] | Yes | Yes |
| 953 | AMAN | Makmur Berkah Amanda Tbk. | 2020-03-13 | 3.873.500.000 | Pengembangan | NaT | NaT | <NA> | No | No |
| 954 | CARE | Metro Healthcare Indonesia Tbk | 2020-03-13 | 33.250.000.000 | Pengembangan | NaT | NaT | <NA> | No | No |
955 rows × 10 columns
Visualisasi Data Bursa
counts = df_emiten_idx["Distress_PPK"].value_counts().reindex(["Yes", "No"], fill_value=0)
# pastikan counts urut Yes lalu No
counts = counts.reindex(["Yes", "No"], fill_value=0)
# warna
soft_red = "#F28B82"
soft_green = "#81C995"
colors = [soft_red, soft_green]
# label persen + jumlah
def autopct_with_count(values):
total = values.sum()
def _fmt(pct):
count = int(round(pct * total / 100.0))
return f"{pct:.1f}%\n({count})"
return _fmt
fig, ax = plt.subplots(figsize=(6, 6))
ax.pie(
counts.values,
labels=counts.index,
colors=colors,
autopct=autopct_with_count(counts),
startangle=90
)
ax.axis("equal")
ax.set_title("Proporsi & Jumlah Perusahaan Distress_PPK\nYes vs No")
plt.tight_layout()
plt.show()
df = df_emiten_idx.copy()
# status per perusahaan (unik per Kode)
status = (
df.groupby("Kode")
.agg(
pernah_masuk=("Tanggal Masuk", lambda s: s.notna().any()),
pernah_keluar=("Tanggal Keluar", lambda s: s.notna().any())
)
)
# ambil hanya perusahaan yg pernah masuk PPK
status = status[status["pernah_masuk"]]
jumlah_masuk_tidak_keluar = int((~status["pernah_keluar"]).sum())
jumlah_masuk_berhasil_keluar = int((status["pernah_keluar"]).sum())
counts = pd.Series(
{
"Masuk & tidak keluar": jumlah_masuk_tidak_keluar,
"Masuk & berhasil keluar": jumlah_masuk_berhasil_keluar
}
)
# warna
soft_red = "#F28B82"
soft_green = "#81C995"
colors = [soft_red, soft_green]
# bar chart
fig, ax = plt.subplots(figsize=(7, 5))
bars = ax.bar(counts.index, counts.values, color=colors)
ax.set_ylabel("Jumlah perusahaan")
ax.set_title("Jumlah Perusahaan yang Masuk PPK\nBelum Keluar vs Berhasil Keluar")
ax.set_ylim(0, max(counts.values) * 1.15 if max(counts.values) > 0 else 1)
# angka di atas batang
for b in bars:
h = b.get_height()
ax.text(b.get_x() + b.get_width()/2, h, f"{int(h)}", ha="center", va="bottom")
plt.tight_layout()
plt.show()
# hitung kemunculan tiap nilai unik (termasuk null)
counts = df_emiten_idx["Papan Pencatatan"].value_counts(dropna=False)
# ganti label null kalau ada
counts.index = counts.index.to_series().astype(str).replace({"nan": "Tidak diketahui"})
total = counts.sum()
pct = counts / total * 100
# label nilai, jumlah, persen
labels = [f"{k}\n{v} ({p:.1f}%)" for k, v, p in zip(counts.index, counts.values, pct.values)]
fig, ax = plt.subplots(figsize=(8, 8))
ax.pie(
counts.values,
labels=labels,
startangle=90
)
ax.axis("equal")
ax.set_title("Jumlah Tiap Jenis Papan")
plt.tight_layout()
plt.show()
df = df_emiten_idx.copy()
# ambil yang punya Kriteria
tmp = df[["Kode", "Kriteria"]].dropna(subset=["Kriteria"]).copy()
# explode list, 1 baris 1 kriteria
exp = tmp.explode("Kriteria")
# rapikan pastikan numerik
exp["Kriteria"] = pd.to_numeric(exp["Kriteria"], errors="coerce")
exp = exp.dropna(subset=["Kriteria"])
exp["Kriteria"] = exp["Kriteria"].astype(int)
# hitung PERUSAHAAN UNIK per kriteria (Kode, Kriteria) unik
unique_pairs = exp.drop_duplicates(subset=["Kode", "Kriteria"])
counts = unique_pairs["Kriteria"].value_counts().sort_index()
# label kriteria, jumlah, persen
total = counts.sum()
percent = counts / total * 100
labels = [f"Kriteria {k}\n{v} ({p:.1f}%)" for k, v, p in zip(counts.index, counts.values, percent.values)]
fig, ax = plt.subplots(figsize=(9, 9))
ax.pie(counts.values, labels=labels, startangle=90)
ax.axis("equal")
ax.set_title("Proporsi Perusahaan per Kriteria PPK")
plt.tight_layout()
plt.show()
df = df_emiten_idx.copy()
# ambil yang punya Kriteria (pernah masuk PPK)
tmp = df[["Kode", "Kriteria"]].dropna(subset=["Kriteria"]).copy()
# explode list, 1 baris 1 kriteria
exp = tmp.explode("Kriteria")
# pastikan numerik bersih
exp["Kriteria"] = pd.to_numeric(exp["Kriteria"], errors="coerce")
exp = exp.dropna(subset=["Kriteria"])
exp["Kriteria"] = exp["Kriteria"].astype(int)
# hitung PERUSAHAAN UNIK per kriteria
# agar 1 perusahaan tidak dihitung dobel untuk kriteria yang sama
unique_pairs = exp.drop_duplicates(subset=["Kode", "Kriteria"])
counts = unique_pairs["Kriteria"].value_counts().sort_index()
# kriteria yg tidak ada perusahaan (0) tetap ditampilkan
full_index = pd.Index(range(1, 12), name="Kriteria")
counts = counts.reindex(full_index, fill_value=0)
# plot bar chart
fig, ax = plt.subplots(figsize=(10, 5))
bars = ax.bar(counts.index.astype(str), counts.values)
ax.set_title("Jumlah Perusahaan per Kriteria PPK")
ax.set_xlabel("Kriteria")
ax.set_ylabel("Jumlah perusahaan")
ax.set_ylim(0, max(counts.values) * 1.15 if max(counts.values) > 0 else 1)
# jumlah
for b in bars:
h = b.get_height()
ax.text(b.get_x() + b.get_width()/2, h, f"{int(h)}", ha="center", va="bottom")
plt.tight_layout()
plt.show()
df = df_emiten_idx.copy()
# hitung jumlah perusahaan IPO per tahun (unik per Kode) dan tampilkan semua tahun termasuk yg 0
df["Tanggal Pencatatan"] = pd.to_datetime(df["Tanggal Pencatatan"], errors="coerce")
df["Tahun Pencatatan"] = df["Tanggal Pencatatan"].dt.year
counts_per_year = (
df.dropna(subset=["Tahun Pencatatan"])
.groupby("Tahun Pencatatan")["Kode"]
.nunique()
.sort_index()
)
min_year = int(df["Tahun Pencatatan"].min())
max_year = int(df["Tahun Pencatatan"].max())
all_years = pd.Index(range(min_year, max_year + 1), name="Tahun Pencatatan")
counts_per_year = counts_per_year.reindex(all_years, fill_value=0)
x = counts_per_year.index.to_numpy()
y = counts_per_year.values.astype(float)
# warna
soft_red = "#F28B82"
soft_green = "#81C995"
cmap = LinearSegmentedColormap.from_list("soft_red_to_green", [soft_red, soft_green])
pos = y[y > 0]
has_positive = len(pos) > 0
if has_positive:
norm = Normalize(vmin=pos.min(), vmax=pos.max())
def color_for_value(v):
if v == 0 or (not has_positive):
return (0, 0, 0, 1) # hitam kalau tidak ada yang IPO
return cmap(norm(v))
point_colors = [color_for_value(v) for v in y]
# garis gradasi pakai segments
segments = []
segment_colors = []
subdiv = 30 # jumlah segmen
for i in range(len(x) - 1):
x0, x1 = x[i], x[i + 1]
y0, y1 = y[i], y[i + 1]
xs = np.linspace(x0, x1, subdiv + 1)
ys = np.linspace(y0, y1, subdiv + 1)
for j in range(subdiv):
v_mid = (ys[j] + ys[j + 1]) / 2.0 # nilai untuk warna segmen kecil
segments.append([[xs[j], ys[j]], [xs[j + 1], ys[j + 1]]])
segment_colors.append(color_for_value(v_mid))
lc = LineCollection(segments, colors=segment_colors, linewidths=2)
# plot
fig, ax = plt.subplots(figsize=(14, 6))
ax.add_collection(lc) # garis gradasi
ax.scatter(x, y, s=120, c=point_colors, edgecolors="white", linewidths=1.2, zorder=3) # titik besar
ax.set_title("Jumlah Perusahaan Masuk Bursa per Tahun")
ax.set_ylabel("Jumlah perusahaan")
# tampilkan semua tahun vertikal di X
ax.set_xticks(x)
ax.set_xticklabels(x.astype(int), rotation=90, va="center", ha="center")
# tambah jarak
ax.tick_params(axis="x", pad=10) # antara tick label & garis X
ax.set_xlabel("Tahun", labelpad=25) # antara label "Tahun" & tick label
# batas & grid
ax.set_xlim(x.min() - 0.5, x.max() + 0.5)
ax.set_ylim(0, max(y) * 1.15 if max(y) > 0 else 1)
ax.grid(True, alpha=0.3)
# space bawah
plt.subplots_adjust(bottom=0.1)
plt.show()
Financial Data S&P Capital IQ
Load Financial Data
df_raw = pd.read_excel(
prefix_path + 'Data Dasar SPCIQ SIMPLIFIED (2).xlsx',
sheet_name="Sheet1",
header=[0, 1] # dua baris pertama sebagai header
)
# Beri nama level kolom
df_raw.columns = df_raw.columns.set_names(["variable", "time_flag"])
print("=== df_raw (awal) ===")
display(df_raw.head())
print("=== Contoh nilai time_flag sebelum normalisasi ===")
time_flags_raw = df_raw.columns.get_level_values("time_flag")
print(pd.unique(time_flags_raw.astype(str))[:20])=== df_raw (awal) ===
| variable | Entity Name | Entity ID | Exchange | exchange_ticker | Primary Industry | 1st Level Primary Industry | Country / Region Name | Address 1 | Web Address | Business Description | Company Type | Investor Type | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Company Type | Investor Type | Company Status | Ownership Structure | Activist Investor? Yes/No | Institution Coverage Level | Consolidation Status | Has Current Financials? Yes/No | Has Bank Regulatory Financials? Yes/No | Has Third Party Financials? Yes/No | Business Description | Long Business Description | Topic Tags | Crunchbase Categories | Year Established | Date Established MM/dd/yyyy | Year Incorporated | Date Incorporated MM/dd/yyyy | Month of Fiscal Year End | Headcount As Of MM/dd/yyyy | Industry Classification | Primary Industry | 1st Level Primary Industry | 2nd Level Primary Industry | 3rd Level Primary Industry | 4th Level Primary Industry | Industry Classification | Sector | Industry Group | Industry | Primary Industry | City | Parent Company Name | Parent Ticker | Parent Exchange: Ticker | Parent Percent Owned (%) | Parent City | Parent Industry | Ultimate Parent Company Name | Ultimate Parent Ticker | Ultimate Parent Exchange: Ticker | Ultimate Parent City | Ultimate Parent Industry | Ultimate Parent Country / Region | All Investors | Percent Owned - All Institutions (%) | Shares Owned - All Institutions (actual) | Market Value - All Institutions (Rp.B) | Percent Owned - Insiders (%) | Shares Owned - Insiders (actual) | Market Value - Insiders (Rp.B) | IPO Date MM/dd/yyyy | IPO Price (Rp.) | Location Type | Data Precision | Data Year | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Total Assets (Rp.M) | Total Liabilities (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.B) | Day Close Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | Current Ratio (x) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| time_flag | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | STATIC.1 | STATIC.1 | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC.1 | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC.1 | STATIC.1 | STATIC | STATIC | STATIC | STATIC.1 | STATIC | STATIC | STATIC | STATIC.2 | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024.1 | 2023.1 | 2022.1 | 2021.1 | 2020.1 | 2019.1 | 2018.1 | 2017.1 | 2016.1 | 2015.1 | 2014.1 | 2013.1 | 2012.1 | 2011.1 | 2010.1 | 2009.1 | 2008.1 | 2024.1 | 2023.1 | 2022.1 | 2021.1 | 2020.1 | 2019.1 | 2018.1 | 2017.1 | 2016.1 | 2015.1 | 2014.1 | 2013.1 | 2012.1 | 2011.1 | 2010.1 | 2009.1 | 2008.1 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024.1 | 2023.1 | 2022.1 | 2021.1 | 2020.1 | 2019.1 | 2018.1 | 2017.1 | 2016.1 | 2015.1 | 2014.1 | 2013.1 | 2012.1 | 2011.1 | 2010.1 | 2009.1 | 2008.1 |
| 0 | Perusahaan Perseroan (Persero) PT Telekomunika... | 4210975 | IDX | IDX:TLKM | Integrated Telecommunication Services | Technology, Media & Telecommunications | Indonesia | Jl. Japati No. 1 | www.telkom.co.id | Perusahaan Perseroan (Persero) PT Telekomunika... | Public Company | NaN | 137185000.000000 | 130480000.000000 | 125742000.000000 | 131785000.000000 | 126054000.000000 | 103958000.000000 | 88893000.000000 | 86354000.000000 | 74067000.000000 | 72745000.000000 | 55830000.000000 | 44391000.000000 | 50527000.000000 | 42073000.000000 | 44086000.000000 | 48228553.000000 | 47258399.000000 | 299675000.000000 | 287042000.000000 | 274817000.000000 | 277184000.000000 | 246943000.000000 | 221208000.000000 | 206196000.000000 | 198484000.000000 | 179611000.000000 | 166173000.000000 | 141822000.000000 | 127951000.000000 | 111369000.000000 | 103054000.000000 | 100501000.000000 | 97814160.000000 | 91256250.000000 | Public Company | NaN | Operating Subsidiary | NaN | No | NaN | NaN | Yes | No | No | Perusahaan Perseroan (Persero) PT Telekomunika... | Perusahaan Perseroan (Persero) PT Telekomunika... | Network Backbone; Optical Infrastructure; Clou... | Digital Media, Information Technology, Telecom... | 1884.000000 | 27/03/1884 01:00:00 | NaN | NaT | Dec | 2025-08-01 | Integrated Telecommunication Services | Integrated Telecommunication Services | Technology, Media & Telecommunications | Telecommunication Services | Integrated Telecommunication Services | NaN | Communication Services; Telecommunication Serv... | Communication Services | Telecommunication Services | Diversified Telecommunication Services | Integrated Telecommunication Services | Bandung | PT Danantara Asset Management (Persero) | NaN | NaN | 0.520900 | Jakarta | Construction and Engineering | Indonesia | NaN | NaN | Jakarta | Diversified Support Services | Indonesia | Indonesia (Prior Subsidiary or Operating Unit,... | 21.060000 | 20859696180.000000 | 77180875.866000 | 0.090000 | 85029954.000000 | 314610.829800 | NaT | NaN | NaN | NaN | NaN | 0.822000 | 0.777000 | 0.784000 | 0.886000 | 0.673000 | 0.715000 | 0.935000 | 1.048000 | 1.200000 | 1.353000 | 1.061000 | 1.163000 | 1.160000 | 0.958000 | 0.915000 | 0.602000 | 0.542000 | 0.692000 | 0.643000 | 0.630000 | 0.730000 | 0.510000 | 0.543000 | 0.677000 | 0.818000 | 0.993000 | 1.031000 | 0.795000 | 0.974000 | 0.965000 | 0.703000 | 0.687000 | 0.474000 | 0.420000 | -13687000.000000 | -15955000.000000 | -15162000.000000 | -7854000.000000 | -22590000.000000 | -16647000.000000 | -2993000.000000 | 2185000.000000 | 7939000.000000 | 12499000.000000 | 1976000.000000 | 4638000.000000 | 3866000.000000 | -931000.000000 | -1744000.000000 | -10707101.000000 | -12375841.000000 | 63080000.000000 | 55613000.000000 | 55073000.000000 | 61277000.000000 | 46503000.000000 | 41722000.000000 | 43268000.000000 | 47561000.000000 | 47701000.000000 | 47912000.000000 | 34294000.000000 | 33075000.000000 | 27973000.000000 | 21258000.000000 | 18729000.000000 | 16186024.000000 | 14622310.000000 | 76767000.000000 | 71568000.000000 | 70235000.000000 | 69131000.000000 | 69093000.000000 | 58369000.000000 | 46261000.000000 | 45376000.000000 | 39762000.000000 | 35413000.000000 | 32318000.000000 | 28437000.000000 | 24107000.000000 | 22189000.000000 | 20473000.000000 | 26893125.000000 | 26998151.000000 | 1096000.000000 | 997000.000000 | 1144000.000000 | 779000.000000 | 983000.000000 | 585000.000000 | 717000.000000 | 631000.000000 | 584000.000000 | 528000.000000 | 474000.000000 | 509000.000000 | 579000.000000 | 758000.000000 | 515000.000000 | 435244.000000 | 511950.000000 | 6655000.000000 | 6520000.000000 | 7124000.000000 | 5108000.000000 | 4993000.000000 | 5471000.000000 | 5218000.000000 | 1576000.000000 | 1463000.000000 | 2147000.000000 | 641000.000000 | 1186000.000000 | 849000.000000 | 731000.000000 | 883000.000000 | 722850.000000 | 1875773.000000 | 299675000.000000 | 287042000.000000 | 274817000.000000 | 277184000.000000 | 246943000.000000 | 221208000.000000 | 206196000.000000 | 198484000.000000 | 179611000.000000 | 166173000.000000 | 141822000.000000 | 127951000.000000 | 111369000.000000 | 103054000.000000 | 100501000.000000 | 97814160.000000 | 91256250.000000 | 137185000.000000 | 130480000.000000 | 125742000.000000 | 131785000.000000 | 126054000.000000 | 103958000.000000 | 88893000.000000 | 86354000.000000 | 74067000.000000 | 72745000.000000 | 55830000.000000 | 50527000.000000 | 44391000.000000 | 42073000.000000 | 44086000.000000 | 48228553.000000 | 47258399.000000 | 25518000.000000 | 27773000.000000 | 27331000.000000 | 36319000.000000 | 30561000.000000 | 32293000.000000 | 31410000.000000 | 24964000.000000 | 23015000.000000 | 26229000.000000 | 11525000.000000 | 10410000.000000 | 11803000.000000 | 12644000.000000 | 16246000.000000 | 14249575.000000 | 11444575.000000 | 11525000.000000 | 9650000.000000 | 8191000.000000 | 6682000.000000 | 9934000.000000 | 8705000.000000 | 4043000.000000 | 2289000.000000 | 911000.000000 | 602000.000000 | 1810000.000000 | 432000.000000 | 37000.000000 | 100000.000000 | 56000.000000 | 43850.000000 | 46000.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 76868000.000000 | 68124000.000000 | 62853000.000000 | 69078000.000000 | 65462000.000000 | 52084000.000000 | 44087000.000000 | 35472000.000000 | 31799000.000000 | 34612000.000000 | 23452000.000000 | 20256000.000000 | 19275000.000000 | 17871000.000000 | 21910000.000000 | 21329926.000000 | 17584731.000000 | 162490000.000000 | 156562000.000000 | 149075000.000000 | 145399000.000000 | 120889000.000000 | 117250000.000000 | 117303000.000000 | 112130000.000000 | 105544000.000000 | 93428000.000000 | 85992000.000000 | 77424000.000000 | 66978000.000000 | 60981000.000000 | 56415000.000000 | 49585607.000000 | 43997851.000000 | 207476000.000000 | 203339000.000000 | 193022000.000000 | 183495000.000000 | 179489000.000000 | 156973000.000000 | 143248000.000000 | 130171000.000000 | 114498000.000000 | 103700000.000000 | 94809000.000000 | 86761000.000000 | 77047000.000000 | 74897000.000000 | 75832000.000000 | 76419897.000000 | 71066244.000000 | 60010000.000000 | 58238000.000000 | 54658000.000000 | 53530000.000000 | 50675000.000000 | 60315000.000000 | 61252000.000000 | 53119000.000000 | 47465000.000000 | 42893000.000000 | 35651000.000000 | 32803000.000000 | 29602000.000000 | 27992000.000000 | 25400000.000000 | 23565712.000000 | 22159917.000000 | 149967000.000000 | 149216000.000000 | 147306000.000000 | 143210000.000000 | 136462000.000000 | 135567000.000000 | 130784000.000000 | 128256000.000000 | 116333000.000000 | 102470000.000000 | 89696000.000000 | 82967000.000000 | 77143000.000000 | 71253000.000000 | 69177000.000000 | 67677518.000000 | 64166429.000000 | 43129000.000000 | 44885000.000000 | 45202000.000000 | 43318000.000000 | 43332000.000000 | 42283000.000000 | 38624000.000000 | 43727000.000000 | 40000000.000000 | 33694000.000000 | 29813000.000000 | 28589000.000000 | 26667000.000000 | 23248000.000000 | 22894000.000000 | 23831275.000000 | 23225173.000000 | 67694000.000000 | 70054000.000000 | 71042000.000000 | 75032000.000000 | 65522000.000000 | 64296000.000000 | 58804000.000000 | 63136000.000000 | 58532000.000000 | 52228000.000000 | 46139000.000000 | 43773000.000000 | 40876000.000000 | 37548000.000000 | 37506000.000000 | 37787661.000000 | 35538389.000000 | 43129000.000000 | 44885000.000000 | 45202000.000000 | 43318000.000000 | 43332000.000000 | 42283000.000000 | 38624000.000000 | 43727000.000000 | 40000000.000000 | 33694000.000000 | 29813000.000000 | 28589000.000000 | 26667000.000000 | 23248000.000000 | 22894000.000000 | 23831275.000000 | 23225173.000000 | 30743000.000000 | 32208000.000000 | 27720000.000000 | 34099000.000000 | 29563000.000000 | 27592000.000000 | 26979000.000000 | 32701000.000000 | 29172000.000000 | 23317000.000000 | 21274000.000000 | 20290000.000000 | 18362000.000000 | 15470000.000000 | 15870000.000000 | 16042898.000000 | 14725429.000000 | 61600000.000000 | 60581000.000000 | 73354000.000000 | 68353000.000000 | 65317000.000000 | 54949000.000000 | 45671000.000000 | 49405000.000000 | 47231000.000000 | 43669000.000000 | 37736000.000000 | 36574000.000000 | 27941000.000000 | 30553000.000000 | 27759000.000000 | 29811604.000000 | 24553925.000000 | 4898000.000000 | -2940000.000000 | -6364000.000000 | 17722000.000000 | 2347000.000000 | 803000.000000 | -7706000.000000 | -4622000.000000 | 1650000.000000 | 10445000.000000 | 2976000.000000 | 1578000.000000 | 3484000.000000 | 514000.000000 | 1315000.000000 | 915515.000000 | -3250846.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 98198216600.000000 | 98175853600.000000 | 97100853600.000000 | 95745344100.000000 | 96931696596.000000 | 98347123896.000000 | 98347123896.000000 | 98347123896.000000 | 268458.606986 | 391295.755570 | 371483.312250 | 400211.355064 | 327895.936946 | 393276.999902 | 371483.312250 | 439836.241704 | 394267.622068 | 304836.025428 | 281273.820564 | 208766.835240 | 173339.797814 | 137673.219768 | 156371.926995 | 185876.064163 | 135790.870326 | 2710.000000 | 3950.000000 | 3750.000000 | 4040.000000 | 3310.000000 | 3970.000000 | 3750.000000 | 4440.000000 | 3980.000000 | 3105.000000 | 2865.000000 | 2150.000000 | 1810.000000 | 1410.000000 | 1590.000000 | 1890.000000 | 1380.000000 | 239358000.000000 | 224686000.000000 | 211928000.000000 | 214477000.000000 | 186351000.000000 | 169334000.000000 | 161390000.000000 | 147602000.000000 | 137343000.000000 | 128040000.000000 | 109444000.000000 | 97680000.000000 | 86253000.000000 | 78852000.000000 | 78325000.000000 | 70915533.000000 | 61582582.000000 | 35027000.000000 | 30430000.000000 | 32883000.000000 | 38740000.000000 | 21818000.000000 | 18620000.000000 | 18590000.000000 | 26638000.000000 | 31001000.000000 | 28566000.000000 | 18036000.000000 | 21256000.000000 | 17428000.000000 | 9995000.000000 | 9490000.000000 | 8164967.000000 | 7156989.000000 | 9442000.000000 | 8731000.000000 | 8302000.000000 | 7506000.000000 | 6846000.000000 | 6446000.000000 | 5032000.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 0.822000 | 0.777000 | 0.784000 | 0.886000 | 0.673000 | 0.715000 | 0.935000 | 1.048000 | 1.200000 | 1.353000 | 1.061000 | 1.163000 | 1.160000 | 0.958000 | 0.915000 | 0.602000 | 0.542000 |
| 1 | PT Abadi Nusantara Hijau Investama Tbk (IDX:PACK) | 109420637 | IDX | IDX:PACK | Packaging and Materials: Paper and Plastic | Materials | Indonesia | Jl. Jababeka 2 Block C/11-D | www.flexypack.com | PT Abadi Nusantara Hijau Investama Tbk provide... | Public Company | NaN | 9131.515000 | 22395.996000 | 37928.982000 | 33821.027000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 66918.151000 | 77129.464000 | 43686.326000 | 36584.088000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | Public Company | NaN | Operating | NaN | No | NaN | NaN | Yes | No | No | PT Abadi Nusantara Hijau Investama Tbk provide... | PT Abadi Nusantara Hijau Investama Tbk provide... | Packaging Services | NaN | 2019.000000 | 2019-11-18 00:00:00 | NaN | NaT | Dec | NaT | Packaging and Materials: Paper and Plastic | Packaging and Materials: Paper and Plastic | Materials | Packaging and Materials: Paper and Plastic | NaN | NaN | Materials; Materials; Containers and Packaging... | Materials | Materials | Containers and Packaging | Paper and Plastic Packaging Products and Mater... | Bekasi | NaN | NaN | NaN | NA% | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | PT Benson Kapital Indonesia (Current Investmen... | 23.240000 | 371188000.000000 | 965088.800000 | 0.000000 | 0.000000 | NaN | 2023-02-06 | 162.000000 | NaN | NaN | NaN | 3.171000 | 1.849000 | 0.397000 | 0.279000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2.532000 | 0.891000 | 0.259000 | 0.116000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 18470.909000 | 16160.048000 | -15748.231000 | -13908.977000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 26979.387000 | 35187.194000 | 10379.496000 | 5381.327000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 8508.479000 | 19027.146000 | 26127.727000 | 19290.304000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 5217.791000 | 18152.157000 | 2309.696000 | 1102.312000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 28.664000 | 14.360000 | 4.889000 | 16.514000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 66918.151000 | 77129.464000 | 43686.326000 | 36584.088000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 9131.515000 | 22395.996000 | 37928.982000 | 33821.027000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2835.577000 | 9595.659000 | 15429.582000 | 17861.356000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 57786.636000 | 54733.468000 | 5757.344000 | 2763.062000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 29813.794000 | 27877.647000 | 26738.757000 | 25626.326000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 40843.331000 | 37002.120000 | 32303.664000 | 30325.181000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 54702.368000 | 52931.736000 | 44150.214000 | 36215.903000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2629.162000 | 5081.433000 | 3948.041000 | -4738.235000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 4465.941000 | 6785.251000 | 5446.039000 | -3352.747000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2629.162000 | 5081.433000 | 3948.041000 | -4738.235000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 899.758000 | 2288.792000 | 2052.793000 | -4690.516000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 8343.775000 | -26767.823000 | 7568.441000 | 1967.910000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | -2271.124000 | 5946.648000 | 539.116000 | -32.844000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1543043824.000000 | 1537579370.000000 | 1229546000.000000 | 571400000.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 948.817070 | 55.351773 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 615.000000 | 36.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 60622.214000 | 64329.128000 | 21186.926000 | 20624.418000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 4249.362000 | 6520.486000 | 573.838000 | 34.722000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 10041.786000 | 9556.735000 | 5626.787000 | 4095.608000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 3.171000 | 1.849000 | 0.397000 | 0.279000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 2 | PT ABM Investama Tbk (IDX:ABMM) | 4980353 | IDX | IDX:ABMM | Coal and Consumable Fuels | Energy and Utilities | Indonesia | Tiara Marga Trakindo I building | www.abm-investama.com | PT ABM Investama Tbk, together with its subsid... | Public Company | NaN | 20093470.573188 | 21545086.944192 | 21197974.640881 | 9685324.903374 | 9268917.351480 | 8427827.077252 | 8713242.360857 | 11934035.187236 | 12314807.459430 | 14025286.606475 | 11507764.542355 | 8840383.482359 | 10840297.762755 | 7019323.859661 | 3763830.000000 | 3099452.000000 | 4360950.000000 | 33732579.888996 | 33243187.213530 | 30787462.520356 | 14769924.153273 | 11521759.429112 | 11820817.650070 | 12287671.907708 | 14134486.114136 | 14460055.871406 | 16420535.888287 | 14028411.858435 | 14757757.263945 | 12175363.933647 | 10077243.517333 | 4890266.000000 | 4199977.000000 | 3549129.000000 | Public Company | NaN | Operating Subsidiary | NaN | No | NaN | NaN | Yes | No | No | PT ABM Investama Tbk, together with its subsid... | PT ABM Investama Tbk operating in the sectors ... | Clean Energy; Freight Service; Marine Transpor... | Energy, Precious Metals | 2006.000000 | 2006-06-01 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Coal and Consumable Fuels | Coal and Consumable Fuels | Energy and Utilities | Oil, Gas and Coal | Coal and Consumable Fuels | NaN | Energy; Energy; Oil, Gas and Consumable Fuels;... | Energy | Energy | Oil, Gas and Consumable Fuels | Coal and Consumable Fuels | Jakarta | PT Tiara Marga Trakindo | NaN | NaN | 0.535500 | Jakarta | Industrial Conglomerates | PT Tiara Marga Trakindo | NaN | NaN | Jakarta | Industrial Conglomerates | Indonesia | PT Tiara Marga Trakindo (Current Subsidiary or... | 0.830000 | 22926501.000000 | 68779.503000 | 7.340000 | 202136500.000000 | 606409.500000 | 2011-12-05 | 3750.000000 | NaN | NaN | NaN | 1.067000 | 0.986000 | 1.145000 | 1.652000 | 1.196000 | 1.204000 | 1.460000 | 1.597000 | 0.792000 | 0.883000 | 0.982000 | 0.996000 | 1.226000 | 1.362000 | 0.893000 | 1.113000 | 0.592000 | 0.905000 | 0.844000 | 0.792000 | 1.341000 | 0.969000 | 0.992000 | 1.275000 | 1.328000 | 0.653000 | 0.751000 | 0.820000 | 0.813000 | 1.007000 | 1.088000 | 0.700000 | 0.879000 | 0.430000 | 540116.972988 | -138088.846896 | 1393425.586718 | 2785291.548858 | 716862.070496 | 610980.039406 | 1477581.268563 | 1720113.072412 | -1048360.536690 | -622368.364089 | -75076.681040 | -19818.463770 | 820288.622790 | 1134634.650895 | -252891.000000 | 203475.000000 | -953832.000000 | 8603535.372060 | 9598638.433986 | 10973792.911797 | 7055017.392177 | 4375629.087504 | 3608327.316374 | 4690972.433270 | 4601979.099836 | 4002183.122758 | 4692284.395771 | 4203180.206570 | 4670518.461795 | 4449825.549909 | 4270489.900468 | 2106194.000000 | 2001334.000000 | 1384649.000000 | 8063418.399072 | 9736727.280882 | 9580367.325079 | 4269725.843319 | 3658767.017008 | 2997347.276968 | 3213391.164707 | 2881866.027424 | 5050543.659448 | 5314652.759860 | 4278256.887610 | 4690336.925565 | 3629536.927119 | 3135855.249573 | 2359085.000000 | 1797859.000000 | 2338481.000000 | 976088.594994 | 821228.155566 | 864077.600244 | 565420.432554 | 525300.327976 | 411458.649184 | 441376.280596 | 406041.471732 | 345572.528640 | 293631.497503 | 310286.074145 | 499804.401930 | 442934.444820 | 414473.810135 | 301356.000000 | 233936.000000 | 241667.000000 | 77287.962918 | 47921.324472 | 68165.694646 | 145468.209915 | 43412.280696 | 124924.565348 | 78087.463339 | 208496.797292 | 304715.857400 | 359407.215314 | 308703.011060 | 234000.429255 | 171860.938376 | 26318.699181 | 11526.000000 | NaN | NaN | 33732579.888996 | 33243187.213530 | 30787462.520356 | 14769924.153273 | 11521759.429112 | 11820817.650070 | 12287671.907708 | 14134486.114136 | 14460055.871406 | 16420535.888287 | 14028411.858435 | 14757757.263945 | 12175363.933647 | 10077243.517333 | 4890266.000000 | 4199977.000000 | 3549129.000000 | 20093470.573188 | 21545086.944192 | 21197974.640881 | 9685324.903374 | 9268917.351480 | 8427827.077252 | 8713242.360857 | 11934035.187236 | 12314807.459430 | 14025286.606475 | 11507764.542355 | 10840297.762755 | 8840383.482359 | 7019323.859661 | 3763830.000000 | 3099452.000000 | 4360950.000000 | 10650442.128438 | 10137708.047964 | 10408734.918322 | 4363219.444311 | 4815187.839656 | 4751102.996552 | 4920968.773924 | 6569397.072784 | 4716889.153288 | 5460877.970722 | 5966403.514665 | 4363521.366120 | 3501077.172306 | 2832092.967225 | 841659.000000 | NaN | NaN | 1725644.443698 | 1070033.159496 | 932422.887775 | 66960.900000 | 557120.000000 | 484330.000000 | NaN | NaN | 579496.717636 | 675321.089765 | 323324.159965 | 740148.951675 | 520493.454485 | 494015.259308 | 292208.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 15716952.113922 | 16033280.386032 | 14577725.216805 | 5488144.613091 | 5924993.624080 | 5400701.282240 | 5215817.182160 | 7009762.688972 | 7134999.566212 | 8304395.601394 | 8039725.363235 | 7610896.646745 | 6493573.658040 | 4929981.462200 | 1924066.000000 | 0.000000 | 0.000000 | 13639109.315808 | 11698100.269338 | 9589487.879475 | 5084599.249899 | 2252842.077632 | 3392990.572818 | 3574429.546851 | 2200450.926900 | 2145248.411976 | 2395249.281812 | 2520647.316080 | 3917459.501190 | 3334980.451288 | 3057919.657672 | 1126436.000000 | 1100525.000000 | -811821.000000 | 13588532.603310 | 13083527.221044 | 10798972.554367 | 6404601.223980 | 5698003.631872 | 5952020.777318 | 6748196.663272 | 8540482.052180 | 9309009.686380 | 10701896.185132 | 8731295.576750 | 8657844.015315 | 6588204.391554 | 4881836.576369 | 2172994.000000 | 1532725.000000 | 1469802.000000 | 16940553.969294 | 16775091.191981 | 13717060.583375 | 9379940.152987 | 7360843.103808 | 6853297.177677 | 8527758.336357 | 7217935.093074 | 6164493.688407 | 7070612.704500 | 6865298.865170 | 6504601.420018 | 6719153.515715 | 5237512.675191 | 3789687.000000 | 3455803.000000 | 3114382.000000 | 19019676.227701 | 22748651.299773 | 21468049.600463 | 14622288.077965 | 8840833.965492 | 8378371.179746 | 11008635.659721 | 9244047.765335 | 7868310.286515 | 8775353.280985 | 8599050.952989 | 8115915.925543 | 8311255.281959 | 6607997.885733 | 4486419.000000 | 3926320.000000 | 3235172.000000 | 1405297.314532 | 4260726.217364 | 6039382.223438 | 4531608.021944 | 359223.936602 | 809280.825757 | 2132909.657311 | 763888.620366 | 695084.433659 | 85615.766681 | -781424.815807 | 327174.492051 | 466344.893687 | 706361.557448 | 271266.000000 | 491429.000000 | -469841.000000 | 3736427.208202 | 6016608.771238 | 7545384.213516 | 5399394.243944 | 1591016.404358 | 1645292.178698 | 2653600.347789 | 1987378.442476 | 2021789.251641 | 1549664.004501 | 1237520.491127 | 1299731.078470 | 1614985.150831 | 1250603.533610 | 705326.000000 | NaN | NaN | 1405297.314532 | 4260726.217364 | 6039382.223438 | 4531608.021944 | 359223.936602 | 809280.825757 | 2132909.657311 | 763888.620366 | 695084.433659 | 85615.766681 | -781424.815807 | 327174.492051 | 466344.893687 | 706361.557448 | 271266.000000 | 491429.000000 | -469841.000000 | 2208286.963756 | 4809124.839499 | 5077732.593725 | 2664181.082945 | -550213.689470 | 55073.183258 | 957348.122798 | 50838.656519 | 91939.054880 | -607731.656746 | -1373861.491832 | 21660.865098 | 116452.543303 | 484431.520834 | 127376.000000 | -11346.000000 | -3913.000000 | 5673781.353325 | 5901279.191712 | 6262674.404548 | 5216623.032704 | 1929513.838089 | 850217.381364 | 1934408.478086 | 2018788.409131 | 1602614.652688 | 2292600.227669 | 1330187.486686 | 1715558.666979 | 1249385.895957 | 53032.416782 | 612476.000000 | 678998.000000 | -4564.000000 | -289365.113950 | -502652.039715 | -232487.728337 | 1831719.103847 | 106265.604590 | -595842.773237 | 47878.928040 | 789290.497254 | -408173.515297 | 107928.957648 | 169184.976267 | -192036.292693 | -708061.937615 | 1190873.399695 | -181536.000000 | 455151.000000 | 7861.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 825760000.000000 | 826000000.000000 | NaN | 9746.204100 | 9360.761000 | 9030.381200 | 3909.494300 | 2092.405400 | 4212.342450 | 6249.684550 | 6332.279500 | 5588.924950 | 7984.178500 | 8397.153250 | 8259.495000 | 8259.495000 | 10530.856125 | NaN | NaN | NaN | 3540.000000 | 3400.000000 | 3280.000000 | 1420.000000 | 760.000000 | 1530.000000 | 2270.000000 | 2300.000000 | 2030.000000 | 2900.000000 | 3050.000000 | 3000.000000 | 3000.000000 | 3825.000000 | NaN | NaN | NaN | 29356061.429730 | 27731380.655370 | 24167213.096280 | 10572743.862990 | 8177835.701712 | 8793691.855058 | 8790246.729011 | 9210213.615872 | 9280247.978188 | 10699644.883206 | 10560372.679315 | 11528356.147935 | 9828554.109328 | 7987901.119872 | 3050502.000000 | 1100525.000000 | -811821.000000 | 2741781.997248 | 2907757.321224 | 3441719.228344 | 3380709.965967 | 1522129.237896 | 1410416.880994 | 2265170.861332 | 1907200.309552 | 1242565.677740 | 1930012.824268 | 1526592.624775 | 1256331.810840 | 1233004.384532 | 1669222.334305 | 433039.000000 | 614575.000000 | 159424.000000 | 241953.535626 | 216885.738972 | 251403.717345 | 223331.241996 | 236740.734304 | 240865.597962 | 45742.429116 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.067000 | 0.986000 | 1.145000 | 1.652000 | 1.196000 | 1.204000 | 1.460000 | 1.597000 | 0.792000 | 0.883000 | 0.982000 | 0.996000 | 1.226000 | 1.362000 | 0.893000 | 1.113000 | 0.592000 |
| 3 | PT Ace Oldfields Tbk (IDX:KUAS) | 7649048 | IDX | IDX:KUAS | Building Products | Industrials | Indonesia | Jl. Raya Cileungsi Jonggol | www.aceoldfields.com | PT Ace Oldfields Tbk manufactures and sells pa... | Public Company | NaN | 115267.049000 | 107904.478000 | 120390.576000 | 122407.148000 | 155435.120000 | 154466.505000 | 161107.849000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 278143.466000 | 264359.356000 | 269542.435000 | 262419.786000 | 206482.511000 | 199903.930000 | 203429.610000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | Public Company | NaN | Operating | NaN | No | NaN | NaN | Yes | No | No | PT Ace Oldfields Tbk manufactures and sells pa... | NaN | NaN | NaN | 1989.000000 | 1989-09-18 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Building Products; Hardware Tools and Equipmen... | Building Products | Industrials | Capital Goods | Building Products | NaN | Industrials; Capital Goods; Building Products;... | Industrials | Capital Goods | Building Products | Building Products | Bogor | NaN | NaN | NaN | NA% | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | Oldfields Holdings Limited (Prior Investment, ... | NaN | NaN | NaN | 0.870000 | 11225400.000000 | 763.327200 | 2021-10-25 | 195.000000 | NaN | NaN | NaN | 1.997000 | 1.974000 | 1.828000 | 1.814000 | 1.308000 | 1.309000 | 1.310000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.172000 | 1.072000 | 1.033000 | 1.210000 | 0.699000 | 0.537000 | 0.544000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 86839.444000 | 78355.409000 | 74169.643000 | 72372.271000 | 23870.410000 | 22439.724000 | 23621.044000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 173948.142000 | 158762.285000 | 163700.210000 | 161289.971000 | 101493.623000 | 94943.288000 | 99749.278000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 87108.698000 | 80406.876000 | 89530.566000 | 88917.700000 | 77623.213000 | 72503.564000 | 76128.234000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 71327.348000 | 71649.956000 | 70940.942000 | 53641.362000 | 47231.311000 | 56009.830000 | 58268.430000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 147.189000 | 224.208000 | 244.110000 | NaN | NaN | NaN | 35.264000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 278143.466000 | 264359.356000 | 269542.435000 | 262419.786000 | 206482.511000 | 199903.930000 | 203429.610000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 115267.049000 | 107904.478000 | 120390.576000 | 122407.148000 | 155435.120000 | 154466.505000 | 161107.849000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1634.488000 | 1073.554000 | 1449.214000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 62574.148000 | 60824.332000 | 68829.023000 | 70330.607000 | 57947.634000 | 58677.517000 | 58922.593000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 89651.801000 | 87684.384000 | 99772.844000 | 100011.914000 | 133144.255000 | 138006.077000 | 141918.039000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 162876.417000 | 156454.878000 | 149151.859000 | 140012.638000 | 51047.391000 | 45437.426000 | 42321.761000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 100287.643000 | 101849.807000 | 102292.856000 | 97649.202000 | 101747.189000 | 102175.913000 | 101709.941000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 130570.138000 | 114707.655000 | 112090.876000 | 106484.602000 | 103949.885000 | 107761.935000 | 113631.570000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 184692.659000 | 161660.709000 | 153828.141000 | 139351.625000 | 131083.726000 | 130073.784000 | 136605.549000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 20523.971000 | 18341.355000 | 15142.784000 | 15351.764000 | 12508.497000 | 9495.797000 | 9791.271000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 24221.391000 | 22206.902000 | 18812.802000 | 18288.391000 | 14108.647000 | 10873.891000 | 11133.338000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 20523.971000 | 18341.355000 | 15142.784000 | 15351.764000 | 12508.497000 | 9495.797000 | 9791.271000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 11300.748000 | 9505.923000 | 7864.938000 | 6178.641000 | 5750.754000 | 3591.997000 | 2840.922000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 5509.571000 | 6666.933000 | -14257.143000 | -4560.385000 | 11269.340000 | 7629.488000 | 5507.511000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1390.124000 | -9025.851000 | -22833.804000 | 46687.897000 | 4214.702000 | 449.762000 | 1393.988000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1292808150.000000 | 1292808150.000000 | 1292808150.000000 | 1292570540.000000 | 630000000.000000 | 902570540.000000 | 902570540.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 67.226024 | 64.640407 | 71.091653 | 93.065079 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 52.000000 | 50.000000 | 55.000000 | 72.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 252528.218000 | 244139.262000 | 248924.703000 | 240024.552000 | 184191.645000 | 183443.502000 | 184239.800000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 26195.870000 | 24805.746000 | 33831.597000 | 56665.401000 | 9977.504000 | 5762.802000 | 5313.040000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.997000 | 1.974000 | 1.828000 | 1.814000 | 1.308000 | 1.309000 | 1.310000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 4 | PT Acset Indonusa Tbk (IDX:ACST) | 4990054 | IDX | IDX:ACST | Construction and Engineering | Industrials | Indonesia | ACSET Building | www.acset.co | PT Acset Indonusa Tbk provides construction an... | Public Company | NaN | 2953727.000000 | 2212725.000000 | 1440027.000000 | 1362982.000000 | 2731074.000000 | 10160043.000000 | 7509598.000000 | 3869352.000000 | 1201946.000000 | 1264639.000000 | 831601.000000 | 536559.738000 | 737915.640000 | 193550.159000 | 96961.292000 | NaN | NaN | 2812734.000000 | 2608782.000000 | 2111024.000000 | 2478713.000000 | 3055106.000000 | 10446519.000000 | 8936391.000000 | 5306479.000000 | 2503171.000000 | 1929498.000000 | 1473649.000000 | 1298358.203000 | 754771.051000 | 359091.317000 | 226443.835000 | NaN | NaN | Public Company | NaN | Operating Subsidiary | NaN | No | NaN | NaN | Yes | No | No | PT Acset Indonusa Tbk provides construction an... | PT Acset Indonusa Tbk, an integrated construct... | NaN | Civil Engineering, Construction | 1995.000000 | 1995-01-10 00:00:00 | 1995.000000 | 1995-01-10 | Dec | 2025-09-01 | Construction and Engineering; Commercial Const... | Construction and Engineering | Industrials | Capital Goods | Construction and Engineering | NaN | Industrials; Capital Goods; Construction and E... | Industrials | Capital Goods | Construction and Engineering | Construction and Engineering | Jakarta | PT Karya Supra Perkasa | NaN | NaN | 0.501000 | NaN | Unclassified | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Karya Supra Perkasa (Current Subsidiary or ... | 0.110000 | 20245000.000000 | 2753.320000 | NaN | NaN | NaN | 2013-06-20 | 2500.000000 | NaN | NaN | NaN | 0.856000 | 0.999000 | 1.150000 | 1.403000 | 0.844000 | 0.946000 | 1.097000 | 1.273000 | 1.796000 | 1.326000 | 1.563000 | 1.484000 | 1.151000 | 1.371000 | 1.504000 | NaN | NaN | 0.750000 | 0.870000 | 1.043000 | 1.141000 | 0.623000 | 0.826000 | 0.794000 | 0.937000 | 1.298000 | 0.897000 | 0.948000 | 0.922000 | 0.750000 | 0.948000 | 0.935000 | NaN | NaN | -420134.000000 | -3142.000000 | 209226.000000 | 519658.000000 | -409901.000000 | -538088.000000 | 717200.000000 | 1010675.000000 | 927046.000000 | 391523.000000 | 437650.000000 | 346109.300000 | 79802.352000 | 70129.762000 | 47966.205000 | NaN | NaN | 2491320.000000 | 2166914.000000 | 1606973.000000 | 1808369.000000 | 2210364.000000 | 9456832.000000 | 8120252.000000 | 4717565.000000 | 2092380.000000 | 1590910.000000 | 1214765.000000 | 1061422.848000 | 607779.928000 | 259326.070000 | 143194.646000 | NaN | NaN | 2911454.000000 | 2170056.000000 | 1397747.000000 | 1288711.000000 | 2620265.000000 | 9994920.000000 | 7403052.000000 | 3706890.000000 | 1165334.000000 | 1199387.000000 | 777115.000000 | 715313.548000 | 527977.576000 | 189196.308000 | 95228.441000 | NaN | NaN | 182037.000000 | 138376.000000 | 64156.000000 | 95506.000000 | 93676.000000 | 262326.000000 | 961291.000000 | 349646.000000 | 370809.000000 | 315771.000000 | 309266.000000 | 237777.966000 | 135685.746000 | 67149.636000 | 27583.420000 | NaN | NaN | 5679.000000 | 6666.000000 | 7352.000000 | 6104.000000 | 49626.000000 | 13146.000000 | 12480.000000 | 34602.000000 | 9532.000000 | 4593.000000 | 4094.000000 | 6015.411000 | NaN | 191.925000 | 145.979000 | NaN | NaN | 2812734.000000 | 2608782.000000 | 2111024.000000 | 2478713.000000 | 3055106.000000 | 10446519.000000 | 8936391.000000 | 5306479.000000 | 2503171.000000 | 1929498.000000 | 1473649.000000 | 1298358.203000 | 754771.051000 | 359091.317000 | 226443.835000 | NaN | NaN | 2953727.000000 | 2212725.000000 | 1440027.000000 | 1362982.000000 | 2731074.000000 | 10160043.000000 | 7509598.000000 | 3869352.000000 | 1201946.000000 | 1264639.000000 | 831601.000000 | 737915.640000 | 536559.738000 | 193550.159000 | 96961.292000 | NaN | NaN | NaN | NaN | 3341.000000 | 22160.000000 | 51397.000000 | 104117.000000 | 63083.000000 | 138669.000000 | 20327.000000 | 56147.000000 | 44362.000000 | 16885.953000 | 5039.545000 | 1967.620000 | 466.667000 | NaN | NaN | 285000.000000 | 40000.000000 | NaN | NaN | 245000.000000 | 817923.000000 | 2656388.000000 | 1092179.000000 | 255000.000000 | 350000.000000 | 45337.000000 | 15961.846000 | 26338.333000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 285000.000000 | 43341.000000 | 22171.000000 | 51569.000000 | 1082372.000000 | 4210330.000000 | 3183252.000000 | 1871951.000000 | 312608.000000 | 440262.000000 | 272965.000000 | 118266.063000 | 125610.468000 | 30522.840000 | 20866.667000 | NaN | NaN | -140993.000000 | 396057.000000 | 670997.000000 | 1115731.000000 | 324032.000000 | 286476.000000 | 1426793.000000 | 1437127.000000 | 1301225.000000 | 664859.000000 | 642048.000000 | 560442.562000 | 218211.313000 | 165541.157000 | 129482.543000 | NaN | NaN | 263754.000000 | 336870.000000 | 429592.000000 | 543775.000000 | 657998.000000 | 745130.000000 | 755129.000000 | 486798.000000 | 370306.000000 | 310061.000000 | 242007.000000 | 220839.892000 | 134582.169000 | 89379.957000 | 80250.650000 | NaN | NaN | 3371726.000000 | 2389679.000000 | 1348817.000000 | 1642358.000000 | 1500270.000000 | 4046981.000000 | 3026009.000000 | 2561089.000000 | 1514363.000000 | 1132494.000000 | 1101969.000000 | 818537.622000 | 554357.668000 | 356616.043000 | 244572.004000 | NaN | NaN | 3172312.000000 | 2349638.000000 | 1036870.000000 | 1494671.000000 | 1204429.000000 | 3947173.000000 | 3725296.000000 | 3026989.000000 | 1794002.000000 | 1356868.000000 | 1350908.000000 | 1014502.030000 | 669905.664000 | 429063.355000 | 303107.369000 | NaN | NaN | -363885.000000 | -190416.000000 | -464514.000000 | -614271.000000 | -1082033.000000 | -430375.000000 | 497768.000000 | 310722.000000 | 182096.000000 | 130916.000000 | 176807.000000 | 146399.359000 | 77033.290000 | 48495.165000 | 42562.582000 | NaN | NaN | -271954.000000 | -88291.000000 | -354321.000000 | -497358.000000 | -957036.000000 | -306643.000000 | 621841.000000 | 397442.000000 | 243054.000000 | 180822.000000 | 234231.000000 | 194155.134000 | 102386.835000 | 68401.903000 | 56844.284000 | NaN | NaN | -363885.000000 | -190416.000000 | -464514.000000 | -614271.000000 | -1082033.000000 | -430375.000000 | 497768.000000 | 310722.000000 | 182096.000000 | 130916.000000 | 176807.000000 | 146399.359000 | 77033.290000 | 48495.165000 | 42562.582000 | NaN | NaN | -542065.000000 | -276638.000000 | -451613.000000 | -693366.000000 | -1340079.000000 | -1131849.000000 | 21419.000000 | 153791.000000 | 67555.000000 | 42222.000000 | 103897.000000 | 99215.342000 | 52233.546000 | 36486.248000 | 27770.706000 | NaN | NaN | -122713.000000 | -101705.000000 | -216864.000000 | 197089.000000 | 1761692.000000 | -341724.000000 | -857235.000000 | -1128265.000000 | -158255.000000 | -24968.000000 | -43287.000000 | -112217.697000 | 44407.666000 | 30836.348000 | -19897.347000 | NaN | NaN | 129384.000000 | 92868.000000 | -265851.000000 | 398257.000000 | -108366.000000 | -40888.000000 | 7535.000000 | 75904.000000 | 78544.000000 | 13831.000000 | 1788.000000 | -19912.184000 | 44619.830000 | 3503.348000 | -7196.844000 | NaN | NaN | 12675160000.000000 | 12675160000.000000 | 12675160000.000000 | 12675160000.000000 | 6425160000.000000 | 700000000.000000 | 700000000.000000 | 700000000.000000 | 700000000.000000 | 500000000.000000 | 500000000.000000 | 500000000.000000 | 400000000.000000 | 400000000.000000 | 400000000.000000 | NaN | NaN | 1090.063760 | 1723.821760 | 1990.000120 | 2661.783600 | 2827.070400 | 679.000000 | 1088.500000 | 1722.000000 | 1974.000000 | 1510.000000 | 1862.500000 | 995.000000 | NaN | NaN | NaN | NaN | NaN | 86.000000 | 136.000000 | 157.000000 | 210.000000 | 440.000000 | 970.000000 | 1555.000000 | 2460.000000 | 2820.000000 | 3020.000000 | 3725.000000 | 1990.000000 | NaN | NaN | NaN | NaN | NaN | 144007.000000 | 439398.000000 | 693168.000000 | 1167300.000000 | 1406404.000000 | 4496806.000000 | 4610045.000000 | 3309078.000000 | 1613833.000000 | 1105121.000000 | 915013.000000 | 678708.625000 | 343821.782000 | 196063.997000 | 150349.210000 | NaN | NaN | 428058.000000 | 298674.000000 | 205806.000000 | 471657.000000 | 73400.000000 | 181766.000000 | 222654.000000 | 215119.000000 | 139215.000000 | 60671.000000 | 49575.000000 | 48718.694000 | 64965.045000 | 20345.215000 | 16841.867000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 0.856000 | 0.999000 | 1.150000 | 1.403000 | 0.844000 | 0.946000 | 1.097000 | 1.273000 | 1.796000 | 1.326000 | 1.563000 | 1.484000 | 1.151000 | 1.371000 | 1.504000 | NaN | NaN |
=== Contoh nilai time_flag sebelum normalisasi ===
['STATIC' '2024' '2023' '2022' '2021' '2020' '2019' '2018' '2017' '2016'
'2015' '2014' '2013' '2012' '2011' '2010' '2009' '2008' 'STATIC.1'
'STATIC.2']
def normalize_time_flag(x):
if pd.isna(x):
return x
x_str = str(x)
if x_str == "STATIC":
return "STATIC"
# "2024.1" -> "2024", "2024.0" -> "2024"
x_str = x_str.split(".")[0]
return x_str
var_level = df_raw.columns.get_level_values("variable")
flag_raw = df_raw.columns.get_level_values("time_flag")
flag_norm = [normalize_time_flag(v) for v in flag_raw]
df_raw.columns = pd.MultiIndex.from_arrays(
[var_level, flag_norm],
names=["variable", "time_flag"]
)
print("=== Nilai unik time_flag setelah normalisasi ===")
print(pd.unique(df_raw.columns.get_level_values("time_flag")))=== Nilai unik time_flag setelah normalisasi ===
['STATIC' '2024' '2023' '2022' '2021' '2020' '2019' '2018' '2017' '2016'
'2015' '2014' '2013' '2012' '2011' '2010' '2009' '2008']
df_raw = df_raw.groupby(
axis=1,
level=["variable", "time_flag"],
sort=False
).first()
df_raw.columns = df_raw.columns.set_names(["variable", "time_flag"])
print("=== df_raw setelah penggabungan kolom duplikat ===")
display(df_raw.head())/tmp/ipykernel_108742/1584443115.py:1: FutureWarning:
DataFrame.groupby with axis=1 is deprecated. Do `frame.T.groupby(...)` without axis instead.
=== df_raw setelah penggabungan kolom duplikat ===
| variable | Entity Name | Entity ID | Exchange | exchange_ticker | Primary Industry | 1st Level Primary Industry | Country / Region Name | Address 1 | Web Address | Business Description | Company Type | Investor Type | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Company Status | Ownership Structure | Activist Investor? Yes/No | Institution Coverage Level | Consolidation Status | Has Current Financials? Yes/No | Has Bank Regulatory Financials? Yes/No | Has Third Party Financials? Yes/No | Long Business Description | Topic Tags | Crunchbase Categories | Year Established | Date Established MM/dd/yyyy | Year Incorporated | Date Incorporated MM/dd/yyyy | Month of Fiscal Year End | Headcount As Of MM/dd/yyyy | Industry Classification | 2nd Level Primary Industry | 3rd Level Primary Industry | 4th Level Primary Industry | Sector | Industry Group | Industry | City | Parent Company Name | Parent Ticker | Parent Exchange: Ticker | Parent Percent Owned (%) | Parent City | Parent Industry | Ultimate Parent Company Name | Ultimate Parent Ticker | Ultimate Parent Exchange: Ticker | Ultimate Parent City | Ultimate Parent Industry | Ultimate Parent Country / Region | All Investors | Percent Owned - All Institutions (%) | Shares Owned - All Institutions (actual) | Market Value - All Institutions (Rp.B) | Percent Owned - Insiders (%) | Shares Owned - Insiders (actual) | Market Value - Insiders (Rp.B) | IPO Date MM/dd/yyyy | IPO Price (Rp.) | Location Type | Data Precision | Data Year | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.B) | Day Close Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| time_flag | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 |
| 0 | Perusahaan Perseroan (Persero) PT Telekomunika... | 4210975 | IDX | IDX:TLKM | Integrated Telecommunication Services | Technology, Media & Telecommunications | Indonesia | Jl. Japati No. 1 | www.telkom.co.id | Perusahaan Perseroan (Persero) PT Telekomunika... | Public Company | None | 137185000.000000 | 130480000.000000 | 125742000.000000 | 131785000.000000 | 126054000.000000 | 103958000.000000 | 88893000.000000 | 86354000.000000 | 74067000.000000 | 72745000.000000 | 55830000.000000 | 44391000.000000 | 50527000.000000 | 42073000.000000 | 44086000.000000 | 48228553.000000 | 47258399.000000 | 299675000.000000 | 287042000.000000 | 274817000.000000 | 277184000.000000 | 246943000.000000 | 221208000.000000 | 206196000.000000 | 198484000.000000 | 179611000.000000 | 166173000.000000 | 141822000.000000 | 127951000.000000 | 111369000.000000 | 103054000.000000 | 100501000.000000 | 97814160.000000 | 91256250.000000 | Operating Subsidiary | None | No | None | None | Yes | No | No | Perusahaan Perseroan (Persero) PT Telekomunika... | Network Backbone; Optical Infrastructure; Clou... | Digital Media, Information Technology, Telecom... | 1884.000000 | 27/03/1884 01:00:00 | NaN | NaT | Dec | 2025-08-01 | Integrated Telecommunication Services | Telecommunication Services | Integrated Telecommunication Services | None | Communication Services | Telecommunication Services | Diversified Telecommunication Services | Bandung | PT Danantara Asset Management (Persero) | None | None | 0.520900 | Jakarta | Construction and Engineering | Indonesia | None | None | Jakarta | Diversified Support Services | Indonesia | Indonesia (Prior Subsidiary or Operating Unit,... | 21.060000 | 20859696180.000000 | 77180875.866000 | 0.090000 | 85029954.000000 | 314610.829800 | NaT | NaN | None | None | None | 0.822000 | 0.777000 | 0.784000 | 0.886000 | 0.673000 | 0.715000 | 0.935000 | 1.048000 | 1.200000 | 1.353000 | 1.061000 | 1.163000 | 1.160000 | 0.958000 | 0.915000 | 0.602000 | 0.542000 | 0.692000 | 0.643000 | 0.630000 | 0.730000 | 0.510000 | 0.543000 | 0.677000 | 0.818000 | 0.993000 | 1.031000 | 0.795000 | 0.974000 | 0.965000 | 0.703000 | 0.687000 | 0.474000 | 0.420000 | -13687000.000000 | -15955000.000000 | -15162000.000000 | -7854000.000000 | -22590000.000000 | -16647000.000000 | -2993000.000000 | 2185000.000000 | 7939000.000000 | 12499000.000000 | 1976000.000000 | 4638000.000000 | 3866000.000000 | -931000.000000 | -1744000.000000 | -10707101.000000 | -12375841.000000 | 63080000.000000 | 55613000.000000 | 55073000.000000 | 61277000.000000 | 46503000.000000 | 41722000.000000 | 43268000.000000 | 47561000.000000 | 47701000.000000 | 47912000.000000 | 34294000.000000 | 33075000.000000 | 27973000.000000 | 21258000.000000 | 18729000.000000 | 16186024.000000 | 14622310.000000 | 76767000.000000 | 71568000.000000 | 70235000.000000 | 69131000.000000 | 69093000.000000 | 58369000.000000 | 46261000.000000 | 45376000.000000 | 39762000.000000 | 35413000.000000 | 32318000.000000 | 28437000.000000 | 24107000.000000 | 22189000.000000 | 20473000.000000 | 26893125.000000 | 26998151.000000 | 1096000.000000 | 997000.000000 | 1144000.000000 | 779000.000000 | 983000.000000 | 585000.000000 | 717000.000000 | 631000.000000 | 584000.000000 | 528000.000000 | 474000.000000 | 509000.000000 | 579000.000000 | 758000.000000 | 515000.000000 | 435244.000000 | 511950.000000 | 6655000.000000 | 6520000.000000 | 7124000.000000 | 5108000.000000 | 4993000.000000 | 5471000.000000 | 5218000.000000 | 1576000.000000 | 1463000.000000 | 2147000.000000 | 641000.000000 | 1186000.000000 | 849000.000000 | 731000.000000 | 883000.000000 | 722850.000000 | 1875773.000000 | 25518000.000000 | 27773000.000000 | 27331000.000000 | 36319000.000000 | 30561000.000000 | 32293000.000000 | 31410000.000000 | 24964000.000000 | 23015000.000000 | 26229000.000000 | 11525000.000000 | 10410000.000000 | 11803000.000000 | 12644000.000000 | 16246000.000000 | 14249575.000000 | 11444575.000000 | 11525000.000000 | 9650000.000000 | 8191000.000000 | 6682000.000000 | 9934000.000000 | 8705000.000000 | 4043000.000000 | 2289000.000000 | 911000.000000 | 602000.000000 | 1810000.000000 | 432000.000000 | 37000.000000 | 100000.000000 | 56000.000000 | 43850.000000 | 46000.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 76868000.000000 | 68124000.000000 | 62853000.000000 | 69078000.000000 | 65462000.000000 | 52084000.000000 | 44087000.000000 | 35472000.000000 | 31799000.000000 | 34612000.000000 | 23452000.000000 | 20256000.000000 | 19275000.000000 | 17871000.000000 | 21910000.000000 | 21329926.000000 | 17584731.000000 | 162490000.000000 | 156562000.000000 | 149075000.000000 | 145399000.000000 | 120889000.000000 | 117250000.000000 | 117303000.000000 | 112130000.000000 | 105544000.000000 | 93428000.000000 | 85992000.000000 | 77424000.000000 | 66978000.000000 | 60981000.000000 | 56415000.000000 | 49585607.000000 | 43997851.000000 | 207476000.000000 | 203339000.000000 | 193022000.000000 | 183495000.000000 | 179489000.000000 | 156973000.000000 | 143248000.000000 | 130171000.000000 | 114498000.000000 | 103700000.000000 | 94809000.000000 | 86761000.000000 | 77047000.000000 | 74897000.000000 | 75832000.000000 | 76419897.000000 | 71066244.000000 | 60010000.000000 | 58238000.000000 | 54658000.000000 | 53530000.000000 | 50675000.000000 | 60315000.000000 | 61252000.000000 | 53119000.000000 | 47465000.000000 | 42893000.000000 | 35651000.000000 | 32803000.000000 | 29602000.000000 | 27992000.000000 | 25400000.000000 | 23565712.000000 | 22159917.000000 | 149967000.000000 | 149216000.000000 | 147306000.000000 | 143210000.000000 | 136462000.000000 | 135567000.000000 | 130784000.000000 | 128256000.000000 | 116333000.000000 | 102470000.000000 | 89696000.000000 | 82967000.000000 | 77143000.000000 | 71253000.000000 | 69177000.000000 | 67677518.000000 | 64166429.000000 | 43129000.000000 | 44885000.000000 | 45202000.000000 | 43318000.000000 | 43332000.000000 | 42283000.000000 | 38624000.000000 | 43727000.000000 | 40000000.000000 | 33694000.000000 | 29813000.000000 | 28589000.000000 | 26667000.000000 | 23248000.000000 | 22894000.000000 | 23831275.000000 | 23225173.000000 | 67694000.000000 | 70054000.000000 | 71042000.000000 | 75032000.000000 | 65522000.000000 | 64296000.000000 | 58804000.000000 | 63136000.000000 | 58532000.000000 | 52228000.000000 | 46139000.000000 | 43773000.000000 | 40876000.000000 | 37548000.000000 | 37506000.000000 | 37787661.000000 | 35538389.000000 | 43129000.000000 | 44885000.000000 | 45202000.000000 | 43318000.000000 | 43332000.000000 | 42283000.000000 | 38624000.000000 | 43727000.000000 | 40000000.000000 | 33694000.000000 | 29813000.000000 | 28589000.000000 | 26667000.000000 | 23248000.000000 | 22894000.000000 | 23831275.000000 | 23225173.000000 | 30743000.000000 | 32208000.000000 | 27720000.000000 | 34099000.000000 | 29563000.000000 | 27592000.000000 | 26979000.000000 | 32701000.000000 | 29172000.000000 | 23317000.000000 | 21274000.000000 | 20290000.000000 | 18362000.000000 | 15470000.000000 | 15870000.000000 | 16042898.000000 | 14725429.000000 | 61600000.000000 | 60581000.000000 | 73354000.000000 | 68353000.000000 | 65317000.000000 | 54949000.000000 | 45671000.000000 | 49405000.000000 | 47231000.000000 | 43669000.000000 | 37736000.000000 | 36574000.000000 | 27941000.000000 | 30553000.000000 | 27759000.000000 | 29811604.000000 | 24553925.000000 | 4898000.000000 | -2940000.000000 | -6364000.000000 | 17722000.000000 | 2347000.000000 | 803000.000000 | -7706000.000000 | -4622000.000000 | 1650000.000000 | 10445000.000000 | 2976000.000000 | 1578000.000000 | 3484000.000000 | 514000.000000 | 1315000.000000 | 915515.000000 | -3250846.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 98198216600.000000 | 98175853600.000000 | 97100853600.000000 | 95745344100.000000 | 96931696596.000000 | 98347123896.000000 | 98347123896.000000 | 98347123896.000000 | 268458.606986 | 391295.755570 | 371483.312250 | 400211.355064 | 327895.936946 | 393276.999902 | 371483.312250 | 439836.241704 | 394267.622068 | 304836.025428 | 281273.820564 | 208766.835240 | 173339.797814 | 137673.219768 | 156371.926995 | 185876.064163 | 135790.870326 | 2710.000000 | 3950.000000 | 3750.000000 | 4040.000000 | 3310.000000 | 3970.000000 | 3750.000000 | 4440.000000 | 3980.000000 | 3105.000000 | 2865.000000 | 2150.000000 | 1810.000000 | 1410.000000 | 1590.000000 | 1890.000000 | 1380.000000 | 239358000.000000 | 224686000.000000 | 211928000.000000 | 214477000.000000 | 186351000.000000 | 169334000.000000 | 161390000.000000 | 147602000.000000 | 137343000.000000 | 128040000.000000 | 109444000.000000 | 97680000.000000 | 86253000.000000 | 78852000.000000 | 78325000.000000 | 70915533.000000 | 61582582.000000 | 35027000.000000 | 30430000.000000 | 32883000.000000 | 38740000.000000 | 21818000.000000 | 18620000.000000 | 18590000.000000 | 26638000.000000 | 31001000.000000 | 28566000.000000 | 18036000.000000 | 21256000.000000 | 17428000.000000 | 9995000.000000 | 9490000.000000 | 8164967.000000 | 7156989.000000 | 9442000.000000 | 8731000.000000 | 8302000.000000 | 7506000.000000 | 6846000.000000 | 6446000.000000 | 5032000.000000 | NaN | NaN | NaN | NaN | None | None | None | None | None | None |
| 1 | PT Abadi Nusantara Hijau Investama Tbk (IDX:PACK) | 109420637 | IDX | IDX:PACK | Packaging and Materials: Paper and Plastic | Materials | Indonesia | Jl. Jababeka 2 Block C/11-D | www.flexypack.com | PT Abadi Nusantara Hijau Investama Tbk provide... | Public Company | None | 9131.515000 | 22395.996000 | 37928.982000 | 33821.027000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 66918.151000 | 77129.464000 | 43686.326000 | 36584.088000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | Operating | None | No | None | None | Yes | No | No | PT Abadi Nusantara Hijau Investama Tbk provide... | Packaging Services | None | 2019.000000 | 2019-11-18 00:00:00 | NaN | NaT | Dec | NaT | Packaging and Materials: Paper and Plastic | Packaging and Materials: Paper and Plastic | None | None | Materials | Materials | Containers and Packaging | Bekasi | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Benson Kapital Indonesia (Current Investmen... | 23.240000 | 371188000.000000 | 965088.800000 | 0.000000 | 0.000000 | NaN | 2023-02-06 | 162.000000 | None | None | None | 3.171000 | 1.849000 | 0.397000 | 0.279000 | None | None | None | None | None | None | None | NaN | None | None | None | None | None | 2.532000 | 0.891000 | 0.259000 | 0.116000 | None | None | None | None | None | None | None | NaN | None | None | NaN | NaN | NaN | 18470.909000 | 16160.048000 | -15748.231000 | -13908.977000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 26979.387000 | 35187.194000 | 10379.496000 | 5381.327000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 8508.479000 | 19027.146000 | 26127.727000 | 19290.304000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 5217.791000 | 18152.157000 | 2309.696000 | 1102.312000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 28.664000 | 14.360000 | 4.889000 | 16.514000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2835.577000 | 9595.659000 | 15429.582000 | 17861.356000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 57786.636000 | 54733.468000 | 5757.344000 | 2763.062000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 29813.794000 | 27877.647000 | 26738.757000 | 25626.326000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 40843.331000 | 37002.120000 | 32303.664000 | 30325.181000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 54702.368000 | 52931.736000 | 44150.214000 | 36215.903000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2629.162000 | 5081.433000 | 3948.041000 | -4738.235000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 4465.941000 | 6785.251000 | 5446.039000 | -3352.747000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2629.162000 | 5081.433000 | 3948.041000 | -4738.235000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 899.758000 | 2288.792000 | 2052.793000 | -4690.516000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 8343.775000 | -26767.823000 | 7568.441000 | 1967.910000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | -2271.124000 | 5946.648000 | 539.116000 | -32.844000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1543043824.000000 | 1537579370.000000 | 1229546000.000000 | 571400000.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 948.817070 | 55.351773 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 615.000000 | 36.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 60622.214000 | 64329.128000 | 21186.926000 | 20624.418000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 4249.362000 | 6520.486000 | 573.838000 | 34.722000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 10041.786000 | 9556.735000 | 5626.787000 | 4095.608000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | None | None | None | None | None | None |
| 2 | PT ABM Investama Tbk (IDX:ABMM) | 4980353 | IDX | IDX:ABMM | Coal and Consumable Fuels | Energy and Utilities | Indonesia | Tiara Marga Trakindo I building | www.abm-investama.com | PT ABM Investama Tbk, together with its subsid... | Public Company | None | 20093470.573188 | 21545086.944192 | 21197974.640881 | 9685324.903374 | 9268917.351480 | 8427827.077252 | 8713242.360857 | 11934035.187236 | 12314807.459430 | 14025286.606475 | 11507764.542355 | 8840383.482359 | 10840297.762755 | 7019323.859661 | 3763830.000000 | 3099452.000000 | 4360950.000000 | 33732579.888996 | 33243187.213530 | 30787462.520356 | 14769924.153273 | 11521759.429112 | 11820817.650070 | 12287671.907708 | 14134486.114136 | 14460055.871406 | 16420535.888287 | 14028411.858435 | 14757757.263945 | 12175363.933647 | 10077243.517333 | 4890266.000000 | 4199977.000000 | 3549129.000000 | Operating Subsidiary | None | No | None | None | Yes | No | No | PT ABM Investama Tbk operating in the sectors ... | Clean Energy; Freight Service; Marine Transpor... | Energy, Precious Metals | 2006.000000 | 2006-06-01 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | PT Tiara Marga Trakindo | None | None | 0.535500 | Jakarta | Industrial Conglomerates | PT Tiara Marga Trakindo | None | None | Jakarta | Industrial Conglomerates | Indonesia | PT Tiara Marga Trakindo (Current Subsidiary or... | 0.830000 | 22926501.000000 | 68779.503000 | 7.340000 | 202136500.000000 | 606409.500000 | 2011-12-05 | 3750.000000 | None | None | None | 1.067000 | 0.986000 | 1.145000 | 1.652000 | 1.196000 | 1.204000 | 1.460000 | 1.597000 | 0.792000 | 0.883000 | 0.982000 | 0.996000 | 1.226000 | 1.362000 | 0.893000 | 1.113000 | 0.592000 | 0.905000 | 0.844000 | 0.792000 | 1.341000 | 0.969000 | 0.992000 | 1.275000 | 1.328000 | 0.653000 | 0.751000 | 0.820000 | 0.813000 | 1.007000 | 1.088000 | 0.700000 | 0.879000 | 0.430000 | 540116.972988 | -138088.846896 | 1393425.586718 | 2785291.548858 | 716862.070496 | 610980.039406 | 1477581.268563 | 1720113.072412 | -1048360.536690 | -622368.364089 | -75076.681040 | -19818.463770 | 820288.622790 | 1134634.650895 | -252891.000000 | 203475.000000 | -953832.000000 | 8603535.372060 | 9598638.433986 | 10973792.911797 | 7055017.392177 | 4375629.087504 | 3608327.316374 | 4690972.433270 | 4601979.099836 | 4002183.122758 | 4692284.395771 | 4203180.206570 | 4670518.461795 | 4449825.549909 | 4270489.900468 | 2106194.000000 | 2001334.000000 | 1384649.000000 | 8063418.399072 | 9736727.280882 | 9580367.325079 | 4269725.843319 | 3658767.017008 | 2997347.276968 | 3213391.164707 | 2881866.027424 | 5050543.659448 | 5314652.759860 | 4278256.887610 | 4690336.925565 | 3629536.927119 | 3135855.249573 | 2359085.000000 | 1797859.000000 | 2338481.000000 | 976088.594994 | 821228.155566 | 864077.600244 | 565420.432554 | 525300.327976 | 411458.649184 | 441376.280596 | 406041.471732 | 345572.528640 | 293631.497503 | 310286.074145 | 499804.401930 | 442934.444820 | 414473.810135 | 301356.000000 | 233936.000000 | 241667.000000 | 77287.962918 | 47921.324472 | 68165.694646 | 145468.209915 | 43412.280696 | 124924.565348 | 78087.463339 | 208496.797292 | 304715.857400 | 359407.215314 | 308703.011060 | 234000.429255 | 171860.938376 | 26318.699181 | 11526.000000 | NaN | NaN | 10650442.128438 | 10137708.047964 | 10408734.918322 | 4363219.444311 | 4815187.839656 | 4751102.996552 | 4920968.773924 | 6569397.072784 | 4716889.153288 | 5460877.970722 | 5966403.514665 | 4363521.366120 | 3501077.172306 | 2832092.967225 | 841659.000000 | NaN | NaN | 1725644.443698 | 1070033.159496 | 932422.887775 | 66960.900000 | 557120.000000 | 484330.000000 | NaN | NaN | 579496.717636 | 675321.089765 | 323324.159965 | 740148.951675 | 520493.454485 | 494015.259308 | 292208.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 15716952.113922 | 16033280.386032 | 14577725.216805 | 5488144.613091 | 5924993.624080 | 5400701.282240 | 5215817.182160 | 7009762.688972 | 7134999.566212 | 8304395.601394 | 8039725.363235 | 7610896.646745 | 6493573.658040 | 4929981.462200 | 1924066.000000 | 0.000000 | 0.000000 | 13639109.315808 | 11698100.269338 | 9589487.879475 | 5084599.249899 | 2252842.077632 | 3392990.572818 | 3574429.546851 | 2200450.926900 | 2145248.411976 | 2395249.281812 | 2520647.316080 | 3917459.501190 | 3334980.451288 | 3057919.657672 | 1126436.000000 | 1100525.000000 | -811821.000000 | 13588532.603310 | 13083527.221044 | 10798972.554367 | 6404601.223980 | 5698003.631872 | 5952020.777318 | 6748196.663272 | 8540482.052180 | 9309009.686380 | 10701896.185132 | 8731295.576750 | 8657844.015315 | 6588204.391554 | 4881836.576369 | 2172994.000000 | 1532725.000000 | 1469802.000000 | 16940553.969294 | 16775091.191981 | 13717060.583375 | 9379940.152987 | 7360843.103808 | 6853297.177677 | 8527758.336357 | 7217935.093074 | 6164493.688407 | 7070612.704500 | 6865298.865170 | 6504601.420018 | 6719153.515715 | 5237512.675191 | 3789687.000000 | 3455803.000000 | 3114382.000000 | 19019676.227701 | 22748651.299773 | 21468049.600463 | 14622288.077965 | 8840833.965492 | 8378371.179746 | 11008635.659721 | 9244047.765335 | 7868310.286515 | 8775353.280985 | 8599050.952989 | 8115915.925543 | 8311255.281959 | 6607997.885733 | 4486419.000000 | 3926320.000000 | 3235172.000000 | 1405297.314532 | 4260726.217364 | 6039382.223438 | 4531608.021944 | 359223.936602 | 809280.825757 | 2132909.657311 | 763888.620366 | 695084.433659 | 85615.766681 | -781424.815807 | 327174.492051 | 466344.893687 | 706361.557448 | 271266.000000 | 491429.000000 | -469841.000000 | 3736427.208202 | 6016608.771238 | 7545384.213516 | 5399394.243944 | 1591016.404358 | 1645292.178698 | 2653600.347789 | 1987378.442476 | 2021789.251641 | 1549664.004501 | 1237520.491127 | 1299731.078470 | 1614985.150831 | 1250603.533610 | 705326.000000 | NaN | NaN | 1405297.314532 | 4260726.217364 | 6039382.223438 | 4531608.021944 | 359223.936602 | 809280.825757 | 2132909.657311 | 763888.620366 | 695084.433659 | 85615.766681 | -781424.815807 | 327174.492051 | 466344.893687 | 706361.557448 | 271266.000000 | 491429.000000 | -469841.000000 | 2208286.963756 | 4809124.839499 | 5077732.593725 | 2664181.082945 | -550213.689470 | 55073.183258 | 957348.122798 | 50838.656519 | 91939.054880 | -607731.656746 | -1373861.491832 | 21660.865098 | 116452.543303 | 484431.520834 | 127376.000000 | -11346.000000 | -3913.000000 | 5673781.353325 | 5901279.191712 | 6262674.404548 | 5216623.032704 | 1929513.838089 | 850217.381364 | 1934408.478086 | 2018788.409131 | 1602614.652688 | 2292600.227669 | 1330187.486686 | 1715558.666979 | 1249385.895957 | 53032.416782 | 612476.000000 | 678998.000000 | -4564.000000 | -289365.113950 | -502652.039715 | -232487.728337 | 1831719.103847 | 106265.604590 | -595842.773237 | 47878.928040 | 789290.497254 | -408173.515297 | 107928.957648 | 169184.976267 | -192036.292693 | -708061.937615 | 1190873.399695 | -181536.000000 | 455151.000000 | 7861.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 825760000.000000 | 826000000.000000 | NaN | 9746.204100 | 9360.761000 | 9030.381200 | 3909.494300 | 2092.405400 | 4212.342450 | 6249.684550 | 6332.279500 | 5588.924950 | 7984.178500 | 8397.153250 | 8259.495000 | 8259.495000 | 10530.856125 | NaN | NaN | NaN | 3540.000000 | 3400.000000 | 3280.000000 | 1420.000000 | 760.000000 | 1530.000000 | 2270.000000 | 2300.000000 | 2030.000000 | 2900.000000 | 3050.000000 | 3000.000000 | 3000.000000 | 3825.000000 | NaN | NaN | NaN | 29356061.429730 | 27731380.655370 | 24167213.096280 | 10572743.862990 | 8177835.701712 | 8793691.855058 | 8790246.729011 | 9210213.615872 | 9280247.978188 | 10699644.883206 | 10560372.679315 | 11528356.147935 | 9828554.109328 | 7987901.119872 | 3050502.000000 | 1100525.000000 | -811821.000000 | 2741781.997248 | 2907757.321224 | 3441719.228344 | 3380709.965967 | 1522129.237896 | 1410416.880994 | 2265170.861332 | 1907200.309552 | 1242565.677740 | 1930012.824268 | 1526592.624775 | 1256331.810840 | 1233004.384532 | 1669222.334305 | 433039.000000 | 614575.000000 | 159424.000000 | 241953.535626 | 216885.738972 | 251403.717345 | 223331.241996 | 236740.734304 | 240865.597962 | 45742.429116 | NaN | NaN | NaN | NaN | None | None | None | None | None | None |
| 3 | PT Ace Oldfields Tbk (IDX:KUAS) | 7649048 | IDX | IDX:KUAS | Building Products | Industrials | Indonesia | Jl. Raya Cileungsi Jonggol | www.aceoldfields.com | PT Ace Oldfields Tbk manufactures and sells pa... | Public Company | None | 115267.049000 | 107904.478000 | 120390.576000 | 122407.148000 | 155435.120000 | 154466.505000 | 161107.849000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 278143.466000 | 264359.356000 | 269542.435000 | 262419.786000 | 206482.511000 | 199903.930000 | 203429.610000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | Operating | None | No | None | None | Yes | No | No | None | None | None | 1989.000000 | 1989-09-18 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Building Products; Hardware Tools and Equipmen... | Capital Goods | Building Products | None | Industrials | Capital Goods | Building Products | Bogor | None | None | None | NA% | None | None | None | None | None | None | None | None | Oldfields Holdings Limited (Prior Investment, ... | NaN | NaN | NaN | 0.870000 | 11225400.000000 | 763.327200 | 2021-10-25 | 195.000000 | None | None | None | 1.997000 | 1.974000 | 1.828000 | 1.814000 | 1.308000 | 1.309000 | 1.310000 | None | None | None | None | NaN | None | None | None | None | None | 1.172000 | 1.072000 | 1.033000 | 1.210000 | 0.699000 | 0.537000 | 0.544000 | None | None | None | None | NaN | None | None | NaN | NaN | NaN | 86839.444000 | 78355.409000 | 74169.643000 | 72372.271000 | 23870.410000 | 22439.724000 | 23621.044000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 173948.142000 | 158762.285000 | 163700.210000 | 161289.971000 | 101493.623000 | 94943.288000 | 99749.278000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 87108.698000 | 80406.876000 | 89530.566000 | 88917.700000 | 77623.213000 | 72503.564000 | 76128.234000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 71327.348000 | 71649.956000 | 70940.942000 | 53641.362000 | 47231.311000 | 56009.830000 | 58268.430000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 147.189000 | 224.208000 | 244.110000 | NaN | NaN | NaN | 35.264000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1634.488000 | 1073.554000 | 1449.214000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 62574.148000 | 60824.332000 | 68829.023000 | 70330.607000 | 57947.634000 | 58677.517000 | 58922.593000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 89651.801000 | 87684.384000 | 99772.844000 | 100011.914000 | 133144.255000 | 138006.077000 | 141918.039000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 162876.417000 | 156454.878000 | 149151.859000 | 140012.638000 | 51047.391000 | 45437.426000 | 42321.761000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 100287.643000 | 101849.807000 | 102292.856000 | 97649.202000 | 101747.189000 | 102175.913000 | 101709.941000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 130570.138000 | 114707.655000 | 112090.876000 | 106484.602000 | 103949.885000 | 107761.935000 | 113631.570000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 184692.659000 | 161660.709000 | 153828.141000 | 139351.625000 | 131083.726000 | 130073.784000 | 136605.549000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 20523.971000 | 18341.355000 | 15142.784000 | 15351.764000 | 12508.497000 | 9495.797000 | 9791.271000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 24221.391000 | 22206.902000 | 18812.802000 | 18288.391000 | 14108.647000 | 10873.891000 | 11133.338000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 20523.971000 | 18341.355000 | 15142.784000 | 15351.764000 | 12508.497000 | 9495.797000 | 9791.271000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 11300.748000 | 9505.923000 | 7864.938000 | 6178.641000 | 5750.754000 | 3591.997000 | 2840.922000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 5509.571000 | 6666.933000 | -14257.143000 | -4560.385000 | 11269.340000 | 7629.488000 | 5507.511000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1390.124000 | -9025.851000 | -22833.804000 | 46687.897000 | 4214.702000 | 449.762000 | 1393.988000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1292808150.000000 | 1292808150.000000 | 1292808150.000000 | 1292570540.000000 | 630000000.000000 | 902570540.000000 | 902570540.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 67.226024 | 64.640407 | 71.091653 | 93.065079 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 52.000000 | 50.000000 | 55.000000 | 72.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 252528.218000 | 244139.262000 | 248924.703000 | 240024.552000 | 184191.645000 | 183443.502000 | 184239.800000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 26195.870000 | 24805.746000 | 33831.597000 | 56665.401000 | 9977.504000 | 5762.802000 | 5313.040000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | None | None | None | None | None | None |
| 4 | PT Acset Indonusa Tbk (IDX:ACST) | 4990054 | IDX | IDX:ACST | Construction and Engineering | Industrials | Indonesia | ACSET Building | www.acset.co | PT Acset Indonusa Tbk provides construction an... | Public Company | None | 2953727.000000 | 2212725.000000 | 1440027.000000 | 1362982.000000 | 2731074.000000 | 10160043.000000 | 7509598.000000 | 3869352.000000 | 1201946.000000 | 1264639.000000 | 831601.000000 | 536559.738000 | 737915.640000 | 193550.159000 | 96961.292000 | NaN | NaN | 2812734.000000 | 2608782.000000 | 2111024.000000 | 2478713.000000 | 3055106.000000 | 10446519.000000 | 8936391.000000 | 5306479.000000 | 2503171.000000 | 1929498.000000 | 1473649.000000 | 1298358.203000 | 754771.051000 | 359091.317000 | 226443.835000 | NaN | NaN | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Acset Indonusa Tbk, an integrated construct... | None | Civil Engineering, Construction | 1995.000000 | 1995-01-10 00:00:00 | 1995.000000 | 1995-01-10 | Dec | 2025-09-01 | Construction and Engineering; Commercial Const... | Capital Goods | Construction and Engineering | None | Industrials | Capital Goods | Construction and Engineering | Jakarta | PT Karya Supra Perkasa | None | None | 0.501000 | None | Unclassified | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Karya Supra Perkasa (Current Subsidiary or ... | 0.110000 | 20245000.000000 | 2753.320000 | NaN | NaN | NaN | 2013-06-20 | 2500.000000 | None | None | None | 0.856000 | 0.999000 | 1.150000 | 1.403000 | 0.844000 | 0.946000 | 1.097000 | 1.273000 | 1.796000 | 1.326000 | 1.563000 | 1.484000 | 1.151000 | 1.371000 | 1.504000 | None | None | 0.750000 | 0.870000 | 1.043000 | 1.141000 | 0.623000 | 0.826000 | 0.794000 | 0.937000 | 1.298000 | 0.897000 | 0.948000 | 0.922000 | 0.750000 | 0.948000 | 0.935000 | NaN | NaN | -420134.000000 | -3142.000000 | 209226.000000 | 519658.000000 | -409901.000000 | -538088.000000 | 717200.000000 | 1010675.000000 | 927046.000000 | 391523.000000 | 437650.000000 | 346109.300000 | 79802.352000 | 70129.762000 | 47966.205000 | NaN | NaN | 2491320.000000 | 2166914.000000 | 1606973.000000 | 1808369.000000 | 2210364.000000 | 9456832.000000 | 8120252.000000 | 4717565.000000 | 2092380.000000 | 1590910.000000 | 1214765.000000 | 1061422.848000 | 607779.928000 | 259326.070000 | 143194.646000 | NaN | NaN | 2911454.000000 | 2170056.000000 | 1397747.000000 | 1288711.000000 | 2620265.000000 | 9994920.000000 | 7403052.000000 | 3706890.000000 | 1165334.000000 | 1199387.000000 | 777115.000000 | 715313.548000 | 527977.576000 | 189196.308000 | 95228.441000 | NaN | NaN | 182037.000000 | 138376.000000 | 64156.000000 | 95506.000000 | 93676.000000 | 262326.000000 | 961291.000000 | 349646.000000 | 370809.000000 | 315771.000000 | 309266.000000 | 237777.966000 | 135685.746000 | 67149.636000 | 27583.420000 | NaN | NaN | 5679.000000 | 6666.000000 | 7352.000000 | 6104.000000 | 49626.000000 | 13146.000000 | 12480.000000 | 34602.000000 | 9532.000000 | 4593.000000 | 4094.000000 | 6015.411000 | NaN | 191.925000 | 145.979000 | NaN | NaN | NaN | NaN | 3341.000000 | 22160.000000 | 51397.000000 | 104117.000000 | 63083.000000 | 138669.000000 | 20327.000000 | 56147.000000 | 44362.000000 | 16885.953000 | 5039.545000 | 1967.620000 | 466.667000 | NaN | NaN | 285000.000000 | 40000.000000 | NaN | NaN | 245000.000000 | 817923.000000 | 2656388.000000 | 1092179.000000 | 255000.000000 | 350000.000000 | 45337.000000 | 15961.846000 | 26338.333000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 285000.000000 | 43341.000000 | 22171.000000 | 51569.000000 | 1082372.000000 | 4210330.000000 | 3183252.000000 | 1871951.000000 | 312608.000000 | 440262.000000 | 272965.000000 | 118266.063000 | 125610.468000 | 30522.840000 | 20866.667000 | NaN | NaN | -140993.000000 | 396057.000000 | 670997.000000 | 1115731.000000 | 324032.000000 | 286476.000000 | 1426793.000000 | 1437127.000000 | 1301225.000000 | 664859.000000 | 642048.000000 | 560442.562000 | 218211.313000 | 165541.157000 | 129482.543000 | NaN | NaN | 263754.000000 | 336870.000000 | 429592.000000 | 543775.000000 | 657998.000000 | 745130.000000 | 755129.000000 | 486798.000000 | 370306.000000 | 310061.000000 | 242007.000000 | 220839.892000 | 134582.169000 | 89379.957000 | 80250.650000 | NaN | NaN | 3371726.000000 | 2389679.000000 | 1348817.000000 | 1642358.000000 | 1500270.000000 | 4046981.000000 | 3026009.000000 | 2561089.000000 | 1514363.000000 | 1132494.000000 | 1101969.000000 | 818537.622000 | 554357.668000 | 356616.043000 | 244572.004000 | NaN | NaN | 3172312.000000 | 2349638.000000 | 1036870.000000 | 1494671.000000 | 1204429.000000 | 3947173.000000 | 3725296.000000 | 3026989.000000 | 1794002.000000 | 1356868.000000 | 1350908.000000 | 1014502.030000 | 669905.664000 | 429063.355000 | 303107.369000 | NaN | NaN | -363885.000000 | -190416.000000 | -464514.000000 | -614271.000000 | -1082033.000000 | -430375.000000 | 497768.000000 | 310722.000000 | 182096.000000 | 130916.000000 | 176807.000000 | 146399.359000 | 77033.290000 | 48495.165000 | 42562.582000 | NaN | NaN | -271954.000000 | -88291.000000 | -354321.000000 | -497358.000000 | -957036.000000 | -306643.000000 | 621841.000000 | 397442.000000 | 243054.000000 | 180822.000000 | 234231.000000 | 194155.134000 | 102386.835000 | 68401.903000 | 56844.284000 | NaN | NaN | -363885.000000 | -190416.000000 | -464514.000000 | -614271.000000 | -1082033.000000 | -430375.000000 | 497768.000000 | 310722.000000 | 182096.000000 | 130916.000000 | 176807.000000 | 146399.359000 | 77033.290000 | 48495.165000 | 42562.582000 | NaN | NaN | -542065.000000 | -276638.000000 | -451613.000000 | -693366.000000 | -1340079.000000 | -1131849.000000 | 21419.000000 | 153791.000000 | 67555.000000 | 42222.000000 | 103897.000000 | 99215.342000 | 52233.546000 | 36486.248000 | 27770.706000 | NaN | NaN | -122713.000000 | -101705.000000 | -216864.000000 | 197089.000000 | 1761692.000000 | -341724.000000 | -857235.000000 | -1128265.000000 | -158255.000000 | -24968.000000 | -43287.000000 | -112217.697000 | 44407.666000 | 30836.348000 | -19897.347000 | NaN | NaN | 129384.000000 | 92868.000000 | -265851.000000 | 398257.000000 | -108366.000000 | -40888.000000 | 7535.000000 | 75904.000000 | 78544.000000 | 13831.000000 | 1788.000000 | -19912.184000 | 44619.830000 | 3503.348000 | -7196.844000 | NaN | NaN | 12675160000.000000 | 12675160000.000000 | 12675160000.000000 | 12675160000.000000 | 6425160000.000000 | 700000000.000000 | 700000000.000000 | 700000000.000000 | 700000000.000000 | 500000000.000000 | 500000000.000000 | 500000000.000000 | 400000000.000000 | 400000000.000000 | 400000000.000000 | NaN | NaN | 1090.063760 | 1723.821760 | 1990.000120 | 2661.783600 | 2827.070400 | 679.000000 | 1088.500000 | 1722.000000 | 1974.000000 | 1510.000000 | 1862.500000 | 995.000000 | NaN | NaN | NaN | NaN | NaN | 86.000000 | 136.000000 | 157.000000 | 210.000000 | 440.000000 | 970.000000 | 1555.000000 | 2460.000000 | 2820.000000 | 3020.000000 | 3725.000000 | 1990.000000 | NaN | NaN | NaN | NaN | NaN | 144007.000000 | 439398.000000 | 693168.000000 | 1167300.000000 | 1406404.000000 | 4496806.000000 | 4610045.000000 | 3309078.000000 | 1613833.000000 | 1105121.000000 | 915013.000000 | 678708.625000 | 343821.782000 | 196063.997000 | 150349.210000 | NaN | NaN | 428058.000000 | 298674.000000 | 205806.000000 | 471657.000000 | 73400.000000 | 181766.000000 | 222654.000000 | 215119.000000 | 139215.000000 | 60671.000000 | 49575.000000 | 48718.694000 | 64965.045000 | 20345.215000 | 16841.867000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | None | None | None | None | None | None |
ticker_col = ("exchange_ticker", "STATIC")
mask_valid = df_raw[ticker_col].notna()
df_raw = df_raw[mask_valid].copy()
print("=== Setelah drop baris tanpa exchange_ticker (STATIC) ===")
display(df_raw.head())
print("Jumlah baris (emiten) setelah filter:", len(df_raw))=== Setelah drop baris tanpa exchange_ticker (STATIC) ===
| variable | Entity Name | Entity ID | Exchange | exchange_ticker | Primary Industry | 1st Level Primary Industry | Country / Region Name | Address 1 | Web Address | Business Description | Company Type | Investor Type | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Company Status | Ownership Structure | Activist Investor? Yes/No | Institution Coverage Level | Consolidation Status | Has Current Financials? Yes/No | Has Bank Regulatory Financials? Yes/No | Has Third Party Financials? Yes/No | Long Business Description | Topic Tags | Crunchbase Categories | Year Established | Date Established MM/dd/yyyy | Year Incorporated | Date Incorporated MM/dd/yyyy | Month of Fiscal Year End | Headcount As Of MM/dd/yyyy | Industry Classification | 2nd Level Primary Industry | 3rd Level Primary Industry | 4th Level Primary Industry | Sector | Industry Group | Industry | City | Parent Company Name | Parent Ticker | Parent Exchange: Ticker | Parent Percent Owned (%) | Parent City | Parent Industry | Ultimate Parent Company Name | Ultimate Parent Ticker | Ultimate Parent Exchange: Ticker | Ultimate Parent City | Ultimate Parent Industry | Ultimate Parent Country / Region | All Investors | Percent Owned - All Institutions (%) | Shares Owned - All Institutions (actual) | Market Value - All Institutions (Rp.B) | Percent Owned - Insiders (%) | Shares Owned - Insiders (actual) | Market Value - Insiders (Rp.B) | IPO Date MM/dd/yyyy | IPO Price (Rp.) | Location Type | Data Precision | Data Year | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.B) | Day Close Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| time_flag | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 | 2024 | 2023 | 2022 | 2021 | 2020 | 2019 | 2018 | 2017 | 2016 | 2015 | 2014 | 2013 | 2012 | 2011 | 2010 | 2009 | 2008 |
| 0 | Perusahaan Perseroan (Persero) PT Telekomunika... | 4210975 | IDX | IDX:TLKM | Integrated Telecommunication Services | Technology, Media & Telecommunications | Indonesia | Jl. Japati No. 1 | www.telkom.co.id | Perusahaan Perseroan (Persero) PT Telekomunika... | Public Company | None | 137185000.000000 | 130480000.000000 | 125742000.000000 | 131785000.000000 | 126054000.000000 | 103958000.000000 | 88893000.000000 | 86354000.000000 | 74067000.000000 | 72745000.000000 | 55830000.000000 | 44391000.000000 | 50527000.000000 | 42073000.000000 | 44086000.000000 | 48228553.000000 | 47258399.000000 | 299675000.000000 | 287042000.000000 | 274817000.000000 | 277184000.000000 | 246943000.000000 | 221208000.000000 | 206196000.000000 | 198484000.000000 | 179611000.000000 | 166173000.000000 | 141822000.000000 | 127951000.000000 | 111369000.000000 | 103054000.000000 | 100501000.000000 | 97814160.000000 | 91256250.000000 | Operating Subsidiary | None | No | None | None | Yes | No | No | Perusahaan Perseroan (Persero) PT Telekomunika... | Network Backbone; Optical Infrastructure; Clou... | Digital Media, Information Technology, Telecom... | 1884.000000 | 27/03/1884 01:00:00 | NaN | NaT | Dec | 2025-08-01 | Integrated Telecommunication Services | Telecommunication Services | Integrated Telecommunication Services | None | Communication Services | Telecommunication Services | Diversified Telecommunication Services | Bandung | PT Danantara Asset Management (Persero) | None | None | 0.520900 | Jakarta | Construction and Engineering | Indonesia | None | None | Jakarta | Diversified Support Services | Indonesia | Indonesia (Prior Subsidiary or Operating Unit,... | 21.060000 | 20859696180.000000 | 77180875.866000 | 0.090000 | 85029954.000000 | 314610.829800 | NaT | NaN | None | None | None | 0.822000 | 0.777000 | 0.784000 | 0.886000 | 0.673000 | 0.715000 | 0.935000 | 1.048000 | 1.200000 | 1.353000 | 1.061000 | 1.163000 | 1.160000 | 0.958000 | 0.915000 | 0.602000 | 0.542000 | 0.692000 | 0.643000 | 0.630000 | 0.730000 | 0.510000 | 0.543000 | 0.677000 | 0.818000 | 0.993000 | 1.031000 | 0.795000 | 0.974000 | 0.965000 | 0.703000 | 0.687000 | 0.474000 | 0.420000 | -13687000.000000 | -15955000.000000 | -15162000.000000 | -7854000.000000 | -22590000.000000 | -16647000.000000 | -2993000.000000 | 2185000.000000 | 7939000.000000 | 12499000.000000 | 1976000.000000 | 4638000.000000 | 3866000.000000 | -931000.000000 | -1744000.000000 | -10707101.000000 | -12375841.000000 | 63080000.000000 | 55613000.000000 | 55073000.000000 | 61277000.000000 | 46503000.000000 | 41722000.000000 | 43268000.000000 | 47561000.000000 | 47701000.000000 | 47912000.000000 | 34294000.000000 | 33075000.000000 | 27973000.000000 | 21258000.000000 | 18729000.000000 | 16186024.000000 | 14622310.000000 | 76767000.000000 | 71568000.000000 | 70235000.000000 | 69131000.000000 | 69093000.000000 | 58369000.000000 | 46261000.000000 | 45376000.000000 | 39762000.000000 | 35413000.000000 | 32318000.000000 | 28437000.000000 | 24107000.000000 | 22189000.000000 | 20473000.000000 | 26893125.000000 | 26998151.000000 | 1096000.000000 | 997000.000000 | 1144000.000000 | 779000.000000 | 983000.000000 | 585000.000000 | 717000.000000 | 631000.000000 | 584000.000000 | 528000.000000 | 474000.000000 | 509000.000000 | 579000.000000 | 758000.000000 | 515000.000000 | 435244.000000 | 511950.000000 | 6655000.000000 | 6520000.000000 | 7124000.000000 | 5108000.000000 | 4993000.000000 | 5471000.000000 | 5218000.000000 | 1576000.000000 | 1463000.000000 | 2147000.000000 | 641000.000000 | 1186000.000000 | 849000.000000 | 731000.000000 | 883000.000000 | 722850.000000 | 1875773.000000 | 25518000.000000 | 27773000.000000 | 27331000.000000 | 36319000.000000 | 30561000.000000 | 32293000.000000 | 31410000.000000 | 24964000.000000 | 23015000.000000 | 26229000.000000 | 11525000.000000 | 10410000.000000 | 11803000.000000 | 12644000.000000 | 16246000.000000 | 14249575.000000 | 11444575.000000 | 11525000.000000 | 9650000.000000 | 8191000.000000 | 6682000.000000 | 9934000.000000 | 8705000.000000 | 4043000.000000 | 2289000.000000 | 911000.000000 | 602000.000000 | 1810000.000000 | 432000.000000 | 37000.000000 | 100000.000000 | 56000.000000 | 43850.000000 | 46000.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 76868000.000000 | 68124000.000000 | 62853000.000000 | 69078000.000000 | 65462000.000000 | 52084000.000000 | 44087000.000000 | 35472000.000000 | 31799000.000000 | 34612000.000000 | 23452000.000000 | 20256000.000000 | 19275000.000000 | 17871000.000000 | 21910000.000000 | 21329926.000000 | 17584731.000000 | 162490000.000000 | 156562000.000000 | 149075000.000000 | 145399000.000000 | 120889000.000000 | 117250000.000000 | 117303000.000000 | 112130000.000000 | 105544000.000000 | 93428000.000000 | 85992000.000000 | 77424000.000000 | 66978000.000000 | 60981000.000000 | 56415000.000000 | 49585607.000000 | 43997851.000000 | 207476000.000000 | 203339000.000000 | 193022000.000000 | 183495000.000000 | 179489000.000000 | 156973000.000000 | 143248000.000000 | 130171000.000000 | 114498000.000000 | 103700000.000000 | 94809000.000000 | 86761000.000000 | 77047000.000000 | 74897000.000000 | 75832000.000000 | 76419897.000000 | 71066244.000000 | 60010000.000000 | 58238000.000000 | 54658000.000000 | 53530000.000000 | 50675000.000000 | 60315000.000000 | 61252000.000000 | 53119000.000000 | 47465000.000000 | 42893000.000000 | 35651000.000000 | 32803000.000000 | 29602000.000000 | 27992000.000000 | 25400000.000000 | 23565712.000000 | 22159917.000000 | 149967000.000000 | 149216000.000000 | 147306000.000000 | 143210000.000000 | 136462000.000000 | 135567000.000000 | 130784000.000000 | 128256000.000000 | 116333000.000000 | 102470000.000000 | 89696000.000000 | 82967000.000000 | 77143000.000000 | 71253000.000000 | 69177000.000000 | 67677518.000000 | 64166429.000000 | 43129000.000000 | 44885000.000000 | 45202000.000000 | 43318000.000000 | 43332000.000000 | 42283000.000000 | 38624000.000000 | 43727000.000000 | 40000000.000000 | 33694000.000000 | 29813000.000000 | 28589000.000000 | 26667000.000000 | 23248000.000000 | 22894000.000000 | 23831275.000000 | 23225173.000000 | 67694000.000000 | 70054000.000000 | 71042000.000000 | 75032000.000000 | 65522000.000000 | 64296000.000000 | 58804000.000000 | 63136000.000000 | 58532000.000000 | 52228000.000000 | 46139000.000000 | 43773000.000000 | 40876000.000000 | 37548000.000000 | 37506000.000000 | 37787661.000000 | 35538389.000000 | 43129000.000000 | 44885000.000000 | 45202000.000000 | 43318000.000000 | 43332000.000000 | 42283000.000000 | 38624000.000000 | 43727000.000000 | 40000000.000000 | 33694000.000000 | 29813000.000000 | 28589000.000000 | 26667000.000000 | 23248000.000000 | 22894000.000000 | 23831275.000000 | 23225173.000000 | 30743000.000000 | 32208000.000000 | 27720000.000000 | 34099000.000000 | 29563000.000000 | 27592000.000000 | 26979000.000000 | 32701000.000000 | 29172000.000000 | 23317000.000000 | 21274000.000000 | 20290000.000000 | 18362000.000000 | 15470000.000000 | 15870000.000000 | 16042898.000000 | 14725429.000000 | 61600000.000000 | 60581000.000000 | 73354000.000000 | 68353000.000000 | 65317000.000000 | 54949000.000000 | 45671000.000000 | 49405000.000000 | 47231000.000000 | 43669000.000000 | 37736000.000000 | 36574000.000000 | 27941000.000000 | 30553000.000000 | 27759000.000000 | 29811604.000000 | 24553925.000000 | 4898000.000000 | -2940000.000000 | -6364000.000000 | 17722000.000000 | 2347000.000000 | 803000.000000 | -7706000.000000 | -4622000.000000 | 1650000.000000 | 10445000.000000 | 2976000.000000 | 1578000.000000 | 3484000.000000 | 514000.000000 | 1315000.000000 | 915515.000000 | -3250846.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 99062216600.000000 | 98198216600.000000 | 98175853600.000000 | 97100853600.000000 | 95745344100.000000 | 96931696596.000000 | 98347123896.000000 | 98347123896.000000 | 98347123896.000000 | 268458.606986 | 391295.755570 | 371483.312250 | 400211.355064 | 327895.936946 | 393276.999902 | 371483.312250 | 439836.241704 | 394267.622068 | 304836.025428 | 281273.820564 | 208766.835240 | 173339.797814 | 137673.219768 | 156371.926995 | 185876.064163 | 135790.870326 | 2710.000000 | 3950.000000 | 3750.000000 | 4040.000000 | 3310.000000 | 3970.000000 | 3750.000000 | 4440.000000 | 3980.000000 | 3105.000000 | 2865.000000 | 2150.000000 | 1810.000000 | 1410.000000 | 1590.000000 | 1890.000000 | 1380.000000 | 239358000.000000 | 224686000.000000 | 211928000.000000 | 214477000.000000 | 186351000.000000 | 169334000.000000 | 161390000.000000 | 147602000.000000 | 137343000.000000 | 128040000.000000 | 109444000.000000 | 97680000.000000 | 86253000.000000 | 78852000.000000 | 78325000.000000 | 70915533.000000 | 61582582.000000 | 35027000.000000 | 30430000.000000 | 32883000.000000 | 38740000.000000 | 21818000.000000 | 18620000.000000 | 18590000.000000 | 26638000.000000 | 31001000.000000 | 28566000.000000 | 18036000.000000 | 21256000.000000 | 17428000.000000 | 9995000.000000 | 9490000.000000 | 8164967.000000 | 7156989.000000 | 9442000.000000 | 8731000.000000 | 8302000.000000 | 7506000.000000 | 6846000.000000 | 6446000.000000 | 5032000.000000 | NaN | NaN | NaN | NaN | None | None | None | None | None | None |
| 1 | PT Abadi Nusantara Hijau Investama Tbk (IDX:PACK) | 109420637 | IDX | IDX:PACK | Packaging and Materials: Paper and Plastic | Materials | Indonesia | Jl. Jababeka 2 Block C/11-D | www.flexypack.com | PT Abadi Nusantara Hijau Investama Tbk provide... | Public Company | None | 9131.515000 | 22395.996000 | 37928.982000 | 33821.027000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 66918.151000 | 77129.464000 | 43686.326000 | 36584.088000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | Operating | None | No | None | None | Yes | No | No | PT Abadi Nusantara Hijau Investama Tbk provide... | Packaging Services | None | 2019.000000 | 2019-11-18 00:00:00 | NaN | NaT | Dec | NaT | Packaging and Materials: Paper and Plastic | Packaging and Materials: Paper and Plastic | None | None | Materials | Materials | Containers and Packaging | Bekasi | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Benson Kapital Indonesia (Current Investmen... | 23.240000 | 371188000.000000 | 965088.800000 | 0.000000 | 0.000000 | NaN | 2023-02-06 | 162.000000 | None | None | None | 3.171000 | 1.849000 | 0.397000 | 0.279000 | None | None | None | None | None | None | None | NaN | None | None | None | None | None | 2.532000 | 0.891000 | 0.259000 | 0.116000 | None | None | None | None | None | None | None | NaN | None | None | NaN | NaN | NaN | 18470.909000 | 16160.048000 | -15748.231000 | -13908.977000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 26979.387000 | 35187.194000 | 10379.496000 | 5381.327000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 8508.479000 | 19027.146000 | 26127.727000 | 19290.304000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 5217.791000 | 18152.157000 | 2309.696000 | 1102.312000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 28.664000 | 14.360000 | 4.889000 | 16.514000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2835.577000 | 9595.659000 | 15429.582000 | 17861.356000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 57786.636000 | 54733.468000 | 5757.344000 | 2763.062000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 29813.794000 | 27877.647000 | 26738.757000 | 25626.326000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 40843.331000 | 37002.120000 | 32303.664000 | 30325.181000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 54702.368000 | 52931.736000 | 44150.214000 | 36215.903000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2629.162000 | 5081.433000 | 3948.041000 | -4738.235000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 4465.941000 | 6785.251000 | 5446.039000 | -3352.747000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2629.162000 | 5081.433000 | 3948.041000 | -4738.235000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 899.758000 | 2288.792000 | 2052.793000 | -4690.516000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 8343.775000 | -26767.823000 | 7568.441000 | 1967.910000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | -2271.124000 | 5946.648000 | 539.116000 | -32.844000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1543043824.000000 | 1537579370.000000 | 1229546000.000000 | 571400000.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 948.817070 | 55.351773 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 615.000000 | 36.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 60622.214000 | 64329.128000 | 21186.926000 | 20624.418000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 4249.362000 | 6520.486000 | 573.838000 | 34.722000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 10041.786000 | 9556.735000 | 5626.787000 | 4095.608000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | None | None | None | None | None | None |
| 2 | PT ABM Investama Tbk (IDX:ABMM) | 4980353 | IDX | IDX:ABMM | Coal and Consumable Fuels | Energy and Utilities | Indonesia | Tiara Marga Trakindo I building | www.abm-investama.com | PT ABM Investama Tbk, together with its subsid... | Public Company | None | 20093470.573188 | 21545086.944192 | 21197974.640881 | 9685324.903374 | 9268917.351480 | 8427827.077252 | 8713242.360857 | 11934035.187236 | 12314807.459430 | 14025286.606475 | 11507764.542355 | 8840383.482359 | 10840297.762755 | 7019323.859661 | 3763830.000000 | 3099452.000000 | 4360950.000000 | 33732579.888996 | 33243187.213530 | 30787462.520356 | 14769924.153273 | 11521759.429112 | 11820817.650070 | 12287671.907708 | 14134486.114136 | 14460055.871406 | 16420535.888287 | 14028411.858435 | 14757757.263945 | 12175363.933647 | 10077243.517333 | 4890266.000000 | 4199977.000000 | 3549129.000000 | Operating Subsidiary | None | No | None | None | Yes | No | No | PT ABM Investama Tbk operating in the sectors ... | Clean Energy; Freight Service; Marine Transpor... | Energy, Precious Metals | 2006.000000 | 2006-06-01 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | PT Tiara Marga Trakindo | None | None | 0.535500 | Jakarta | Industrial Conglomerates | PT Tiara Marga Trakindo | None | None | Jakarta | Industrial Conglomerates | Indonesia | PT Tiara Marga Trakindo (Current Subsidiary or... | 0.830000 | 22926501.000000 | 68779.503000 | 7.340000 | 202136500.000000 | 606409.500000 | 2011-12-05 | 3750.000000 | None | None | None | 1.067000 | 0.986000 | 1.145000 | 1.652000 | 1.196000 | 1.204000 | 1.460000 | 1.597000 | 0.792000 | 0.883000 | 0.982000 | 0.996000 | 1.226000 | 1.362000 | 0.893000 | 1.113000 | 0.592000 | 0.905000 | 0.844000 | 0.792000 | 1.341000 | 0.969000 | 0.992000 | 1.275000 | 1.328000 | 0.653000 | 0.751000 | 0.820000 | 0.813000 | 1.007000 | 1.088000 | 0.700000 | 0.879000 | 0.430000 | 540116.972988 | -138088.846896 | 1393425.586718 | 2785291.548858 | 716862.070496 | 610980.039406 | 1477581.268563 | 1720113.072412 | -1048360.536690 | -622368.364089 | -75076.681040 | -19818.463770 | 820288.622790 | 1134634.650895 | -252891.000000 | 203475.000000 | -953832.000000 | 8603535.372060 | 9598638.433986 | 10973792.911797 | 7055017.392177 | 4375629.087504 | 3608327.316374 | 4690972.433270 | 4601979.099836 | 4002183.122758 | 4692284.395771 | 4203180.206570 | 4670518.461795 | 4449825.549909 | 4270489.900468 | 2106194.000000 | 2001334.000000 | 1384649.000000 | 8063418.399072 | 9736727.280882 | 9580367.325079 | 4269725.843319 | 3658767.017008 | 2997347.276968 | 3213391.164707 | 2881866.027424 | 5050543.659448 | 5314652.759860 | 4278256.887610 | 4690336.925565 | 3629536.927119 | 3135855.249573 | 2359085.000000 | 1797859.000000 | 2338481.000000 | 976088.594994 | 821228.155566 | 864077.600244 | 565420.432554 | 525300.327976 | 411458.649184 | 441376.280596 | 406041.471732 | 345572.528640 | 293631.497503 | 310286.074145 | 499804.401930 | 442934.444820 | 414473.810135 | 301356.000000 | 233936.000000 | 241667.000000 | 77287.962918 | 47921.324472 | 68165.694646 | 145468.209915 | 43412.280696 | 124924.565348 | 78087.463339 | 208496.797292 | 304715.857400 | 359407.215314 | 308703.011060 | 234000.429255 | 171860.938376 | 26318.699181 | 11526.000000 | NaN | NaN | 10650442.128438 | 10137708.047964 | 10408734.918322 | 4363219.444311 | 4815187.839656 | 4751102.996552 | 4920968.773924 | 6569397.072784 | 4716889.153288 | 5460877.970722 | 5966403.514665 | 4363521.366120 | 3501077.172306 | 2832092.967225 | 841659.000000 | NaN | NaN | 1725644.443698 | 1070033.159496 | 932422.887775 | 66960.900000 | 557120.000000 | 484330.000000 | NaN | NaN | 579496.717636 | 675321.089765 | 323324.159965 | 740148.951675 | 520493.454485 | 494015.259308 | 292208.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 15716952.113922 | 16033280.386032 | 14577725.216805 | 5488144.613091 | 5924993.624080 | 5400701.282240 | 5215817.182160 | 7009762.688972 | 7134999.566212 | 8304395.601394 | 8039725.363235 | 7610896.646745 | 6493573.658040 | 4929981.462200 | 1924066.000000 | 0.000000 | 0.000000 | 13639109.315808 | 11698100.269338 | 9589487.879475 | 5084599.249899 | 2252842.077632 | 3392990.572818 | 3574429.546851 | 2200450.926900 | 2145248.411976 | 2395249.281812 | 2520647.316080 | 3917459.501190 | 3334980.451288 | 3057919.657672 | 1126436.000000 | 1100525.000000 | -811821.000000 | 13588532.603310 | 13083527.221044 | 10798972.554367 | 6404601.223980 | 5698003.631872 | 5952020.777318 | 6748196.663272 | 8540482.052180 | 9309009.686380 | 10701896.185132 | 8731295.576750 | 8657844.015315 | 6588204.391554 | 4881836.576369 | 2172994.000000 | 1532725.000000 | 1469802.000000 | 16940553.969294 | 16775091.191981 | 13717060.583375 | 9379940.152987 | 7360843.103808 | 6853297.177677 | 8527758.336357 | 7217935.093074 | 6164493.688407 | 7070612.704500 | 6865298.865170 | 6504601.420018 | 6719153.515715 | 5237512.675191 | 3789687.000000 | 3455803.000000 | 3114382.000000 | 19019676.227701 | 22748651.299773 | 21468049.600463 | 14622288.077965 | 8840833.965492 | 8378371.179746 | 11008635.659721 | 9244047.765335 | 7868310.286515 | 8775353.280985 | 8599050.952989 | 8115915.925543 | 8311255.281959 | 6607997.885733 | 4486419.000000 | 3926320.000000 | 3235172.000000 | 1405297.314532 | 4260726.217364 | 6039382.223438 | 4531608.021944 | 359223.936602 | 809280.825757 | 2132909.657311 | 763888.620366 | 695084.433659 | 85615.766681 | -781424.815807 | 327174.492051 | 466344.893687 | 706361.557448 | 271266.000000 | 491429.000000 | -469841.000000 | 3736427.208202 | 6016608.771238 | 7545384.213516 | 5399394.243944 | 1591016.404358 | 1645292.178698 | 2653600.347789 | 1987378.442476 | 2021789.251641 | 1549664.004501 | 1237520.491127 | 1299731.078470 | 1614985.150831 | 1250603.533610 | 705326.000000 | NaN | NaN | 1405297.314532 | 4260726.217364 | 6039382.223438 | 4531608.021944 | 359223.936602 | 809280.825757 | 2132909.657311 | 763888.620366 | 695084.433659 | 85615.766681 | -781424.815807 | 327174.492051 | 466344.893687 | 706361.557448 | 271266.000000 | 491429.000000 | -469841.000000 | 2208286.963756 | 4809124.839499 | 5077732.593725 | 2664181.082945 | -550213.689470 | 55073.183258 | 957348.122798 | 50838.656519 | 91939.054880 | -607731.656746 | -1373861.491832 | 21660.865098 | 116452.543303 | 484431.520834 | 127376.000000 | -11346.000000 | -3913.000000 | 5673781.353325 | 5901279.191712 | 6262674.404548 | 5216623.032704 | 1929513.838089 | 850217.381364 | 1934408.478086 | 2018788.409131 | 1602614.652688 | 2292600.227669 | 1330187.486686 | 1715558.666979 | 1249385.895957 | 53032.416782 | 612476.000000 | 678998.000000 | -4564.000000 | -289365.113950 | -502652.039715 | -232487.728337 | 1831719.103847 | 106265.604590 | -595842.773237 | 47878.928040 | 789290.497254 | -408173.515297 | 107928.957648 | 169184.976267 | -192036.292693 | -708061.937615 | 1190873.399695 | -181536.000000 | 455151.000000 | 7861.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 2753165000.000000 | 825760000.000000 | 826000000.000000 | NaN | 9746.204100 | 9360.761000 | 9030.381200 | 3909.494300 | 2092.405400 | 4212.342450 | 6249.684550 | 6332.279500 | 5588.924950 | 7984.178500 | 8397.153250 | 8259.495000 | 8259.495000 | 10530.856125 | NaN | NaN | NaN | 3540.000000 | 3400.000000 | 3280.000000 | 1420.000000 | 760.000000 | 1530.000000 | 2270.000000 | 2300.000000 | 2030.000000 | 2900.000000 | 3050.000000 | 3000.000000 | 3000.000000 | 3825.000000 | NaN | NaN | NaN | 29356061.429730 | 27731380.655370 | 24167213.096280 | 10572743.862990 | 8177835.701712 | 8793691.855058 | 8790246.729011 | 9210213.615872 | 9280247.978188 | 10699644.883206 | 10560372.679315 | 11528356.147935 | 9828554.109328 | 7987901.119872 | 3050502.000000 | 1100525.000000 | -811821.000000 | 2741781.997248 | 2907757.321224 | 3441719.228344 | 3380709.965967 | 1522129.237896 | 1410416.880994 | 2265170.861332 | 1907200.309552 | 1242565.677740 | 1930012.824268 | 1526592.624775 | 1256331.810840 | 1233004.384532 | 1669222.334305 | 433039.000000 | 614575.000000 | 159424.000000 | 241953.535626 | 216885.738972 | 251403.717345 | 223331.241996 | 236740.734304 | 240865.597962 | 45742.429116 | NaN | NaN | NaN | NaN | None | None | None | None | None | None |
| 3 | PT Ace Oldfields Tbk (IDX:KUAS) | 7649048 | IDX | IDX:KUAS | Building Products | Industrials | Indonesia | Jl. Raya Cileungsi Jonggol | www.aceoldfields.com | PT Ace Oldfields Tbk manufactures and sells pa... | Public Company | None | 115267.049000 | 107904.478000 | 120390.576000 | 122407.148000 | 155435.120000 | 154466.505000 | 161107.849000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 278143.466000 | 264359.356000 | 269542.435000 | 262419.786000 | 206482.511000 | 199903.930000 | 203429.610000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | Operating | None | No | None | None | Yes | No | No | None | None | None | 1989.000000 | 1989-09-18 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Building Products; Hardware Tools and Equipmen... | Capital Goods | Building Products | None | Industrials | Capital Goods | Building Products | Bogor | None | None | None | NA% | None | None | None | None | None | None | None | None | Oldfields Holdings Limited (Prior Investment, ... | NaN | NaN | NaN | 0.870000 | 11225400.000000 | 763.327200 | 2021-10-25 | 195.000000 | None | None | None | 1.997000 | 1.974000 | 1.828000 | 1.814000 | 1.308000 | 1.309000 | 1.310000 | None | None | None | None | NaN | None | None | None | None | None | 1.172000 | 1.072000 | 1.033000 | 1.210000 | 0.699000 | 0.537000 | 0.544000 | None | None | None | None | NaN | None | None | NaN | NaN | NaN | 86839.444000 | 78355.409000 | 74169.643000 | 72372.271000 | 23870.410000 | 22439.724000 | 23621.044000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 173948.142000 | 158762.285000 | 163700.210000 | 161289.971000 | 101493.623000 | 94943.288000 | 99749.278000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 87108.698000 | 80406.876000 | 89530.566000 | 88917.700000 | 77623.213000 | 72503.564000 | 76128.234000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 71327.348000 | 71649.956000 | 70940.942000 | 53641.362000 | 47231.311000 | 56009.830000 | 58268.430000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 147.189000 | 224.208000 | 244.110000 | NaN | NaN | NaN | 35.264000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1634.488000 | 1073.554000 | 1449.214000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 62574.148000 | 60824.332000 | 68829.023000 | 70330.607000 | 57947.634000 | 58677.517000 | 58922.593000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 89651.801000 | 87684.384000 | 99772.844000 | 100011.914000 | 133144.255000 | 138006.077000 | 141918.039000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 162876.417000 | 156454.878000 | 149151.859000 | 140012.638000 | 51047.391000 | 45437.426000 | 42321.761000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 100287.643000 | 101849.807000 | 102292.856000 | 97649.202000 | 101747.189000 | 102175.913000 | 101709.941000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 130570.138000 | 114707.655000 | 112090.876000 | 106484.602000 | 103949.885000 | 107761.935000 | 113631.570000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 184692.659000 | 161660.709000 | 153828.141000 | 139351.625000 | 131083.726000 | 130073.784000 | 136605.549000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 20523.971000 | 18341.355000 | 15142.784000 | 15351.764000 | 12508.497000 | 9495.797000 | 9791.271000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 24221.391000 | 22206.902000 | 18812.802000 | 18288.391000 | 14108.647000 | 10873.891000 | 11133.338000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 20523.971000 | 18341.355000 | 15142.784000 | 15351.764000 | 12508.497000 | 9495.797000 | 9791.271000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 11300.748000 | 9505.923000 | 7864.938000 | 6178.641000 | 5750.754000 | 3591.997000 | 2840.922000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 5509.571000 | 6666.933000 | -14257.143000 | -4560.385000 | 11269.340000 | 7629.488000 | 5507.511000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1390.124000 | -9025.851000 | -22833.804000 | 46687.897000 | 4214.702000 | 449.762000 | 1393.988000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1292808150.000000 | 1292808150.000000 | 1292808150.000000 | 1292570540.000000 | 630000000.000000 | 902570540.000000 | 902570540.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 67.226024 | 64.640407 | 71.091653 | 93.065079 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 52.000000 | 50.000000 | 55.000000 | 72.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 252528.218000 | 244139.262000 | 248924.703000 | 240024.552000 | 184191.645000 | 183443.502000 | 184239.800000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 26195.870000 | 24805.746000 | 33831.597000 | 56665.401000 | 9977.504000 | 5762.802000 | 5313.040000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | None | None | None | None | None | None |
| 4 | PT Acset Indonusa Tbk (IDX:ACST) | 4990054 | IDX | IDX:ACST | Construction and Engineering | Industrials | Indonesia | ACSET Building | www.acset.co | PT Acset Indonusa Tbk provides construction an... | Public Company | None | 2953727.000000 | 2212725.000000 | 1440027.000000 | 1362982.000000 | 2731074.000000 | 10160043.000000 | 7509598.000000 | 3869352.000000 | 1201946.000000 | 1264639.000000 | 831601.000000 | 536559.738000 | 737915.640000 | 193550.159000 | 96961.292000 | NaN | NaN | 2812734.000000 | 2608782.000000 | 2111024.000000 | 2478713.000000 | 3055106.000000 | 10446519.000000 | 8936391.000000 | 5306479.000000 | 2503171.000000 | 1929498.000000 | 1473649.000000 | 1298358.203000 | 754771.051000 | 359091.317000 | 226443.835000 | NaN | NaN | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Acset Indonusa Tbk, an integrated construct... | None | Civil Engineering, Construction | 1995.000000 | 1995-01-10 00:00:00 | 1995.000000 | 1995-01-10 | Dec | 2025-09-01 | Construction and Engineering; Commercial Const... | Capital Goods | Construction and Engineering | None | Industrials | Capital Goods | Construction and Engineering | Jakarta | PT Karya Supra Perkasa | None | None | 0.501000 | None | Unclassified | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Karya Supra Perkasa (Current Subsidiary or ... | 0.110000 | 20245000.000000 | 2753.320000 | NaN | NaN | NaN | 2013-06-20 | 2500.000000 | None | None | None | 0.856000 | 0.999000 | 1.150000 | 1.403000 | 0.844000 | 0.946000 | 1.097000 | 1.273000 | 1.796000 | 1.326000 | 1.563000 | 1.484000 | 1.151000 | 1.371000 | 1.504000 | None | None | 0.750000 | 0.870000 | 1.043000 | 1.141000 | 0.623000 | 0.826000 | 0.794000 | 0.937000 | 1.298000 | 0.897000 | 0.948000 | 0.922000 | 0.750000 | 0.948000 | 0.935000 | NaN | NaN | -420134.000000 | -3142.000000 | 209226.000000 | 519658.000000 | -409901.000000 | -538088.000000 | 717200.000000 | 1010675.000000 | 927046.000000 | 391523.000000 | 437650.000000 | 346109.300000 | 79802.352000 | 70129.762000 | 47966.205000 | NaN | NaN | 2491320.000000 | 2166914.000000 | 1606973.000000 | 1808369.000000 | 2210364.000000 | 9456832.000000 | 8120252.000000 | 4717565.000000 | 2092380.000000 | 1590910.000000 | 1214765.000000 | 1061422.848000 | 607779.928000 | 259326.070000 | 143194.646000 | NaN | NaN | 2911454.000000 | 2170056.000000 | 1397747.000000 | 1288711.000000 | 2620265.000000 | 9994920.000000 | 7403052.000000 | 3706890.000000 | 1165334.000000 | 1199387.000000 | 777115.000000 | 715313.548000 | 527977.576000 | 189196.308000 | 95228.441000 | NaN | NaN | 182037.000000 | 138376.000000 | 64156.000000 | 95506.000000 | 93676.000000 | 262326.000000 | 961291.000000 | 349646.000000 | 370809.000000 | 315771.000000 | 309266.000000 | 237777.966000 | 135685.746000 | 67149.636000 | 27583.420000 | NaN | NaN | 5679.000000 | 6666.000000 | 7352.000000 | 6104.000000 | 49626.000000 | 13146.000000 | 12480.000000 | 34602.000000 | 9532.000000 | 4593.000000 | 4094.000000 | 6015.411000 | NaN | 191.925000 | 145.979000 | NaN | NaN | NaN | NaN | 3341.000000 | 22160.000000 | 51397.000000 | 104117.000000 | 63083.000000 | 138669.000000 | 20327.000000 | 56147.000000 | 44362.000000 | 16885.953000 | 5039.545000 | 1967.620000 | 466.667000 | NaN | NaN | 285000.000000 | 40000.000000 | NaN | NaN | 245000.000000 | 817923.000000 | 2656388.000000 | 1092179.000000 | 255000.000000 | 350000.000000 | 45337.000000 | 15961.846000 | 26338.333000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 285000.000000 | 43341.000000 | 22171.000000 | 51569.000000 | 1082372.000000 | 4210330.000000 | 3183252.000000 | 1871951.000000 | 312608.000000 | 440262.000000 | 272965.000000 | 118266.063000 | 125610.468000 | 30522.840000 | 20866.667000 | NaN | NaN | -140993.000000 | 396057.000000 | 670997.000000 | 1115731.000000 | 324032.000000 | 286476.000000 | 1426793.000000 | 1437127.000000 | 1301225.000000 | 664859.000000 | 642048.000000 | 560442.562000 | 218211.313000 | 165541.157000 | 129482.543000 | NaN | NaN | 263754.000000 | 336870.000000 | 429592.000000 | 543775.000000 | 657998.000000 | 745130.000000 | 755129.000000 | 486798.000000 | 370306.000000 | 310061.000000 | 242007.000000 | 220839.892000 | 134582.169000 | 89379.957000 | 80250.650000 | NaN | NaN | 3371726.000000 | 2389679.000000 | 1348817.000000 | 1642358.000000 | 1500270.000000 | 4046981.000000 | 3026009.000000 | 2561089.000000 | 1514363.000000 | 1132494.000000 | 1101969.000000 | 818537.622000 | 554357.668000 | 356616.043000 | 244572.004000 | NaN | NaN | 3172312.000000 | 2349638.000000 | 1036870.000000 | 1494671.000000 | 1204429.000000 | 3947173.000000 | 3725296.000000 | 3026989.000000 | 1794002.000000 | 1356868.000000 | 1350908.000000 | 1014502.030000 | 669905.664000 | 429063.355000 | 303107.369000 | NaN | NaN | -363885.000000 | -190416.000000 | -464514.000000 | -614271.000000 | -1082033.000000 | -430375.000000 | 497768.000000 | 310722.000000 | 182096.000000 | 130916.000000 | 176807.000000 | 146399.359000 | 77033.290000 | 48495.165000 | 42562.582000 | NaN | NaN | -271954.000000 | -88291.000000 | -354321.000000 | -497358.000000 | -957036.000000 | -306643.000000 | 621841.000000 | 397442.000000 | 243054.000000 | 180822.000000 | 234231.000000 | 194155.134000 | 102386.835000 | 68401.903000 | 56844.284000 | NaN | NaN | -363885.000000 | -190416.000000 | -464514.000000 | -614271.000000 | -1082033.000000 | -430375.000000 | 497768.000000 | 310722.000000 | 182096.000000 | 130916.000000 | 176807.000000 | 146399.359000 | 77033.290000 | 48495.165000 | 42562.582000 | NaN | NaN | -542065.000000 | -276638.000000 | -451613.000000 | -693366.000000 | -1340079.000000 | -1131849.000000 | 21419.000000 | 153791.000000 | 67555.000000 | 42222.000000 | 103897.000000 | 99215.342000 | 52233.546000 | 36486.248000 | 27770.706000 | NaN | NaN | -122713.000000 | -101705.000000 | -216864.000000 | 197089.000000 | 1761692.000000 | -341724.000000 | -857235.000000 | -1128265.000000 | -158255.000000 | -24968.000000 | -43287.000000 | -112217.697000 | 44407.666000 | 30836.348000 | -19897.347000 | NaN | NaN | 129384.000000 | 92868.000000 | -265851.000000 | 398257.000000 | -108366.000000 | -40888.000000 | 7535.000000 | 75904.000000 | 78544.000000 | 13831.000000 | 1788.000000 | -19912.184000 | 44619.830000 | 3503.348000 | -7196.844000 | NaN | NaN | 12675160000.000000 | 12675160000.000000 | 12675160000.000000 | 12675160000.000000 | 6425160000.000000 | 700000000.000000 | 700000000.000000 | 700000000.000000 | 700000000.000000 | 500000000.000000 | 500000000.000000 | 500000000.000000 | 400000000.000000 | 400000000.000000 | 400000000.000000 | NaN | NaN | 1090.063760 | 1723.821760 | 1990.000120 | 2661.783600 | 2827.070400 | 679.000000 | 1088.500000 | 1722.000000 | 1974.000000 | 1510.000000 | 1862.500000 | 995.000000 | NaN | NaN | NaN | NaN | NaN | 86.000000 | 136.000000 | 157.000000 | 210.000000 | 440.000000 | 970.000000 | 1555.000000 | 2460.000000 | 2820.000000 | 3020.000000 | 3725.000000 | 1990.000000 | NaN | NaN | NaN | NaN | NaN | 144007.000000 | 439398.000000 | 693168.000000 | 1167300.000000 | 1406404.000000 | 4496806.000000 | 4610045.000000 | 3309078.000000 | 1613833.000000 | 1105121.000000 | 915013.000000 | 678708.625000 | 343821.782000 | 196063.997000 | 150349.210000 | NaN | NaN | 428058.000000 | 298674.000000 | 205806.000000 | 471657.000000 | 73400.000000 | 181766.000000 | 222654.000000 | 215119.000000 | 139215.000000 | 60671.000000 | 49575.000000 | 48718.694000 | 64965.045000 | 20345.215000 | 16841.867000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | None | None | None | None | None | None |
Jumlah baris (emiten) setelah filter: 924
# mask STATIC
static_mask = df_raw.columns.get_level_values("time_flag") == "STATIC"
df_static = df_raw.loc[:, static_mask].copy()
print("=== df_static (masih 2-level header, semua STATIC) ===")
display(df_static.head())
# flatten
# ambil level 'STATIC' → tinggal nama variable saja
df_static = df_static.xs("STATIC", axis=1, level="time_flag")
print("=== df_static setelah flatten (1 header, 1 baris per emiten) ===")
display(df_static.head())=== df_static (masih 2-level header, semua STATIC) ===
| variable | Entity Name | Entity ID | Exchange | exchange_ticker | Primary Industry | 1st Level Primary Industry | Country / Region Name | Address 1 | Web Address | Business Description | Company Type | Investor Type | Company Status | Ownership Structure | Activist Investor? Yes/No | Institution Coverage Level | Consolidation Status | Has Current Financials? Yes/No | Has Bank Regulatory Financials? Yes/No | Has Third Party Financials? Yes/No | Long Business Description | Topic Tags | Crunchbase Categories | Year Established | Date Established MM/dd/yyyy | Year Incorporated | Date Incorporated MM/dd/yyyy | Month of Fiscal Year End | Headcount As Of MM/dd/yyyy | Industry Classification | 2nd Level Primary Industry | 3rd Level Primary Industry | 4th Level Primary Industry | Sector | Industry Group | Industry | City | Parent Company Name | Parent Ticker | Parent Exchange: Ticker | Parent Percent Owned (%) | Parent City | Parent Industry | Ultimate Parent Company Name | Ultimate Parent Ticker | Ultimate Parent Exchange: Ticker | Ultimate Parent City | Ultimate Parent Industry | Ultimate Parent Country / Region | All Investors | Percent Owned - All Institutions (%) | Shares Owned - All Institutions (actual) | Market Value - All Institutions (Rp.B) | Percent Owned - Insiders (%) | Shares Owned - Insiders (actual) | Market Value - Insiders (Rp.B) | IPO Date MM/dd/yyyy | IPO Price (Rp.) | Location Type | Data Precision | Data Year |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| time_flag | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC | STATIC |
| 0 | Perusahaan Perseroan (Persero) PT Telekomunika... | 4210975 | IDX | IDX:TLKM | Integrated Telecommunication Services | Technology, Media & Telecommunications | Indonesia | Jl. Japati No. 1 | www.telkom.co.id | Perusahaan Perseroan (Persero) PT Telekomunika... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | Perusahaan Perseroan (Persero) PT Telekomunika... | Network Backbone; Optical Infrastructure; Clou... | Digital Media, Information Technology, Telecom... | 1884.000000 | 27/03/1884 01:00:00 | NaN | NaT | Dec | 2025-08-01 | Integrated Telecommunication Services | Telecommunication Services | Integrated Telecommunication Services | None | Communication Services | Telecommunication Services | Diversified Telecommunication Services | Bandung | PT Danantara Asset Management (Persero) | None | None | 0.520900 | Jakarta | Construction and Engineering | Indonesia | None | None | Jakarta | Diversified Support Services | Indonesia | Indonesia (Prior Subsidiary or Operating Unit,... | 21.060000 | 20859696180.000000 | 77180875.866000 | 0.090000 | 85029954.000000 | 314610.829800 | NaT | NaN | None | None | None |
| 1 | PT Abadi Nusantara Hijau Investama Tbk (IDX:PACK) | 109420637 | IDX | IDX:PACK | Packaging and Materials: Paper and Plastic | Materials | Indonesia | Jl. Jababeka 2 Block C/11-D | www.flexypack.com | PT Abadi Nusantara Hijau Investama Tbk provide... | Public Company | None | Operating | None | No | None | None | Yes | No | No | PT Abadi Nusantara Hijau Investama Tbk provide... | Packaging Services | None | 2019.000000 | 2019-11-18 00:00:00 | NaN | NaT | Dec | NaT | Packaging and Materials: Paper and Plastic | Packaging and Materials: Paper and Plastic | None | None | Materials | Materials | Containers and Packaging | Bekasi | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Benson Kapital Indonesia (Current Investmen... | 23.240000 | 371188000.000000 | 965088.800000 | 0.000000 | 0.000000 | NaN | 2023-02-06 | 162.000000 | None | None | None |
| 2 | PT ABM Investama Tbk (IDX:ABMM) | 4980353 | IDX | IDX:ABMM | Coal and Consumable Fuels | Energy and Utilities | Indonesia | Tiara Marga Trakindo I building | www.abm-investama.com | PT ABM Investama Tbk, together with its subsid... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT ABM Investama Tbk operating in the sectors ... | Clean Energy; Freight Service; Marine Transpor... | Energy, Precious Metals | 2006.000000 | 2006-06-01 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | PT Tiara Marga Trakindo | None | None | 0.535500 | Jakarta | Industrial Conglomerates | PT Tiara Marga Trakindo | None | None | Jakarta | Industrial Conglomerates | Indonesia | PT Tiara Marga Trakindo (Current Subsidiary or... | 0.830000 | 22926501.000000 | 68779.503000 | 7.340000 | 202136500.000000 | 606409.500000 | 2011-12-05 | 3750.000000 | None | None | None |
| 3 | PT Ace Oldfields Tbk (IDX:KUAS) | 7649048 | IDX | IDX:KUAS | Building Products | Industrials | Indonesia | Jl. Raya Cileungsi Jonggol | www.aceoldfields.com | PT Ace Oldfields Tbk manufactures and sells pa... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | None | None | 1989.000000 | 1989-09-18 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Building Products; Hardware Tools and Equipmen... | Capital Goods | Building Products | None | Industrials | Capital Goods | Building Products | Bogor | None | None | None | NA% | None | None | None | None | None | None | None | None | Oldfields Holdings Limited (Prior Investment, ... | NaN | NaN | NaN | 0.870000 | 11225400.000000 | 763.327200 | 2021-10-25 | 195.000000 | None | None | None |
| 4 | PT Acset Indonusa Tbk (IDX:ACST) | 4990054 | IDX | IDX:ACST | Construction and Engineering | Industrials | Indonesia | ACSET Building | www.acset.co | PT Acset Indonusa Tbk provides construction an... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Acset Indonusa Tbk, an integrated construct... | None | Civil Engineering, Construction | 1995.000000 | 1995-01-10 00:00:00 | 1995.000000 | 1995-01-10 | Dec | 2025-09-01 | Construction and Engineering; Commercial Const... | Capital Goods | Construction and Engineering | None | Industrials | Capital Goods | Construction and Engineering | Jakarta | PT Karya Supra Perkasa | None | None | 0.501000 | None | Unclassified | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Karya Supra Perkasa (Current Subsidiary or ... | 0.110000 | 20245000.000000 | 2753.320000 | NaN | NaN | NaN | 2013-06-20 | 2500.000000 | None | None | None |
=== df_static setelah flatten (1 header, 1 baris per emiten) ===
| variable | Entity Name | Entity ID | Exchange | exchange_ticker | Primary Industry | 1st Level Primary Industry | Country / Region Name | Address 1 | Web Address | Business Description | Company Type | Investor Type | Company Status | Ownership Structure | Activist Investor? Yes/No | Institution Coverage Level | Consolidation Status | Has Current Financials? Yes/No | Has Bank Regulatory Financials? Yes/No | Has Third Party Financials? Yes/No | Long Business Description | Topic Tags | Crunchbase Categories | Year Established | Date Established MM/dd/yyyy | Year Incorporated | Date Incorporated MM/dd/yyyy | Month of Fiscal Year End | Headcount As Of MM/dd/yyyy | Industry Classification | 2nd Level Primary Industry | 3rd Level Primary Industry | 4th Level Primary Industry | Sector | Industry Group | Industry | City | Parent Company Name | Parent Ticker | Parent Exchange: Ticker | Parent Percent Owned (%) | Parent City | Parent Industry | Ultimate Parent Company Name | Ultimate Parent Ticker | Ultimate Parent Exchange: Ticker | Ultimate Parent City | Ultimate Parent Industry | Ultimate Parent Country / Region | All Investors | Percent Owned - All Institutions (%) | Shares Owned - All Institutions (actual) | Market Value - All Institutions (Rp.B) | Percent Owned - Insiders (%) | Shares Owned - Insiders (actual) | Market Value - Insiders (Rp.B) | IPO Date MM/dd/yyyy | IPO Price (Rp.) | Location Type | Data Precision | Data Year |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Perusahaan Perseroan (Persero) PT Telekomunika... | 4210975 | IDX | IDX:TLKM | Integrated Telecommunication Services | Technology, Media & Telecommunications | Indonesia | Jl. Japati No. 1 | www.telkom.co.id | Perusahaan Perseroan (Persero) PT Telekomunika... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | Perusahaan Perseroan (Persero) PT Telekomunika... | Network Backbone; Optical Infrastructure; Clou... | Digital Media, Information Technology, Telecom... | 1884.000000 | 27/03/1884 01:00:00 | NaN | NaT | Dec | 2025-08-01 | Integrated Telecommunication Services | Telecommunication Services | Integrated Telecommunication Services | None | Communication Services | Telecommunication Services | Diversified Telecommunication Services | Bandung | PT Danantara Asset Management (Persero) | None | None | 0.520900 | Jakarta | Construction and Engineering | Indonesia | None | None | Jakarta | Diversified Support Services | Indonesia | Indonesia (Prior Subsidiary or Operating Unit,... | 21.060000 | 20859696180.000000 | 77180875.866000 | 0.090000 | 85029954.000000 | 314610.829800 | NaT | NaN | None | None | None |
| 1 | PT Abadi Nusantara Hijau Investama Tbk (IDX:PACK) | 109420637 | IDX | IDX:PACK | Packaging and Materials: Paper and Plastic | Materials | Indonesia | Jl. Jababeka 2 Block C/11-D | www.flexypack.com | PT Abadi Nusantara Hijau Investama Tbk provide... | Public Company | None | Operating | None | No | None | None | Yes | No | No | PT Abadi Nusantara Hijau Investama Tbk provide... | Packaging Services | None | 2019.000000 | 2019-11-18 00:00:00 | NaN | NaT | Dec | NaT | Packaging and Materials: Paper and Plastic | Packaging and Materials: Paper and Plastic | None | None | Materials | Materials | Containers and Packaging | Bekasi | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Benson Kapital Indonesia (Current Investmen... | 23.240000 | 371188000.000000 | 965088.800000 | 0.000000 | 0.000000 | NaN | 2023-02-06 | 162.000000 | None | None | None |
| 2 | PT ABM Investama Tbk (IDX:ABMM) | 4980353 | IDX | IDX:ABMM | Coal and Consumable Fuels | Energy and Utilities | Indonesia | Tiara Marga Trakindo I building | www.abm-investama.com | PT ABM Investama Tbk, together with its subsid... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT ABM Investama Tbk operating in the sectors ... | Clean Energy; Freight Service; Marine Transpor... | Energy, Precious Metals | 2006.000000 | 2006-06-01 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | PT Tiara Marga Trakindo | None | None | 0.535500 | Jakarta | Industrial Conglomerates | PT Tiara Marga Trakindo | None | None | Jakarta | Industrial Conglomerates | Indonesia | PT Tiara Marga Trakindo (Current Subsidiary or... | 0.830000 | 22926501.000000 | 68779.503000 | 7.340000 | 202136500.000000 | 606409.500000 | 2011-12-05 | 3750.000000 | None | None | None |
| 3 | PT Ace Oldfields Tbk (IDX:KUAS) | 7649048 | IDX | IDX:KUAS | Building Products | Industrials | Indonesia | Jl. Raya Cileungsi Jonggol | www.aceoldfields.com | PT Ace Oldfields Tbk manufactures and sells pa... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | None | None | 1989.000000 | 1989-09-18 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Building Products; Hardware Tools and Equipmen... | Capital Goods | Building Products | None | Industrials | Capital Goods | Building Products | Bogor | None | None | None | NA% | None | None | None | None | None | None | None | None | Oldfields Holdings Limited (Prior Investment, ... | NaN | NaN | NaN | 0.870000 | 11225400.000000 | 763.327200 | 2021-10-25 | 195.000000 | None | None | None |
| 4 | PT Acset Indonusa Tbk (IDX:ACST) | 4990054 | IDX | IDX:ACST | Construction and Engineering | Industrials | Indonesia | ACSET Building | www.acset.co | PT Acset Indonusa Tbk provides construction an... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Acset Indonusa Tbk, an integrated construct... | None | Civil Engineering, Construction | 1995.000000 | 1995-01-10 00:00:00 | 1995.000000 | 1995-01-10 | Dec | 2025-09-01 | Construction and Engineering; Commercial Const... | Capital Goods | Construction and Engineering | None | Industrials | Capital Goods | Construction and Engineering | Jakarta | PT Karya Supra Perkasa | None | None | 0.501000 | None | Unclassified | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Karya Supra Perkasa (Current Subsidiary or ... | 0.110000 | 20245000.000000 | 2753.320000 | NaN | NaN | NaN | 2013-06-20 | 2500.000000 | None | None | None |
all_flags = df_raw.columns.get_level_values("time_flag")
years = sorted({
f for f in all_flags
if (str(f) != "STATIC") and pd.notna(f)
})
print("=== Daftar Year unik (String) ===")
print(years)=== Daftar Year unik (String) ===
['2008', '2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016', '2017', '2018', '2019', '2020', '2021', '2022', '2023', '2024']
panel_frames = []
for y in years:
# Ambil semua kolom yang time_flag = y (misal 2024)
df_y = df_raw.xs(y, axis=1, level="time_flag")
# Gabung dengan kolom STATIC (company_name, industry, dll)
tmp = pd.concat([df_static, df_y], axis=1)
# Tambahkan kolom Year
tmp = tmp.copy()
# Kalau mau int:
tmp["Year"] = pd.to_numeric(y, errors="ignore")
panel_frames.append(tmp)
# Gabungkan semua year jadi 1 DataFrame panjang
df_final = pd.concat(panel_frames, ignore_index=True)
print("=== df_final (panel awal, sebelum filter) ===")
display(df_final.head())
print("Shape df_final:", df_final.shape)=== df_final (panel awal, sebelum filter) ===
/tmp/ipykernel_108742/1820188604.py:13: FutureWarning:
errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead
/tmp/ipykernel_108742/1820188604.py:13: FutureWarning:
errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead
/tmp/ipykernel_108742/1820188604.py:13: FutureWarning:
errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead
/tmp/ipykernel_108742/1820188604.py:13: FutureWarning:
errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead
/tmp/ipykernel_108742/1820188604.py:13: FutureWarning:
errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead
/tmp/ipykernel_108742/1820188604.py:13: FutureWarning:
errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead
/tmp/ipykernel_108742/1820188604.py:13: FutureWarning:
errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead
/tmp/ipykernel_108742/1820188604.py:13: FutureWarning:
errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead
/tmp/ipykernel_108742/1820188604.py:13: FutureWarning:
errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead
/tmp/ipykernel_108742/1820188604.py:13: FutureWarning:
errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead
/tmp/ipykernel_108742/1820188604.py:13: FutureWarning:
errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead
/tmp/ipykernel_108742/1820188604.py:13: FutureWarning:
errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead
/tmp/ipykernel_108742/1820188604.py:13: FutureWarning:
errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead
/tmp/ipykernel_108742/1820188604.py:13: FutureWarning:
errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead
/tmp/ipykernel_108742/1820188604.py:13: FutureWarning:
errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead
/tmp/ipykernel_108742/1820188604.py:13: FutureWarning:
errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead
/tmp/ipykernel_108742/1820188604.py:13: FutureWarning:
errors='ignore' is deprecated and will raise in a future version. Use to_numeric without passing `errors` and catch exceptions explicitly instead
/tmp/ipykernel_108742/1820188604.py:18: FutureWarning:
The behavior of DataFrame concatenation with empty or all-NA entries is deprecated. In a future version, this will no longer exclude empty or all-NA columns when determining the result dtypes. To retain the old behavior, exclude the relevant entries before the concat operation.
| variable | Entity Name | Entity ID | Exchange | exchange_ticker | Primary Industry | 1st Level Primary Industry | Country / Region Name | Address 1 | Web Address | Business Description | Company Type | Investor Type | Company Status | Ownership Structure | Activist Investor? Yes/No | Institution Coverage Level | Consolidation Status | Has Current Financials? Yes/No | Has Bank Regulatory Financials? Yes/No | Has Third Party Financials? Yes/No | Long Business Description | Topic Tags | Crunchbase Categories | Year Established | Date Established MM/dd/yyyy | Year Incorporated | Date Incorporated MM/dd/yyyy | Month of Fiscal Year End | Headcount As Of MM/dd/yyyy | Industry Classification | 2nd Level Primary Industry | 3rd Level Primary Industry | 4th Level Primary Industry | Sector | Industry Group | Industry | City | Parent Company Name | Parent Ticker | Parent Exchange: Ticker | Parent Percent Owned (%) | Parent City | Parent Industry | Ultimate Parent Company Name | Ultimate Parent Ticker | Ultimate Parent Exchange: Ticker | Ultimate Parent City | Ultimate Parent Industry | Ultimate Parent Country / Region | All Investors | Percent Owned - All Institutions (%) | Shares Owned - All Institutions (actual) | Market Value - All Institutions (Rp.B) | Percent Owned - Insiders (%) | Shares Owned - Insiders (actual) | Market Value - Insiders (Rp.B) | IPO Date MM/dd/yyyy | IPO Price (Rp.) | Location Type | Data Precision | Data Year | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.B) | Day Close Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | Year |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Perusahaan Perseroan (Persero) PT Telekomunika... | 4210975 | IDX | IDX:TLKM | Integrated Telecommunication Services | Technology, Media & Telecommunications | Indonesia | Jl. Japati No. 1 | www.telkom.co.id | Perusahaan Perseroan (Persero) PT Telekomunika... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | Perusahaan Perseroan (Persero) PT Telekomunika... | Network Backbone; Optical Infrastructure; Clou... | Digital Media, Information Technology, Telecom... | 1884.000000 | 27/03/1884 01:00:00 | NaN | NaT | Dec | 2025-08-01 | Integrated Telecommunication Services | Telecommunication Services | Integrated Telecommunication Services | None | Communication Services | Telecommunication Services | Diversified Telecommunication Services | Bandung | PT Danantara Asset Management (Persero) | None | None | 0.520900 | Jakarta | Construction and Engineering | Indonesia | None | None | Jakarta | Diversified Support Services | Indonesia | Indonesia (Prior Subsidiary or Operating Unit,... | 21.060000 | 20859696180.000000 | 77180875.866000 | 0.090000 | 85029954.000000 | 314610.829800 | NaT | NaN | None | None | None | 47258399.000000 | 91256250.000000 | 0.542000 | 0.420000 | -12375841.000000 | 14622310.000000 | 26998151.000000 | 511950.000000 | 1875773.000000 | 11444575.000000 | 46000.000000 | NaN | 17584731.000000 | 43997851.000000 | 71066244.000000 | 22159917.000000 | 64166429.000000 | 23225173.000000 | 35538389.000000 | 23225173.000000 | 14725429.000000 | 24553925.000000 | -3250846.000000 | 98347123896.000000 | 135790.870326 | 1380.000000 | 61582582.000000 | 7156989.000000 | NaN | 2008 |
| 1 | PT Abadi Nusantara Hijau Investama Tbk (IDX:PACK) | 109420637 | IDX | IDX:PACK | Packaging and Materials: Paper and Plastic | Materials | Indonesia | Jl. Jababeka 2 Block C/11-D | www.flexypack.com | PT Abadi Nusantara Hijau Investama Tbk provide... | Public Company | None | Operating | None | No | None | None | Yes | No | No | PT Abadi Nusantara Hijau Investama Tbk provide... | Packaging Services | None | 2019.000000 | 2019-11-18 00:00:00 | NaN | NaT | Dec | NaT | Packaging and Materials: Paper and Plastic | Packaging and Materials: Paper and Plastic | None | None | Materials | Materials | Containers and Packaging | Bekasi | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Benson Kapital Indonesia (Current Investmen... | 23.240000 | 371188000.000000 | 965088.800000 | 0.000000 | 0.000000 | NaN | 2023-02-06 | 162.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
| 2 | PT ABM Investama Tbk (IDX:ABMM) | 4980353 | IDX | IDX:ABMM | Coal and Consumable Fuels | Energy and Utilities | Indonesia | Tiara Marga Trakindo I building | www.abm-investama.com | PT ABM Investama Tbk, together with its subsid... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT ABM Investama Tbk operating in the sectors ... | Clean Energy; Freight Service; Marine Transpor... | Energy, Precious Metals | 2006.000000 | 2006-06-01 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | PT Tiara Marga Trakindo | None | None | 0.535500 | Jakarta | Industrial Conglomerates | PT Tiara Marga Trakindo | None | None | Jakarta | Industrial Conglomerates | Indonesia | PT Tiara Marga Trakindo (Current Subsidiary or... | 0.830000 | 22926501.000000 | 68779.503000 | 7.340000 | 202136500.000000 | 606409.500000 | 2011-12-05 | 3750.000000 | None | None | None | 4360950.000000 | 3549129.000000 | 0.592000 | 0.430000 | -953832.000000 | 1384649.000000 | 2338481.000000 | 241667.000000 | NaN | NaN | NaN | NaN | 0.000000 | -811821.000000 | 1469802.000000 | 3114382.000000 | 3235172.000000 | -469841.000000 | NaN | -469841.000000 | -3913.000000 | -4564.000000 | 7861.000000 | NaN | NaN | NaN | -811821.000000 | 159424.000000 | NaN | 2008 |
| 3 | PT Ace Oldfields Tbk (IDX:KUAS) | 7649048 | IDX | IDX:KUAS | Building Products | Industrials | Indonesia | Jl. Raya Cileungsi Jonggol | www.aceoldfields.com | PT Ace Oldfields Tbk manufactures and sells pa... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | None | None | 1989.000000 | 1989-09-18 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Building Products; Hardware Tools and Equipmen... | Capital Goods | Building Products | None | Industrials | Capital Goods | Building Products | Bogor | None | None | None | NA% | None | None | None | None | None | None | None | None | Oldfields Holdings Limited (Prior Investment, ... | NaN | NaN | NaN | 0.870000 | 11225400.000000 | 763.327200 | 2021-10-25 | 195.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
| 4 | PT Acset Indonusa Tbk (IDX:ACST) | 4990054 | IDX | IDX:ACST | Construction and Engineering | Industrials | Indonesia | ACSET Building | www.acset.co | PT Acset Indonusa Tbk provides construction an... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Acset Indonusa Tbk, an integrated construct... | None | Civil Engineering, Construction | 1995.000000 | 1995-01-10 00:00:00 | 1995.000000 | 1995-01-10 | Dec | 2025-09-01 | Construction and Engineering; Commercial Const... | Capital Goods | Construction and Engineering | None | Industrials | Capital Goods | Construction and Engineering | Jakarta | PT Karya Supra Perkasa | None | None | 0.501000 | None | Unclassified | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Karya Supra Perkasa (Current Subsidiary or ... | 0.110000 | 20245000.000000 | 2753.320000 | NaN | NaN | NaN | 2013-06-20 | 2500.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
Shape df_final: (15708, 91)
# Buang baris yang mungkin masih ada NaN di exchange_ticker atau Year
df_final = df_final[
df_final["exchange_ticker"].notna() &
df_final["Year"].notna()
].copy()
print("=== df_final setelah filter ticker & Year non-NaN ===")
display(df_final.head(20))
print("Total baris:", len(df_final))
print("Jumlah emiten unik:", df_final["exchange_ticker"].nunique())
print("Jumlah Year unik:", df_final["Year"].nunique())=== df_final setelah filter ticker & Year non-NaN ===
| variable | Entity Name | Entity ID | Exchange | exchange_ticker | Primary Industry | 1st Level Primary Industry | Country / Region Name | Address 1 | Web Address | Business Description | Company Type | Investor Type | Company Status | Ownership Structure | Activist Investor? Yes/No | Institution Coverage Level | Consolidation Status | Has Current Financials? Yes/No | Has Bank Regulatory Financials? Yes/No | Has Third Party Financials? Yes/No | Long Business Description | Topic Tags | Crunchbase Categories | Year Established | Date Established MM/dd/yyyy | Year Incorporated | Date Incorporated MM/dd/yyyy | Month of Fiscal Year End | Headcount As Of MM/dd/yyyy | Industry Classification | 2nd Level Primary Industry | 3rd Level Primary Industry | 4th Level Primary Industry | Sector | Industry Group | Industry | City | Parent Company Name | Parent Ticker | Parent Exchange: Ticker | Parent Percent Owned (%) | Parent City | Parent Industry | Ultimate Parent Company Name | Ultimate Parent Ticker | Ultimate Parent Exchange: Ticker | Ultimate Parent City | Ultimate Parent Industry | Ultimate Parent Country / Region | All Investors | Percent Owned - All Institutions (%) | Shares Owned - All Institutions (actual) | Market Value - All Institutions (Rp.B) | Percent Owned - Insiders (%) | Shares Owned - Insiders (actual) | Market Value - Insiders (Rp.B) | IPO Date MM/dd/yyyy | IPO Price (Rp.) | Location Type | Data Precision | Data Year | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.B) | Day Close Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | Year |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Perusahaan Perseroan (Persero) PT Telekomunika... | 4210975 | IDX | IDX:TLKM | Integrated Telecommunication Services | Technology, Media & Telecommunications | Indonesia | Jl. Japati No. 1 | www.telkom.co.id | Perusahaan Perseroan (Persero) PT Telekomunika... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | Perusahaan Perseroan (Persero) PT Telekomunika... | Network Backbone; Optical Infrastructure; Clou... | Digital Media, Information Technology, Telecom... | 1884.000000 | 27/03/1884 01:00:00 | NaN | NaT | Dec | 2025-08-01 | Integrated Telecommunication Services | Telecommunication Services | Integrated Telecommunication Services | None | Communication Services | Telecommunication Services | Diversified Telecommunication Services | Bandung | PT Danantara Asset Management (Persero) | None | None | 0.520900 | Jakarta | Construction and Engineering | Indonesia | None | None | Jakarta | Diversified Support Services | Indonesia | Indonesia (Prior Subsidiary or Operating Unit,... | 21.060000 | 20859696180.000000 | 77180875.866000 | 0.090000 | 85029954.000000 | 314610.829800 | NaT | NaN | None | None | None | 47258399.000000 | 91256250.000000 | 0.542000 | 0.420000 | -12375841.000000 | 14622310.000000 | 26998151.000000 | 511950.000000 | 1875773.000000 | 11444575.000000 | 46000.000000 | NaN | 17584731.000000 | 43997851.000000 | 71066244.000000 | 22159917.000000 | 64166429.000000 | 23225173.000000 | 35538389.000000 | 23225173.000000 | 14725429.000000 | 24553925.000000 | -3250846.000000 | 98347123896.000000 | 135790.870326 | 1380.000000 | 61582582.000000 | 7156989.000000 | NaN | 2008 |
| 1 | PT Abadi Nusantara Hijau Investama Tbk (IDX:PACK) | 109420637 | IDX | IDX:PACK | Packaging and Materials: Paper and Plastic | Materials | Indonesia | Jl. Jababeka 2 Block C/11-D | www.flexypack.com | PT Abadi Nusantara Hijau Investama Tbk provide... | Public Company | None | Operating | None | No | None | None | Yes | No | No | PT Abadi Nusantara Hijau Investama Tbk provide... | Packaging Services | None | 2019.000000 | 2019-11-18 00:00:00 | NaN | NaT | Dec | NaT | Packaging and Materials: Paper and Plastic | Packaging and Materials: Paper and Plastic | None | None | Materials | Materials | Containers and Packaging | Bekasi | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Benson Kapital Indonesia (Current Investmen... | 23.240000 | 371188000.000000 | 965088.800000 | 0.000000 | 0.000000 | NaN | 2023-02-06 | 162.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
| 2 | PT ABM Investama Tbk (IDX:ABMM) | 4980353 | IDX | IDX:ABMM | Coal and Consumable Fuels | Energy and Utilities | Indonesia | Tiara Marga Trakindo I building | www.abm-investama.com | PT ABM Investama Tbk, together with its subsid... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT ABM Investama Tbk operating in the sectors ... | Clean Energy; Freight Service; Marine Transpor... | Energy, Precious Metals | 2006.000000 | 2006-06-01 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | PT Tiara Marga Trakindo | None | None | 0.535500 | Jakarta | Industrial Conglomerates | PT Tiara Marga Trakindo | None | None | Jakarta | Industrial Conglomerates | Indonesia | PT Tiara Marga Trakindo (Current Subsidiary or... | 0.830000 | 22926501.000000 | 68779.503000 | 7.340000 | 202136500.000000 | 606409.500000 | 2011-12-05 | 3750.000000 | None | None | None | 4360950.000000 | 3549129.000000 | 0.592000 | 0.430000 | -953832.000000 | 1384649.000000 | 2338481.000000 | 241667.000000 | NaN | NaN | NaN | NaN | 0.000000 | -811821.000000 | 1469802.000000 | 3114382.000000 | 3235172.000000 | -469841.000000 | NaN | -469841.000000 | -3913.000000 | -4564.000000 | 7861.000000 | NaN | NaN | NaN | -811821.000000 | 159424.000000 | NaN | 2008 |
| 3 | PT Ace Oldfields Tbk (IDX:KUAS) | 7649048 | IDX | IDX:KUAS | Building Products | Industrials | Indonesia | Jl. Raya Cileungsi Jonggol | www.aceoldfields.com | PT Ace Oldfields Tbk manufactures and sells pa... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | None | None | 1989.000000 | 1989-09-18 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Building Products; Hardware Tools and Equipmen... | Capital Goods | Building Products | None | Industrials | Capital Goods | Building Products | Bogor | None | None | None | NA% | None | None | None | None | None | None | None | None | Oldfields Holdings Limited (Prior Investment, ... | NaN | NaN | NaN | 0.870000 | 11225400.000000 | 763.327200 | 2021-10-25 | 195.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
| 4 | PT Acset Indonusa Tbk (IDX:ACST) | 4990054 | IDX | IDX:ACST | Construction and Engineering | Industrials | Indonesia | ACSET Building | www.acset.co | PT Acset Indonusa Tbk provides construction an... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Acset Indonusa Tbk, an integrated construct... | None | Civil Engineering, Construction | 1995.000000 | 1995-01-10 00:00:00 | 1995.000000 | 1995-01-10 | Dec | 2025-09-01 | Construction and Engineering; Commercial Const... | Capital Goods | Construction and Engineering | None | Industrials | Capital Goods | Construction and Engineering | Jakarta | PT Karya Supra Perkasa | None | None | 0.501000 | None | Unclassified | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Karya Supra Perkasa (Current Subsidiary or ... | 0.110000 | 20245000.000000 | 2753.320000 | NaN | NaN | NaN | 2013-06-20 | 2500.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
| 5 | PT Adaro Andalan Indonesia Tbk (IDX:AADI) | 4422654 | IDX | IDX:AADI | Coal and Consumable Fuels | Energy and Utilities | Indonesia | Cyber 2 Tower | www.adaroindonesia.com | PT Adaro Andalan Indonesia Tbk engages in the ... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Freight Service; Marine Transportation; Mining... | None | 2004.000000 | 2004-12-01 00:00:00 | NaN | NaT | None | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Alamtri Resources Indonesia Tbk (Current In... | 1.890000 | 147513763.000000 | 1143231.663250 | 5.870000 | 456756180.000000 | 3539860.395000 | 2024-12-03 | 5550.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
| 6 | PT Adhi Commuter Properti Tbk (IDX:ADCP) | 13992197 | IDX | IDX:ADCP | Real Estate Development | Real Estate | Indonesia | Jalan Pengantin Ali No. 88 Ciracas | www.adcp.co.id | PT Adhi Commuter Properti Tbk operates as a ma... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Adhi Commuter Properti Tbk operates in the ... | Smart Building; Real Estate Technology | Property Management, Real Estate | 2018.000000 | 2018-03-09 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Real Estate Development | Real Estate Management and Development | Real Estate Development | None | Real Estate | Real Estate Management and Development | Real Estate Management and Development | Jakarta Timur | PT Adhi Karya (Persero) Tbk | ADHI | IDX:ADHI | NA% | Jakarta | Construction and Engineering | Indonesia | None | None | Jakarta | Diversified Support Services | Indonesia | PT Adhi Karya (Persero) Tbk (Current Subsidiar... | NaN | NaN | NaN | 0.000000 | 330700.000000 | 17.857800 | 2022-02-21 | 130.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
| 7 | PT Adhi Kartiko Pratama Tbk (IDX:NICE) | 114680692 | IDX | IDX:NICE | Diversified Metals and Mining | Materials | Indonesia | Jalan Sorumba Nomor 80 | www.akp.co.id | PT Adhi Kartiko Pratama Tbk operates as a nick... | Public Company | None | Operating Subsidiary | Stock Corporation | No | Full | Consolidated | Yes | No | No | PT Adhi Kartiko Pratama Tbk operates as a nick... | Marine Transportation; Mining Technology | Mining | 2008.000000 | 2008-07-09 00:00:00 | 2008.000000 | 2008-07-09 | Dec | 2025-09-01 | Diversified Metals and Mining; Diversified Met... | Metals and Mining | Diversified Metals and Mining | None | Materials | Materials | Metals and Mining | Kendari | PT. Energy Battery Indonesia | None | None | 0.600000 | Jakarta Selatan | Unclassified | LX International Corp. | A001120 | KOSE:A001120 | Seoul | Trading Companies and Distributors | South Korea | PT Indo Tambangraya Megah Tbk (Current Investm... | 0.150000 | 9382800.000000 | 3828.182400 | NaN | NaN | NaN | 2024-01-05 | 438.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
| 8 | PT Adhi Karya (Persero) Tbk (IDX:ADHI) | 4995632 | IDX | IDX:ADHI | Construction and Engineering | Industrials | Indonesia | Jalan Raya Pasar Minggu Km. 18 | adhi.co.id | PT Adhi Karya (Persero) Tbk engages in the con... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Environmental Engineering | Construction,Infrastructure,Real Estate | 1960.000000 | 1960-03-11 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Construction and Engineering; Commercial Const... | Capital Goods | Construction and Engineering | None | Industrials | Capital Goods | Construction and Engineering | Jakarta | Indonesia | None | None | NA% | Jakarta | Diversified Support Services | Indonesia | None | None | Jakarta | Diversified Support Services | Indonesia | Indonesia (Current Investment, Majority) | 0.260000 | 21597230.000000 | 5528.890880 | 0.060000 | 5119600.000000 | 1310.617600 | NaT | NaN | None | None | None | 4525468.985000 | 5125368.542000 | 1.174000 | 0.856000 | 689925.514000 | 4652976.411000 | 3963050.897000 | 606987.785000 | 15869.334000 | 498313.341000 | 217249.869000 | NaN | 820626.327000 | 599899.556000 | 247790.039000 | 6095668.547000 | 6639941.611000 | 192704.468000 | 223441.322000 | 192704.468000 | 82994.360000 | -3306.168000 | -419393.749000 | 1769847500.000000 | 486.356400 | 270.000000 | 1420525.883000 | 365762.580000 | NaN | 2008 |
| 9 | PT Adi Sarana Armada Tbk (IDX:ASSA) | 4994188 | IDX | IDX:ASSA | Passenger Ground Transportation | Industrials | Indonesia | Graha Kirana Building | www.assarent.co.id | PT Adi Sarana Armada Tbk provides corporate ca... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Fleet Management; Freight Service; Car Sharing | Logistics,Railroad,Transportation | 1999.000000 | 1999-12-17 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Passenger Ground Transportation; Motor Vehicle... | Transportation | Passenger Ground Transportation | None | Industrials | Transportation | Ground Transportation | Jakarta Utara | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Triputra Investindo Arya (Prior Investment,... | 8.180000 | 302116753.000000 | 332328.428300 | 23.460000 | 866110656.000000 | 952721.721600 | 2012-11-08 | 390.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
| 10 | PT Adira Dinamika Multi Finance Tbk (IDX:ADMF) | 4310851 | IDX | IDX:ADMF | Specialty Finance: Consumer Focused | Financials | Indonesia | Millennium Centennial Center | adira.co.id | PT Adira Dinamika Multi Finance Tbk provides c... | Public Company | None | Operating Subsidiary | Stock Corporation | No | Summary | Consolidated | Yes | No | No | PT Adira Dinamika Multi Finance Tbk provides c... | Consumer Lending; Micro Lending | Financial Services | 1990.000000 | 1990-11-13 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Specialty Finance: Consumer Focused | Specialty Finance | Specialty Finance: Consumer Focused | None | Financials | Financial Services | Consumer Finance | Jakarta Selatan | PT Bank Danamon Indonesia Tbk | BDMN | IDX:BDMN | 0.920700 | Jakarta Selatan | Bank | Mitsubishi UFJ Financial Group, Inc. | 8306 | TSE:8306 | Tokyo | Bank | Japan | MUFG Bank, Ltd. (Prior Subsidiary or Operating... | 0.440000 | 4366312.000000 | 38423.545600 | NaN | NaN | NaN | NaT | NaN | None | None | None | 1642021.000000 | 3592024.000000 | 2.395000 | 2.278000 | 1417431.000000 | 2433353.000000 | 1015922.000000 | NaN | NaN | 254534.000000 | NaN | NaN | 844876.000000 | 1950003.000000 | 155195.000000 | 749482.000000 | 2930949.000000 | 1406208.000000 | NaN | NaN | 1020233.000000 | -3207435.000000 | 97892.000000 | 1000000000.000000 | 1450.000000 | 1450.000000 | 2794879.000000 | 474195.000000 | NaN | 2008 |
| 11 | PT Adiwarna Anugerah Abadi Tbk (IDX:NAIK) | 12731053 | IDX | IDX:NAIK | Office Services and Supplies | Industrials | Indonesia | Perkantoran Mutiara Taman Palem No.53 | www.adiwarna.co.id | PT Adiwarna Anugerah Abadi Tbk designs, suppli... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | None | Environment, Health and Safety (EH&S) | None | 2007.000000 | 2007-05-08 00:00:00 | NaN | NaT | None | 2025-09-01 | Office Services and Supplies | Commercial and Professional Services | Office Services and Supplies | None | Industrials | Commercial and Professional Services | Commercial Services and Supplies | Jakarta Barat | PT Adiwarna Anugerah Investama | None | None | 0.725800 | None | Unclassified | PT Adiwarna Anugerah Investama | None | None | None | Unclassified | Indonesia | PT Adiwarna Anugerah Investama (Current Subsid... | 0.040000 | 1392000.000000 | 175.392000 | 20.610000 | 685616400.000000 | 86387.666400 | 2024-11-11 | 107.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
| 12 | PT Aesler Grup Internasional Tbk (IDX:RONY) | 20023601 | IDX | IDX:RONY | Construction and Engineering | Industrials | Indonesia | Noble House Building | None | PT Aesler Grup Internasional Tbk provides arch... | Public Company | None | Operating | None | No | None | None | Yes | No | No | PT Aesler Grup Internasional Tbk (PT Aesler) o... | Sensors | Architecture, Construction | 2017.000000 | 2017-08-04 00:00:00 | NaN | NaT | Dec | NaT | Construction and Engineering; Construction Sup... | Capital Goods | Construction and Engineering | None | Industrials | Capital Goods | Construction and Engineering | Jakarta Selatan | None | None | None | NA% | None | None | None | None | None | None | None | None | Honour Accord Limited (Pending Acquisition or ... | NaN | NaN | NaN | 0.000000 | 0.000000 | NaN | 2020-04-07 | 100.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
| 13 | PT Agro Bahari Nusantara Tbk (IDX:UDNG) | 114111620 | IDX | IDX:UDNG | Packaged Foods and Meats Producers | Consumer | Indonesia | Ruko Shibuya SBC 012 PIK 2 | abn.farm | PT Agro Bahari Nusantara Tbk, together with it... | Public Company | None | Operating | None | No | None | None | Yes | No | No | PT Agro Bahari Nusantara Tbk operates primaril... | Aquaculture | Aquaculture, Food Processing | 2019.000000 | 2019-04-01 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Packaged Foods and Meats Producers; Seafood, S... | Producers | Packaged Foods and Meats Producers | None | Consumer Staples | Food, Beverage and Tobacco | Food Products | Tangerang | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Adrina Abdi Wisesa (Current Investment, Min... | NaN | NaN | NaN | 71.430000 | 1250000000.000000 | 4337500.000000 | 2023-10-27 | 100.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
| 14 | PT Agro Yasa Lestari Tbk (IDX:AYLS) | 12884080 | IDX | IDX:AYLS | Trading Companies and Distributors | Industrials | Indonesia | Gondangdia Lama Building 25 | agroyasalestari.com | PT Agro Yasa Lestari Tbk designs, supplies, an... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | None | None | Civil Engineering, Construction | 2010.000000 | 2010-02-15 00:00:00 | NaN | NaT | Dec | NaT | Trading Companies and Distributors; Constructi... | Capital Goods | Trading Companies and Distributors | None | Industrials | Capital Goods | Trading Companies and Distributors | Jakarta Pusat | PT Anugrah Cakrawala Dunia | None | None | 0.696900 | Grogol Petamburan | Trading Companies and Distributors | PT Anugrah Cakrawala Dunia | None | None | Grogol Petamburan | Trading Companies and Distributors | Indonesia | PT Anugrah Cakrawala Dunia (Current Subsidiary... | NaN | NaN | NaN | NaN | NaN | NaN | 2020-02-07 | 100.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
| 15 | PT Agung Menjangan Mas Tbk (IDX:AMMS) | 106546520 | IDX | IDX:AMMS | Packaged Foods and Meats Producers | Consumer | Indonesia | Generali Tower Gran Rubina Business Park | agungmm.com | PT Agung Menjangan Mas Tbk provides services t... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Agung Menjangan Mas Tbk engages in the agri... | Aquaculture | None | 2004.000000 | None | NaN | NaT | Dec | NaT | Packaged Foods and Meats Producers; Seafood, S... | Producers | Packaged Foods and Meats Producers | None | Consumer Staples | Food, Beverage and Tobacco | Food Products | Jakarta | PT Mandara Mas Semesta | None | None | 0.997700 | None | Unclassified | PT Mandara Mas Semesta | None | None | None | Unclassified | Indonesia | PT Mandara Mas Semesta (Current Subsidiary or ... | NaN | NaN | NaN | 2.540000 | 30437500.000000 | 14549.125000 | 2022-08-04 | 100.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
| 16 | PT Agung Podomoro Land Tbk (IDX:APLN) | 4432432 | IDX | IDX:APLN | Real Estate Development | Real Estate | Indonesia | APL Tower | www.agungpodomoroland.com | PT Agung Podomoro Land Tbk, together with its ... | Public Company | None | Operating Subsidiary | Stock Corporation | No | Full | Consolidated | Yes | No | No | PT Agung Podomoro Land Tbk engages in property... | None | Commercial, Property Development, Real Estate,... | 2004.000000 | 2004-07-30 00:00:00 | NaN | NaT | Dec | 2025-08-01 | Real Estate Development | Real Estate Management and Development | Real Estate Development | None | Real Estate | Real Estate Management and Development | Real Estate Management and Development | Jakarta | PT Indofica | None | None | 0.827200 | Jakarta | Real Estate Development | PT Indofica | None | None | Jakarta | Real Estate Development | Indonesia | PT Indofica (Current Subsidiary or Operating U... | 0.020000 | 3760100.000000 | 368.489800 | 5.020000 | 1139706185.000000 | 111691.206130 | 2010-10-28 | 365.000000 | None | None | None | 2032513.571000 | 2734460.554000 | 5.148000 | 1.978000 | 1088876.420000 | 1351411.625000 | 262535.205000 | 803761.971000 | 2972.490000 | 995393.484000 | NaN | NaN | 1070393.484000 | 701946.983000 | 1229306.782000 | 686364.623000 | 810031.342000 | -50087.790000 | -48343.054000 | -50087.790000 | -25050.220000 | 734367.660000 | 280429.191000 | 150000000.000000 | NaN | NaN | 1772340.467000 | 340551.102000 | NaN | 2008 |
| 17 | PT Agung Semesta Sejahtera Tbk (IDX:TARA) | 4990209 | IDX | IDX:TARA | Construction and Engineering | Industrials | Indonesia | Wisma 77 Lt. 19 | www.agungsemestasejahtera.com | PT Agung Semesta Sejahtera Tbk primarily engag... | Public Company | None | Operating | None | No | None | None | Yes | No | No | PT Agung Semesta Sejahtera Tbk engages in dive... | None | Property Development, Property Management, Rea... | 2006.000000 | 2006-06-01 00:00:00 | NaN | NaT | Dec | NaT | Construction and Engineering; Commercial Const... | Capital Goods | Construction and Engineering | None | Industrials | Capital Goods | Construction and Engineering | Jakarta Barat | None | None | None | NA% | None | None | None | None | None | None | None | None | None | 5.480000 | 551389038.000000 | 14336.114988 | NaN | NaN | NaN | 2014-07-08 | 106.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
| 18 | PT AirAsia Indonesia Tbk (IDX:CMPP) | 7476312 | IDX | IDX:CMPP | Passenger Airlines | Industrials | Indonesia | AirAsia Redhouse | ir.aaid.co.id | PT AirAsia Indonesia Tbk provides scheduled co... | Public Company | None | Operating | None | No | None | None | Yes | No | No | PT Airasia Indonesia Tbk is a prominent airlin... | None | Air Transportation, Transportation | 1989.000000 | 1989-07-25 00:00:00 | NaN | NaT | Dec | NaT | Passenger Airlines; Commercial Airlines | Transportation | Passenger Airlines | None | Industrials | Transportation | Passenger Airlines | Tangerang | None | None | None | NA% | None | None | None | None | None | None | None | None | Capital A Berhad (Prior Subsidiary or Operatin... | NaN | NaN | NaN | NaN | NaN | NaN | NaT | NaN | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
| 19 | PT Akasha Wira International Tbk (IDX:ADES) | 4913827 | IDX | IDX:ADES | Soft Drinks and Non-alcoholic Beverages | Consumer | Indonesia | Jl. TB. Simatupang Kav. 89 RT 01 RW 02 | www.akashainternational.com | PT Akasha Wira International Tbk engages in th... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Akasha Wira International Tbk engages in th... | Lifestyle; Nutraceutical; Alternative Protein | Consumer Goods, Manufacturing | 1985.000000 | None | 2006.000000 | 2006-03-10 | Dec | 2025-09-01 | Soft Drinks and Non-alcoholic Beverages; Bottl... | Producers | Beverage Producers | Soft Drinks and Non-alcoholic Beverages | Consumer Staples | Food, Beverage and Tobacco | Beverages | Jakarta Selatan | Water Partners Bottling S.A. | None | None | 0.915200 | Zurich | Unclassified | Water Partners Bottling S.A. | None | None | Zurich | Unclassified | Switzerland | Nestlé S.A. (Prior Investment, Minority); Wate... | 0.010000 | 62100.000000 | 954.787500 | 0.000000 | 11900.000000 | 182.962500 | NaT | NaN | None | None | None | 133117.000000 | 185015.000000 | 0.514000 | 0.394000 | -56009.000000 | 59208.000000 | 115217.000000 | 9581.000000 | 993.000000 | NaN | 84251.000000 | NaN | 84251.000000 | 51898.000000 | 124311.000000 | 94734.000000 | 129542.000000 | -38740.000000 | -21976.000000 | -38740.000000 | -15208.000000 | -48514.000000 | 25286.000000 | 589896800.000000 | 132.726780 | 225.000000 | 136149.000000 | 29311.000000 | NaN | 2008 |
Total baris: 15708
Jumlah emiten unik: 924
Jumlah Year unik: 17
# Tentukan beberapa kolom penting di depan
kolom_depan = [
"exchange_ticker",
"Year",
"Entity Name",
"IPO Date MM/dd/yyyy",
"Primary Industry",
]
# Sisanya biarkan mengikuti urutan semula
kolom_lain = [c for c in df_final.columns if c not in kolom_depan]
df_findat_final = df_final[kolom_depan + kolom_lain]
print("=== df_final dengan urutan kolom yang rapi ===")
df_findat_final=== df_final dengan urutan kolom yang rapi ===
| variable | exchange_ticker | Year | Entity Name | IPO Date MM/dd/yyyy | Primary Industry | Entity ID | Exchange | 1st Level Primary Industry | Country / Region Name | Address 1 | Web Address | Business Description | Company Type | Investor Type | Company Status | Ownership Structure | Activist Investor? Yes/No | Institution Coverage Level | Consolidation Status | Has Current Financials? Yes/No | Has Bank Regulatory Financials? Yes/No | Has Third Party Financials? Yes/No | Long Business Description | Topic Tags | Crunchbase Categories | Year Established | Date Established MM/dd/yyyy | Year Incorporated | Date Incorporated MM/dd/yyyy | Month of Fiscal Year End | Headcount As Of MM/dd/yyyy | Industry Classification | 2nd Level Primary Industry | 3rd Level Primary Industry | 4th Level Primary Industry | Sector | Industry Group | Industry | City | Parent Company Name | Parent Ticker | Parent Exchange: Ticker | Parent Percent Owned (%) | Parent City | Parent Industry | Ultimate Parent Company Name | Ultimate Parent Ticker | Ultimate Parent Exchange: Ticker | Ultimate Parent City | Ultimate Parent Industry | Ultimate Parent Country / Region | All Investors | Percent Owned - All Institutions (%) | Shares Owned - All Institutions (actual) | Market Value - All Institutions (Rp.B) | Percent Owned - Insiders (%) | Shares Owned - Insiders (actual) | Market Value - Insiders (Rp.B) | IPO Price (Rp.) | Location Type | Data Precision | Data Year | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.B) | Day Close Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | IDX:TLKM | 2008 | Perusahaan Perseroan (Persero) PT Telekomunika... | NaT | Integrated Telecommunication Services | 4210975 | IDX | Technology, Media & Telecommunications | Indonesia | Jl. Japati No. 1 | www.telkom.co.id | Perusahaan Perseroan (Persero) PT Telekomunika... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | Perusahaan Perseroan (Persero) PT Telekomunika... | Network Backbone; Optical Infrastructure; Clou... | Digital Media, Information Technology, Telecom... | 1884.000000 | 27/03/1884 01:00:00 | NaN | NaT | Dec | 2025-08-01 | Integrated Telecommunication Services | Telecommunication Services | Integrated Telecommunication Services | None | Communication Services | Telecommunication Services | Diversified Telecommunication Services | Bandung | PT Danantara Asset Management (Persero) | None | None | 0.520900 | Jakarta | Construction and Engineering | Indonesia | None | None | Jakarta | Diversified Support Services | Indonesia | Indonesia (Prior Subsidiary or Operating Unit,... | 21.060000 | 20859696180.000000 | 77180875.866000 | 0.090000 | 85029954.000000 | 314610.829800 | NaN | None | None | None | 47258399.000000 | 91256250.000000 | 0.542000 | 0.420000 | -12375841.000000 | 14622310.000000 | 26998151.000000 | 511950.000000 | 1875773.000000 | 11444575.000000 | 46000.000000 | NaN | 17584731.000000 | 43997851.000000 | 71066244.000000 | 22159917.000000 | 64166429.000000 | 23225173.000000 | 35538389.000000 | 23225173.000000 | 14725429.000000 | 24553925.000000 | -3250846.000000 | 98347123896.000000 | 135790.870326 | 1380.000000 | 61582582.000000 | 7156989.000000 | NaN |
| 1 | IDX:PACK | 2008 | PT Abadi Nusantara Hijau Investama Tbk (IDX:PACK) | 2023-02-06 | Packaging and Materials: Paper and Plastic | 109420637 | IDX | Materials | Indonesia | Jl. Jababeka 2 Block C/11-D | www.flexypack.com | PT Abadi Nusantara Hijau Investama Tbk provide... | Public Company | None | Operating | None | No | None | None | Yes | No | No | PT Abadi Nusantara Hijau Investama Tbk provide... | Packaging Services | None | 2019.000000 | 2019-11-18 00:00:00 | NaN | NaT | Dec | NaT | Packaging and Materials: Paper and Plastic | Packaging and Materials: Paper and Plastic | None | None | Materials | Materials | Containers and Packaging | Bekasi | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Benson Kapital Indonesia (Current Investmen... | 23.240000 | 371188000.000000 | 965088.800000 | 0.000000 | 0.000000 | NaN | 162.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 2 | IDX:ABMM | 2008 | PT ABM Investama Tbk (IDX:ABMM) | 2011-12-05 | Coal and Consumable Fuels | 4980353 | IDX | Energy and Utilities | Indonesia | Tiara Marga Trakindo I building | www.abm-investama.com | PT ABM Investama Tbk, together with its subsid... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT ABM Investama Tbk operating in the sectors ... | Clean Energy; Freight Service; Marine Transpor... | Energy, Precious Metals | 2006.000000 | 2006-06-01 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | PT Tiara Marga Trakindo | None | None | 0.535500 | Jakarta | Industrial Conglomerates | PT Tiara Marga Trakindo | None | None | Jakarta | Industrial Conglomerates | Indonesia | PT Tiara Marga Trakindo (Current Subsidiary or... | 0.830000 | 22926501.000000 | 68779.503000 | 7.340000 | 202136500.000000 | 606409.500000 | 3750.000000 | None | None | None | 4360950.000000 | 3549129.000000 | 0.592000 | 0.430000 | -953832.000000 | 1384649.000000 | 2338481.000000 | 241667.000000 | NaN | NaN | NaN | NaN | 0.000000 | -811821.000000 | 1469802.000000 | 3114382.000000 | 3235172.000000 | -469841.000000 | NaN | -469841.000000 | -3913.000000 | -4564.000000 | 7861.000000 | NaN | NaN | NaN | -811821.000000 | 159424.000000 | NaN |
| 3 | IDX:KUAS | 2008 | PT Ace Oldfields Tbk (IDX:KUAS) | 2021-10-25 | Building Products | 7649048 | IDX | Industrials | Indonesia | Jl. Raya Cileungsi Jonggol | www.aceoldfields.com | PT Ace Oldfields Tbk manufactures and sells pa... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | None | None | 1989.000000 | 1989-09-18 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Building Products; Hardware Tools and Equipmen... | Capital Goods | Building Products | None | Industrials | Capital Goods | Building Products | Bogor | None | None | None | NA% | None | None | None | None | None | None | None | None | Oldfields Holdings Limited (Prior Investment, ... | NaN | NaN | NaN | 0.870000 | 11225400.000000 | 763.327200 | 195.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 4 | IDX:ACST | 2008 | PT Acset Indonusa Tbk (IDX:ACST) | 2013-06-20 | Construction and Engineering | 4990054 | IDX | Industrials | Indonesia | ACSET Building | www.acset.co | PT Acset Indonusa Tbk provides construction an... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Acset Indonusa Tbk, an integrated construct... | None | Civil Engineering, Construction | 1995.000000 | 1995-01-10 00:00:00 | 1995.000000 | 1995-01-10 00:00:00 | Dec | 2025-09-01 | Construction and Engineering; Commercial Const... | Capital Goods | Construction and Engineering | None | Industrials | Capital Goods | Construction and Engineering | Jakarta | PT Karya Supra Perkasa | None | None | 0.501000 | None | Unclassified | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Karya Supra Perkasa (Current Subsidiary or ... | 0.110000 | 20245000.000000 | 2753.320000 | NaN | NaN | NaN | 2500.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 15703 | IDX:ADMG | 2024 | PT. Polychem Indonesia Tbk (IDX:ADMG) | NaT | Commodity Chemicals | 4986761 | IDX | Materials | Indonesia | Gedung Wisma 46 Kota BNI | www.polychemindo.com | PT. Polychem Indonesia Tbk engages in the chem... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Bio-based and Renewable Materials | Chemical, Manufacturing | 1978.000000 | None | NaN | NaT | Dec | 2025-08-01 | Commodity Chemicals; Industrial Organic Chemic... | Chemicals | Commodity Chemicals | None | Materials | Materials | Chemicals | Jakarta Pusat | Provestment Limited | None | None | 0.495000 | None | Unclassified | Provestment Limited | None | None | None | Unclassified | Indonesia | HSBC Global Asset Management (UK) Limited (Pri... | NaN | NaN | NaN | 6.700000 | 260689900.000000 | 42231.763800 | NaN | None | None | None | 633525.279930 | 2502397.042404 | 1.582000 | 0.658000 | 324806.222988 | 883354.230822 | 558548.007834 | 457628.197014 | 9209.520918 | NaN | NaN | NaN | 2306.714616 | 1868871.762474 | 1550937.131766 | 1721786.548931 | 1738143.281730 | -64988.560197 | 8869.037256 | -64988.560197 | -161541.845097 | 29243.074738 | 23742.525826 | 3889179559.000000 | 423.920572 | 109.000000 | 1871178.477090 | 204124.233702 | NaN |
| 15704 | IDX:BIMA | 2024 | PT. Primarindo Asia Infrastructure, Tbk. (IDX:... | NaT | Footwear Producers | 4914053 | IDX | Consumer | Indonesia | Gedung Tatapuri | www.primarindo.co.id | PT. Primarindo Asia Infrastructure, Tbk. engag... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT. Primarindo Asia Infrastructure, Tbk. engag... | Lifestyle | Shoes | 1988.000000 | 1988-07-01 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Footwear Producers | Producers | Apparel, Footwear and Textile Producers | Footwear Producers | Consumer Discretionary | Consumer Durables and Apparel | Textiles, Apparel and Luxury Goods | Jakarta | PT. Golden Lestari | None | None | 0.865600 | None | Footwear Producers | PT. Golden Lestari | None | None | None | Footwear Producers | Indonesia | PT. Golden Lestari (Current Subsidiary or Oper... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | None | None | None | 254016.336000 | 319791.244000 | 0.486000 | 0.057000 | -65139.176000 | 61640.844000 | 126780.020000 | 53945.441000 | 151.607000 | NaN | 38535.590000 | NaN | 39348.507000 | 65774.907000 | 144368.933000 | 63473.693000 | 92500.319000 | -7837.238000 | -7539.673000 | -7837.238000 | -14713.779000 | 576.555000 | 574.851000 | 608175716.000000 | 52.911287 | 87.000000 | 105123.414000 | 1132.703000 | 2149.983000 |
| 15705 | IDX:ARTI | 2024 | PT. Ratu Prabu Energi, Tbk (IDX:ARTI) | NaT | Oil and Gas Equipment and Services | 4978413 | IDX | Energy and Utilities | Indonesia | Gedung Ratu Prabu 1 | www.ratuprabuenergi.com | PT. Ratu Prabu Energi, Tbk, through its subsid... | Public Company | None | Operating | None | No | None | None | Yes | No | No | PT. Ratu Prabu Energi Tbk, through its subsidi... | Mining Technology | Energy, Oil and Gas | 1993.000000 | 1993-03-31 01:00:00 | 1993.000000 | 1993-03-31 01:00:00 | Dec | NaT | Oil and Gas Equipment and Services; Oil and Ga... | Oil, Gas and Coal | Oil and Gas Equipment and Services | None | Energy | Energy | Energy Equipment and Services | Jakarta Selatan | None | None | None | NA% | None | None | None | None | None | None | None | None | None | NaN | NaN | NaN | 1.830000 | 143195600.000000 | 286.391200 | NaN | None | None | None | 753564.461000 | 550707.999000 | 0.002000 | 0.002000 | -744152.585000 | 1449.639000 | 745602.224000 | NaN | NaN | NaN | NaN | NaN | 7447.628000 | -202856.462000 | 80983.328000 | 24540.998000 | 37082.047000 | 1384.617000 | 19343.993000 | 1384.617000 | -4425.791000 | 1400.707000 | -530.714000 | 7840000000.000000 | 15.680000 | 2.000000 | -195408.833000 | 464.548000 | NaN |
| 15706 | IDX:TGRA | 2024 | PT. Terregra Asia Energy Tbk (IDX:TGRA) | 2017-05-15 | Renewable Electricity | 6676356 | IDX | Energy and Utilities | Indonesia | Lippo Puri Tower | www.terregra.co.id | PT. Terregra Asia Energy Tbk develops, builds,... | Public Company | None | Operating | None | No | None | None | Yes | No | No | PT. Terregra Asia Energy Tbk is a prominent pl... | Clean Energy; CleanTech; Power Grid; Utility S... | Energy, Renewable Energy | 1995.000000 | 1995-11-07 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Renewable Electricity; Hydroelectric Power Gen... | Renewable Electricity | None | None | Utilities | Utilities | Independent Power and Renewable Electricity Pr... | Jakarta Barat | PT terregra asia equity | None | None | 0.164600 | None | Unclassified | PT terregra asia equity | None | None | None | Unclassified | Indonesia | Permodalan Nasional Berhad (Current Investment... | 76.540000 | 2104808600.000000 | 56829.832200 | 5.970000 | 164141600.000000 | 4431.823200 | 200.000000 | None | None | None | 147870.135000 | 458585.037000 | 0.249000 | 0.017000 | -41786.202000 | 13851.873000 | 55638.076000 | NaN | NaN | 88291.188000 | 8816.043000 | NaN | 97107.231000 | 310714.902000 | 356206.187000 | 355.071000 | NaN | -13672.830000 | -13091.231000 | -13672.830000 | -23698.806000 | -4570.877000 | -566.809000 | 2750000000.000000 | 85.250000 | 31.000000 | 407822.132000 | 181.181000 | NaN |
| 15707 | IDX:FILM | 2024 | PT.MD Entertainment Tbk (IDX:FILM) | 2018-08-02 | Movies and Entertainment | 10882429 | IDX | Technology, Media & Telecommunications | Indonesia | MD Place, Tower 1 | www.mdentertainment.com | PT.MD Entertainment Tbk produces, trades in, a... | Public Company | None | Operating | None | No | None | None | Yes | No | No | PT.MD Entertainment Tbk produces, trades in, a... | Digital Media; Content Creation | Film Production, TV | 2002.000000 | 2002-08-01 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Movies and Entertainment; Entertainment Produc... | Media and Entertainment | Movies and Entertainment | None | Communication Services | Media and Entertainment | Entertainment | Jakarta | None | None | None | NA% | None | None | None | None | None | None | None | None | Seoul Broadcasting System (Current Investment,... | 0.450000 | 44259784.000000 | 324202.917800 | 62.690000 | 6204924122.000000 | 45451069.193650 | 210.000000 | None | None | None | 1226285.218000 | 3937308.585000 | 2.227000 | 0.949000 | 576812.777000 | 1047028.354000 | 470215.577000 | 505070.855000 | 6179.398000 | 702642.890000 | 224320.000000 | NaN | 1006762.890000 | 2711023.367000 | 1009918.730000 | 187256.689000 | 455949.732000 | 70451.896000 | 97855.130000 | 70451.896000 | 20551.670000 | -89533.613000 | -145940.433000 | 9897787962.000000 | 38205.461533 | 3860.000000 | 3717786.257000 | 372089.069000 | 1096619.608000 |
15708 rows × 91 columns
# Rename kolom 'exchange_ticker' jadi 'ticker'
df_findat_final = df_findat_final.rename(columns={'exchange_ticker': 'ticker'})
# Hapus 'IDX:' prefix dari kolom 'ticker'
df_findat_final['ticker'] = df_findat_final['ticker'].astype(str).str.replace('IDX:', '', regex=False)
# sort DF berdasarkan 'ticker' asc, lalu 'Year' desc
df_findat_final = df_findat_final.sort_values(by=['ticker', 'Year'], ascending=[True, False]).reset_index(drop=True)
# Display
df_findat_final.head()| variable | ticker | Year | Entity Name | IPO Date MM/dd/yyyy | Primary Industry | Entity ID | Exchange | 1st Level Primary Industry | Country / Region Name | Address 1 | Web Address | Business Description | Company Type | Investor Type | Company Status | Ownership Structure | Activist Investor? Yes/No | Institution Coverage Level | Consolidation Status | Has Current Financials? Yes/No | Has Bank Regulatory Financials? Yes/No | Has Third Party Financials? Yes/No | Long Business Description | Topic Tags | Crunchbase Categories | Year Established | Date Established MM/dd/yyyy | Year Incorporated | Date Incorporated MM/dd/yyyy | Month of Fiscal Year End | Headcount As Of MM/dd/yyyy | Industry Classification | 2nd Level Primary Industry | 3rd Level Primary Industry | 4th Level Primary Industry | Sector | Industry Group | Industry | City | Parent Company Name | Parent Ticker | Parent Exchange: Ticker | Parent Percent Owned (%) | Parent City | Parent Industry | Ultimate Parent Company Name | Ultimate Parent Ticker | Ultimate Parent Exchange: Ticker | Ultimate Parent City | Ultimate Parent Industry | Ultimate Parent Country / Region | All Investors | Percent Owned - All Institutions (%) | Shares Owned - All Institutions (actual) | Market Value - All Institutions (Rp.B) | Percent Owned - Insiders (%) | Shares Owned - Insiders (actual) | Market Value - Insiders (Rp.B) | IPO Price (Rp.) | Location Type | Data Precision | Data Year | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.B) | Day Close Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | AADI | 2024 | PT Adaro Andalan Indonesia Tbk (IDX:AADI) | 2024-12-03 | Coal and Consumable Fuels | 4422654 | IDX | Energy and Utilities | Indonesia | Cyber 2 Tower | www.adaroindonesia.com | PT Adaro Andalan Indonesia Tbk engages in the ... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Freight Service; Marine Transportation; Mining... | None | 2004.000000 | 2004-12-01 00:00:00 | NaN | NaT | None | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Alamtri Resources Indonesia Tbk (Current In... | 1.890000 | 147513763.000000 | 1143231.663250 | 5.870000 | 456756180.000000 | 3539860.395000 | 5550.000000 | None | None | None | 42324475.248000 | 96469808.484000 | 2.537000 | 2.292000 | 21593100.594000 | 35641809.096000 | 14048708.502000 | 1103501.802000 | 105989.232000 | 23185756.224000 | NaN | NaN | 23924525.640000 | 54145333.236000 | 22391497.002000 | 61072270.545424 | 84304634.017260 | 18392374.252099 | 19927346.291702 | 18392374.252099 | 21026086.808611 | 18994042.847576 | -16098252.230656 | 7786891760.000000 | 65993.907666 | 8475.000000 | 78069858.876000 | 25454061.012000 | 37588.830000 |
| 1 | AADI | 2023 | PT Adaro Andalan Indonesia Tbk (IDX:AADI) | 2024-12-03 | Coal and Consumable Fuels | 4422654 | IDX | Energy and Utilities | Indonesia | Cyber 2 Tower | www.adaroindonesia.com | PT Adaro Andalan Indonesia Tbk engages in the ... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Freight Service; Marine Transportation; Mining... | None | 2004.000000 | 2004-12-01 00:00:00 | NaN | NaT | None | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Alamtri Resources Indonesia Tbk (Current In... | 1.890000 | 147513763.000000 | 1143231.663250 | 5.870000 | 456756180.000000 | 3539860.395000 | 5550.000000 | None | None | None | 35027575.128000 | 108867787.224000 | 1.818000 | 1.699000 | 22687219.212000 | 50406307.896000 | 27719088.684000 | 1003297.260000 | 66758.034000 | 2978539.704000 | NaN | NaN | 14784754.278000 | 73840212.096000 | 14962801.392000 | 63789705.022615 | 90132389.148923 | 21711813.969577 | 23121835.271885 | 21711813.969577 | 19592972.794962 | 9069085.144385 | -12888255.983885 | 7008202560.000000 | NaN | NaN | 88624966.374000 | 39810523.914000 | 35390.544000 |
| 2 | AADI | 2022 | PT Adaro Andalan Indonesia Tbk (IDX:AADI) | 2024-12-03 | Coal and Consumable Fuels | 4422654 | IDX | Energy and Utilities | Indonesia | Cyber 2 Tower | www.adaroindonesia.com | PT Adaro Andalan Indonesia Tbk engages in the ... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Freight Service; Marine Transportation; Mining... | None | 2004.000000 | 2004-12-01 00:00:00 | NaN | NaT | None | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Alamtri Resources Indonesia Tbk (Current In... | 1.890000 | 147513763.000000 | 1143231.663250 | 5.870000 | 456756180.000000 | 3539860.395000 | 5550.000000 | None | None | None | 53697263.230000 | 123705349.494000 | 2.043000 | 1.960000 | 34306837.619000 | 67190488.975000 | 32883651.356000 | 1864970.784000 | 10124.908000 | 16805809.909000 | NaN | NaN | 18638045.561000 | 70008086.264000 | 13750882.913000 | 56091453.934708 | 114740624.204931 | 52675640.780862 | 57656683.481331 | 52675640.780862 | 34894343.232646 | 46564112.440085 | 27880000.623946 | 7008203000.000000 | NaN | NaN | 88646131.825000 | 54122633.598000 | 32610.900000 |
| 3 | AADI | 2021 | PT Adaro Andalan Indonesia Tbk (IDX:AADI) | 2024-12-03 | Coal and Consumable Fuels | 4422654 | IDX | Energy and Utilities | Indonesia | Cyber 2 Tower | www.adaroindonesia.com | PT Adaro Andalan Indonesia Tbk engages in the ... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Freight Service; Marine Transportation; Mining... | None | 2004.000000 | 2004-12-01 00:00:00 | NaN | NaT | None | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Alamtri Resources Indonesia Tbk (Current In... | 1.890000 | 147513763.000000 | 1143231.663250 | 5.870000 | 456756180.000000 | 3539860.395000 | 5550.000000 | None | None | None | 36597123.720000 | 88200470.070000 | 2.217000 | 2.091000 | 18995952.510000 | 34608541.707000 | 15612589.197000 | 1033762.320000 | 95027.490000 | 16900161.822000 | NaN | NaN | 18489656.871000 | 51603346.350000 | 15934956.066000 | 33903719.536034 | 55264915.482548 | 18421357.075862 | 23354567.767490 | 18421357.075862 | 11866461.387490 | 15245298.528812 | 5766604.814061 | 7008203000.000000 | NaN | NaN | 70093003.221000 | 24558551.190000 | 6154.704000 |
| 4 | AADI | 2020 | PT Adaro Andalan Indonesia Tbk (IDX:AADI) | 2024-12-03 | Coal and Consumable Fuels | 4422654 | IDX | Energy and Utilities | Indonesia | Cyber 2 Tower | www.adaroindonesia.com | PT Adaro Andalan Indonesia Tbk engages in the ... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Freight Service; Marine Transportation; Mining... | None | 2004.000000 | 2004-12-01 00:00:00 | NaN | NaT | None | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Alamtri Resources Indonesia Tbk (Current In... | 1.890000 | 147513763.000000 | 1143231.663250 | 5.870000 | 456756180.000000 | 3539860.395000 | 5550.000000 | None | None | None | NaN | NaN | None | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
Ubah Nama Kolom
df_findat_final = df_findat_final.rename(columns={'Day Close Price (Rp.)': 'Year Close Stock Price (Rp.)'})
df_findat_final.head()| variable | ticker | Year | Entity Name | IPO Date MM/dd/yyyy | Primary Industry | Entity ID | Exchange | 1st Level Primary Industry | Country / Region Name | Address 1 | Web Address | Business Description | Company Type | Investor Type | Company Status | Ownership Structure | Activist Investor? Yes/No | Institution Coverage Level | Consolidation Status | Has Current Financials? Yes/No | Has Bank Regulatory Financials? Yes/No | Has Third Party Financials? Yes/No | Long Business Description | Topic Tags | Crunchbase Categories | Year Established | Date Established MM/dd/yyyy | Year Incorporated | Date Incorporated MM/dd/yyyy | Month of Fiscal Year End | Headcount As Of MM/dd/yyyy | Industry Classification | 2nd Level Primary Industry | 3rd Level Primary Industry | 4th Level Primary Industry | Sector | Industry Group | Industry | City | Parent Company Name | Parent Ticker | Parent Exchange: Ticker | Parent Percent Owned (%) | Parent City | Parent Industry | Ultimate Parent Company Name | Ultimate Parent Ticker | Ultimate Parent Exchange: Ticker | Ultimate Parent City | Ultimate Parent Industry | Ultimate Parent Country / Region | All Investors | Percent Owned - All Institutions (%) | Shares Owned - All Institutions (actual) | Market Value - All Institutions (Rp.B) | Percent Owned - Insiders (%) | Shares Owned - Insiders (actual) | Market Value - Insiders (Rp.B) | IPO Price (Rp.) | Location Type | Data Precision | Data Year | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.B) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | AADI | 2024 | PT Adaro Andalan Indonesia Tbk (IDX:AADI) | 2024-12-03 | Coal and Consumable Fuels | 4422654 | IDX | Energy and Utilities | Indonesia | Cyber 2 Tower | www.adaroindonesia.com | PT Adaro Andalan Indonesia Tbk engages in the ... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Freight Service; Marine Transportation; Mining... | None | 2004.000000 | 2004-12-01 00:00:00 | NaN | NaT | None | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Alamtri Resources Indonesia Tbk (Current In... | 1.890000 | 147513763.000000 | 1143231.663250 | 5.870000 | 456756180.000000 | 3539860.395000 | 5550.000000 | None | None | None | 42324475.248000 | 96469808.484000 | 2.537000 | 2.292000 | 21593100.594000 | 35641809.096000 | 14048708.502000 | 1103501.802000 | 105989.232000 | 23185756.224000 | NaN | NaN | 23924525.640000 | 54145333.236000 | 22391497.002000 | 61072270.545424 | 84304634.017260 | 18392374.252099 | 19927346.291702 | 18392374.252099 | 21026086.808611 | 18994042.847576 | -16098252.230656 | 7786891760.000000 | 65993.907666 | 8475.000000 | 78069858.876000 | 25454061.012000 | 37588.830000 |
| 1 | AADI | 2023 | PT Adaro Andalan Indonesia Tbk (IDX:AADI) | 2024-12-03 | Coal and Consumable Fuels | 4422654 | IDX | Energy and Utilities | Indonesia | Cyber 2 Tower | www.adaroindonesia.com | PT Adaro Andalan Indonesia Tbk engages in the ... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Freight Service; Marine Transportation; Mining... | None | 2004.000000 | 2004-12-01 00:00:00 | NaN | NaT | None | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Alamtri Resources Indonesia Tbk (Current In... | 1.890000 | 147513763.000000 | 1143231.663250 | 5.870000 | 456756180.000000 | 3539860.395000 | 5550.000000 | None | None | None | 35027575.128000 | 108867787.224000 | 1.818000 | 1.699000 | 22687219.212000 | 50406307.896000 | 27719088.684000 | 1003297.260000 | 66758.034000 | 2978539.704000 | NaN | NaN | 14784754.278000 | 73840212.096000 | 14962801.392000 | 63789705.022615 | 90132389.148923 | 21711813.969577 | 23121835.271885 | 21711813.969577 | 19592972.794962 | 9069085.144385 | -12888255.983885 | 7008202560.000000 | NaN | NaN | 88624966.374000 | 39810523.914000 | 35390.544000 |
| 2 | AADI | 2022 | PT Adaro Andalan Indonesia Tbk (IDX:AADI) | 2024-12-03 | Coal and Consumable Fuels | 4422654 | IDX | Energy and Utilities | Indonesia | Cyber 2 Tower | www.adaroindonesia.com | PT Adaro Andalan Indonesia Tbk engages in the ... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Freight Service; Marine Transportation; Mining... | None | 2004.000000 | 2004-12-01 00:00:00 | NaN | NaT | None | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Alamtri Resources Indonesia Tbk (Current In... | 1.890000 | 147513763.000000 | 1143231.663250 | 5.870000 | 456756180.000000 | 3539860.395000 | 5550.000000 | None | None | None | 53697263.230000 | 123705349.494000 | 2.043000 | 1.960000 | 34306837.619000 | 67190488.975000 | 32883651.356000 | 1864970.784000 | 10124.908000 | 16805809.909000 | NaN | NaN | 18638045.561000 | 70008086.264000 | 13750882.913000 | 56091453.934708 | 114740624.204931 | 52675640.780862 | 57656683.481331 | 52675640.780862 | 34894343.232646 | 46564112.440085 | 27880000.623946 | 7008203000.000000 | NaN | NaN | 88646131.825000 | 54122633.598000 | 32610.900000 |
| 3 | AADI | 2021 | PT Adaro Andalan Indonesia Tbk (IDX:AADI) | 2024-12-03 | Coal and Consumable Fuels | 4422654 | IDX | Energy and Utilities | Indonesia | Cyber 2 Tower | www.adaroindonesia.com | PT Adaro Andalan Indonesia Tbk engages in the ... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Freight Service; Marine Transportation; Mining... | None | 2004.000000 | 2004-12-01 00:00:00 | NaN | NaT | None | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Alamtri Resources Indonesia Tbk (Current In... | 1.890000 | 147513763.000000 | 1143231.663250 | 5.870000 | 456756180.000000 | 3539860.395000 | 5550.000000 | None | None | None | 36597123.720000 | 88200470.070000 | 2.217000 | 2.091000 | 18995952.510000 | 34608541.707000 | 15612589.197000 | 1033762.320000 | 95027.490000 | 16900161.822000 | NaN | NaN | 18489656.871000 | 51603346.350000 | 15934956.066000 | 33903719.536034 | 55264915.482548 | 18421357.075862 | 23354567.767490 | 18421357.075862 | 11866461.387490 | 15245298.528812 | 5766604.814061 | 7008203000.000000 | NaN | NaN | 70093003.221000 | 24558551.190000 | 6154.704000 |
| 4 | AADI | 2020 | PT Adaro Andalan Indonesia Tbk (IDX:AADI) | 2024-12-03 | Coal and Consumable Fuels | 4422654 | IDX | Energy and Utilities | Indonesia | Cyber 2 Tower | www.adaroindonesia.com | PT Adaro Andalan Indonesia Tbk engages in the ... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Freight Service; Marine Transportation; Mining... | None | 2004.000000 | 2004-12-01 00:00:00 | NaN | NaT | None | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Alamtri Resources Indonesia Tbk (Current In... | 1.890000 | 147513763.000000 | 1143231.663250 | 5.870000 | 456756180.000000 | 3539860.395000 | 5550.000000 | None | None | None | NaN | NaN | None | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
Cek Saham Tersedia di Financial Data
list_symbol_findat = df_findat_final['ticker'].unique().tolist()
print(list_symbol_findat)
print(len(list_symbol_findat))['AADI', 'AALI', 'ABBA', 'ABDA', 'ABMM', 'ACES', 'ACRO', 'ACST', 'ADCP', 'ADES', 'ADHI', 'ADMF', 'ADMG', 'ADMR', 'ADRO', 'AEGS', 'AGAR', 'AGII', 'AGRO', 'AGRS', 'AHAP', 'AIMS', 'AISA', 'AKKU', 'AKPI', 'AKRA', 'AKSI', 'ALDO', 'ALII', 'ALKA', 'ALMI', 'ALTO', 'AMAG', 'AMAN', 'AMAR', 'AMFG', 'AMIN', 'AMMN', 'AMMS', 'AMOR', 'AMRT', 'ANDI', 'ANJT', 'ANTM', 'APEX', 'APIC', 'APII', 'APLI', 'APLN', 'ARCI', 'AREA', 'ARGO', 'ARII', 'ARKA', 'ARKO', 'ARNA', 'ARTA', 'ARTI', 'ARTO', 'ASBI', 'ASDM', 'ASGR', 'ASHA', 'ASII', 'ASJT', 'ASLC', 'ASLI', 'ASMI', 'ASPI', 'ASPR', 'ASRI', 'ASRM', 'ASSA', 'ATAP', 'ATIC', 'ATLA', 'AUTO', 'AVIA', 'AWAN', 'AXIO', 'AYAM', 'AYLS', 'BABP', 'BABY', 'BACA', 'BAIK', 'BAJA', 'BALI', 'BANK', 'BAPA', 'BAPI', 'BATA', 'BATR', 'BAUT', 'BAYU', 'BBCA', 'BBHI', 'BBKP', 'BBLD', 'BBMD', 'BBNI', 'BBRI', 'BBRM', 'BBSI', 'BBSS', 'BBTN', 'BBYB', 'BCAP', 'BCIC', 'BCIP', 'BDKR', 'BDMN', 'BEBS', 'BEEF', 'BEER', 'BEKS', 'BELI', 'BELL', 'BESS', 'BEST', 'BFIN', 'BGTG', 'BHAT', 'BHIT', 'BIKA', 'BIKE', 'BIMA', 'BINA', 'BINO', 'BIPI', 'BIPP', 'BIRD', 'BISI', 'BJBR', 'BJTM', 'BKDP', 'BKSL', 'BKSW', 'BLES', 'BLOG', 'BLTA', 'BLTZ', 'BLUE', 'BMAS', 'BMBL', 'BMHS', 'BMRI', 'BMSR', 'BMTR', 'BNBA', 'BNBR', 'BNGA', 'BNII', 'BNLI', 'BOAT', 'BOBA', 'BOGA', 'BOLA', 'BOLT', 'BOSS', 'BPFI', 'BPII', 'BPTR', 'BRAM', 'BREN', 'BRIS', 'BRMS', 'BRNA', 'BRPT', 'BRRC', 'BSBK', 'BSDE', 'BSIM', 'BSML', 'BSSR', 'BSWD', 'BTEK', 'BTON', 'BTPN', 'BTPS', 'BUAH', 'BUDI', 'BUKA', 'BUKK', 'BULL', 'BUMI', 'BUVA', 'BVIC', 'BWPT', 'BYAN', 'CAKK', 'CAMP', 'CANI', 'CARE', 'CARS', 'CASA', 'CASH', 'CASS', 'CBDK', 'CBPE', 'CBRE', 'CBUT', 'CCSI', 'CDIA', 'CEKA', 'CENT', 'CFIN', 'CGAS', 'CHEK', 'CHEM', 'CHIP', 'CINT', 'CITA', 'CITY', 'CLAY', 'CLEO', 'CLPI', 'CMNP', 'CMNT', 'CMPP', 'CMRY', 'CNKO', 'CNMA', 'COAL', 'COCO', 'COIN', 'CPIN', 'CPRI', 'CPRO', 'CRAB', 'CRSN', 'CSAP', 'CSIS', 'CSMI', 'CSRA', 'CTBN', 'CTRA', 'CTTH', 'CUAN', 'CYBR', 'DAAZ', 'DADA', 'DART', 'DATA', 'DAYA', 'DCII', 'DEAL', 'DEFI', 'DEPO', 'DEWA', 'DEWI', 'DFAM', 'DGIK', 'DGNS', 'DGWG', 'DIGI', 'DILD', 'DIVA', 'DKFT', 'DKHH', 'DLTA', 'DMAS', 'DMMX', 'DMND', 'DNAR', 'DNET', 'DOID', 'DOOH', 'DOSS', 'DPNS', 'DPUM', 'DRMA', 'DSFI', 'DSNG', 'DSSA', 'DUTI', 'DVLA', 'DWGL', 'DYAN', 'EAST', 'ECII', 'EDGE', 'EKAD', 'ELIT', 'ELPI', 'ELSA', 'ELTY', 'EMAS', 'EMDE', 'EMTK', 'ENAK', 'ENRG', 'ENZO', 'EPAC', 'EPMT', 'ERAA', 'ERAL', 'ERTX', 'ESIP', 'ESSA', 'ESTA', 'ESTI', 'ETWA', 'EURO', 'EXCL', 'FAPA', 'FAST', 'FASW', 'FILM', 'FIMP', 'FIRE', 'FISH', 'FITT', 'FLMC', 'FMII', 'FOLK', 'FOOD', 'FORE', 'FORU', 'FPNI', 'FUJI', 'FUTR', 'FWCT', 'GAMA', 'GDST', 'GDYR', 'GEMA', 'GEMS', 'GGRM', 'GGRP', 'GHON', 'GIAA', 'GJTL', 'GLOB', 'GLVA', 'GMFI', 'GMTD', 'GOLD', 'GOLF', 'GOOD', 'GOTO', 'GPRA', 'GPSO', 'GRIA', 'GRPH', 'GRPM', 'GSMF', 'GTBO', 'GTRA', 'GTSI', 'GULA', 'GUNA', 'GWSA', 'GZCO', 'HADE', 'HAIS', 'HAJJ', 'HALO', 'HATM', 'HBAT', 'HDFA', 'HDIT', 'HEAL', 'HELI', 'HERO', 'HEXA', 'HGII', 'HILL', 'HITS', 'HKMU', 'HMSP', 'HOKI', 'HOMI', 'HOPE', 'HRME', 'HRTA', 'HRUM', 'HUMI', 'HYGN', 'IATA', 'IBFN', 'IBOS', 'IBST', 'ICBP', 'ICON', 'IDEA', 'IDPR', 'IFII', 'IFSH', 'IGAR', 'IKAI', 'IKAN', 'IKBI', 'IKPM', 'IMAS', 'IMJS', 'IMPC', 'INAF', 'INAI', 'INCF', 'INCI', 'INCO', 'INDF', 'INDO', 'INDR', 'INDS', 'INDX', 'INDY', 'INET', 'INKP', 'INOV', 'INPC', 'INPP', 'INPS', 'INRU', 'INTA', 'INTD', 'INTP', 'IOTF', 'IPAC', 'IPCC', 'IPCM', 'IPOL', 'IPPE', 'IPTV', 'IRRA', 'IRSX', 'ISAP', 'ISAT', 'ISEA', 'ISSP', 'ITIC', 'ITMA', 'ITMG', 'JARR', 'JAST', 'JATI', 'JAWA', 'JAYA', 'JECC', 'JGLE', 'JIHD', 'JKON', 'JMAS', 'JPFA', 'JRPT', 'JSMR', 'JSPT', 'JTPE', 'KAEF', 'KAQI', 'KARW', 'KAYU', 'KBAG', 'KBLI', 'KBLM', 'KBLV', 'KDSI', 'KDTN', 'KEEN', 'KEJU', 'KETR', 'KIAS', 'KICI', 'KIJA', 'KING', 'KINO', 'KIOS', 'KJEN', 'KKES', 'KKGI', 'KLAS', 'KLBF', 'KLIN', 'KMDS', 'KMTR', 'KOBX', 'KOCI', 'KOIN', 'KOKA', 'KONI', 'KOPI', 'KOTA', 'KPIG', 'KRAS', 'KREN', 'KRYA', 'KSIX', 'KUAS', 'LABA', 'LABS', 'LAJU', 'LAND', 'LAPD', 'LCKM', 'LEAD', 'LFLO', 'LIFE', 'LINK', 'LION', 'LIVE', 'LMAX', 'LMPI', 'LMSH', 'LOPI', 'LPCK', 'LPGI', 'LPIN', 'LPKR', 'LPLI', 'LPPF', 'LPPS', 'LRNA', 'LSIP', 'LTLS', 'LUCK', 'LUCY', 'MAHA', 'MAIN', 'MANG', 'MAPA', 'MAPB', 'MAPI', 'MARI', 'MARK', 'MASB', 'MAXI', 'MAYA', 'MBAP', 'MBMA', 'MBSS', 'MBTO', 'MCAS', 'MCOL', 'MCOR', 'MDIA', 'MDIY', 'MDKA', 'MDKI', 'MDLA', 'MDLN', 'MDRN', 'MEDC', 'MEDS', 'MEGA', 'MEJA', 'MENN', 'MERI', 'MERK', 'META', 'MFMI', 'MGLV', 'MGNA', 'MGRO', 'MHKI', 'MICE', 'MIDI', 'MIKA', 'MINA', 'MINE', 'MIRA', 'MITI', 'MKAP', 'MKNT', 'MKPI', 'MKTR', 'MLBI', 'MLIA', 'MLPL', 'MLPT', 'MMIX', 'MMLP', 'MNCN', 'MOLI', 'MORA', 'MPIX', 'MPMX', 'MPOW', 'MPPA', 'MPRO', 'MPXL', 'MRAT', 'MREI', 'MSIE', 'MSIN', 'MSJA', 'MSKY', 'MSTI', 'MTDL', 'MTEL', 'MTFN', 'MTLA', 'MTMH', 'MTPS', 'MTSM', 'MTWI', 'MUTU', 'MYOH', 'MYOR', 'MYTX', 'NAIK', 'NANO', 'NASA', 'NASI', 'NATO', 'NAYZ', 'NCKL', 'NELY', 'NEST', 'NETV', 'NFCX', 'NICE', 'NICK', 'NICL', 'NIKL', 'NINE', 'NIRO', 'NISP', 'NOBU', 'NPGF', 'NRCA', 'NSSS', 'NTBK', 'NZIA', 'OASA', 'OBAT', 'OBMD', 'OILS', 'OKAS', 'OLIV', 'OMED', 'OMRE', 'OPMS', 'PACK', 'PADA', 'PADI', 'PALM', 'PAMG', 'PANI', 'PANR', 'PANS', 'PART', 'PBID', 'PBRX', 'PBSA', 'PCAR', 'PDES', 'PDPP', 'PEGE', 'PEHA', 'PEVE', 'PGAS', 'PGEO', 'PGJO', 'PGLI', 'PGUN', 'PICO', 'PIPA', 'PJAA', 'PJHB', 'PKPK', 'PLAN', 'PLIN', 'PMJS', 'PMMP', 'PMUI', 'PNBN', 'PNBS', 'PNGO', 'PNIN', 'PNLF', 'PNSE', 'POLA', 'POLI', 'POLL', 'POLU', 'POLY', 'PORT', 'POWR', 'PPGL', 'PPRE', 'PPRI', 'PPRO', 'PRAY', 'PRDA', 'PRIM', 'PSAB', 'PSAT', 'PSDN', 'PSGO', 'PSKT', 'PSSI', 'PTBA', 'PTDU', 'PTIS', 'PTMP', 'PTMR', 'PTPP', 'PTPS', 'PTPW', 'PTRO', 'PTSN', 'PTSP', 'PUDP', 'PURA', 'PURI', 'PWON', 'PYFA', 'PZZA', 'RAAM', 'RAFI', 'RAJA', 'RALS', 'RANC', 'RATU', 'RBMS', 'RCCC', 'RDTX', 'REAL', 'RELF', 'RELI', 'RGAS', 'RICY', 'RIGS', 'RISE', 'RMKE', 'RMKO', 'ROCK', 'RODA', 'RONY', 'ROTI', 'RSCH', 'RSGK', 'RUIS', 'RUNS', 'SAFE', 'SAGE', 'SAME', 'SAMF', 'SAPX', 'SATU', 'SBAT', 'SBMA', 'SCCO', 'SCMA', 'SCNP', 'SDMU', 'SDPC', 'SDRA', 'SEMA', 'SFAN', 'SGER', 'SGRO', 'SHID', 'SHIP', 'SICO', 'SIDO', 'SILO', 'SIMP', 'SINI', 'SIPD', 'SKBM', 'SKLT', 'SKRN', 'SLIS', 'SMAR', 'SMBR', 'SMCB', 'SMDM', 'SMDR', 'SMGA', 'SMGR', 'SMIL', 'SMKL', 'SMKM', 'SMLE', 'SMMA', 'SMMT', 'SMRA', 'SMSM', 'SNLK', 'SOCI', 'SOFA', 'SOHO', 'SOLA', 'SONA', 'SOSS', 'SOTS', 'SOUL', 'SPMA', 'SPRE', 'SPTO', 'SQMI', 'SRAJ', 'SRSN', 'SRTG', 'SSIA', 'SSMS', 'SSTM', 'STAA', 'STAR', 'STRK', 'STTP', 'SULI', 'SUNI', 'SUPR', 'SURE', 'SURI', 'SWAT', 'SWID', 'TALF', 'TAMA', 'TAMU', 'TAPG', 'TARA', 'TAXI', 'TAYS', 'TBIG', 'TBLA', 'TBMS', 'TCID', 'TCPI', 'TEBE', 'TECH', 'TELE', 'TFAS', 'TFCO', 'TGKA', 'TGRA', 'TGUK', 'TIFA', 'TINS', 'TIRA', 'TIRT', 'TKIM', 'TLDN', 'TLKM', 'TMAS', 'TMPO', 'TNCA', 'TOBA', 'TOOL', 'TOPS', 'TOSK', 'TOTL', 'TOTO', 'TOWR', 'TOYS', 'TPIA', 'TPMA', 'TRGU', 'TRIM', 'TRIN', 'TRIS', 'TRJA', 'TRON', 'TRST', 'TRUE', 'TRUK', 'TRUS', 'TSPC', 'TUGU', 'TYRE', 'UANG', 'UCID', 'UDNG', 'UFOE', 'ULTJ', 'UNIC', 'UNIQ', 'UNSP', 'UNTD', 'UNTR', 'UNVR', 'URBN', 'UVCR', 'VAST', 'VERN', 'VICI', 'VICO', 'VINS', 'VISI', 'VIVA', 'VKTR', 'VOKS', 'VRNA', 'VTNY', 'WAPO', 'WEGE', 'WEHA', 'WGSH', 'WICO', 'WIDI', 'WIFI', 'WIIM', 'WIKA', 'WINE', 'WINR', 'WINS', 'WIRG', 'WMPP', 'WMUU', 'WOMF', 'WOOD', 'WOWS', 'WSBP', 'WSKT', 'WTON', 'XDIF', 'XIFE', 'XISB', 'XMGB', 'XMLF', 'XPSG', 'YELO', 'YOII', 'YPAS', 'YULE', 'YUPI', 'ZATA', 'ZBRA', 'ZINC', 'ZONE', 'ZYRX']
924
Hapus Efek Fund/Nonperusahaan (Ticker diawali X)
df_findat_final[df_findat_final['ticker'].str.startswith('X')]['Entity Name'].unique().tolist()['PT Danareksa Investment Management - Danareksa ETF Indonesia Top 40 Fund (IDX:XDIF)',
'Pt Indo Premier Investment Management - FTSE Indonesia ESG ETF (IDX:XIFE)',
'Pt Indo Premier Investment Management - Reksa Dana Premier Etf Indonesia Sovereign Bonds (IDX:XISB)',
'PT Majoris Asset Management - Government Bonds ETF (IDX:XMGB)',
'Pt Mandiri Manajemen Investasi - Reksa Dana Indeks Mandiri Indeks LQ45 (IDX:XMLF)',
'Pt Pinnacle Persada Investama - Pinnacle Indonesia Esg Etf (IDX:XPSG)']
df_findat_final = df_findat_final[~df_findat_final['ticker'].str.startswith('X')]
print("Cek Apakah Emiten Fund (Bukan Perusahaan) Masih Ada :")
df_findat_final[df_findat_final['ticker'].str.startswith('X')]['Entity Name'].unique().tolist()Cek Apakah Emiten Fund (Bukan Perusahaan) Masih Ada :
[]
Hapus Perusahaan Baru IPO (2024-2025)
df_emiten_idx[df_emiten_idx['Tanggal Pencatatan'].dt.year.isin([2024, 2025])]| Kode | Nama Perusahaan | Tanggal Pencatatan | Saham | Papan Pencatatan | Tanggal Masuk | Tanggal Keluar | Kriteria | PPK | Distress_PPK | |
|---|---|---|---|---|---|---|---|---|---|---|
| 884 | ASLI | Asri Karya Lestari Tbk. | 2024-01-05 | 6.250.000.000 | Pengembangan | NaT | NaT | <NA> | No | No |
| 885 | CGAS | Citra Nusantara Gemilang Tbk. | 2024-01-08 | 1.771.499.039 | Pengembangan | 2024-03-04 | 2024-04-04 | [10] | Yes | Yes |
| 886 | NICE | Adhi Kartiko Pratama Tbk. | 2024-01-09 | 6.082.020.000 | Pengembangan | NaT | NaT | <NA> | No | No |
| 887 | MSJA | Multi Spunindo Jaya Tbk. | 2024-01-10 | 5.882.352.900 | Pengembangan | NaT | NaT | <NA> | No | No |
| 888 | SMLE | Sinergi Multi Lestarindo Tbk. | 2024-01-10 | 2.328.153.048 | Pengembangan | NaT | NaT | <NA> | No | No |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 943 | KAQI | Jantra Grupo Indonesia Tbk. | 2025-03-10 | 2.075.800.000 | Pengembangan | NaT | NaT | <NA> | No | No |
| 944 | YUPI | Yupi Indo Jelly Gum Tbk. | 2025-03-25 | 8.544.488.700 | Utama | NaT | NaT | <NA> | No | No |
| 945 | FORE | Fore Kopi Indonesia Tbk. | 2025-04-14 | 8.918.359.270 | Pengembangan | NaT | NaT | <NA> | No | No |
| 946 | MDLA | Medela Potentia Tbk. | 2025-04-15 | 14.012.825.000 | Utama | NaT | NaT | <NA> | No | No |
| 947 | DKHH | Cipta Sarana Medika Tbk. | 2025-05-08 | 2.550.000.000 | Pengembangan | 2025-07-22 | 2025-07-31 | [10] | Yes | Yes |
64 rows × 10 columns
list_perusahaan_baru_ipo = df_emiten_idx[df_emiten_idx['Tanggal Pencatatan'].dt.year.isin([2024, 2025])]['Kode'].tolist()
list_perusahaan_baru_ipo.append('PJHB')
list_perusahaan_baru_ipo['ASLI',
'CGAS',
'NICE',
'MSJA',
'SMLE',
'ACRO',
'MANG',
'GRPH',
'SMGA',
'UNTD',
'TOSK',
'MPIX',
'ALII',
'MKAP',
'MEJA',
'LIVE',
'HYGN',
'BAIK',
'VISI',
'AREA',
'MHKI',
'ATLA',
'DATA',
'SOLA',
'BATR',
'SPRE',
'PART',
'GOLF',
'ISEA',
'BLES',
'GUNA',
'LABS',
'DOSS',
'NEST',
'PTMR',
'VERN',
'DAAZ',
'BOAT',
'NAIK',
'AADI',
'MDIY',
'KSIX',
'RATU',
'YOII',
'HGII',
'BRRC',
'DGWG',
'CBDK',
'OBAT',
'MINE',
'ASPR',
'PSAT',
'COIN',
'CDIA',
'BLOG',
'MERI',
'CHEK',
'PMUI',
'EMAS',
'KAQI',
'YUPI',
'FORE',
'MDLA',
'DKHH',
'PJHB']
print('Baris data finansial yang akan dihapus :')
df_findat_final[df_findat_final['ticker'].isin(list_perusahaan_baru_ipo)]Baris data finansial yang akan dihapus :
| variable | ticker | Year | Entity Name | IPO Date MM/dd/yyyy | Primary Industry | Entity ID | Exchange | 1st Level Primary Industry | Country / Region Name | Address 1 | Web Address | Business Description | Company Type | Investor Type | Company Status | Ownership Structure | Activist Investor? Yes/No | Institution Coverage Level | Consolidation Status | Has Current Financials? Yes/No | Has Bank Regulatory Financials? Yes/No | Has Third Party Financials? Yes/No | Long Business Description | Topic Tags | Crunchbase Categories | Year Established | Date Established MM/dd/yyyy | Year Incorporated | Date Incorporated MM/dd/yyyy | Month of Fiscal Year End | Headcount As Of MM/dd/yyyy | Industry Classification | 2nd Level Primary Industry | 3rd Level Primary Industry | 4th Level Primary Industry | Sector | Industry Group | Industry | City | Parent Company Name | Parent Ticker | Parent Exchange: Ticker | Parent Percent Owned (%) | Parent City | Parent Industry | Ultimate Parent Company Name | Ultimate Parent Ticker | Ultimate Parent Exchange: Ticker | Ultimate Parent City | Ultimate Parent Industry | Ultimate Parent Country / Region | All Investors | Percent Owned - All Institutions (%) | Shares Owned - All Institutions (actual) | Market Value - All Institutions (Rp.B) | Percent Owned - Insiders (%) | Shares Owned - Insiders (actual) | Market Value - Insiders (Rp.B) | IPO Price (Rp.) | Location Type | Data Precision | Data Year | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.B) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | AADI | 2024 | PT Adaro Andalan Indonesia Tbk (IDX:AADI) | 2024-12-03 | Coal and Consumable Fuels | 4422654 | IDX | Energy and Utilities | Indonesia | Cyber 2 Tower | www.adaroindonesia.com | PT Adaro Andalan Indonesia Tbk engages in the ... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Freight Service; Marine Transportation; Mining... | None | 2004.000000 | 2004-12-01 00:00:00 | NaN | NaT | None | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Alamtri Resources Indonesia Tbk (Current In... | 1.890000 | 147513763.000000 | 1143231.663250 | 5.870000 | 456756180.000000 | 3539860.395000 | 5550.000000 | None | None | None | 42324475.248000 | 96469808.484000 | 2.537000 | 2.292000 | 21593100.594000 | 35641809.096000 | 14048708.502000 | 1103501.802000 | 105989.232000 | 23185756.224000 | NaN | NaN | 23924525.640000 | 54145333.236000 | 22391497.002000 | 61072270.545424 | 84304634.017260 | 18392374.252099 | 19927346.291702 | 18392374.252099 | 21026086.808611 | 18994042.847576 | -16098252.230656 | 7786891760.000000 | 65993.907666 | 8475.000000 | 78069858.876000 | 25454061.012000 | 37588.830000 |
| 1 | AADI | 2023 | PT Adaro Andalan Indonesia Tbk (IDX:AADI) | 2024-12-03 | Coal and Consumable Fuels | 4422654 | IDX | Energy and Utilities | Indonesia | Cyber 2 Tower | www.adaroindonesia.com | PT Adaro Andalan Indonesia Tbk engages in the ... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Freight Service; Marine Transportation; Mining... | None | 2004.000000 | 2004-12-01 00:00:00 | NaN | NaT | None | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Alamtri Resources Indonesia Tbk (Current In... | 1.890000 | 147513763.000000 | 1143231.663250 | 5.870000 | 456756180.000000 | 3539860.395000 | 5550.000000 | None | None | None | 35027575.128000 | 108867787.224000 | 1.818000 | 1.699000 | 22687219.212000 | 50406307.896000 | 27719088.684000 | 1003297.260000 | 66758.034000 | 2978539.704000 | NaN | NaN | 14784754.278000 | 73840212.096000 | 14962801.392000 | 63789705.022615 | 90132389.148923 | 21711813.969577 | 23121835.271885 | 21711813.969577 | 19592972.794962 | 9069085.144385 | -12888255.983885 | 7008202560.000000 | NaN | NaN | 88624966.374000 | 39810523.914000 | 35390.544000 |
| 2 | AADI | 2022 | PT Adaro Andalan Indonesia Tbk (IDX:AADI) | 2024-12-03 | Coal and Consumable Fuels | 4422654 | IDX | Energy and Utilities | Indonesia | Cyber 2 Tower | www.adaroindonesia.com | PT Adaro Andalan Indonesia Tbk engages in the ... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Freight Service; Marine Transportation; Mining... | None | 2004.000000 | 2004-12-01 00:00:00 | NaN | NaT | None | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Alamtri Resources Indonesia Tbk (Current In... | 1.890000 | 147513763.000000 | 1143231.663250 | 5.870000 | 456756180.000000 | 3539860.395000 | 5550.000000 | None | None | None | 53697263.230000 | 123705349.494000 | 2.043000 | 1.960000 | 34306837.619000 | 67190488.975000 | 32883651.356000 | 1864970.784000 | 10124.908000 | 16805809.909000 | NaN | NaN | 18638045.561000 | 70008086.264000 | 13750882.913000 | 56091453.934708 | 114740624.204931 | 52675640.780862 | 57656683.481331 | 52675640.780862 | 34894343.232646 | 46564112.440085 | 27880000.623946 | 7008203000.000000 | NaN | NaN | 88646131.825000 | 54122633.598000 | 32610.900000 |
| 3 | AADI | 2021 | PT Adaro Andalan Indonesia Tbk (IDX:AADI) | 2024-12-03 | Coal and Consumable Fuels | 4422654 | IDX | Energy and Utilities | Indonesia | Cyber 2 Tower | www.adaroindonesia.com | PT Adaro Andalan Indonesia Tbk engages in the ... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Freight Service; Marine Transportation; Mining... | None | 2004.000000 | 2004-12-01 00:00:00 | NaN | NaT | None | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Alamtri Resources Indonesia Tbk (Current In... | 1.890000 | 147513763.000000 | 1143231.663250 | 5.870000 | 456756180.000000 | 3539860.395000 | 5550.000000 | None | None | None | 36597123.720000 | 88200470.070000 | 2.217000 | 2.091000 | 18995952.510000 | 34608541.707000 | 15612589.197000 | 1033762.320000 | 95027.490000 | 16900161.822000 | NaN | NaN | 18489656.871000 | 51603346.350000 | 15934956.066000 | 33903719.536034 | 55264915.482548 | 18421357.075862 | 23354567.767490 | 18421357.075862 | 11866461.387490 | 15245298.528812 | 5766604.814061 | 7008203000.000000 | NaN | NaN | 70093003.221000 | 24558551.190000 | 6154.704000 |
| 4 | AADI | 2020 | PT Adaro Andalan Indonesia Tbk (IDX:AADI) | 2024-12-03 | Coal and Consumable Fuels | 4422654 | IDX | Energy and Utilities | Indonesia | Cyber 2 Tower | www.adaroindonesia.com | PT Adaro Andalan Indonesia Tbk engages in the ... | Public Company | None | Operating | None | No | None | None | Yes | No | No | None | Freight Service; Marine Transportation; Mining... | None | 2004.000000 | 2004-12-01 00:00:00 | NaN | NaT | None | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | None | None | None | NA% | None | None | None | None | None | None | None | None | PT Alamtri Resources Indonesia Tbk (Current In... | 1.890000 | 147513763.000000 | 1143231.663250 | 5.870000 | 456756180.000000 | 3539860.395000 | 5550.000000 | None | None | None | NaN | NaN | None | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 15618 | YUPI | 2012 | PT Yupi Indo Jelly Gum Tbk (IDX:YUPI) | 2025-03-21 | Packaged Foods and Meats Producers | 8608372 | IDX | Consumer | Indonesia | Gedung Mugi Griya Lt 4 | www.yupindo.com | PT Yupi Indo Jelly Gum Tbk manufactures and se... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | None | Fast-Moving Consumer Goods; Packaging Services... | None | 1995.000000 | 1995-07-06 00:00:00 | NaN | NaT | None | 2025-09-01 | Packaged Foods and Meats Producers; Confection... | Producers | Packaged Foods and Meats Producers | None | Consumer Staples | Food, Beverage and Tobacco | Food Products | Jakarta Selatan | PT Sweets Indonesia | None | None | 0.899000 | Jakarta Selatan | Holding Companies, Patent Owners, and Trusts o... | PT Sweets Indonesia | None | None | Jakarta Selatan | Holding Companies, Patent Owners, and Trusts o... | Indonesia | Affinity Equity Partners Limited (Current Inve... | NaN | NaN | NaN | NaN | NaN | NaN | 2390.000000 | None | None | None | NaN | NaN | None | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 15619 | YUPI | 2011 | PT Yupi Indo Jelly Gum Tbk (IDX:YUPI) | 2025-03-21 | Packaged Foods and Meats Producers | 8608372 | IDX | Consumer | Indonesia | Gedung Mugi Griya Lt 4 | www.yupindo.com | PT Yupi Indo Jelly Gum Tbk manufactures and se... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | None | Fast-Moving Consumer Goods; Packaging Services... | None | 1995.000000 | 1995-07-06 00:00:00 | NaN | NaT | None | 2025-09-01 | Packaged Foods and Meats Producers; Confection... | Producers | Packaged Foods and Meats Producers | None | Consumer Staples | Food, Beverage and Tobacco | Food Products | Jakarta Selatan | PT Sweets Indonesia | None | None | 0.899000 | Jakarta Selatan | Holding Companies, Patent Owners, and Trusts o... | PT Sweets Indonesia | None | None | Jakarta Selatan | Holding Companies, Patent Owners, and Trusts o... | Indonesia | Affinity Equity Partners Limited (Current Inve... | NaN | NaN | NaN | NaN | NaN | NaN | 2390.000000 | None | None | None | NaN | NaN | None | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 15620 | YUPI | 2010 | PT Yupi Indo Jelly Gum Tbk (IDX:YUPI) | 2025-03-21 | Packaged Foods and Meats Producers | 8608372 | IDX | Consumer | Indonesia | Gedung Mugi Griya Lt 4 | www.yupindo.com | PT Yupi Indo Jelly Gum Tbk manufactures and se... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | None | Fast-Moving Consumer Goods; Packaging Services... | None | 1995.000000 | 1995-07-06 00:00:00 | NaN | NaT | None | 2025-09-01 | Packaged Foods and Meats Producers; Confection... | Producers | Packaged Foods and Meats Producers | None | Consumer Staples | Food, Beverage and Tobacco | Food Products | Jakarta Selatan | PT Sweets Indonesia | None | None | 0.899000 | Jakarta Selatan | Holding Companies, Patent Owners, and Trusts o... | PT Sweets Indonesia | None | None | Jakarta Selatan | Holding Companies, Patent Owners, and Trusts o... | Indonesia | Affinity Equity Partners Limited (Current Inve... | NaN | NaN | NaN | NaN | NaN | NaN | 2390.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 15621 | YUPI | 2009 | PT Yupi Indo Jelly Gum Tbk (IDX:YUPI) | 2025-03-21 | Packaged Foods and Meats Producers | 8608372 | IDX | Consumer | Indonesia | Gedung Mugi Griya Lt 4 | www.yupindo.com | PT Yupi Indo Jelly Gum Tbk manufactures and se... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | None | Fast-Moving Consumer Goods; Packaging Services... | None | 1995.000000 | 1995-07-06 00:00:00 | NaN | NaT | None | 2025-09-01 | Packaged Foods and Meats Producers; Confection... | Producers | Packaged Foods and Meats Producers | None | Consumer Staples | Food, Beverage and Tobacco | Food Products | Jakarta Selatan | PT Sweets Indonesia | None | None | 0.899000 | Jakarta Selatan | Holding Companies, Patent Owners, and Trusts o... | PT Sweets Indonesia | None | None | Jakarta Selatan | Holding Companies, Patent Owners, and Trusts o... | Indonesia | Affinity Equity Partners Limited (Current Inve... | NaN | NaN | NaN | NaN | NaN | NaN | 2390.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 15622 | YUPI | 2008 | PT Yupi Indo Jelly Gum Tbk (IDX:YUPI) | 2025-03-21 | Packaged Foods and Meats Producers | 8608372 | IDX | Consumer | Indonesia | Gedung Mugi Griya Lt 4 | www.yupindo.com | PT Yupi Indo Jelly Gum Tbk manufactures and se... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | None | Fast-Moving Consumer Goods; Packaging Services... | None | 1995.000000 | 1995-07-06 00:00:00 | NaN | NaT | None | 2025-09-01 | Packaged Foods and Meats Producers; Confection... | Producers | Packaged Foods and Meats Producers | None | Consumer Staples | Food, Beverage and Tobacco | Food Products | Jakarta Selatan | PT Sweets Indonesia | None | None | 0.899000 | Jakarta Selatan | Holding Companies, Patent Owners, and Trusts o... | PT Sweets Indonesia | None | None | Jakarta Selatan | Holding Companies, Patent Owners, and Trusts o... | Indonesia | Affinity Equity Partners Limited (Current Inve... | NaN | NaN | NaN | NaN | NaN | NaN | 2390.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1105 rows × 91 columns
df_findat_final = df_findat_final[~df_findat_final['ticker'].isin(list_perusahaan_baru_ipo)]
print("Dataset Setelah Emiten Baru IPO Dihapus :\n")
df_findat_finalDataset Setelah Emiten Baru IPO Dihapus :
| variable | ticker | Year | Entity Name | IPO Date MM/dd/yyyy | Primary Industry | Entity ID | Exchange | 1st Level Primary Industry | Country / Region Name | Address 1 | Web Address | Business Description | Company Type | Investor Type | Company Status | Ownership Structure | Activist Investor? Yes/No | Institution Coverage Level | Consolidation Status | Has Current Financials? Yes/No | Has Bank Regulatory Financials? Yes/No | Has Third Party Financials? Yes/No | Long Business Description | Topic Tags | Crunchbase Categories | Year Established | Date Established MM/dd/yyyy | Year Incorporated | Date Incorporated MM/dd/yyyy | Month of Fiscal Year End | Headcount As Of MM/dd/yyyy | Industry Classification | 2nd Level Primary Industry | 3rd Level Primary Industry | 4th Level Primary Industry | Sector | Industry Group | Industry | City | Parent Company Name | Parent Ticker | Parent Exchange: Ticker | Parent Percent Owned (%) | Parent City | Parent Industry | Ultimate Parent Company Name | Ultimate Parent Ticker | Ultimate Parent Exchange: Ticker | Ultimate Parent City | Ultimate Parent Industry | Ultimate Parent Country / Region | All Investors | Percent Owned - All Institutions (%) | Shares Owned - All Institutions (actual) | Market Value - All Institutions (Rp.B) | Percent Owned - Insiders (%) | Shares Owned - Insiders (actual) | Market Value - Insiders (Rp.B) | IPO Price (Rp.) | Location Type | Data Precision | Data Year | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.B) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 17 | AALI | 2024 | PT Astra Agro Lestari Tbk (IDX:AALI) | NaT | Agricultural Products and Services | 4567075 | IDX | Consumer | Indonesia | Jl Puloayang Raya | www.astra-agro.co.id | PT Astra Agro Lestari Tbk engages in the crude... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Astra Agro Lestari Tbk is a prominent playe... | None | Agriculture, Business Intelligence, Communities | 1988.000000 | 1988-10-03 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Agricultural Products and Services; Grain And ... | Producers | Agricultural Products and Services | None | Consumer Staples | Food, Beverage and Tobacco | Food Products | Jakarta | PT Astra International Tbk | ASII | IDX:ASII | 0.796800 | Jakarta | Automobile Manufacturers | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Astra International Tbk (Current Subsidiary... | 2.100000 | 40473542.000000 | 315693.627600 | NaN | NaN | NaN | NaN | None | None | None | 5591163.000000 | 28793225.000000 | 2.605000 | 1.126000 | 5195985.000000 | 8433638.000000 | 3237653.000000 | 3981669.000000 | NaN | 1500000.000000 | NaN | NaN | 3189537.000000 | 23202062.000000 | 17429693.000000 | 18358928.000000 | 21815035.000000 | 1788141.000000 | 3202738.000000 | 1788141.000000 | 1186783.000000 | 3379195.000000 | 1146504.000000 | 1924688333.000000 | 11933.067665 | 6200.000000 | 26391599.000000 | 3236012.000000 | NaN |
| 18 | AALI | 2023 | PT Astra Agro Lestari Tbk (IDX:AALI) | NaT | Agricultural Products and Services | 4567075 | IDX | Consumer | Indonesia | Jl Puloayang Raya | www.astra-agro.co.id | PT Astra Agro Lestari Tbk engages in the crude... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Astra Agro Lestari Tbk is a prominent playe... | None | Agriculture, Business Intelligence, Communities | 1988.000000 | 1988-10-03 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Agricultural Products and Services; Grain And ... | Producers | Agricultural Products and Services | None | Consumer Staples | Food, Beverage and Tobacco | Food Products | Jakarta | PT Astra International Tbk | ASII | IDX:ASII | 0.796800 | Jakarta | Automobile Manufacturers | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Astra International Tbk (Current Subsidiary... | 2.100000 | 40473542.000000 | 315693.627600 | NaN | NaN | NaN | NaN | None | None | None | 6280237.000000 | 28846243.000000 | 1.834000 | 0.765000 | 3236061.000000 | 7118202.000000 | 3882141.000000 | 3122454.000000 | NaN | 1689754.000000 | NaN | NaN | 4005052.000000 | 22566006.000000 | 18105649.000000 | 17950652.000000 | 20745473.000000 | 1672092.000000 | 3007949.000000 | 1672092.000000 | 1088170.000000 | 2538738.000000 | 469892.000000 | 1924688333.000000 | 13520.935539 | 7025.000000 | 26571058.000000 | 2089508.000000 | NaN |
| 19 | AALI | 2022 | PT Astra Agro Lestari Tbk (IDX:AALI) | NaT | Agricultural Products and Services | 4567075 | IDX | Consumer | Indonesia | Jl Puloayang Raya | www.astra-agro.co.id | PT Astra Agro Lestari Tbk engages in the crude... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Astra Agro Lestari Tbk is a prominent playe... | None | Agriculture, Business Intelligence, Communities | 1988.000000 | 1988-10-03 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Agricultural Products and Services; Grain And ... | Producers | Agricultural Products and Services | None | Consumer Staples | Food, Beverage and Tobacco | Food Products | Jakarta | PT Astra International Tbk | ASII | IDX:ASII | 0.796800 | Jakarta | Automobile Manufacturers | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Astra International Tbk (Current Subsidiary... | 2.100000 | 40473542.000000 | 315693.627600 | NaN | NaN | NaN | NaN | None | None | None | 7006119.000000 | 29249340.000000 | 3.600000 | 1.227000 | 5337669.000000 | 7390608.000000 | 2052939.000000 | 3434768.000000 | NaN | 4048767.000000 | NaN | NaN | 4053767.000000 | 22243221.000000 | 17996321.000000 | 18176356.000000 | 21828591.000000 | 2447192.000000 | 3710458.000000 | 2447192.000000 | 1792050.000000 | 1835397.000000 | -2276406.000000 | 1924688333.000000 | 15445.623872 | 8025.000000 | 26296988.000000 | 1619616.000000 | NaN |
| 20 | AALI | 2021 | PT Astra Agro Lestari Tbk (IDX:AALI) | NaT | Agricultural Products and Services | 4567075 | IDX | Consumer | Indonesia | Jl Puloayang Raya | www.astra-agro.co.id | PT Astra Agro Lestari Tbk engages in the crude... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Astra Agro Lestari Tbk is a prominent playe... | None | Agriculture, Business Intelligence, Communities | 1988.000000 | 1988-10-03 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Agricultural Products and Services; Grain And ... | Producers | Agricultural Products and Services | None | Consumer Staples | Food, Beverage and Tobacco | Food Products | Jakarta | PT Astra International Tbk | ASII | IDX:ASII | 0.796800 | Jakarta | Automobile Manufacturers | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Astra International Tbk (Current Subsidiary... | 2.100000 | 40473542.000000 | 315693.627600 | NaN | NaN | NaN | NaN | None | None | None | 9228733.000000 | 30399906.000000 | 1.579000 | 0.752000 | 3453812.000000 | 9414208.000000 | 5960396.000000 | 3381506.000000 | NaN | 2253611.000000 | NaN | NaN | 5818072.000000 | 21171173.000000 | 17781227.000000 | 19442136.000000 | 24322048.000000 | 3066334.000000 | 4314916.000000 | 3066334.000000 | 2067362.000000 | 4895119.000000 | 2917130.000000 | 1924688333.000000 | 18284.539164 | 9500.000000 | 26989245.000000 | 3896022.000000 | NaN |
| 21 | AALI | 2020 | PT Astra Agro Lestari Tbk (IDX:AALI) | NaT | Agricultural Products and Services | 4567075 | IDX | Consumer | Indonesia | Jl Puloayang Raya | www.astra-agro.co.id | PT Astra Agro Lestari Tbk engages in the crude... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Astra Agro Lestari Tbk is a prominent playe... | None | Agriculture, Business Intelligence, Communities | 1988.000000 | 1988-10-03 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Agricultural Products and Services; Grain And ... | Producers | Agricultural Products and Services | None | Consumer Staples | Food, Beverage and Tobacco | Food Products | Jakarta | PT Astra International Tbk | ASII | IDX:ASII | 0.796800 | Jakarta | Automobile Manufacturers | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Astra International Tbk (Current Subsidiary... | 2.100000 | 40473542.000000 | 315693.627600 | NaN | NaN | NaN | NaN | None | None | None | 8533437.000000 | 27781231.000000 | 3.313000 | 1.322000 | 4145384.000000 | 5937890.000000 | 1792506.000000 | 2505048.000000 | NaN | 5858300.000000 | NaN | NaN | 5858300.000000 | 19247794.000000 | 17741481.000000 | 15809752.000000 | 18807043.000000 | 1708412.000000 | 3031177.000000 | 1708412.000000 | 893779.000000 | 2322164.000000 | 595526.000000 | 1924688333.000000 | 23721.783704 | 12325.000000 | 25106094.000000 | 978892.000000 | NaN |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 15703 | ZYRX | 2012 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | 2021-03-26 | Technology Hardware, Storage and Peripherals | 5557676 | IDX | Technology, Media & Telecommunications | Indonesia | Jl. Daan Mogot No. 59 | zyrex.com | PT Zyrexindo Mandiri Buana Tbk assembles and s... | Public Company | None | Operating | None | No | Full | Consolidated | Yes | No | No | PT Zyrexindo Mandiri Buana Tbk actively operat... | Software Development; Sensors; Cloud Infrastru... | None | 1996.000000 | None | 1996.000000 | 1996-05-14 | Dec | 2025-08-01 | Technology Hardware, Storage and Peripherals; ... | Information Technology | Technology Hardware and Equipment | Technology Hardware, Storage and Peripherals | Information Technology | Technology Hardware and Equipment | Technology Hardware, Storage and Peripherals | Jakarta Barat | None | None | None | NA% | None | None | None | None | None | None | None | None | None | NaN | NaN | NaN | 75.160000 | 1002076700.000000 | 178369.652600 | 250.000000 | None | None | None | NaN | NaN | None | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 15704 | ZYRX | 2011 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | 2021-03-26 | Technology Hardware, Storage and Peripherals | 5557676 | IDX | Technology, Media & Telecommunications | Indonesia | Jl. Daan Mogot No. 59 | zyrex.com | PT Zyrexindo Mandiri Buana Tbk assembles and s... | Public Company | None | Operating | None | No | Full | Consolidated | Yes | No | No | PT Zyrexindo Mandiri Buana Tbk actively operat... | Software Development; Sensors; Cloud Infrastru... | None | 1996.000000 | None | 1996.000000 | 1996-05-14 | Dec | 2025-08-01 | Technology Hardware, Storage and Peripherals; ... | Information Technology | Technology Hardware and Equipment | Technology Hardware, Storage and Peripherals | Information Technology | Technology Hardware and Equipment | Technology Hardware, Storage and Peripherals | Jakarta Barat | None | None | None | NA% | None | None | None | None | None | None | None | None | None | NaN | NaN | NaN | 75.160000 | 1002076700.000000 | 178369.652600 | 250.000000 | None | None | None | NaN | NaN | None | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 15705 | ZYRX | 2010 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | 2021-03-26 | Technology Hardware, Storage and Peripherals | 5557676 | IDX | Technology, Media & Telecommunications | Indonesia | Jl. Daan Mogot No. 59 | zyrex.com | PT Zyrexindo Mandiri Buana Tbk assembles and s... | Public Company | None | Operating | None | No | Full | Consolidated | Yes | No | No | PT Zyrexindo Mandiri Buana Tbk actively operat... | Software Development; Sensors; Cloud Infrastru... | None | 1996.000000 | None | 1996.000000 | 1996-05-14 | Dec | 2025-08-01 | Technology Hardware, Storage and Peripherals; ... | Information Technology | Technology Hardware and Equipment | Technology Hardware, Storage and Peripherals | Information Technology | Technology Hardware and Equipment | Technology Hardware, Storage and Peripherals | Jakarta Barat | None | None | None | NA% | None | None | None | None | None | None | None | None | None | NaN | NaN | NaN | 75.160000 | 1002076700.000000 | 178369.652600 | 250.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 15706 | ZYRX | 2009 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | 2021-03-26 | Technology Hardware, Storage and Peripherals | 5557676 | IDX | Technology, Media & Telecommunications | Indonesia | Jl. Daan Mogot No. 59 | zyrex.com | PT Zyrexindo Mandiri Buana Tbk assembles and s... | Public Company | None | Operating | None | No | Full | Consolidated | Yes | No | No | PT Zyrexindo Mandiri Buana Tbk actively operat... | Software Development; Sensors; Cloud Infrastru... | None | 1996.000000 | None | 1996.000000 | 1996-05-14 | Dec | 2025-08-01 | Technology Hardware, Storage and Peripherals; ... | Information Technology | Technology Hardware and Equipment | Technology Hardware, Storage and Peripherals | Information Technology | Technology Hardware and Equipment | Technology Hardware, Storage and Peripherals | Jakarta Barat | None | None | None | NA% | None | None | None | None | None | None | None | None | None | NaN | NaN | NaN | 75.160000 | 1002076700.000000 | 178369.652600 | 250.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 15707 | ZYRX | 2008 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | 2021-03-26 | Technology Hardware, Storage and Peripherals | 5557676 | IDX | Technology, Media & Telecommunications | Indonesia | Jl. Daan Mogot No. 59 | zyrex.com | PT Zyrexindo Mandiri Buana Tbk assembles and s... | Public Company | None | Operating | None | No | Full | Consolidated | Yes | No | No | PT Zyrexindo Mandiri Buana Tbk actively operat... | Software Development; Sensors; Cloud Infrastru... | None | 1996.000000 | None | 1996.000000 | 1996-05-14 | Dec | 2025-08-01 | Technology Hardware, Storage and Peripherals; ... | Information Technology | Technology Hardware and Equipment | Technology Hardware, Storage and Peripherals | Information Technology | Technology Hardware and Equipment | Technology Hardware, Storage and Peripherals | Jakarta Barat | None | None | None | NA% | None | None | None | None | None | None | None | None | None | NaN | NaN | NaN | 75.160000 | 1002076700.000000 | 178369.652600 | 250.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
14501 rows × 91 columns
Hapus Baris Tahun Sebelum IPO
print('Cek perusahaan yang ada di df_findat_final tapi tidak ada di df_emiten_idx')
df_findat_final[~df_findat_final['ticker'].isin(df_emiten_idx['Kode'])]Cek perusahaan yang ada di df_findat_final tapi tidak ada di df_emiten_idx
| variable | ticker | Year | Entity Name | IPO Date MM/dd/yyyy | Primary Industry | Entity ID | Exchange | 1st Level Primary Industry | Country / Region Name | Address 1 | Web Address | Business Description | Company Type | Investor Type | Company Status | Ownership Structure | Activist Investor? Yes/No | Institution Coverage Level | Consolidation Status | Has Current Financials? Yes/No | Has Bank Regulatory Financials? Yes/No | Has Third Party Financials? Yes/No | Long Business Description | Topic Tags | Crunchbase Categories | Year Established | Date Established MM/dd/yyyy | Year Incorporated | Date Incorporated MM/dd/yyyy | Month of Fiscal Year End | Headcount As Of MM/dd/yyyy | Industry Classification | 2nd Level Primary Industry | 3rd Level Primary Industry | 4th Level Primary Industry | Sector | Industry Group | Industry | City | Parent Company Name | Parent Ticker | Parent Exchange: Ticker | Parent Percent Owned (%) | Parent City | Parent Industry | Ultimate Parent Company Name | Ultimate Parent Ticker | Ultimate Parent Exchange: Ticker | Ultimate Parent City | Ultimate Parent Industry | Ultimate Parent Country / Region | All Investors | Percent Owned - All Institutions (%) | Shares Owned - All Institutions (actual) | Market Value - All Institutions (Rp.B) | Percent Owned - Insiders (%) | Shares Owned - Insiders (actual) | Market Value - Insiders (Rp.B) | IPO Price (Rp.) | Location Type | Data Precision | Data Year | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.B) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) |
|---|
dict_perusahaan_tahun_ipo = dict(zip(df_emiten_idx['Kode'], df_emiten_idx['Tanggal Pencatatan'].dt.year))
print(dict_perusahaan_tahun_ipo){'AALI': 1997, 'ABBA': 2002, 'ABDA': 1989, 'ABMM': 2011, 'ACES': 2007, 'ACST': 2013, 'ADES': 1994, 'ADHI': 2004, 'ADMF': 2004, 'ADMG': 1993, 'ADRO': 2008, 'AGII': 2016, 'AGRO': 2003, 'AGRS': 2014, 'AHAP': 1990, 'AIMS': 2001, 'AISA': 1997, 'AKKU': 2004, 'AKPI': 1992, 'AKRA': 1994, 'AKSI': 2001, 'ALDO': 2011, 'ALKA': 1990, 'ALMI': 1997, 'ALTO': 2012, 'AMAG': 2005, 'AMFG': 1995, 'AMIN': 2015, 'AMRT': 2009, 'ANJT': 2013, 'ANTM': 1997, 'APEX': 2013, 'APIC': 2002, 'APII': 2013, 'APLI': 2000, 'APLN': 2010, 'ARGO': 1991, 'ARII': 2011, 'ARNA': 2001, 'ARTA': 2002, 'ARTI': 2003, 'ARTO': 2016, 'ASBI': 1989, 'ASDM': 1989, 'ASGR': 1989, 'ASII': 1990, 'ASJT': 2003, 'ASMI': 2014, 'ASRI': 2007, 'ASRM': 1990, 'ASSA': 2012, 'ATIC': 2015, 'AUTO': 1998, 'BABP': 2002, 'BACA': 2007, 'BAJA': 2011, 'BALI': 2014, 'BAPA': 2008, 'BATA': 1982, 'BAYU': 1989, 'BBCA': 2000, 'BBHI': 2015, 'BBKP': 2006, 'BBLD': 1990, 'BBMD': 2013, 'BBNI': 1996, 'BBRI': 2003, 'BBRM': 2013, 'BBTN': 2009, 'BBYB': 2015, 'BCAP': 2001, 'BCIC': 1997, 'BCIP': 2009, 'BDMN': 1989, 'BEKS': 2001, 'BEST': 2012, 'BFIN': 1990, 'BGTG': 2016, 'BHIT': 1997, 'BIKA': 2015, 'BIMA': 1994, 'BINA': 2014, 'BIPI': 2010, 'BIPP': 1995, 'BIRD': 2014, 'BISI': 2007, 'BJBR': 2010, 'BJTM': 2012, 'BKDP': 2007, 'BKSL': 1997, 'BKSW': 2002, 'BLTA': 1990, 'BLTZ': 2014, 'BMAS': 2013, 'BMRI': 2003, 'BMSR': 1999, 'BMTR': 1995, 'BNBA': 2006, 'BNBR': 1989, 'BNGA': 1989, 'BNII': 1989, 'BNLI': 1990, 'BOLT': 2015, 'BPFI': 2009, 'BPII': 2014, 'BRAM': 1990, 'BRMS': 2010, 'BRNA': 1989, 'BRPT': 1993, 'BSDE': 2008, 'BSIM': 2010, 'BSSR': 2012, 'BSWD': 2002, 'BTEK': 2004, 'BTEL': 2006, 'BTON': 2001, 'BTPN': 2008, 'BUDI': 1995, 'BUKK': 2015, 'BULL': 2011, 'BUMI': 1990, 'BUVA': 2010, 'BVIC': 1999, 'BWPT': 2009, 'BYAN': 2008, 'CANI': 2014, 'CASS': 2011, 'CEKA': 1996, 'CENT': 2001, 'CFIN': 1990, 'CINT': 2014, 'CITA': 2002, 'CLPI': 2001, 'CMNP': 1995, 'CMPP': 1994, 'CNKO': 2001, 'CNTX': 1979, 'COWL': 2007, 'CPIN': 1991, 'CPRO': 2006, 'CSAP': 2007, 'CTBN': 1989, 'CTRA': 1994, 'CTTH': 1996, 'DART': 1990, 'DEFI': 2001, 'DEWA': 2007, 'DGIK': 2007, 'DILD': 1991, 'DKFT': 1997, 'DLTA': 1984, 'DMAS': 2015, 'DNAR': 2014, 'DNET': 2000, 'DOID': 2001, 'DPNS': 1990, 'DSFI': 2000, 'DSNG': 2013, 'DSSA': 2009, 'DUTI': 1994, 'DVLA': 1994, 'DYAN': 2013, 'ECII': 2013, 'EKAD': 1990, 'ELSA': 2008, 'ELTY': 1995, 'EMDE': 2011, 'EMTK': 2010, 'ENRG': 2004, 'EPMT': 1994, 'ERAA': 2011, 'ERTX': 1990, 'ESSA': 2012, 'ESTI': 1992, 'ETWA': 1997, 'EXCL': 2005, 'FAST': 1993, 'FASW': 1994, 'FISH': 2002, 'FMII': 2000, 'FORU': 2002, 'FPNI': 2002, 'GAMA': 2012, 'GDST': 2009, 'GDYR': 1980, 'GEMA': 2002, 'GEMS': 2011, 'GGRM': 1990, 'GIAA': 2011, 'GJTL': 1990, 'GLOB': 2012, 'GMTD': 2000, 'GOLD': 2010, 'GOLL': 2014, 'GPRA': 2007, 'GSMF': 1989, 'GTBO': 2009, 'GWSA': 2011, 'GZCO': 2008, 'HADE': 2004, 'HDFA': 2011, 'HERO': 1989, 'HEXA': 1995, 'HITS': 1997, 'HMSP': 1990, 'HOME': 2008, 'HOTL': 2013, 'HRUM': 2010, 'IATA': 2006, 'IBFN': 2014, 'IBST': 2012, 'ICBP': 2010, 'ICON': 2005, 'IGAR': 1990, 'IIKP': 2002, 'IKAI': 1997, 'IKBI': 1991, 'IMAS': 1993, 'IMJS': 2013, 'IMPC': 2014, 'INAF': 2001, 'INAI': 1994, 'INCI': 1990, 'INCO': 1990, 'INDF': 1994, 'INDR': 1990, 'INDS': 1990, 'INDX': 2001, 'INDY': 2008, 'INKP': 1990, 'INPC': 1990, 'INPP': 2004, 'INRU': 1990, 'INTA': 1993, 'INTD': 1989, 'INTP': 1989, 'IPOL': 2010, 'ISAT': 1994, 'ISSP': 2013, 'ITMA': 1990, 'ITMG': 2007, 'JAWA': 2011, 'JECC': 1992, 'JIHD': 1984, 'JKON': 2007, 'JPFA': 1989, 'JRPT': 1994, 'JSMR': 2007, 'JSPT': 1998, 'JTPE': 2002, 'KAEF': 2001, 'KARW': 1994, 'KBLI': 1992, 'KBLM': 1992, 'KBLV': 2000, 'KBRI': 2008, 'KDSI': 1996, 'KIAS': 1994, 'KICI': 1993, 'KIJA': 1995, 'KKGI': 1991, 'KLBF': 1991, 'KOBX': 2012, 'KOIN': 2008, 'KONI': 1995, 'KOPI': 2015, 'KPIG': 2000, 'KRAS': 2010, 'KREN': 2002, 'LAPD': 2001, 'LCGP': 2007, 'LEAD': 2013, 'LINK': 2014, 'LION': 1993, 'LMAS': 2001, 'LMPI': 1994, 'LMSH': 1990, 'LPCK': 1997, 'LPGI': 1997, 'LPIN': 1990, 'LPKR': 1996, 'LPLI': 1989, 'LPPF': 1989, 'LPPS': 1994, 'LRNA': 2014, 'LSIP': 1996, 'LTLS': 1997, 'MAGP': 2013, 'MAIN': 2006, 'MAPI': 2004, 'MASA': 2005, 'MAYA': 1997, 'MBAP': 2014, 'MBSS': 2011, 'MBTO': 2011, 'MCOR': 2007, 'MDIA': 2014, 'MDKA': 2015, 'MDLN': 1993, 'MDRN': 1991, 'MEDC': 1994, 'MEGA': 2000, 'MERK': 1981, 'META': 2001, 'MFIN': 2005, 'MFMI': 2010, 'MGNA': 2014, 'MICE': 2005, 'MIDI': 2010, 'MIKA': 2015, 'MIRA': 1997, 'MITI': 1997, 'MKPI': 2009, 'MLBI': 1981, 'MLIA': 1994, 'MLPL': 1989, 'MLPT': 2013, 'MMLP': 2015, 'MNCN': 2007, 'MPMX': 2013, 'MPPA': 1992, 'MRAT': 1995, 'MREI': 1989, 'MSKY': 2012, 'MTDL': 1990, 'MTFN': 1990, 'MTLA': 2011, 'MTSM': 1992, 'MYOH': 2000, 'MYOR': 1990, 'MYTX': 1989, 'NELY': 2012, 'NIKL': 2009, 'NIRO': 2012, 'NISP': 1994, 'NOBU': 2013, 'NRCA': 2013, 'OCAP': 2003, 'OKAS': 2006, 'OMRE': 1994, 'PADI': 2012, 'PALM': 2012, 'PANR': 2001, 'PANS': 2000, 'PBRX': 1990, 'PDES': 2008, 'PEGE': 2005, 'PGAS': 2003, 'PGLI': 2000, 'PICO': 1996, 'PJAA': 2004, 'PKPK': 2007, 'PLAS': 2001, 'PLIN': 1992, 'PNBN': 1982, 'PNBS': 2014, 'PNIN': 1983, 'PNLF': 1983, 'PNSE': 1990, 'POLY': 1991, 'POOL': 1991, 'PPRO': 2015, 'PSAB': 2003, 'PSDN': 1994, 'PSKT': 1995, 'PTBA': 2002, 'PTIS': 2011, 'PTPP': 2010, 'PTRO': 1990, 'PTSN': 2007, 'PTSP': 1994, 'PUDP': 1994, 'PWON': 1989, 'PYFA': 2001, 'RAJA': 2006, 'RALS': 1996, 'RANC': 2012, 'RBMS': 1997, 'RDTX': 1990, 'RELI': 2005, 'RICY': 1998, 'RIGS': 1990, 'RIMO': 2000, 'RODA': 2001, 'ROTI': 2010, 'RUIS': 2006, 'SAFE': 1994, 'SAME': 2013, 'SCCO': 1982, 'SCMA': 2002, 'SCPI': 1990, 'SDMU': 2011, 'SDPC': 1990, 'SDRA': 2006, 'SGRO': 2007, 'SHID': 1990, 'SIDO': 2013, 'SILO': 2013, 'SIMA': 1994, 'SIMP': 2011, 'SIPD': 1996, 'SKBM': 2012, 'SKLT': 1993, 'SKYB': 2010, 'SMAR': 1992, 'SMBR': 2013, 'SMCB': 1977, 'SMDM': 1995, 'SMDR': 1999, 'SMGR': 1991, 'SMMA': 1995, 'SMMT': 1997, 'SMRA': 1990, 'SMRU': 2011, 'SMSM': 1996, 'SOCI': 2014, 'SONA': 1992, 'SPMA': 1994, 'SQMI': 2004, 'SRAJ': 2011, 'SRIL': 2013, 'SRSN': 1993, 'SRTG': 2013, 'SSIA': 1997, 'SSMS': 2013, 'SSTM': 1997, 'STAR': 2011, 'STTP': 1996, 'SUGI': 2002, 'SULI': 1994, 'SUPR': 2011, 'TALF': 2014, 'TARA': 2014, 'TAXI': 2012, 'TBIG': 2010, 'TBLA': 2000, 'TBMS': 1990, 'TCID': 1993, 'TELE': 2012, 'TFCO': 1980, 'TGKA': 1990, 'TIFA': 2011, 'TINS': 1995, 'TIRA': 1993, 'TIRT': 1999, 'TKIM': 1990, 'TLKM': 1995, 'TMAS': 2003, 'TMPO': 2001, 'TOBA': 2012, 'TOTL': 2006, 'TOTO': 1990, 'TOWR': 2010, 'TPIA': 2008, 'TPMA': 2013, 'TRAM': 2008, 'TRIL': 2008, 'TRIM': 2000, 'TRIO': 2009, 'TRIS': 2012, 'TRST': 1990, 'TRUS': 2002, 'TSPC': 1994, 'ULTJ': 1990, 'UNIC': 1989, 'UNIT': 2002, 'UNSP': 1990, 'UNTR': 1989, 'UNVR': 1982, 'VICO': 2013, 'VINS': 2015, 'VIVA': 2011, 'VOKS': 1990, 'VRNA': 2008, 'WAPO': 2001, 'WEHA': 2007, 'WICO': 1994, 'WIIM': 2012, 'WIKA': 2007, 'WINS': 2010, 'WOMF': 2004, 'WSKT': 2012, 'WTON': 2014, 'YPAS': 2008, 'YULE': 2004, 'ZBRA': 1991, 'SHIP': 2016, 'CASA': 2016, 'DAYA': 2016, 'DPUM': 2015, 'IDPR': 2015, 'JGLE': 2016, 'KINO': 2015, 'MARI': 2016, 'MKNT': 2015, 'MTRA': 2016, 'OASA': 2016, 'POWR': 2016, 'INCF': 2016, 'WSBP': 2016, 'PBSA': 2016, 'PRDA': 2016, 'BOGA': 2016, 'BRIS': 2018, 'PORT': 2017, 'CARS': 2017, 'MINA': 2017, 'CLEO': 2017, 'TAMU': 2017, 'CSIS': 2017, 'TGRA': 2017, 'FIRE': 2017, 'TOPS': 2017, 'KMTR': 2017, 'ARMY': 2017, 'MAPB': 2017, 'WOOD': 2017, 'HRTA': 2017, 'MABA': 2017, 'HOKI': 2017, 'MPOW': 2017, 'MARK': 2017, 'NASA': 2017, 'MDKI': 2017, 'BELL': 2017, 'KIOS': 2017, 'GMFI': 2017, 'MTWI': 2017, 'ZINC': 2017, 'MCAS': 2017, 'PPRE': 2017, 'WEGE': 2017, 'PSSI': 2017, 'MORA': 2022, 'DWGL': 2017, 'PBID': 2017, 'JMAS': 2017, 'CAMP': 2017, 'IPCM': 2017, 'PCAR': 2017, 'LCKM': 2018, 'BOSS': 2018, 'HELI': 2018, 'JSKY': 2018, 'INPS': 2018, 'GHON': 2018, 'TDPM': 2018, 'DFAM': 2018, 'NICK': 2018, 'BTPS': 2018, 'SPTO': 2018, 'PRIM': 2018, 'HEAL': 2018, 'TRUK': 2018, 'PZZA': 2018, 'TUGU': 2018, 'MSIN': 2018, 'SWAT': 2018, 'TNCA': 2018, 'MAPA': 2018, 'TCPI': 2018, 'IPCC': 2018, 'RISE': 2018, 'BPTR': 2018, 'POLL': 2018, 'NFCX': 2018, 'MGRO': 2018, 'NUSA': 2018, 'FILM': 2018, 'ANDI': 2018, 'LAND': 2018, 'MOLI': 2018, 'PANI': 2018, 'DIGI': 2018, 'CITY': 2018, 'SAPX': 2018, 'SURE': 2018, 'HKMU': 2018, 'MPRO': 2018, 'DUCK': 2018, 'GOOD': 2018, 'SKRN': 2018, 'YELO': 2018, 'CAKK': 2018, 'SATU': 2018, 'SOSS': 2018, 'DEAL': 2018, 'POLA': 2018, 'DIVA': 2018, 'LUCK': 2018, 'URBN': 2018, 'SOTS': 2018, 'ZONE': 2018, 'PEHA': 2018, 'FOOD': 2019, 'BEEF': 2019, 'POLI': 2019, 'CLAY': 2019, 'NATO': 2019, 'JAYA': 2019, 'COCO': 2019, 'MTPS': 2019, 'CPRI': 2019, 'HRME': 2019, 'POSA': 2019, 'JAST': 2019, 'FITT': 2019, 'BOLA': 2019, 'CCSI': 2019, 'SFAN': 2019, 'POLU': 2019, 'KJEN': 2019, 'KAYU': 2019, 'ITIC': 2019, 'PAMG': 2019, 'IPTV': 2019, 'BLUE': 2019, 'ENVY': 2019, 'EAST': 2019, 'LIFE': 2019, 'FUJI': 2019, 'KOTA': 2019, 'INOV': 2019, 'ARKA': 2019, 'SMKL': 2019, 'HDIT': 2019, 'KEEN': 2019, 'BAPI': 2019, 'TFAS': 2019, 'GGRP': 2019, 'OPMS': 2019, 'NZIA': 2019, 'SLIS': 2019, 'PURE': 2019, 'IRRA': 2019, 'DMMX': 2019, 'SINI': 2019, 'WOWS': 2019, 'ESIP': 2019, 'TEBE': 2019, 'KEJU': 2019, 'PSGO': 2019, 'AGAR': 2019, 'IFSH': 2019, 'REAL': 2019, 'IFII': 2019, 'PMJS': 2019, 'UCID': 2019, 'GLVA': 2019, 'PGJO': 2020, 'AMAR': 2020, 'CSRA': 2020, 'INDO': 2020, 'AMOR': 2020, 'TRIN': 2020, 'DMND': 2020, 'PURA': 2020, 'PTPW': 2020, 'TAMA': 2020, 'IKAN': 2020, 'SAMF': 2020, 'SBAT': 2020, 'KBAG': 2020, 'CBMF': 2020, 'RONY': 2020, 'CSMI': 2020, 'BBSS': 2020, 'BHAT': 2020, 'CASH': 2020, 'TECH': 2020, 'EPAC': 2020, 'UANG': 2020, 'PGUN': 2020, 'SOFA': 2020, 'PPGL': 2020, 'TOYS': 2020, 'SGER': 2020, 'TRJA': 2020, 'PNGO': 2020, 'SCNP': 2020, 'BBSI': 2020, 'KMDS': 2020, 'PURI': 2020, 'SOHO': 2020, 'HOMI': 2020, 'ROCK': 2020, 'ENZO': 2020, 'PLAN': 2020, 'PTDU': 2020, 'ATAP': 2020, 'VICI': 2020, 'PMMP': 2020, 'WIFI': 2020, 'FAPA': 2021, 'DCII': 2021, 'KETR': 2021, 'DGNS': 2021, 'UFOE': 2021, 'BANK': 2021, 'WMUU': 2021, 'EDGE': 2021, 'UNIQ': 2021, 'BEBS': 2021, 'SNLK': 2021, 'ZYRX': 2021, 'LFLO': 2021, 'FIMP': 2021, 'TAPG': 2021, 'NPGF': 2021, 'LUCY': 2021, 'ADCP': 2021, 'HOPE': 2021, 'MGLV': 2021, 'TRUE': 2021, 'LABA': 2021, 'ARCI': 2021, 'IPAC': 2021, 'MASB': 2021, 'BMHS': 2021, 'FLMC': 2021, 'NICL': 2021, 'UVCR': 2021, 'BUKA': 2021, 'HAIS': 2021, 'OILS': 2021, 'GPSO': 2021, 'MCOL': 2021, 'RSGK': 2021, 'RUNS': 2021, 'SBMA': 2021, 'CMNT': 2021, 'GTSI': 2021, 'IDEA': 2021, 'KUAS': 2021, 'BOBA': 2021, 'MTEL': 2021, 'DEPO': 2021, 'BINO': 2021, 'CMRY': 2021, 'WGSH': 2021, 'TAYS': 2021, 'WMPP': 2021, 'RMKE': 2021, 'OBMD': 2021, 'AVIA': 2021, 'IPPE': 2021, 'NASI': 2021, 'BSML': 2021, 'DRMA': 2021, 'ADMR': 2022, 'SEMA': 2022, 'ASLC': 2022, 'NETV': 2022, 'BAUT': 2022, 'ENAK': 2022, 'NTBK': 2022, 'SMKM': 2022, 'STAA': 2022, 'NANO': 2022, 'BIKE': 2022, 'WIRG': 2022, 'SICO': 2022, 'GOTO': 2022, 'TLDN': 2022, 'MTMH': 2022, 'WINR': 2022, 'IBOS': 2022, 'OLIV': 2022, 'ASHA': 2022, 'SWID': 2022, 'TRGU': 2022, 'ARKO': 2022, 'CHEM': 2022, 'DEWI': 2022, 'AXIO': 2022, 'KRYA': 2022, 'HATM': 2022, 'RCCC': 2022, 'GULA': 2022, 'JARR': 2022, 'AMMS': 2022, 'RAFI': 2022, 'KKES': 2022, 'ELPI': 2022, 'EURO': 2022, 'KLIN': 2022, 'TOOL': 2022, 'BUAH': 2022, 'CRAB': 2022, 'MEDS': 2022, 'COAL': 2022, 'PRAY': 2022, 'CBUT': 2022, 'BELI': 2022, 'MKTR': 2022, 'OMED': 2022, 'BSBK': 2022, 'PDPP': 2022, 'KDTN': 2022, 'ZATA': 2022, 'NINE': 2022, 'MMIX': 2022, 'PADA': 2022, 'ISAP': 2022, 'VTNY': 2022, 'SOUL': 2023, 'ELIT': 2023, 'BEER': 2023, 'CBPE': 2023, 'SUNI': 2023, 'CBRE': 2023, 'WINE': 2023, 'BMBL': 2023, 'PEVE': 2023, 'LAJU': 2023, 'FWCT': 2023, 'NAYZ': 2023, 'IRSX': 2023, 'PACK': 2023, 'VAST': 2023, 'CHIP': 2023, 'HALO': 2023, 'KING': 2023, 'PGEO': 2023, 'FUTR': 2023, 'HILL': 2023, 'BDKR': 2023, 'PTMP': 2023, 'SAGE': 2023, 'TRON': 2023, 'CUAN': 2023, 'NSSS': 2023, 'GTRA': 2023, 'HAJJ': 2023, 'PIPA': 2023, 'NCKL': 2023, 'MENN': 2023, 'AWAN': 2023, 'MBMA': 2023, 'RAAM': 2023, 'DOOH': 2023, 'JATI': 2023, 'TYRE': 2023, 'MPXL': 2023, 'SMIL': 2023, 'KLAS': 2023, 'MAXI': 2023, 'VKTR': 2023, 'RELF': 2023, 'AMMN': 2023, 'CRSN': 2023, 'GRPM': 2023, 'WIDI': 2023, 'TGUK': 2023, 'INET': 2023, 'MAHA': 2023, 'RMKO': 2023, 'CNMA': 2023, 'FOLK': 2023, 'HBAT': 2023, 'GRIA': 2023, 'PPRI': 2023, 'ERAL': 2023, 'CYBR': 2023, 'MUTU': 2023, 'LMAX': 2023, 'HUMI': 2023, 'MSIE': 2023, 'RSCH': 2023, 'BABY': 2023, 'AEGS': 2023, 'IOTF': 2023, 'KOCI': 2023, 'PTPS': 2023, 'BREN': 2023, 'STRK': 2023, 'KOKA': 2023, 'LOPI': 2023, 'UDNG': 2023, 'RGAS': 2023, 'MSTI': 2023, 'IKPM': 2023, 'AYAM': 2023, 'SURI': 2023, 'ASLI': 2024, 'CGAS': 2024, 'NICE': 2024, 'MSJA': 2024, 'SMLE': 2024, 'ACRO': 2024, 'MANG': 2024, 'GRPH': 2024, 'SMGA': 2024, 'UNTD': 2024, 'TOSK': 2024, 'MPIX': 2024, 'ALII': 2024, 'MKAP': 2024, 'MEJA': 2024, 'LIVE': 2024, 'HYGN': 2024, 'BAIK': 2024, 'VISI': 2024, 'AREA': 2024, 'MHKI': 2024, 'ATLA': 2024, 'DATA': 2024, 'SOLA': 2024, 'BATR': 2024, 'SPRE': 2024, 'PART': 2024, 'GOLF': 2024, 'ISEA': 2024, 'BLES': 2024, 'GUNA': 2024, 'LABS': 2024, 'DOSS': 2024, 'NEST': 2024, 'PTMR': 2024, 'VERN': 2024, 'DAAZ': 2024, 'BOAT': 2024, 'NAIK': 2024, 'AADI': 2024, 'MDIY': 2024, 'KSIX': 2025, 'RATU': 2025, 'YOII': 2025, 'HGII': 2025, 'BRRC': 2025, 'DGWG': 2025, 'CBDK': 2025, 'OBAT': 2025, 'MINE': 2025, 'ASPR': 2025, 'PSAT': 2025, 'COIN': 2025, 'CDIA': 2025, 'BLOG': 2025, 'MERI': 2025, 'CHEK': 2025, 'PMUI': 2025, 'EMAS': 2025, 'KAQI': 2025, 'YUPI': 2025, 'FORE': 2025, 'MDLA': 2025, 'DKHH': 2025, 'AYLS': 2020, 'DADA': 2020, 'ASPI': 2020, 'ESTA': 2020, 'BESS': 2020, 'AMAN': 2020, 'CARE': 2020}
indices_to_drop = []
for ticker, ipo_year in dict_perusahaan_tahun_ipo.items():
# Find rows where the ticker matches and the year is before the IPO year
rows_before_ipo = df_findat_final[
(df_findat_final['ticker'] == ticker) &
(df_findat_final['Year'] <= ipo_year)
]
indices_to_drop.extend(rows_before_ipo.index.tolist())# Baris yang akan dihapus
df_findat_final.loc[indices_to_drop]| variable | ticker | Year | Entity Name | IPO Date MM/dd/yyyy | Primary Industry | Entity ID | Exchange | 1st Level Primary Industry | Country / Region Name | Address 1 | Web Address | Business Description | Company Type | Investor Type | Company Status | Ownership Structure | Activist Investor? Yes/No | Institution Coverage Level | Consolidation Status | Has Current Financials? Yes/No | Has Bank Regulatory Financials? Yes/No | Has Third Party Financials? Yes/No | Long Business Description | Topic Tags | Crunchbase Categories | Year Established | Date Established MM/dd/yyyy | Year Incorporated | Date Incorporated MM/dd/yyyy | Month of Fiscal Year End | Headcount As Of MM/dd/yyyy | Industry Classification | 2nd Level Primary Industry | 3rd Level Primary Industry | 4th Level Primary Industry | Sector | Industry Group | Industry | City | Parent Company Name | Parent Ticker | Parent Exchange: Ticker | Parent Percent Owned (%) | Parent City | Parent Industry | Ultimate Parent Company Name | Ultimate Parent Ticker | Ultimate Parent Exchange: Ticker | Ultimate Parent City | Ultimate Parent Industry | Ultimate Parent Country / Region | All Investors | Percent Owned - All Institutions (%) | Shares Owned - All Institutions (actual) | Market Value - All Institutions (Rp.B) | Percent Owned - Insiders (%) | Shares Owned - Insiders (actual) | Market Value - Insiders (Rp.B) | IPO Price (Rp.) | Location Type | Data Precision | Data Year | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.B) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 81 | ABMM | 2011 | PT ABM Investama Tbk (IDX:ABMM) | 2011-12-05 | Coal and Consumable Fuels | 4980353 | IDX | Energy and Utilities | Indonesia | Tiara Marga Trakindo I building | www.abm-investama.com | PT ABM Investama Tbk, together with its subsid... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT ABM Investama Tbk operating in the sectors ... | Clean Energy; Freight Service; Marine Transpor... | Energy, Precious Metals | 2006.000000 | 2006-06-01 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | PT Tiara Marga Trakindo | None | None | 0.535500 | Jakarta | Industrial Conglomerates | PT Tiara Marga Trakindo | None | None | Jakarta | Industrial Conglomerates | Indonesia | PT Tiara Marga Trakindo (Current Subsidiary or... | 0.830000 | 22926501.000000 | 68779.503000 | 7.340000 | 202136500.000000 | 606409.500000 | 3750.000000 | None | None | None | 7019323.859661 | 10077243.517333 | 1.362000 | 1.088000 | 1134634.650895 | 4270489.900468 | 3135855.249573 | 414473.810135 | 26318.699181 | 2832092.967225 | 494015.259308 | NaN | 4929981.462200 | 3057919.657672 | 4881836.576369 | 5237512.675191 | 6607997.885733 | 706361.557448 | 1250603.533610 | 706361.557448 | 484431.520834 | 53032.416782 | 1190873.399695 | 2753165000.000000 | 10530.856125 | 3825.000000 | 7987901.119872 | 1669222.334305 | NaN |
| 82 | ABMM | 2010 | PT ABM Investama Tbk (IDX:ABMM) | 2011-12-05 | Coal and Consumable Fuels | 4980353 | IDX | Energy and Utilities | Indonesia | Tiara Marga Trakindo I building | www.abm-investama.com | PT ABM Investama Tbk, together with its subsid... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT ABM Investama Tbk operating in the sectors ... | Clean Energy; Freight Service; Marine Transpor... | Energy, Precious Metals | 2006.000000 | 2006-06-01 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | PT Tiara Marga Trakindo | None | None | 0.535500 | Jakarta | Industrial Conglomerates | PT Tiara Marga Trakindo | None | None | Jakarta | Industrial Conglomerates | Indonesia | PT Tiara Marga Trakindo (Current Subsidiary or... | 0.830000 | 22926501.000000 | 68779.503000 | 7.340000 | 202136500.000000 | 606409.500000 | 3750.000000 | None | None | None | 3763830.000000 | 4890266.000000 | 0.893000 | 0.700000 | -252891.000000 | 2106194.000000 | 2359085.000000 | 301356.000000 | 11526.000000 | 841659.000000 | 292208.000000 | NaN | 1924066.000000 | 1126436.000000 | 2172994.000000 | 3789687.000000 | 4486419.000000 | 271266.000000 | 705326.000000 | 271266.000000 | 127376.000000 | 612476.000000 | -181536.000000 | 825760000.000000 | NaN | NaN | 3050502.000000 | 433039.000000 | NaN |
| 83 | ABMM | 2009 | PT ABM Investama Tbk (IDX:ABMM) | 2011-12-05 | Coal and Consumable Fuels | 4980353 | IDX | Energy and Utilities | Indonesia | Tiara Marga Trakindo I building | www.abm-investama.com | PT ABM Investama Tbk, together with its subsid... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT ABM Investama Tbk operating in the sectors ... | Clean Energy; Freight Service; Marine Transpor... | Energy, Precious Metals | 2006.000000 | 2006-06-01 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | PT Tiara Marga Trakindo | None | None | 0.535500 | Jakarta | Industrial Conglomerates | PT Tiara Marga Trakindo | None | None | Jakarta | Industrial Conglomerates | Indonesia | PT Tiara Marga Trakindo (Current Subsidiary or... | 0.830000 | 22926501.000000 | 68779.503000 | 7.340000 | 202136500.000000 | 606409.500000 | 3750.000000 | None | None | None | 3099452.000000 | 4199977.000000 | 1.113000 | 0.879000 | 203475.000000 | 2001334.000000 | 1797859.000000 | 233936.000000 | NaN | NaN | NaN | NaN | 0.000000 | 1100525.000000 | 1532725.000000 | 3455803.000000 | 3926320.000000 | 491429.000000 | NaN | 491429.000000 | -11346.000000 | 678998.000000 | 455151.000000 | 826000000.000000 | NaN | NaN | 1100525.000000 | 614575.000000 | NaN |
| 84 | ABMM | 2008 | PT ABM Investama Tbk (IDX:ABMM) | 2011-12-05 | Coal and Consumable Fuels | 4980353 | IDX | Energy and Utilities | Indonesia | Tiara Marga Trakindo I building | www.abm-investama.com | PT ABM Investama Tbk, together with its subsid... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT ABM Investama Tbk operating in the sectors ... | Clean Energy; Freight Service; Marine Transpor... | Energy, Precious Metals | 2006.000000 | 2006-06-01 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Coal and Consumable Fuels | Oil, Gas and Coal | Coal and Consumable Fuels | None | Energy | Energy | Oil, Gas and Consumable Fuels | Jakarta | PT Tiara Marga Trakindo | None | None | 0.535500 | Jakarta | Industrial Conglomerates | PT Tiara Marga Trakindo | None | None | Jakarta | Industrial Conglomerates | Indonesia | PT Tiara Marga Trakindo (Current Subsidiary or... | 0.830000 | 22926501.000000 | 68779.503000 | 7.340000 | 202136500.000000 | 606409.500000 | 3750.000000 | None | None | None | 4360950.000000 | 3549129.000000 | 0.592000 | 0.430000 | -953832.000000 | 1384649.000000 | 2338481.000000 | 241667.000000 | NaN | NaN | NaN | NaN | 0.000000 | -811821.000000 | 1469802.000000 | 3114382.000000 | 3235172.000000 | -469841.000000 | NaN | -469841.000000 | -3913.000000 | -4564.000000 | 7861.000000 | NaN | NaN | NaN | -811821.000000 | 159424.000000 | NaN |
| 130 | ACST | 2013 | PT Acset Indonusa Tbk (IDX:ACST) | 2013-06-20 | Construction and Engineering | 4990054 | IDX | Industrials | Indonesia | ACSET Building | www.acset.co | PT Acset Indonusa Tbk provides construction an... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Acset Indonusa Tbk, an integrated construct... | None | Civil Engineering, Construction | 1995.000000 | 1995-01-10 00:00:00 | 1995.000000 | 1995-01-10 | Dec | 2025-09-01 | Construction and Engineering; Commercial Const... | Capital Goods | Construction and Engineering | None | Industrials | Capital Goods | Construction and Engineering | Jakarta | PT Karya Supra Perkasa | None | None | 0.501000 | None | Unclassified | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Karya Supra Perkasa (Current Subsidiary or ... | 0.110000 | 20245000.000000 | 2753.320000 | NaN | NaN | NaN | 2500.000000 | None | None | None | 536559.738000 | 1298358.203000 | 1.484000 | 0.922000 | 346109.300000 | 1061422.848000 | 715313.548000 | 237777.966000 | 6015.411000 | 16885.953000 | 15961.846000 | NaN | 118266.063000 | 560442.562000 | 220839.892000 | 818537.622000 | 1014502.030000 | 146399.359000 | 194155.134000 | 146399.359000 | 99215.342000 | -112217.697000 | -19912.184000 | 500000000.000000 | 995.000000 | 1990.000000 | 678708.625000 | 48718.694000 | NaN |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 3293 | CARE | 2012 | PT Metro Healthcare Indonesia Tbk (IDX:CARE) | 2020-03-11 | Health Care Facilities | 19890578 | IDX | Health Care | Indonesia | Jl. Raya Serang Km 16,8 | www.metrohealthcareindonesia.co.id | PT Metro Healthcare Indonesia Tbk owns, operat... | Public Company | None | Operating | None | No | None | None | Yes | No | No | PT Metro Healthcare Indonesia Tbk engages prim... | Outpatient Care; Assistive Technology; Digital... | Facilities Support Services, Health Care, Hosp... | 2015.000000 | 2015-10-07 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Health Care Facilities; Hospitals and Healthca... | Health Care Providers and Services | Health Care Facilities | None | Health Care | Health Care Equipment and Services | Health Care Providers and Services | Tangerang | None | None | None | NA% | None | None | None | None | None | None | None | None | Pt Anugrah Kasih Rajawali (Current Investment,... | 20.370000 | 6773088070.000000 | 2438311.705200 | NaN | NaN | NaN | 103.000000 | None | None | None | NaN | NaN | None | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 3294 | CARE | 2011 | PT Metro Healthcare Indonesia Tbk (IDX:CARE) | 2020-03-11 | Health Care Facilities | 19890578 | IDX | Health Care | Indonesia | Jl. Raya Serang Km 16,8 | www.metrohealthcareindonesia.co.id | PT Metro Healthcare Indonesia Tbk owns, operat... | Public Company | None | Operating | None | No | None | None | Yes | No | No | PT Metro Healthcare Indonesia Tbk engages prim... | Outpatient Care; Assistive Technology; Digital... | Facilities Support Services, Health Care, Hosp... | 2015.000000 | 2015-10-07 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Health Care Facilities; Hospitals and Healthca... | Health Care Providers and Services | Health Care Facilities | None | Health Care | Health Care Equipment and Services | Health Care Providers and Services | Tangerang | None | None | None | NA% | None | None | None | None | None | None | None | None | Pt Anugrah Kasih Rajawali (Current Investment,... | 20.370000 | 6773088070.000000 | 2438311.705200 | NaN | NaN | NaN | 103.000000 | None | None | None | NaN | NaN | None | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 3295 | CARE | 2010 | PT Metro Healthcare Indonesia Tbk (IDX:CARE) | 2020-03-11 | Health Care Facilities | 19890578 | IDX | Health Care | Indonesia | Jl. Raya Serang Km 16,8 | www.metrohealthcareindonesia.co.id | PT Metro Healthcare Indonesia Tbk owns, operat... | Public Company | None | Operating | None | No | None | None | Yes | No | No | PT Metro Healthcare Indonesia Tbk engages prim... | Outpatient Care; Assistive Technology; Digital... | Facilities Support Services, Health Care, Hosp... | 2015.000000 | 2015-10-07 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Health Care Facilities; Hospitals and Healthca... | Health Care Providers and Services | Health Care Facilities | None | Health Care | Health Care Equipment and Services | Health Care Providers and Services | Tangerang | None | None | None | NA% | None | None | None | None | None | None | None | None | Pt Anugrah Kasih Rajawali (Current Investment,... | 20.370000 | 6773088070.000000 | 2438311.705200 | NaN | NaN | NaN | 103.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 3296 | CARE | 2009 | PT Metro Healthcare Indonesia Tbk (IDX:CARE) | 2020-03-11 | Health Care Facilities | 19890578 | IDX | Health Care | Indonesia | Jl. Raya Serang Km 16,8 | www.metrohealthcareindonesia.co.id | PT Metro Healthcare Indonesia Tbk owns, operat... | Public Company | None | Operating | None | No | None | None | Yes | No | No | PT Metro Healthcare Indonesia Tbk engages prim... | Outpatient Care; Assistive Technology; Digital... | Facilities Support Services, Health Care, Hosp... | 2015.000000 | 2015-10-07 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Health Care Facilities; Hospitals and Healthca... | Health Care Providers and Services | Health Care Facilities | None | Health Care | Health Care Equipment and Services | Health Care Providers and Services | Tangerang | None | None | None | NA% | None | None | None | None | None | None | None | None | Pt Anugrah Kasih Rajawali (Current Investment,... | 20.370000 | 6773088070.000000 | 2438311.705200 | NaN | NaN | NaN | 103.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 3297 | CARE | 2008 | PT Metro Healthcare Indonesia Tbk (IDX:CARE) | 2020-03-11 | Health Care Facilities | 19890578 | IDX | Health Care | Indonesia | Jl. Raya Serang Km 16,8 | www.metrohealthcareindonesia.co.id | PT Metro Healthcare Indonesia Tbk owns, operat... | Public Company | None | Operating | None | No | None | None | Yes | No | No | PT Metro Healthcare Indonesia Tbk engages prim... | Outpatient Care; Assistive Technology; Digital... | Facilities Support Services, Health Care, Hosp... | 2015.000000 | 2015-10-07 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Health Care Facilities; Hospitals and Healthca... | Health Care Providers and Services | Health Care Facilities | None | Health Care | Health Care Equipment and Services | Health Care Providers and Services | Tangerang | None | None | None | NA% | None | None | None | None | None | None | None | None | Pt Anugrah Kasih Rajawali (Current Investment,... | 20.370000 | 6773088070.000000 | 2438311.705200 | NaN | NaN | NaN | 103.000000 | None | None | None | NaN | NaN | None | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
5952 rows × 91 columns
# Drop the identified rows from df_findat_final
df_findat_final = df_findat_final.drop(indices_to_drop)
print("Dataset setelah baris tahun sebelum IPO dihapus :")
display(df_findat_final.head())
print(f"Jumlah baris setelah penghapusan: {len(df_findat_final)}")Dataset setelah baris tahun sebelum IPO dihapus :
| variable | ticker | Year | Entity Name | IPO Date MM/dd/yyyy | Primary Industry | Entity ID | Exchange | 1st Level Primary Industry | Country / Region Name | Address 1 | Web Address | Business Description | Company Type | Investor Type | Company Status | Ownership Structure | Activist Investor? Yes/No | Institution Coverage Level | Consolidation Status | Has Current Financials? Yes/No | Has Bank Regulatory Financials? Yes/No | Has Third Party Financials? Yes/No | Long Business Description | Topic Tags | Crunchbase Categories | Year Established | Date Established MM/dd/yyyy | Year Incorporated | Date Incorporated MM/dd/yyyy | Month of Fiscal Year End | Headcount As Of MM/dd/yyyy | Industry Classification | 2nd Level Primary Industry | 3rd Level Primary Industry | 4th Level Primary Industry | Sector | Industry Group | Industry | City | Parent Company Name | Parent Ticker | Parent Exchange: Ticker | Parent Percent Owned (%) | Parent City | Parent Industry | Ultimate Parent Company Name | Ultimate Parent Ticker | Ultimate Parent Exchange: Ticker | Ultimate Parent City | Ultimate Parent Industry | Ultimate Parent Country / Region | All Investors | Percent Owned - All Institutions (%) | Shares Owned - All Institutions (actual) | Market Value - All Institutions (Rp.B) | Percent Owned - Insiders (%) | Shares Owned - Insiders (actual) | Market Value - Insiders (Rp.B) | IPO Price (Rp.) | Location Type | Data Precision | Data Year | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.B) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 17 | AALI | 2024 | PT Astra Agro Lestari Tbk (IDX:AALI) | NaT | Agricultural Products and Services | 4567075 | IDX | Consumer | Indonesia | Jl Puloayang Raya | www.astra-agro.co.id | PT Astra Agro Lestari Tbk engages in the crude... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Astra Agro Lestari Tbk is a prominent playe... | None | Agriculture, Business Intelligence, Communities | 1988.000000 | 1988-10-03 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Agricultural Products and Services; Grain And ... | Producers | Agricultural Products and Services | None | Consumer Staples | Food, Beverage and Tobacco | Food Products | Jakarta | PT Astra International Tbk | ASII | IDX:ASII | 0.796800 | Jakarta | Automobile Manufacturers | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Astra International Tbk (Current Subsidiary... | 2.100000 | 40473542.000000 | 315693.627600 | NaN | NaN | NaN | NaN | None | None | None | 5591163.000000 | 28793225.000000 | 2.605000 | 1.126000 | 5195985.000000 | 8433638.000000 | 3237653.000000 | 3981669.000000 | NaN | 1500000.000000 | NaN | NaN | 3189537.000000 | 23202062.000000 | 17429693.000000 | 18358928.000000 | 21815035.000000 | 1788141.000000 | 3202738.000000 | 1788141.000000 | 1186783.000000 | 3379195.000000 | 1146504.000000 | 1924688333.000000 | 11933.067665 | 6200.000000 | 26391599.000000 | 3236012.000000 | NaN |
| 18 | AALI | 2023 | PT Astra Agro Lestari Tbk (IDX:AALI) | NaT | Agricultural Products and Services | 4567075 | IDX | Consumer | Indonesia | Jl Puloayang Raya | www.astra-agro.co.id | PT Astra Agro Lestari Tbk engages in the crude... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Astra Agro Lestari Tbk is a prominent playe... | None | Agriculture, Business Intelligence, Communities | 1988.000000 | 1988-10-03 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Agricultural Products and Services; Grain And ... | Producers | Agricultural Products and Services | None | Consumer Staples | Food, Beverage and Tobacco | Food Products | Jakarta | PT Astra International Tbk | ASII | IDX:ASII | 0.796800 | Jakarta | Automobile Manufacturers | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Astra International Tbk (Current Subsidiary... | 2.100000 | 40473542.000000 | 315693.627600 | NaN | NaN | NaN | NaN | None | None | None | 6280237.000000 | 28846243.000000 | 1.834000 | 0.765000 | 3236061.000000 | 7118202.000000 | 3882141.000000 | 3122454.000000 | NaN | 1689754.000000 | NaN | NaN | 4005052.000000 | 22566006.000000 | 18105649.000000 | 17950652.000000 | 20745473.000000 | 1672092.000000 | 3007949.000000 | 1672092.000000 | 1088170.000000 | 2538738.000000 | 469892.000000 | 1924688333.000000 | 13520.935539 | 7025.000000 | 26571058.000000 | 2089508.000000 | NaN |
| 19 | AALI | 2022 | PT Astra Agro Lestari Tbk (IDX:AALI) | NaT | Agricultural Products and Services | 4567075 | IDX | Consumer | Indonesia | Jl Puloayang Raya | www.astra-agro.co.id | PT Astra Agro Lestari Tbk engages in the crude... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Astra Agro Lestari Tbk is a prominent playe... | None | Agriculture, Business Intelligence, Communities | 1988.000000 | 1988-10-03 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Agricultural Products and Services; Grain And ... | Producers | Agricultural Products and Services | None | Consumer Staples | Food, Beverage and Tobacco | Food Products | Jakarta | PT Astra International Tbk | ASII | IDX:ASII | 0.796800 | Jakarta | Automobile Manufacturers | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Astra International Tbk (Current Subsidiary... | 2.100000 | 40473542.000000 | 315693.627600 | NaN | NaN | NaN | NaN | None | None | None | 7006119.000000 | 29249340.000000 | 3.600000 | 1.227000 | 5337669.000000 | 7390608.000000 | 2052939.000000 | 3434768.000000 | NaN | 4048767.000000 | NaN | NaN | 4053767.000000 | 22243221.000000 | 17996321.000000 | 18176356.000000 | 21828591.000000 | 2447192.000000 | 3710458.000000 | 2447192.000000 | 1792050.000000 | 1835397.000000 | -2276406.000000 | 1924688333.000000 | 15445.623872 | 8025.000000 | 26296988.000000 | 1619616.000000 | NaN |
| 20 | AALI | 2021 | PT Astra Agro Lestari Tbk (IDX:AALI) | NaT | Agricultural Products and Services | 4567075 | IDX | Consumer | Indonesia | Jl Puloayang Raya | www.astra-agro.co.id | PT Astra Agro Lestari Tbk engages in the crude... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Astra Agro Lestari Tbk is a prominent playe... | None | Agriculture, Business Intelligence, Communities | 1988.000000 | 1988-10-03 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Agricultural Products and Services; Grain And ... | Producers | Agricultural Products and Services | None | Consumer Staples | Food, Beverage and Tobacco | Food Products | Jakarta | PT Astra International Tbk | ASII | IDX:ASII | 0.796800 | Jakarta | Automobile Manufacturers | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Astra International Tbk (Current Subsidiary... | 2.100000 | 40473542.000000 | 315693.627600 | NaN | NaN | NaN | NaN | None | None | None | 9228733.000000 | 30399906.000000 | 1.579000 | 0.752000 | 3453812.000000 | 9414208.000000 | 5960396.000000 | 3381506.000000 | NaN | 2253611.000000 | NaN | NaN | 5818072.000000 | 21171173.000000 | 17781227.000000 | 19442136.000000 | 24322048.000000 | 3066334.000000 | 4314916.000000 | 3066334.000000 | 2067362.000000 | 4895119.000000 | 2917130.000000 | 1924688333.000000 | 18284.539164 | 9500.000000 | 26989245.000000 | 3896022.000000 | NaN |
| 21 | AALI | 2020 | PT Astra Agro Lestari Tbk (IDX:AALI) | NaT | Agricultural Products and Services | 4567075 | IDX | Consumer | Indonesia | Jl Puloayang Raya | www.astra-agro.co.id | PT Astra Agro Lestari Tbk engages in the crude... | Public Company | None | Operating Subsidiary | None | No | None | None | Yes | No | No | PT Astra Agro Lestari Tbk is a prominent playe... | None | Agriculture, Business Intelligence, Communities | 1988.000000 | 1988-10-03 00:00:00 | NaN | NaT | Dec | 2025-09-01 | Agricultural Products and Services; Grain And ... | Producers | Agricultural Products and Services | None | Consumer Staples | Food, Beverage and Tobacco | Food Products | Jakarta | PT Astra International Tbk | ASII | IDX:ASII | 0.796800 | Jakarta | Automobile Manufacturers | Jardine Matheson Holdings Limited | J36 | SGX:J36 | Hamilton | Industrial Conglomerates | Bermuda | PT Astra International Tbk (Current Subsidiary... | 2.100000 | 40473542.000000 | 315693.627600 | NaN | NaN | NaN | NaN | None | None | None | 8533437.000000 | 27781231.000000 | 3.313000 | 1.322000 | 4145384.000000 | 5937890.000000 | 1792506.000000 | 2505048.000000 | NaN | 5858300.000000 | NaN | NaN | 5858300.000000 | 19247794.000000 | 17741481.000000 | 15809752.000000 | 18807043.000000 | 1708412.000000 | 3031177.000000 | 1708412.000000 | 893779.000000 | 2322164.000000 | 595526.000000 | 1924688333.000000 | 23721.783704 | 12325.000000 | 25106094.000000 | 978892.000000 | NaN |
Jumlah baris setelah penghapusan: 8549
Sesuaikan Billion ke Million
rp_b_columns = [col for col in df_findat_final.columns if 'Rp.B' in col]
if rp_b_columns:
for col in rp_b_columns:
df_findat_final[col] = df_findat_final[col] * 1000
new_col_name = col.replace('(Rp.B)', '(Rp.M)')
df_findat_final = df_findat_final.rename(columns={col: new_col_name})
print(f"Successfully converted {len(rp_b_columns)} column(s) from 'Rp.B' to 'Rp.M' and multiplied values by 1000.")
print("Updated columns:")
for col_name in rp_b_columns:
print(f"- {col_name.replace('(Rp.B)', '(Rp.M)')}")
else:
print("No columns found with 'Rp.B' in their name in df_findat_final.")Successfully converted 3 column(s) from 'Rp.B' to 'Rp.M' and multiplied values by 1000.
Updated columns:
- Market Value - All Institutions (Rp.M)
- Market Value - Insiders (Rp.M)
- Market Capitalization (Rp.M)
Hapus Kolom Unused
cols_to_drop = [
"Location Type",
"Data Year",
"Data Precision",
"Investor Type",
"Parent Ticker",
"Ultimate Parent Ticker",
"Date Incorporated MM/dd/yyyy",
"Parent Exchange: Ticker",
"Ownership Structure",
"4th Level Primary Industry",
"Year Incorporated",
"Institution Coverage Level",
"Ultimate Parent City",
"Parent City",
"IPO Price (Rp.)",
"Ultimate Parent Company Name",
"Ultimate Parent Country / Region",
"Parent Company Name",
"Topic Tags",
"Long Business Description",
"Headcount As Of MM/dd/yyyy",
"Crunchbase Categories",
"All Investors",
# "Date Established MM/dd/yyyy",
"Web Address",
"Month of Fiscal Year End",
"Entity ID",
"Primary Industry",
"Exchange",
"Country / Region Name",
"Address 1",
"Has Bank Regulatory Financials? Yes/No",
"Has Current Financials? Yes/No",
"Has Third Party Financials? Yes/No",
"Activist Investor? Yes/No",
"Company Status",
"Company Type",
"Business Description",
"Industry Classification",
"2nd Level Primary Industry",
"IPO Date MM/dd/yyyy",
"3rd Level Primary Industry",
"City",
"Industry",
"Parent Industry",
"Ultimate Parent Exchange: Ticker",
"Ultimate Parent Industry",
"Consolidation Status",
"Market Value - All Institutions (Rp.M)",
"Shares Owned - All Institutions (actual)",
"Market Value - Insiders (Rp.M)",
"Shares Owned - Insiders (actual)",
]
df_findat_final = df_findat_final.drop(columns=cols_to_drop, errors='ignore')
print(f"Ukuran dataframe setelah drop: {df_findat_final.shape}")Ukuran dataframe setelah drop: (8549, 40)
print("Sisa kolom:")
df_findat_final.columns.tolist()Sisa kolom:
['ticker',
'Year',
'Entity Name',
'1st Level Primary Industry',
'Year Established',
'Date Established MM/dd/yyyy',
'Sector',
'Industry Group',
'Parent Percent Owned (%)',
'Percent Owned - All Institutions (%)',
'Percent Owned - Insiders (%)',
'Total Liabilities (Rp.M)',
'Total Assets (Rp.M)',
'Current Ratio (x)',
'Quick Ratio (x)',
'Working Capital (Rp.M)',
'Total Current Assets (Rp.M)',
'Total Current Liabilities (Rp.M)',
'Inventory (Rp.M)',
'Prepaid Exp. (Rp.M)',
'Long-term Debt (Rp.M)',
'Short-term Borrowings (Rp.M)',
'Current Portion of LT Debt & Leases (Rp.M)',
'Total Debt (Rp.M)',
'Total Equity (Rp.M)',
'Net Property, Plant & Equipment (Rp.M)',
'Cost Of Goods Sold (Rp.M)',
'Total Revenue (Rp.M)',
'Operating Income (Rp.M)',
'EBITDA (Rp.M)',
'EBIT (Rp.M)',
'Net Income to Company (Rp.M)',
'Cash from Ops. (Rp.M)',
'Net Change in Cash (Rp.M)',
'ECS Total Common Shares Outstanding (actual)',
'Market Capitalization (Rp.M)',
'Year Close Stock Price (Rp.)',
'Total Capital (Rp.M)',
'Cash & Short-term Investments (Rp.M)',
'Net Intangibles (Rp.M)']
Sesuaikan String NA & NM
Cek kolom non-numerik terlebih dahulu.
numeric_cols = df_findat_final.select_dtypes(include=['number']).columns.tolist()
categorical_cols = df_findat_final.select_dtypes(include=['object']).columns.tolist()
print("Numeric columns:")
print(numeric_cols)
print("\nCategorical columns:")
print(categorical_cols)Numeric columns:
['Year', 'Year Established', 'Percent Owned - All Institutions (%)', 'Percent Owned - Insiders (%)', 'Total Liabilities (Rp.M)', 'Total Assets (Rp.M)', 'Working Capital (Rp.M)', 'Total Current Assets (Rp.M)', 'Total Current Liabilities (Rp.M)', 'Inventory (Rp.M)', 'Prepaid Exp. (Rp.M)', 'Long-term Debt (Rp.M)', 'Short-term Borrowings (Rp.M)', 'Current Portion of LT Debt & Leases (Rp.M)', 'Total Debt (Rp.M)', 'Total Equity (Rp.M)', 'Net Property, Plant & Equipment (Rp.M)', 'Cost Of Goods Sold (Rp.M)', 'Total Revenue (Rp.M)', 'Operating Income (Rp.M)', 'EBITDA (Rp.M)', 'EBIT (Rp.M)', 'Net Income to Company (Rp.M)', 'Cash from Ops. (Rp.M)', 'Net Change in Cash (Rp.M)', 'ECS Total Common Shares Outstanding (actual)', 'Market Capitalization (Rp.M)', 'Year Close Stock Price (Rp.)', 'Total Capital (Rp.M)', 'Cash & Short-term Investments (Rp.M)', 'Net Intangibles (Rp.M)']
Categorical columns:
['ticker', 'Entity Name', '1st Level Primary Industry', 'Date Established MM/dd/yyyy', 'Sector', 'Industry Group', 'Parent Percent Owned (%)', 'Current Ratio (x)', 'Quick Ratio (x)']
Ubah string NA, NM, atau NAN menjadi null
# Kolom yang akan dicek dan diconvert
target_cols = [
'ticker',
'Entity Name',
'1st Level Primary Industry',
'Date Established MM/dd/yyyy',
'Sector',
'Industry Group',
'Parent Percent Owned (%)',
'Current Ratio (x)',
'Quick Ratio (x)'
]
# Pastikan hanya menggunakan kolom yang ada di dataframe (untuk menghindari error jika ada typo nama kolom)
# valid_cols = [c for c in target_cols if c in df_findat_final.columns]
valid_cols = target_cols
# Pola Regex
# Menangkap: "na", "nan", "nm" dengan variasi spasi di awal
# dan variasi spasi/persen di akhir.
trash_pattern_specific = r'(?i)^\s*(?:NA|NAN|NM)[\s%]*$'
# --- TAHAP 1: DETEKSI & TAMPILKAN ---
# Buat filter hanya pada kolom target
# Convert ke string sementara (.astype(str)) agar regex berjalan lancar
mask = df_findat_final[valid_cols].astype(str).apply(
lambda x: x.str.contains(trash_pattern_specific, regex=True)
)
# Ambil baris yang setidaknya memiliki 1 nilai sampah di kolom target
rows_with_trash = df_findat_final[mask.any(axis=1)]
print(f"Ditemukan {len(rows_with_trash)} baris yang mengandung 'NA', 'NAN', atau 'NM' pada kolom target.")
if len(rows_with_trash) > 0:
print("\n--- Preview Data (Hanya Kolom Target) ---")
# Menampilkan hanya kolom target sesuai permintaan
display(rows_with_trash[valid_cols])
# --- TAHAP 2: UBAH JADI NULL ---
# Lakukan replace hanya pada kolom target
df_findat_final[valid_cols] = df_findat_final[valid_cols].replace(
to_replace=trash_pattern_specific,
value=np.nan,
regex=True
)
print("\n[SUKSES] Nilai tersebut telah diubah menjadi Null (NaN).")Ditemukan 3806 baris yang mengandung 'NA', 'NAN', atau 'NM' pada kolom target.
--- Preview Data (Hanya Kolom Target) ---
| variable | ticker | Entity Name | 1st Level Primary Industry | Date Established MM/dd/yyyy | Sector | Industry Group | Parent Percent Owned (%) | Current Ratio (x) | Quick Ratio (x) |
|---|---|---|---|---|---|---|---|---|---|
| 136 | ADCP | PT Adhi Commuter Properti Tbk (IDX:ADCP) | Real Estate | 2018-03-09 00:00:00 | Real Estate | Real Estate Management and Development | NA% | 1.441000 | 0.047000 |
| 137 | ADCP | PT Adhi Commuter Properti Tbk (IDX:ADCP) | Real Estate | 2018-03-09 00:00:00 | Real Estate | Real Estate Management and Development | NA% | 1.333000 | 0.133000 |
| 138 | ADCP | PT Adhi Commuter Properti Tbk (IDX:ADCP) | Real Estate | 2018-03-09 00:00:00 | Real Estate | Real Estate Management and Development | NA% | 0.960000 | 0.064000 |
| 170 | ADHI | PT Adhi Karya (Persero) Tbk (IDX:ADHI) | Industrials | 1960-03-11 00:00:00 | Industrials | Capital Goods | NA% | 1.123000 | 0.697000 |
| 171 | ADHI | PT Adhi Karya (Persero) Tbk (IDX:ADHI) | Industrials | 1960-03-11 00:00:00 | Industrials | Capital Goods | NA% | 1.144000 | 0.843000 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 15678 | ZONE | PT Mega Perintis Tbk (IDX:ZONE) | Consumer | 2005-10-21 00:00:00 | Consumer Discretionary | Consumer Discretionary Distribution and Retail | NA% | 1.583000 | 0.168000 |
| 15679 | ZONE | PT Mega Perintis Tbk (IDX:ZONE) | Consumer | 2005-10-21 00:00:00 | Consumer Discretionary | Consumer Discretionary Distribution and Retail | NA% | 2.369000 | 0.477000 |
| 15691 | ZYRX | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | Technology, Media & Telecommunications | None | Information Technology | Technology Hardware and Equipment | NA% | 3.402000 | 0.775000 |
| 15692 | ZYRX | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | Technology, Media & Telecommunications | None | Information Technology | Technology Hardware and Equipment | NA% | 2.073000 | 0.401000 |
| 15693 | ZYRX | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | Technology, Media & Telecommunications | None | Information Technology | Technology Hardware and Equipment | NA% | 1.442000 | 0.668000 |
3806 rows × 9 columns
[SUKSES] Nilai tersebut telah diubah menjadi Null (NaN).
/tmp/ipykernel_108742/3875649896.py:44: FutureWarning:
Downcasting behavior in `replace` is deprecated and will be removed in a future version. To retain the old behavior, explicitly call `result.infer_objects(copy=False)`. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)`
print("--- HASIL VERIFIKASI KEBERSIHAN DATA ---")
print(f"Mengecek {len(valid_cols)} kolom target...\n")
total_sisa = 0
for col in valid_cols:
# LANGKAH KUNCI:
# .dropna() membuang Null asli agar tidak diubah jadi string "nan"
# .astype(str) mengubah sisa data menjadi string untuk dicek regex
sisa_data = df_findat_final[col].dropna().astype(str)
# Hitung berapa yang cocok dengan pola sampah
jumlah_sampah = sisa_data.str.contains(trash_pattern_specific, regex=True).sum()
if jumlah_sampah > 0:
print(f"[!] Kolom '{col}' MASIH KOTOR: ditemukan {jumlah_sampah} nilai.")
total_sisa += jumlah_sampah
else:
print(f"[OK] Kolom '{col}': Bersih.")
print("-" * 40)
if total_sisa == 0:
print("KESIMPULAN: Sukses! Tidak ada lagi nilai NA, NAN, atau NM di kolom-kolom tersebut.")
else:
print(f"PERINGATAN: Masih ada total {total_sisa} nilai sampah yang tertinggal.")--- HASIL VERIFIKASI KEBERSIHAN DATA ---
Mengecek 9 kolom target...
[OK] Kolom 'ticker': Bersih.
[OK] Kolom 'Entity Name': Bersih.
[OK] Kolom '1st Level Primary Industry': Bersih.
[OK] Kolom 'Date Established MM/dd/yyyy': Bersih.
[OK] Kolom 'Sector': Bersih.
[OK] Kolom 'Industry Group': Bersih.
[OK] Kolom 'Parent Percent Owned (%)': Bersih.
[OK] Kolom 'Current Ratio (x)': Bersih.
[OK] Kolom 'Quick Ratio (x)': Bersih.
----------------------------------------
KESIMPULAN: Sukses! Tidak ada lagi nilai NA, NAN, atau NM di kolom-kolom tersebut.
Ubah Kolom Numerik yang Masih String
numeric_cols = df_findat_final.select_dtypes(include=['number']).columns.tolist()
categorical_cols = df_findat_final.select_dtypes(include=['object']).columns.tolist()
print("Numeric columns:")
print(numeric_cols)
print("\nCategorical columns:")
print(categorical_cols)Numeric columns:
['Year', 'Year Established', 'Percent Owned - All Institutions (%)', 'Percent Owned - Insiders (%)', 'Total Liabilities (Rp.M)', 'Total Assets (Rp.M)', 'Current Ratio (x)', 'Quick Ratio (x)', 'Working Capital (Rp.M)', 'Total Current Assets (Rp.M)', 'Total Current Liabilities (Rp.M)', 'Inventory (Rp.M)', 'Prepaid Exp. (Rp.M)', 'Long-term Debt (Rp.M)', 'Short-term Borrowings (Rp.M)', 'Current Portion of LT Debt & Leases (Rp.M)', 'Total Debt (Rp.M)', 'Total Equity (Rp.M)', 'Net Property, Plant & Equipment (Rp.M)', 'Cost Of Goods Sold (Rp.M)', 'Total Revenue (Rp.M)', 'Operating Income (Rp.M)', 'EBITDA (Rp.M)', 'EBIT (Rp.M)', 'Net Income to Company (Rp.M)', 'Cash from Ops. (Rp.M)', 'Net Change in Cash (Rp.M)', 'ECS Total Common Shares Outstanding (actual)', 'Market Capitalization (Rp.M)', 'Year Close Stock Price (Rp.)', 'Total Capital (Rp.M)', 'Cash & Short-term Investments (Rp.M)', 'Net Intangibles (Rp.M)']
Categorical columns:
['ticker', 'Entity Name', '1st Level Primary Industry', 'Date Established MM/dd/yyyy', 'Sector', 'Industry Group', 'Parent Percent Owned (%)']
Parent Percent Owned (%)
df_findat_final['Parent Percent Owned (%)'].unique()array([0.7968, 0.5116, 0.8776, 0.5355, 0.6, 0.501, nan, 0.9152, 0.9207,
0.495, 0.84, 0.4566, 0.8685, 0.9149, 0.6257, 0.8363, 0.568, 0.7421,
0.596, 0.9825, 0.6875, 0.5109, 0.8079, 0.9604, 0.7095, 0.5839,
0.9977, 0.5319, 0.9592, 0.65, 0.5784, 0.588, 0.8272, 1, 0.7333,
0.7686, 0.5011, 0.7739, 0.776, 0.0101, 0.5146, 0.8, 0.9999, 0.6969,
0.9235, 0.747, 0.5969, 0.5367, 0.8201, 0.5494, 0.6088, 0.6688,
0.676, 0.8944, 0.5682, 0.75, 0.7291, 0.3445, 0.5048, 0.7317,
0.7059, 0.6611, 0.7911, 0.7929, 0.4815, 0.2393, 0.54,
'15.85%; 6.30%', 0.8656, 0.2283, 0.5249, 0.3852, 0.5113, 0.5267,
0.9157, 0.51, 0.811, 0.5737, 0.52, 0.4575, 0.2953, 0.9144,
'33.95%; 45.02%', 0.9871, 0.576, 0.8451, 0.6577, 0.6159, 0.6467,
0.5147, 0.2009, 0.5456, 0.6714, 0.5999, 0.5, 0.9096, 0.9105, 0.7,
0.4423, 0.5502, 0.6706, 0.6113, 0.68, 0.8702, 0.9616, 0.5149,
0.5961, 0.7021, 0.606, 0.7777, 0.5577, 0.5099, 0.5475, 0.0963,
0.6112, 0.5553, 0.32, 0.5723, 0.5284, 0.7386, 0.5343, 0.5229,
0.6163, 0.5833, 0.5728, 0.8973, 0.9961, 0.5118, 0.6929, 0.5989,
0.8856, 0.9246, 0.4852, 0.8213, 0.8236, 0.511, 0.6677, 0.9247,
0.5469, 0.7999, 0.5646, 0.6817, 0.8689, 0.7926, 0.997, 0.7906,
'50.00%; 50.00%', 0.7733, 0.925, 0.6107, 0.7981, 0.85, 0.7474,
0.6928, 0.5043, 0.72, 0.7517, 0.6828, 0.848, 0.708, 0.996, 0.5434,
0.6565, 0.6358, 0.4943, 0.7832, 0.7084, 0.7979, 0.7199, 0.5507,
0.7017, 0.9998, 0.5566, 0.7942, 0.9146, 0.9764, 0.4949, 0.9197,
0.8066, 0.5007, 0.6728, 0.881, 0.5628, 0.5554, 0.0538, 0.7371,
0.9242, 0.5473, 0.5604, 0.7128, 0.6067, 0.7848, 0.99, 0.6564,
0.569, 0.6514, 0.9991, 0.5256, 0.4003, 0.5795, 0.5298, 0.6402,
0.5767, 0.9002, 0.8019, 0.5421, 0.4947, 0.5602, 0.7481, 0.6607,
0.8031, 0.9204, 0.7692, 0.525, 0.9987, 0.55, 0.625, 0.8842, 0.9909,
0.727, 0.5075, 0.9, 0.6252, 0.8083, 0.8171, 0.57, 0.6752, 0.5714,
0.595, 0.5572, 0.5727, 0.6884, 0.7909, 0.4239, 0.5004, 0.6726,
0.515, 0.5802, 0.7399, 0.763, 0.9935, 0.7874, 0.5649, 0.7709,
0.6187, 0.8178, 0.4203, 0.8694, 0.8367, 0.5138, 0.5669, 0.505,
0.7126, 0.2053, 0.7257, 0.919, 0.9233, 0.8679, 0.5903, 0.7505,
0.5951, 0.8469, 0.8489, 0.805, 0.5128, 0.6344, 0.4, 0.7525, 0.8508,
0.2397, 0.6098, 0.833, 0.6215, 0.7466, 0.6293, 0.5677, 0.5696,
0.6901, 0.6196, 0.5009, 0.7118, 0.4604, 0.673, 0.2971, 0.6786,
0.557, 0.8498, 0.5785, 0.6495, 0.6593, 0.8182, 0.97, 0.9912,
0.6868, 0.685, 0.6479, 0.5886, 0.7056, 0.7792, 0.8649, 0.8054,
0.8492, 0.993, 0.7984, 0.777, 0.7188, 0.7426, 0.6301, 0.7136, 0.45,
0.9075, 0.668, 0.9194, 0.6572, 0.7897, 0.8162, 0.7346, 0.5975,
0.7125, 0.9239, 0.7551, 0.8352, 0.9897, 0.5797, 0.512, 0.8007,
0.5611, 0.5104, 0.7165, 0.3383, 0.5812, 0.7989, 0.793, 0.7562,
0.5027, 0.5375, 0.7665, 0.5676, 0.9996, 0.7722, 0.678, 0.9189,
0.629, 0.7115, 0.7995, 0.1646, 0.6934, 0.8465, 0.7354, 0.5967,
0.5209, 0.8522, 0.6191, 0.761, 0.5875, 0.565, 0.5246, 0.5956,
0.5112, 0.6218, 0.8381, 0.266, 0.5283, 0.6415, 0.7753, 0.585,
0.6055, 0.5929, 0.2378, 0.7429, 0.6998, 0.7295, 0.5694, 0.8966,
0.6744, 0.701, 0.6505, 0.6749, 0.6815, 0.7534, 0.8946, 0.912,
0.6321], dtype=object)
Terlihat bahwa sebagian besar data pada kolom ‘Parent Percent Owned (%)’ masih berupa desimal, belum persentase. Ada juga yang berisi 2 data karena memiliki 2 parent. Untuk itu data kolom ini perlu disesuaikan.
def clean_ownership_data(value):
# handle "NA%" atau data kosong/null
if pd.isna(value) or value == "NA%":
return np.nan
val_str = str(value).strip() # trim spasi
# handle jika ada MULTIPLE values (dipisahkan titik koma ';')
# Contoh: "15.85; 6.30" atau "50%; 25%"
if ";" in val_str:
parts = val_str.split(";")
total = 0
valid_parts = 0
for part in parts:
# Panggil fungsi ini sendiri secara rekursif untuk setiap bagian
cleaned_part = clean_ownership_data(part)
if pd.notna(cleaned_part):
total += cleaned_part
valid_parts += 1
return total if valid_parts > 0 else np.nan
# handle format persentase (misal 50.25%) -> hapus % jadi float
if "%" in val_str:
try:
return float(val_str.replace("%", ""))
except ValueError:
return np.nan
# 4. Handle format desimal atau angka biasa
else:
try:
float_val = float(val_str)
# LOGIC TAMBAHAN :
# angka <= 1, kemungkinan desimal (0.50 -> 50%)
# angka > 1, kemungkinan sudah persen tapi tanpa simbol (15.85 -> 15.85%)
if float_val <= 1.0:
return float_val * 100
else:
return float_val
except ValueError:
return np.nan
# terapkan
df_findat_final["Parent Percent Owned (%)"] = df_findat_final["Parent Percent Owned (%)"].apply(clean_ownership_data)
# cek hasil
print("Statistik Data:")
print(df_findat_final["Parent Percent Owned (%)"].describe())
print("\nContoh data:")
print(df_findat_final[["ticker", "Year", "Parent Percent Owned (%)"]].sample(10))Statistik Data:
count 4868.000000
mean 66.388841
std 17.783044
min 1.010000
25% 54.892500
50% 65.140000
75% 80.000000
max 100.000000
Name: Parent Percent Owned (%), dtype: float64
Contoh data:
variable ticker Year Parent Percent Owned (%)
5571 GEMS 2012 51.000000
1859 BCIP 2018 NaN
4440 DMAS 2021 57.280000
2258 BISI 2010 NaN
1221 ASRM 2010 NaN
12480 RUNS 2022 NaN
11427 POLA 2021 99.990000
6741 IMPC 2015 NaN
15423 WTON 2020 60.000000
5557 GEMA 2009 74.740000
Tambah Kolom Tahun IPO
dict_perusahaan_tahun_ipo = dict(zip(df_emiten_idx['Kode'], df_emiten_idx['Tanggal Pencatatan'].dt.year))
print(dict_perusahaan_tahun_ipo){'AALI': 1997, 'ABBA': 2002, 'ABDA': 1989, 'ABMM': 2011, 'ACES': 2007, 'ACST': 2013, 'ADES': 1994, 'ADHI': 2004, 'ADMF': 2004, 'ADMG': 1993, 'ADRO': 2008, 'AGII': 2016, 'AGRO': 2003, 'AGRS': 2014, 'AHAP': 1990, 'AIMS': 2001, 'AISA': 1997, 'AKKU': 2004, 'AKPI': 1992, 'AKRA': 1994, 'AKSI': 2001, 'ALDO': 2011, 'ALKA': 1990, 'ALMI': 1997, 'ALTO': 2012, 'AMAG': 2005, 'AMFG': 1995, 'AMIN': 2015, 'AMRT': 2009, 'ANJT': 2013, 'ANTM': 1997, 'APEX': 2013, 'APIC': 2002, 'APII': 2013, 'APLI': 2000, 'APLN': 2010, 'ARGO': 1991, 'ARII': 2011, 'ARNA': 2001, 'ARTA': 2002, 'ARTI': 2003, 'ARTO': 2016, 'ASBI': 1989, 'ASDM': 1989, 'ASGR': 1989, 'ASII': 1990, 'ASJT': 2003, 'ASMI': 2014, 'ASRI': 2007, 'ASRM': 1990, 'ASSA': 2012, 'ATIC': 2015, 'AUTO': 1998, 'BABP': 2002, 'BACA': 2007, 'BAJA': 2011, 'BALI': 2014, 'BAPA': 2008, 'BATA': 1982, 'BAYU': 1989, 'BBCA': 2000, 'BBHI': 2015, 'BBKP': 2006, 'BBLD': 1990, 'BBMD': 2013, 'BBNI': 1996, 'BBRI': 2003, 'BBRM': 2013, 'BBTN': 2009, 'BBYB': 2015, 'BCAP': 2001, 'BCIC': 1997, 'BCIP': 2009, 'BDMN': 1989, 'BEKS': 2001, 'BEST': 2012, 'BFIN': 1990, 'BGTG': 2016, 'BHIT': 1997, 'BIKA': 2015, 'BIMA': 1994, 'BINA': 2014, 'BIPI': 2010, 'BIPP': 1995, 'BIRD': 2014, 'BISI': 2007, 'BJBR': 2010, 'BJTM': 2012, 'BKDP': 2007, 'BKSL': 1997, 'BKSW': 2002, 'BLTA': 1990, 'BLTZ': 2014, 'BMAS': 2013, 'BMRI': 2003, 'BMSR': 1999, 'BMTR': 1995, 'BNBA': 2006, 'BNBR': 1989, 'BNGA': 1989, 'BNII': 1989, 'BNLI': 1990, 'BOLT': 2015, 'BPFI': 2009, 'BPII': 2014, 'BRAM': 1990, 'BRMS': 2010, 'BRNA': 1989, 'BRPT': 1993, 'BSDE': 2008, 'BSIM': 2010, 'BSSR': 2012, 'BSWD': 2002, 'BTEK': 2004, 'BTEL': 2006, 'BTON': 2001, 'BTPN': 2008, 'BUDI': 1995, 'BUKK': 2015, 'BULL': 2011, 'BUMI': 1990, 'BUVA': 2010, 'BVIC': 1999, 'BWPT': 2009, 'BYAN': 2008, 'CANI': 2014, 'CASS': 2011, 'CEKA': 1996, 'CENT': 2001, 'CFIN': 1990, 'CINT': 2014, 'CITA': 2002, 'CLPI': 2001, 'CMNP': 1995, 'CMPP': 1994, 'CNKO': 2001, 'CNTX': 1979, 'COWL': 2007, 'CPIN': 1991, 'CPRO': 2006, 'CSAP': 2007, 'CTBN': 1989, 'CTRA': 1994, 'CTTH': 1996, 'DART': 1990, 'DEFI': 2001, 'DEWA': 2007, 'DGIK': 2007, 'DILD': 1991, 'DKFT': 1997, 'DLTA': 1984, 'DMAS': 2015, 'DNAR': 2014, 'DNET': 2000, 'DOID': 2001, 'DPNS': 1990, 'DSFI': 2000, 'DSNG': 2013, 'DSSA': 2009, 'DUTI': 1994, 'DVLA': 1994, 'DYAN': 2013, 'ECII': 2013, 'EKAD': 1990, 'ELSA': 2008, 'ELTY': 1995, 'EMDE': 2011, 'EMTK': 2010, 'ENRG': 2004, 'EPMT': 1994, 'ERAA': 2011, 'ERTX': 1990, 'ESSA': 2012, 'ESTI': 1992, 'ETWA': 1997, 'EXCL': 2005, 'FAST': 1993, 'FASW': 1994, 'FISH': 2002, 'FMII': 2000, 'FORU': 2002, 'FPNI': 2002, 'GAMA': 2012, 'GDST': 2009, 'GDYR': 1980, 'GEMA': 2002, 'GEMS': 2011, 'GGRM': 1990, 'GIAA': 2011, 'GJTL': 1990, 'GLOB': 2012, 'GMTD': 2000, 'GOLD': 2010, 'GOLL': 2014, 'GPRA': 2007, 'GSMF': 1989, 'GTBO': 2009, 'GWSA': 2011, 'GZCO': 2008, 'HADE': 2004, 'HDFA': 2011, 'HERO': 1989, 'HEXA': 1995, 'HITS': 1997, 'HMSP': 1990, 'HOME': 2008, 'HOTL': 2013, 'HRUM': 2010, 'IATA': 2006, 'IBFN': 2014, 'IBST': 2012, 'ICBP': 2010, 'ICON': 2005, 'IGAR': 1990, 'IIKP': 2002, 'IKAI': 1997, 'IKBI': 1991, 'IMAS': 1993, 'IMJS': 2013, 'IMPC': 2014, 'INAF': 2001, 'INAI': 1994, 'INCI': 1990, 'INCO': 1990, 'INDF': 1994, 'INDR': 1990, 'INDS': 1990, 'INDX': 2001, 'INDY': 2008, 'INKP': 1990, 'INPC': 1990, 'INPP': 2004, 'INRU': 1990, 'INTA': 1993, 'INTD': 1989, 'INTP': 1989, 'IPOL': 2010, 'ISAT': 1994, 'ISSP': 2013, 'ITMA': 1990, 'ITMG': 2007, 'JAWA': 2011, 'JECC': 1992, 'JIHD': 1984, 'JKON': 2007, 'JPFA': 1989, 'JRPT': 1994, 'JSMR': 2007, 'JSPT': 1998, 'JTPE': 2002, 'KAEF': 2001, 'KARW': 1994, 'KBLI': 1992, 'KBLM': 1992, 'KBLV': 2000, 'KBRI': 2008, 'KDSI': 1996, 'KIAS': 1994, 'KICI': 1993, 'KIJA': 1995, 'KKGI': 1991, 'KLBF': 1991, 'KOBX': 2012, 'KOIN': 2008, 'KONI': 1995, 'KOPI': 2015, 'KPIG': 2000, 'KRAS': 2010, 'KREN': 2002, 'LAPD': 2001, 'LCGP': 2007, 'LEAD': 2013, 'LINK': 2014, 'LION': 1993, 'LMAS': 2001, 'LMPI': 1994, 'LMSH': 1990, 'LPCK': 1997, 'LPGI': 1997, 'LPIN': 1990, 'LPKR': 1996, 'LPLI': 1989, 'LPPF': 1989, 'LPPS': 1994, 'LRNA': 2014, 'LSIP': 1996, 'LTLS': 1997, 'MAGP': 2013, 'MAIN': 2006, 'MAPI': 2004, 'MASA': 2005, 'MAYA': 1997, 'MBAP': 2014, 'MBSS': 2011, 'MBTO': 2011, 'MCOR': 2007, 'MDIA': 2014, 'MDKA': 2015, 'MDLN': 1993, 'MDRN': 1991, 'MEDC': 1994, 'MEGA': 2000, 'MERK': 1981, 'META': 2001, 'MFIN': 2005, 'MFMI': 2010, 'MGNA': 2014, 'MICE': 2005, 'MIDI': 2010, 'MIKA': 2015, 'MIRA': 1997, 'MITI': 1997, 'MKPI': 2009, 'MLBI': 1981, 'MLIA': 1994, 'MLPL': 1989, 'MLPT': 2013, 'MMLP': 2015, 'MNCN': 2007, 'MPMX': 2013, 'MPPA': 1992, 'MRAT': 1995, 'MREI': 1989, 'MSKY': 2012, 'MTDL': 1990, 'MTFN': 1990, 'MTLA': 2011, 'MTSM': 1992, 'MYOH': 2000, 'MYOR': 1990, 'MYTX': 1989, 'NELY': 2012, 'NIKL': 2009, 'NIRO': 2012, 'NISP': 1994, 'NOBU': 2013, 'NRCA': 2013, 'OCAP': 2003, 'OKAS': 2006, 'OMRE': 1994, 'PADI': 2012, 'PALM': 2012, 'PANR': 2001, 'PANS': 2000, 'PBRX': 1990, 'PDES': 2008, 'PEGE': 2005, 'PGAS': 2003, 'PGLI': 2000, 'PICO': 1996, 'PJAA': 2004, 'PKPK': 2007, 'PLAS': 2001, 'PLIN': 1992, 'PNBN': 1982, 'PNBS': 2014, 'PNIN': 1983, 'PNLF': 1983, 'PNSE': 1990, 'POLY': 1991, 'POOL': 1991, 'PPRO': 2015, 'PSAB': 2003, 'PSDN': 1994, 'PSKT': 1995, 'PTBA': 2002, 'PTIS': 2011, 'PTPP': 2010, 'PTRO': 1990, 'PTSN': 2007, 'PTSP': 1994, 'PUDP': 1994, 'PWON': 1989, 'PYFA': 2001, 'RAJA': 2006, 'RALS': 1996, 'RANC': 2012, 'RBMS': 1997, 'RDTX': 1990, 'RELI': 2005, 'RICY': 1998, 'RIGS': 1990, 'RIMO': 2000, 'RODA': 2001, 'ROTI': 2010, 'RUIS': 2006, 'SAFE': 1994, 'SAME': 2013, 'SCCO': 1982, 'SCMA': 2002, 'SCPI': 1990, 'SDMU': 2011, 'SDPC': 1990, 'SDRA': 2006, 'SGRO': 2007, 'SHID': 1990, 'SIDO': 2013, 'SILO': 2013, 'SIMA': 1994, 'SIMP': 2011, 'SIPD': 1996, 'SKBM': 2012, 'SKLT': 1993, 'SKYB': 2010, 'SMAR': 1992, 'SMBR': 2013, 'SMCB': 1977, 'SMDM': 1995, 'SMDR': 1999, 'SMGR': 1991, 'SMMA': 1995, 'SMMT': 1997, 'SMRA': 1990, 'SMRU': 2011, 'SMSM': 1996, 'SOCI': 2014, 'SONA': 1992, 'SPMA': 1994, 'SQMI': 2004, 'SRAJ': 2011, 'SRIL': 2013, 'SRSN': 1993, 'SRTG': 2013, 'SSIA': 1997, 'SSMS': 2013, 'SSTM': 1997, 'STAR': 2011, 'STTP': 1996, 'SUGI': 2002, 'SULI': 1994, 'SUPR': 2011, 'TALF': 2014, 'TARA': 2014, 'TAXI': 2012, 'TBIG': 2010, 'TBLA': 2000, 'TBMS': 1990, 'TCID': 1993, 'TELE': 2012, 'TFCO': 1980, 'TGKA': 1990, 'TIFA': 2011, 'TINS': 1995, 'TIRA': 1993, 'TIRT': 1999, 'TKIM': 1990, 'TLKM': 1995, 'TMAS': 2003, 'TMPO': 2001, 'TOBA': 2012, 'TOTL': 2006, 'TOTO': 1990, 'TOWR': 2010, 'TPIA': 2008, 'TPMA': 2013, 'TRAM': 2008, 'TRIL': 2008, 'TRIM': 2000, 'TRIO': 2009, 'TRIS': 2012, 'TRST': 1990, 'TRUS': 2002, 'TSPC': 1994, 'ULTJ': 1990, 'UNIC': 1989, 'UNIT': 2002, 'UNSP': 1990, 'UNTR': 1989, 'UNVR': 1982, 'VICO': 2013, 'VINS': 2015, 'VIVA': 2011, 'VOKS': 1990, 'VRNA': 2008, 'WAPO': 2001, 'WEHA': 2007, 'WICO': 1994, 'WIIM': 2012, 'WIKA': 2007, 'WINS': 2010, 'WOMF': 2004, 'WSKT': 2012, 'WTON': 2014, 'YPAS': 2008, 'YULE': 2004, 'ZBRA': 1991, 'SHIP': 2016, 'CASA': 2016, 'DAYA': 2016, 'DPUM': 2015, 'IDPR': 2015, 'JGLE': 2016, 'KINO': 2015, 'MARI': 2016, 'MKNT': 2015, 'MTRA': 2016, 'OASA': 2016, 'POWR': 2016, 'INCF': 2016, 'WSBP': 2016, 'PBSA': 2016, 'PRDA': 2016, 'BOGA': 2016, 'BRIS': 2018, 'PORT': 2017, 'CARS': 2017, 'MINA': 2017, 'CLEO': 2017, 'TAMU': 2017, 'CSIS': 2017, 'TGRA': 2017, 'FIRE': 2017, 'TOPS': 2017, 'KMTR': 2017, 'ARMY': 2017, 'MAPB': 2017, 'WOOD': 2017, 'HRTA': 2017, 'MABA': 2017, 'HOKI': 2017, 'MPOW': 2017, 'MARK': 2017, 'NASA': 2017, 'MDKI': 2017, 'BELL': 2017, 'KIOS': 2017, 'GMFI': 2017, 'MTWI': 2017, 'ZINC': 2017, 'MCAS': 2017, 'PPRE': 2017, 'WEGE': 2017, 'PSSI': 2017, 'MORA': 2022, 'DWGL': 2017, 'PBID': 2017, 'JMAS': 2017, 'CAMP': 2017, 'IPCM': 2017, 'PCAR': 2017, 'LCKM': 2018, 'BOSS': 2018, 'HELI': 2018, 'JSKY': 2018, 'INPS': 2018, 'GHON': 2018, 'TDPM': 2018, 'DFAM': 2018, 'NICK': 2018, 'BTPS': 2018, 'SPTO': 2018, 'PRIM': 2018, 'HEAL': 2018, 'TRUK': 2018, 'PZZA': 2018, 'TUGU': 2018, 'MSIN': 2018, 'SWAT': 2018, 'TNCA': 2018, 'MAPA': 2018, 'TCPI': 2018, 'IPCC': 2018, 'RISE': 2018, 'BPTR': 2018, 'POLL': 2018, 'NFCX': 2018, 'MGRO': 2018, 'NUSA': 2018, 'FILM': 2018, 'ANDI': 2018, 'LAND': 2018, 'MOLI': 2018, 'PANI': 2018, 'DIGI': 2018, 'CITY': 2018, 'SAPX': 2018, 'SURE': 2018, 'HKMU': 2018, 'MPRO': 2018, 'DUCK': 2018, 'GOOD': 2018, 'SKRN': 2018, 'YELO': 2018, 'CAKK': 2018, 'SATU': 2018, 'SOSS': 2018, 'DEAL': 2018, 'POLA': 2018, 'DIVA': 2018, 'LUCK': 2018, 'URBN': 2018, 'SOTS': 2018, 'ZONE': 2018, 'PEHA': 2018, 'FOOD': 2019, 'BEEF': 2019, 'POLI': 2019, 'CLAY': 2019, 'NATO': 2019, 'JAYA': 2019, 'COCO': 2019, 'MTPS': 2019, 'CPRI': 2019, 'HRME': 2019, 'POSA': 2019, 'JAST': 2019, 'FITT': 2019, 'BOLA': 2019, 'CCSI': 2019, 'SFAN': 2019, 'POLU': 2019, 'KJEN': 2019, 'KAYU': 2019, 'ITIC': 2019, 'PAMG': 2019, 'IPTV': 2019, 'BLUE': 2019, 'ENVY': 2019, 'EAST': 2019, 'LIFE': 2019, 'FUJI': 2019, 'KOTA': 2019, 'INOV': 2019, 'ARKA': 2019, 'SMKL': 2019, 'HDIT': 2019, 'KEEN': 2019, 'BAPI': 2019, 'TFAS': 2019, 'GGRP': 2019, 'OPMS': 2019, 'NZIA': 2019, 'SLIS': 2019, 'PURE': 2019, 'IRRA': 2019, 'DMMX': 2019, 'SINI': 2019, 'WOWS': 2019, 'ESIP': 2019, 'TEBE': 2019, 'KEJU': 2019, 'PSGO': 2019, 'AGAR': 2019, 'IFSH': 2019, 'REAL': 2019, 'IFII': 2019, 'PMJS': 2019, 'UCID': 2019, 'GLVA': 2019, 'PGJO': 2020, 'AMAR': 2020, 'CSRA': 2020, 'INDO': 2020, 'AMOR': 2020, 'TRIN': 2020, 'DMND': 2020, 'PURA': 2020, 'PTPW': 2020, 'TAMA': 2020, 'IKAN': 2020, 'SAMF': 2020, 'SBAT': 2020, 'KBAG': 2020, 'CBMF': 2020, 'RONY': 2020, 'CSMI': 2020, 'BBSS': 2020, 'BHAT': 2020, 'CASH': 2020, 'TECH': 2020, 'EPAC': 2020, 'UANG': 2020, 'PGUN': 2020, 'SOFA': 2020, 'PPGL': 2020, 'TOYS': 2020, 'SGER': 2020, 'TRJA': 2020, 'PNGO': 2020, 'SCNP': 2020, 'BBSI': 2020, 'KMDS': 2020, 'PURI': 2020, 'SOHO': 2020, 'HOMI': 2020, 'ROCK': 2020, 'ENZO': 2020, 'PLAN': 2020, 'PTDU': 2020, 'ATAP': 2020, 'VICI': 2020, 'PMMP': 2020, 'WIFI': 2020, 'FAPA': 2021, 'DCII': 2021, 'KETR': 2021, 'DGNS': 2021, 'UFOE': 2021, 'BANK': 2021, 'WMUU': 2021, 'EDGE': 2021, 'UNIQ': 2021, 'BEBS': 2021, 'SNLK': 2021, 'ZYRX': 2021, 'LFLO': 2021, 'FIMP': 2021, 'TAPG': 2021, 'NPGF': 2021, 'LUCY': 2021, 'ADCP': 2021, 'HOPE': 2021, 'MGLV': 2021, 'TRUE': 2021, 'LABA': 2021, 'ARCI': 2021, 'IPAC': 2021, 'MASB': 2021, 'BMHS': 2021, 'FLMC': 2021, 'NICL': 2021, 'UVCR': 2021, 'BUKA': 2021, 'HAIS': 2021, 'OILS': 2021, 'GPSO': 2021, 'MCOL': 2021, 'RSGK': 2021, 'RUNS': 2021, 'SBMA': 2021, 'CMNT': 2021, 'GTSI': 2021, 'IDEA': 2021, 'KUAS': 2021, 'BOBA': 2021, 'MTEL': 2021, 'DEPO': 2021, 'BINO': 2021, 'CMRY': 2021, 'WGSH': 2021, 'TAYS': 2021, 'WMPP': 2021, 'RMKE': 2021, 'OBMD': 2021, 'AVIA': 2021, 'IPPE': 2021, 'NASI': 2021, 'BSML': 2021, 'DRMA': 2021, 'ADMR': 2022, 'SEMA': 2022, 'ASLC': 2022, 'NETV': 2022, 'BAUT': 2022, 'ENAK': 2022, 'NTBK': 2022, 'SMKM': 2022, 'STAA': 2022, 'NANO': 2022, 'BIKE': 2022, 'WIRG': 2022, 'SICO': 2022, 'GOTO': 2022, 'TLDN': 2022, 'MTMH': 2022, 'WINR': 2022, 'IBOS': 2022, 'OLIV': 2022, 'ASHA': 2022, 'SWID': 2022, 'TRGU': 2022, 'ARKO': 2022, 'CHEM': 2022, 'DEWI': 2022, 'AXIO': 2022, 'KRYA': 2022, 'HATM': 2022, 'RCCC': 2022, 'GULA': 2022, 'JARR': 2022, 'AMMS': 2022, 'RAFI': 2022, 'KKES': 2022, 'ELPI': 2022, 'EURO': 2022, 'KLIN': 2022, 'TOOL': 2022, 'BUAH': 2022, 'CRAB': 2022, 'MEDS': 2022, 'COAL': 2022, 'PRAY': 2022, 'CBUT': 2022, 'BELI': 2022, 'MKTR': 2022, 'OMED': 2022, 'BSBK': 2022, 'PDPP': 2022, 'KDTN': 2022, 'ZATA': 2022, 'NINE': 2022, 'MMIX': 2022, 'PADA': 2022, 'ISAP': 2022, 'VTNY': 2022, 'SOUL': 2023, 'ELIT': 2023, 'BEER': 2023, 'CBPE': 2023, 'SUNI': 2023, 'CBRE': 2023, 'WINE': 2023, 'BMBL': 2023, 'PEVE': 2023, 'LAJU': 2023, 'FWCT': 2023, 'NAYZ': 2023, 'IRSX': 2023, 'PACK': 2023, 'VAST': 2023, 'CHIP': 2023, 'HALO': 2023, 'KING': 2023, 'PGEO': 2023, 'FUTR': 2023, 'HILL': 2023, 'BDKR': 2023, 'PTMP': 2023, 'SAGE': 2023, 'TRON': 2023, 'CUAN': 2023, 'NSSS': 2023, 'GTRA': 2023, 'HAJJ': 2023, 'PIPA': 2023, 'NCKL': 2023, 'MENN': 2023, 'AWAN': 2023, 'MBMA': 2023, 'RAAM': 2023, 'DOOH': 2023, 'JATI': 2023, 'TYRE': 2023, 'MPXL': 2023, 'SMIL': 2023, 'KLAS': 2023, 'MAXI': 2023, 'VKTR': 2023, 'RELF': 2023, 'AMMN': 2023, 'CRSN': 2023, 'GRPM': 2023, 'WIDI': 2023, 'TGUK': 2023, 'INET': 2023, 'MAHA': 2023, 'RMKO': 2023, 'CNMA': 2023, 'FOLK': 2023, 'HBAT': 2023, 'GRIA': 2023, 'PPRI': 2023, 'ERAL': 2023, 'CYBR': 2023, 'MUTU': 2023, 'LMAX': 2023, 'HUMI': 2023, 'MSIE': 2023, 'RSCH': 2023, 'BABY': 2023, 'AEGS': 2023, 'IOTF': 2023, 'KOCI': 2023, 'PTPS': 2023, 'BREN': 2023, 'STRK': 2023, 'KOKA': 2023, 'LOPI': 2023, 'UDNG': 2023, 'RGAS': 2023, 'MSTI': 2023, 'IKPM': 2023, 'AYAM': 2023, 'SURI': 2023, 'ASLI': 2024, 'CGAS': 2024, 'NICE': 2024, 'MSJA': 2024, 'SMLE': 2024, 'ACRO': 2024, 'MANG': 2024, 'GRPH': 2024, 'SMGA': 2024, 'UNTD': 2024, 'TOSK': 2024, 'MPIX': 2024, 'ALII': 2024, 'MKAP': 2024, 'MEJA': 2024, 'LIVE': 2024, 'HYGN': 2024, 'BAIK': 2024, 'VISI': 2024, 'AREA': 2024, 'MHKI': 2024, 'ATLA': 2024, 'DATA': 2024, 'SOLA': 2024, 'BATR': 2024, 'SPRE': 2024, 'PART': 2024, 'GOLF': 2024, 'ISEA': 2024, 'BLES': 2024, 'GUNA': 2024, 'LABS': 2024, 'DOSS': 2024, 'NEST': 2024, 'PTMR': 2024, 'VERN': 2024, 'DAAZ': 2024, 'BOAT': 2024, 'NAIK': 2024, 'AADI': 2024, 'MDIY': 2024, 'KSIX': 2025, 'RATU': 2025, 'YOII': 2025, 'HGII': 2025, 'BRRC': 2025, 'DGWG': 2025, 'CBDK': 2025, 'OBAT': 2025, 'MINE': 2025, 'ASPR': 2025, 'PSAT': 2025, 'COIN': 2025, 'CDIA': 2025, 'BLOG': 2025, 'MERI': 2025, 'CHEK': 2025, 'PMUI': 2025, 'EMAS': 2025, 'KAQI': 2025, 'YUPI': 2025, 'FORE': 2025, 'MDLA': 2025, 'DKHH': 2025, 'AYLS': 2020, 'DADA': 2020, 'ASPI': 2020, 'ESTA': 2020, 'BESS': 2020, 'AMAN': 2020, 'CARE': 2020}
# pastikan key di dict cocok dengan format ticker (4 huruf uppercase)
df_findat_final["IPO Year"] = (
df_findat_final["ticker"]
.map(dict_perusahaan_tahun_ipo)
.astype("Int64") # nullable integer kalau ada ticker yang tidak ketemu di dict
)
df_findat_final[["ticker", "Year", "IPO Year"]].head()| variable | ticker | Year | IPO Year |
|---|---|---|---|
| 17 | AALI | 2024 | 1997 |
| 18 | AALI | 2023 | 1997 |
| 19 | AALI | 2022 | 1997 |
| 20 | AALI | 2021 | 1997 |
| 21 | AALI | 2020 | 1997 |
missing_tickers = (
df_findat_final.loc[df_findat_final["IPO Year"].isna(), "ticker"]
.dropna()
.unique()
)
if len(missing_tickers) > 0:
raise ValueError(
f"Ada perusahaan yang tidak punya tahun IPO di dict_perusahaan_tahun_ipo: "
f"{', '.join(sorted(map(str, missing_tickers)))}"
)Hasil Load Data Finansial
df_findat_final.sample(5)| variable | ticker | Year | Entity Name | 1st Level Primary Industry | Year Established | Date Established MM/dd/yyyy | Sector | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.M) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | IPO Year |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1609 | BAYU | 2013 | PT Bayu Buana Tbk (IDX:BAYU) | Consumer | 1972.000000 | 1972-10-17 00:00:00 | Consumer Discretionary | Consumer Services | NaN | 34.670000 | NaN | 181911.296000 | 453681.364000 | 1.444000 | 1.324000 | 101215.968000 | 329164.065000 | 227948.097000 | NaN | 6822.698000 | NaN | NaN | NaN | 0.000000 | 221787.103000 | 20548.433000 | 1382666.226000 | 1469958.432000 | 20998.587000 | 24942.392000 | 20998.587000 | 21104.422000 | 66059.556000 | 64460.340000 | 353220780.000000 | 141288.312000 | 400.000000 | 221787.103000 | 205843.369000 | NaN | 1989 |
| 12121 | RANC | 2024 | PT Supra Boga Lestari Tbk (IDX:RANC) | Consumer | 1997.000000 | 1997-05-28 00:00:00 | Consumer Staples | Consumer Staples Distribution and Retail | 70.560000 | NaN | 0.110000 | 875045.588000 | 1197109.086000 | 0.892000 | 0.207000 | -65606.887000 | 541365.814000 | 606972.701000 | 399459.356000 | 12241.835000 | NaN | 60000.000000 | NaN | 370451.007000 | 322063.498000 | 543896.928000 | 2186574.396000 | 2874068.978000 | -42324.176000 | 28033.186000 | -42324.176000 | 26698.165000 | 77998.345000 | -16938.505000 | 1564487500.000000 | 735309.125000 | 470.000000 | 692514.505000 | 106107.689000 | 10329.092000 | 2012 |
| 5224 | FASW | 2019 | PT Fajar Surya Wisesa Tbk (IDX:FASW) | Materials | 1987.000000 | 1987-06-13 00:00:00 | Materials | Materials | 99.700000 | NaN | NaN | 6059395.121000 | 10751992.944000 | 0.704000 | 0.369000 | -1110259.102000 | 2641761.194000 | 3752020.296000 | 1094392.608000 | 38441.889000 | 1567244.621000 | 1692335.200000 | NaN | 4248225.014000 | 4692597.823000 | 7847119.796000 | 6459851.090000 | 8268503.880000 | 1369618.341000 | 1598327.786000 | 1369618.341000 | 968833.391000 | 1116219.496000 | -253971.195000 | 2477888787.000000 | 19079743.659900 | 7700.000000 | 8940822.837000 | 101255.876000 | 1472.464000 | 1994 |
| 9911 | MSIE | 2024 | PT Multisarana Intan Eduka Tbk (IDX:MSIE) | Real Estate | 2011.000000 | 2011-07-30 00:00:00 | Real Estate | Real Estate Management and Development | NaN | NaN | 75.430000 | 18596.055000 | 131321.384000 | 1.108000 | 0.774000 | 1078.118000 | 11062.096000 | 9983.978000 | NaN | NaN | 2405.192000 | 1751.469000 | NaN | 7953.600000 | 112725.330000 | 115853.389000 | 2165.145000 | 6414.263000 | -1120.784000 | -82.906000 | -1120.784000 | -301.893000 | 6808.031000 | -20330.500000 | 1460007754.000000 | 18980.100802 | 13.000000 | 120678.929000 | 5394.008000 | 2405.900000 | 2023 |
| 5092 | ESSA | 2015 | PT ESSA Industries Indonesia Tbk. (IDX:ESSA) | Materials | 2006.000000 | 2006-03-24 01:00:00 | Materials | Materials | NaN | 14.520000 | 38.100000 | 1307723.953939 | 3834551.707532 | 0.816000 | 0.471000 | -106312.815469 | 470937.843858 | 577250.659327 | 14021.277761 | 6627.861245 | 715744.356775 | NaN | NaN | 739219.623158 | 2526827.753593 | 1196028.348241 | 313956.026525 | 542945.657748 | 117129.147409 | 197604.250616 | 117129.147409 | 65297.007445 | -87070.568237 | 574.780607 | 11000000000.000000 | 1815000.000000 | 165.000000 | 3266047.376751 | 155674.162119 | NaN | 2012 |
Perbaikan Data Finansial
Cek Kesesuaian Tipe Data Kolom
numeric_cols = df_findat_final.select_dtypes(include=['number']).columns.tolist()
categorical_cols = df_findat_final.select_dtypes(include=['object']).columns.tolist()
print("Numeric columns:")
print(numeric_cols)
print("\nCategorical columns:")
print(categorical_cols)Numeric columns:
['Year', 'Year Established', 'Parent Percent Owned (%)', 'Percent Owned - All Institutions (%)', 'Percent Owned - Insiders (%)', 'Total Liabilities (Rp.M)', 'Total Assets (Rp.M)', 'Current Ratio (x)', 'Quick Ratio (x)', 'Working Capital (Rp.M)', 'Total Current Assets (Rp.M)', 'Total Current Liabilities (Rp.M)', 'Inventory (Rp.M)', 'Prepaid Exp. (Rp.M)', 'Long-term Debt (Rp.M)', 'Short-term Borrowings (Rp.M)', 'Current Portion of LT Debt & Leases (Rp.M)', 'Total Debt (Rp.M)', 'Total Equity (Rp.M)', 'Net Property, Plant & Equipment (Rp.M)', 'Cost Of Goods Sold (Rp.M)', 'Total Revenue (Rp.M)', 'Operating Income (Rp.M)', 'EBITDA (Rp.M)', 'EBIT (Rp.M)', 'Net Income to Company (Rp.M)', 'Cash from Ops. (Rp.M)', 'Net Change in Cash (Rp.M)', 'ECS Total Common Shares Outstanding (actual)', 'Market Capitalization (Rp.M)', 'Year Close Stock Price (Rp.)', 'Total Capital (Rp.M)', 'Cash & Short-term Investments (Rp.M)', 'Net Intangibles (Rp.M)', 'IPO Year']
Categorical columns:
['ticker', 'Entity Name', '1st Level Primary Industry', 'Date Established MM/dd/yyyy', 'Sector', 'Industry Group']
print('Cek apakah Date Established sudah berformat Datetime:')
print(pd.api.types.is_datetime64_any_dtype(df_findat_final['Date Established MM/dd/yyyy']))
df_findat_final['Date Established MM/dd/yyyy'].head(3)Cek apakah Date Established sudah berformat Datetime:
False
| Date Established MM/dd/yyyy | |
|---|---|
| 17 | 1988-10-03 00:00:00 |
| 18 | 1988-10-03 00:00:00 |
| 19 | 1988-10-03 00:00:00 |
print('Konversi Date Established ke Datetime dan Ubah Nama Kolom:')
df_findat_final['Date Established MM/dd/yyyy'] = pd.to_datetime(df_findat_final['Date Established MM/dd/yyyy'], errors="coerce")
df_findat_final = df_findat_final.rename(columns={'Date Established MM/dd/yyyy': 'Date Established'})
df_findat_final['Date Established'].head(3)Konversi Date Established ke Datetime dan Ubah Nama Kolom:
| Date Established | |
|---|---|
| 17 | 1988-10-03 |
| 18 | 1988-10-03 |
| 19 | 1988-10-03 |
Cek Null yg Bisa Dihitung Manual
Total Aset = Total Ekuitas + Liabilitas
cols_to_check = [
'Total Assets (Rp.M)',
'Total Equity (Rp.M)',
'Total Liabilities (Rp.M)'
]
# Cek yang bisa diisi
null_counts_before = df_findat_final[cols_to_check].isnull().sum(axis=1)
rows_target = (null_counts_before == 1).sum()
print(f"Jumlah baris yang akan diperbaiki (tepat 1 null): {rows_target}")
if rows_target == 0:
print("Tidak ada baris yang bisa diperbaiki.")Jumlah baris yang akan diperbaiki (tepat 1 null): 0
Tidak ada baris yang bisa diperbaiki.
Market Cap = Harga x Jumlah Saham
Dari ketiga kolom tersebut, jika yang null hanya 1 maka dapat dihitung.
cols_to_check = [
'ECS Total Common Shares Outstanding (actual)',
'Market Capitalization (Rp.M)',
'Year Close Stock Price (Rp.)'
]
col_shares = 'ECS Total Common Shares Outstanding (actual)'
col_mcap = 'Market Capitalization (Rp.M)'
col_price = 'Year Close Stock Price (Rp.)'
# 1. Cek jumlah null awal
null_counts_before = df_findat_final[cols_to_check].isnull().sum(axis=1)
rows_target = (null_counts_before == 1).sum()
print(f"Jumlah baris yang akan diperbaiki (tepat 1 null): {rows_target}")
# ---------------------------------------------------------
# PROSES PENGISIAN (IMPUTASI) DENGAN RUMUS
# ---------------------------------------------------------
# KASUS A: Shares Kosong -> Hitung pakai (Market Cap * 1jt) / Price
# Syarat: Shares IS NULL, tapi Market Cap & Price TIDAK NULL
mask_shares_null = (
df_findat_final[col_shares].isnull() &
df_findat_final[col_mcap].notnull() &
df_findat_final[col_price].notnull()
)
df_findat_final.loc[mask_shares_null, col_shares] = (
df_findat_final.loc[mask_shares_null, col_mcap] * 1_000_000
) / df_findat_final.loc[mask_shares_null, col_price]
# KASUS B: Market Cap Kosong -> Hitung pakai (Price * Shares) / 1jt
# Syarat: Market Cap IS NULL, tapi Price & Shares TIDAK NULL
mask_mcap_null = (
df_findat_final[col_mcap].isnull() &
df_findat_final[col_price].notnull() &
df_findat_final[col_shares].notnull()
)
df_findat_final.loc[mask_mcap_null, col_mcap] = (
df_findat_final.loc[mask_mcap_null, col_price] * df_findat_final.loc[mask_mcap_null, col_shares]
) / 1_000_000
# KASUS C: Price Kosong -> Hitung pakai (Market Cap * 1jt) / Shares
# Syarat: Price IS NULL, tapi Market Cap & Shares TIDAK NULL
mask_price_null = (
df_findat_final[col_price].isnull() &
df_findat_final[col_mcap].notnull() &
df_findat_final[col_shares].notnull()
)
df_findat_final.loc[mask_price_null, col_price] = (
df_findat_final.loc[mask_price_null, col_mcap] * 1_000_000
) / df_findat_final.loc[mask_price_null, col_shares]
# ---------------------------------------------------------
# Verifikasi Hasil
null_counts_after = df_findat_final[cols_to_check].isnull().sum(axis=1)
rows_remaining = (null_counts_after == 1).sum()
print(f"Sisa baris dengan 1 null setelah perbaikan: {rows_remaining} (Harus 0)")Jumlah baris yang akan diperbaiki (tepat 1 null): 38
Sisa baris dengan 1 null setelah perbaikan: 0 (Harus 0)
Working Capital = CA - CL
cols_to_check = [
'Working Capital (Rp.M)',
'Total Current Assets (Rp.M)',
'Total Current Liabilities (Rp.M)'
]
# Cek jumlah null tepat 1
null_counts_before = df_findat_final[cols_to_check].isnull().sum(axis=1)
rows_target = (null_counts_before == 1).sum()
print(f"Jumlah baris yang dapat diperbaiki (tepat 1 null): {rows_target}")Jumlah baris yang dapat diperbaiki (tepat 1 null): 21
df_findat_final[df_findat_final[cols_to_check].isna().sum(axis=1)==1][cols_to_check]| variable | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) |
|---|---|---|---|
| 3011 | NaN | 303261.676000 | 101907.258000 |
| 3012 | NaN | 233819.275000 | 67363.137000 |
| 3013 | NaN | 194827.419000 | 41344.686000 |
| 3014 | NaN | 189163.251000 | 41766.037000 |
| 3015 | NaN | 176074.194000 | 30419.619000 |
| 3016 | NaN | 138161.400000 | 25235.541000 |
| 3017 | NaN | 128801.476000 | 30523.107000 |
| 3019 | NaN | 125563.723000 | 24837.582000 |
| 3020 | NaN | 126889.991000 | 34947.793000 |
| 5731 | NaN | 88045.362000 | 29098.404000 |
| 5732 | NaN | 112647.222000 | 31756.190000 |
| 5733 | NaN | 110339.347000 | 25792.856000 |
| 5734 | NaN | 114100.749000 | 31386.202000 |
| 5738 | NaN | 78360.594000 | 10766.730000 |
| 5739 | NaN | 78937.813000 | 8735.836000 |
| 5740 | NaN | 78380.255000 | 13497.036000 |
| 9879 | NaN | 586852.139000 | 236276.100000 |
| 9880 | NaN | 459338.630000 | 215622.712000 |
| 9881 | NaN | 432576.455000 | 195801.413000 |
| 9892 | NaN | 279386.668000 | 38918.133000 |
| 9893 | NaN | 274498.610000 | 43498.273000 |
# Cek apakah benar working capital nilainya sama dengan total current assets dikurangi total current liabilities
wc = "Working Capital (Rp.M)"
tca = "Total Current Assets (Rp.M)"
tcl = "Total Current Liabilities (Rp.M)"
# hanya cek baris yang lengkap (tidak null)
mask = df_findat_final[[wc, tca, tcl]].notna().all(axis=1)
calc = df_findat_final[tca] - df_findat_final[tcl]
diff = (df_findat_final.loc[mask, wc] - calc.loc[mask]).abs()
print("Jumlah baris dicek:", int(mask.sum()))
print("Jumlah yang sama persis:", int((diff == 0).sum()))
print("Jumlah yang beda:", int((diff != 0).sum()))
print("Max abs diff:", float(diff.max()))
print("Median abs diff:", float(diff.median()))Jumlah baris dicek: 8468
Jumlah yang sama persis: 4134
Jumlah yang beda: 4334
Max abs diff: 0.0010000020265579224
Median abs diff: 1.8189894035458565e-12
# Hitung WC = CA - CL
wc_calculated = df_findat_final['Total Current Assets (Rp.M)'] - df_findat_final['Total Current Liabilities (Rp.M)']
# Tentukan baris 'Working Capital (Rp.M)' null
# DAN kedua kolom 'Total Current Assets (Rp.M)' & 'Total Current Liabilities (Rp.M)' tidak null
mask_to_fill = (
df_findat_final['Working Capital (Rp.M)'].isnull() &
df_findat_final['Total Current Assets (Rp.M)'].notnull() &
df_findat_final['Total Current Liabilities (Rp.M)'].notnull()
)
# Isi null
df_findat_final.loc[mask_to_fill, 'Working Capital (Rp.M)'] = wc_calculated[mask_to_fill]
print("Null values di 'Working Capital (Rp.M)' yang memungkinkan diisi berhasil diisi.")
print(f"Jumlah baris yang diimputasi dengan wc_calculated: {mask_to_fill.sum()}")
print(f"Jumlah null di 'Working Capital (Rp.M)' setelah proses: {df_findat_final['Working Capital (Rp.M)'].isnull().sum()}")Null values di 'Working Capital (Rp.M)' yang memungkinkan diisi berhasil diisi.
Jumlah baris yang diimputasi dengan wc_calculated: 21
Jumlah null di 'Working Capital (Rp.M)' setelah proses: 60
Total Debt = LTD + STB + CPLTD
df_findat_final[(df_findat_final['Total Debt (Rp.M)'].isna()) &
(df_findat_final['Short-term Borrowings (Rp.M)'].notna()) &
(df_findat_final['Current Portion of LT Debt & Leases (Rp.M)'].notna()) &
(df_findat_final['Long-term Debt (Rp.M)'].notna())
]
# Tidak ada yang bisa diisi dengan dihitung| variable | ticker | Year | Entity Name | 1st Level Primary Industry | Year Established | Date Established | Sector | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.M) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | IPO Year |
|---|
Total Capital = Total Equity + Total Debt
jml_bisa_diisi = len(df_findat_final[(df_findat_final['Total Capital (Rp.M)'].isna()) &
(df_findat_final['Total Equity (Rp.M)'].notna()) &
(df_findat_final['Total Debt (Rp.M)'].notna())
])
print(f"Jumlah baris yang bisa diisi: {jml_bisa_diisi}")Jumlah baris yang bisa diisi: 669
tc = "Total Capital (Rp.M)"
te = "Total Equity (Rp.M)"
td = "Total Debt (Rp.M)"
tc_calc = df_findat_final[te] + df_findat_final[td]
mask = df_findat_final[[tc, te, td]].notna().all(axis=1)
diff = df_findat_final.loc[mask, tc] - tc_calc.loc[mask]
abs_diff = diff.abs()
# error relatif (hindari pembagian 0)
den = df_findat_final.loc[mask, tc].abs().replace(0, np.nan)
rel_err = abs_diff / den
print("n dibandingkan:", int(mask.sum()))
print("match persis:", int((diff == 0).sum()), "| prop:", float((diff == 0).mean()))
print("median abs diff:", float(abs_diff.median()))
print("mean abs diff:", float(abs_diff.mean()))
print("median rel err:", float(rel_err.median()))n dibandingkan: 7799
match persis: 5571 | prop: 0.714322349019105
median abs diff: 0.0
mean abs diff: 0.00011411735155438838
median rel err: 0.0
# Isi Total Capital dengan rumus TE + TD
mask_fill = (
df_findat_final["Total Capital (Rp.M)"].isna() &
df_findat_final[["Total Equity (Rp.M)", "Total Debt (Rp.M)"]].notna().all(axis=1)
)
df_findat_final.loc[mask_fill, "Total Capital (Rp.M)"] = (
df_findat_final.loc[mask_fill, "Total Equity (Rp.M)"] +
df_findat_final.loc[mask_fill, "Total Debt (Rp.M)"]
)print('Sisa null Total Capital sekarang : ', df_findat_final['Total Capital (Rp.M)'].isna().sum())Sisa null Total Capital sekarang : 81
Year Established
print('Jumlah baris yang Year Established-nya null:', df_findat_final['Year Established'].isna().sum())Jumlah baris yang Year Established-nya null: 21
print('Cek baris dimana Year Established null tapi Date Established ada')
display(df_findat_final[(df_findat_final['Year Established'].isna()) &
(df_findat_final['Date Established'].notna())])
if (len(df_findat_final[(df_findat_final['Year Established'].isna()) &
(df_findat_final['Date Established'].notna())]) == 0):
print('Tidak ada Year Established kosong yang bisa diisi dengan Date Established')Cek baris dimana Year Established null tapi Date Established ada
| variable | ticker | Year | Entity Name | 1st Level Primary Industry | Year Established | Date Established | Sector | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.M) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | IPO Year |
|---|
Tidak ada Year Established kosong yang bisa diisi dengan Date Established
Year Established tidak bisa diisi dengan Date Established, maka drop Date Established
# Drop Date Established
df_findat_final = df_findat_final.drop(columns=['Date Established'])print('Perusahaan yang punya baris dengan Year Established null:')
perusahaan_dengan_baris_year_established_null = df_findat_final[df_findat_final['Year Established'].isna()]['Entity Name'].unique().tolist()
perusahaan_dengan_baris_year_established_nullPerusahaan yang punya baris dengan Year Established null:
['PT Ashmore Asset Management Indonesia Tbk (IDX:AMOR)',
'PT Bank Syariah Indonesia Tbk (IDX:BRIS)',
'PT Saratoga Investama Sedaya Tbk (IDX:SRTG)']
print('Baris Year Established null:')
df_findat_final[df_findat_final['Year Established'].isna()][['Entity Name','Year', 'Year Established']]Baris Year Established null:
| variable | Entity Name | Year | Year Established |
|---|---|---|---|
| 663 | PT Ashmore Asset Management Indonesia Tbk (IDX... | 2024 | NaN |
| 664 | PT Ashmore Asset Management Indonesia Tbk (IDX... | 2023 | NaN |
| 665 | PT Ashmore Asset Management Indonesia Tbk (IDX... | 2022 | NaN |
| 666 | PT Ashmore Asset Management Indonesia Tbk (IDX... | 2021 | NaN |
| 2805 | PT Bank Syariah Indonesia Tbk (IDX:BRIS) | 2024 | NaN |
| 2806 | PT Bank Syariah Indonesia Tbk (IDX:BRIS) | 2023 | NaN |
| 2807 | PT Bank Syariah Indonesia Tbk (IDX:BRIS) | 2022 | NaN |
| 2808 | PT Bank Syariah Indonesia Tbk (IDX:BRIS) | 2021 | NaN |
| 2809 | PT Bank Syariah Indonesia Tbk (IDX:BRIS) | 2020 | NaN |
| 2810 | PT Bank Syariah Indonesia Tbk (IDX:BRIS) | 2019 | NaN |
| 13515 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2024 | NaN |
| 13516 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2023 | NaN |
| 13517 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2022 | NaN |
| 13518 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2021 | NaN |
| 13519 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2020 | NaN |
| 13520 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2019 | NaN |
| 13521 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2018 | NaN |
| 13522 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2017 | NaN |
| 13523 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2016 | NaN |
| 13524 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2015 | NaN |
| 13525 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2014 | NaN |
print('Cek apakah bisa diisi dengan baris lain\n')
df_findat_final[df_findat_final['Entity Name'].isin(perusahaan_dengan_baris_year_established_null)][['Entity Name','Year', 'Year Established']]Cek apakah bisa diisi dengan baris lain
| variable | Entity Name | Year | Year Established |
|---|---|---|---|
| 663 | PT Ashmore Asset Management Indonesia Tbk (IDX... | 2024 | NaN |
| 664 | PT Ashmore Asset Management Indonesia Tbk (IDX... | 2023 | NaN |
| 665 | PT Ashmore Asset Management Indonesia Tbk (IDX... | 2022 | NaN |
| 666 | PT Ashmore Asset Management Indonesia Tbk (IDX... | 2021 | NaN |
| 2805 | PT Bank Syariah Indonesia Tbk (IDX:BRIS) | 2024 | NaN |
| 2806 | PT Bank Syariah Indonesia Tbk (IDX:BRIS) | 2023 | NaN |
| 2807 | PT Bank Syariah Indonesia Tbk (IDX:BRIS) | 2022 | NaN |
| 2808 | PT Bank Syariah Indonesia Tbk (IDX:BRIS) | 2021 | NaN |
| 2809 | PT Bank Syariah Indonesia Tbk (IDX:BRIS) | 2020 | NaN |
| 2810 | PT Bank Syariah Indonesia Tbk (IDX:BRIS) | 2019 | NaN |
| 13515 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2024 | NaN |
| 13516 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2023 | NaN |
| 13517 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2022 | NaN |
| 13518 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2021 | NaN |
| 13519 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2020 | NaN |
| 13520 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2019 | NaN |
| 13521 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2018 | NaN |
| 13522 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2017 | NaN |
| 13523 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2016 | NaN |
| 13524 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2015 | NaN |
| 13525 | PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2014 | NaN |
Cek Kolom Tidak Boleh Negatif (Biasanya)
columns_to_check_negative = ['Total Revenue (Rp.M)', 'Market Capitalization (Rp.M)', 'Total Assets (Rp.M)']
for col in columns_to_check_negative:
negative_count = (df_findat_final[col] < 0).sum()
print(f"Jumlah baris dengan nilai negatif pada kolom '{col}': {negative_count}")Jumlah baris dengan nilai negatif pada kolom 'Total Revenue (Rp.M)': 71
Jumlah baris dengan nilai negatif pada kolom 'Market Capitalization (Rp.M)': 0
Jumlah baris dengan nilai negatif pada kolom 'Total Assets (Rp.M)': 0
df_findat_final[df_findat_final['Total Revenue (Rp.M)'] < 0][['Entity Name']].value_counts()| count | |
|---|---|
| Entity Name | |
| PT Lenox Pasifik Investama Tbk (IDX:LPPS) | 7 |
| PT Intan Baru Prana Tbk (IDX:IBFN) | 6 |
| PT Bank KB Indonesia Tbk (IDX:BBKP) | 5 |
| PT Minna Padi Investama Sekuritas Tbk (IDX:PADI) | 5 |
| PT Panca Global Kapital Tbk (IDX:PEGE) | 4 |
| PT Pool Advista Finance Tbk (IDX:POLA) | 4 |
| PT Capitalinc Investment Tbk (IDX:MTFN) | 3 |
| PT Charnic Capital Tbk (IDX:NICK) | 3 |
| PT. Bank Pembangunan Daerah Banten, Tbk (IDX:BEKS) | 3 |
| PT Bank JTrust Indonesia Tbk (IDX:BCIC) | 3 |
| PT Bank QNB Indonesia Tbk (IDX:BKSW) | 3 |
| PT Provident Investasi Bersama Tbk (IDX:PALM) | 2 |
| PT Danasupra Erapacific Tbk (IDX:DEFI) | 2 |
| PT Bank Panin Dubai Syariah Tbk (IDX:PNBS) | 2 |
| PT Saratoga Investama Sedaya Tbk (IDX:SRTG) | 2 |
| PT Radana Bhaskara Finance Tbk (IDX:HDFA) | 2 |
| PT Indonesia Prima Property Tbk (IDX:OMRE) | 2 |
| PT Bank Permata Tbk (IDX:BNLI) | 1 |
| PT Bank IBK Indonesia Tbk (IDX:AGRS) | 1 |
| PT Allo Bank Indonesia Tbk (IDX:BBHI) | 1 |
| PT Bank Jago Tbk (IDX:ARTO) | 1 |
| PT Bank MNC Internasional Tbk (IDX:BABP) | 1 |
| PT Bank of India Indonesia Tbk (IDX:BSWD) | 1 |
| PT Bank Raya Indonesia Tbk (IDX:AGRO) | 1 |
| PT MNC Tourism Indonesia Tbk (IDX:KPIG) | 1 |
| PT Himalaya Energi Perkasa Tbk (IDX:HADE) | 1 |
| PT Mineral Sumberdaya Mandiri Tbk (IDX:AKSI) | 1 |
| PT Magna Investama Mandiri Tbk (IDX:MGNA) | 1 |
| PT Mizuho Leasing Indonesia Tbk (IDX:VRNA) | 1 |
| PT Yulie Sekuritas Indonesia Tbk (IDX:YULE) | 1 |
cols = ["ticker","Year","Sector", "Industry Group",
"Total Revenue (Rp.M)", "Operating Income (Rp.M)",
"EBITDA (Rp.M)", "Net Income to Company (Rp.M)"]
df_findat_final.loc[df_findat_final["Total Revenue (Rp.M)"] < 0, cols] \
.sort_values(["ticker","Year"])| variable | ticker | Year | Sector | Industry Group | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | Net Income to Company (Rp.M) |
|---|---|---|---|---|---|---|---|---|
| 309 | AGRO | 2021 | Financials | Banks | -2900117.934000 | -3302855.412000 | NaN | -3045701.407000 |
| 328 | AGRS | 2019 | Financials | Banks | -46978.000000 | -270402.000000 | NaN | -248836.000000 |
| 455 | AKSI | 2011 | Industrials | Transportation | -29020.539000 | -38088.940000 | -37466.047000 | -36437.585000 |
| 991 | ARTO | 2019 | Financials | Banks | -48665.000000 | -91150.000000 | NaN | -121966.000000 |
| 1401 | BABP | 2017 | Financials | Banks | -349818.000000 | -886730.000000 | NaN | -685193.000000 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 11424 | POLA | 2024 | Financials | Financial Services | -5408.036000 | -29851.381000 | NaN | -28745.813000 |
| 13521 | SRTG | 2018 | Financials | Financial Services | -6089443.000000 | -6313642.000000 | -6312597.000000 | -6134832.000000 |
| 13516 | SRTG | 2023 | Financials | Financial Services | -10991269.000000 | -11211922.000000 | -11210658.000000 | -10151341.000000 |
| 15051 | VRNA | 2018 | Financials | Financial Services | -125436.117000 | -202662.629000 | NaN | -192758.906000 |
| 15593 | YULE | 2020 | Financials | Financial Services | -1657.122000 | -10844.380000 | NaN | -7225.945000 |
71 rows × 8 columns
Mayoritas perusahaan finansial dan Net Income memang negatif, sehingga sesuai dan tetap dipertahankan.
Cek Zero Values
zero_values_summary = {}
for col in df_findat_final.select_dtypes(include=np.number).columns:
zero_count = (df_findat_final[col] == 0).sum()
if zero_count > 0:
percentage = (zero_count / len(df_findat_final)) * 100
zero_values_summary[col] = {
"count": zero_count,
"percentage": f"{percentage:.2f}%"
}
if zero_values_summary:
print("Columns in df_findat_final with zero values:")
for col_name, data in zero_values_summary.items():
print(f"- {col_name}: {data['count']} zeros ({data['percentage']})")
else:
print("No numeric columns in df_findat_final contain zero values.")Columns in df_findat_final with zero values:
- Percent Owned - All Institutions (%): 201 zeros (2.35%)
- Percent Owned - Insiders (%): 630 zeros (7.37%)
- Current Ratio (x): 2 zeros (0.02%)
- Quick Ratio (x): 2 zeros (0.02%)
- Inventory (Rp.M): 13 zeros (0.15%)
- Total Debt (Rp.M): 804 zeros (9.40%)
- Net Property, Plant & Equipment (Rp.M): 7 zeros (0.08%)
Cek Kolom Seharusnya Tidak Kosong
cols_to_impute = [
"Net Property, Plant & Equipment (Rp.M)", "Total Revenue (Rp.M)",
"Cash from Ops. (Rp.M)", "Net Change in Cash (Rp.M)", "Total Assets (Rp.M)"
]
for col in cols_to_impute:
print(f"Jumlah Null pada {col}: {df_findat_final[col].isnull().sum()}")Jumlah Null pada Net Property, Plant & Equipment (Rp.M): 79
Jumlah Null pada Total Revenue (Rp.M): 91
Jumlah Null pada Cash from Ops. (Rp.M): 62
Jumlah Null pada Net Change in Cash (Rp.M): 62
Jumlah Null pada Total Assets (Rp.M): 60
Jika perlu imputasi, imputasi dilakukan di tahap setelah feature engineering agar dataset yang mempertahankan null bisa dibuat fitur-fitur raisonya terlebih dahulu.
Cek Null
Jumlah Null Tiap Kolom
null_counts = df_findat_final.isnull().sum()
total_rows = len(df_findat_final)
null_percentages = (null_counts / total_rows) * 100
null_summary_df = pd.DataFrame({
'Null Count': null_counts,
'Null Percentage': null_percentages
})
null_summary_df = null_summary_df.sort_values(by='Null Count', ascending=False)
print("Summary of Null Values in df_findat_final:")
print(null_summary_df.to_string())Summary of Null Values in df_findat_final:
Null Count Null Percentage
variable
Current Portion of LT Debt & Leases (Rp.M) 8212 96.058018
Net Intangibles (Rp.M) 6786 79.377705
Percent Owned - All Institutions (%) 3983 46.590244
Parent Percent Owned (%) 3681 43.057668
Short-term Borrowings (Rp.M) 2970 34.740905
Long-term Debt (Rp.M) 2641 30.892502
Percent Owned - Insiders (%) 2578 30.155574
Prepaid Exp. (Rp.M) 1522 17.803252
Inventory (Rp.M) 1266 14.808750
EBITDA (Rp.M) 1166 13.639022
EBIT (Rp.M) 1159 13.557141
Current Ratio (x) 766 8.960112
Quick Ratio (x) 764 8.936718
Cost Of Goods Sold (Rp.M) 240 2.807346
Total Revenue (Rp.M) 91 1.064452
Total Capital (Rp.M) 81 0.947479
Total Debt (Rp.M) 81 0.947479
Net Property, Plant & Equipment (Rp.M) 79 0.924085
Net Change in Cash (Rp.M) 62 0.725231
Cash from Ops. (Rp.M) 62 0.725231
Operating Income (Rp.M) 61 0.713534
Working Capital (Rp.M) 60 0.701836
Total Current Assets (Rp.M) 60 0.701836
Total Assets (Rp.M) 60 0.701836
Net Income to Company (Rp.M) 60 0.701836
Total Equity (Rp.M) 60 0.701836
Cash & Short-term Investments (Rp.M) 60 0.701836
Total Current Liabilities (Rp.M) 60 0.701836
Total Liabilities (Rp.M) 58 0.678442
Market Capitalization (Rp.M) 52 0.608258
Year Close Stock Price (Rp.) 52 0.608258
ECS Total Common Shares Outstanding (actual) 28 0.327524
Year Established 21 0.245643
Sector 0 0.000000
Industry Group 0 0.000000
1st Level Primary Industry 0 0.000000
Year 0 0.000000
ticker 0 0.000000
Entity Name 0 0.000000
IPO Year 0 0.000000
Jumlah Baris untuk tiap Jumlah Null
# hitung frekuensi jumlah null per baris
print('Jumlah baris untuk tiap jumlah null:')
hasil_null_per_baris = df_findat_final.isnull().sum(axis=1).value_counts().sort_index()
print(hasil_null_per_baris)Jumlah baris untuk tiap jumlah null:
1 236
2 1057
3 1766
4 1958
5 1483
6 877
7 468
8 294
9 210
10 95
11 26
12 18
13 1
26 4
27 8
28 16
29 9
30 8
31 7
32 8
Name: count, dtype: int64
print('Jumlah baris dengan null >25 :')
df_findat_final[df_findat_final.isnull().sum(axis=1) > 25]Jumlah baris dengan null >25 :
| variable | ticker | Year | Entity Name | 1st Level Primary Industry | Year Established | Sector | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.M) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | IPO Year |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 527 | ALTO | 2024 | PT Tri Banyan Tirta Tbk (IDX:ALTO) | Consumer | 1997.000000 | Consumer Staples | Food, Beverage and Tobacco | 51.090000 | 16.440000 | 2.240000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2191870558.000000 | 35069.928928 | 16.000000 | NaN | NaN | NaN | 2012 |
| 1904 | BEBS | 2024 | PT Berkah Beton Sadaya Tbk (IDX:BEBS) | Materials | 2019.000000 | Materials | Materials | NaN | 5.810000 | 4.510000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 45000000000.000000 | 225000.000000 | 5.000000 | NaN | NaN | NaN | 2021 |
| 2108 | BIKA | 2024 | PT Binakarya Jaya Abadi Tbk (IDX:BIKA) | Real Estate | 2007.000000 | Real Estate | Real Estate Management and Development | NaN | NaN | 72.190000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 592280000.000000 | 32575.400000 | 55.000000 | NaN | NaN | NaN | 2015 |
| 2703 | BOSS | 2024 | PT. Borneo Olah Sarana Sukses Tbk (IDX:BOSS) | Energy and Utilities | 2011.000000 | Energy | Energy | NaN | NaN | 0.020000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1400000000.000000 | 70000.000000 | 50.000000 | NaN | NaN | NaN | 2018 |
| 2704 | BOSS | 2023 | PT. Borneo Olah Sarana Sukses Tbk (IDX:BOSS) | Energy and Utilities | 2011.000000 | Energy | Energy | NaN | NaN | 0.020000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1400000000.000000 | 70000.000000 | 50.000000 | NaN | NaN | NaN | 2018 |
| 3002 | BTEK | 2014 | PT Bumi Teknokultura Unggul Tbk (IDX:BTEK) | Consumer | 2001.000000 | Consumer Staples | Food, Beverage and Tobacco | NaN | 14.650000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2004 |
| 3003 | BTEK | 2013 | PT Bumi Teknokultura Unggul Tbk (IDX:BTEK) | Consumer | 2001.000000 | Consumer Staples | Food, Beverage and Tobacco | NaN | 14.650000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2004 |
| 3004 | BTEK | 2012 | PT Bumi Teknokultura Unggul Tbk (IDX:BTEK) | Consumer | 2001.000000 | Consumer Staples | Food, Beverage and Tobacco | NaN | 14.650000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2004 |
| 3005 | BTEK | 2011 | PT Bumi Teknokultura Unggul Tbk (IDX:BTEK) | Consumer | 2001.000000 | Consumer Staples | Food, Beverage and Tobacco | NaN | 14.650000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2004 |
| 3006 | BTEK | 2010 | PT Bumi Teknokultura Unggul Tbk (IDX:BTEK) | Consumer | 2001.000000 | Consumer Staples | Food, Beverage and Tobacco | NaN | 14.650000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2004 |
| 3007 | BTEK | 2009 | PT Bumi Teknokultura Unggul Tbk (IDX:BTEK) | Consumer | 2001.000000 | Consumer Staples | Food, Beverage and Tobacco | NaN | 14.650000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2004 |
| 3008 | BTEK | 2008 | PT Bumi Teknokultura Unggul Tbk (IDX:BTEK) | Consumer | 2001.000000 | Consumer Staples | Food, Beverage and Tobacco | NaN | 14.650000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2004 |
| 3208 | BWPT | 2012 | PT Eagle High Plantations Tbk (IDX:BWPT) | Consumer | 2000.000000 | Consumer Staples | Food, Beverage and Tobacco | NaN | 0.060000 | 0.040000 | 4789012.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2009 |
| 3209 | BWPT | 2011 | PT Eagle High Plantations Tbk (IDX:BWPT) | Consumer | 2000.000000 | Consumer Staples | Food, Beverage and Tobacco | NaN | 0.060000 | 0.040000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2009 |
| 3210 | BWPT | 2010 | PT Eagle High Plantations Tbk (IDX:BWPT) | Consumer | 2000.000000 | Consumer Staples | Food, Beverage and Tobacco | NaN | 0.060000 | 0.040000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2009 |
| 3732 | CMPP | 2015 | PT AirAsia Indonesia Tbk (IDX:CMPP) | Industrials | 1989.000000 | Industrials | Transportation | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1994 |
| 3733 | CMPP | 2014 | PT AirAsia Indonesia Tbk (IDX:CMPP) | Industrials | 1989.000000 | Industrials | Transportation | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1994 |
| 3734 | CMPP | 2013 | PT AirAsia Indonesia Tbk (IDX:CMPP) | Industrials | 1989.000000 | Industrials | Transportation | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1994 |
| 3735 | CMPP | 2012 | PT AirAsia Indonesia Tbk (IDX:CMPP) | Industrials | 1989.000000 | Industrials | Transportation | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1994 |
| 3736 | CMPP | 2011 | PT AirAsia Indonesia Tbk (IDX:CMPP) | Industrials | 1989.000000 | Industrials | Transportation | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1994 |
| 3737 | CMPP | 2010 | PT AirAsia Indonesia Tbk (IDX:CMPP) | Industrials | 1989.000000 | Industrials | Transportation | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1994 |
| 3738 | CMPP | 2009 | PT AirAsia Indonesia Tbk (IDX:CMPP) | Industrials | 1989.000000 | Industrials | Transportation | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1994 |
| 3739 | CMPP | 2008 | PT AirAsia Indonesia Tbk (IDX:CMPP) | Industrials | 1989.000000 | Industrials | Transportation | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1994 |
| 3859 | CPRI | 2024 | PT Capri Nusa Satu Properti Tbk (IDX:CPRI) | Real Estate | 2011.000000 | Real Estate | Real Estate Management and Development | NaN | NaN | 0.720000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2433379958.000000 | 121668.997900 | 50.000000 | NaN | NaN | NaN | 2019 |
| 3860 | CPRI | 2023 | PT Capri Nusa Satu Properti Tbk (IDX:CPRI) | Real Estate | 2011.000000 | Real Estate | Real Estate Management and Development | NaN | NaN | 0.720000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2433379958.000000 | 121668.997900 | 50.000000 | NaN | NaN | NaN | 2019 |
| 3861 | CPRI | 2022 | PT Capri Nusa Satu Properti Tbk (IDX:CPRI) | Real Estate | 2011.000000 | Real Estate | Real Estate Management and Development | NaN | NaN | 0.720000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2433379958.000000 | 121668.997900 | 50.000000 | NaN | NaN | NaN | 2019 |
| 4182 | DEAL | 2024 | PT Dewata Freightinternational Tbk (IDX:DEAL) | Industrials | 1995.000000 | Industrials | Transportation | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1146170959.000000 | 6877.025754 | 6.000000 | NaN | NaN | NaN | 2018 |
| 4183 | DEAL | 2023 | PT Dewata Freightinternational Tbk (IDX:DEAL) | Industrials | 1995.000000 | Industrials | Transportation | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1146170959.000000 | 10315.538631 | 9.000000 | NaN | NaN | NaN | 2018 |
| 5134 | ETWA | 2024 | PT Eterindo Wahanatama Tbk (IDX:ETWA) | Energy and Utilities | 1992.000000 | Energy | Energy | 79.260000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 4668671400.000000 | 326806.998000 | 70.000000 | NaN | NaN | NaN | 1997 |
| 5135 | ETWA | 2023 | PT Eterindo Wahanatama Tbk (IDX:ETWA) | Energy and Utilities | 1992.000000 | Energy | Energy | 79.260000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 4668671400.000001 | 452861.125800 | 97.000000 | NaN | NaN | NaN | 1997 |
| 5491 | GAMA | 2024 | PT Aksara Global Development Tbk (IDX:GAMA) | Real Estate | 2003.000000 | Real Estate | Real Estate Management and Development | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 10011027656.000000 | 180198.497808 | 18.000000 | NaN | NaN | NaN | 2012 |
| 5492 | GAMA | 2023 | PT Aksara Global Development Tbk (IDX:GAMA) | Real Estate | 2003.000000 | Real Estate | Real Estate Management and Development | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 10011027656.000000 | 180198.497808 | 18.000000 | NaN | NaN | NaN | 2012 |
| 5493 | GAMA | 2022 | PT Aksara Global Development Tbk (IDX:GAMA) | Real Estate | 2003.000000 | Real Estate | Real Estate Management and Development | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 10011027656.000000 | 500551.382800 | 50.000000 | NaN | NaN | NaN | 2012 |
| 5570 | GEMS | 2013 | PT Golden Energy Mines Tbk (IDX:GEMS) | Energy and Utilities | 1997.000000 | Energy | Energy | 51.000000 | 0.020000 | 0.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2011 |
| 5571 | GEMS | 2012 | PT Golden Energy Mines Tbk (IDX:GEMS) | Energy and Utilities | 1997.000000 | Energy | Energy | 51.000000 | 0.020000 | 0.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2011 |
| 6273 | HKMU | 2024 | PT HK Metals Utama Tbk (IDX:HKMU) | Industrials | 1995.000000 | Industrials | Capital Goods | NaN | NaN | 0.100000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 3221750000.000000 | 161087.500000 | 50.000000 | NaN | NaN | NaN | 2018 |
| 6274 | HKMU | 2023 | PT HK Metals Utama Tbk (IDX:HKMU) | Industrials | 1995.000000 | Industrials | Capital Goods | NaN | NaN | 0.100000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 3221750000.000000 | 161087.500000 | 50.000000 | NaN | NaN | NaN | 2018 |
| 6544 | ICON | 2008 | PT Island Concepts Indonesia Tbk (IDX:ICON) | Industrials | 2001.000000 | Industrials | Commercial and Professional Services | NaN | 5.020000 | 36.140000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2005 |
| 7191 | IPPE | 2024 | PT Indo Pureco Pratama Tbk (IDX:IPPE) | Consumer | 2019.000000 | Consumer Staples | Food, Beverage and Tobacco | NaN | NaN | 5.320000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 4600000000.000001 | 64400.000000 | 14.000000 | NaN | NaN | NaN | 2021 |
| 7684 | KAYU | 2024 | PT Darmi Bersaudara Tbk (IDX:KAYU) | Materials | 1998.000000 | Materials | Materials | 54.210000 | NaN | 0.050000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 665000000.000000 | 11970.000000 | 18.000000 | NaN | NaN | NaN | 2019 |
| 9571 | MKNT | 2024 | PT Mitra Komunikasi Nusantara Tbk (IDX:MKNT) | Technology, Media & Telecommunications | 2008.000000 | Information Technology | Technology Hardware and Equipment | NaN | 5.010000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 5500000000.000000 | 5500.000000 | 1.000000 | NaN | NaN | NaN | 2015 |
| 9572 | MKNT | 2023 | PT Mitra Komunikasi Nusantara Tbk (IDX:MKNT) | Technology, Media & Telecommunications | 2008.000000 | Information Technology | Technology Hardware and Equipment | NaN | 5.010000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 5500000000.000000 | 11000.000000 | 2.000000 | NaN | NaN | NaN | 2015 |
| 10149 | MYOH | 2024 | PT Samindo Resources Tbk (IDX:MYOH) | Energy and Utilities | 2000.000000 | Energy | Energy | 59.030000 | 10.570000 | 14.180000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2206312500.000000 | 3508036.875000 | 1590.000000 | NaN | NaN | NaN | 2000 |
| 11288 | PMMP | 2024 | PT Panca Mitra Multiperdana Tbk (IDX:PMMP) | Consumer | 1997.000000 | Consumer Staples | Food, Beverage and Tobacco | 51.000000 | NaN | 62.450000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2588300000.000000 | 194122.500000 | 75.000000 | NaN | NaN | NaN | 2020 |
| 11458 | POLL | 2024 | PT Pollux Properties Indonesia Tbk (IDX:POLL) | Real Estate | 2014.000000 | Real Estate | Real Estate Management and Development | 84.980000 | 6.010000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 8318823600.000000 | 931708.243200 | 112.000000 | NaN | NaN | NaN | 2018 |
| 12597 | SBAT | 2024 | PT Sejahtera Bintang Abadi Textile Tbk (IDX:SBAT) | Consumer | 2003.000000 | Consumer Discretionary | Consumer Durables and Apparel | NaN | NaN | 34.480000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 4752982978.000000 | 4752.982978 | 1.000000 | NaN | NaN | NaN | 2020 |
| 12598 | SBAT | 2023 | PT Sejahtera Bintang Abadi Textile Tbk (IDX:SBAT) | Consumer | 2003.000000 | Consumer Discretionary | Consumer Durables and Apparel | NaN | NaN | 34.480000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 4752982978.000000 | 42776.846802 | 9.000000 | NaN | NaN | NaN | 2020 |
| 12728 | SDRA | 2012 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | Financials | 1906.000000 | Financials | Banks | 90.750000 | NaN | 0.020000 | 4573918.000000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2006 |
| 12729 | SDRA | 2011 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | Financials | 1906.000000 | Financials | Banks | 90.750000 | NaN | 0.020000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2006 |
| 12730 | SDRA | 2010 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | Financials | 1906.000000 | Financials | Banks | 90.750000 | NaN | 0.020000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2006 |
| 12731 | SDRA | 2009 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | Financials | 1906.000000 | Financials | Banks | 90.750000 | NaN | 0.020000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2006 |
| 12732 | SDRA | 2008 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | Financials | 1906.000000 | Financials | Banks | 90.750000 | NaN | 0.020000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2006 |
| 13474 | SQMI | 2014 | PT Wilton Makmur indonesia Tbk. (IDX:SQMI) | Energy and Utilities | 2000.000000 | Energy | Energy | 75.620000 | 41.730000 | 20.320000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 301200000.000000 | 448788.000000 | 1490.000000 | NaN | NaN | NaN | 2004 |
| 13736 | SWAT | 2024 | PT Sriwahana Adityakarta Tbk (IDX:SWAT) | Materials | 1990.000000 | Materials | Materials | 77.220000 | NaN | 11.800000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 3019200000.000000 | 75480.000000 | 25.000000 | NaN | NaN | NaN | 2018 |
| 13991 | TECH | 2024 | PT Indosterling Technomedia TBK (IDX:TECH) | Technology, Media & Telecommunications | 2011.000000 | Information Technology | Software and Services | 79.950000 | 0.050000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1256300000.000000 | 62815.000000 | 50.000000 | NaN | NaN | NaN | 2020 |
| 13992 | TECH | 2023 | PT Indosterling Technomedia TBK (IDX:TECH) | Technology, Media & Telecommunications | 2011.000000 | Information Technology | Software and Services | 79.950000 | 0.050000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1256300000.000000 | 62815.000000 | 50.000000 | NaN | NaN | NaN | 2020 |
| 14314 | TOPS | 2024 | PT Totalindo Eka Persada Tbk (IDX:TOPS) | Industrials | 1996.000000 | Industrials | Capital Goods | 58.750000 | NaN | 5.800000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 33330000000.000000 | 33330.000000 | 1.000000 | NaN | NaN | NaN | 2017 |
| 14399 | TOYS | 2024 | PT Sunindo Adipersada Tbk (IDX:TOYS) | Consumer | 1991.000000 | Consumer Discretionary | Consumer Durables and Apparel | NaN | NaN | 39.900000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1435000712.000000 | 11480.005696 | 8.000000 | NaN | NaN | NaN | 2020 |
| 14431 | TPIA | 2009 | PT Chandra Asri Pacific Tbk (IDX:TPIA) | Materials | 1984.000000 | Materials | Materials | NaN | 0.600000 | 5.200000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2008 |
| 15640 | ZBRA | 2024 | PT Dosni Roha Indonesia Tbk (IDX:ZBRA) | Health Care | 1987.000000 | Health Care | Health Care Equipment and Services | 63.210000 | 30.770000 | 2.270000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 2510706263.000000 | 135578.138202 | 54.000000 | NaN | NaN | NaN | 1991 |
Null pada Kolom Penentu Financial Distress
Selain status masuknya perusahaan ke Papan Pemantauan Khusus, financial distress juga ditentukan dengan Total Ekuitas dan Laba Bersih. Cek apakah ada baris yang memiliki null pada kedua kolom tadi.
print('Baris dengan Net Income to Company atau Total Equity null :\n')
df_findat_final[(df_findat_final['Net Income to Company (Rp.M)'].isna()) | (df_findat_final['Total Equity (Rp.M)'].isna())][['Entity Name','Year', 'Net Income to Company (Rp.M)', 'Total Equity (Rp.M)']]Baris dengan Net Income to Company atau Total Equity null :
| variable | Entity Name | Year | Net Income to Company (Rp.M) | Total Equity (Rp.M) |
|---|---|---|---|---|
| 527 | PT Tri Banyan Tirta Tbk (IDX:ALTO) | 2024 | NaN | NaN |
| 1904 | PT Berkah Beton Sadaya Tbk (IDX:BEBS) | 2024 | NaN | NaN |
| 2108 | PT Binakarya Jaya Abadi Tbk (IDX:BIKA) | 2024 | NaN | NaN |
| 2703 | PT. Borneo Olah Sarana Sukses Tbk (IDX:BOSS) | 2024 | NaN | NaN |
| 2704 | PT. Borneo Olah Sarana Sukses Tbk (IDX:BOSS) | 2023 | NaN | NaN |
| 3002 | PT Bumi Teknokultura Unggul Tbk (IDX:BTEK) | 2014 | NaN | NaN |
| 3003 | PT Bumi Teknokultura Unggul Tbk (IDX:BTEK) | 2013 | NaN | NaN |
| 3004 | PT Bumi Teknokultura Unggul Tbk (IDX:BTEK) | 2012 | NaN | NaN |
| 3005 | PT Bumi Teknokultura Unggul Tbk (IDX:BTEK) | 2011 | NaN | NaN |
| 3006 | PT Bumi Teknokultura Unggul Tbk (IDX:BTEK) | 2010 | NaN | NaN |
| 3007 | PT Bumi Teknokultura Unggul Tbk (IDX:BTEK) | 2009 | NaN | NaN |
| 3008 | PT Bumi Teknokultura Unggul Tbk (IDX:BTEK) | 2008 | NaN | NaN |
| 3208 | PT Eagle High Plantations Tbk (IDX:BWPT) | 2012 | NaN | NaN |
| 3209 | PT Eagle High Plantations Tbk (IDX:BWPT) | 2011 | NaN | NaN |
| 3210 | PT Eagle High Plantations Tbk (IDX:BWPT) | 2010 | NaN | NaN |
| 3732 | PT AirAsia Indonesia Tbk (IDX:CMPP) | 2015 | NaN | NaN |
| 3733 | PT AirAsia Indonesia Tbk (IDX:CMPP) | 2014 | NaN | NaN |
| 3734 | PT AirAsia Indonesia Tbk (IDX:CMPP) | 2013 | NaN | NaN |
| 3735 | PT AirAsia Indonesia Tbk (IDX:CMPP) | 2012 | NaN | NaN |
| 3736 | PT AirAsia Indonesia Tbk (IDX:CMPP) | 2011 | NaN | NaN |
| 3737 | PT AirAsia Indonesia Tbk (IDX:CMPP) | 2010 | NaN | NaN |
| 3738 | PT AirAsia Indonesia Tbk (IDX:CMPP) | 2009 | NaN | NaN |
| 3739 | PT AirAsia Indonesia Tbk (IDX:CMPP) | 2008 | NaN | NaN |
| 3859 | PT Capri Nusa Satu Properti Tbk (IDX:CPRI) | 2024 | NaN | NaN |
| 3860 | PT Capri Nusa Satu Properti Tbk (IDX:CPRI) | 2023 | NaN | NaN |
| 3861 | PT Capri Nusa Satu Properti Tbk (IDX:CPRI) | 2022 | NaN | NaN |
| 4182 | PT Dewata Freightinternational Tbk (IDX:DEAL) | 2024 | NaN | NaN |
| 4183 | PT Dewata Freightinternational Tbk (IDX:DEAL) | 2023 | NaN | NaN |
| 5134 | PT Eterindo Wahanatama Tbk (IDX:ETWA) | 2024 | NaN | NaN |
| 5135 | PT Eterindo Wahanatama Tbk (IDX:ETWA) | 2023 | NaN | NaN |
| 5491 | PT Aksara Global Development Tbk (IDX:GAMA) | 2024 | NaN | NaN |
| 5492 | PT Aksara Global Development Tbk (IDX:GAMA) | 2023 | NaN | NaN |
| 5493 | PT Aksara Global Development Tbk (IDX:GAMA) | 2022 | NaN | NaN |
| 5570 | PT Golden Energy Mines Tbk (IDX:GEMS) | 2013 | NaN | NaN |
| 5571 | PT Golden Energy Mines Tbk (IDX:GEMS) | 2012 | NaN | NaN |
| 6273 | PT HK Metals Utama Tbk (IDX:HKMU) | 2024 | NaN | NaN |
| 6274 | PT HK Metals Utama Tbk (IDX:HKMU) | 2023 | NaN | NaN |
| 6544 | PT Island Concepts Indonesia Tbk (IDX:ICON) | 2008 | NaN | NaN |
| 7191 | PT Indo Pureco Pratama Tbk (IDX:IPPE) | 2024 | NaN | NaN |
| 7684 | PT Darmi Bersaudara Tbk (IDX:KAYU) | 2024 | NaN | NaN |
| 9571 | PT Mitra Komunikasi Nusantara Tbk (IDX:MKNT) | 2024 | NaN | NaN |
| 9572 | PT Mitra Komunikasi Nusantara Tbk (IDX:MKNT) | 2023 | NaN | NaN |
| 10149 | PT Samindo Resources Tbk (IDX:MYOH) | 2024 | NaN | NaN |
| 11288 | PT Panca Mitra Multiperdana Tbk (IDX:PMMP) | 2024 | NaN | NaN |
| 11458 | PT Pollux Properties Indonesia Tbk (IDX:POLL) | 2024 | NaN | NaN |
| 12597 | PT Sejahtera Bintang Abadi Textile Tbk (IDX:SBAT) | 2024 | NaN | NaN |
| 12598 | PT Sejahtera Bintang Abadi Textile Tbk (IDX:SBAT) | 2023 | NaN | NaN |
| 12728 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2012 | NaN | NaN |
| 12729 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2011 | NaN | NaN |
| 12730 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2010 | NaN | NaN |
| 12731 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2009 | NaN | NaN |
| 12732 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2008 | NaN | NaN |
| 13474 | PT Wilton Makmur indonesia Tbk. (IDX:SQMI) | 2014 | NaN | NaN |
| 13736 | PT Sriwahana Adityakarta Tbk (IDX:SWAT) | 2024 | NaN | NaN |
| 13991 | PT Indosterling Technomedia TBK (IDX:TECH) | 2024 | NaN | NaN |
| 13992 | PT Indosterling Technomedia TBK (IDX:TECH) | 2023 | NaN | NaN |
| 14314 | PT Totalindo Eka Persada Tbk (IDX:TOPS) | 2024 | NaN | NaN |
| 14399 | PT Sunindo Adipersada Tbk (IDX:TOYS) | 2024 | NaN | NaN |
| 14431 | PT Chandra Asri Pacific Tbk (IDX:TPIA) | 2009 | NaN | NaN |
| 15640 | PT Dosni Roha Indonesia Tbk (IDX:ZBRA) | 2024 | NaN | NaN |
print('Keseluruhan data dari perusahaan tersebut :')
perusahaan_yang_laba_atau_ekuitas_null = df_findat_final[(df_findat_final['Net Income to Company (Rp.M)'].isna()) | (df_findat_final['Total Equity (Rp.M)'].isna())]['Entity Name'].unique().tolist()
df_findat_final[df_findat_final['Entity Name'].isin(perusahaan_yang_laba_atau_ekuitas_null)][['Entity Name','Year', 'Net Income to Company (Rp.M)', 'Total Equity (Rp.M)']]Keseluruhan data dari perusahaan tersebut :
| variable | Entity Name | Year | Net Income to Company (Rp.M) | Total Equity (Rp.M) |
|---|---|---|---|---|
| 527 | PT Tri Banyan Tirta Tbk (IDX:ALTO) | 2024 | NaN | NaN |
| 528 | PT Tri Banyan Tirta Tbk (IDX:ALTO) | 2023 | -25917.766000 | 323765.890000 |
| 529 | PT Tri Banyan Tirta Tbk (IDX:ALTO) | 2022 | -16129.027000 | 348916.160000 |
| 530 | PT Tri Banyan Tirta Tbk (IDX:ALTO) | 2021 | -8932.198000 | 363835.661000 |
| 531 | PT Tri Banyan Tirta Tbk (IDX:ALTO) | 2020 | -10506.939000 | 372883.080000 |
| ... | ... | ... | ... | ... |
| 15652 | PT Dosni Roha Indonesia Tbk (IDX:ZBRA) | 2012 | -8699.679000 | 9807.578000 |
| 15653 | PT Dosni Roha Indonesia Tbk (IDX:ZBRA) | 2011 | -9334.155000 | 18507.257000 |
| 15654 | PT Dosni Roha Indonesia Tbk (IDX:ZBRA) | 2010 | -9424.290000 | 27841.411000 |
| 15655 | PT Dosni Roha Indonesia Tbk (IDX:ZBRA) | 2009 | -7642.007000 | 37729.599000 |
| 15656 | PT Dosni Roha Indonesia Tbk (IDX:ZBRA) | 2008 | -7037.408000 | 45371.606000 |
291 rows × 4 columns
Untuk konsistensi data dan pelabelan target prediksi, baris yang null pada Total Ekuitas ATAU Net Income perlu dihapus.
df_findat_final = df_findat_final.dropna(subset=['Net Income to Company (Rp.M)', 'Total Equity (Rp.M)'], how='any')Null Total Asset, Ekuitas, atau Liabilitas
df_findat_final[
(df_findat_final['Total Assets (Rp.M)'].isna()) |
(df_findat_final['Total Equity (Rp.M)'].isna()) |
(df_findat_final['Total Liabilities (Rp.M)'].isna())]| variable | ticker | Year | Entity Name | 1st Level Primary Industry | Year Established | Sector | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.M) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | IPO Year |
|---|
Null Total Lembar Saham
print('Baris data yang total lembar sahamnya null:')
df_findat_final[df_findat_final['ECS Total Common Shares Outstanding (actual)'].isna()][['Entity Name','Year', 'ECS Total Common Shares Outstanding (actual)']]Baris data yang total lembar sahamnya null:
| variable | Entity Name | Year | ECS Total Common Shares Outstanding (actual) |
|---|---|---|---|
| 12727 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2013 | NaN |
print("Perusahaan yang memiliki baris total lembar saham null:")
tickers_with_null_total_shares = df_findat_final[df_findat_final['ECS Total Common Shares Outstanding (actual)'].isna()]['Entity Name'].unique().tolist()
tickers_with_null_total_sharesPerusahaan yang memiliki baris total lembar saham null:
['PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:SDRA)']
print('Cek apakah perusahaan tsb ada baris lain yang tidak null pada total lembar sahamnya :')
df_findat_final[df_findat_final['Entity Name'].isin(tickers_with_null_total_shares)][['Entity Name','Year', 'ECS Total Common Shares Outstanding (actual)']]Cek apakah perusahaan tsb ada baris lain yang tidak null pada total lembar sahamnya :
| variable | Entity Name | Year | ECS Total Common Shares Outstanding (actual) |
|---|---|---|---|
| 12716 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2024 | 14692189889.000000 |
| 12717 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2023 | 8568234364.000000 |
| 12718 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2022 | 8568234364.000000 |
| 12719 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2021 | 8568234364.000000 |
| 12720 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2020 | 6580926254.000000 |
| 12721 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2019 | 6580926254.000000 |
| 12722 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2018 | 6580926254.000000 |
| 12723 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2017 | 6580926254.000000 |
| 12724 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2016 | 5072356660.000000 |
| 12725 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2015 | 5072356660.000000 |
| 12726 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2014 | 5072356660.000000 |
| 12727 | PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:... | 2013 | NaN |
print('Perusahaan yang punya data lembar saham di baris lain:')
df_findat_final[(df_findat_final['Entity Name'].isin(tickers_with_null_total_shares)) & (df_findat_final['ECS Total Common Shares Outstanding (actual)'].notna())]['Entity Name'].unique().tolist()Perusahaan yang punya data lembar saham di baris lain:
['PT Bank Woori Saudara Indonesia 1906 Tbk (IDX:SDRA)']
Baris Tidak Layak Pakai
Cek dan hapus baris-baris yang tidak layak pakai, untuk baris yang :
- terlalu banyak null - null pada account keuangan yang krusial - pola null tidak masuk akal sehingga merusak konsistensi data
# TODOKesesuaian Tipe Data Kolom
df_findat_final.info()<class 'pandas.core.frame.DataFrame'>
Index: 8489 entries, 17 to 15693
Data columns (total 40 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 ticker 8489 non-null object
1 Year 8489 non-null int64
2 Entity Name 8489 non-null object
3 1st Level Primary Industry 8489 non-null object
4 Year Established 8468 non-null float64
5 Sector 8489 non-null object
6 Industry Group 8489 non-null object
7 Parent Percent Owned (%) 4848 non-null float64
8 Percent Owned - All Institutions (%) 4542 non-null float64
9 Percent Owned - Insiders (%) 5938 non-null float64
10 Total Liabilities (Rp.M) 8489 non-null float64
11 Total Assets (Rp.M) 8489 non-null float64
12 Current Ratio (x) 7783 non-null float64
13 Quick Ratio (x) 7785 non-null float64
14 Working Capital (Rp.M) 8489 non-null float64
15 Total Current Assets (Rp.M) 8489 non-null float64
16 Total Current Liabilities (Rp.M) 8489 non-null float64
17 Inventory (Rp.M) 7283 non-null float64
18 Prepaid Exp. (Rp.M) 7027 non-null float64
19 Long-term Debt (Rp.M) 5908 non-null float64
20 Short-term Borrowings (Rp.M) 5579 non-null float64
21 Current Portion of LT Debt & Leases (Rp.M) 337 non-null float64
22 Total Debt (Rp.M) 8468 non-null float64
23 Total Equity (Rp.M) 8489 non-null float64
24 Net Property, Plant & Equipment (Rp.M) 8470 non-null float64
25 Cost Of Goods Sold (Rp.M) 8309 non-null float64
26 Total Revenue (Rp.M) 8458 non-null float64
27 Operating Income (Rp.M) 8488 non-null float64
28 EBITDA (Rp.M) 7383 non-null float64
29 EBIT (Rp.M) 7390 non-null float64
30 Net Income to Company (Rp.M) 8489 non-null float64
31 Cash from Ops. (Rp.M) 8487 non-null float64
32 Net Change in Cash (Rp.M) 8487 non-null float64
33 ECS Total Common Shares Outstanding (actual) 8488 non-null float64
34 Market Capitalization (Rp.M) 8464 non-null float64
35 Year Close Stock Price (Rp.) 8464 non-null float64
36 Total Capital (Rp.M) 8468 non-null float64
37 Cash & Short-term Investments (Rp.M) 8489 non-null float64
38 Net Intangibles (Rp.M) 1763 non-null float64
39 IPO Year 8489 non-null Int64
dtypes: Int64(1), float64(33), int64(1), object(5)
memory usage: 2.7+ MB
df_findat_final['Year Established'].head(3)| Year Established | |
|---|---|
| 17 | 1988.000000 |
| 18 | 1988.000000 |
| 19 | 1988.000000 |
Kolom ‘Year Established’ harusnya integer, bukan float.
import pandas.testing as tm
original_col = df_findat_final['Year Established'].copy()
converted_col = df_findat_final['Year Established'].astype('Int64')
try:
# convert balik 'converted_col' ke float hanya untuk perbandingan
# check_dtype=False: abaikan beda tipe (float vs int), fokus ke nilainya
tm.assert_series_equal(original_col, converted_col.astype(float), check_dtype=False)
print("SUKSES: Semua nilai sama persis. Konversi aman.")
except AssertionError as e:
print("GAGAL: Ada nilai yang berubah (mungkin karena pembulatan desimal).")
print(e)SUKSES: Semua nilai sama persis. Konversi aman.
# Cek apakah ada nilai yang memiliki koma/desimal (selain NaN)
# dropna() digunakan agar NaN tidak dianggap error
ada_desimal = (df_findat_final['Year Established'].dropna() % 1 != 0).any()
if ada_desimal:
print("HATI-HATI: Ada data yang mengandung desimal. Konversi ke Int64 akan membuang komanya.")
# Lihat data mana yang punya desimal
print(df_findat_final[df_findat_final['Year Established'] % 1 != 0])
else:
print("AMAN: Semua data adalah bilangan bulat atau NaN.")AMAN: Semua data adalah bilangan bulat atau NaN.
print('CONVERT')
df_findat_final['Year Established'] = df_findat_final['Year Established'].astype('Int64')
print("Cek isi 'Year Established' sekarang:")
df_findat_final['Year Established'].head(3)CONVERT
Cek isi 'Year Established' sekarang:
| Year Established | |
|---|---|
| 17 | 1988 |
| 18 | 1988 |
| 19 | 1988 |
Sort dan Reset Index
df_findat_final = (
df_findat_final
.sort_values(by=["ticker", "Year"], ascending=[True, True])
.reset_index(drop=True)
)Hasil Dataset Finansial
print('Hasil dataset df_findat_final :')
print(df_findat_final.shape)
df_findat_finalHasil dataset df_findat_final :
(8489, 40)
| variable | ticker | Year | Entity Name | 1st Level Primary Industry | Year Established | Sector | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Total Equity (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Net Income to Company (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.M) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | IPO Year |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | AALI | 2008 | PT Astra Agro Lestari Tbk (IDX:AALI) | Consumer | 1988 | Consumer Staples | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1183215.000000 | 6519791.000000 | 1.944000 | 0.878000 | 959489.000000 | 1975656.000000 | 1016167.000000 | 781363.000000 | 37429.000000 | NaN | NaN | NaN | 0.000000 | 5336576.000000 | 4123645.000000 | 4357818.000000 | 8161217.000000 | 3370969.000000 | 3616720.000000 | 3370969.000000 | 2715518.000000 | 2087429.000000 | -145096.000000 | 1574745000.000000 | 15432501.000000 | 9800.000000 | 5336576.000000 | 867676.000000 | NaN | 1997 |
| 1 | AALI | 2009 | PT Astra Agro Lestari Tbk (IDX:AALI) | Consumer | 1988 | Consumer Staples | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1144783.000000 | 7571399.000000 | 1.826000 | 1.007000 | 775450.000000 | 1714426.000000 | 938976.000000 | 610031.000000 | 36849.000000 | NaN | NaN | NaN | 0.000000 | 6426616.000000 | 5242447.000000 | 4322498.000000 | 7424283.000000 | 2603280.000000 | 2898342.000000 | 2603280.000000 | 1729648.000000 | 1984894.000000 | -79127.000000 | 1574745000.000000 | 35825448.750000 | 22750.000000 | 6426616.000000 | 788549.000000 | NaN | 1997 |
| 2 | AALI | 2010 | PT Astra Agro Lestari Tbk (IDX:AALI) | Consumer | 1988 | Consumer Staples | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1334542.000000 | 8791799.000000 | 1.932000 | 1.262000 | 989325.000000 | 2051177.000000 | 1061852.000000 | 624694.000000 | 22315.000000 | NaN | NaN | NaN | 0.000000 | 7457257.000000 | 6103150.000000 | 5234372.000000 | 8843721.000000 | 2992793.000000 | 3335987.000000 | 2992793.000000 | 2103652.000000 | 2946657.000000 | 452232.000000 | 1574745000.000000 | 41258319.000000 | 26200.000000 | 7457257.000000 | 1240781.000000 | NaN | 1997 |
| 3 | AALI | 2011 | PT Astra Agro Lestari Tbk (IDX:AALI) | Consumer | 1988 | Consumer Staples | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1778337.000000 | 10204495.000000 | 1.265000 | 0.582000 | 389456.000000 | 1857025.000000 | 1467569.000000 | 769903.000000 | NaN | NaN | NaN | NaN | 0.000000 | 8426158.000000 | 7702571.000000 | 6837674.000000 | 10772582.000000 | 3195661.000000 | 3572651.000000 | 3195661.000000 | 2498565.000000 | 3162475.000000 | -402591.000000 | 1574745000.000000 | 34171966.500000 | 21700.000000 | 8426158.000000 | 838190.000000 | NaN | 1997 |
| 4 | AALI | 2012 | PT Astra Agro Lestari Tbk (IDX:AALI) | Consumer | 1988 | Consumer Staples | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 4701077.000000 | 12419820.000000 | 0.685000 | 0.107000 | -820145.000000 | 1780395.000000 | 2600540.000000 | 1249050.000000 | NaN | NaN | 971950.000000 | NaN | 971950.000000 | 9365411.000000 | 9894266.000000 | 7206837.000000 | 11564319.000000 | 3453729.000000 | 3973578.000000 | 3453729.000000 | 2520266.000000 | 2609511.000000 | -610421.000000 | 1574745000.000000 | 31022476.500000 | 19700.000000 | 10337361.000000 | 227769.000000 | NaN | 1997 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 8484 | ZONE | 2023 | PT Mega Perintis Tbk (IDX:ZONE) | Consumer | 2005 | Consumer Discretionary | Consumer Discretionary Distribution and Retail | NaN | NaN | 62.030000 | 376866.895000 | 752956.580000 | 1.787000 | 0.183000 | 187443.551000 | 425743.875000 | 238300.324000 | 370381.888000 | 8229.119000 | 45537.359000 | 85403.842000 | NaN | 230078.796000 | 376089.685000 | 254442.974000 | 327373.203000 | 735452.174000 | 80467.569000 | 109639.609000 | 80467.569000 | 46972.766000 | 101276.087000 | -292.586000 | 870171478.000000 | 965890.340580 | 1110.000000 | 606168.481000 | 4617.740000 | 36266.170000 | 2018 |
| 8485 | ZONE | 2024 | PT Mega Perintis Tbk (IDX:ZONE) | Consumer | 2005 | Consumer Discretionary | Consumer Discretionary Distribution and Retail | NaN | NaN | 62.030000 | 378069.858000 | 748744.072000 | 1.750000 | 0.178000 | 175199.380000 | 408802.739000 | 233603.359000 | 356567.003000 | 9325.106000 | 37101.093000 | 111850.141000 | NaN | 262139.509000 | 370674.214000 | 262571.197000 | 347862.483000 | 708360.249000 | 33764.860000 | 68145.652000 | 33764.860000 | 7044.347000 | 92620.371000 | 3123.094000 | 870171478.000000 | 717891.469350 | 825.000000 | 632813.723000 | 7740.834000 | 36707.806000 | 2018 |
| 8486 | ZYRX | 2022 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | Technology, Media & Telecommunications | 1996 | Information Technology | Technology Hardware and Equipment | NaN | NaN | 75.160000 | 438940.489000 | 707229.819000 | 1.442000 | 0.668000 | 190967.877000 | 623252.109000 | 432284.232000 | 321857.019000 | 32.094000 | NaN | 154814.692000 | NaN | 154972.192000 | 268289.329000 | 62069.361000 | 626487.712000 | 770370.215000 | 124050.223000 | 126197.456000 | 124050.223000 | 78627.417000 | 108226.350000 | 205303.596000 | 1333333837.000000 | 426666.704320 | 320.000000 | 423261.521000 | 206376.347000 | NaN | 2021 |
| 8487 | ZYRX | 2023 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | Technology, Media & Telecommunications | 1996 | Information Technology | Technology Hardware and Equipment | NaN | NaN | 75.160000 | 201592.951000 | 491041.729000 | 2.073000 | 0.401000 | 208245.855000 | 402244.527000 | 193998.671000 | 320652.316000 | 1146.130000 | 136.413000 | 123103.971000 | NaN | 123328.362000 | 289448.779000 | 68457.678000 | 198332.059000 | 289633.302000 | 75643.212000 | 77211.203000 | 75643.212000 | 32952.892000 | -128496.218000 | -193672.184000 | 1333334556.000000 | 221333.536296 | 166.000000 | 412777.140000 | 12704.163000 | 149.572000 | 2021 |
| 8488 | ZYRX | 2024 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | Technology, Media & Telecommunications | 1996 | Information Technology | Technology Hardware and Equipment | NaN | NaN | 75.160000 | 92393.711000 | 392444.599000 | 3.402000 | 0.775000 | 201817.814000 | 285854.627000 | 84036.813000 | 219621.479000 | 1021.593000 | 41.627000 | 46780.579000 | NaN | 47050.482000 | 300050.888000 | 92660.098000 | 296495.058000 | 364238.674000 | 37276.725000 | 38767.823000 | 37276.725000 | 15171.892000 | 113472.348000 | -6431.849000 | 1333334556.000000 | 177333.495948 | 133.000000 | 347101.370000 | 6272.314000 | 388.017000 | 2021 |
8489 rows × 40 columns
df_findat_final.isna().sum()| 0 | |
|---|---|
| variable | |
| ticker | 0 |
| Year | 0 |
| Entity Name | 0 |
| 1st Level Primary Industry | 0 |
| Year Established | 21 |
| Sector | 0 |
| Industry Group | 0 |
| Parent Percent Owned (%) | 3641 |
| Percent Owned - All Institutions (%) | 3947 |
| Percent Owned - Insiders (%) | 2551 |
| Total Liabilities (Rp.M) | 0 |
| Total Assets (Rp.M) | 0 |
| Current Ratio (x) | 706 |
| Quick Ratio (x) | 704 |
| Working Capital (Rp.M) | 0 |
| Total Current Assets (Rp.M) | 0 |
| Total Current Liabilities (Rp.M) | 0 |
| Inventory (Rp.M) | 1206 |
| Prepaid Exp. (Rp.M) | 1462 |
| Long-term Debt (Rp.M) | 2581 |
| Short-term Borrowings (Rp.M) | 2910 |
| Current Portion of LT Debt & Leases (Rp.M) | 8152 |
| Total Debt (Rp.M) | 21 |
| Total Equity (Rp.M) | 0 |
| Net Property, Plant & Equipment (Rp.M) | 19 |
| Cost Of Goods Sold (Rp.M) | 180 |
| Total Revenue (Rp.M) | 31 |
| Operating Income (Rp.M) | 1 |
| EBITDA (Rp.M) | 1106 |
| EBIT (Rp.M) | 1099 |
| Net Income to Company (Rp.M) | 0 |
| Cash from Ops. (Rp.M) | 2 |
| Net Change in Cash (Rp.M) | 2 |
| ECS Total Common Shares Outstanding (actual) | 1 |
| Market Capitalization (Rp.M) | 25 |
| Year Close Stock Price (Rp.) | 25 |
| Total Capital (Rp.M) | 21 |
| Cash & Short-term Investments (Rp.M) | 0 |
| Net Intangibles (Rp.M) | 6726 |
| IPO Year | 0 |
Pelabelan Target Prediksi Financial Distress
Fungsi Integrasi
def distress_target_labeling_and_adjustment(
df_findat_final: pd.DataFrame,
df_emiten_idx: pd.DataFrame,
range_years: int,
distress_type: str,
labeling_logic: str,
col_ticker: str = "ticker",
col_year: str = "Year",
col_equity: str = "Total Equity (Rp.M)",
col_net_income: str = "Net Income to Company (Rp.M)",
col_kode: str = "Kode",
col_tanggal_masuk: str = "Tanggal Masuk",
col_distress_ppk: str = "Distress_PPK",
col_distress_year: str = "tahun_distress",
col_target: str = "target_distress",
ppk_year_min: int = 2021,
ppk_year_max: int = 2025,
verbose: bool = True,
) -> pd.DataFrame:
"""
Membuat label target prediksi distress (0/1/NA) + tahun distress pertama (tahun_distress)
berdasarkan salah satu indikator distress: 'ppk', 'neg_equity', 'consecutive_loss'.
Note:
- Baris yang tidak bisa ditentukan targetnya TIDAK dihapus; target_distress cukup di-NA-kan.
- Aturan "no recovery" diterapkan dengan meng-NA-kan target pada tahun-tahun tertentu setelah distress.
- Untuk neg_equity: tahun dianggap ambigu bila row missing atau equity null.
- Untuk consecutive_loss: distress(y) hanya observable bila NI(y) dan NI(y-1) ada & tidak null & year berurutan.
- Untuk PPK: gunakan Distress_PPK == 'Yes', coverage diasumsikan ppk_year_min hingga ppk_year_max.
Parameters
----------
range_years : int > 0
labeling_logic : 'within' atau 'in_exactly'
- within: target=1 bila distress terjadi pada t+1..t+range
- in_exactly: target=1 bila distress terjadi tepat pada t+range
Returns
-------
df_out : pd.DataFrame
Copy dari df_findat_final + kolom tahun_distress dan target_distress (nullable Int64).
"""
# Range harus >0
if not isinstance(range_years, int) or range_years <= 0:
raise ValueError("range_years harus integer > 0")
distress_type = str(distress_type).strip().lower()
labeling_logic = str(labeling_logic).strip().lower()
if distress_type not in {"ppk", "neg_equity", "consecutive_loss"}:
raise ValueError("distress_type harus salah satu dari: 'ppk', 'neg_equity', 'consecutive_loss'")
if labeling_logic not in {"within", "in_exactly"}:
raise ValueError("labeling_logic harus 'within' atau 'in_exactly'")
# copy dari df_findat_final
df = df_findat_final.copy()
# Cek ketersediaan kolom
required_cols = {col_ticker, col_year}
missing_cols = [c for c in required_cols if c not in df.columns]
if missing_cols:
raise KeyError(f"Kolom wajib tidak ditemukan di df_findat_final: {missing_cols}")
# Pastikan Year numeric int
df[col_year] = pd.to_numeric(df[col_year], errors="coerce").astype("Int64")
if df[col_year].isna().any():
raise ValueError("Ada nilai Year yang tidak bisa dikonversi ke integer.")
if verbose:
print("--- MEMPROSES DATA DENGAN KONFIGURASI BERIKUT ---")
print(f"Tipe distress : {distress_type}")
print(f"Range tahun prediksi: {range_years}")
print(f"Logika pelabelan : {labeling_logic}")
print(f"Shape awal : {df.shape}")
# ======================================================================
# A) Hitung tahun_distress per ticker (distress_year_map: ticker -> distress year)
# ======================================================================
distress_year_map = pd.Series(dtype="Int64")
if distress_type == "ppk":
# Cek ketersediaan kolom wajid di df_emiten_idx
for c in [col_kode, col_distress_ppk, col_tanggal_masuk]:
if c not in df_emiten_idx.columns:
raise KeyError(f"Kolom '{c}' tidak ditemukan di df_emiten_idx")
emiten = df_emiten_idx.copy()
emiten[col_tanggal_masuk] = pd.to_datetime(emiten[col_tanggal_masuk], errors="coerce")
# Cari yang Distress_PPK bernilai YES dan ada tanggal masuknya
emiten_distress = emiten.loc[emiten[col_distress_ppk].astype(str).str.upper().eq("YES") & emiten[col_tanggal_masuk].notna(), [col_kode, col_tanggal_masuk]]
# Ada distress
if not emiten_distress.empty:
emiten_distress = emiten_distress.assign(_year=emiten_distress[col_tanggal_masuk].dt.year.astype("Int64"))
distress_year_map = emiten_distress.groupby(col_kode)["_year"].min().astype("Int64")
# Tidak ada distress
else:
distress_year_map = pd.Series(dtype="Int64")
elif distress_type == "neg_equity":
# Cek ketersediaan kolom ekuitas di df_findat_final
if col_equity not in df.columns:
raise KeyError(f"Kolom '{col_equity}' tidak ditemukan di df_findat_final")
# Pastikan numerik dan cari yang negatif tidak null
equity = pd.to_numeric(df[col_equity], errors="coerce")
mask_neg = equity.notna() & (equity < 0)
# distress year = tahun pertama equity negatif yang TEROBSERVASI (non-null)
if mask_neg.any():
distress_year_map = (
df.loc[mask_neg]
.groupby(col_ticker)[col_year]
.min()
.astype("Int64")
)
else:
distress_year_map = pd.Series(dtype="Int64")
else: # distress_type == "consecutive_loss"
# Cek ketersediaan kolom net income di df_findat_final
if col_net_income not in df.columns:
raise KeyError(f"Kolom '{col_net_income}' tidak ditemukan di df_findat_final")
# pastikan numerik
tmp = df[[col_ticker, col_year, col_net_income]].copy()
tmp[col_net_income] = pd.to_numeric(tmp[col_net_income], errors="coerce")
# sort berdasarkan ticker-year agar shift benar
tmp = tmp.sort_values([col_ticker, col_year])
# cari nilai prev year dan prev net income untuk tiap ticker-year
prev_year = tmp.groupby(col_ticker)[col_year].shift(1)
prev_ni = tmp.groupby(col_ticker)[col_net_income].shift(1)
# distress di tahun y JIKA DAN HANYA JIKA y dan y-1 ada dan berurutan, NI(y) & NI(y-1) non-null dan negatif
cond = (
tmp[col_net_income].notna() & # net income tahun tsb ada
prev_ni.notna() & # net income tahun sebelumnya ada
prev_year.notna() & # tahun sebelumnya ada
((tmp[col_year] - prev_year) == 1) & # selisih tahun tsb dan tahun sbelumnya 1 (negatif berturut-turut)
(tmp[col_net_income] < 0) & # net income tahun tsb negatif
(prev_ni < 0) # net income tahun sebelumnya negatif
)
if cond.any(): # ditemukan distress
distress_year_map = (
tmp.loc[cond]
.groupby(col_ticker)[col_year]
.min()
.astype("Int64")
)
else: # tidak ditemukan distress
distress_year_map = pd.Series(dtype="Int64")
# Assign tahun_distress ke semua baris
df[col_distress_year] = df[col_ticker].map(distress_year_map).astype("Int64")
# ======================================================================
# B) Inisialisasi target_distress (0/1) lalu apply 'censoring + ambiguity' dengan NA
# ======================================================================
# buat kolom, isi 0 semua dulu
df[col_target] = pd.Series(0, index=df.index, dtype="Int64")
t = df[col_year].astype(int) # tahun data
d = df[col_distress_year] # tahun distress Int64 (nullable)
r = range_years # rentang tahun prediksi
t_arr = df[col_year].to_numpy(dtype=float) # e.g. [2019., 2020., ...]
d_arr = df[col_distress_year].to_numpy(dtype=float) # e.g. [2023., nan, ...]
has_d = ~np.isnan(d_arr)
if labeling_logic == "within":
mask_one = has_d & (d_arr >= (t_arr + 1)) & (d_arr <= (t_arr + r)) # ada distress, terjadi antara t+1 hingga t+r inklusif
else: # labeling_logic == "in_exactly"
mask_one = has_d & (d_arr == (t_arr + r)) # ada distress, terjadi tepat t+r
df.loc[mask_one, col_target] = 1 # baris yang memenuhi diisi 1
# ======================================================================
# C) "No recovery" censoring: set NA untuk tahun-tahun yang tidak dipakai
# ======================================================================
if labeling_logic == "within":
# NA untuk Year >= tahun_distress
mask_censor = has_d & (t_arr >= d_arr) # d.notna() & (t >= d.astype(int))
else: # labeling_logic == "in_exactly"
# NA untuk Year >= (tahun_distress - r + 1)
mask_censor = has_d & (t_arr >= (d_arr - r + 1)) # d.notna() & (t >= (d.astype(int) - r + 1))
df.loc[mask_censor, col_target] = pd.NA
# ======================================================================
# D) Ambiguity / observability: hanya mempengaruhi label 0 (kalau tidak pasti => NA)
# - within: butuh semua tahun t+1..t+r harus observable
# - in_exactly: butuh tahun t+r saja yang harus observable
# ======================================================================
# Hanya cek baris yang saat ini masih 0 (bukan 1, bukan NA)
mask_zero = df[col_target].eq(0)
if mask_zero.any():
if distress_type == "ppk":
# Kalau ticker tidak ada di df_emiten_idx, tidak observable.
known_tickers = set(df_emiten_idx[col_kode].astype(str).unique()) if col_kode in df_emiten_idx.columns else set()
ticker_known = df[col_ticker].astype(str).isin(known_tickers)
if labeling_logic == "within":
observability_ok = (
ticker_known & # ticker harus known
((t + 1) >= ppk_year_min) & # year + 1 harus >= ppk tersedia terkecil
((t + r) <= ppk_year_max) # year + range harus <= ppk tersedia terbesar
)
else: # labeling_logic == "in_exactly"
observability_ok = (
ticker_known & # ticker harus known
((t + r) >= ppk_year_min) & # year + range harus >= ppk tersedia terkecil
((t + r) <= ppk_year_max) # year + range harus <= ppk tersedia terbesar
)
# pada baris yang kolom target 0 dan tidak observable, isi null
df.loc[mask_zero & ~observability_ok, col_target] = pd.NA
# REVISI untuk within, label 1 juga wajib observable (kalau tidak -> NA)
# bertujuan agar tahun-tahun akhir tidak menumpuk label distress semua, proporsi distress train-test lebih setara
# karena baris dg tahun-tahun terakhir tidak memiliki r tahun ke depan
if labeling_logic == "within":
df.loc[df[col_target].eq(1) & ~observability_ok, col_target] = pd.NA
elif distress_type == "neg_equity":
equity = pd.to_numeric(df[col_equity], errors="coerce") # pastikan equity numeric
df_equity_observable = df.loc[equity.notna(), [col_ticker, col_year]].copy() # ticker-year yang equity tidak null, bisa ditentukan distressnya
# MultiIndex pasangan (ticker, year) yang OBSERVABLE untuk equity
index_equity_observable = pd.MultiIndex.from_frame(df_equity_observable.astype({col_year: int}))
if labeling_logic == "within":
observability_ok = np.ones(len(df), dtype=bool) # isi 1 semua dulu
for k in range(1, r + 1): # cek untuk tiap r dari 1 sampai r -> ( tahun target t+1, t+2, ... , t+r )
candidate = pd.MultiIndex.from_arrays([df[col_ticker].astype(str), (t + k).astype(int)]) # buat multiindex pair ticker-target(t+k)
observability_ok &= candidate.isin(index_equity_observable) # cek apakah ada di observable, jika iya maka boolean diakumulasi ke observability_ok
observability_ok = pd.Series(observability_ok, index=df.index) # ubah ke series agar indeksnya sama dengan df
else: # labeling_logic == "in_exactly"
candidate = pd.MultiIndex.from_arrays([df[col_ticker].astype(str), (t + r).astype(int)]) # hanya cek t+r
observability_ok = pd.Series(candidate.isin(index_equity_observable), index=df.index) # cek apakah observable
df.loc[mask_zero & ~observability_ok, col_target] = pd.NA # yang target 0 dan tidak observable, maka diset null
# REVISI untuk within, label 1 juga wajib observable (kalau tidak -> NA)
# bertujuan agar tahun-tahun akhir tidak menumpuk label distress semua, proporsi distress train-test lebih setara
# karena baris dg tahun-tahun terakhir tidak memiliki r tahun ke depan
if labeling_logic == "within":
df.loc[df[col_target].eq(1) & ~observability_ok, col_target] = pd.NA
else: # distress_type == "consecutive_loss"
ni = pd.to_numeric(df[col_net_income], errors="coerce") # pastikan net income numeric
df_ni_observable = df.loc[ni.notna(), [col_ticker, col_year]].copy() # ticker-year yang net income tidak null, bisa ditentukan distressnya
# MultiIndex pasangan (ticker, year) yang OBSERVABLE untuk NI (row ada & NI non-null)
index_ni_observable = pd.MultiIndex.from_frame(df_ni_observable.astype({col_year: int}))
if labeling_logic == "within":
observability_ok = np.ones(len(df), dtype=bool) # isi 1 semua dulu
for k in range(1, r + 1): # cek untuk tiap r dari 1 sampai r -> ( tahun target t+1, t+2, ... , t+r )
y = (t + k).astype(int) # tahun target t+k
y_prev = (t + k - 1).astype(int) # tahun sebelumnya
candidate_y = pd.MultiIndex.from_arrays([df[col_ticker].astype(str), y]) # multiindex ticker dan y
candidate_prev = pd.MultiIndex.from_arrays([df[col_ticker].astype(str), y_prev]) # multiindex ticker dan y_prev
# distress(y) observable hanya jika NI(y) dan NI(y-1) observable
observability_ok &= candidate_y.isin(index_ni_observable) & candidate_prev.isin(index_ni_observable) # cek apakah y dan yprev ada di observable, jika iya maka boolean diakumulasi ke observability_ok
observability_ok = pd.Series(observability_ok, index=df.index) # sesuaikan index
else: # labeling_logic == "in_exactly" (cek hanya untuk tepat r tahun setelah t)
y = (t + r).astype(int) # tahun target t+k
y_prev = (t + r - 1).astype(int) # tahun sebelum target
candidate_y = pd.MultiIndex.from_arrays([df[col_ticker].astype(str), y]) # multiindex ticker dan y
candidate_prev = pd.MultiIndex.from_arrays([df[col_ticker].astype(str), y_prev]) # multiindex ticker dan yprev
observability_ok = pd.Series(candidate_y.isin(index_ni_observable) & candidate_prev.isin(index_ni_observable), index=df.index) # cek apakah y dan yprev ada di observable, masukkan ke observability_ok
df.loc[mask_zero & ~observability_ok, col_target] = pd.NA # yang targetnya 0 dan observabilty tidak ok, maka null
# REVISI untuk within, label 1 juga wajib observable (kalau tidak -> NA)
# bertujuan agar tahun-tahun akhir tidak menumpuk label distress semua, proporsi distress train-test lebih setara
# karena baris dg tahun-tahun terakhir tidak memiliki r tahun ke depan
if labeling_logic == "within":
df.loc[df[col_target].eq(1) & ~observability_ok, col_target] = pd.NA
# Urutkan Kolom
first_cols = [col_ticker, col_year, 'Entity Name', col_target, col_distress_year, col_equity, col_net_income]
df = df[first_cols + [col for col in df.columns if col not in first_cols]]
if verbose:
vc = df[col_target].value_counts(dropna=False)
print("--- RINGKASAN HASIL LABELING ---")
print(vc)
print()
return dfdef distress_target_labeling_and_adjustment_all_indicators_at_once(
df_findat_final: pd.DataFrame,
df_emiten_idx: pd.DataFrame,
range_years: int,
# distress_type: str,
labeling_logic: str,
col_ticker: str = "ticker",
col_year: str = "Year",
col_equity: str = "Total Equity (Rp.M)",
col_net_income: str = "Net Income to Company (Rp.M)",
col_kode: str = "Kode",
col_tanggal_masuk: str = "Tanggal Masuk",
col_distress_ppk: str = "Distress_PPK",
col_distress_year: str = "tahun_distress",
col_target: str = "target_distress",
ppk_year_min: int = 2021,
ppk_year_max: int = 2025,
verbose: bool = True,
) -> pd.DataFrame:
"""
Membuat label target prediksi distress (0/1/NA) + tahun distress pertama (tahun_distress)
berdasarkan salah satu indikator distress: 'ppk', 'neg_equity', 'consecutive_loss'.
Note:
- Baris yang tidak bisa ditentukan targetnya TIDAK dihapus; target_distress cukup di-NA-kan.
- Aturan "no recovery" diterapkan dengan meng-NA-kan target pada tahun-tahun tertentu setelah distress.
- Untuk neg_equity: tahun dianggap ambigu bila row missing atau equity null.
- Untuk consecutive_loss: distress(y) hanya observable bila NI(y) dan NI(y-1) ada & tidak null & year berurutan.
- Untuk PPK: gunakan Distress_PPK == 'Yes', coverage diasumsikan ppk_year_min hingga ppk_year_max.
Parameters
----------
range_years : int > 0
labeling_logic : 'within' atau 'in_exactly'
- within: target=1 bila distress terjadi pada t+1..t+range
- in_exactly: target=1 bila distress terjadi tepat pada t+range
Returns
-------
df_out : pd.DataFrame
Copy dari df_findat_final + kolom tahun_distress dan target_distress (nullable Int64).
"""
# CEK INPUT KONFIGURASI
# Range harus >0
if not isinstance(range_years, int) or range_years <= 0:
raise ValueError("range_years harus integer > 0")
# distress_type = str(distress_type).strip().lower()
labeling_logic = str(labeling_logic).strip().lower()
# if distress_type not in {"ppk", "neg_equity", "consecutive_loss"}:
# raise ValueError("distress_type harus salah satu dari: 'ppk', 'neg_equity', 'consecutive_loss'")
if labeling_logic not in {"within", "in_exactly"}:
raise ValueError("labeling_logic harus 'within' atau 'in_exactly'")
# copy dari df_findat_final
df = df_findat_final.copy()
# Cek ketersediaan kolom
required_cols = {col_ticker, col_year}
missing_cols = [c for c in required_cols if c not in df.columns]
if missing_cols:
raise KeyError(f"Kolom wajib tidak ditemukan di df_findat_final: {missing_cols}")
# Pastikan Year numeric int
df[col_year] = pd.to_numeric(df[col_year], errors="coerce").astype("Int64")
if df[col_year].isna().any():
raise ValueError("Ada nilai Year yang tidak bisa dikonversi ke integer.")
# -- BUAT UNTUK SEMUA TIPE INDIKATOR DISTRESS -- #
# Dijadikan 1 dataset dengan kolom berbeda-beda #
for distress_type in ["ppk", "neg_equity", "consecutive_loss"]:
# Set nama kolom yang akan dibuat
col_distress_year = f"tahun_distress_{distress_type}"
col_target = f"target_distress_{distress_type}"
if verbose:
print("--- MEMPROSES DATA DENGAN KONFIGURASI BERIKUT ---")
print(f"Tipe distress : {distress_type}")
print(f"Range tahun prediksi: {range_years}")
print(f"Logika pelabelan : {labeling_logic}")
print(f"Shape awal : {df.shape}")
# ======================================================================
# A) Hitung tahun_distress per ticker (distress_year_map: ticker -> distress year)
# ======================================================================
distress_year_map = pd.Series(dtype="Int64")
if distress_type == "ppk":
# Cek ketersediaan kolom wajid di df_emiten_idx
for c in [col_kode, col_distress_ppk, col_tanggal_masuk]:
if c not in df_emiten_idx.columns:
raise KeyError(f"Kolom '{c}' tidak ditemukan di df_emiten_idx")
emiten = df_emiten_idx.copy()
emiten[col_tanggal_masuk] = pd.to_datetime(emiten[col_tanggal_masuk], errors="coerce")
# Cari yang Distress_PPK bernilai YES dan ada tanggal masuknya
emiten_distress = emiten.loc[emiten[col_distress_ppk].astype(str).str.upper().eq("YES") & emiten[col_tanggal_masuk].notna(), [col_kode, col_tanggal_masuk]]
# IF ada distress
if not emiten_distress.empty:
emiten_distress = emiten_distress.assign(_year=emiten_distress[col_tanggal_masuk].dt.year.astype("Int64"))
distress_year_map = emiten_distress.groupby(col_kode)["_year"].min().astype("Int64")
# ELSE (tidak ada distress)
else:
distress_year_map = pd.Series(dtype="Int64")
elif distress_type == "neg_equity":
# Cek ketersediaan kolom ekuitas di df_findat_final
if col_equity not in df.columns:
raise KeyError(f"Kolom '{col_equity}' tidak ditemukan di df_findat_final")
# Pastikan numerik dan cari yang negatif && tidak null
equity = pd.to_numeric(df[col_equity], errors="coerce")
mask_neg = equity.notna() & (equity < 0)
# distress year = tahun pertama equity negatif yang TEROBSERVASI (non-null)
if mask_neg.any():
distress_year_map = (
df.loc[mask_neg]
.groupby(col_ticker)[col_year]
.min()
.astype("Int64")
)
else:
distress_year_map = pd.Series(dtype="Int64")
else: # distress_type == "consecutive_loss"
# Cek ketersediaan kolom Net Income di df_findat_final
if col_net_income not in df.columns:
raise KeyError(f"Kolom '{col_net_income}' tidak ditemukan di df_findat_final")
# pastikan numerik
tmp = df[[col_ticker, col_year, col_net_income]].copy()
tmp[col_net_income] = pd.to_numeric(tmp[col_net_income], errors="coerce")
# sort berdasarkan ticker-year agar shift benar
tmp = tmp.sort_values([col_ticker, col_year])
# cari nilai prev year dan prev net income untuk tiap ticker-year
prev_year = tmp.groupby(col_ticker)[col_year].shift(1)
prev_ni = tmp.groupby(col_ticker)[col_net_income].shift(1)
# distress di tahun y JIKA DAN HANYA JIKA y dan y-1 ada dan berurutan DAN NI(y) & NI(y-1) non-null negatif
cond = (
tmp[col_net_income].notna() & # net income tahun tsb ada
prev_ni.notna() & # net income tahun sebelumnya ada
prev_year.notna() & # tahun sebelumnya ada
((tmp[col_year] - prev_year) == 1) & # selisih tahun tsb dan tahun sbelumnya 1 (berturut-turut)
(tmp[col_net_income] < 0) & # net income tahun tsb negatif
(prev_ni < 0) # net income tahun sebelumnya negatif
)
if cond.any(): # ditemukan distress
distress_year_map = (
tmp.loc[cond]
.groupby(col_ticker)[col_year]
.min()
.astype("Int64")
)
else: # tidak ditemukan distress
distress_year_map = pd.Series(dtype="Int64")
# Assign tahun_distress ke semua baris
df[col_distress_year] = df[col_ticker].map(distress_year_map).astype("Int64")
# ======================================================================
# B) Inisialisasi target_distress (0/1) lalu apply 'censoring + ambiguity' dengan NA
# ======================================================================
# buat kolom target, isi 0 semua dulu
df[col_target] = pd.Series(0, index=df.index, dtype="Int64")
t = df[col_year].astype(int) # tahun data
d = df[col_distress_year] # tahun distress Int64 (nullable)
r = range_years # rentang tahun prediksi
t_arr = df[col_year].to_numpy(dtype=float) # e.g. [2019., 2020., ...] kolom berisi tahun, tidak ada null
d_arr = df[col_distress_year].to_numpy(dtype=float) # e.g. [2023., nan, ...] kolom berisi tahun distress, null artinya tidak pernah distress
has_d = ~np.isnan(d_arr)
if labeling_logic == "within":
mask_one = has_d & (d_arr >= (t_arr + 1)) & (d_arr <= (t_arr + r)) # ada distress, terjadi antara t+1 hingga t+r inklusif
else: # labeling_logic == "in_exactly"
mask_one = has_d & (d_arr == (t_arr + r)) # ada distress, distress terjadi tepat di t+r
df.loc[mask_one, col_target] = 1 # baris yang memenuhi mask sblmnya diisi 1 pada kolom target
# ======================================================================
# C) "No recovery" censoring: set NA untuk tahun-tahun yang tidak dipakai
# ======================================================================
if labeling_logic == "within":
# di-NA-kan untuk yang Year >= tahun_distress
mask_censor = has_d & (t_arr >= d_arr) # d.notna() & (t >= d.astype(int))
else: # labeling_logic == "in_exactly"
# di-NA-kan untuk yang Year >= (tahun_distress - r + 1)
mask_censor = has_d & (t_arr >= (d_arr - r + 1)) # d.notna() & (t >= (d.astype(int) - r + 1))
df.loc[mask_censor, col_target] = pd.NA
# ======================================================================
# D) Ambiguity / observability: hanya mempengaruhi label 0 (kalau tidak pasti => NA)
# - within: butuh semua tahun t+1..t+r harus observable
# - in_exactly: butuh tahun t+r saja yang harus observable
# ======================================================================
# Hanya cek baris yang saat ini masih 0 (bukan 1, bukan NA)
mask_zero = df[col_target].eq(0)
if mask_zero.any():
if distress_type == "ppk":
# Kalau ticker tidak ada di df_emiten_idx, tidak observable.
known_tickers = set(df_emiten_idx[col_kode].astype(str).unique()) if col_kode in df_emiten_idx.columns else set()
ticker_known = df[col_ticker].astype(str).isin(known_tickers)
if labeling_logic == "within":
observability_ok = (
ticker_known & # ticker harus known
((t + 1) >= ppk_year_min) & # year + 1 harus >= ppk tersedia terkecil
((t + r) <= ppk_year_max) # year + range harus <= ppk tersedia terbesar
)
else: # labeling_logic == "in_exactly"
observability_ok = (
ticker_known & # ticker harus known
((t + r) >= ppk_year_min) & # year + range harus >= ppk tersedia terkecil
((t + r) <= ppk_year_max) # year + range harus <= ppk tersedia terbesar
)
# pada baris yang kolom target 0 dan tidak observable, isi null
df.loc[mask_zero & ~observability_ok, col_target] = pd.NA
# REVISI untuk within, label 1 juga wajib observable (kalau tidak -> NA)
# bertujuan agar tahun-tahun akhir tidak menumpuk label distress semua, proporsi distress train-test lebih setara
# karena baris dg tahun-tahun terakhir tidak memiliki r tahun ke depan
if labeling_logic == "within":
df.loc[df[col_target].eq(1) & ~observability_ok, col_target] = pd.NA
elif distress_type == "neg_equity":
equity = pd.to_numeric(df[col_equity], errors="coerce") # pastikan equity numeric
df_equity_observable = df.loc[equity.notna(), [col_ticker, col_year]].copy() # ticker-year yang equity tidak null, bisa ditentukan distressnya
# MultiIndex pasangan (ticker, year) yang OBSERVABLE untuk equity
index_equity_observable = pd.MultiIndex.from_frame(df_equity_observable.astype({col_year: int}))
if labeling_logic == "within":
observability_ok = np.ones(len(df), dtype=bool) # isi 1 semua dulu
for k in range(1, r + 1): # cek untuk tiap r dari 1 sampai r -> ( tahun target t+1, t+2, ... , t+r )
candidate = pd.MultiIndex.from_arrays([df[col_ticker].astype(str), (t + k).astype(int)]) # buat multiindex pair ticker-target(t+k)
observability_ok &= candidate.isin(index_equity_observable) # cek apakah ada di observable, jika iya maka boolean diakumulasi ke observability_ok
observability_ok = pd.Series(observability_ok, index=df.index) # ubah ke series agar indeksnya sama dengan df
else: # labeling_logic == "in_exactly"
candidate = pd.MultiIndex.from_arrays([df[col_ticker].astype(str), (t + r).astype(int)]) # hanya cek t+r
observability_ok = pd.Series(candidate.isin(index_equity_observable), index=df.index) # cek apakah observable
df.loc[mask_zero & ~observability_ok, col_target] = pd.NA # yang target 0 dan tidak observable, maka diset null
# REVISI untuk within, label 1 juga wajib observable (kalau tidak -> NA)
# bertujuan agar tahun-tahun akhir tidak menumpuk label distress semua, proporsi distress train-test lebih setara
# karena baris dg tahun-tahun terakhir tidak memiliki r tahun ke depan
if labeling_logic == "within":
df.loc[df[col_target].eq(1) & ~observability_ok, col_target] = pd.NA
else: # distress_type == "consecutive_loss"
ni = pd.to_numeric(df[col_net_income], errors="coerce") # pastikan net income numeric
df_ni_observable = df.loc[ni.notna(), [col_ticker, col_year]].copy() # ticker-year yang net income tidak null, bisa ditentukan distressnya
# MultiIndex pasangan (ticker, year) yang OBSERVABLE untuk NI (row ada & NI non-null)
index_ni_observable = pd.MultiIndex.from_frame(df_ni_observable.astype({col_year: int}))
if labeling_logic == "within":
observability_ok = np.ones(len(df), dtype=bool) # isi 1 semua dulu
for k in range(1, r + 1): # cek untuk tiap r dari 1 sampai r -> ( tahun target t+1, t+2, ... , t+r )
y = (t + k).astype(int) # tahun target t+k
y_prev = (t + k - 1).astype(int) # tahun sebelumnya
candidate_y = pd.MultiIndex.from_arrays([df[col_ticker].astype(str), y]) # multiindex ticker dan y
candidate_prev = pd.MultiIndex.from_arrays([df[col_ticker].astype(str), y_prev]) # multiindex ticker dan y_prev
# distress(y) observable hanya jika NI(y) dan NI(y-1) observable
observability_ok &= candidate_y.isin(index_ni_observable) & candidate_prev.isin(index_ni_observable) # cek apakah y dan yprev ada di observable, jika iya maka boolean diakumulasi ke observability_ok
observability_ok = pd.Series(observability_ok, index=df.index) # sesuaikan index
else: # labeling_logic == "in_exactly" (cek hanya untuk tepat r tahun setelah t)
y = (t + r).astype(int) # tahun target t+k
y_prev = (t + r - 1).astype(int) # tahun sebelum target
candidate_y = pd.MultiIndex.from_arrays([df[col_ticker].astype(str), y]) # multiindex ticker dan y
candidate_prev = pd.MultiIndex.from_arrays([df[col_ticker].astype(str), y_prev]) # multiindex ticker dan yprev
observability_ok = pd.Series(candidate_y.isin(index_ni_observable) & candidate_prev.isin(index_ni_observable), index=df.index) # cek apakah y dan yprev ada di observable, masukkan ke observability_ok
df.loc[mask_zero & ~observability_ok, col_target] = pd.NA # yang targetnya 0 dan observabilty tidak ok, maka null
# REVISI untuk within, label 1 juga wajib observable (kalau tidak -> NA)
# bertujuan agar tahun-tahun akhir tidak menumpuk label distress semua, proporsi distress train-test lebih setara
# karena baris dg tahun-tahun terakhir tidak memiliki r tahun ke depan
if labeling_logic == "within":
df.loc[df[col_target].eq(1) & ~observability_ok, col_target] = pd.NA
# Urutkan Kolom
first_cols = [col_ticker, col_year, 'Entity Name', col_equity, col_net_income, col_target, col_distress_year]
df = df[first_cols + [col for col in df.columns if col not in first_cols]]
if verbose:
vc = df[col_target].value_counts(dropna=False)
print("--- RINGKASAN HASIL LABELING ---")
print(vc)
print()
return dfCONFIG Target & Range Prediksi
RANGE = 2
# LABELING_LOGIC = 'in_exactly'
LABELING_LOGIC = 'within'
df_integrated_ppk = distress_target_labeling_and_adjustment(
df_findat_final=df_findat_final,
df_emiten_idx=df_emiten_idx,
range_years=RANGE,
distress_type='ppk',
labeling_logic=LABELING_LOGIC)
df_integrated_neg_eq = distress_target_labeling_and_adjustment(
df_findat_final=df_findat_final,
df_emiten_idx=df_emiten_idx,
range_years=RANGE,
distress_type='neg_equity',
labeling_logic=LABELING_LOGIC)
df_integrated_2_loss = distress_target_labeling_and_adjustment(
df_findat_final=df_findat_final,
df_emiten_idx=df_emiten_idx,
range_years=RANGE,
distress_type='consecutive_loss',
labeling_logic=LABELING_LOGIC)--- MEMPROSES DATA DENGAN KONFIGURASI BERIKUT ---
Tipe distress : ppk
Range tahun prediksi: 2
Logika pelabelan : within
Shape awal : (8489, 40)
--- RINGKASAN HASIL LABELING ---
target_distress
<NA> 5906
0 2073
1 510
Name: count, dtype: Int64
--- MEMPROSES DATA DENGAN KONFIGURASI BERIKUT ---
Tipe distress : neg_equity
Range tahun prediksi: 2
Logika pelabelan : within
Shape awal : (8489, 40)
--- RINGKASAN HASIL LABELING ---
target_distress
0 6287
<NA> 2086
1 116
Name: count, dtype: Int64
--- MEMPROSES DATA DENGAN KONFIGURASI BERIKUT ---
Tipe distress : consecutive_loss
Range tahun prediksi: 2
Logika pelabelan : within
Shape awal : (8489, 40)
--- RINGKASAN HASIL LABELING ---
target_distress
0 4595
<NA> 3329
1 565
Name: count, dtype: Int64
RANGE = 2
LABELING_LOGIC = 'within' # 'in_exactly'
df_integrated_all_indicators = distress_target_labeling_and_adjustment_all_indicators_at_once(
df_findat_final=df_findat_final,
df_emiten_idx=df_emiten_idx,
range_years=RANGE,
labeling_logic=LABELING_LOGIC)--- MEMPROSES DATA DENGAN KONFIGURASI BERIKUT ---
Tipe distress : ppk
Range tahun prediksi: 2
Logika pelabelan : within
Shape awal : (8489, 40)
--- RINGKASAN HASIL LABELING ---
target_distress_ppk
<NA> 5906
0 2073
1 510
Name: count, dtype: Int64
--- MEMPROSES DATA DENGAN KONFIGURASI BERIKUT ---
Tipe distress : neg_equity
Range tahun prediksi: 2
Logika pelabelan : within
Shape awal : (8489, 42)
--- RINGKASAN HASIL LABELING ---
target_distress_neg_equity
0 6287
<NA> 2086
1 116
Name: count, dtype: Int64
--- MEMPROSES DATA DENGAN KONFIGURASI BERIKUT ---
Tipe distress : consecutive_loss
Range tahun prediksi: 2
Logika pelabelan : within
Shape awal : (8489, 44)
--- RINGKASAN HASIL LABELING ---
target_distress_consecutive_loss
0 4595
<NA> 3329
1 565
Name: count, dtype: Int64
Uji Hasil
def audit_distress_labeling_suite(
df_findat_final: pd.DataFrame,
df_emiten_idx: pd.DataFrame | None = None,
ranges=(1, 2),
distress_types=("ppk", "neg_equity", "consecutive_loss"),
labeling_logics=("within", "in_exactly"),
labeling_fn=None, # default: distress_target_labeling_and_adjustment kalau ada di scope
precomputed: dict | None = None, # optional: {(distress_type, labeling_logic, r): df_out}
col_ticker: str = "ticker",
col_year: str = "Year",
col_equity: str = "Total Equity (Rp.M)",
col_net_income: str = "Net Income to Company (Rp.M)",
col_distress_year: str = "tahun_distress",
col_target: str = "target_distress",
# PPK specifics
col_kode: str = "Kode",
col_tanggal_masuk: str = "Tanggal Masuk",
col_distress_ppk: str = "Distress_PPK",
ppk_year_min: int = 2021,
ppk_year_max: int = 2025,
# audit behavior
sample_rows: int = 8,
recompute_distress_year: bool = True, # cek ulang tahun_distress
):
"""
Audit konsistensi output labeling untuk berbagai konfigurasi.
Return:
- summary_df: ringkasan jumlah pelanggaran per aturan per konfigurasi
- issues: dict contoh baris pelanggaran untuk tiap konfigurasi
"""
if labeling_fn is None:
labeling_fn = distress_target_labeling_and_adjustment # harus ada di scope notebook
def _sample(df, n=sample_rows):
if df is None or len(df) == 0:
return df
return df.head(n)
def _recompute_distress_year(df_out, distress_type):
"""Recompute tahun_distress dari df_out + (df_emiten_idx kalau ppk). Return Series map per ticker."""
if distress_type == "neg_equity":
eq = pd.to_numeric(df_out[col_equity], errors="coerce")
mask = eq.notna() & (eq < 0)
if not mask.any():
return pd.Series(dtype="Int64")
return df_out.loc[mask].groupby(col_ticker)[col_year].min().astype("Int64")
if distress_type == "consecutive_loss":
tmp = df_out[[col_ticker, col_year, col_net_income]].copy()
tmp[col_net_income] = pd.to_numeric(tmp[col_net_income], errors="coerce")
tmp = tmp.sort_values([col_ticker, col_year])
prev_year = tmp.groupby(col_ticker)[col_year].shift(1)
prev_ni = tmp.groupby(col_ticker)[col_net_income].shift(1)
cond = (
tmp[col_net_income].notna() &
prev_ni.notna() &
prev_year.notna() &
((tmp[col_year] - prev_year) == 1) &
(tmp[col_net_income] < 0) &
(prev_ni < 0)
)
if not cond.any():
return pd.Series(dtype="Int64")
return tmp.loc[cond].groupby(col_ticker)[col_year].min().astype("Int64")
if distress_type == "ppk":
if df_emiten_idx is None:
return pd.Series(dtype="Int64")
em = df_emiten_idx.copy()
if col_tanggal_masuk in em.columns:
em[col_tanggal_masuk] = pd.to_datetime(em[col_tanggal_masuk], errors="coerce")
mask = (
em[col_distress_ppk].astype(str).str.upper().eq("YES") &
em[col_tanggal_masuk].notna()
)
em_dist = em.loc[mask, [col_kode, col_tanggal_masuk]].copy()
if em_dist.empty:
return pd.Series(dtype="Int64")
em_dist["_year"] = em_dist[col_tanggal_masuk].dt.year.astype("Int64")
return em_dist.groupby(col_kode)["_year"].min().astype("Int64")
return pd.Series(dtype="Int64")
def _audit_single(df_out, distress_type, labeling_logic, r):
"""Return (summary_row_dict, issues_dict)."""
issues = {}
# basic columns check
need_cols = {col_ticker, col_year, col_distress_year, col_target}
miss = [c for c in need_cols if c not in df_out.columns]
if miss:
return (
dict(
distress_type=distress_type, labeling_logic=labeling_logic, range_years=r,
n_rows=len(df_out),
error=f"Missing columns: {miss}",
),
{"missing_columns": miss},
)
df_chk = df_out.copy()
df_chk[col_year] = pd.to_numeric(df_chk[col_year], errors="coerce")
t_arr = df_chk[col_year].to_numpy(dtype=float)
d_arr = pd.to_numeric(df_chk[col_distress_year], errors="coerce").to_numpy(dtype=float)
target = df_chk[col_target]
# handle target possibly object -> numeric Int64-like comparisons
# (if target already Int64, this is fine)
is1 = target.eq(1)
is0 = target.eq(0)
isna = target.isna()
has_d = ~np.isnan(d_arr)
# -------- Rule checks --------
# R1: target==1 => d exists
v_r1 = is1 & ~has_d
issues["R1_target1_requires_distress_year"] = _sample(df_chk.loc[v_r1, [col_ticker, col_year, col_distress_year, col_target]])
# R2: target==1 must satisfy window condition
if labeling_logic == "within":
ok_r2 = (d_arr >= (t_arr + 1)) & (d_arr <= (t_arr + r))
else:
ok_r2 = (d_arr == (t_arr + r))
v_r2 = is1 & ~ok_r2
issues["R2_target1_window_mismatch"] = _sample(df_chk.loc[v_r2, [col_ticker, col_year, col_distress_year, col_target]])
# R3: target==0 => (d NA) OR (d > t+r)
ok_r3 = (~has_d) | (d_arr > (t_arr + r))
v_r3 = is0 & ~ok_r3
issues["R3_target0_inconsistent_with_distress_year"] = _sample(df_chk.loc[v_r3, [col_ticker, col_year, col_distress_year, col_target]])
# R4: censoring no-recovery
if labeling_logic == "within":
should_be_na = has_d & (t_arr >= d_arr)
else:
should_be_na = has_d & (t_arr >= (d_arr - r + 1))
v_r4 = (~isna) & should_be_na
issues["R4_no_recovery_censor_violations"] = _sample(df_chk.loc[v_r4, [col_ticker, col_year, col_distress_year, col_target]])
# R5: anchor year exists => must be 1
# within: Year in [d-r, d-1] => target should be 1 (if row exists and not censored)
# exact: Year == d-r => target should be 1 (if row exists)
# We'll check row-level using d_arr, not groupby.
if labeling_logic == "within":
in_anchor = has_d & (t_arr >= (d_arr - r)) & (t_arr <= (d_arr - 1))
v_r5 = in_anchor & ~is1 & ~isna # if NA due to other reasons, still suspicious, but we won't flag NA here
else:
in_anchor = has_d & (t_arr == (d_arr - r))
v_r5 = in_anchor & ~is1 # should never be 0/NA per current function logic
issues["R5_anchor_year_should_be_1"] = _sample(df_chk.loc[v_r5, [col_ticker, col_year, col_distress_year, col_target]])
# R6: observability for rows labeled 0 (strong check)
# Only possible if we have required columns / df_emiten_idx for ppk
v_r6 = pd.Series(False, index=df_chk.index)
if distress_type == "ppk":
if df_emiten_idx is not None and col_kode in df_emiten_idx.columns:
known = set(df_emiten_idx[col_kode].astype(str).unique())
ticker_known = df_chk[col_ticker].astype(str).isin(known).to_numpy()
if labeling_logic == "within":
obs_ok = ticker_known & ((t_arr + 1) >= ppk_year_min) & ((t_arr + r) <= ppk_year_max)
else:
obs_ok = ticker_known & ((t_arr + r) >= ppk_year_min) & ((t_arr + r) <= ppk_year_max)
v_r6 = is0 & ~obs_ok
elif distress_type == "neg_equity":
if col_equity in df_chk.columns:
eq = pd.to_numeric(df_chk[col_equity], errors="coerce")
df_obs = df_chk.loc[eq.notna(), [col_ticker, col_year]].copy()
idx_obs = pd.MultiIndex.from_frame(df_obs.astype({col_year: int}))
if labeling_logic == "within":
ok = np.ones(len(df_chk), dtype=bool)
for k in range(1, r + 1):
cand = pd.MultiIndex.from_arrays([df_chk[col_ticker].astype(str), (df_chk[col_year].astype(int) + k)])
ok &= cand.isin(idx_obs)
v_r6 = is0 & ~ok
else:
cand = pd.MultiIndex.from_arrays([df_chk[col_ticker].astype(str), (df_chk[col_year].astype(int) + r)])
ok = cand.isin(idx_obs)
v_r6 = is0 & ~ok
elif distress_type == "consecutive_loss":
if col_net_income in df_chk.columns:
ni = pd.to_numeric(df_chk[col_net_income], errors="coerce")
df_obs = df_chk.loc[ni.notna(), [col_ticker, col_year]].copy()
idx_obs = pd.MultiIndex.from_frame(df_obs.astype({col_year: int}))
if labeling_logic == "within":
ok = np.ones(len(df_chk), dtype=bool)
base_year = df_chk[col_year].astype(int)
for k in range(1, r + 1):
y = base_year + k
y_prev = base_year + k - 1
cand_y = pd.MultiIndex.from_arrays([df_chk[col_ticker].astype(str), y])
cand_prev = pd.MultiIndex.from_arrays([df_chk[col_ticker].astype(str), y_prev])
ok &= cand_y.isin(idx_obs) & cand_prev.isin(idx_obs)
v_r6 = is0 & ~ok
else:
base_year = df_chk[col_year].astype(int)
y = base_year + r
y_prev = base_year + r - 1
cand_y = pd.MultiIndex.from_arrays([df_chk[col_ticker].astype(str), y])
cand_prev = pd.MultiIndex.from_arrays([df_chk[col_ticker].astype(str), y_prev])
ok = cand_y.isin(idx_obs) & cand_prev.isin(idx_obs)
v_r6 = is0 & ~ok
issues["R6_target0_requires_observable_horizon"] = _sample(df_chk.loc[v_r6, [col_ticker, col_year, col_distress_year, col_target]])
# R7: recompute tahun_distress and compare (optional)
v_r7 = pd.Series(False, index=df_chk.index)
if recompute_distress_year:
recomputed_map = _recompute_distress_year(df_chk, distress_type) # Series index=ticker->year
if not recomputed_map.empty:
# map to rows
d2 = df_chk[col_ticker].map(recomputed_map)
# Compare: treat both as numeric, allow NA==NA
a = pd.to_numeric(df_chk[col_distress_year], errors="coerce")
b = pd.to_numeric(d2, errors="coerce")
v_r7 = ~(a.fillna(-999999).eq(b.fillna(-999999)))
else:
# if recomputed empty, then all years should be NA
a = pd.to_numeric(df_chk[col_distress_year], errors="coerce")
v_r7 = a.notna()
issues["R7_tahun_distress_mismatch_recomputed"] = _sample(df_chk.loc[v_r7, [col_ticker, col_year, col_distress_year, col_target]])
# summary counts
summary = dict(
distress_type=distress_type,
labeling_logic=labeling_logic,
range_years=r,
n_rows=len(df_chk),
n_target_1=int(is1.sum()),
n_target_0=int(is0.sum()),
n_target_na=int(isna.sum()),
n_R1=int(v_r1.sum()),
n_R2=int(v_r2.sum()),
n_R3=int(v_r3.sum()),
n_R4=int(v_r4.sum()),
n_R5=int(v_r5.sum()),
n_R6=int(v_r6.sum()),
n_R7=int(v_r7.sum()) if recompute_distress_year else 0,
)
# Keep only non-empty issue samples to reduce clutter
issues = {k: v for k, v in issues.items() if v is not None and len(v) > 0}
return summary, issues
summary_rows = []
all_issues = {}
for distress_type in distress_types:
for labeling_logic in labeling_logics:
for r in ranges:
cfg = (distress_type, labeling_logic, int(r))
if precomputed is not None and cfg in precomputed:
df_out = precomputed[cfg]
else:
# call labeling function
if distress_type == "ppk":
if df_emiten_idx is None:
raise ValueError("df_emiten_idx wajib disediakan untuk distress_type='ppk'")
df_out = labeling_fn(
df_findat_final=df_findat_final,
df_emiten_idx=df_emiten_idx,
range_years=int(r),
distress_type=distress_type,
labeling_logic=labeling_logic,
verbose=False,
)
else:
# labeling_fn saat ini butuh df_emiten_idx sebagai argumen juga
df_out = labeling_fn(
df_findat_final=df_findat_final,
df_emiten_idx=df_emiten_idx if df_emiten_idx is not None else pd.DataFrame({col_kode: []}),
range_years=int(r),
distress_type=distress_type,
labeling_logic=labeling_logic,
verbose=False,
)
summary, issues = _audit_single(df_out, distress_type, labeling_logic, int(r))
summary_rows.append(summary)
all_issues[cfg] = issues # selalu simpan, meskipun kosong {}
summary_df = pd.DataFrame(summary_rows).sort_values(["distress_type", "labeling_logic", "range_years"]).reset_index(drop=True)
return summary_df, all_issuessummary, issues = audit_distress_labeling_suite(
df_findat_final=df_findat_final,
df_emiten_idx=df_emiten_idx,
ranges=[1,2,3],
distress_types=["ppk","neg_equity","consecutive_loss"],
labeling_logics=["within","in_exactly"],
sample_rows=10,
recompute_distress_year=True
)
summary
# Kalau ada pelanggaran, lihat contohnya:
issues.keys()
cfg = ("neg_equity","in_exactly",2)
for cfg in issues.keys():
print(f"Cek konfigurasi : {cfg}")
if issues[cfg]:
print(f"Jumlah baris kesalahan: {len(issues[cfg])}")
display(issues[cfg])
else:
print("Tidak ada isu untuk konfigurasi ini.")Cek konfigurasi : ('ppk', 'within', 1)
Tidak ada isu untuk konfigurasi ini.
Cek konfigurasi : ('ppk', 'within', 2)
Tidak ada isu untuk konfigurasi ini.
Cek konfigurasi : ('ppk', 'within', 3)
Tidak ada isu untuk konfigurasi ini.
Cek konfigurasi : ('ppk', 'in_exactly', 1)
Tidak ada isu untuk konfigurasi ini.
Cek konfigurasi : ('ppk', 'in_exactly', 2)
Tidak ada isu untuk konfigurasi ini.
Cek konfigurasi : ('ppk', 'in_exactly', 3)
Tidak ada isu untuk konfigurasi ini.
Cek konfigurasi : ('neg_equity', 'within', 1)
Tidak ada isu untuk konfigurasi ini.
Cek konfigurasi : ('neg_equity', 'within', 2)
Tidak ada isu untuk konfigurasi ini.
Cek konfigurasi : ('neg_equity', 'within', 3)
Tidak ada isu untuk konfigurasi ini.
Cek konfigurasi : ('neg_equity', 'in_exactly', 1)
Tidak ada isu untuk konfigurasi ini.
Cek konfigurasi : ('neg_equity', 'in_exactly', 2)
Tidak ada isu untuk konfigurasi ini.
Cek konfigurasi : ('neg_equity', 'in_exactly', 3)
Tidak ada isu untuk konfigurasi ini.
Cek konfigurasi : ('consecutive_loss', 'within', 1)
Tidak ada isu untuk konfigurasi ini.
Cek konfigurasi : ('consecutive_loss', 'within', 2)
Tidak ada isu untuk konfigurasi ini.
Cek konfigurasi : ('consecutive_loss', 'within', 3)
Tidak ada isu untuk konfigurasi ini.
Cek konfigurasi : ('consecutive_loss', 'in_exactly', 1)
Tidak ada isu untuk konfigurasi ini.
Cek konfigurasi : ('consecutive_loss', 'in_exactly', 2)
Tidak ada isu untuk konfigurasi ini.
Cek konfigurasi : ('consecutive_loss', 'in_exactly', 3)
Tidak ada isu untuk konfigurasi ini.
issues.keys()dict_keys([('ppk', 'within', 1), ('ppk', 'within', 2), ('ppk', 'within', 3), ('ppk', 'in_exactly', 1), ('ppk', 'in_exactly', 2), ('ppk', 'in_exactly', 3), ('neg_equity', 'within', 1), ('neg_equity', 'within', 2), ('neg_equity', 'within', 3), ('neg_equity', 'in_exactly', 1), ('neg_equity', 'in_exactly', 2), ('neg_equity', 'in_exactly', 3), ('consecutive_loss', 'within', 1), ('consecutive_loss', 'within', 2), ('consecutive_loss', 'within', 3), ('consecutive_loss', 'in_exactly', 1), ('consecutive_loss', 'in_exactly', 2), ('consecutive_loss', 'in_exactly', 3)])
summary.query("distress_type=='neg_equity' and labeling_logic=='within' and range_years==2")| distress_type | labeling_logic | range_years | n_rows | n_target_1 | n_target_0 | n_target_na | n_R1 | n_R2 | n_R3 | n_R4 | n_R5 | n_R6 | n_R7 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 10 | neg_equity | within | 2 | 8489 | 116 | 6287 | 2086 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
df_integrated_ppk[(df_integrated_ppk['Year']<(2020)) & (df_integrated_ppk['target_distress'].notna())]| variable | ticker | Year | Entity Name | target_distress | tahun_distress | Total Equity (Rp.M) | Net Income to Company (Rp.M) | 1st Level Primary Industry | Year Established | Sector | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.M) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | IPO Year |
|---|
Cek hasil dataset integrated yang menggabungkan semua tipe distress.
Hasil harus sama dengan dataset yang tiap tipe distressnya dipisah.
print("Cek perbedaan antara dataset distress PPK yang dipisah dan digabung:")
pd.concat([df_integrated_ppk,
(df_integrated_all_indicators
.drop(columns=["target_distress_neg_equity", "target_distress_consecutive_loss",
"tahun_distress_neg_equity", "tahun_distress_consecutive_loss"], errors="ignore")
.rename(columns={"target_distress_ppk": "target_distress",
"tahun_distress_ppk": "tahun_distress"}))
]).drop_duplicates(keep=False)Cek perbedaan antara dataset distress PPK yang dipisah dan digabung:
| variable | ticker | Year | Entity Name | target_distress | tahun_distress | Total Equity (Rp.M) | Net Income to Company (Rp.M) | 1st Level Primary Industry | Year Established | Sector | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.M) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | IPO Year |
|---|
print("Cek perbedaan antara dataset distress ekuitas negatif yang dipisah dan digabung:")
pd.concat([df_integrated_neg_eq,
(df_integrated_all_indicators
.drop(columns=["target_distress_ppk", "target_distress_consecutive_loss",
"tahun_distress_ppk", "tahun_distress_consecutive_loss"], errors="ignore")
.rename(columns={"target_distress_neg_equity": "target_distress",
"tahun_distress_neg_equity": "tahun_distress"}))
]).drop_duplicates(keep=False)Cek perbedaan antara dataset distress ekuitas negatif yang dipisah dan digabung:
| variable | ticker | Year | Entity Name | target_distress | tahun_distress | Total Equity (Rp.M) | Net Income to Company (Rp.M) | 1st Level Primary Industry | Year Established | Sector | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.M) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | IPO Year |
|---|
print("Cek perbedaan antara dataset distress rugi 2 tahun yang dipisah dan digabung:")
pd.concat([df_integrated_2_loss,
(df_integrated_all_indicators
.drop(columns=["target_distress_neg_equity", "target_distress_ppk",
"tahun_distress_neg_equity", "tahun_distress_ppk"], errors="ignore")
.rename(columns={"target_distress_consecutive_loss": "target_distress",
"tahun_distress_consecutive_loss": "tahun_distress"}))
]).drop_duplicates(keep=False)Cek perbedaan antara dataset distress rugi 2 tahun yang dipisah dan digabung:
| variable | ticker | Year | Entity Name | target_distress | tahun_distress | Total Equity (Rp.M) | Net Income to Company (Rp.M) | 1st Level Primary Industry | Year Established | Sector | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.M) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | IPO Year |
|---|
Pilih dan Copy Hasil Dataset Terintegrasi
# df_findat_integrated = df_integrated_ppk.copy()
# df_findat_integrated = df_integrated_neg_eq.copy()
# df_findat_integrated = df_integrated_2_loss.copy()
df_findat_integrated = df_integrated_all_indicators.copy()# # temporary, cuma utk liat output kode2 setelahnya
# df_findat_integrated = df_integrated_ppk[df_integrated_ppk['target_distress'].notna()].copy()
# df_findat_integrated = df_findat_integrated.rename(columns={'target_distress': 'distress'})Data Understanding
Sebaran Data
df_findat_integrated.info()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 8489 entries, 0 to 8488
Data columns (total 46 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 ticker 8489 non-null object
1 Year 8489 non-null Int64
2 Entity Name 8489 non-null object
3 Total Equity (Rp.M) 8489 non-null float64
4 Net Income to Company (Rp.M) 8489 non-null float64
5 target_distress_consecutive_loss 5160 non-null Int64
6 tahun_distress_consecutive_loss 4169 non-null Int64
7 target_distress_neg_equity 6403 non-null Int64
8 tahun_distress_neg_equity 1073 non-null Int64
9 target_distress_ppk 2583 non-null Int64
10 tahun_distress_ppk 3421 non-null Int64
11 1st Level Primary Industry 8489 non-null object
12 Year Established 8468 non-null Int64
13 Sector 8489 non-null object
14 Industry Group 8489 non-null object
15 Parent Percent Owned (%) 4848 non-null float64
16 Percent Owned - All Institutions (%) 4542 non-null float64
17 Percent Owned - Insiders (%) 5938 non-null float64
18 Total Liabilities (Rp.M) 8489 non-null float64
19 Total Assets (Rp.M) 8489 non-null float64
20 Current Ratio (x) 7783 non-null float64
21 Quick Ratio (x) 7785 non-null float64
22 Working Capital (Rp.M) 8489 non-null float64
23 Total Current Assets (Rp.M) 8489 non-null float64
24 Total Current Liabilities (Rp.M) 8489 non-null float64
25 Inventory (Rp.M) 7283 non-null float64
26 Prepaid Exp. (Rp.M) 7027 non-null float64
27 Long-term Debt (Rp.M) 5908 non-null float64
28 Short-term Borrowings (Rp.M) 5579 non-null float64
29 Current Portion of LT Debt & Leases (Rp.M) 337 non-null float64
30 Total Debt (Rp.M) 8468 non-null float64
31 Net Property, Plant & Equipment (Rp.M) 8470 non-null float64
32 Cost Of Goods Sold (Rp.M) 8309 non-null float64
33 Total Revenue (Rp.M) 8458 non-null float64
34 Operating Income (Rp.M) 8488 non-null float64
35 EBITDA (Rp.M) 7383 non-null float64
36 EBIT (Rp.M) 7390 non-null float64
37 Cash from Ops. (Rp.M) 8487 non-null float64
38 Net Change in Cash (Rp.M) 8487 non-null float64
39 ECS Total Common Shares Outstanding (actual) 8488 non-null float64
40 Market Capitalization (Rp.M) 8464 non-null float64
41 Year Close Stock Price (Rp.) 8464 non-null float64
42 Total Capital (Rp.M) 8468 non-null float64
43 Cash & Short-term Investments (Rp.M) 8489 non-null float64
44 Net Intangibles (Rp.M) 1763 non-null float64
45 IPO Year 8489 non-null Int64
dtypes: Int64(9), float64(32), object(5)
memory usage: 3.1+ MB
# =========================
# SETUP KOLOM (df_findat_integrated)
# =========================
df = df_findat_integrated
# Exclude:
# - ticker, Year, Entity Name
# - kolom awalan 'tahun' atau 'target'
exclude_exact = {'ticker', 'Year', 'Entity Name'}
exclude_prefixes = ('tahun', 'target')
cols_to_exclude = {
c for c in df.columns
if (c in exclude_exact) or c.startswith(exclude_prefixes)
}
potential_cols = [c for c in df.columns if c not in cols_to_exclude]
# kategorikal khusus
cat_cols = ['1st Level Primary Industry', 'Sector', 'Industry Group']
cat_cols = [c for c in cat_cols if c in df.columns]
# numerik selain kolom kategorikal
numeric_cols = [
c for c in potential_cols
if (c not in cat_cols) and pd.api.types.is_numeric_dtype(df[c])
]
print(f"Total Baris Data: {len(df)}")
print(f"Fitur Numerik: {len(numeric_cols)}")
print(f"Fitur Kategorikal (pie): {len(cat_cols)} -> {cat_cols}")
N = len(df)
# =========================
# VISUALISASI NUMERIK (Boxplot + Hist) - format sama persis
# =========================
features_per_page = 5
num_batches = math.ceil(len(numeric_cols) / features_per_page)
MAX_POINTS_PLOT = 80_000
MAX_POINTS_KDE = 8_000
HIST_BINS = 50
for i in range(num_batches):
start = i * features_per_page
end = min((i + 1) * features_per_page, len(numeric_cols))
batch = numeric_cols[start:end]
fig, axes = plt.subplots(len(batch), 2, figsize=(16, 4 * len(batch)))
fig.suptitle(f'Distribusi Numerik Part {i+1}', fontsize=16, fontweight='bold', y=1.01)
if len(batch) == 1:
axes = [axes]
for idx, col in enumerate(batch):
ax_box = axes[idx][0]
ax_hist = axes[idx][1]
try:
s_raw = df[col]
# hitung null (tanpa mengubah df)
null_count = int(s_raw.isna().sum())
null_prop = null_count / N if N else 0.0
# bersihkan untuk plotting: inf -> nan, coerce numeric, dropna sementara
s_clean = (
s_raw.replace([np.inf, -np.inf], np.nan)
.pipe(pd.to_numeric, errors="coerce")
.dropna()
)
if s_clean.empty:
msg = f"ALL NULL/INF | Null: {null_count} ({null_prop:.1%})"
ax_box.set_title(f"Outlier Check: {col}", fontsize=10, fontweight='bold')
ax_box.text(0.5, 0.5, msg, ha='center', va='center')
ax_hist.set_title(msg, fontsize=10)
ax_hist.text(0.5, 0.5, "No data to plot", ha='center', va='center')
continue
# sampling agar tidak berat
s_plot = s_clean.sample(MAX_POINTS_PLOT, random_state=42) if len(s_clean) > MAX_POINTS_PLOT else s_clean
# statistik (dari non-null)
desc = s_plot.describe()
# skew = s_plot.skew()
stats_text = (
f"Min: {desc['min']:.2f} | Max: {desc['max']:.2f} | "
f"Mean: {desc['mean']:.2f} | Med: {desc['50%']:.2f} | "
# f"Skew: {skew:.2f} | "
f"Null: {null_count} ({null_prop:.1%})"
)
# boxplot
sns.boxplot(x=s_plot, ax=ax_box, color='#3498db')
ax_box.set_title(f"Outlier Check: {col}", fontsize=10, fontweight='bold')
ax_box.set_xlabel('')
# hist (+ KDE dibatasi agar tidak hang)
use_kde = (s_plot.nunique() > 1) and (len(s_plot) <= MAX_POINTS_KDE)
sns.histplot(s_plot, bins=HIST_BINS, kde=use_kde, ax=ax_hist, color='#e67e22', stat='density')
ax_hist.axvline(desc['mean'], color='red', linestyle='--', label='Mean')
ax_hist.axvline(desc['50%'], color='green', linestyle='-', label='Median')
ax_hist.set_title(stats_text, fontsize=10)
ax_hist.legend(loc='upper right', fontsize='small')
ax_hist.set_xlabel('')
except Exception as e:
err = f"{type(e).__name__}: {e}"
ax_box.clear()
ax_hist.clear()
ax_box.set_title(f"Outlier Check: {col}", fontsize=10, fontweight='bold')
ax_box.text(0.5, 0.5, f"Error:\n{err}", ha='center', va='center', wrap=True)
ax_hist.set_title(f"Error on: {col}", fontsize=10)
ax_hist.text(0.5, 0.5, f"Error:\n{err}", ha='center', va='center', wrap=True)
plt.tight_layout()
plt.show()
plt.close(fig)
plt.close('all')
# =========================
# VISUALISASI KATEGORIKAL (Pie Chart)
# =========================
# - tampilkan jumlah + persentase
# - termasuk NaN kalau ada
# - diasumsikan nunique <= 30 (sesuai requirement)
if len(cat_cols) > 0:
for col in cat_cols:
s = df[col]
# value_counts termasuk NaN
vc = s.value_counts(dropna=False)
# label NaN agar terlihat
labels = [("NaN" if pd.isna(x) else str(x)) for x in vc.index]
sizes = vc.values
total = sizes.sum()
# autopct yang menampilkan count + percent
def autopct_count_pct(pct):
count = int(round(pct * total / 100.0))
return f"{count}\n({pct:.1f}%)"
fig, ax = plt.subplots(figsize=(10, 7))
wedges, texts, autotexts = ax.pie(
sizes,
labels=labels,
autopct=autopct_count_pct,
startangle=90,
textprops={'fontsize': 10}
)
ax.set_title(f"{col} | Total: {total} | Null: {int(s.isna().sum())} ({(s.isna().mean() if total else 0):.1%})",
fontsize=14, fontweight='bold')
ax.axis('equal') # pie jadi bulat
plt.tight_layout()
plt.show()
plt.close(fig)
plt.close('all')Total Baris Data: 8489
Fitur Numerik: 34
Fitur Kategorikal (pie): 3 -> ['1st Level Primary Industry', 'Sector', 'Industry Group']










cat_cols = ['1st Level Primary Industry', 'Sector', 'Industry Group']
df = df_findat_integrated[cat_cols].copy().fillna("NaN")
# urutkan kolom: unik paling sedikit -> paling banyak (kiri -> kanan)
order = sorted(cat_cols, key=lambda c: df[c].nunique(dropna=False))
left_col, mid_col, right_col = order
# urutkan kategori kiri dari count terbesar
left_cats = df[left_col].value_counts(dropna=False).index.tolist()
# untuk level lain: biarkan urut by frequency juga (rapi, simple)
mid_cats = df[mid_col].value_counts(dropna=False).index.tolist()
right_cats = df[right_col].value_counts(dropna=False).index.tolist()
def node_id(col, val): return f"{col}||{val}" # unik (internal)
def node_label(val): return str(val) # yang ditampilkan (tanpa nama kolom)
node_ids = (
[node_id(left_col, v) for v in left_cats] +
[node_id(mid_col, v) for v in mid_cats] +
[node_id(right_col, v) for v in right_cats]
)
node_labels = (
[node_label(v) for v in left_cats] +
[node_label(v) for v in mid_cats] +
[node_label(v) for v in right_cats]
)
id_to_index = {nid: i for i, nid in enumerate(node_ids)}
# hitung flow kiri->tengah dan tengah->kanan
flow_lm = df.groupby([left_col, mid_col]).size().reset_index(name="value")
flow_mr = df.groupby([mid_col, right_col]).size().reset_index(name="value")
sources, targets, values = [], [], []
for _, r in flow_lm.iterrows():
sources.append(id_to_index[node_id(left_col, r[left_col])])
targets.append(id_to_index[node_id(mid_col, r[mid_col])])
values.append(int(r["value"]))
for _, r in flow_mr.iterrows():
sources.append(id_to_index[node_id(mid_col, r[mid_col])])
targets.append(id_to_index[node_id(right_col, r[right_col])])
values.append(int(r["value"]))
fig = go.Figure(
data=[go.Sankey(
arrangement="snap",
node=dict(
label=node_labels,
pad=12,
thickness=16
),
link=dict(
source=sources,
target=targets,
value=values
)
)]
)
fig.update_layout(
# title=f"{left_col} → {mid_col} → {right_col} (n={len(df)})",
title={
"text": "Alur Sankey Diagram Kolom Sektor/Industri per BARIS DATA",
"x": 0.5, # center
"xanchor": "center"
},
font_size=11,
height=1100, # perpanjang ke bawah supaya lebih lega
annotations=[
dict(x=0.02, y=1.03, xref="paper", yref="paper",
text=f"<b>{left_col}</b>", showarrow=False, xanchor="left"),
dict(x=0.50, y=1.03, xref="paper", yref="paper",
text=f"<b>{mid_col}</b>", showarrow=False, xanchor="center"),
dict(x=0.98, y=1.03, xref="paper", yref="paper",
text=f"<b>{right_col}</b>", showarrow=False, xanchor="right"),
]
)
# fig.show(renderer="notebook")
fig.show(renderer="svg")Analisis dan Eksplorasi Data
pd.set_option('display.max_columns', None)
pd.set_option('display.width', 220)
sns.set_theme(style='whitegrid')
df = df_findat_integrated.copy()
# DEFINISI KELOMPOK KOLOM
# ---------------------------------------------------------------------
id_cols = ['ticker', 'Year', 'Entity Name']
categorical_cols = ['1st Level Primary Industry', 'Sector', 'Industry Group']
date_year_cols = ['Date Established', 'Year Established', 'IPO Year']
ownership_cols = [
'Parent Percent Owned (%)',
'Percent Owned - All Institutions (%)',
'Percent Owned - Insiders (%)',
]
target_cols = [
'target_distress_ppk',
'target_distress_neg_equity',
'target_distress_consecutive_loss',
]
year_distress_cols = [
'tahun_distress_ppk',
'tahun_distress_neg_equity',
'tahun_distress_consecutive_loss',
]# Kolom akun keuangan numerik = seluruh kolom numerik di luar kelompok di atas
non_account = set(id_cols + categorical_cols + date_year_cols +
target_cols + year_distress_cols + ownership_cols)
account_cols = [
c for c in df.columns
if c not in non_account and pd.api.types.is_numeric_dtype(df[c])
]
print("Jumlah kolom akun keuangan numerik:", len(account_cols))
print(account_cols)Jumlah kolom akun keuangan numerik: 29
['Total Equity (Rp.M)', 'Net Income to Company (Rp.M)', 'Total Liabilities (Rp.M)', 'Total Assets (Rp.M)', 'Current Ratio (x)', 'Quick Ratio (x)', 'Working Capital (Rp.M)', 'Total Current Assets (Rp.M)', 'Total Current Liabilities (Rp.M)', 'Inventory (Rp.M)', 'Prepaid Exp. (Rp.M)', 'Long-term Debt (Rp.M)', 'Short-term Borrowings (Rp.M)', 'Current Portion of LT Debt & Leases (Rp.M)', 'Total Debt (Rp.M)', 'Net Property, Plant & Equipment (Rp.M)', 'Cost Of Goods Sold (Rp.M)', 'Total Revenue (Rp.M)', 'Operating Income (Rp.M)', 'EBITDA (Rp.M)', 'EBIT (Rp.M)', 'Cash from Ops. (Rp.M)', 'Net Change in Cash (Rp.M)', 'ECS Total Common Shares Outstanding (actual)', 'Market Capitalization (Rp.M)', 'Year Close Stock Price (Rp.)', 'Total Capital (Rp.M)', 'Cash & Short-term Investments (Rp.M)', 'Net Intangibles (Rp.M)']
Gambaran Umum Dataset
# GAMBARAN UMUM DATASET
# ---------------------------------------------------------------------
print("Dimensi dataset :", df.shape)
print("Jumlah perusahaan unik:", df['ticker'].nunique())
print("Rentang tahun fiskal :", int(df['Year'].min()), "-", int(df['Year'].max()))
print("\nJumlah baris per tahun fiskal:")
print(df['Year'].value_counts().sort_index())
print("\nInfo tipe data:")
df.info()Dimensi dataset : (8489, 46)
Jumlah perusahaan unik: 853
Rentang tahun fiskal : 2008 - 2024
Jumlah baris per tahun fiskal:
Year
2008 302
2009 315
2010 325
2011 344
2012 367
2013 392
2014 417
2015 441
2016 460
2017 475
2018 508
2019 559
2020 611
2021 661
2022 715
2023 765
2024 832
Name: count, dtype: Int64
Info tipe data:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 8489 entries, 0 to 8488
Data columns (total 46 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 ticker 8489 non-null object
1 Year 8489 non-null Int64
2 Entity Name 8489 non-null object
3 Total Equity (Rp.M) 8489 non-null float64
4 Net Income to Company (Rp.M) 8489 non-null float64
5 target_distress_consecutive_loss 5160 non-null Int64
6 tahun_distress_consecutive_loss 4169 non-null Int64
7 target_distress_neg_equity 6403 non-null Int64
8 tahun_distress_neg_equity 1073 non-null Int64
9 target_distress_ppk 2583 non-null Int64
10 tahun_distress_ppk 3421 non-null Int64
11 1st Level Primary Industry 8489 non-null object
12 Year Established 8468 non-null Int64
13 Sector 8489 non-null object
14 Industry Group 8489 non-null object
15 Parent Percent Owned (%) 4848 non-null float64
16 Percent Owned - All Institutions (%) 4542 non-null float64
17 Percent Owned - Insiders (%) 5938 non-null float64
18 Total Liabilities (Rp.M) 8489 non-null float64
19 Total Assets (Rp.M) 8489 non-null float64
20 Current Ratio (x) 7783 non-null float64
21 Quick Ratio (x) 7785 non-null float64
22 Working Capital (Rp.M) 8489 non-null float64
23 Total Current Assets (Rp.M) 8489 non-null float64
24 Total Current Liabilities (Rp.M) 8489 non-null float64
25 Inventory (Rp.M) 7283 non-null float64
26 Prepaid Exp. (Rp.M) 7027 non-null float64
27 Long-term Debt (Rp.M) 5908 non-null float64
28 Short-term Borrowings (Rp.M) 5579 non-null float64
29 Current Portion of LT Debt & Leases (Rp.M) 337 non-null float64
30 Total Debt (Rp.M) 8468 non-null float64
31 Net Property, Plant & Equipment (Rp.M) 8470 non-null float64
32 Cost Of Goods Sold (Rp.M) 8309 non-null float64
33 Total Revenue (Rp.M) 8458 non-null float64
34 Operating Income (Rp.M) 8488 non-null float64
35 EBITDA (Rp.M) 7383 non-null float64
36 EBIT (Rp.M) 7390 non-null float64
37 Cash from Ops. (Rp.M) 8487 non-null float64
38 Net Change in Cash (Rp.M) 8487 non-null float64
39 ECS Total Common Shares Outstanding (actual) 8488 non-null float64
40 Market Capitalization (Rp.M) 8464 non-null float64
41 Year Close Stock Price (Rp.) 8464 non-null float64
42 Total Capital (Rp.M) 8468 non-null float64
43 Cash & Short-term Investments (Rp.M) 8489 non-null float64
44 Net Intangibles (Rp.M) 1763 non-null float64
45 IPO Year 8489 non-null Int64
dtypes: Int64(9), float64(32), object(5)
memory usage: 3.1+ MB
Statistik Deskriptif
# STATISTIK DESKRIPTIF
# ---------------------------------------------------------------------
# Statistik akun keuangan + ukuran kemiringan (skewness) untuk melihat outlier/skew
desc = df[account_cols].describe().T
desc['skew'] = df[account_cols].skew()
print("Statistik deskriptif kolom akun keuangan:")
display(desc)
print("\nStatistik deskriptif kolom kepemilikan (%):")
display(df[ownership_cols].describe().T)Statistik deskriptif kolom akun keuangan:
| count | mean | std | min | 25% | 50% | 75% | max | skew | |
|---|---|---|---|---|---|---|---|---|---|
| variable | |||||||||
| Total Equity (Rp.M) | 8489.000000 | 5533269.486397 | 18365513.535013 | -87050020.759605 | 273118.240000 | 1041363.576000 | 3799002.091000 | 323189047.000000 | 9.005794 |
| Net Income to Company (Rp.M) | 8489.000000 | 636614.144596 | 3546958.614528 | -90518726.000000 | 430.230000 | 42459.356000 | 301507.000000 | 61165121.000000 | 4.953522 |
| Total Liabilities (Rp.M) | 8489.000000 | 14365855.735244 | 83669335.745741 | 105.469000 | 250001.554000 | 1040515.029000 | 4483434.000000 | 2113748581.000000 | 13.740723 |
| Total Assets (Rp.M) | 8489.000000 | 19900305.166074 | 99115652.675446 | 64.531000 | 660311.259000 | 2342166.844000 | 8664310.151000 | 2427223262.000000 | 13.246400 |
| Current Ratio (x) | 7783.000000 | 4.063096 | 14.353216 | 0.000000 | 1.037000 | 1.586000 | 2.857000 | 276.365000 | 10.952540 |
| Quick Ratio (x) | 7785.000000 | 2.903037 | 13.437101 | 0.000000 | 0.434000 | 0.880000 | 1.742000 | 297.354000 | 12.758758 |
| Working Capital (Rp.M) | 8489.000000 | -5792751.533121 | 58173059.258148 | -1447507351.000000 | -26159.942214 | 133714.513000 | 772744.000000 | 63082588.680000 | -14.131973 |
| Total Current Assets (Rp.M) | 8489.000000 | 5552412.025094 | 19778922.730145 | 14.919000 | 257225.476000 | 949464.000000 | 3265410.243732 | 359147736.000000 | 10.001406 |
| Total Current Liabilities (Rp.M) | 8489.000000 | 11345163.558216 | 75324978.564407 | 1.110000 | 150305.093000 | 607793.174262 | 2543062.000000 | 1789344987.000000 | 13.654257 |
| Inventory (Rp.M) | 7283.000000 | 998161.513457 | 2866979.425961 | 0.000000 | 42322.937000 | 213830.125000 | 771717.868103 | 47739541.000000 | 8.077677 |
| Prepaid Exp. (Rp.M) | 7027.000000 | 106623.493042 | 514971.576669 | 0.028000 | 1174.321000 | 5660.100000 | 28173.587500 | 10906161.000000 | 9.480459 |
| Long-term Debt (Rp.M) | 5908.000000 | 2737886.565554 | 7386712.479530 | 5.000000 | 55077.386890 | 363538.872500 | 1838513.763017 | 109128071.000000 | 5.578868 |
| Short-term Borrowings (Rp.M) | 5579.000000 | 949371.219869 | 3102496.051113 | 0.253000 | 35626.251000 | 150000.000000 | 601172.524500 | 116140873.000000 | 13.437665 |
| Current Portion of LT Debt & Leases (Rp.M) | 337.000000 | 4794345.240881 | 13616851.799595 | 9.000000 | 101826.000000 | 981859.000000 | 4294254.000000 | 125043760.000000 | 5.908768 |
| Total Debt (Rp.M) | 8468.000000 | 3305024.447430 | 10654043.662490 | 0.000000 | 42059.531000 | 348717.826000 | 1879074.686445 | 298631967.000000 | 9.208874 |
| Net Property, Plant & Equipment (Rp.M) | 8470.000000 | 3216671.766077 | 10052754.873905 | 0.000000 | 95125.648500 | 456350.858500 | 2023958.363500 | 207476000.000000 | 8.534967 |
| Cost Of Goods Sold (Rp.M) | 8309.000000 | 3667029.770752 | 10817107.827832 | -76682.746564 | 103930.792000 | 625488.781000 | 2637254.308000 | 242444000.000000 | 9.336591 |
| Total Revenue (Rp.M) | 8458.000000 | 5632032.500394 | 15745437.066905 | -10991269.000000 | 247222.537750 | 1122399.420500 | 4206282.250000 | 330920000.000000 | 8.205025 |
| Operating Income (Rp.M) | 8488.000000 | 937616.844427 | 3934202.484944 | -27748197.047589 | 4897.785075 | 81820.979500 | 455310.152481 | 76194365.000000 | 9.010808 |
| EBITDA (Rp.M) | 7383.000000 | 1104429.396194 | 4236552.760944 | -16905265.101372 | 18402.918000 | 143412.780010 | 630543.853500 | 88404432.185000 | 10.105122 |
| EBIT (Rp.M) | 7390.000000 | 756315.294702 | 3026788.571486 | -27748197.047589 | 4090.300000 | 78170.976573 | 430061.987958 | 63300052.352331 | 8.750251 |
| Cash from Ops. (Rp.M) | 8487.000000 | 193650.044304 | 6030080.153494 | -208081950.000000 | -6481.723000 | 44403.802000 | 364037.821000 | 73354000.000000 | -9.426745 |
| Net Change in Cash (Rp.M) | 8487.000000 | 124838.588478 | 3250552.461486 | -65116261.000000 | -33438.422987 | 700.148000 | 64222.911500 | 114267567.000000 | 9.234775 |
| ECS Total Common Shares Outstanding (actual) | 8488.000000 | 9892981510.426485 | 24765778836.124523 | 283500.000000 | 1137579698.000000 | 3415271200.000000 | 9696291166.000000 | 1070558449408.000000 | 20.680081 |
| Market Capitalization (Rp.M) | 8464.000000 | 34619318.249208 | 1062974538.590965 | 5549.999700 | 305968.304052 | 1246700.197998 | 5292261.822750 | 50060622505.000000 | 45.942635 |
| Year Close Stock Price (Rp.) | 8464.000000 | 8379.534973 | 188130.954536 | 2.000000 | 130.000000 | 346.342933 | 1000.000000 | 5000000.000000 | 26.491285 |
| Total Capital (Rp.M) | 8468.000000 | 8851445.762053 | 27309273.207565 | -1401798.980000 | 468295.568750 | 1610557.023196 | 5967422.430000 | 612106648.000000 | 9.316133 |
| Cash & Short-term Investments (Rp.M) | 8489.000000 | 2498566.689343 | 11510112.520983 | 2.092000 | 27901.282000 | 169876.793000 | 920131.000000 | 271881498.000000 | 11.131815 |
| Net Intangibles (Rp.M) | 1763.000000 | 927546.615175 | 6573842.548758 | 0.197000 | 1497.722000 | 11922.000000 | 92345.264500 | 123032740.000000 | 12.089476 |
Statistik deskriptif kolom kepemilikan (%):
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| variable | ||||||||
| Parent Percent Owned (%) | 4848.000000 | 66.363779 | 17.788215 | 1.010000 | 54.892500 | 65.140000 | 80.000000 | 100.000000 |
| Percent Owned - All Institutions (%) | 4542.000000 | 8.113311 | 12.933426 | 0.000000 | 0.460000 | 4.380000 | 10.570000 | 99.350000 |
| Percent Owned - Insiders (%) | 5938.000000 | 13.472383 | 21.880073 | 0.000000 | 0.090000 | 2.800000 | 15.720000 | 92.500000 |
Analisis Missing Value
# ANALISIS NILAI KOSONG (MISSING VALUE)
# ---------------------------------------------------------------------
null_summary = pd.DataFrame({
'Null Count': df.isnull().sum(),
'Null %': (df.isnull().mean() * 100).round(2)
}).sort_values('Null %', ascending=False)
print("Ringkasan nilai kosong per kolom:")
display(null_summary)
# Visualisasi hanya untuk kolom yang punya nilai kosong
nz = null_summary[null_summary['Null Count'] > 0].reset_index(names='Kolom')
# nz = nz.rename(columns={'index': 'Kolom'})
plt.figure(figsize=(10, max(4, 0.32 * len(nz))))
sns.barplot(x='Null %', y='Kolom', data=nz, color='steelblue')
plt.title('Persentase Nilai Kosong per Kolom (df_findat_integrated)')
plt.xlabel('Persentase Kosong (%)')
plt.tight_layout()
plt.show()Ringkasan nilai kosong per kolom:
| Null Count | Null % | |
|---|---|---|
| variable | ||
| Current Portion of LT Debt & Leases (Rp.M) | 8152 | 96.030000 |
| tahun_distress_neg_equity | 7416 | 87.360000 |
| Net Intangibles (Rp.M) | 6726 | 79.230000 |
| target_distress_ppk | 5906 | 69.570000 |
| tahun_distress_ppk | 5068 | 59.700000 |
| tahun_distress_consecutive_loss | 4320 | 50.890000 |
| Percent Owned - All Institutions (%) | 3947 | 46.500000 |
| Parent Percent Owned (%) | 3641 | 42.890000 |
| target_distress_consecutive_loss | 3329 | 39.220000 |
| Short-term Borrowings (Rp.M) | 2910 | 34.280000 |
| Long-term Debt (Rp.M) | 2581 | 30.400000 |
| Percent Owned - Insiders (%) | 2551 | 30.050000 |
| target_distress_neg_equity | 2086 | 24.570000 |
| Prepaid Exp. (Rp.M) | 1462 | 17.220000 |
| Inventory (Rp.M) | 1206 | 14.210000 |
| EBITDA (Rp.M) | 1106 | 13.030000 |
| EBIT (Rp.M) | 1099 | 12.950000 |
| Current Ratio (x) | 706 | 8.320000 |
| Quick Ratio (x) | 704 | 8.290000 |
| Cost Of Goods Sold (Rp.M) | 180 | 2.120000 |
| Total Revenue (Rp.M) | 31 | 0.370000 |
| Year Close Stock Price (Rp.) | 25 | 0.290000 |
| Market Capitalization (Rp.M) | 25 | 0.290000 |
| Total Debt (Rp.M) | 21 | 0.250000 |
| Total Capital (Rp.M) | 21 | 0.250000 |
| Year Established | 21 | 0.250000 |
| Net Property, Plant & Equipment (Rp.M) | 19 | 0.220000 |
| Net Change in Cash (Rp.M) | 2 | 0.020000 |
| Cash from Ops. (Rp.M) | 2 | 0.020000 |
| Operating Income (Rp.M) | 1 | 0.010000 |
| ECS Total Common Shares Outstanding (actual) | 1 | 0.010000 |
| Sector | 0 | 0.000000 |
| Total Equity (Rp.M) | 0 | 0.000000 |
| 1st Level Primary Industry | 0 | 0.000000 |
| Year | 0 | 0.000000 |
| Entity Name | 0 | 0.000000 |
| Net Income to Company (Rp.M) | 0 | 0.000000 |
| ticker | 0 | 0.000000 |
| Total Current Assets (Rp.M) | 0 | 0.000000 |
| Working Capital (Rp.M) | 0 | 0.000000 |
| Total Current Liabilities (Rp.M) | 0 | 0.000000 |
| Total Liabilities (Rp.M) | 0 | 0.000000 |
| Total Assets (Rp.M) | 0 | 0.000000 |
| Industry Group | 0 | 0.000000 |
| Cash & Short-term Investments (Rp.M) | 0 | 0.000000 |
| IPO Year | 0 | 0.000000 |

# Kolom fitur numerik (sesuaikan bila perlu)
non_feature = {
'ticker', 'Year', 'Entity Name', 'Industry Group',
'target_distress_ppk', 'tahun_distress_ppk',
'target_distress_neg_equity', 'tahun_distress_neg_equity',
'target_distress_consecutive_loss', 'tahun_distress_consecutive_loss'
}
num_feature_cols = [c for c in df.columns
if c not in non_feature
and pd.api.types.is_numeric_dtype(df[c])
and not c.startswith('industry_')]
# Hanya fitur dengan null cukup berarti, agar heatmap ringkas
null_share = df[num_feature_cols].isnull().mean()
features = null_share[null_share > 0.002].sort_values(ascending=False).index.tolist()
# --- Heatmap 1: persentase null tiap fitur di tiap sektor ---
heat_sector = pd.DataFrame({
c: df.groupby('Industry Group')[c].apply(lambda s: s.isnull().mean() * 100)
for c in features
}).T # baris = fitur, kolom = sektor
plt.figure(figsize=(14, 9))
sns.heatmap(heat_sector, cmap='Reds', linewidths=0.3,
cbar_kws={'label': 'Persentase Nilai Kosong dalam Sektor (%)'})
plt.title('Persentase Nilai Kosong Tiap Fitur di Tiap Sektor')
plt.xlabel('Industry Group'); plt.ylabel('Fitur')
plt.tight_layout(); plt.show()
# --- Heatmap 2: persentase null tiap fitur per tahun ---
heat_year = pd.DataFrame({
c: df.groupby('Year')[c].apply(lambda s: s.isnull().mean() * 100)
for c in features
}).T
plt.figure(figsize=(12, 9))
sns.heatmap(heat_year, cmap='Blues', linewidths=0.3,
cbar_kws={'label': 'Persentase Nilai Kosong per Tahun (%)'})
plt.title('Persentase Nilai Kosong Tiap Fitur per Tahun Fiskal')
plt.xlabel('Tahun'); plt.ylabel('Fitur')
plt.tight_layout(); plt.show()

Distribusi Kolom Numerik
# DISTRIBUSI NILAI KOLOM NUMERIK (HISTOGRAM)
# ---------------------------------------------------------------------
# data keuangan umumnya sangat skewed & banyak outlier.
# Histogram skala asli memperlihatkan kemiringan; aktifkan log jika perlu.
dist_cols = account_cols + ownership_cols
ncols = 4
nrows = int(np.ceil(len(dist_cols) / ncols))
fig, axes = plt.subplots(nrows, ncols, figsize=(4 * ncols, 3 * nrows))
for ax, col in zip(axes.flatten(), dist_cols):
data = df[col].dropna()
ax.hist(data, bins=50, color='steelblue')
ax.set_title(col, fontsize=8)
ax.tick_params(labelsize=7)
for ax in axes.flatten()[len(dist_cols):]:
ax.axis('off')
fig.suptitle('Distribusi Nilai Kolom Numerik', y=1.002, fontsize=12)
plt.tight_layout()
plt.show()
dist_cols = account_cols + ownership_cols
ncols = 4
nrows = int(np.ceil(len(dist_cols) / ncols))
fig, axes = plt.subplots(nrows, ncols, figsize=(4 * ncols, 3 * nrows))
for ax, col in zip(axes.flatten(), dist_cols):
data = df[col].dropna()
ax.boxplot(data, vert=False,
patch_artist=True,
boxprops=dict(facecolor='steelblue', color='steelblue'),
medianprops=dict(color='black'))
ax.set_title(col, fontsize=8)
ax.tick_params(labelsize=7)
for ax in axes.flatten()[len(dist_cols):]:
ax.axis('off')
fig.suptitle('Distribusi Nilai Kolom Numerik', y=1.002, fontsize=12)
plt.tight_layout()
plt.show()
Distribusi Kolom Kategorikal
# DISTRIBUSI KOLOM KATEGORIKAL (SEKTOR / INDUSTRI)
# ---------------------------------------------------------------------
for col in categorical_cols:
vc = df[col].value_counts()
print(f"\n=== {col} ({df[col].nunique()} kategori) ===")
print(vc)
plt.figure(figsize=(9, max(3, 0.32 * len(vc))))
sns.barplot(x=vc.values, y=vc.index, color='teal')
plt.title(f'Distribusi Jumlah Baris per {col}')
plt.xlabel('Jumlah Baris')
plt.tight_layout()
plt.show()
=== 1st Level Primary Industry (8 kategori) ===
1st Level Primary Industry
Consumer 2210
Industrials 1428
Financials 1318
Materials 1019
Real Estate 871
Energy and Utilities 730
Technology, Media & Telecommunications 638
Health Care 275
Name: count, dtype: int64

=== Sector (11 kategori) ===
Sector
Industrials 1445
Financials 1331
Consumer Discretionary 1136
Consumer Staples 1062
Materials 1019
Real Estate 854
Energy 665
Communication Services 462
Health Care 275
Information Technology 175
Utilities 65
Name: count, dtype: int64

=== Industry Group (23 kategori) ===
Industry Group
Materials 1019
Real Estate Management and Development 854
Food, Beverage and Tobacco 802
Capital Goods 769
Energy 665
Banks 635
Transportation 544
Financial Services 419
Consumer Services 360
Consumer Durables and Apparel 323
Consumer Discretionary Distribution and Retail 322
Insurance 277
Media and Entertainment 254
Telecommunication Services 208
Consumer Staples Distribution and Retail 159
Health Care Equipment and Services 146
Commercial and Professional Services 132
Automobiles and Components 131
Pharmaceuticals, Biotechnology and Life Sciences 129
Household and Personal Products 101
Technology Hardware and Equipment 93
Software and Services 82
Utilities 65
Name: count, dtype: int64

Riwayat Tahun Data Perusahaan
# CAKUPAN TEMPORAL PERUSAHAAN
# ---------------------------------------------------------------------
comp_per_year = df.groupby('Year')['ticker'].nunique()
print("Jumlah perusahaan unik per tahun:")
print(comp_per_year)
comp_per_year.plot(kind='bar', figsize=(10, 4), color='steelblue')
plt.title('Jumlah Perusahaan Unik per Tahun Fiskal')
plt.ylabel('Jumlah Perusahaan')
plt.tight_layout()
plt.show()
years_per_comp = df.groupby('ticker')['Year'].nunique()
print("\nDistribusi jumlah tahun fiskal per perusahaan:")
print(years_per_comp.describe())Jumlah perusahaan unik per tahun:
Year
2008 302
2009 315
2010 325
2011 344
2012 367
2013 392
2014 417
2015 441
2016 460
2017 475
2018 508
2019 559
2020 611
2021 661
2022 715
2023 765
2024 832
Name: ticker, dtype: int64

Distribusi jumlah tahun fiskal per perusahaan:
count 853.000000
mean 9.951934
std 6.263298
min 1.000000
25% 4.000000
50% 10.000000
75% 17.000000
max 17.000000
Name: Year, dtype: float64
Analisis Target : Tingkat Distress Per Sektor & Per Tahun
# ANALISIS TARGET: TINGKAT DISTRESS PER SEKTOR & PER WAKTU
# ---------------------------------------------------------------------
for target in target_cols:
sub = df[df[target].notna()].copy()
n_pos = int(sub[target].sum())
print(f"\n########## {target} ##########")
print(f"Total berlabel: {len(sub)} | Distress: {n_pos} "
f"({sub[target].mean() * 100:.2f}%)")
# Tingkat distress per Industry Group (sektor dengan minimal 20 baris)
grp = sub.groupby('Industry Group')[target].agg(['mean', 'sum', 'count'])
grp = grp[grp['count'] >= 20].sort_values('mean', ascending=False)
grp['mean'] = (grp['mean'] * 100).round(2)
grp = grp.rename(columns={'mean': 'Distress %',
'sum': 'Jumlah Distress',
'count': 'Jumlah Baris'})
print("\nTingkat distress per Industry Group (min. 20 baris):")
display(grp)
if len(grp) > 0:
plt.figure(figsize=(9, max(3, 0.32 * len(grp))))
sns.barplot(x='Distress %', y=grp.index, data=grp.reset_index(),
color='indianred')
plt.title(f'Tingkat Distress per Sektor - {target}')
plt.tight_layout()
plt.show()
# Tingkat distress per tahun fiskal
rate_year = sub.groupby('Year')[target].mean() * 100
rate_year.plot(marker='o', figsize=(8, 4), color='indianred')
plt.ylim(ymin=0)
plt.title(f'Tingkat Distress per Tahun Fiskal - {target}')
plt.ylabel('Distress %')
plt.tight_layout()
plt.show()
########## target_distress_ppk ##########
Total berlabel: 2583 | Distress: 510 (19.74%)
Tingkat distress per Industry Group (min. 20 baris):
| Distress % | Jumlah Distress | Jumlah Baris | |
|---|---|---|---|
| Industry Group | |||
| Consumer Services | 33.330000 | 37 | 111 |
| Consumer Durables and Apparel | 28.720000 | 27 | 94 |
| Media and Entertainment | 26.970000 | 24 | 89 |
| Real Estate Management and Development | 25.190000 | 68 | 270 |
| Capital Goods | 22.880000 | 54 | 236 |
| Commercial and Professional Services | 22.860000 | 8 | 35 |
| Transportation | 22.030000 | 39 | 177 |
| Materials | 20.410000 | 60 | 294 |
| Energy | 20.110000 | 37 | 184 |
| Software and Services | 19.440000 | 7 | 36 |
| Financial Services | 18.350000 | 20 | 109 |
| Telecommunication Services | 18.030000 | 11 | 61 |
| Consumer Discretionary Distribution and Retail | 17.480000 | 18 | 103 |
| Food, Beverage and Tobacco | 15.950000 | 41 | 257 |
| Consumer Staples Distribution and Retail | 15.560000 | 7 | 45 |
| Insurance | 14.630000 | 12 | 82 |
| Technology Hardware and Equipment | 13.890000 | 5 | 36 |
| Utilities | 12.500000 | 3 | 24 |
| Health Care Equipment and Services | 12.070000 | 7 | 58 |
| Pharmaceuticals, Biotechnology and Life Sciences | 11.760000 | 4 | 34 |
| Automobiles and Components | 11.760000 | 4 | 34 |
| Household and Personal Products | 8.570000 | 3 | 35 |
| Banks | 7.820000 | 14 | 179 |


########## target_distress_neg_equity ##########
Total berlabel: 6403 | Distress: 116 (1.81%)
Tingkat distress per Industry Group (min. 20 baris):
| Distress % | Jumlah Distress | Jumlah Baris | |
|---|---|---|---|
| Industry Group | |||
| Transportation | 5.370000 | 18 | 335 |
| Technology Hardware and Equipment | 5.080000 | 3 | 59 |
| Utilities | 4.440000 | 2 | 45 |
| Energy | 3.790000 | 18 | 475 |
| Consumer Durables and Apparel | 3.700000 | 7 | 189 |
| Media and Entertainment | 3.630000 | 7 | 193 |
| Consumer Discretionary Distribution and Retail | 2.840000 | 6 | 211 |
| Capital Goods | 2.830000 | 16 | 566 |
| Telecommunication Services | 2.350000 | 4 | 170 |
| Consumer Services | 2.140000 | 6 | 281 |
| Health Care Equipment and Services | 2.080000 | 2 | 96 |
| Pharmaceuticals, Biotechnology and Life Sciences | 1.820000 | 2 | 110 |
| Consumer Staples Distribution and Retail | 1.560000 | 2 | 128 |
| Materials | 1.490000 | 12 | 808 |
| Food, Beverage and Tobacco | 1.150000 | 7 | 610 |
| Real Estate Management and Development | 0.440000 | 3 | 682 |
| Banks | 0.200000 | 1 | 510 |
| Commercial and Professional Services | 0.000000 | 0 | 106 |
| Automobiles and Components | 0.000000 | 0 | 111 |
| Insurance | 0.000000 | 0 | 235 |
| Household and Personal Products | 0.000000 | 0 | 78 |
| Financial Services | 0.000000 | 0 | 355 |
| Software and Services | 0.000000 | 0 | 50 |


########## target_distress_consecutive_loss ##########
Total berlabel: 5160 | Distress: 565 (10.95%)
Tingkat distress per Industry Group (min. 20 baris):
| Distress % | Jumlah Distress | Jumlah Baris | |
|---|---|---|---|
| Industry Group | |||
| Software and Services | 23.810000 | 10 | 42 |
| Consumer Services | 21.430000 | 45 | 210 |
| Transportation | 19.830000 | 46 | 232 |
| Consumer Durables and Apparel | 17.280000 | 28 | 162 |
| Media and Entertainment | 16.780000 | 24 | 143 |
| Energy | 15.020000 | 50 | 333 |
| Capital Goods | 12.900000 | 60 | 465 |
| Utilities | 12.500000 | 5 | 40 |
| Consumer Discretionary Distribution and Retail | 12.220000 | 22 | 180 |
| Technology Hardware and Equipment | 11.900000 | 5 | 42 |
| Real Estate Management and Development | 11.520000 | 60 | 521 |
| Materials | 11.180000 | 69 | 617 |
| Consumer Staples Distribution and Retail | 10.780000 | 11 | 102 |
| Health Care Equipment and Services | 10.390000 | 8 | 77 |
| Telecommunication Services | 9.380000 | 12 | 128 |
| Household and Personal Products | 9.230000 | 6 | 65 |
| Food, Beverage and Tobacco | 8.020000 | 41 | 511 |
| Financial Services | 7.220000 | 21 | 291 |
| Commercial and Professional Services | 6.520000 | 6 | 92 |
| Automobiles and Components | 6.000000 | 6 | 100 |
| Banks | 4.870000 | 23 | 472 |
| Pharmaceuticals, Biotechnology and Life Sciences | 4.760000 | 5 | 105 |
| Insurance | 0.870000 | 2 | 230 |


n_targets = len(target_cols)
# 1. MENENTUKAN JANGKAR URUTAN SECARA ALFABETIS (MASTER ORDER)
target_acuan = target_cols[0]
sub_acuan = df[df[target_acuan].notna()].copy()
grp_acuan = sub_acuan.groupby('Industry Group')[target_acuan].agg(['mean', 'count'])
# Saring minimal 20 baris, lalu urutkan nama sektornya secara ALFABETIS.
# ascending=False digunakan agar abjad A dirender di posisi paling atas layar.
grp_acuan = grp_acuan[grp_acuan['count'] >= 20].sort_index(ascending=False)
master_order = grp_acuan.index.tolist()
# 2. INISIALISASI KANVAS UTAMA
fig, axes = plt.subplots(nrows=1, ncols=n_targets, figsize=(5 * n_targets, 12), sharey=True)
if n_targets == 1:
axes = [axes]
for i, target in enumerate(target_cols):
ax = axes[i]
sub = df[df[target].notna()].copy()
grp = sub.groupby('Industry Group')[target].agg(['mean', 'sum', 'count'])
# Kunci urutan baris mengikuti jangkar alfabetis
grp = grp.reindex(master_order).fillna(0)
plot_df = pd.DataFrame(index=grp.index)
plot_df['Aman (Target: 0)'] = grp['count'] - grp['sum']
plot_df['Distress (Target: 1)'] = grp['sum']
plot_df.plot(
kind='barh',
stacked=True,
color=['#A8E6CF', '#FF8A80'],
width=0.8,
ax=ax
)
# Anotasi Teks HANYA di bar merah
for n, (index, row) in enumerate(plot_df.iterrows()):
sehat_count = row['Aman (Target: 0)']
distress_count = row['Distress (Target: 1)']
total_bar_count = grp.loc[index, 'count']
if distress_count > 0 and total_bar_count > 0:
distress_pct = (distress_count / total_bar_count) * 100
x_pos = sehat_count + (distress_count / 2)
teks_anotasi = f"{int(distress_count)}/{int(total_bar_count)} ({distress_pct:.1f}%)"
ax.text(x_pos, n, teks_anotasi, va='center', ha='center', color='black', fontsize=9)
ax.set_title(f"Target:\n{target}", pad=15, fontweight='bold')
ax.set_xlabel("Jumlah Baris Data")
ax.set_ylabel("")
# 3. MENGHAPUS SEMUA LEGENDA INTERNAL
if ax.get_legend():
ax.get_legend().remove()
if i > 0:
ax.tick_params(axis='y', which='both', length=0)
# 4. MEMBUAT LEGENDA GLOBAL DI TENGAH BAWAH
handles, labels = axes[0].get_legend_handles_labels()
fig.legend(handles, labels, loc='lower center', bbox_to_anchor=(0.5, 0.01), ncol=2, fontsize=11)
# 5. MERAPIKAN JARAK DAN MEMBERI RUANG BAWAH
plt.tight_layout(w_pad=0.5, rect=[0, 0.05, 1, 1])
plt.show()
import matplotlib.pyplot as plt
import pandas as pd
n_targets = len(target_cols)
# 1. MENENTUKAN JANGKAR URUTAN (MASTER ORDER)
# Gunakan target pertama di dalam list sebagai acuan urutan sektor.
target_acuan = target_cols[0]
sub_acuan = df[df[target_acuan].notna()].copy()
grp_acuan = sub_acuan.groupby('Industry Group')[target_acuan].agg(['mean', 'count'])
grp_acuan = grp_acuan[grp_acuan['count'] >= 20].sort_values('mean', ascending=True)
# Kunci urutan nama sektor industri ke dalam satu list paten
master_order = grp_acuan.index.tolist()
# 2. INISIALISASI KANVAS DENGAN SUMBU Y BERSAMA (sharey=True)
# Lebar kanvas dikurangi sedikit (5 inci per grafik) karena ruang nama sektor sudah hemat
fig, axes = plt.subplots(nrows=1, ncols=n_targets, figsize=(5 * n_targets, 12), sharey=True)
if n_targets == 1:
axes = [axes]
for i, target in enumerate(target_cols):
ax = axes[i]
sub = df[df[target].notna()].copy()
# Agregasi data
grp = sub.groupby('Industry Group')[target].agg(['mean', 'sum', 'count'])
# 3. KUNCI STRUKTUR BARIS MENGGUNAKAN MASTER ORDER
# Jika ada sektor di target lain yang tidak punya data, isi dengan 0 (fillna)
grp = grp.reindex(master_order).fillna(0)
# Siapkan DataFrame khusus plotting
plot_df = pd.DataFrame(index=grp.index)
plot_df['Aman (Target: 0)'] = grp['count'] - grp['sum']
plot_df['Distress (Target: 1)'] = grp['sum']
# Plotting
plot_df.plot(
kind='barh',
stacked=True,
color=['#A8E6CF', '#FF8A80'],
width=0.8,
ax=ax
)
# Anotasi Teks HANYA di bar merah
for n, (index, row) in enumerate(plot_df.iterrows()):
sehat_count = row['Aman (Target: 0)']
distress_count = row['Distress (Target: 1)']
total_bar_count = grp.loc[index, 'count']
# Validasi tambahan: total bar tidak boleh 0 untuk mencegah error pembagian (ZeroDivisionError)
if distress_count > 0 and total_bar_count > 0:
distress_pct = (distress_count / total_bar_count) * 100
x_pos = sehat_count + (distress_count / 2)
teks_anotasi = f"{int(distress_count)}/{int(total_bar_count)} ({distress_pct:.1f}%)"
ax.text(x_pos, n, teks_anotasi, va='center', ha='center', color='black', fontsize=9)
# Konfigurasi Tampilan
ax.set_title(f"Target:\n{target}", pad=15, fontweight='bold')
ax.set_xlabel("Jumlah Baris Data")
ax.set_ylabel("")
# Manajemen Legenda dan Garis Sumbu
if i == 0:
ax.legend(loc='lower right')
else:
# Hapus legenda untuk grafik ke-2, ke-3, dst.
ax.get_legend().remove()
# Hilangkan juga garis tick kecil-kecil di sumbu Y pada grafik sebelah kanan agar mulus
ax.tick_params(axis='y', which='both', length=0)
# 4. MERAPIKAN JARAK ANTAR GRAFIK
# Menggunakan w_pad (width padding) yang sangat kecil agar antar grafik seolah menempel
plt.tight_layout(w_pad=0.5)
plt.show()
Korelasi Antar Kolom Keuangan
# KORELASI ANTAR KOLOM AKUN KEUANGAN
# ---------------------------------------------------------------------
# Spearman dipakai karena hubungan antar akun keuangan kerap non-linear &
# banyak outlier. Korelasi tinggi antar akun mentah lazim terjadi karena
# nilainya sama-sama meningkat seiring ukuran perusahaan -- ini sekaligus
# menjadi alasan transformasi ke rasio keuangan pada tahap persiapan data.
corr = df[account_cols].corr(method='spearman')
plt.figure(figsize=(14, 12))
sns.heatmap(corr, cmap='coolwarm', center=0, square=True,
cbar_kws={'shrink': 0.6})
plt.title('Heatmap Korelasi Spearman Antar Kolom Akun Keuangan')
plt.tight_layout()
plt.show()
# Pasangan kolom dengan korelasi absolut tinggi
mask = np.triu(np.ones(corr.shape), k=1).astype(bool)
high_pairs = corr.abs().where(mask).stack().sort_values(ascending=False)
print("Pasangan kolom dengan |korelasi Spearman| > 0.8:")
print(high_pairs[high_pairs > 0.8])
Pasangan kolom dengan |korelasi Spearman| > 0.8:
variable variable
Operating Income (Rp.M) EBIT (Rp.M) 1.000000
Total Assets (Rp.M) Total Capital (Rp.M) 0.969918
Total Liabilities (Rp.M) Total Current Liabilities (Rp.M) 0.955142
Total Assets (Rp.M) 0.943063
Total Equity (Rp.M) Total Capital (Rp.M) 0.937788
Operating Income (Rp.M) EBITDA (Rp.M) 0.931527
EBITDA (Rp.M) EBIT (Rp.M) 0.931527
Total Assets (Rp.M) Total Current Assets (Rp.M) 0.924849
Long-term Debt (Rp.M) Total Debt (Rp.M) 0.923254
Cost Of Goods Sold (Rp.M) Total Revenue (Rp.M) 0.910787
Total Equity (Rp.M) Total Assets (Rp.M) 0.910582
Total Assets (Rp.M) Total Current Liabilities (Rp.M) 0.900747
Total Current Assets (Rp.M) Total Capital (Rp.M) 0.889587
Total Liabilities (Rp.M) Total Current Assets (Rp.M) 0.881772
Total Current Assets (Rp.M) Total Current Liabilities (Rp.M) 0.881733
Total Liabilities (Rp.M) Total Capital (Rp.M) 0.879436
Net Income to Company (Rp.M) Operating Income (Rp.M) 0.870587
Total Current Assets (Rp.M) Cash & Short-term Investments (Rp.M) 0.866649
Net Income to Company (Rp.M) EBIT (Rp.M) 0.854423
Total Liabilities (Rp.M) Long-term Debt (Rp.M) 0.851622
Total Equity (Rp.M) Total Current Assets (Rp.M) 0.846800
Market Capitalization (Rp.M) 0.844694
Total Assets (Rp.M) Cash & Short-term Investments (Rp.M) 0.838243
Market Capitalization (Rp.M) Total Capital (Rp.M) 0.834973
Total Assets (Rp.M) Market Capitalization (Rp.M) 0.833850
Long-term Debt (Rp.M) Total Capital (Rp.M) 0.833134
Current Ratio (x) Quick Ratio (x) 0.821145
Total Current Assets (Rp.M) Total Revenue (Rp.M) 0.820885
Total Current Liabilities (Rp.M) Total Capital (Rp.M) 0.818591
Total Assets (Rp.M) Long-term Debt (Rp.M) 0.818238
Total Revenue (Rp.M) EBITDA (Rp.M) 0.817761
Total Equity (Rp.M) Cash & Short-term Investments (Rp.M) 0.814297
Total Liabilities (Rp.M) Total Debt (Rp.M) 0.813864
Net Property, Plant & Equipment (Rp.M) Total Capital (Rp.M) 0.806563
Current Portion of LT Debt & Leases (Rp.M) Total Debt (Rp.M) 0.800969
dtype: float64
Analisis MNAR Kolom Raw
# # %% DETEKSI MEKANISME NILAI KOSONG (informative missingness)
# import pandas as pd, numpy as np
# from scipy.stats import chi2_contingency
df = df_findat_integrated.copy()
# Kolom fitur numerik yang memiliki nilai kosong (di luar identitas/target/sektor)
non_feature = {
'ticker', 'Year', 'Entity Name', '1st Level Primary Industry', 'Sector',
'Industry Group', 'Date Established', 'Year Established', 'IPO Year',
'target_distress_ppk', 'target_distress_neg_equity',
'target_distress_consecutive_loss', 'tahun_distress_ppk',
'tahun_distress_neg_equity', 'tahun_distress_consecutive_loss'
}
feature_cols = [c for c in df.columns
if c not in non_feature
and pd.api.types.is_numeric_dtype(df[c])
and df[c].isnull().any()]
target_cols = ['target_distress_ppk',
'target_distress_neg_equity',
'target_distress_consecutive_loss']
# 1) ASOSIASI MISSINGNESS DENGAN TARGET
# Bandingkan tingkat distress saat suatu fitur kosong vs terisi, lalu uji p value
rows = []
for target in target_cols:
sub = df[df[target].notna()]
for col in feature_cols:
is_null = sub[col].isnull()
if is_null.sum() < 10 or (~is_null).sum() < 10:
continue
ct = pd.crosstab(is_null, sub[target])
if ct.shape != (2, 2):
continue
_, p = fisher_exact(ct) # sebelumnya: _, p, _, _ = chi2_contingency(ct)
rows.append({
'target': target, 'fitur': col,
'distress_saat_kosong_%': round(sub.loc[is_null, target].mean() * 100, 2),
'distress_saat_terisi_%': round(sub.loc[~is_null, target].mean() * 100, 2),
'p_value': p
})
assoc = pd.DataFrame(rows)
assoc['signifikan_(p<0.05)'] = assoc['p_value'] < 0.05
assoc = assoc.sort_values(['target', 'p_value'])
# rows = []
# for target in target_cols:
# sub = df[df[target].notna()]
# for col in feature_cols:
# is_null = sub[col].isnull()
# if is_null.sum() < 10 or (~is_null).sum() < 10:
# continue # lewati bila salah satu kelompok terlalu sedikit
# ct = pd.crosstab(is_null, sub[target])
# try:
# _, p, _, _ = chi2_contingency(ct)
# except Exception:
# p = np.nan
# rows.append({
# 'target': target,
# 'fitur': col,
# 'distress_saat_kosong_%': round(sub.loc[is_null, target].mean() * 100, 2),
# 'distress_saat_terisi_%': round(sub.loc[~is_null, target].mean() * 100, 2),
# 'p_value': p
# })
# assoc = pd.DataFrame(rows)
# assoc['signifikan_(p<0.05)'] = assoc['p_value'] < 0.05
# assoc = assoc.sort_values(['target', 'p_value'])
# print("Keterkaitan ketiadaan data dengan target distress:")
display(assoc)
# 2) KO-OKUR ENSI MISSINGNESS (apakah kolom cenderung kosong bersamaan)
miss = df[feature_cols].isnull().astype(int)
corr_miss = miss.corr()
mask = np.triu(np.ones(corr_miss.shape), k=1).astype(bool)
pairs = corr_miss.where(mask).stack().sort_values(ascending=False)
print("\nPasangan indikator ketiadaan data dengan korelasi tertinggi:")
print(pairs[pairs > 0.3].head(20))| target | fitur | distress_saat_kosong_% | distress_saat_terisi_% | p_value | signifikan_(p<0.05) | |
|---|---|---|---|---|---|---|
| 35 | target_distress_consecutive_loss | Percent Owned - All Institutions (%) | 16.140000 | 7.440000 | 0.000000 | True |
| 34 | target_distress_consecutive_loss | Parent Percent Owned (%) | 14.800000 | 8.550000 | 0.000000 | True |
| 38 | target_distress_consecutive_loss | Quick Ratio (x) | 4.830000 | 11.630000 | 0.000000 | True |
| 37 | target_distress_consecutive_loss | Current Ratio (x) | 4.810000 | 11.640000 | 0.000000 | True |
| 46 | target_distress_consecutive_loss | EBITDA (Rp.M) | 6.370000 | 11.770000 | 0.000003 | True |
| 47 | target_distress_consecutive_loss | EBIT (Rp.M) | 6.370000 | 11.770000 | 0.000003 | True |
| 43 | target_distress_consecutive_loss | Current Portion of LT Debt & Leases (Rp.M) | 11.320000 | 3.610000 | 0.000027 | True |
| 39 | target_distress_consecutive_loss | Inventory (Rp.M) | 7.840000 | 11.500000 | 0.002222 | True |
| 40 | target_distress_consecutive_loss | Prepaid Exp. (Rp.M) | 13.600000 | 10.460000 | 0.009990 | True |
| 41 | target_distress_consecutive_loss | Long-term Debt (Rp.M) | 9.740000 | 11.480000 | 0.072464 | False |
| 48 | target_distress_consecutive_loss | Market Capitalization (Rp.M) | 23.530000 | 10.910000 | 0.106952 | False |
| 49 | target_distress_consecutive_loss | Year Close Stock Price (Rp.) | 23.530000 | 10.910000 | 0.106952 | False |
| 36 | target_distress_consecutive_loss | Percent Owned - Insiders (%) | 12.010000 | 10.500000 | 0.118931 | False |
| 51 | target_distress_consecutive_loss | Net Intangibles (Rp.M) | 10.740000 | 12.110000 | 0.265771 | False |
| 42 | target_distress_consecutive_loss | Short-term Borrowings (Rp.M) | 10.440000 | 11.200000 | 0.421441 | False |
| 44 | target_distress_consecutive_loss | Total Debt (Rp.M) | 14.290000 | 10.940000 | 0.660313 | False |
| 50 | target_distress_consecutive_loss | Total Capital (Rp.M) | 14.290000 | 10.940000 | 0.660313 | False |
| 45 | target_distress_consecutive_loss | Cost Of Goods Sold (Rp.M) | 9.350000 | 10.980000 | 0.753745 | False |
| 15 | target_distress_neg_equity | Percent Owned - All Institutions (%) | 2.850000 | 1.020000 | 0.000000 | True |
| 14 | target_distress_neg_equity | Parent Percent Owned (%) | 2.880000 | 1.060000 | 0.000000 | True |
| 17 | target_distress_neg_equity | Current Ratio (x) | 0.180000 | 1.970000 | 0.000409 | True |
| 18 | target_distress_neg_equity | Quick Ratio (x) | 0.180000 | 1.970000 | 0.000409 | True |
| 30 | target_distress_neg_equity | Market Capitalization (Rp.M) | 19.050000 | 1.750000 | 0.000482 | True |
| 31 | target_distress_neg_equity | Year Close Stock Price (Rp.) | 19.050000 | 1.750000 | 0.000482 | True |
| 21 | target_distress_neg_equity | Long-term Debt (Rp.M) | 0.980000 | 2.170000 | 0.000705 | True |
| 29 | target_distress_neg_equity | EBIT (Rp.M) | 0.780000 | 1.980000 | 0.009799 | True |
| 28 | target_distress_neg_equity | EBITDA (Rp.M) | 0.780000 | 1.980000 | 0.009856 | True |
| 23 | target_distress_neg_equity | Current Portion of LT Debt & Leases (Rp.M) | 1.890000 | 0.000000 | 0.015199 | True |
| 22 | target_distress_neg_equity | Short-term Borrowings (Rp.M) | 1.420000 | 2.010000 | 0.093650 | False |
| 33 | target_distress_neg_equity | Net Intangibles (Rp.M) | 1.720000 | 2.270000 | 0.208212 | False |
| 27 | target_distress_neg_equity | Total Revenue (Rp.M) | 7.690000 | 1.800000 | 0.211718 | False |
| 20 | target_distress_neg_equity | Prepaid Exp. (Rp.M) | 2.250000 | 1.720000 | 0.217022 | False |
| 26 | target_distress_neg_equity | Cost Of Goods Sold (Rp.M) | 2.800000 | 1.790000 | 0.331140 | False |
| 19 | target_distress_neg_equity | Inventory (Rp.M) | 1.490000 | 1.870000 | 0.508083 | False |
| 16 | target_distress_neg_equity | Percent Owned - Insiders (%) | 1.950000 | 1.750000 | 0.610674 | False |
| 24 | target_distress_neg_equity | Total Debt (Rp.M) | 0.000000 | 1.820000 | 1.000000 | False |
| 25 | target_distress_neg_equity | Net Property, Plant & Equipment (Rp.M) | 0.000000 | 1.820000 | 1.000000 | False |
| 32 | target_distress_neg_equity | Total Capital (Rp.M) | 0.000000 | 1.820000 | 1.000000 | False |
| 1 | target_distress_ppk | Percent Owned - All Institutions (%) | 26.170000 | 13.430000 | 0.000000 | True |
| 0 | target_distress_ppk | Parent Percent Owned (%) | 24.630000 | 15.910000 | 0.000000 | True |
| 9 | target_distress_ppk | Current Portion of LT Debt & Leases (Rp.M) | 20.440000 | 5.740000 | 0.000012 | True |
| 4 | target_distress_ppk | Quick Ratio (x) | 9.760000 | 20.610000 | 0.000077 | True |
| 3 | target_distress_ppk | Current Ratio (x) | 10.140000 | 20.580000 | 0.000170 | True |
| 2 | target_distress_ppk | Percent Owned - Insiders (%) | 24.530000 | 17.970000 | 0.000288 | True |
| 6 | target_distress_ppk | Prepaid Exp. (Rp.M) | 25.500000 | 18.540000 | 0.001064 | True |
| 13 | target_distress_ppk | Net Intangibles (Rp.M) | 21.660000 | 16.730000 | 0.002328 | True |
| 10 | target_distress_ppk | Cost Of Goods Sold (Rp.M) | 4.080000 | 20.050000 | 0.003105 | True |
| 11 | target_distress_ppk | EBITDA (Rp.M) | 14.090000 | 20.460000 | 0.009757 | True |
| 12 | target_distress_ppk | EBIT (Rp.M) | 14.240000 | 20.440000 | 0.011911 | True |
| 7 | target_distress_ppk | Long-term Debt (Rp.M) | 21.540000 | 18.950000 | 0.133723 | False |
| 8 | target_distress_ppk | Short-term Borrowings (Rp.M) | 21.210000 | 18.880000 | 0.152223 | False |
| 5 | target_distress_ppk | Inventory (Rp.M) | 20.450000 | 19.630000 | 0.720486 | False |
Pasangan indikator ketiadaan data dengan korelasi tertinggi:
variable variable
Cash from Ops. (Rp.M) Net Change in Cash (Rp.M) 1.000000
Total Debt (Rp.M) Total Capital (Rp.M) 1.000000
Market Capitalization (Rp.M) Year Close Stock Price (Rp.) 1.000000
Current Ratio (x) Quick Ratio (x) 0.998454
EBITDA (Rp.M) EBIT (Rp.M) 0.996358
Quick Ratio (x) EBIT (Rp.M) 0.761981
Current Ratio (x) EBIT (Rp.M) 0.760670
Quick Ratio (x) EBITDA (Rp.M) 0.759190
Current Ratio (x) EBITDA (Rp.M) 0.757881
Inventory (Rp.M) EBITDA (Rp.M) 0.350709
EBIT (Rp.M) 0.345621
Prepaid Exp. (Rp.M) 0.318408
dtype: float64
# === Visualisasi 1: selisih proporsi distress (kosong - terisi) per fitur ===
# 'assoc' adalah dataframe hasil uji keterkaitan yang sudah di peroleh
assoc = assoc.copy()
assoc['selisih'] = assoc['distress_saat_kosong_%'] - assoc['distress_saat_terisi_%']
targets = ['target_distress_ppk', 'target_distress_neg_equity', 'target_distress_consecutive_loss']
judul = ['Papan Pemantauan Khusus', 'Ekuitas Negatif', 'Kerugian Beruntun']
fig, axes = plt.subplots(1, 3, figsize=(18, 8))
for ax, tgt, jdl in zip(axes, targets, judul):
sub = assoc[assoc['target'] == tgt].sort_values('selisih')
warna = ['#c0392b' if p < 0.05 else '#bdc3c7' for p in sub['p_value']]
ax.barh(sub['fitur'], sub['selisih'], color=warna)
ax.axvline(0, color='black', linewidth=0.8)
ax.set_title(jdl, fontsize=11)
ax.set_xlabel('% Selisih Proporsi Distress (Kosong − Terisi)')
ax.tick_params(axis='y', labelsize=8)
fig.legend(handles=[Patch(color='#c0392b', label='Signifikan (p < 0,05)'),
Patch(color='#bdc3c7', label='Tidak signifikan')],
loc='upper right')
plt.suptitle('Selisih Proporsi Distress antara Baris Kosong dan Terisi per Fitur', fontsize=13)
plt.tight_layout()
plt.show()
# === Visualisasi 2: heatmap kecenderungan kolom kosong secara bersamaan ===
non_feature = {
'ticker', 'Year', 'Entity Name', '1st Level Primary Industry', 'Sector',
'Industry Group', 'Date Established', 'Year Established', 'IPO Year',
'target_distress_ppk', 'target_distress_neg_equity',
'target_distress_consecutive_loss', 'tahun_distress_ppk',
'tahun_distress_neg_equity', 'tahun_distress_consecutive_loss'
}
feat = [c for c in df_findat_integrated.columns
if c not in non_feature
and pd.api.types.is_numeric_dtype(df_findat_integrated[c])
and df_findat_integrated[c].isnull().any()]
miss = df_findat_integrated[feat].isnull().astype(int)
corr_miss = miss.corr()
plt.figure(figsize=(12, 10))
sns.heatmap(corr_miss, cmap='coolwarm', center=0, square=True, linewidths=0.3,
cbar_kws={'label': 'Korelasi Pola Kosong'})
plt.title('Heatmap Kecenderungan Kolom Kosong Secara Bersamaan')
plt.tight_layout()
plt.show()

Verifikasi Kualitias Data
DatPrep : Feature Engineering
Pilih Fitur Sektor/Industri
df_findat_integrated| variable | ticker | Year | Entity Name | Total Equity (Rp.M) | Net Income to Company (Rp.M) | target_distress_consecutive_loss | tahun_distress_consecutive_loss | target_distress_neg_equity | tahun_distress_neg_equity | target_distress_ppk | tahun_distress_ppk | 1st Level Primary Industry | Year Established | Sector | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.M) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | IPO Year |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | AALI | 2008 | PT Astra Agro Lestari Tbk (IDX:AALI) | 5336576.000000 | 2715518.000000 | 0 | <NA> | 0 | <NA> | <NA> | <NA> | Consumer | 1988 | Consumer Staples | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1183215.000000 | 6519791.000000 | 1.944000 | 0.878000 | 959489.000000 | 1975656.000000 | 1016167.000000 | 781363.000000 | 37429.000000 | NaN | NaN | NaN | 0.000000 | 4123645.000000 | 4357818.000000 | 8161217.000000 | 3370969.000000 | 3616720.000000 | 3370969.000000 | 2087429.000000 | -145096.000000 | 1574745000.000000 | 15432501.000000 | 9800.000000 | 5336576.000000 | 867676.000000 | NaN | 1997 |
| 1 | AALI | 2009 | PT Astra Agro Lestari Tbk (IDX:AALI) | 6426616.000000 | 1729648.000000 | 0 | <NA> | 0 | <NA> | <NA> | <NA> | Consumer | 1988 | Consumer Staples | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1144783.000000 | 7571399.000000 | 1.826000 | 1.007000 | 775450.000000 | 1714426.000000 | 938976.000000 | 610031.000000 | 36849.000000 | NaN | NaN | NaN | 0.000000 | 5242447.000000 | 4322498.000000 | 7424283.000000 | 2603280.000000 | 2898342.000000 | 2603280.000000 | 1984894.000000 | -79127.000000 | 1574745000.000000 | 35825448.750000 | 22750.000000 | 6426616.000000 | 788549.000000 | NaN | 1997 |
| 2 | AALI | 2010 | PT Astra Agro Lestari Tbk (IDX:AALI) | 7457257.000000 | 2103652.000000 | 0 | <NA> | 0 | <NA> | <NA> | <NA> | Consumer | 1988 | Consumer Staples | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1334542.000000 | 8791799.000000 | 1.932000 | 1.262000 | 989325.000000 | 2051177.000000 | 1061852.000000 | 624694.000000 | 22315.000000 | NaN | NaN | NaN | 0.000000 | 6103150.000000 | 5234372.000000 | 8843721.000000 | 2992793.000000 | 3335987.000000 | 2992793.000000 | 2946657.000000 | 452232.000000 | 1574745000.000000 | 41258319.000000 | 26200.000000 | 7457257.000000 | 1240781.000000 | NaN | 1997 |
| 3 | AALI | 2011 | PT Astra Agro Lestari Tbk (IDX:AALI) | 8426158.000000 | 2498565.000000 | 0 | <NA> | 0 | <NA> | <NA> | <NA> | Consumer | 1988 | Consumer Staples | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1778337.000000 | 10204495.000000 | 1.265000 | 0.582000 | 389456.000000 | 1857025.000000 | 1467569.000000 | 769903.000000 | NaN | NaN | NaN | NaN | 0.000000 | 7702571.000000 | 6837674.000000 | 10772582.000000 | 3195661.000000 | 3572651.000000 | 3195661.000000 | 3162475.000000 | -402591.000000 | 1574745000.000000 | 34171966.500000 | 21700.000000 | 8426158.000000 | 838190.000000 | NaN | 1997 |
| 4 | AALI | 2012 | PT Astra Agro Lestari Tbk (IDX:AALI) | 9365411.000000 | 2520266.000000 | 0 | <NA> | 0 | <NA> | <NA> | <NA> | Consumer | 1988 | Consumer Staples | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 4701077.000000 | 12419820.000000 | 0.685000 | 0.107000 | -820145.000000 | 1780395.000000 | 2600540.000000 | 1249050.000000 | NaN | NaN | 971950.000000 | NaN | 971950.000000 | 9894266.000000 | 7206837.000000 | 11564319.000000 | 3453729.000000 | 3973578.000000 | 3453729.000000 | 2609511.000000 | -610421.000000 | 1574745000.000000 | 31022476.500000 | 19700.000000 | 10337361.000000 | 227769.000000 | NaN | 1997 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 8484 | ZONE | 2023 | PT Mega Perintis Tbk (IDX:ZONE) | 376089.685000 | 46972.766000 | <NA> | <NA> | <NA> | <NA> | 0 | <NA> | Consumer | 2005 | Consumer Discretionary | Consumer Discretionary Distribution and Retail | NaN | NaN | 62.030000 | 376866.895000 | 752956.580000 | 1.787000 | 0.183000 | 187443.551000 | 425743.875000 | 238300.324000 | 370381.888000 | 8229.119000 | 45537.359000 | 85403.842000 | NaN | 230078.796000 | 254442.974000 | 327373.203000 | 735452.174000 | 80467.569000 | 109639.609000 | 80467.569000 | 101276.087000 | -292.586000 | 870171478.000000 | 965890.340580 | 1110.000000 | 606168.481000 | 4617.740000 | 36266.170000 | 2018 |
| 8485 | ZONE | 2024 | PT Mega Perintis Tbk (IDX:ZONE) | 370674.214000 | 7044.347000 | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | Consumer | 2005 | Consumer Discretionary | Consumer Discretionary Distribution and Retail | NaN | NaN | 62.030000 | 378069.858000 | 748744.072000 | 1.750000 | 0.178000 | 175199.380000 | 408802.739000 | 233603.359000 | 356567.003000 | 9325.106000 | 37101.093000 | 111850.141000 | NaN | 262139.509000 | 262571.197000 | 347862.483000 | 708360.249000 | 33764.860000 | 68145.652000 | 33764.860000 | 92620.371000 | 3123.094000 | 870171478.000000 | 717891.469350 | 825.000000 | 632813.723000 | 7740.834000 | 36707.806000 | 2018 |
| 8486 | ZYRX | 2022 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | 268289.329000 | 78627.417000 | 0 | <NA> | 0 | <NA> | 0 | <NA> | Technology, Media & Telecommunications | 1996 | Information Technology | Technology Hardware and Equipment | NaN | NaN | 75.160000 | 438940.489000 | 707229.819000 | 1.442000 | 0.668000 | 190967.877000 | 623252.109000 | 432284.232000 | 321857.019000 | 32.094000 | NaN | 154814.692000 | NaN | 154972.192000 | 62069.361000 | 626487.712000 | 770370.215000 | 124050.223000 | 126197.456000 | 124050.223000 | 108226.350000 | 205303.596000 | 1333333837.000000 | 426666.704320 | 320.000000 | 423261.521000 | 206376.347000 | NaN | 2021 |
| 8487 | ZYRX | 2023 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | 289448.779000 | 32952.892000 | <NA> | <NA> | <NA> | <NA> | 0 | <NA> | Technology, Media & Telecommunications | 1996 | Information Technology | Technology Hardware and Equipment | NaN | NaN | 75.160000 | 201592.951000 | 491041.729000 | 2.073000 | 0.401000 | 208245.855000 | 402244.527000 | 193998.671000 | 320652.316000 | 1146.130000 | 136.413000 | 123103.971000 | NaN | 123328.362000 | 68457.678000 | 198332.059000 | 289633.302000 | 75643.212000 | 77211.203000 | 75643.212000 | -128496.218000 | -193672.184000 | 1333334556.000000 | 221333.536296 | 166.000000 | 412777.140000 | 12704.163000 | 149.572000 | 2021 |
| 8488 | ZYRX | 2024 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | 300050.888000 | 15171.892000 | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | Technology, Media & Telecommunications | 1996 | Information Technology | Technology Hardware and Equipment | NaN | NaN | 75.160000 | 92393.711000 | 392444.599000 | 3.402000 | 0.775000 | 201817.814000 | 285854.627000 | 84036.813000 | 219621.479000 | 1021.593000 | 41.627000 | 46780.579000 | NaN | 47050.482000 | 92660.098000 | 296495.058000 | 364238.674000 | 37276.725000 | 38767.823000 | 37276.725000 | 113472.348000 | -6431.849000 | 1333334556.000000 | 177333.495948 | 133.000000 | 347101.370000 | 6272.314000 | 388.017000 | 2021 |
8489 rows × 46 columns
Analisis
Unique value dan null
print('Isi Sector :')
print(df_findat_integrated['Sector'].unique())
print('\nIsi 1st Level Primary Industry:')
print(df_findat_integrated['1st Level Primary Industry'].unique())
print('\nIsi Indusry Group')
print(df_findat_integrated['Industry Group'].unique())Isi Sector :
['Consumer Staples' 'Communication Services' 'Financials' 'Energy'
'Consumer Discretionary' 'Industrials' 'Real Estate' 'Materials'
'Utilities' 'Information Technology' 'Health Care']
Isi 1st Level Primary Industry:
['Consumer' 'Technology, Media & Telecommunications' 'Financials'
'Energy and Utilities' 'Industrials' 'Real Estate' 'Materials'
'Health Care']
Isi Indusry Group
['Food, Beverage and Tobacco' 'Media and Entertainment' 'Insurance'
'Energy' 'Consumer Discretionary Distribution and Retail' 'Capital Goods'
'Real Estate Management and Development' 'Financial Services' 'Materials'
'Automobiles and Components' 'Banks' 'Consumer Services' 'Transportation'
'Consumer Staples Distribution and Retail'
'Consumer Durables and Apparel' 'Utilities'
'Commercial and Professional Services' 'Software and Services'
'Technology Hardware and Equipment' 'Telecommunication Services'
'Health Care Equipment and Services'
'Pharmaceuticals, Biotechnology and Life Sciences'
'Household and Personal Products']
(df_findat_integrated[["Sector", "1st Level Primary Industry", "Industry Group"]]
.agg(["count", "nunique"])
.T
.assign(missing=lambda x: len(df_findat_final) - x["count"])
)| count | nunique | missing | |
|---|---|---|---|
| variable | |||
| Sector | 8489 | 11 | 0 |
| 1st Level Primary Industry | 8489 | 8 | 0 |
| Industry Group | 8489 | 23 | 0 |
Cek pemetaan dari yg paling detail ke yang lebih general
# Berapa banyak Sector unik untuk tiap Industry Group?
ig_to_sector_n = (df_findat_integrated
.groupby("Industry Group")["Sector"]
.nunique(dropna=True)
.sort_values(ascending=False))
ig_to_sector_n # kalau semuanya 1, berarti deterministik| Sector | |
|---|---|
| Industry Group | |
| Automobiles and Components | 1 |
| Banks | 1 |
| Capital Goods | 1 |
| Commercial and Professional Services | 1 |
| Consumer Discretionary Distribution and Retail | 1 |
| Consumer Durables and Apparel | 1 |
| Consumer Services | 1 |
| Consumer Staples Distribution and Retail | 1 |
| Energy | 1 |
| Financial Services | 1 |
| Food, Beverage and Tobacco | 1 |
| Health Care Equipment and Services | 1 |
| Household and Personal Products | 1 |
| Insurance | 1 |
| Materials | 1 |
| Media and Entertainment | 1 |
| Pharmaceuticals, Biotechnology and Life Sciences | 1 |
| Real Estate Management and Development | 1 |
| Software and Services | 1 |
| Technology Hardware and Equipment | 1 |
| Telecommunication Services | 1 |
| Transportation | 1 |
| Utilities | 1 |
ig_to_primary_n = (df_findat_integrated
.groupby("Industry Group")["1st Level Primary Industry"]
.nunique(dropna=True)
.sort_values(ascending=False))
ig_to_primary_n| 1st Level Primary Industry | |
|---|---|
| Industry Group | |
| Capital Goods | 2 |
| Consumer Services | 2 |
| Consumer Discretionary Distribution and Retail | 2 |
| Software and Services | 2 |
| Financial Services | 2 |
| Banks | 1 |
| Automobiles and Components | 1 |
| Consumer Durables and Apparel | 1 |
| Commercial and Professional Services | 1 |
| Energy | 1 |
| Consumer Staples Distribution and Retail | 1 |
| Health Care Equipment and Services | 1 |
| Household and Personal Products | 1 |
| Insurance | 1 |
| Food, Beverage and Tobacco | 1 |
| Materials | 1 |
| Media and Entertainment | 1 |
| Pharmaceuticals, Biotechnology and Life Sciences | 1 |
| Real Estate Management and Development | 1 |
| Technology Hardware and Equipment | 1 |
| Telecommunication Services | 1 |
| Transportation | 1 |
| Utilities | 1 |
Lihat tiap tipe sektor/industri terhadap distress
targets = ["target_distress_ppk", "target_distress_neg_equity", "target_distress_consecutive_loss"]
def distress_rate_multi_targets(df, group_col, target_cols):
print('Distress rate pada fitur', group_col)
n = df.groupby(group_col).size().rename("n")
rates = df.groupby(group_col)[target_cols].mean()
return pd.concat([n, rates], axis=1).reset_index()distress_rate_multi_targets(df_findat_integrated, "Industry Group", targets).sort_values(by=targets, ascending=False)Distress rate pada fitur Industry Group
| Industry Group | n | target_distress_ppk | target_distress_neg_equity | target_distress_consecutive_loss | |
|---|---|---|---|---|---|
| 6 | Consumer Services | 360 | 0.333333 | 0.021352 | 0.214286 |
| 5 | Consumer Durables and Apparel | 323 | 0.287234 | 0.037037 | 0.172840 |
| 15 | Media and Entertainment | 254 | 0.269663 | 0.036269 | 0.167832 |
| 17 | Real Estate Management and Development | 854 | 0.251852 | 0.004399 | 0.115163 |
| 2 | Capital Goods | 769 | 0.228814 | 0.028269 | 0.129032 |
| 3 | Commercial and Professional Services | 132 | 0.228571 | 0.000000 | 0.065217 |
| 21 | Transportation | 544 | 0.220339 | 0.053731 | 0.198276 |
| 14 | Materials | 1019 | 0.204082 | 0.014851 | 0.111831 |
| 8 | Energy | 665 | 0.201087 | 0.037895 | 0.150150 |
| 18 | Software and Services | 82 | 0.194444 | 0.000000 | 0.238095 |
| 9 | Financial Services | 419 | 0.183486 | 0.000000 | 0.072165 |
| 20 | Telecommunication Services | 208 | 0.180328 | 0.023529 | 0.093750 |
| 4 | Consumer Discretionary Distribution and Retail | 322 | 0.174757 | 0.028436 | 0.122222 |
| 10 | Food, Beverage and Tobacco | 802 | 0.159533 | 0.011475 | 0.080235 |
| 7 | Consumer Staples Distribution and Retail | 159 | 0.155556 | 0.015625 | 0.107843 |
| 13 | Insurance | 277 | 0.146341 | 0.000000 | 0.008696 |
| 19 | Technology Hardware and Equipment | 93 | 0.138889 | 0.050847 | 0.119048 |
| 22 | Utilities | 65 | 0.125000 | 0.044444 | 0.125000 |
| 11 | Health Care Equipment and Services | 146 | 0.120690 | 0.020833 | 0.103896 |
| 16 | Pharmaceuticals, Biotechnology and Life Sciences | 129 | 0.117647 | 0.018182 | 0.047619 |
| 0 | Automobiles and Components | 131 | 0.117647 | 0.000000 | 0.060000 |
| 12 | Household and Personal Products | 101 | 0.085714 | 0.000000 | 0.092308 |
| 1 | Banks | 635 | 0.078212 | 0.001961 | 0.048729 |
distress_rate_multi_targets(df_findat_integrated, "Sector", targets).sort_values(by=targets, ascending=False)Distress rate pada fitur Sector
| Sector | n | target_distress_ppk | target_distress_neg_equity | target_distress_consecutive_loss | |
|---|---|---|---|---|---|
| 9 | Real Estate | 854 | 0.251852 | 0.004399 | 0.115163 |
| 1 | Consumer Discretionary | 1136 | 0.251462 | 0.023990 | 0.154908 |
| 0 | Communication Services | 462 | 0.233333 | 0.030303 | 0.132841 |
| 6 | Industrials | 1445 | 0.225446 | 0.033764 | 0.141952 |
| 8 | Materials | 1019 | 0.204082 | 0.014851 | 0.111831 |
| 3 | Energy | 665 | 0.201087 | 0.037895 | 0.150150 |
| 7 | Information Technology | 175 | 0.166667 | 0.027523 | 0.178571 |
| 2 | Consumer Staples | 1062 | 0.151335 | 0.011029 | 0.085546 |
| 10 | Utilities | 65 | 0.125000 | 0.044444 | 0.125000 |
| 4 | Financials | 1331 | 0.124324 | 0.000909 | 0.046324 |
| 5 | Health Care | 275 | 0.119565 | 0.019417 | 0.071429 |
distress_rate_multi_targets(df_findat_integrated, "1st Level Primary Industry", targets).sort_values(by=targets, ascending=False)Distress rate pada fitur 1st Level Primary Industry
| 1st Level Primary Industry | n | target_distress_ppk | target_distress_neg_equity | target_distress_consecutive_loss | |
|---|---|---|---|---|---|
| 6 | Real Estate | 871 | 0.257353 | 0.004304 | 0.116858 |
| 4 | Industrials | 1428 | 0.227477 | 0.034274 | 0.144703 |
| 7 | Technology, Media & Telecommunications | 638 | 0.210762 | 0.029661 | 0.143662 |
| 5 | Materials | 1019 | 0.204082 | 0.014851 | 0.111831 |
| 0 | Consumer | 2210 | 0.198830 | 0.017295 | 0.116605 |
| 1 | Energy and Utilities | 730 | 0.192308 | 0.038462 | 0.147453 |
| 2 | Financials | 1318 | 0.122951 | 0.000918 | 0.046843 |
| 3 | Health Care | 275 | 0.119565 | 0.019417 | 0.071429 |
vc = df_findat_integrated["Industry Group"].value_counts()
print("min:", vc.min(), "median:", vc.median(), "max:", vc.max(), "max/min:", vc.max()/vc.min())
vcmin: 65 median: 277.0 max: 1019 max/min: 15.676923076923076
| count | |
|---|---|
| Industry Group | |
| Materials | 1019 |
| Real Estate Management and Development | 854 |
| Food, Beverage and Tobacco | 802 |
| Capital Goods | 769 |
| Energy | 665 |
| Banks | 635 |
| Transportation | 544 |
| Financial Services | 419 |
| Consumer Services | 360 |
| Consumer Durables and Apparel | 323 |
| Consumer Discretionary Distribution and Retail | 322 |
| Insurance | 277 |
| Media and Entertainment | 254 |
| Telecommunication Services | 208 |
| Consumer Staples Distribution and Retail | 159 |
| Health Care Equipment and Services | 146 |
| Commercial and Professional Services | 132 |
| Automobiles and Components | 131 |
| Pharmaceuticals, Biotechnology and Life Sciences | 129 |
| Household and Personal Products | 101 |
| Technology Hardware and Equipment | 93 |
| Software and Services | 82 |
| Utilities | 65 |
Berikut ini insight yang ditemukan untuk ketiga kasus distress dengan range prediksi within 2 years:
Terlihat bahwa Industry Group merupakan fitur yang paling detail dengan banyak nilai unik, sedangkan fitur 1st Level Primary Industry merupakan fitur yang paling general dengan sedikit nilai unik. Apabila Industry Group dipetakan terhadap Sector hasilnya terlihat terpetakan 1-on-1, sedangkan terhadap 1st Level Primary Industry hasilnya ada 5 yang 2-on-1 dan kebanyakan sisanya tetap 1-on-1.
Pada jenis Financials dari fitur Sector, ditemukan bahwa rate distressnya sebesar 0.143. Namun, pada jenis Banks, Financial Services, dan Insurance dari fitur Industry Group, terlihat bahwa ketiganya memiliki rate distress yang bervariasi, ada yang lebih tinggi dan ada yang lebih rendah dari 0.143. Hal ini menunjukkan bahwa fitur Industry Group memiliki kemungkinan untuk dapat menjadi prediktor atau setidaknya pemisah yang lebih baik daripada Sector yang terlalu general, begitu pula fitur 1st Level Primary Industry. Dengan demikian, pada tahap selanjutnya, fitur Industry Group dipilih untuk merepresentasikan pada sektor/industri apa perusahaan beroperasi, sedangkan fitur lainnya akan didrop untuk mengurangi redundansi.
Pilih Kolom Industry Group
# Pilih kolom 'Industry Group' yang akan dipertahankan
df_findat_integrated = df_findat_integrated.drop(['Sector', '1st Level Primary Industry'], axis=1)columns_to_check = ['Sector', '1st Level Primary Industry', 'Industry Group']
print('CEK KOLOM PADA df_findat_integrated :')
for col in columns_to_check:
if col in df_findat_integrated.columns:
print(f"- '{col}' ADA.")
else:
print(f"- '{col}' TIDAK ADA.")CEK KOLOM PADA df_findat_integrated :
- 'Sector' TIDAK ADA.
- '1st Level Primary Industry' TIDAK ADA.
- 'Industry Group' ADA.
(?) Null Handling Pre-Building
Dilakukan khusus pada dataset yang dihilangkan nullnya.
df_findat_integrated_with_null = df_findat_integrated.copy()null_counts_fitur = df_findat_integrated_with_null.isnull().sum()
total_rows_fitur = len(df_findat_integrated_with_null)
null_percentages_fitur = (null_counts_fitur / total_rows_fitur) * 100
null_summary_fitur_df = pd.DataFrame({
'Null Count': null_counts_fitur,
'Null Pctg': null_percentages_fitur
})
null_summary_fitur_df = null_summary_fitur_df.sort_values(by='Null Count', ascending=False)
print("Summary Null Values di df_findat_integrated_with_null:")
print(null_summary_fitur_df.to_string())Summary Null Values di df_findat_integrated_with_null:
Null Count Null Pctg
variable
Current Portion of LT Debt & Leases (Rp.M) 8152 96.030157
tahun_distress_neg_equity 7416 87.360113
Net Intangibles (Rp.M) 6726 79.231947
target_distress_ppk 5906 69.572388
tahun_distress_ppk 5068 59.700789
tahun_distress_consecutive_loss 4320 50.889386
Percent Owned - All Institutions (%) 3947 46.495465
Parent Percent Owned (%) 3641 42.890800
target_distress_consecutive_loss 3329 39.215455
Short-term Borrowings (Rp.M) 2910 34.279656
Long-term Debt (Rp.M) 2581 30.404052
Percent Owned - Insiders (%) 2551 30.050654
target_distress_neg_equity 2086 24.572977
Prepaid Exp. (Rp.M) 1462 17.222288
Inventory (Rp.M) 1206 14.206620
EBITDA (Rp.M) 1106 13.028625
EBIT (Rp.M) 1099 12.946166
Current Ratio (x) 706 8.316645
Quick Ratio (x) 704 8.293085
Cost Of Goods Sold (Rp.M) 180 2.120391
Total Revenue (Rp.M) 31 0.365178
Year Close Stock Price (Rp.) 25 0.294499
Market Capitalization (Rp.M) 25 0.294499
Total Debt (Rp.M) 21 0.247379
Total Capital (Rp.M) 21 0.247379
Year Established 21 0.247379
Net Property, Plant & Equipment (Rp.M) 19 0.223819
Net Change in Cash (Rp.M) 2 0.023560
Cash from Ops. (Rp.M) 2 0.023560
Operating Income (Rp.M) 1 0.011780
ECS Total Common Shares Outstanding (actual) 1 0.011780
Net Income to Company (Rp.M) 0 0.000000
ticker 0 0.000000
Year 0 0.000000
Entity Name 0 0.000000
Total Equity (Rp.M) 0 0.000000
Total Current Assets (Rp.M) 0 0.000000
Working Capital (Rp.M) 0 0.000000
Total Current Liabilities (Rp.M) 0 0.000000
Total Liabilities (Rp.M) 0 0.000000
Total Assets (Rp.M) 0 0.000000
Industry Group 0 0.000000
Cash & Short-term Investments (Rp.M) 0 0.000000
IPO Year 0 0.000000
null_counts_per_row_fitur = df_findat_integrated_with_null.isnull().sum(axis=1)
null_counts_grouped_fitur = null_counts_per_row_fitur.value_counts().sort_index(ascending=False)
print("Jumlah baris berdasarkan jumlah nilai null (df_findat_integrated_with_null, diurutkan dari yang terbanyak):")
print(null_counts_grouped_fitur.to_string())Jumlah baris berdasarkan jumlah nilai null (df_findat_integrated_with_null, diurutkan dari yang terbanyak):
18 1
17 2
16 12
15 17
14 46
13 171
12 264
11 406
10 715
9 961
8 1475
7 1761
6 1490
5 784
4 331
3 49
2 4
print('Baris dengan banyak null (>25)')
df_findat_integrated_with_null[df_findat_integrated_with_null.isna().sum(axis=1)>25]Baris dengan banyak null (>25)
| variable | ticker | Year | Entity Name | Total Equity (Rp.M) | Net Income to Company (Rp.M) | target_distress_consecutive_loss | tahun_distress_consecutive_loss | target_distress_neg_equity | tahun_distress_neg_equity | target_distress_ppk | tahun_distress_ppk | Year Established | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | Total Liabilities (Rp.M) | Total Assets (Rp.M) | Current Ratio (x) | Quick Ratio (x) | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.M) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | IPO Year |
|---|
Feature Building
Fungsi Penyusun Fitur
def build_features(df_raw):
df = df_raw.copy()
# Function Pembagi
# bagi dengan aman: jika 0 atau NaN -> NaN, bukan inf.
def safe_div(num, denom):
# if isinstance(denom, pd.Series) and (denom == 0).any():
# print('dibagi 0!!!')
# elif not isinstance(denom, pd.Series) and denom == 0:
# print('dibagi 0!!!')
res = num / denom
return res.replace([np.inf, -np.inf], np.nan)
# Fungsi log agar bisa menerima negatif
# input 0 hasilnya juga 0 dengan menambahkan angka sangat kecil
small_num = 1e-6
def signed_log(x):
return np.sign(x) * np.log(np.abs(x) + small_num)
# Sort & Reset Index
df = df.sort_values(["ticker", "Year"]).reset_index(drop=True)
# Nama fitur
CA = df['Total Current Assets (Rp.M)']
CL = df['Total Current Liabilities (Rp.M)']
TA = df['Total Assets (Rp.M)']
TL = df['Total Liabilities (Rp.M)']
Equity = df['Total Equity (Rp.M)']
Debt_rep = df['Total Debt (Rp.M)']
LTD = df['Long-term Debt (Rp.M)']
STB = df['Short-term Borrowings (Rp.M)']
CPLTD = df['Current Portion of LT Debt & Leases (Rp.M)']
Inventory = df['Inventory (Rp.M)']
Prepaid = df['Prepaid Exp. (Rp.M)']
PPE = df['Net Property, Plant & Equipment (Rp.M)']
Sales = df['Total Revenue (Rp.M)']
COGS = df['Cost Of Goods Sold (Rp.M)']
OpInc = df['Operating Income (Rp.M)']
EBITDA = df['EBITDA (Rp.M)']
EBIT = df['EBIT (Rp.M)']
NI = df['Net Income to Company (Rp.M)']
CFO = df['Cash from Ops. (Rp.M)']
NetCash = df['Net Change in Cash (Rp.M)']
CashST = df['Cash & Short-term Investments (Rp.M)']
Intang = df['Net Intangibles (Rp.M)']
Shares = df['ECS Total Common Shares Outstanding (actual)']
MktCap = df['Market Capitalization (Rp.M)']
Price = df['Year Close Stock Price (Rp.)']
TotalCap = df['Total Capital (Rp.M)']
WC = df['Working Capital (Rp.M)']
Debt = df['Total Debt (Rp.M)']
Sales_log = signed_log(Sales)
MktCap_log = signed_log(MktCap)
TA_log = signed_log(TA)
# Umur perusahaan
AgeWhenIPO = df['IPO Year'] - df['Year Established']
YearsSinceIPO = df['Year'] - df['IPO Year']
# Net debt
NetDebt = Debt - CashST
#### SUSUN NUMERIK ####
# fitur dasar identitas & struktur
df['Age_When_IPO'] = AgeWhenIPO
df['Years_Since_IPO'] = YearsSinceIPO
# --- LIQUIDITY & WORKING CAPITAL ----
df = df.rename(columns={"Current Ratio (x)": "CR", "Quick Ratio (x)": "QR"})
# df['WC_TA'] = safe_div(WC, TA) # UDAH DI ALTMAN
df['WC_Sales'] = safe_div(WC, Sales)
df['CA_TA'] = safe_div(CA, TA)
df['CL_TA'] = safe_div(CL, TA)
df['CashST_TA'] = safe_div(CashST, TA)
df['CashST_CL'] = safe_div(CashST, CL)
df['Inventory_CA'] = safe_div(Inventory, CA)
df['Prepaid_CA'] = safe_div(Prepaid, CA)
# --- LEVERAGE & CAPITAL STRUCTURE ---
df['TL_TA'] = safe_div(TL, TA)
df['Debt_TA'] = safe_div(Debt, TA)
df['Debt_Equity'] = safe_div(Debt, Equity) # DER
df['Equity_TA'] = safe_div(Equity, TA)
df['NetDebt_EBITDA'] = safe_div(NetDebt, EBITDA) # Leverage Ratio
df['PPE_TA'] = safe_div(PPE, TA)
df['Intang_TA'] = safe_div(Intang, TA)
# Altman-like komponen yang bisa dihitung
df['Altman_X1_WC_TA'] = safe_div(WC, TA)
df['Altman_X3_EBIT_TA'] = safe_div(EBIT, TA)
df['Altman_X4_MVE_TL'] = safe_div(MktCap, TL)
df['Altman_X5_SalesTA_AssetTurnover'] = safe_div(Sales, TA)
# --- PROFITABILITY & MARGINS ---
df['ROA'] = safe_div(NI, TA)
df['ROE'] = safe_div(NI, Equity)
# df['EBIT_TA'] = safe_div(EBIT, TA) # UDAH DI ALTMAN
df['EBITDA_TA'] = safe_div(EBITDA, TA)
df['GrossMargin'] = safe_div(Sales - COGS, Sales)
df['OpMargin'] = safe_div(OpInc, Sales)
df['NetMargin'] = safe_div(NI, Sales)
# --- CASH FLOW BASED ---
df['CFO_TA'] = safe_div(CFO, TA)
df['CFO_TL'] = safe_div(CFO, TL)
df['CFO_Sales'] = safe_div(CFO, Sales)
df['NetCash_TA'] = safe_div(NetCash, TA)
# --- EFFICIENCY SEDERHANA (TURNOVER) ---
# df['AssetTurnover'] = safe_div(Sales, TA) # UDAH DI ALTMAN
# --- PER SHARE & MARKET ---
df['EPS_proxy'] = safe_div(NI, Shares) * 1_000_000 # Rp per saham, dibuaat proporsional thd aproksimasi EPS karena ketidaktersediaan data cc
df['Sales_per_share'] = safe_div(Sales, Shares) * 1_000_000
df['CFO_per_share'] = safe_div(CFO, Shares) * 1_000_000
df['log_TA'] = TA_log
df['log_Sales'] = Sales_log
df['log_MktCap'] = MktCap_log
df['PB'] = safe_div(MktCap, Equity)
# TODO : Bisa tambahkan P/E, nnti cek dulu unit NI & Shares
#### FITUR GROWTH YoY ####
# Tambahkan fill_method=None untuk menghilangkan warning dan memastikan akurasi
df['Sales_growth'] = df.groupby('ticker')['Total Revenue (Rp.M)'].pct_change(fill_method=None)
df['NI_growth'] = df.groupby('ticker')['Net Income to Company (Rp.M)'].pct_change(fill_method=None)
df['TA_growth'] = df.groupby('ticker')['Total Assets (Rp.M)'].pct_change(fill_method=None)
df['Equity_growth'] = df.groupby('ticker')['Total Equity (Rp.M)'].pct_change(fill_method=None)
df['CFO_growth'] = df.groupby('ticker')['Cash from Ops. (Rp.M)'].pct_change(fill_method=None)
#### ENCODING FITUR KATEGORIKAL ####
# Buat copy kolom untuk dipakai analisis (dihapus lagi nanti)
df['Industry_Group_Copy'] = df['Industry Group'].str.replace(',', '')
# One-hot encoding untuk Industry Group
df_fitur_result = pd.get_dummies(
df,
columns=['Industry_Group_Copy'],
prefix='industry',
drop_first=True
)
#### URUTKAN KOLOM HASIL AKHIR ####
first_cols = [
"ticker",
"Year",
"Entity Name",
"target_distress_ppk",
"tahun_distress_ppk",
"Total Equity (Rp.M)",
"target_distress_neg_equity",
"tahun_distress_neg_equity",
"Net Income to Company (Rp.M)",
"target_distress_consecutive_loss",
"tahun_distress_consecutive_loss"
]
first_cols = [c for c in first_cols if c in df_fitur_result.columns]
df_fitur_result = df_fitur_result[first_cols + [c for c in df_fitur_result.columns if c not in first_cols]]
# Delete Columns
# sementara disimpan dulu
return df_fitur_result.copy()df_findat_integrated[['Year', 'Year Established', 'IPO Year']].dtypes| 0 | |
|---|---|
| variable | |
| Year | Int64 |
| Year Established | Int64 |
| IPO Year | Int64 |
Execute Feature Building
Dataset disini masih mempertahankan null
df_fitur = build_features(df_findat_integrated)
df_fitur| ticker | Year | Entity Name | target_distress_ppk | tahun_distress_ppk | Total Equity (Rp.M) | target_distress_neg_equity | tahun_distress_neg_equity | Net Income to Company (Rp.M) | target_distress_consecutive_loss | tahun_distress_consecutive_loss | Year Established | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | Total Liabilities (Rp.M) | Total Assets (Rp.M) | CR | QR | Working Capital (Rp.M) | Total Current Assets (Rp.M) | Total Current Liabilities (Rp.M) | Inventory (Rp.M) | Prepaid Exp. (Rp.M) | Long-term Debt (Rp.M) | Short-term Borrowings (Rp.M) | Current Portion of LT Debt & Leases (Rp.M) | Total Debt (Rp.M) | Net Property, Plant & Equipment (Rp.M) | Cost Of Goods Sold (Rp.M) | Total Revenue (Rp.M) | Operating Income (Rp.M) | EBITDA (Rp.M) | EBIT (Rp.M) | Cash from Ops. (Rp.M) | Net Change in Cash (Rp.M) | ECS Total Common Shares Outstanding (actual) | Market Capitalization (Rp.M) | Year Close Stock Price (Rp.) | Total Capital (Rp.M) | Cash & Short-term Investments (Rp.M) | Net Intangibles (Rp.M) | IPO Year | Age_When_IPO | Years_Since_IPO | WC_Sales | CA_TA | CL_TA | CashST_TA | CashST_CL | Inventory_CA | Prepaid_CA | TL_TA | Debt_TA | Debt_Equity | Equity_TA | NetDebt_EBITDA | PPE_TA | Intang_TA | Altman_X1_WC_TA | Altman_X3_EBIT_TA | Altman_X4_MVE_TL | Altman_X5_SalesTA_AssetTurnover | ROA | ROE | EBITDA_TA | GrossMargin | OpMargin | NetMargin | CFO_TA | CFO_TL | CFO_Sales | NetCash_TA | EPS_proxy | Sales_per_share | CFO_per_share | log_TA | log_Sales | log_MktCap | PB | Sales_growth | NI_growth | TA_growth | Equity_growth | CFO_growth | industry_Banks | industry_Capital Goods | industry_Commercial and Professional Services | industry_Consumer Discretionary Distribution and Retail | industry_Consumer Durables and Apparel | industry_Consumer Services | industry_Consumer Staples Distribution and Retail | industry_Energy | industry_Financial Services | industry_Food Beverage and Tobacco | industry_Health Care Equipment and Services | industry_Household and Personal Products | industry_Insurance | industry_Materials | industry_Media and Entertainment | industry_Pharmaceuticals Biotechnology and Life Sciences | industry_Real Estate Management and Development | industry_Software and Services | industry_Technology Hardware and Equipment | industry_Telecommunication Services | industry_Transportation | industry_Utilities | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | AALI | 2008 | PT Astra Agro Lestari Tbk (IDX:AALI) | <NA> | <NA> | 5336576.000000 | 0 | <NA> | 2715518.000000 | 0 | <NA> | 1988 | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1183215.000000 | 6519791.000000 | 1.944000 | 0.878000 | 959489.000000 | 1975656.000000 | 1016167.000000 | 781363.000000 | 37429.000000 | NaN | NaN | NaN | 0.000000 | 4123645.000000 | 4357818.000000 | 8161217.000000 | 3370969.000000 | 3616720.000000 | 3370969.000000 | 2087429.000000 | -145096.000000 | 1574745000.000000 | 15432501.000000 | 9800.000000 | 5336576.000000 | 867676.000000 | NaN | 1997 | 9 | 11 | 0.117567 | 0.303024 | 0.155859 | 0.133083 | 0.853871 | 0.395495 | 0.018945 | 0.181481 | 0.000000 | 0.000000 | 0.818519 | -0.239907 | 0.632481 | NaN | 0.147166 | 0.517036 | 13.042854 | 1.251761 | 0.416504 | 0.508850 | 0.554729 | 0.466033 | 0.413047 | 0.332734 | 0.320168 | 1.764201 | 0.255774 | -0.022255 | 1724.417604 | 5182.564161 | 1325.566362 | 15.690353 | 15.914904 | 16.551986 | 2.891836 | NaN | NaN | NaN | NaN | NaN | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 1 | AALI | 2009 | PT Astra Agro Lestari Tbk (IDX:AALI) | <NA> | <NA> | 6426616.000000 | 0 | <NA> | 1729648.000000 | 0 | <NA> | 1988 | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1144783.000000 | 7571399.000000 | 1.826000 | 1.007000 | 775450.000000 | 1714426.000000 | 938976.000000 | 610031.000000 | 36849.000000 | NaN | NaN | NaN | 0.000000 | 5242447.000000 | 4322498.000000 | 7424283.000000 | 2603280.000000 | 2898342.000000 | 2603280.000000 | 1984894.000000 | -79127.000000 | 1574745000.000000 | 35825448.750000 | 22750.000000 | 6426616.000000 | 788549.000000 | NaN | 1997 | 9 | 12 | 0.104448 | 0.226435 | 0.124016 | 0.104148 | 0.839797 | 0.355822 | 0.021493 | 0.151198 | 0.000000 | 0.000000 | 0.848802 | -0.272069 | 0.692401 | NaN | 0.102418 | 0.343831 | 31.294532 | 0.980570 | 0.228445 | 0.269138 | 0.382801 | 0.417789 | 0.350644 | 0.232972 | 0.262157 | 1.733860 | 0.267352 | -0.010451 | 1098.367037 | 4714.593791 | 1260.454232 | 15.839888 | 15.820267 | 17.394169 | 5.574543 | -0.090297 | -0.363050 | 0.161295 | 0.204258 | -0.049120 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 2 | AALI | 2010 | PT Astra Agro Lestari Tbk (IDX:AALI) | <NA> | <NA> | 7457257.000000 | 0 | <NA> | 2103652.000000 | 0 | <NA> | 1988 | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1334542.000000 | 8791799.000000 | 1.932000 | 1.262000 | 989325.000000 | 2051177.000000 | 1061852.000000 | 624694.000000 | 22315.000000 | NaN | NaN | NaN | 0.000000 | 6103150.000000 | 5234372.000000 | 8843721.000000 | 2992793.000000 | 3335987.000000 | 2992793.000000 | 2946657.000000 | 452232.000000 | 1574745000.000000 | 41258319.000000 | 26200.000000 | 7457257.000000 | 1240781.000000 | NaN | 1997 | 9 | 13 | 0.111868 | 0.233306 | 0.120778 | 0.141129 | 1.168507 | 0.304554 | 0.010879 | 0.151794 | 0.000000 | 0.000000 | 0.848206 | -0.371938 | 0.694187 | NaN | 0.112528 | 0.340407 | 30.915714 | 1.005906 | 0.239274 | 0.282095 | 0.379443 | 0.408126 | 0.338409 | 0.237870 | 0.335160 | 2.207991 | 0.333192 | 0.051438 | 1335.868347 | 5615.970205 | 1871.196289 | 15.989330 | 15.995218 | 17.535363 | 5.532640 | 0.191189 | 0.216231 | 0.161186 | 0.160371 | 0.484541 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 3 | AALI | 2011 | PT Astra Agro Lestari Tbk (IDX:AALI) | <NA> | <NA> | 8426158.000000 | 0 | <NA> | 2498565.000000 | 0 | <NA> | 1988 | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1778337.000000 | 10204495.000000 | 1.265000 | 0.582000 | 389456.000000 | 1857025.000000 | 1467569.000000 | 769903.000000 | NaN | NaN | NaN | NaN | 0.000000 | 7702571.000000 | 6837674.000000 | 10772582.000000 | 3195661.000000 | 3572651.000000 | 3195661.000000 | 3162475.000000 | -402591.000000 | 1574745000.000000 | 34171966.500000 | 21700.000000 | 8426158.000000 | 838190.000000 | NaN | 1997 | 9 | 14 | 0.036153 | 0.181981 | 0.143816 | 0.082139 | 0.571142 | 0.414589 | NaN | 0.174270 | 0.000000 | 0.000000 | 0.825730 | -0.234613 | 0.754821 | NaN | 0.038165 | 0.313162 | 19.215687 | 1.055670 | 0.244849 | 0.296525 | 0.350106 | 0.365271 | 0.296648 | 0.231937 | 0.309910 | 1.778333 | 0.293567 | -0.039452 | 1586.647362 | 6840.842168 | 2008.245779 | 16.138339 | 16.192515 | 17.346916 | 4.055462 | 0.218105 | 0.187727 | 0.160683 | 0.129927 | 0.073242 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 4 | AALI | 2012 | PT Astra Agro Lestari Tbk (IDX:AALI) | <NA> | <NA> | 9365411.000000 | 0 | <NA> | 2520266.000000 | 0 | <NA> | 1988 | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 4701077.000000 | 12419820.000000 | 0.685000 | 0.107000 | -820145.000000 | 1780395.000000 | 2600540.000000 | 1249050.000000 | NaN | NaN | 971950.000000 | NaN | 971950.000000 | 9894266.000000 | 7206837.000000 | 11564319.000000 | 3453729.000000 | 3973578.000000 | 3453729.000000 | 2609511.000000 | -610421.000000 | 1574745000.000000 | 31022476.500000 | 19700.000000 | 10337361.000000 | 227769.000000 | NaN | 1997 | 9 | 15 | -0.070920 | 0.143351 | 0.209386 | 0.018339 | 0.087585 | 0.701558 | NaN | 0.378514 | 0.078258 | 0.103781 | 0.754070 | 0.187282 | 0.796651 | NaN | -0.066035 | 0.278082 | 6.599015 | 0.931118 | 0.202923 | 0.269104 | 0.319938 | 0.376804 | 0.298654 | 0.217935 | 0.210109 | 0.555088 | 0.225652 | -0.049149 | 1600.428006 | 7343.613728 | 1657.100673 | 16.334804 | 16.263435 | 17.250223 | 3.312452 | 0.073496 | 0.008685 | 0.217093 | 0.111469 | -0.174852 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 8484 | ZONE | 2023 | PT Mega Perintis Tbk (IDX:ZONE) | 0 | <NA> | 376089.685000 | <NA> | <NA> | 46972.766000 | <NA> | <NA> | 2005 | Consumer Discretionary Distribution and Retail | NaN | NaN | 62.030000 | 376866.895000 | 752956.580000 | 1.787000 | 0.183000 | 187443.551000 | 425743.875000 | 238300.324000 | 370381.888000 | 8229.119000 | 45537.359000 | 85403.842000 | NaN | 230078.796000 | 254442.974000 | 327373.203000 | 735452.174000 | 80467.569000 | 109639.609000 | 80467.569000 | 101276.087000 | -292.586000 | 870171478.000000 | 965890.340580 | 1110.000000 | 606168.481000 | 4617.740000 | 36266.170000 | 2018 | 13 | 5 | 0.254868 | 0.565430 | 0.316486 | 0.006133 | 0.019378 | 0.869964 | 0.019329 | 0.500516 | 0.305567 | 0.611766 | 0.499484 | 2.056383 | 0.337925 | 0.048165 | 0.248943 | 0.106869 | 2.562948 | 0.976752 | 0.062384 | 0.124898 | 0.145612 | 0.554868 | 0.109412 | 0.063869 | 0.134505 | 0.268732 | 0.137706 | -0.000389 | 53.981045 | 845.180740 | 116.386356 | 13.531763 | 13.508241 | 13.780806 | 2.568245 | 0.092989 | -0.356013 | 0.155229 | 0.084306 | -0.015960 | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False |
| 8485 | ZONE | 2024 | PT Mega Perintis Tbk (IDX:ZONE) | <NA> | <NA> | 370674.214000 | <NA> | <NA> | 7044.347000 | <NA> | <NA> | 2005 | Consumer Discretionary Distribution and Retail | NaN | NaN | 62.030000 | 378069.858000 | 748744.072000 | 1.750000 | 0.178000 | 175199.380000 | 408802.739000 | 233603.359000 | 356567.003000 | 9325.106000 | 37101.093000 | 111850.141000 | NaN | 262139.509000 | 262571.197000 | 347862.483000 | 708360.249000 | 33764.860000 | 68145.652000 | 33764.860000 | 92620.371000 | 3123.094000 | 870171478.000000 | 717891.469350 | 825.000000 | 632813.723000 | 7740.834000 | 36707.806000 | 2018 | 13 | 6 | 0.247331 | 0.545985 | 0.311994 | 0.010338 | 0.033137 | 0.872223 | 0.022811 | 0.504939 | 0.350106 | 0.707197 | 0.495061 | 3.733161 | 0.350682 | 0.049026 | 0.233991 | 0.045095 | 1.898833 | 0.946065 | 0.009408 | 0.019004 | 0.091013 | 0.508919 | 0.047666 | 0.009945 | 0.123701 | 0.244982 | 0.130753 | 0.004171 | 8.095355 | 814.046733 | 106.439217 | 13.526153 | 13.470708 | 13.484074 | 1.936718 | -0.036837 | -0.850033 | -0.005595 | -0.014399 | -0.085467 | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False |
| 8486 | ZYRX | 2022 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | 0 | <NA> | 268289.329000 | 0 | <NA> | 78627.417000 | 0 | <NA> | 1996 | Technology Hardware and Equipment | NaN | NaN | 75.160000 | 438940.489000 | 707229.819000 | 1.442000 | 0.668000 | 190967.877000 | 623252.109000 | 432284.232000 | 321857.019000 | 32.094000 | NaN | 154814.692000 | NaN | 154972.192000 | 62069.361000 | 626487.712000 | 770370.215000 | 124050.223000 | 126197.456000 | 124050.223000 | 108226.350000 | 205303.596000 | 1333333837.000000 | 426666.704320 | 320.000000 | 423261.521000 | 206376.347000 | NaN | 2021 | 25 | 1 | 0.247891 | 0.881258 | 0.611236 | 0.291809 | 0.477409 | 0.516415 | 0.000051 | 0.620648 | 0.219126 | 0.577631 | 0.379352 | -0.407331 | 0.087764 | NaN | 0.270022 | 0.175403 | 0.972038 | 1.089278 | 0.111177 | 0.293069 | 0.178439 | 0.186771 | 0.161027 | 0.102064 | 0.153029 | 0.246563 | 0.140486 | 0.290293 | 58.970540 | 577.777443 | 81.169732 | 13.469111 | 13.554626 | 12.963758 | 1.590323 | NaN | NaN | NaN | NaN | NaN | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | True | False | False | False |
| 8487 | ZYRX | 2023 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | 0 | <NA> | 289448.779000 | <NA> | <NA> | 32952.892000 | <NA> | <NA> | 1996 | Technology Hardware and Equipment | NaN | NaN | 75.160000 | 201592.951000 | 491041.729000 | 2.073000 | 0.401000 | 208245.855000 | 402244.527000 | 193998.671000 | 320652.316000 | 1146.130000 | 136.413000 | 123103.971000 | NaN | 123328.362000 | 68457.678000 | 198332.059000 | 289633.302000 | 75643.212000 | 77211.203000 | 75643.212000 | -128496.218000 | -193672.184000 | 1333334556.000000 | 221333.536296 | 166.000000 | 412777.140000 | 12704.163000 | 149.572000 | 2021 | 25 | 2 | 0.718998 | 0.819166 | 0.395076 | 0.025872 | 0.065486 | 0.797158 | 0.002849 | 0.410541 | 0.251157 | 0.426080 | 0.589459 | 1.432748 | 0.139413 | 0.000305 | 0.424090 | 0.154046 | 1.097923 | 0.589834 | 0.067108 | 0.113847 | 0.157240 | 0.315230 | 0.261169 | 0.113775 | -0.261681 | -0.637404 | -0.443651 | -0.394411 | 24.714646 | 217.224777 | -96.372075 | 13.104284 | 12.576371 | 12.307426 | 0.764673 | -0.624034 | -0.580898 | -0.305683 | 0.078868 | -2.187291 | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | True | False | False | False |
| 8488 | ZYRX | 2024 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | <NA> | <NA> | 300050.888000 | <NA> | <NA> | 15171.892000 | <NA> | <NA> | 1996 | Technology Hardware and Equipment | NaN | NaN | 75.160000 | 92393.711000 | 392444.599000 | 3.402000 | 0.775000 | 201817.814000 | 285854.627000 | 84036.813000 | 219621.479000 | 1021.593000 | 41.627000 | 46780.579000 | NaN | 47050.482000 | 92660.098000 | 296495.058000 | 364238.674000 | 37276.725000 | 38767.823000 | 37276.725000 | 113472.348000 | -6431.849000 | 1333334556.000000 | 177333.495948 | 133.000000 | 347101.370000 | 6272.314000 | 388.017000 | 2021 | 25 | 3 | 0.554081 | 0.728395 | 0.214137 | 0.015983 | 0.074638 | 0.768298 | 0.003574 | 0.235431 | 0.119891 | 0.156808 | 0.764569 | 1.051856 | 0.236110 | 0.000989 | 0.514258 | 0.094986 | 1.919324 | 0.928128 | 0.038660 | 0.050564 | 0.098785 | 0.185987 | 0.102341 | 0.041654 | 0.289142 | 1.228139 | 0.311533 | -0.016389 | 11.378909 | 273.178755 | 85.104183 | 12.880151 | 12.805565 | 12.085787 | 0.591011 | 0.257586 | -0.539588 | -0.200792 | 0.036629 | -1.883079 | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | True | False | False | False |
8489 rows × 108 columns
Hapus Kolom Raw Number
exclude = {'ticker', 'Year', 'Entity Name', 'Parent Percent Owned (%)', 'Percent Owned - All Institutions (%)', 'Percent Owned - Insiders (%)', 'Industry Group'}
raw_cols = [c for c in df_fitur.columns if c in set(df_findat_final.columns) and c not in exclude]
raw_cols['Total Equity (Rp.M)',
'Net Income to Company (Rp.M)',
'Year Established',
'Total Liabilities (Rp.M)',
'Total Assets (Rp.M)',
'Working Capital (Rp.M)',
'Total Current Assets (Rp.M)',
'Total Current Liabilities (Rp.M)',
'Inventory (Rp.M)',
'Prepaid Exp. (Rp.M)',
'Long-term Debt (Rp.M)',
'Short-term Borrowings (Rp.M)',
'Current Portion of LT Debt & Leases (Rp.M)',
'Total Debt (Rp.M)',
'Net Property, Plant & Equipment (Rp.M)',
'Cost Of Goods Sold (Rp.M)',
'Total Revenue (Rp.M)',
'Operating Income (Rp.M)',
'EBITDA (Rp.M)',
'EBIT (Rp.M)',
'Cash from Ops. (Rp.M)',
'Net Change in Cash (Rp.M)',
'ECS Total Common Shares Outstanding (actual)',
'Market Capitalization (Rp.M)',
'Year Close Stock Price (Rp.)',
'Total Capital (Rp.M)',
'Cash & Short-term Investments (Rp.M)',
'Net Intangibles (Rp.M)',
'IPO Year']
df_fitur = df_fitur.drop(columns=raw_cols)
df_fitur| ticker | Year | Entity Name | target_distress_ppk | tahun_distress_ppk | target_distress_neg_equity | tahun_distress_neg_equity | target_distress_consecutive_loss | tahun_distress_consecutive_loss | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | CR | QR | Age_When_IPO | Years_Since_IPO | WC_Sales | CA_TA | CL_TA | CashST_TA | CashST_CL | Inventory_CA | Prepaid_CA | TL_TA | Debt_TA | Debt_Equity | Equity_TA | NetDebt_EBITDA | PPE_TA | Intang_TA | Altman_X1_WC_TA | Altman_X3_EBIT_TA | Altman_X4_MVE_TL | Altman_X5_SalesTA_AssetTurnover | ROA | ROE | EBITDA_TA | GrossMargin | OpMargin | NetMargin | CFO_TA | CFO_TL | CFO_Sales | NetCash_TA | EPS_proxy | Sales_per_share | CFO_per_share | log_TA | log_Sales | log_MktCap | PB | Sales_growth | NI_growth | TA_growth | Equity_growth | CFO_growth | industry_Banks | industry_Capital Goods | industry_Commercial and Professional Services | industry_Consumer Discretionary Distribution and Retail | industry_Consumer Durables and Apparel | industry_Consumer Services | industry_Consumer Staples Distribution and Retail | industry_Energy | industry_Financial Services | industry_Food Beverage and Tobacco | industry_Health Care Equipment and Services | industry_Household and Personal Products | industry_Insurance | industry_Materials | industry_Media and Entertainment | industry_Pharmaceuticals Biotechnology and Life Sciences | industry_Real Estate Management and Development | industry_Software and Services | industry_Technology Hardware and Equipment | industry_Telecommunication Services | industry_Transportation | industry_Utilities | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | AALI | 2008 | PT Astra Agro Lestari Tbk (IDX:AALI) | <NA> | <NA> | 0 | <NA> | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1.944000 | 0.878000 | 9 | 11 | 0.117567 | 0.303024 | 0.155859 | 0.133083 | 0.853871 | 0.395495 | 0.018945 | 0.181481 | 0.000000 | 0.000000 | 0.818519 | -0.239907 | 0.632481 | NaN | 0.147166 | 0.517036 | 13.042854 | 1.251761 | 0.416504 | 0.508850 | 0.554729 | 0.466033 | 0.413047 | 0.332734 | 0.320168 | 1.764201 | 0.255774 | -0.022255 | 1724.417604 | 5182.564161 | 1325.566362 | 15.690353 | 15.914904 | 16.551986 | 2.891836 | NaN | NaN | NaN | NaN | NaN | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 1 | AALI | 2009 | PT Astra Agro Lestari Tbk (IDX:AALI) | <NA> | <NA> | 0 | <NA> | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1.826000 | 1.007000 | 9 | 12 | 0.104448 | 0.226435 | 0.124016 | 0.104148 | 0.839797 | 0.355822 | 0.021493 | 0.151198 | 0.000000 | 0.000000 | 0.848802 | -0.272069 | 0.692401 | NaN | 0.102418 | 0.343831 | 31.294532 | 0.980570 | 0.228445 | 0.269138 | 0.382801 | 0.417789 | 0.350644 | 0.232972 | 0.262157 | 1.733860 | 0.267352 | -0.010451 | 1098.367037 | 4714.593791 | 1260.454232 | 15.839888 | 15.820267 | 17.394169 | 5.574543 | -0.090297 | -0.363050 | 0.161295 | 0.204258 | -0.049120 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 2 | AALI | 2010 | PT Astra Agro Lestari Tbk (IDX:AALI) | <NA> | <NA> | 0 | <NA> | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1.932000 | 1.262000 | 9 | 13 | 0.111868 | 0.233306 | 0.120778 | 0.141129 | 1.168507 | 0.304554 | 0.010879 | 0.151794 | 0.000000 | 0.000000 | 0.848206 | -0.371938 | 0.694187 | NaN | 0.112528 | 0.340407 | 30.915714 | 1.005906 | 0.239274 | 0.282095 | 0.379443 | 0.408126 | 0.338409 | 0.237870 | 0.335160 | 2.207991 | 0.333192 | 0.051438 | 1335.868347 | 5615.970205 | 1871.196289 | 15.989330 | 15.995218 | 17.535363 | 5.532640 | 0.191189 | 0.216231 | 0.161186 | 0.160371 | 0.484541 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 3 | AALI | 2011 | PT Astra Agro Lestari Tbk (IDX:AALI) | <NA> | <NA> | 0 | <NA> | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1.265000 | 0.582000 | 9 | 14 | 0.036153 | 0.181981 | 0.143816 | 0.082139 | 0.571142 | 0.414589 | NaN | 0.174270 | 0.000000 | 0.000000 | 0.825730 | -0.234613 | 0.754821 | NaN | 0.038165 | 0.313162 | 19.215687 | 1.055670 | 0.244849 | 0.296525 | 0.350106 | 0.365271 | 0.296648 | 0.231937 | 0.309910 | 1.778333 | 0.293567 | -0.039452 | 1586.647362 | 6840.842168 | 2008.245779 | 16.138339 | 16.192515 | 17.346916 | 4.055462 | 0.218105 | 0.187727 | 0.160683 | 0.129927 | 0.073242 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 4 | AALI | 2012 | PT Astra Agro Lestari Tbk (IDX:AALI) | <NA> | <NA> | 0 | <NA> | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 0.685000 | 0.107000 | 9 | 15 | -0.070920 | 0.143351 | 0.209386 | 0.018339 | 0.087585 | 0.701558 | NaN | 0.378514 | 0.078258 | 0.103781 | 0.754070 | 0.187282 | 0.796651 | NaN | -0.066035 | 0.278082 | 6.599015 | 0.931118 | 0.202923 | 0.269104 | 0.319938 | 0.376804 | 0.298654 | 0.217935 | 0.210109 | 0.555088 | 0.225652 | -0.049149 | 1600.428006 | 7343.613728 | 1657.100673 | 16.334804 | 16.263435 | 17.250223 | 3.312452 | 0.073496 | 0.008685 | 0.217093 | 0.111469 | -0.174852 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 8484 | ZONE | 2023 | PT Mega Perintis Tbk (IDX:ZONE) | 0 | <NA> | <NA> | <NA> | <NA> | <NA> | Consumer Discretionary Distribution and Retail | NaN | NaN | 62.030000 | 1.787000 | 0.183000 | 13 | 5 | 0.254868 | 0.565430 | 0.316486 | 0.006133 | 0.019378 | 0.869964 | 0.019329 | 0.500516 | 0.305567 | 0.611766 | 0.499484 | 2.056383 | 0.337925 | 0.048165 | 0.248943 | 0.106869 | 2.562948 | 0.976752 | 0.062384 | 0.124898 | 0.145612 | 0.554868 | 0.109412 | 0.063869 | 0.134505 | 0.268732 | 0.137706 | -0.000389 | 53.981045 | 845.180740 | 116.386356 | 13.531763 | 13.508241 | 13.780806 | 2.568245 | 0.092989 | -0.356013 | 0.155229 | 0.084306 | -0.015960 | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False |
| 8485 | ZONE | 2024 | PT Mega Perintis Tbk (IDX:ZONE) | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | Consumer Discretionary Distribution and Retail | NaN | NaN | 62.030000 | 1.750000 | 0.178000 | 13 | 6 | 0.247331 | 0.545985 | 0.311994 | 0.010338 | 0.033137 | 0.872223 | 0.022811 | 0.504939 | 0.350106 | 0.707197 | 0.495061 | 3.733161 | 0.350682 | 0.049026 | 0.233991 | 0.045095 | 1.898833 | 0.946065 | 0.009408 | 0.019004 | 0.091013 | 0.508919 | 0.047666 | 0.009945 | 0.123701 | 0.244982 | 0.130753 | 0.004171 | 8.095355 | 814.046733 | 106.439217 | 13.526153 | 13.470708 | 13.484074 | 1.936718 | -0.036837 | -0.850033 | -0.005595 | -0.014399 | -0.085467 | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False |
| 8486 | ZYRX | 2022 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | 0 | <NA> | 0 | <NA> | 0 | <NA> | Technology Hardware and Equipment | NaN | NaN | 75.160000 | 1.442000 | 0.668000 | 25 | 1 | 0.247891 | 0.881258 | 0.611236 | 0.291809 | 0.477409 | 0.516415 | 0.000051 | 0.620648 | 0.219126 | 0.577631 | 0.379352 | -0.407331 | 0.087764 | NaN | 0.270022 | 0.175403 | 0.972038 | 1.089278 | 0.111177 | 0.293069 | 0.178439 | 0.186771 | 0.161027 | 0.102064 | 0.153029 | 0.246563 | 0.140486 | 0.290293 | 58.970540 | 577.777443 | 81.169732 | 13.469111 | 13.554626 | 12.963758 | 1.590323 | NaN | NaN | NaN | NaN | NaN | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | True | False | False | False |
| 8487 | ZYRX | 2023 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | 0 | <NA> | <NA> | <NA> | <NA> | <NA> | Technology Hardware and Equipment | NaN | NaN | 75.160000 | 2.073000 | 0.401000 | 25 | 2 | 0.718998 | 0.819166 | 0.395076 | 0.025872 | 0.065486 | 0.797158 | 0.002849 | 0.410541 | 0.251157 | 0.426080 | 0.589459 | 1.432748 | 0.139413 | 0.000305 | 0.424090 | 0.154046 | 1.097923 | 0.589834 | 0.067108 | 0.113847 | 0.157240 | 0.315230 | 0.261169 | 0.113775 | -0.261681 | -0.637404 | -0.443651 | -0.394411 | 24.714646 | 217.224777 | -96.372075 | 13.104284 | 12.576371 | 12.307426 | 0.764673 | -0.624034 | -0.580898 | -0.305683 | 0.078868 | -2.187291 | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | True | False | False | False |
| 8488 | ZYRX | 2024 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | Technology Hardware and Equipment | NaN | NaN | 75.160000 | 3.402000 | 0.775000 | 25 | 3 | 0.554081 | 0.728395 | 0.214137 | 0.015983 | 0.074638 | 0.768298 | 0.003574 | 0.235431 | 0.119891 | 0.156808 | 0.764569 | 1.051856 | 0.236110 | 0.000989 | 0.514258 | 0.094986 | 1.919324 | 0.928128 | 0.038660 | 0.050564 | 0.098785 | 0.185987 | 0.102341 | 0.041654 | 0.289142 | 1.228139 | 0.311533 | -0.016389 | 11.378909 | 273.178755 | 85.104183 | 12.880151 | 12.805565 | 12.085787 | 0.591011 | 0.257586 | -0.539588 | -0.200792 | 0.036629 | -1.883079 | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | True | False | False | False |
8489 rows × 79 columns
Hasil Kolom Tersisa
df_fitur.columns.to_list()['ticker',
'Year',
'Entity Name',
'target_distress_ppk',
'tahun_distress_ppk',
'target_distress_neg_equity',
'tahun_distress_neg_equity',
'target_distress_consecutive_loss',
'tahun_distress_consecutive_loss',
'Industry Group',
'Parent Percent Owned (%)',
'Percent Owned - All Institutions (%)',
'Percent Owned - Insiders (%)',
'CR',
'QR',
'Age_When_IPO',
'Years_Since_IPO',
'WC_Sales',
'CA_TA',
'CL_TA',
'CashST_TA',
'CashST_CL',
'Inventory_CA',
'Prepaid_CA',
'TL_TA',
'Debt_TA',
'Debt_Equity',
'Equity_TA',
'NetDebt_EBITDA',
'PPE_TA',
'Intang_TA',
'Altman_X1_WC_TA',
'Altman_X3_EBIT_TA',
'Altman_X4_MVE_TL',
'Altman_X5_SalesTA_AssetTurnover',
'ROA',
'ROE',
'EBITDA_TA',
'GrossMargin',
'OpMargin',
'NetMargin',
'CFO_TA',
'CFO_TL',
'CFO_Sales',
'NetCash_TA',
'EPS_proxy',
'Sales_per_share',
'CFO_per_share',
'log_TA',
'log_Sales',
'log_MktCap',
'PB',
'Sales_growth',
'NI_growth',
'TA_growth',
'Equity_growth',
'CFO_growth',
'industry_Banks',
'industry_Capital Goods',
'industry_Commercial and Professional Services',
'industry_Consumer Discretionary Distribution and Retail',
'industry_Consumer Durables and Apparel',
'industry_Consumer Services',
'industry_Consumer Staples Distribution and Retail',
'industry_Energy',
'industry_Financial Services',
'industry_Food Beverage and Tobacco',
'industry_Health Care Equipment and Services',
'industry_Household and Personal Products',
'industry_Insurance',
'industry_Materials',
'industry_Media and Entertainment',
'industry_Pharmaceuticals Biotechnology and Life Sciences',
'industry_Real Estate Management and Development',
'industry_Software and Services',
'industry_Technology Hardware and Equipment',
'industry_Telecommunication Services',
'industry_Transportation',
'industry_Utilities']
Feature Data Understanding
Sebaran Data
targets = [col for col in df_fitur.columns if col.startswith('target')]
excluded_cols = [
'ticker', 'Year', 'Entity Name',
'tahun_distress_ppk', 'tahun_distress_neg_equity', 'tahun_distress_consecutive_loss'
]
# --- 1. Persiapan & Pemisahan Kolom ---
cols_to_exclude = targets + excluded_cols
potential_cols = [col for col in df_fitur.columns if col not in cols_to_exclude]
numeric_cols = []
categorical_cols = []
for col in potential_cols:
# Cek prioritas: Industry -> Boolean -> Object -> Numeric
if col.startswith('industry') or df_fitur[col].dtype == 'bool' or df_fitur[col].dtype == 'object':
categorical_cols.append(col)
elif pd.api.types.is_numeric_dtype(df_fitur[col]):
numeric_cols.append(col)
print(f"Total Baris Data: {len(df_fitur)}")
print(f"Fitur Numerik: {len(numeric_cols)}")
print(f"Fitur Kategorikal: {len(categorical_cols)}")Total Baris Data: 8489
Fitur Numerik: 47
Fitur Kategorikal: 23
# =========================
# PILIH KOLOM NUMERIK SAJA
# =========================
targets = [col for col in df_fitur.columns if col.startswith('target')]
industries = [col for col in df_fitur.columns if col.startswith('industry')]
excluded_cols = [
'ticker', 'Year', 'Entity Name',
'tahun_distress_ppk', 'tahun_distress_neg_equity', 'tahun_distress_consecutive_loss'
]
cols_to_exclude = set(targets + excluded_cols + industries)
potential_cols = [col for col in df_fitur.columns if col not in cols_to_exclude]
numeric_cols = [
col for col in potential_cols
if pd.api.types.is_numeric_dtype(df_fitur[col])
]
print(f"Total Baris Data: {len(df_fitur)}")
print(f"Fitur Numerik: {len(numeric_cols)}")
N = len(df_fitur)
# =========================
# VISUALISASI NUMERIK (Boxplot + Hist)
# =========================
features_per_page = 5
num_batches = math.ceil(len(numeric_cols) / features_per_page)
MAX_POINTS_PLOT = 80_000 # sampling agar plotting stabil (tidak mengubah df)
MAX_POINTS_KDE = 8_000 # KDE hanya kalau data kecil (hindari hang)
HIST_BINS = 50 # bins tetap agar cepat & konsisten
for i in range(num_batches):
start = i * features_per_page
end = min((i + 1) * features_per_page, len(numeric_cols))
batch = numeric_cols[start:end]
fig, axes = plt.subplots(len(batch), 2, figsize=(16, 4 * len(batch)))
fig.suptitle(f'Distribusi Numerik Part {i+1}', fontsize=16, fontweight='bold', y=1.01)
if len(batch) == 1:
axes = [axes]
for idx, col in enumerate(batch):
ax_box = axes[idx][0]
ax_hist = axes[idx][1]
try:
s_raw = df_fitur[col]
# hitung null (tanpa mengubah df)
null_count = int(s_raw.isna().sum())
null_prop = null_count / N if N else 0.0
# bersihkan untuk plotting: inf -> nan, coerce numeric, dropna sementara
s_clean = (
s_raw.replace([np.inf, -np.inf], np.nan)
.pipe(pd.to_numeric, errors="coerce")
.dropna()
)
if s_clean.empty:
msg = f"ALL NULL/INF | Null: {null_count} ({null_prop:.1%})"
ax_box.set_title(f"Outlier Check: {col}", fontsize=10, fontweight='bold')
ax_box.text(0.5, 0.5, msg, ha='center', va='center')
ax_hist.set_title(msg, fontsize=10)
ax_hist.text(0.5, 0.5, "No data to plot", ha='center', va='center')
continue
# sampling agar tidak berat
s_plot = s_clean.sample(MAX_POINTS_PLOT, random_state=42) if len(s_clean) > MAX_POINTS_PLOT else s_clean
# statistik (dari non-null)
desc = s_plot.describe()
skew = s_plot.skew()
stats_text = (
f"Min: {desc['min']:.2f} | Max: {desc['max']:.2f} | "
f"Mean: {desc['mean']:.2f} | Med: {desc['50%']:.2f} | "
f"Skew: {skew:.2f} | "
f"Null: {null_count} ({null_prop:.1%})"
)
# boxplot
sns.boxplot(x=s_plot, ax=ax_box, color='#3498db')
ax_box.set_title(f"Outlier Check: {col}", fontsize=10, fontweight='bold')
ax_box.set_xlabel('')
# hist (+ KDE dibatasi agar tidak hang)
use_kde = (s_plot.nunique() > 1) and (len(s_plot) <= MAX_POINTS_KDE)
sns.histplot(s_plot, bins=HIST_BINS, kde=use_kde, ax=ax_hist, color='#e67e22', stat='density')
ax_hist.axvline(desc['mean'], color='red', linestyle='--', label='Mean')
ax_hist.axvline(desc['50%'], color='green', linestyle='-', label='Median')
ax_hist.set_title(stats_text, fontsize=10)
ax_hist.legend(loc='upper right', fontsize='small')
ax_hist.set_xlabel('')
except Exception as e:
err = f"{type(e).__name__}: {e}"
ax_box.clear()
ax_hist.clear()
ax_box.set_title(f"Outlier Check: {col}", fontsize=10, fontweight='bold')
ax_box.text(0.5, 0.5, f"Error:\n{err}", ha='center', va='center', wrap=True)
ax_hist.set_title(f"Error on: {col}", fontsize=10)
ax_hist.text(0.5, 0.5, f"Error:\n{err}", ha='center', va='center', wrap=True)
plt.tight_layout()
plt.show()
plt.close(fig)
plt.close('all')Total Baris Data: 8489
Fitur Numerik: 47










# Menghitung min dan max, lalu di-transpose (.T) agar nama kolom berjejer ke bawah
min_max_summary = df_fitur[numeric_cols].agg(['min', 'max']).T
print("=== Ringkasan Nilai Minimum dan Maksimum ===")
print(min_max_summary)=== Ringkasan Nilai Minimum dan Maksimum ===
min max
Parent Percent Owned (%) 1.010000 100.000000
Percent Owned - All Institutions (%) 0.000000 99.350000
Percent Owned - Insiders (%) 0.000000 92.500000
CR 0.000000 276.365000
QR 0.000000 297.354000
Age_When_IPO 0.000000 184.000000
Years_Since_IPO 1.000000 47.000000
WC_Sales -34330.458213 15731.271526
CA_TA 0.001489 1.000000
CL_TA 0.000026 3183.852925
CashST_TA 0.000004 0.998560
CashST_CL 0.000128 8055.691012
Inventory_CA 0.000000 2.029279
Prepaid_CA 0.000000 0.680819
TL_TA 0.000102 3191.109276
Debt_TA 0.000000 1089.080293
Debt_Equity -2932.733637 121.548567
Equity_TA -3190.109289 0.999898
NetDebt_EBITDA -7836.128283 9588.426487
PPE_TA 0.000000 0.989719
Intang_TA 0.000000 1.337714
Altman_X1_WC_TA -3183.211894 0.995606
Altman_X3_EBIT_TA -428.451925 1.373432
Altman_X4_MVE_TL 0.002928 6996.499691
Altman_X5_SalesTA_AssetTurnover -1.172519 28.823814
ROA -1391.146305 3612.433265
ROE -1453.379504 139.022391
EBITDA_TA -110.412651 19.026523
GrossMargin -1312.195304 9.075947
OpMargin -1685.703170 91.388504
NetMargin -1672.147241 984.260833
CFO_TA -90.064793 1.950671
CFO_TL -92.228120 58.936858
CFO_Sales -3425.034582 797.276777
NetCash_TA -6.540044 0.848656
EPS_proxy -25683068.783069 918674.008205
Sales_per_share -23102507.936508 2127660.925358
CFO_per_share -9389487.162303 17421.566813
log_TA 4.167146 21.610014
log_Sales -16.212612 19.617387
log_MktCap 8.621553 24.636501
PB -1848.307716 38071.722785
Sales_growth -265.130822 1923.627045
NI_growth -6386.715890 3864.340840
TA_growth -0.999342 3154.072957
Equity_growth -871.179934 1060.375319
CFO_growth -613.065049 1397.118210
targets = [col for col in df_fitur.columns if col.startswith('target')]
years = [col for col in df_fitur.columns if col.startswith('tahun_distress')]
industries = [col for col in df_fitur.columns if col.startswith('industry')]
excluded_cols = [
'ticker', 'Year', 'Entity Name',
] + targets + years + industries
dist_cols = [col for col in df_fitur.select_dtypes(include='number').columns.tolist() if col not in excluded_cols]
ncols = 6
nrows = int(np.ceil(len(dist_cols) / ncols))
fig, axes = plt.subplots(nrows, ncols, figsize=(4 * ncols, 3 * nrows))
for ax, col in zip(axes.flatten(), dist_cols):
data = df_fitur[col].dropna()
ax.hist(data, bins=50, color='steelblue')
ax.set_title(col, fontsize=18)
ax.tick_params(labelsize=7)
for ax in axes.flatten()[len(dist_cols):]:
ax.axis('off')
fig.suptitle('Distribusi Nilai Kolom Numerik', y=1.002, fontsize=30, fontweight='bold')
plt.tight_layout()
plt.show()
targets = [col for col in df_fitur.columns if col.startswith('target')]
years = [col for col in df_fitur.columns if col.startswith('tahun_distress')]
industries = [col for col in df_fitur.columns if col.startswith('industry')]
excluded_cols = [
'ticker', 'Year', 'Entity Name',
] + targets + years + industries
dist_cols = [col for col in df_fitur.select_dtypes(include='number').columns.tolist() if col not in excluded_cols]
ncols = 6
nrows = int(np.ceil(len(dist_cols) / ncols))
fig, axes = plt.subplots(nrows, ncols, figsize=(4 * ncols, 3 * nrows))
for ax, col in zip(axes.flatten(), dist_cols):
data = df_fitur[col].dropna()
ax.boxplot(data, vert=False,
patch_artist=True,
boxprops=dict(facecolor='steelblue', color='steelblue'),
medianprops=dict(color='black'))
ax.set_title(col, fontsize=18)
ax.tick_params(labelsize=7)
for ax in axes.flatten()[len(dist_cols):]:
ax.axis('off')
fig.suptitle('Distribusi Nilai Kolom Numerik', y=1.002, fontsize=30, fontweight='bold')
plt.tight_layout()
plt.show()
Korelasi Antar Kolom
Pearson
corr_matrix_pearson = (
df_fitur[numeric_cols]
.apply(pd.to_numeric, errors="coerce")
.corr()
)
mask = np.triu(np.ones_like(corr_matrix_pearson, dtype=bool))
plt.figure(figsize=(18, 14))
sns.heatmap(
corr_matrix_pearson,
mask=mask,
cmap="RdBu_r",
center=0,
vmin=-1,
vmax=1,
linewidths=0.3,
cbar_kws={"shrink": 0.8}
)
plt.title("Heatmap Korelasi (PEARSON) Antar Kolom Numerik pada df_fitur", fontsize=14, fontweight="bold")
plt.xticks(rotation=45, ha="right")
plt.yticks(rotation=0)
plt.tight_layout()
plt.show()
# 1. Tentukan ambang batas (threshold) korelasi tinggi, misalnya 0.8 atau 0.85
threshold = 0.80
# 2. Ambil nilai absolut dari matriks korelasi
corr_abs_pearson = corr_matrix_pearson.abs()
# 3. Buat "topeng" (mask) untuk mengambil nilai di segitiga atas (upper triangle)
# k=1 berfungsi untuk membuang garis diagonal utama (korelasi dengan diri sendiri)
upper_triangle = corr_abs_pearson.where(np.triu(np.ones(corr_abs_pearson.shape), k=1).astype(bool))
# 4. Ubah format menjadi series berjejer (stack) dan saring berdasarkan threshold
high_corr_pairs = upper_triangle.stack()[upper_triangle.stack() > threshold]
# 5. Urutkan dari korelasi tertinggi ke terendah, lalu cetak
high_corr_pairs = high_corr_pairs.sort_values(ascending=False)
print(f"Daftar pasangan fitur dengan korelasi Pearson > {threshold}:\n")
for (fitur_1, fitur_2), nilai_absolut in high_corr_pairs.items():
# Mengambil nilai asli (positif/negatif) dari matriks awal
nilai_asli = corr_matrix_pearson.loc[fitur_1, fitur_2]
print(f"{fitur_1} & {fitur_2} : {nilai_asli:.4f}")Daftar pasangan fitur dengan korelasi Pearson > 0.8:
TL_TA & Equity_TA : -1.0000
CL_TA & Altman_X1_WC_TA : -1.0000
CL_TA & Equity_TA : -0.9996
CL_TA & TL_TA : 0.9996
Equity_TA & Altman_X1_WC_TA : 0.9996
TL_TA & Altman_X1_WC_TA : -0.9996
EPS_proxy & Sales_per_share : 0.9984
Debt_TA & Equity_TA : -0.9981
TL_TA & Debt_TA : 0.9981
Altman_X1_WC_TA & Altman_X3_EBIT_TA : 0.9971
CL_TA & Altman_X3_EBIT_TA : -0.9970
CL_TA & Debt_TA : 0.9964
Debt_TA & Altman_X1_WC_TA : -0.9964
Equity_TA & Altman_X3_EBIT_TA : 0.9960
TL_TA & Altman_X3_EBIT_TA : -0.9960
Altman_X3_EBIT_TA & CFO_TA : 0.9940
Debt_TA & Altman_X3_EBIT_TA : -0.9899
Altman_X1_WC_TA & CFO_TA : 0.9875
CL_TA & CFO_TA : -0.9875
Equity_TA & CFO_TA : 0.9865
TL_TA & CFO_TA : -0.9865
Debt_TA & CFO_TA : -0.9798
CR & QR : 0.9522
EBITDA_TA & CFO_TA : 0.9510
Altman_X3_EBIT_TA & EBITDA_TA : 0.9494
Altman_X1_WC_TA & EBITDA_TA : 0.9355
CL_TA & EBITDA_TA : -0.9355
Equity_TA & EBITDA_TA : 0.9351
TL_TA & EBITDA_TA : -0.9351
Debt_TA & EBITDA_TA : -0.9244
QR & CashST_CL : 0.8509
log_TA & log_MktCap : 0.8266
WC_Sales & CFO_Sales : 0.8116
CR & CashST_CL : 0.8060
Spearman
corr_matrix_spearman = (
df_fitur[numeric_cols]
.apply(pd.to_numeric, errors="coerce")
.corr(method='spearman')
)
mask = np.triu(np.ones_like(corr_matrix_spearman, dtype=bool))
plt.figure(figsize=(18, 14))
sns.heatmap(
corr_matrix_spearman,
mask=mask,
cmap="RdBu_r",
center=0,
vmin=-1,
vmax=1,
linewidths=0.3,
cbar_kws={"shrink": 0.8}
)
plt.title("Heatmap Korelasi (SPEARMAN) Antar Kolom Numerik pada df_fitur", fontsize=14, fontweight="bold")
plt.xticks(rotation=45, ha="right")
plt.yticks(rotation=0)
plt.tight_layout()
plt.show()
# 1. Tentukan ambang batas (threshold) korelasi tinggi, misalnya 0.8 atau 0.85
threshold = 0.80
# 2. Ambil nilai absolut dari matriks korelasi
corr_abs_spearman = corr_matrix_spearman.abs()
# 3. Buat "topeng" (mask) untuk mengambil nilai di segitiga atas (upper triangle)
# k=1 berfungsi untuk membuang garis diagonal utama (korelasi dengan diri sendiri)
upper_triangle = corr_abs_spearman.where(np.triu(np.ones(corr_abs_spearman.shape), k=1).astype(bool))
# 4. Ubah format menjadi series berjejer (stack) dan saring berdasarkan threshold
high_corr_pairs = upper_triangle.stack()[upper_triangle.stack() > threshold]
# 5. Urutkan dari korelasi tertinggi ke terendah, lalu cetak
high_corr_pairs = high_corr_pairs.sort_values(ascending=False)
print(f"Daftar pasangan fitur dengan korelasi Spearman > {threshold}:\n")
for (fitur_1, fitur_2), nilai_absolut in high_corr_pairs.items():
# Mengambil nilai asli (positif/negatif) dari matriks awal
nilai_asli = corr_matrix_spearman.loc[fitur_1, fitur_2]
print(f"{fitur_1} & {fitur_2} : {nilai_asli:.4f}")Daftar pasangan fitur dengan korelasi Spearman > 0.8:
TL_TA & Equity_TA : -0.9820
CFO_TA & CFO_TL : 0.9357
Altman_X3_EBIT_TA & EBITDA_TA : 0.9344
CR & Altman_X1_WC_TA : 0.9094
CR & WC_Sales : 0.8636
CFO_TA & CFO_per_share : 0.8615
Altman_X3_EBIT_TA & ROA : 0.8475
log_TA & log_MktCap : 0.8339
WC_Sales & Altman_X1_WC_TA : 0.8322
ROA & EPS_proxy : 0.8270
CashST_TA & CashST_CL : 0.8212
CR & QR : 0.8211
CFO_TA & CFO_Sales : 0.8182
ROA & ROE : 0.8065
CFO_TL & CFO_Sales : 0.8044
OpMargin & NetMargin : 0.8038
CFO_TL & CFO_per_share : 0.8012
Variance Inflation Factors (VIF)
# # 1. Siapkan data (VIF tidak bisa memproses data yang memiliki missing values/NaN)
# # Pastikan menggunakan data yang sudah di-imputasi!
# X = df_fitur[numeric_cols].copy()
# # 2. Tambahkan konstanta (Intercept)
# # Ini adalah langkah WAJIB. Tanpa konstanta, nilai VIF akan terdistorsi menjadi sangat besar
# X = add_constant(X)
# # 3. Hitung VIF untuk setiap fitur
# vif_data = pd.DataFrame()
# vif_data["Fitur"] = X.columns
# vif_data["VIF"] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]
# # 4. Buang baris 'const' (karena kita hanya peduli pada VIF rasio keuangannya saja)
# vif_data = vif_data[vif_data["Fitur"] != "const"]
# # 5. Urutkan dari VIF tertinggi ke terendah agar rapi di grafik
# vif_data = vif_data.sort_values(by="VIF", ascending=False)
# # ==========================================
# # BAGIAN VISUALISASI
# # ==========================================
# plt.figure(figsize=(10, max(6, len(numeric_cols) * 0.4))) # Tinggi gambar menyesuaikan jumlah fitur
# # Buat bar chart
# sns.barplot(x="VIF", y="Fitur", data=vif_data, palette="coolwarm_r")
# # Tambahkan garis putus-putus sebagai batas toleransi (Threshold)
# plt.axvline(x=10, color='red', linestyle='--', linewidth=2, label='Batas Bahaya (VIF = 10)')
# plt.axvline(x=5, color='orange', linestyle='--', linewidth=2, label='Batas Peringatan (VIF = 5)')
# # Rapikan teks dan label
# plt.title("Deteksi Multikolinearitas dengan Variance Inflation Factor (VIF)", fontsize=14, pad=15)
# plt.xlabel("Skor VIF", fontsize=12)
# plt.ylabel("Fitur / Rasio Keuangan", fontsize=12)
# plt.legend()
# plt.tight_layout()
# # Tampilkan grafik
# plt.show()
# # Cetak juga dalam bentuk tabel teks untuk dokumentasi
# print(vif_data.to_string(index=False))Sebaran Nilai Negatif
id_cols = ['ticker', 'Year', 'Entity Name']
cols_to_check = [
col for col in df_fitur.select_dtypes(include=['number']).columns
if 'growth' not in col.lower()
and col not in id_cols
and 'distress' not in col.lower()
]
neg_counts = (df_fitur[cols_to_check] < 0).sum()
neg_proportions = (neg_counts / len(df_fitur)) * 100
# gabungkan 1 df
neg_report = pd.DataFrame({
'Negative Count': neg_counts,
'Percentage (%)': neg_proportions
}).sort_values(by='Negative Count', ascending=False)
print(f"Total baris dalam dataset: {len(df_fitur)}")
print("-" * 50)
print(neg_report)Total baris dalam dataset: 8489
--------------------------------------------------
Negative Count Percentage (%)
NetCash_TA 4025 47.414301
NetDebt_EBITDA 2694 31.735187
CFO_TA 2529 29.791495
CFO_TL 2529 29.791495
CFO_per_share 2528 29.779715
CFO_Sales 2506 29.520556
Altman_X1_WC_TA 2391 28.165862
WC_Sales 2384 28.083402
ROA 2032 23.936859
EPS_proxy 2032 23.936859
NetMargin 1947 22.935564
ROE 1884 22.193427
OpMargin 1636 19.271999
Altman_X3_EBIT_TA 1540 18.141124
EBITDA_TA 935 11.014254
GrossMargin 340 4.005183
Equity_TA 310 3.651785
Debt_Equity 308 3.628225
PB 307 3.616445
log_Sales 71 0.836376
Sales_per_share 71 0.836376
Altman_X5_SalesTA_AssetTurnover 71 0.836376
Parent Percent Owned (%) 0 0.000000
QR 0 0.000000
Percent Owned - Insiders (%) 0 0.000000
Percent Owned - All Institutions (%) 0 0.000000
CL_TA 0 0.000000
CA_TA 0 0.000000
Years_Since_IPO 0 0.000000
Age_When_IPO 0 0.000000
CR 0 0.000000
Altman_X4_MVE_TL 0 0.000000
Debt_TA 0 0.000000
TL_TA 0 0.000000
CashST_TA 0 0.000000
Intang_TA 0 0.000000
PPE_TA 0 0.000000
CashST_CL 0 0.000000
Prepaid_CA 0 0.000000
Inventory_CA 0 0.000000
log_TA 0 0.000000
log_MktCap 0 0.000000
id_cols = ['ticker', 'Year', 'Entity Name']
cols_to_check = [
col for col in df_fitur.select_dtypes(include=['number']).columns
if 'growth' not in col.lower()
and col not in id_cols
and 'distress' not in col.lower()
]
df_negatif = df_fitur[(df_fitur[cols_to_check] < 0).any(axis=1)]
df_negatif| ticker | Year | Entity Name | target_distress_ppk | tahun_distress_ppk | target_distress_neg_equity | tahun_distress_neg_equity | target_distress_consecutive_loss | tahun_distress_consecutive_loss | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | CR | QR | Age_When_IPO | Years_Since_IPO | WC_Sales | CA_TA | CL_TA | CashST_TA | CashST_CL | Inventory_CA | Prepaid_CA | TL_TA | Debt_TA | Debt_Equity | Equity_TA | NetDebt_EBITDA | PPE_TA | Intang_TA | Altman_X1_WC_TA | Altman_X3_EBIT_TA | Altman_X4_MVE_TL | Altman_X5_SalesTA_AssetTurnover | ROA | ROE | EBITDA_TA | GrossMargin | OpMargin | NetMargin | CFO_TA | CFO_TL | CFO_Sales | NetCash_TA | EPS_proxy | Sales_per_share | CFO_per_share | log_TA | log_Sales | log_MktCap | PB | Sales_growth | NI_growth | TA_growth | Equity_growth | CFO_growth | industry_Banks | industry_Capital Goods | industry_Commercial and Professional Services | industry_Consumer Discretionary Distribution and Retail | industry_Consumer Durables and Apparel | industry_Consumer Services | industry_Consumer Staples Distribution and Retail | industry_Energy | industry_Financial Services | industry_Food Beverage and Tobacco | industry_Health Care Equipment and Services | industry_Household and Personal Products | industry_Insurance | industry_Materials | industry_Media and Entertainment | industry_Pharmaceuticals Biotechnology and Life Sciences | industry_Real Estate Management and Development | industry_Software and Services | industry_Technology Hardware and Equipment | industry_Telecommunication Services | industry_Transportation | industry_Utilities | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | AALI | 2008 | PT Astra Agro Lestari Tbk (IDX:AALI) | <NA> | <NA> | 0 | <NA> | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1.944000 | 0.878000 | 9 | 11 | 0.117567 | 0.303024 | 0.155859 | 0.133083 | 0.853871 | 0.395495 | 0.018945 | 0.181481 | 0.000000 | 0.000000 | 0.818519 | -0.239907 | 0.632481 | NaN | 0.147166 | 0.517036 | 13.042854 | 1.251761 | 0.416504 | 0.508850 | 0.554729 | 0.466033 | 0.413047 | 0.332734 | 0.320168 | 1.764201 | 0.255774 | -0.022255 | 1724.417604 | 5182.564161 | 1325.566362 | 15.690353 | 15.914904 | 16.551986 | 2.891836 | NaN | NaN | NaN | NaN | NaN | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 1 | AALI | 2009 | PT Astra Agro Lestari Tbk (IDX:AALI) | <NA> | <NA> | 0 | <NA> | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1.826000 | 1.007000 | 9 | 12 | 0.104448 | 0.226435 | 0.124016 | 0.104148 | 0.839797 | 0.355822 | 0.021493 | 0.151198 | 0.000000 | 0.000000 | 0.848802 | -0.272069 | 0.692401 | NaN | 0.102418 | 0.343831 | 31.294532 | 0.980570 | 0.228445 | 0.269138 | 0.382801 | 0.417789 | 0.350644 | 0.232972 | 0.262157 | 1.733860 | 0.267352 | -0.010451 | 1098.367037 | 4714.593791 | 1260.454232 | 15.839888 | 15.820267 | 17.394169 | 5.574543 | -0.090297 | -0.363050 | 0.161295 | 0.204258 | -0.049120 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 2 | AALI | 2010 | PT Astra Agro Lestari Tbk (IDX:AALI) | <NA> | <NA> | 0 | <NA> | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1.932000 | 1.262000 | 9 | 13 | 0.111868 | 0.233306 | 0.120778 | 0.141129 | 1.168507 | 0.304554 | 0.010879 | 0.151794 | 0.000000 | 0.000000 | 0.848206 | -0.371938 | 0.694187 | NaN | 0.112528 | 0.340407 | 30.915714 | 1.005906 | 0.239274 | 0.282095 | 0.379443 | 0.408126 | 0.338409 | 0.237870 | 0.335160 | 2.207991 | 0.333192 | 0.051438 | 1335.868347 | 5615.970205 | 1871.196289 | 15.989330 | 15.995218 | 17.535363 | 5.532640 | 0.191189 | 0.216231 | 0.161186 | 0.160371 | 0.484541 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 3 | AALI | 2011 | PT Astra Agro Lestari Tbk (IDX:AALI) | <NA> | <NA> | 0 | <NA> | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1.265000 | 0.582000 | 9 | 14 | 0.036153 | 0.181981 | 0.143816 | 0.082139 | 0.571142 | 0.414589 | NaN | 0.174270 | 0.000000 | 0.000000 | 0.825730 | -0.234613 | 0.754821 | NaN | 0.038165 | 0.313162 | 19.215687 | 1.055670 | 0.244849 | 0.296525 | 0.350106 | 0.365271 | 0.296648 | 0.231937 | 0.309910 | 1.778333 | 0.293567 | -0.039452 | 1586.647362 | 6840.842168 | 2008.245779 | 16.138339 | 16.192515 | 17.346916 | 4.055462 | 0.218105 | 0.187727 | 0.160683 | 0.129927 | 0.073242 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 4 | AALI | 2012 | PT Astra Agro Lestari Tbk (IDX:AALI) | <NA> | <NA> | 0 | <NA> | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 0.685000 | 0.107000 | 9 | 15 | -0.070920 | 0.143351 | 0.209386 | 0.018339 | 0.087585 | 0.701558 | NaN | 0.378514 | 0.078258 | 0.103781 | 0.754070 | 0.187282 | 0.796651 | NaN | -0.066035 | 0.278082 | 6.599015 | 0.931118 | 0.202923 | 0.269104 | 0.319938 | 0.376804 | 0.298654 | 0.217935 | 0.210109 | 0.555088 | 0.225652 | -0.049149 | 1600.428006 | 7343.613728 | 1657.100673 | 16.334804 | 16.263435 | 17.250223 | 3.312452 | 0.073496 | 0.008685 | 0.217093 | 0.111469 | -0.174852 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 8483 | ZONE | 2022 | PT Mega Perintis Tbk (IDX:ZONE) | 0 | <NA> | 0 | <NA> | 0 | <NA> | Consumer Discretionary Distribution and Retail | NaN | NaN | 62.030000 | 1.877000 | 0.161000 | 13 | 4 | 0.259725 | 0.573719 | 0.305585 | 0.007534 | 0.024653 | 0.867753 | 0.044572 | 0.467846 | 0.309258 | 0.581144 | 0.532154 | 1.399138 | 0.324746 | 0.049686 | 0.268134 | 0.176153 | 3.509987 | 1.032373 | 0.111910 | 0.210295 | 0.215650 | 0.559230 | 0.170629 | 0.108400 | 0.157904 | 0.337512 | 0.152952 | -0.002362 | 83.823150 | 773.274480 | 118.273962 | 13.387464 | 13.419324 | 13.883460 | 3.085819 | 0.450564 | 1.369640 | 0.158230 | 0.222280 | 0.033258 | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False |
| 8484 | ZONE | 2023 | PT Mega Perintis Tbk (IDX:ZONE) | 0 | <NA> | <NA> | <NA> | <NA> | <NA> | Consumer Discretionary Distribution and Retail | NaN | NaN | 62.030000 | 1.787000 | 0.183000 | 13 | 5 | 0.254868 | 0.565430 | 0.316486 | 0.006133 | 0.019378 | 0.869964 | 0.019329 | 0.500516 | 0.305567 | 0.611766 | 0.499484 | 2.056383 | 0.337925 | 0.048165 | 0.248943 | 0.106869 | 2.562948 | 0.976752 | 0.062384 | 0.124898 | 0.145612 | 0.554868 | 0.109412 | 0.063869 | 0.134505 | 0.268732 | 0.137706 | -0.000389 | 53.981045 | 845.180740 | 116.386356 | 13.531763 | 13.508241 | 13.780806 | 2.568245 | 0.092989 | -0.356013 | 0.155229 | 0.084306 | -0.015960 | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False |
| 8486 | ZYRX | 2022 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | 0 | <NA> | 0 | <NA> | 0 | <NA> | Technology Hardware and Equipment | NaN | NaN | 75.160000 | 1.442000 | 0.668000 | 25 | 1 | 0.247891 | 0.881258 | 0.611236 | 0.291809 | 0.477409 | 0.516415 | 0.000051 | 0.620648 | 0.219126 | 0.577631 | 0.379352 | -0.407331 | 0.087764 | NaN | 0.270022 | 0.175403 | 0.972038 | 1.089278 | 0.111177 | 0.293069 | 0.178439 | 0.186771 | 0.161027 | 0.102064 | 0.153029 | 0.246563 | 0.140486 | 0.290293 | 58.970540 | 577.777443 | 81.169732 | 13.469111 | 13.554626 | 12.963758 | 1.590323 | NaN | NaN | NaN | NaN | NaN | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | True | False | False | False |
| 8487 | ZYRX | 2023 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | 0 | <NA> | <NA> | <NA> | <NA> | <NA> | Technology Hardware and Equipment | NaN | NaN | 75.160000 | 2.073000 | 0.401000 | 25 | 2 | 0.718998 | 0.819166 | 0.395076 | 0.025872 | 0.065486 | 0.797158 | 0.002849 | 0.410541 | 0.251157 | 0.426080 | 0.589459 | 1.432748 | 0.139413 | 0.000305 | 0.424090 | 0.154046 | 1.097923 | 0.589834 | 0.067108 | 0.113847 | 0.157240 | 0.315230 | 0.261169 | 0.113775 | -0.261681 | -0.637404 | -0.443651 | -0.394411 | 24.714646 | 217.224777 | -96.372075 | 13.104284 | 12.576371 | 12.307426 | 0.764673 | -0.624034 | -0.580898 | -0.305683 | 0.078868 | -2.187291 | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | True | False | False | False |
| 8488 | ZYRX | 2024 | PT Zyrexindo Mandiri Buana Tbk (IDX:ZYRX) | <NA> | <NA> | <NA> | <NA> | <NA> | <NA> | Technology Hardware and Equipment | NaN | NaN | 75.160000 | 3.402000 | 0.775000 | 25 | 3 | 0.554081 | 0.728395 | 0.214137 | 0.015983 | 0.074638 | 0.768298 | 0.003574 | 0.235431 | 0.119891 | 0.156808 | 0.764569 | 1.051856 | 0.236110 | 0.000989 | 0.514258 | 0.094986 | 1.919324 | 0.928128 | 0.038660 | 0.050564 | 0.098785 | 0.185987 | 0.102341 | 0.041654 | 0.289142 | 1.228139 | 0.311533 | -0.016389 | 11.378909 | 273.178755 | 85.104183 | 12.880151 | 12.805565 | 12.085787 | 0.591011 | 0.257586 | -0.539588 | -0.200792 | 0.036629 | -1.883079 | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | False | True | False | False | False |
7188 rows × 79 columns
Yang normal negatif :
- NetCash_TA - CFO_TA, CFO_TL, CFO_Sales - ROA, ROE, NetMargin, OpMargin - WC_TA, WC_Sales, Altman_X1_WCTA - Altman_X3_EBITTA, EBIT_TA - EPS_proxy
Bisa negatif tapi hati-hati :
- NetDebt_EBITDA
Sangat jarang dan perlu cek definisi :
- GrossMargin - Equity_TA, Debt_Equity - PB
Sebaran Null
features_with_null = [
col for col in df_fitur.columns[df_fitur.isnull().any()]
if col not in ['ticker', 'Year', 'Entity Name']
and not col.startswith(('target', 'tahun'))
]
features_with_null['Parent Percent Owned (%)',
'Percent Owned - All Institutions (%)',
'Percent Owned - Insiders (%)',
'CR',
'QR',
'Age_When_IPO',
'WC_Sales',
'Inventory_CA',
'Prepaid_CA',
'Debt_TA',
'Debt_Equity',
'NetDebt_EBITDA',
'PPE_TA',
'Intang_TA',
'Altman_X3_EBIT_TA',
'Altman_X4_MVE_TL',
'Altman_X5_SalesTA_AssetTurnover',
'EBITDA_TA',
'GrossMargin',
'OpMargin',
'NetMargin',
'CFO_TA',
'CFO_TL',
'CFO_Sales',
'NetCash_TA',
'EPS_proxy',
'Sales_per_share',
'CFO_per_share',
'log_Sales',
'log_MktCap',
'PB',
'Sales_growth',
'NI_growth',
'TA_growth',
'Equity_growth',
'CFO_growth']
Jumlah Per Kolom dan Per Baris
for distress in ['distress_ppk', 'distress_neg_equity', 'distress_consecutive_loss']:
print(f"\n=== {distress} ===")
null_summary_fitur_df = pd.DataFrame({
'Null Count': df_fitur[df_fitur['target_' + distress].notna()][features_with_null].isnull().sum(),
'Null Pctg': (df_fitur[df_fitur['target_' + distress].notna()][features_with_null].isnull().sum() / len(df_fitur[df_fitur['target_' + distress].notna()])) * 100
}).sort_values(by='Null Count', ascending=False)
null_summary_fitur_df['Null Pctg'] = null_summary_fitur_df['Null Pctg'].map('{:.2f}%'.format)
print("Summary Null Values :")
print(null_summary_fitur_df.to_string())
=== distress_ppk ===
Summary Null Values :
Null Count Null Pctg
Intang_TA 1579 61.13%
Percent Owned - All Institutions (%) 1280 49.55%
Parent Percent Owned (%) 1137 44.02%
Percent Owned - Insiders (%) 697 26.98%
Prepaid_CA 447 17.31%
Inventory_CA 357 13.82%
NetDebt_EBITDA 300 11.61%
EBITDA_TA 291 11.27%
Altman_X3_EBIT_TA 288 11.15%
Sales_growth 213 8.25%
CR 207 8.01%
QR 205 7.94%
TA_growth 202 7.82%
Equity_growth 202 7.82%
NI_growth 202 7.82%
CFO_growth 202 7.82%
GrossMargin 52 2.01%
Age_When_IPO 11 0.43%
Debt_Equity 9 0.35%
Debt_TA 9 0.35%
PPE_TA 6 0.23%
OpMargin 5 0.19%
Altman_X5_SalesTA_AssetTurnover 5 0.19%
WC_Sales 5 0.19%
log_Sales 5 0.19%
NetMargin 5 0.19%
Sales_per_share 5 0.19%
CFO_Sales 5 0.19%
Altman_X4_MVE_TL 2 0.08%
PB 2 0.08%
log_MktCap 2 0.08%
CFO_per_share 0 0.00%
CFO_TA 0 0.00%
EPS_proxy 0 0.00%
NetCash_TA 0 0.00%
CFO_TL 0 0.00%
=== distress_neg_equity ===
Summary Null Values :
Null Count Null Pctg
Intang_TA 5348 83.52%
Percent Owned - All Institutions (%) 2773 43.31%
Parent Percent Owned (%) 2638 41.20%
Percent Owned - Insiders (%) 1947 30.41%
Prepaid_CA 1109 17.32%
Inventory_CA 941 14.70%
NetDebt_EBITDA 923 14.42%
EBITDA_TA 903 14.10%
Altman_X3_EBIT_TA 896 13.99%
Sales_growth 720 11.24%
CFO_growth 704 10.99%
NI_growth 700 10.93%
Equity_growth 700 10.93%
TA_growth 700 10.93%
CR 567 8.86%
QR 565 8.82%
GrossMargin 152 2.37%
Altman_X4_MVE_TL 21 0.33%
Debt_Equity 21 0.33%
Debt_TA 21 0.33%
PB 21 0.33%
log_MktCap 21 0.33%
Age_When_IPO 15 0.23%
CFO_Sales 15 0.23%
Sales_per_share 14 0.22%
PPE_TA 14 0.22%
WC_Sales 13 0.20%
Altman_X5_SalesTA_AssetTurnover 13 0.20%
OpMargin 13 0.20%
NetMargin 13 0.20%
log_Sales 13 0.20%
CFO_per_share 3 0.05%
CFO_TA 2 0.03%
NetCash_TA 2 0.03%
CFO_TL 2 0.03%
EPS_proxy 1 0.02%
=== distress_consecutive_loss ===
Summary Null Values :
Null Count Null Pctg
Intang_TA 4367 84.63%
Percent Owned - All Institutions (%) 2082 40.35%
Parent Percent Owned (%) 1980 38.37%
Percent Owned - Insiders (%) 1540 29.84%
Prepaid_CA 809 15.68%
NetDebt_EBITDA 798 15.47%
EBITDA_TA 785 15.21%
Altman_X3_EBIT_TA 785 15.21%
Inventory_CA 778 15.08%
Sales_growth 722 13.99%
CFO_growth 714 13.84%
NI_growth 712 13.80%
Equity_growth 712 13.80%
TA_growth 712 13.80%
CR 520 10.08%
QR 518 10.04%
GrossMargin 110 2.13%
Altman_X4_MVE_TL 17 0.33%
log_MktCap 17 0.33%
PB 17 0.33%
Age_When_IPO 15 0.29%
Debt_TA 14 0.27%
Debt_Equity 14 0.27%
CFO_Sales 10 0.19%
Sales_per_share 10 0.19%
OpMargin 9 0.17%
Altman_X5_SalesTA_AssetTurnover 9 0.17%
WC_Sales 9 0.17%
log_Sales 9 0.17%
NetMargin 9 0.17%
PPE_TA 6 0.12%
CFO_per_share 2 0.04%
NetCash_TA 1 0.02%
EPS_proxy 1 0.02%
CFO_TA 1 0.02%
CFO_TL 1 0.02%
null_summary_fitur_df = pd.DataFrame({
'Null Count': df_fitur[features_with_null].isnull().sum(),
'Null Pctg': (df_fitur[features_with_null].isnull().sum() / len(df_fitur)) * 100
}).sort_values(by='Null Count', ascending=False)
null_summary_fitur_df['Null Pctg'] = null_summary_fitur_df['Null Pctg'].map('{:.2f}%'.format)
print("Summary Null Values di df_fitur:")
print(null_summary_fitur_df.to_string())Summary Null Values di df_fitur:
Null Count Null Pctg
Intang_TA 6726 79.23%
Percent Owned - All Institutions (%) 3947 46.50%
Parent Percent Owned (%) 3641 42.89%
Percent Owned - Insiders (%) 2551 30.05%
Prepaid_CA 1462 17.22%
Inventory_CA 1206 14.21%
NetDebt_EBITDA 1126 13.26%
EBITDA_TA 1106 13.03%
Altman_X3_EBIT_TA 1099 12.95%
Sales_growth 902 10.63%
CFO_growth 857 10.10%
NI_growth 853 10.05%
Equity_growth 853 10.05%
TA_growth 853 10.05%
CR 706 8.32%
QR 704 8.29%
GrossMargin 198 2.33%
CFO_Sales 33 0.39%
Sales_per_share 32 0.38%
Altman_X5_SalesTA_AssetTurnover 31 0.37%
log_Sales 31 0.37%
WC_Sales 31 0.37%
OpMargin 31 0.37%
NetMargin 31 0.37%
Altman_X4_MVE_TL 25 0.29%
PB 25 0.29%
log_MktCap 25 0.29%
Debt_Equity 21 0.25%
Debt_TA 21 0.25%
Age_When_IPO 21 0.25%
PPE_TA 19 0.22%
CFO_per_share 3 0.04%
CFO_TA 2 0.02%
NetCash_TA 2 0.02%
CFO_TL 2 0.02%
EPS_proxy 1 0.01%
Jumlah Baris tiap Kombinasi Jumlah Null
null_counts_per_row_fitur = df_fitur.isnull().sum(axis=1)
null_counts_grouped_fitur = null_counts_per_row_fitur.value_counts().sort_index(ascending=False)
print("Jumlah baris berdasarkan jumlah nilai null (df_fitur, diurutkan dari yang terbanyak):")
print(null_counts_grouped_fitur.to_string())Jumlah baris berdasarkan jumlah nilai null (df_fitur, diurutkan dari yang terbanyak):
23 1
21 1
20 1
19 5
18 4
17 22
16 28
15 37
14 62
13 115
12 264
11 433
10 586
9 391
8 541
7 960
6 1778
5 1819
4 973
3 391
2 68
1 9
Proporsi Industri pada Null tiap Kolom
# Pastikan variabel df_fitur dan list features_with_null telah didefinisikan sebelumnya
total_baris_df = len(df_fitur)
n_features = len(features_with_null)
if n_features == 0:
print("Tidak ada fitur yang memiliki nilai null untuk divisualisasikan.")
else:
cols = 4
rows = math.ceil(n_features / cols)
fig, axes = plt.subplots(rows, cols, figsize=(6 * cols, 4.5 * rows))
axes = axes.flatten()
# --- 1. PEMETAAN WARNA KONSISTEN ---
# Mengambil semua kategori unik Industry Group yang ada di df_fitur
semua_industri = df_fitur['Industry Group'].dropna().unique()
# Membuat dictionary warna menggunakan palet 'tab20' (mendukung hingga 20 warna berbeda)
cmap = plt.get_cmap('tab20')
color_mapping = {}
for idx, industri in enumerate(semua_industri):
color_mapping[industri] = cmap(idx % cmap.N)
# Menetapkan warna abu-abu terang secara absolut untuk kategori 'Other'
color_mapping['Other'] = '#D3D3D3'
for i, feature in enumerate(features_with_null):
ax = axes[i]
df_null = df_fitur[df_fitur[feature].isnull()]
total_null = len(df_null)
pct_dari_total = (total_null / total_baris_df) * 100
jumlah_sebaran = df_null['Industry Group'].value_counts()
persentase_sebaran = (jumlah_sebaran / total_null) * 100
mask_utama = persentase_sebaran >= 10
mask_other = persentase_sebaran < 10
final_counts = jumlah_sebaran[mask_utama].copy()
other_count = jumlah_sebaran[mask_other].sum()
if other_count > 0:
final_counts['Other'] = other_count
custom_labels = []
custom_colors = []
for idx, val in final_counts.items():
pct = (val / total_null) * 100
# Memotong teks yang panjang
nama_grup_wrapped = textwrap.fill(str(idx), width=15)
custom_labels.append(f"{nama_grup_wrapped}\n{val} ({pct:.1f}%)")
# Memanggil warna dari dictionary menggunakan nama industri yang asli
custom_colors.append(color_mapping[idx])
# Memasukkan custom_colors ke dalam parameter colors
ax.pie(final_counts, labels=custom_labels, colors=custom_colors, startangle=90,
wedgeprops={'edgecolor': 'white', 'linewidth': 1},
textprops={'fontsize': 9})
ax.set_title(f"{feature}\nTotal Null: {total_null} ({pct_dari_total:.2f}% dari total)",
fontsize=11, fontweight='bold')
# --- 2. GARIS PEMBATAS HITAM TIPIS ---
# Memaksa frame kotak untuk tampil di sekitar pie chart
ax.set_frame_on(True)
# Menghapus penanda angka (ticks) agar kotak tetap bersih
ax.set_xticks([])
ax.set_yticks([])
# Mengatur warna dan ketebalan garis tepi kotak menjadi hitam tipis
for spine in ax.spines.values():
spine.set_edgecolor('black')
spine.set_linewidth(0.5)
spine.set_visible(True)
# Menghapus subplot yang tidak terpakai
for j in range(i + 1, len(axes)):
fig.delaxes(axes[j])
# Merapatkan jarak antar plot
plt.tight_layout(h_pad=0.5, w_pad=1.0)
plt.show()
for feature in features_with_null:
# Mengambil subset data di mana kolom fitur tersebut bernilai null
df_null = df_fitur[df_fitur[feature].isnull()]
# Menghitung total baris yang null pada fitur tersebut
total_null = len(df_null)
print(f"=== Fitur: {feature} ===")
print(f"Total Baris Null : {total_null}")
if total_null > 0:
# Menghitung jumlah null di tiap Industry Group
jumlah_sebaran = df_null['Industry Group'].value_counts()
# Menghitung persentase sebaran null di tiap Industry Group
persentase_sebaran = df_null['Industry Group'].value_counts(normalize=True) * 100
# Menggabungkan hasil ke dalam satu DataFrame agar tampilannya rapi
df_sebaran = pd.DataFrame({
'Jumlah Null': jumlah_sebaran,
'Persentase (%)': persentase_sebaran
})
# Membulatkan persentase menjadi 2 angka di belakang koma
df_sebaran['Persentase (%)'] = df_sebaran['Persentase (%)'].round(2)
print(df_sebaran)
else:
print("Tidak ada nilai null pada fitur ini.")
print("\n" + "="*40 + "\n")=== Fitur: Parent Percent Owned (%) ===
Total Baris Null : 3641
Jumlah Null Persentase (%)
Industry Group
Real Estate Management and Development 531 14.580000
Materials 449 12.330000
Capital Goods 399 10.960000
Food, Beverage and Tobacco 378 10.380000
Energy 331 9.090000
Transportation 255 7.000000
Consumer Services 191 5.250000
Financial Services 182 5.000000
Consumer Discretionary Distribution and Retail 139 3.820000
Consumer Durables and Apparel 133 3.650000
Media and Entertainment 92 2.530000
Technology Hardware and Equipment 82 2.250000
Commercial and Professional Services 74 2.030000
Banks 70 1.920000
Telecommunication Services 65 1.790000
Software and Services 59 1.620000
Insurance 51 1.400000
Consumer Staples Distribution and Retail 39 1.070000
Utilities 32 0.880000
Health Care Equipment and Services 28 0.770000
Automobiles and Components 22 0.600000
Pharmaceuticals, Biotechnology and Life Sciences 21 0.580000
Household and Personal Products 18 0.490000
========================================
=== Fitur: Percent Owned - All Institutions (%) ===
Total Baris Null : 3947
Jumlah Null Persentase (%)
Industry Group
Materials 467 11.830000
Real Estate Management and Development 456 11.550000
Capital Goods 437 11.070000
Food, Beverage and Tobacco 295 7.470000
Transportation 266 6.740000
Consumer Durables and Apparel 251 6.360000
Consumer Services 219 5.550000
Energy 201 5.090000
Banks 198 5.020000
Financial Services 167 4.230000
Insurance 134 3.390000
Telecommunication Services 112 2.840000
Media and Entertainment 110 2.790000
Consumer Discretionary Distribution and Retail 108 2.740000
Consumer Staples Distribution and Retail 96 2.430000
Commercial and Professional Services 68 1.720000
Health Care Equipment and Services 68 1.720000
Technology Hardware and Equipment 62 1.570000
Pharmaceuticals, Biotechnology and Life Sciences 61 1.550000
Automobiles and Components 60 1.520000
Household and Personal Products 55 1.390000
Software and Services 37 0.940000
Utilities 19 0.480000
========================================
=== Fitur: Percent Owned - Insiders (%) ===
Total Baris Null : 2551
Jumlah Null Persentase (%)
Industry Group
Capital Goods 320 12.540000
Real Estate Management and Development 267 10.470000
Food, Beverage and Tobacco 262 10.270000
Financial Services 210 8.230000
Banks 189 7.410000
Materials 170 6.660000
Energy 161 6.310000
Consumer Durables and Apparel 126 4.940000
Consumer Services 126 4.940000
Transportation 103 4.040000
Insurance 95 3.720000
Pharmaceuticals, Biotechnology and Life Sciences 85 3.330000
Consumer Discretionary Distribution and Retail 80 3.140000
Commercial and Professional Services 72 2.820000
Telecommunication Services 52 2.040000
Consumer Staples Distribution and Retail 51 2.000000
Automobiles and Components 41 1.610000
Health Care Equipment and Services 41 1.610000
Software and Services 41 1.610000
Media and Entertainment 33 1.290000
Household and Personal Products 19 0.740000
Technology Hardware and Equipment 7 0.270000
========================================
=== Fitur: CR ===
Total Baris Null : 706
Jumlah Null Persentase (%)
Industry Group
Banks 635 89.940000
Insurance 34 4.820000
Financial Services 30 4.250000
Real Estate Management and Development 3 0.420000
Transportation 2 0.280000
Capital Goods 2 0.280000
========================================
=== Fitur: QR ===
Total Baris Null : 704
Jumlah Null Persentase (%)
Industry Group
Banks 635 90.200000
Insurance 34 4.830000
Financial Services 30 4.260000
Transportation 2 0.280000
Capital Goods 2 0.280000
Real Estate Management and Development 1 0.140000
========================================
=== Fitur: Age_When_IPO ===
Total Baris Null : 21
Jumlah Null Persentase (%)
Industry Group
Financial Services 15 71.430000
Banks 6 28.570000
========================================
=== Fitur: WC_Sales ===
Total Baris Null : 31
Jumlah Null Persentase (%)
Industry Group
Energy 6 19.350000
Materials 6 19.350000
Transportation 5 16.130000
Utilities 3 9.680000
Real Estate Management and Development 3 9.680000
Capital Goods 2 6.450000
Consumer Services 2 6.450000
Commercial and Professional Services 2 6.450000
Telecommunication Services 1 3.230000
Financial Services 1 3.230000
========================================
=== Fitur: Inventory_CA ===
Total Baris Null : 1206
Jumlah Null Persentase (%)
Industry Group
Financial Services 381 31.590000
Insurance 253 20.980000
Transportation 112 9.290000
Banks 80 6.630000
Telecommunication Services 74 6.140000
Real Estate Management and Development 63 5.220000
Capital Goods 63 5.220000
Energy 49 4.060000
Consumer Services 36 2.990000
Media and Entertainment 22 1.820000
Software and Services 21 1.740000
Commercial and Professional Services 16 1.330000
Utilities 16 1.330000
Materials 11 0.910000
Food, Beverage and Tobacco 5 0.410000
Household and Personal Products 2 0.170000
Consumer Discretionary Distribution and Retail 2 0.170000
========================================
=== Fitur: Prepaid_CA ===
Total Baris Null : 1462
Jumlah Null Persentase (%)
Industry Group
Financial Services 391 26.740000
Real Estate Management and Development 277 18.950000
Energy 129 8.820000
Capital Goods 114 7.800000
Materials 95 6.500000
Food, Beverage and Tobacco 73 4.990000
Transportation 73 4.990000
Consumer Services 50 3.420000
Commercial and Professional Services 33 2.260000
Software and Services 28 1.920000
Consumer Discretionary Distribution and Retail 25 1.710000
Insurance 21 1.440000
Automobiles and Components 20 1.370000
Telecommunication Services 20 1.370000
Banks 20 1.370000
Consumer Durables and Apparel 19 1.300000
Consumer Staples Distribution and Retail 18 1.230000
Technology Hardware and Equipment 17 1.160000
Utilities 15 1.030000
Media and Entertainment 14 0.960000
Pharmaceuticals, Biotechnology and Life Sciences 8 0.550000
Household and Personal Products 1 0.070000
Health Care Equipment and Services 1 0.070000
========================================
=== Fitur: Debt_TA ===
Total Baris Null : 21
Jumlah Null Persentase (%)
Industry Group
Materials 9 42.860000
Telecommunication Services 7 33.330000
Household and Personal Products 5 23.810000
========================================
=== Fitur: Debt_Equity ===
Total Baris Null : 21
Jumlah Null Persentase (%)
Industry Group
Materials 9 42.860000
Telecommunication Services 7 33.330000
Household and Personal Products 5 23.810000
========================================
=== Fitur: NetDebt_EBITDA ===
Total Baris Null : 1126
Jumlah Null Persentase (%)
Industry Group
Banks 635 56.390000
Financial Services 375 33.300000
Insurance 34 3.020000
Energy 18 1.600000
Software and Services 17 1.510000
Capital Goods 15 1.330000
Consumer Services 10 0.890000
Materials 9 0.800000
Telecommunication Services 7 0.620000
Household and Personal Products 5 0.440000
Transportation 1 0.090000
========================================
=== Fitur: PPE_TA ===
Total Baris Null : 19
Jumlah Null Persentase (%)
Industry Group
Financial Services 9 47.370000
Capital Goods 3 15.790000
Transportation 3 15.790000
Energy 3 15.790000
Telecommunication Services 1 5.260000
========================================
=== Fitur: Intang_TA ===
Total Baris Null : 6726
Jumlah Null Persentase (%)
Industry Group
Materials 849 12.620000
Real Estate Management and Development 752 11.180000
Food, Beverage and Tobacco 660 9.810000
Capital Goods 645 9.590000
Energy 594 8.830000
Transportation 470 6.990000
Banks 417 6.200000
Financial Services 329 4.890000
Consumer Services 315 4.680000
Consumer Durables and Apparel 261 3.880000
Consumer Discretionary Distribution and Retail 227 3.370000
Insurance 206 3.060000
Media and Entertainment 155 2.300000
Telecommunication Services 137 2.040000
Consumer Staples Distribution and Retail 115 1.710000
Commercial and Professional Services 100 1.490000
Automobiles and Components 99 1.470000
Health Care Equipment and Services 92 1.370000
Household and Personal Products 83 1.230000
Pharmaceuticals, Biotechnology and Life Sciences 77 1.140000
Technology Hardware and Equipment 70 1.040000
Utilities 54 0.800000
Software and Services 19 0.280000
========================================
=== Fitur: Altman_X3_EBIT_TA ===
Total Baris Null : 1099
Jumlah Null Persentase (%)
Industry Group
Banks 635 57.780000
Financial Services 375 34.120000
Insurance 34 3.090000
Software and Services 17 1.550000
Energy 17 1.550000
Capital Goods 10 0.910000
Consumer Services 10 0.910000
Telecommunication Services 1 0.090000
========================================
=== Fitur: Altman_X4_MVE_TL ===
Total Baris Null : 25
Jumlah Null Persentase (%)
Industry Group
Banks 6 24.000000
Consumer Services 6 24.000000
Materials 4 16.000000
Energy 3 12.000000
Food, Beverage and Tobacco 2 8.000000
Transportation 2 8.000000
Financial Services 1 4.000000
Capital Goods 1 4.000000
========================================
=== Fitur: Altman_X5_SalesTA_AssetTurnover ===
Total Baris Null : 31
Jumlah Null Persentase (%)
Industry Group
Energy 6 19.350000
Materials 6 19.350000
Transportation 5 16.130000
Utilities 3 9.680000
Real Estate Management and Development 3 9.680000
Capital Goods 2 6.450000
Consumer Services 2 6.450000
Commercial and Professional Services 2 6.450000
Telecommunication Services 1 3.230000
Financial Services 1 3.230000
========================================
=== Fitur: EBITDA_TA ===
Total Baris Null : 1106
Jumlah Null Persentase (%)
Industry Group
Banks 635 57.410000
Financial Services 375 33.910000
Insurance 34 3.070000
Energy 18 1.630000
Software and Services 17 1.540000
Capital Goods 15 1.360000
Consumer Services 10 0.900000
Telecommunication Services 1 0.090000
Transportation 1 0.090000
========================================
=== Fitur: GrossMargin ===
Total Baris Null : 198
Jumlah Null Persentase (%)
Industry Group
Banks 87 43.940000
Financial Services 43 21.720000
Materials 14 7.070000
Commercial and Professional Services 13 6.570000
Energy 12 6.060000
Capital Goods 11 5.560000
Transportation 5 2.530000
Consumer Services 3 1.520000
Real Estate Management and Development 3 1.520000
Utilities 3 1.520000
Software and Services 1 0.510000
Telecommunication Services 1 0.510000
Food, Beverage and Tobacco 1 0.510000
Insurance 1 0.510000
========================================
=== Fitur: OpMargin ===
Total Baris Null : 31
Jumlah Null Persentase (%)
Industry Group
Energy 6 19.350000
Materials 6 19.350000
Transportation 5 16.130000
Utilities 3 9.680000
Real Estate Management and Development 3 9.680000
Capital Goods 2 6.450000
Consumer Services 2 6.450000
Commercial and Professional Services 2 6.450000
Telecommunication Services 1 3.230000
Financial Services 1 3.230000
========================================
=== Fitur: NetMargin ===
Total Baris Null : 31
Jumlah Null Persentase (%)
Industry Group
Energy 6 19.350000
Materials 6 19.350000
Transportation 5 16.130000
Utilities 3 9.680000
Real Estate Management and Development 3 9.680000
Capital Goods 2 6.450000
Consumer Services 2 6.450000
Commercial and Professional Services 2 6.450000
Telecommunication Services 1 3.230000
Financial Services 1 3.230000
========================================
=== Fitur: CFO_TA ===
Total Baris Null : 2
Jumlah Null Persentase (%)
Industry Group
Energy 2 100.000000
========================================
=== Fitur: CFO_TL ===
Total Baris Null : 2
Jumlah Null Persentase (%)
Industry Group
Energy 2 100.000000
========================================
=== Fitur: CFO_Sales ===
Total Baris Null : 33
Jumlah Null Persentase (%)
Industry Group
Energy 8 24.240000
Materials 6 18.180000
Transportation 5 15.150000
Utilities 3 9.090000
Real Estate Management and Development 3 9.090000
Capital Goods 2 6.060000
Consumer Services 2 6.060000
Commercial and Professional Services 2 6.060000
Telecommunication Services 1 3.030000
Financial Services 1 3.030000
========================================
=== Fitur: NetCash_TA ===
Total Baris Null : 2
Jumlah Null Persentase (%)
Industry Group
Energy 2 100.000000
========================================
=== Fitur: EPS_proxy ===
Total Baris Null : 1
Jumlah Null Persentase (%)
Industry Group
Banks 1 100.000000
========================================
=== Fitur: Sales_per_share ===
Total Baris Null : 32
Jumlah Null Persentase (%)
Industry Group
Materials 6 18.750000
Energy 6 18.750000
Transportation 5 15.620000
Real Estate Management and Development 3 9.380000
Utilities 3 9.380000
Consumer Services 2 6.250000
Capital Goods 2 6.250000
Commercial and Professional Services 2 6.250000
Financial Services 1 3.120000
Telecommunication Services 1 3.120000
Banks 1 3.120000
========================================
=== Fitur: CFO_per_share ===
Total Baris Null : 3
Jumlah Null Persentase (%)
Industry Group
Energy 2 66.670000
Banks 1 33.330000
========================================
=== Fitur: log_Sales ===
Total Baris Null : 31
Jumlah Null Persentase (%)
Industry Group
Energy 6 19.350000
Materials 6 19.350000
Transportation 5 16.130000
Utilities 3 9.680000
Real Estate Management and Development 3 9.680000
Capital Goods 2 6.450000
Consumer Services 2 6.450000
Commercial and Professional Services 2 6.450000
Telecommunication Services 1 3.230000
Financial Services 1 3.230000
========================================
=== Fitur: log_MktCap ===
Total Baris Null : 25
Jumlah Null Persentase (%)
Industry Group
Banks 6 24.000000
Consumer Services 6 24.000000
Materials 4 16.000000
Energy 3 12.000000
Food, Beverage and Tobacco 2 8.000000
Transportation 2 8.000000
Financial Services 1 4.000000
Capital Goods 1 4.000000
========================================
=== Fitur: PB ===
Total Baris Null : 25
Jumlah Null Persentase (%)
Industry Group
Banks 6 24.000000
Consumer Services 6 24.000000
Materials 4 16.000000
Energy 3 12.000000
Food, Beverage and Tobacco 2 8.000000
Transportation 2 8.000000
Financial Services 1 4.000000
Capital Goods 1 4.000000
========================================
=== Fitur: Sales_growth ===
Total Baris Null : 902
Jumlah Null Persentase (%)
Industry Group
Materials 100 11.090000
Real Estate Management and Development 93 10.310000
Food, Beverage and Tobacco 92 10.200000
Capital Goods 88 9.760000
Transportation 68 7.540000
Energy 65 7.210000
Banks 48 5.320000
Consumer Services 42 4.660000
Consumer Discretionary Distribution and Retail 37 4.100000
Financial Services 34 3.770000
Media and Entertainment 30 3.330000
Consumer Durables and Apparel 27 2.990000
Health Care Equipment and Services 24 2.660000
Insurance 21 2.330000
Telecommunication Services 20 2.220000
Software and Services 19 2.110000
Commercial and Professional Services 18 2.000000
Technology Hardware and Equipment 16 1.770000
Consumer Staples Distribution and Retail 15 1.660000
Utilities 13 1.440000
Household and Personal Products 12 1.330000
Automobiles and Components 11 1.220000
Pharmaceuticals, Biotechnology and Life Sciences 9 1.000000
========================================
=== Fitur: NI_growth ===
Total Baris Null : 853
Jumlah Null Persentase (%)
Industry Group
Food, Beverage and Tobacco 92 10.790000
Materials 92 10.790000
Real Estate Management and Development 88 10.320000
Capital Goods 85 9.960000
Transportation 59 6.920000
Energy 56 6.570000
Banks 48 5.630000
Consumer Services 38 4.450000
Consumer Discretionary Distribution and Retail 37 4.340000
Financial Services 32 3.750000
Media and Entertainment 30 3.520000
Consumer Durables and Apparel 27 3.170000
Health Care Equipment and Services 24 2.810000
Insurance 21 2.460000
Software and Services 19 2.230000
Telecommunication Services 18 2.110000
Technology Hardware and Equipment 16 1.880000
Commercial and Professional Services 15 1.760000
Consumer Staples Distribution and Retail 15 1.760000
Household and Personal Products 12 1.410000
Automobiles and Components 11 1.290000
Utilities 9 1.060000
Pharmaceuticals, Biotechnology and Life Sciences 9 1.060000
========================================
=== Fitur: TA_growth ===
Total Baris Null : 853
Jumlah Null Persentase (%)
Industry Group
Food, Beverage and Tobacco 92 10.790000
Materials 92 10.790000
Real Estate Management and Development 88 10.320000
Capital Goods 85 9.960000
Transportation 59 6.920000
Energy 56 6.570000
Banks 48 5.630000
Consumer Services 38 4.450000
Consumer Discretionary Distribution and Retail 37 4.340000
Financial Services 32 3.750000
Media and Entertainment 30 3.520000
Consumer Durables and Apparel 27 3.170000
Health Care Equipment and Services 24 2.810000
Insurance 21 2.460000
Software and Services 19 2.230000
Telecommunication Services 18 2.110000
Technology Hardware and Equipment 16 1.880000
Commercial and Professional Services 15 1.760000
Consumer Staples Distribution and Retail 15 1.760000
Household and Personal Products 12 1.410000
Automobiles and Components 11 1.290000
Utilities 9 1.060000
Pharmaceuticals, Biotechnology and Life Sciences 9 1.060000
========================================
=== Fitur: Equity_growth ===
Total Baris Null : 853
Jumlah Null Persentase (%)
Industry Group
Food, Beverage and Tobacco 92 10.790000
Materials 92 10.790000
Real Estate Management and Development 88 10.320000
Capital Goods 85 9.960000
Transportation 59 6.920000
Energy 56 6.570000
Banks 48 5.630000
Consumer Services 38 4.450000
Consumer Discretionary Distribution and Retail 37 4.340000
Financial Services 32 3.750000
Media and Entertainment 30 3.520000
Consumer Durables and Apparel 27 3.170000
Health Care Equipment and Services 24 2.810000
Insurance 21 2.460000
Software and Services 19 2.230000
Telecommunication Services 18 2.110000
Technology Hardware and Equipment 16 1.880000
Commercial and Professional Services 15 1.760000
Consumer Staples Distribution and Retail 15 1.760000
Household and Personal Products 12 1.410000
Automobiles and Components 11 1.290000
Utilities 9 1.060000
Pharmaceuticals, Biotechnology and Life Sciences 9 1.060000
========================================
=== Fitur: CFO_growth ===
Total Baris Null : 857
Jumlah Null Persentase (%)
Industry Group
Food, Beverage and Tobacco 92 10.740000
Materials 92 10.740000
Real Estate Management and Development 88 10.270000
Capital Goods 85 9.920000
Energy 60 7.000000
Transportation 59 6.880000
Banks 48 5.600000
Consumer Services 38 4.430000
Consumer Discretionary Distribution and Retail 37 4.320000
Financial Services 32 3.730000
Media and Entertainment 30 3.500000
Consumer Durables and Apparel 27 3.150000
Health Care Equipment and Services 24 2.800000
Insurance 21 2.450000
Software and Services 19 2.220000
Telecommunication Services 18 2.100000
Technology Hardware and Equipment 16 1.870000
Commercial and Professional Services 15 1.750000
Consumer Staples Distribution and Retail 15 1.750000
Household and Personal Products 12 1.400000
Automobiles and Components 11 1.280000
Utilities 9 1.050000
Pharmaceuticals, Biotechnology and Life Sciences 9 1.050000
========================================
Sebaran Null tiap Tahun Data
total_baris_df = len(df_fitur)
n_features = len(features_with_null)
# Mendefinisikan rentang tahun unik secara eksplisit (2008 hingga 2024)
all_years = list(range(2008, 2025))
if n_features == 0:
print("Tidak ada fitur yang memiliki nilai null untuk divisualisasikan.")
else:
cols = 4
rows = math.ceil(n_features / cols)
fig, axes = plt.subplots(rows, cols, figsize=(7 * cols, 6 * rows))
axes = axes.flatten()
for i, feature in enumerate(features_with_null):
ax = axes[i]
df_null = df_fitur[df_fitur[feature].isnull()]
total_null = len(df_null)
pct_dari_total = (total_null / total_baris_df) * 100
# Menghitung jumlah null dan melakukan reindex agar mencakup semua tahun 2008-2024
jumlah_sebaran = df_null['Year'].value_counts().reindex(all_years, fill_value=0)
# Menghitung persentase terhadap total null pada fitur tersebut
# Jika total_null == 0, diabaikan karena fitur tersebut tidak akan masuk ke list
persentase_sebaran = (jumlah_sebaran / total_null) * 100
years = jumlah_sebaran.index.astype(str)
counts = jumlah_sebaran.values
pcts = persentase_sebaran.values
bars = ax.bar(years, counts, color='steelblue')
# Menambahkan teks anotasi secara vertikal di atas setiap batang
for bar, count, pct in zip(bars, counts, pcts):
# Teks anotasi hanya ditambahkan jika jumlah null > 0 agar grafik tetap bersih
if count > 0:
height = bar.get_height()
ax.annotate(f'{int(count)} ({pct:.1f}%)',
xy=(bar.get_x() + bar.get_width() / 2, height),
xytext=(0, 5),
textcoords="offset points",
ha='center', va='bottom',
fontsize=9, rotation=90)
ax.set_title(f"{feature}\nTotal Null: {total_null} ({pct_dari_total:.2f}% dari total)",
fontsize=11, fontweight='bold')
ax.set_xlabel('Tahun', fontsize=10)
ax.set_ylabel('Jumlah Null', fontsize=10)
ax.set_xticks(range(len(years)))
ax.set_xticklabels(years, rotation=90)
if max(counts) > 0:
ax.set_ylim(0, max(counts) * 1.6)
# Menghapus subplot (axes) yang tidak terpakai
for j in range(i + 1, len(axes)):
fig.delaxes(axes[j])
plt.tight_layout()
plt.show()
for feature in features_with_null:
# Mengambil subset data di mana kolom fitur tersebut bernilai null
df_null = df_fitur[df_fitur[feature].isnull()]
# Menghitung total baris yang null pada fitur tersebut
total_null = len(df_null)
print(f"=== Fitur: {feature} ===")
print(f"Total Baris Null : {total_null}")
if total_null > 0:
# Menghitung jumlah null di tiap Year
jumlah_sebaran = df_null['Year'].value_counts()
# Menghitung persentase sebaran null di tiap Year
persentase_sebaran = df_null['Year'].value_counts(normalize=True) * 100
# Menggabungkan hasil ke dalam satu DataFrame agar tampilannya rapi
df_sebaran = pd.DataFrame({
'Jumlah Null': jumlah_sebaran,
'Persentase (%)': persentase_sebaran
})
# Membulatkan persentase menjadi 2 angka di belakang koma
df_sebaran['Persentase (%)'] = df_sebaran['Persentase (%)'].round(2)
print(df_sebaran.sort_index(ascending=False))
else:
print("Tidak ada nilai null pada fitur ini.")
print("\n" + "="*40 + "\n")=== Fitur: Parent Percent Owned (%) ===
Total Baris Null : 3641
Jumlah Null Persentase (%)
Year
2024 403 11.070000
2023 359 9.860000
2022 330 9.060000
2021 292 8.020000
2020 261 7.170000
2019 234 6.430000
2018 211 5.800000
2017 196 5.380000
2016 186 5.110000
2015 176 4.830000
2014 167 4.590000
2013 160 4.390000
2012 149 4.090000
2011 139 3.820000
2010 131 3.600000
2009 126 3.460000
2008 121 3.320000
========================================
=== Fitur: Percent Owned - All Institutions (%) ===
Total Baris Null : 3947
Jumlah Null Persentase (%)
Year
2024 460 11.650000
2023 405 10.260000
2022 367 9.300000
2021 327 8.280000
2020 286 7.250000
2019 243 6.160000
2018 213 5.400000
2017 200 5.070000
2016 196 4.970000
2015 189 4.790000
2014 176 4.460000
2013 168 4.260000
2012 156 3.950000
2011 145 3.670000
2010 142 3.600000
2009 140 3.550000
2008 134 3.390000
========================================
=== Fitur: Percent Owned - Insiders (%) ===
Total Baris Null : 2551
Jumlah Null Persentase (%)
Year
2024 205 8.040000
2023 196 7.680000
2022 193 7.570000
2021 187 7.330000
2020 178 6.980000
2019 170 6.660000
2018 159 6.230000
2017 149 5.840000
2016 146 5.720000
2015 142 5.570000
2014 133 5.210000
2013 128 5.020000
2012 121 4.740000
2011 116 4.550000
2010 113 4.430000
2009 109 4.270000
2008 106 4.160000
========================================
=== Fitur: CR ===
Total Baris Null : 706
Jumlah Null Persentase (%)
Year
2024 55 7.790000
2023 54 7.650000
2022 57 8.070000
2021 55 7.790000
2020 50 7.080000
2019 47 6.660000
2018 45 6.370000
2017 44 6.230000
2016 44 6.230000
2015 42 5.950000
2014 37 5.240000
2013 32 4.530000
2012 31 4.390000
2011 32 4.530000
2010 28 3.970000
2009 27 3.820000
2008 26 3.680000
========================================
=== Fitur: QR ===
Total Baris Null : 704
Jumlah Null Persentase (%)
Year
2024 55 7.810000
2023 54 7.670000
2022 56 7.950000
2021 54 7.670000
2020 50 7.100000
2019 47 6.680000
2018 45 6.390000
2017 44 6.250000
2016 44 6.250000
2015 42 5.970000
2014 37 5.260000
2013 32 4.550000
2012 31 4.400000
2011 32 4.550000
2010 28 3.980000
2009 27 3.840000
2008 26 3.690000
========================================
=== Fitur: Age_When_IPO ===
Total Baris Null : 21
Jumlah Null Persentase (%)
Year
2024 3 14.290000
2023 3 14.290000
2022 3 14.290000
2021 3 14.290000
2020 2 9.520000
2019 2 9.520000
2018 1 4.760000
2017 1 4.760000
2016 1 4.760000
2015 1 4.760000
2014 1 4.760000
========================================
=== Fitur: WC_Sales ===
Total Baris Null : 31
Jumlah Null Persentase (%)
Year
2024 1 3.230000
2023 1 3.230000
2022 2 6.450000
2021 1 3.230000
2020 2 6.450000
2019 2 6.450000
2018 2 6.450000
2017 2 6.450000
2016 4 12.900000
2015 2 6.450000
2014 1 3.230000
2013 1 3.230000
2012 1 3.230000
2011 2 6.450000
2010 4 12.900000
2009 2 6.450000
2008 1 3.230000
========================================
=== Fitur: Inventory_CA ===
Total Baris Null : 1206
Jumlah Null Persentase (%)
Year
2024 112 9.290000
2023 103 8.540000
2022 101 8.370000
2021 98 8.130000
2020 83 6.880000
2019 77 6.380000
2018 66 5.470000
2017 65 5.390000
2016 65 5.390000
2015 64 5.310000
2014 57 4.730000
2013 59 4.890000
2012 52 4.310000
2011 52 4.310000
2010 52 4.310000
2009 49 4.060000
2008 51 4.230000
========================================
=== Fitur: Prepaid_CA ===
Total Baris Null : 1462
Jumlah Null Persentase (%)
Year
2024 141 9.640000
2023 134 9.170000
2022 126 8.620000
2021 123 8.410000
2020 112 7.660000
2019 89 6.090000
2018 79 5.400000
2017 74 5.060000
2016 74 5.060000
2015 74 5.060000
2014 72 4.920000
2013 68 4.650000
2012 58 3.970000
2011 54 3.690000
2010 62 4.240000
2009 64 4.380000
2008 58 3.970000
========================================
=== Fitur: Debt_TA ===
Total Baris Null : 21
Jumlah Null Persentase (%)
Year
2022 3 14.290000
2021 3 14.290000
2020 3 14.290000
2019 2 9.520000
2018 1 4.760000
2017 1 4.760000
2016 1 4.760000
2015 1 4.760000
2014 2 9.520000
2013 2 9.520000
2009 1 4.760000
2008 1 4.760000
========================================
=== Fitur: Debt_Equity ===
Total Baris Null : 21
Jumlah Null Persentase (%)
Year
2022 3 14.290000
2021 3 14.290000
2020 3 14.290000
2019 2 9.520000
2018 1 4.760000
2017 1 4.760000
2016 1 4.760000
2015 1 4.760000
2014 2 9.520000
2013 2 9.520000
2009 1 4.760000
2008 1 4.760000
========================================
=== Fitur: NetDebt_EBITDA ===
Total Baris Null : 1126
Jumlah Null Persentase (%)
Year
2024 80 7.100000
2023 80 7.100000
2022 83 7.370000
2021 81 7.190000
2020 79 7.020000
2019 77 6.840000
2018 73 6.480000
2017 72 6.390000
2016 71 6.310000
2015 68 6.040000
2014 62 5.510000
2013 57 5.060000
2012 52 4.620000
2011 51 4.530000
2010 48 4.260000
2009 47 4.170000
2008 45 4.000000
========================================
=== Fitur: PPE_TA ===
Total Baris Null : 19
Jumlah Null Persentase (%)
Year
2024 3 15.790000
2023 1 5.260000
2022 1 5.260000
2021 2 10.530000
2020 2 10.530000
2019 1 5.260000
2018 1 5.260000
2017 2 10.530000
2016 2 10.530000
2015 1 5.260000
2014 1 5.260000
2011 1 5.260000
2010 1 5.260000
========================================
=== Fitur: Intang_TA ===
Total Baris Null : 6726
Jumlah Null Persentase (%)
Year
2024 493 7.330000
2023 452 6.720000
2022 440 6.540000
2021 414 6.160000
2020 393 5.840000
2019 367 5.460000
2018 341 5.070000
2017 470 6.990000
2016 458 6.810000
2015 439 6.530000
2014 414 6.160000
2013 392 5.830000
2012 367 5.460000
2011 344 5.110000
2010 325 4.830000
2009 315 4.680000
2008 302 4.490000
========================================
=== Fitur: Altman_X3_EBIT_TA ===
Total Baris Null : 1099
Jumlah Null Persentase (%)
Year
2024 80 7.280000
2023 80 7.280000
2022 79 7.190000
2021 77 7.010000
2020 75 6.820000
2019 74 6.730000
2018 71 6.460000
2017 71 6.460000
2016 69 6.280000
2015 68 6.190000
2014 60 5.460000
2013 55 5.000000
2012 52 4.730000
2011 50 4.550000
2010 48 4.370000
2009 46 4.190000
2008 44 4.000000
========================================
=== Fitur: Altman_X4_MVE_TL ===
Total Baris Null : 25
Jumlah Null Persentase (%)
Year
2024 1 4.000000
2022 1 4.000000
2021 1 4.000000
2020 1 4.000000
2018 1 4.000000
2017 1 4.000000
2016 1 4.000000
2015 1 4.000000
2014 2 8.000000
2013 3 12.000000
2012 1 4.000000
2011 1 4.000000
2010 3 12.000000
2009 2 8.000000
2008 5 20.000000
========================================
=== Fitur: Altman_X5_SalesTA_AssetTurnover ===
Total Baris Null : 31
Jumlah Null Persentase (%)
Year
2024 1 3.230000
2023 1 3.230000
2022 2 6.450000
2021 1 3.230000
2020 2 6.450000
2019 2 6.450000
2018 2 6.450000
2017 2 6.450000
2016 4 12.900000
2015 2 6.450000
2014 1 3.230000
2013 1 3.230000
2012 1 3.230000
2011 2 6.450000
2010 4 12.900000
2009 2 6.450000
2008 1 3.230000
========================================
=== Fitur: EBITDA_TA ===
Total Baris Null : 1106
Jumlah Null Persentase (%)
Year
2024 80 7.230000
2023 80 7.230000
2022 80 7.230000
2021 78 7.050000
2020 76 6.870000
2019 75 6.780000
2018 72 6.510000
2017 71 6.420000
2016 70 6.330000
2015 68 6.150000
2014 60 5.420000
2013 55 4.970000
2012 52 4.700000
2011 51 4.610000
2010 48 4.340000
2009 46 4.160000
2008 44 3.980000
========================================
=== Fitur: GrossMargin ===
Total Baris Null : 198
Jumlah Null Persentase (%)
Year
2024 12 6.060000
2023 12 6.060000
2022 15 7.580000
2021 13 6.570000
2020 14 7.070000
2019 11 5.560000
2018 10 5.050000
2017 12 6.060000
2016 17 8.590000
2015 14 7.070000
2014 10 5.050000
2013 10 5.050000
2012 10 5.050000
2011 8 4.040000
2010 11 5.560000
2009 9 4.550000
2008 10 5.050000
========================================
=== Fitur: OpMargin ===
Total Baris Null : 31
Jumlah Null Persentase (%)
Year
2024 1 3.230000
2023 1 3.230000
2022 2 6.450000
2021 1 3.230000
2020 2 6.450000
2019 2 6.450000
2018 2 6.450000
2017 2 6.450000
2016 4 12.900000
2015 2 6.450000
2014 1 3.230000
2013 1 3.230000
2012 1 3.230000
2011 2 6.450000
2010 4 12.900000
2009 2 6.450000
2008 1 3.230000
========================================
=== Fitur: NetMargin ===
Total Baris Null : 31
Jumlah Null Persentase (%)
Year
2024 1 3.230000
2023 1 3.230000
2022 2 6.450000
2021 1 3.230000
2020 2 6.450000
2019 2 6.450000
2018 2 6.450000
2017 2 6.450000
2016 4 12.900000
2015 2 6.450000
2014 1 3.230000
2013 1 3.230000
2012 1 3.230000
2011 2 6.450000
2010 4 12.900000
2009 2 6.450000
2008 1 3.230000
========================================
=== Fitur: CFO_TA ===
Total Baris Null : 2
Jumlah Null Persentase (%)
Year
2011 1 50.000000
2010 1 50.000000
========================================
=== Fitur: CFO_TL ===
Total Baris Null : 2
Jumlah Null Persentase (%)
Year
2011 1 50.000000
2010 1 50.000000
========================================
=== Fitur: CFO_Sales ===
Total Baris Null : 33
Jumlah Null Persentase (%)
Year
2024 1 3.030000
2023 1 3.030000
2022 2 6.060000
2021 1 3.030000
2020 2 6.060000
2019 2 6.060000
2018 2 6.060000
2017 2 6.060000
2016 4 12.120000
2015 2 6.060000
2014 1 3.030000
2013 1 3.030000
2012 1 3.030000
2011 3 9.090000
2010 5 15.150000
2009 2 6.060000
2008 1 3.030000
========================================
=== Fitur: NetCash_TA ===
Total Baris Null : 2
Jumlah Null Persentase (%)
Year
2011 1 50.000000
2010 1 50.000000
========================================
=== Fitur: EPS_proxy ===
Total Baris Null : 1
Jumlah Null Persentase (%)
Year
2013 1 100.000000
========================================
=== Fitur: Sales_per_share ===
Total Baris Null : 32
Jumlah Null Persentase (%)
Year
2024 1 3.120000
2023 1 3.120000
2022 2 6.250000
2021 1 3.120000
2020 2 6.250000
2019 2 6.250000
2018 2 6.250000
2017 2 6.250000
2016 4 12.500000
2015 2 6.250000
2014 1 3.120000
2013 2 6.250000
2012 1 3.120000
2011 2 6.250000
2010 4 12.500000
2009 2 6.250000
2008 1 3.120000
========================================
=== Fitur: CFO_per_share ===
Total Baris Null : 3
Jumlah Null Persentase (%)
Year
2013 1 33.330000
2011 1 33.330000
2010 1 33.330000
========================================
=== Fitur: log_Sales ===
Total Baris Null : 31
Jumlah Null Persentase (%)
Year
2024 1 3.230000
2023 1 3.230000
2022 2 6.450000
2021 1 3.230000
2020 2 6.450000
2019 2 6.450000
2018 2 6.450000
2017 2 6.450000
2016 4 12.900000
2015 2 6.450000
2014 1 3.230000
2013 1 3.230000
2012 1 3.230000
2011 2 6.450000
2010 4 12.900000
2009 2 6.450000
2008 1 3.230000
========================================
=== Fitur: log_MktCap ===
Total Baris Null : 25
Jumlah Null Persentase (%)
Year
2024 1 4.000000
2022 1 4.000000
2021 1 4.000000
2020 1 4.000000
2018 1 4.000000
2017 1 4.000000
2016 1 4.000000
2015 1 4.000000
2014 2 8.000000
2013 3 12.000000
2012 1 4.000000
2011 1 4.000000
2010 3 12.000000
2009 2 8.000000
2008 5 20.000000
========================================
=== Fitur: PB ===
Total Baris Null : 25
Jumlah Null Persentase (%)
Year
2024 1 4.000000
2022 1 4.000000
2021 1 4.000000
2020 1 4.000000
2018 1 4.000000
2017 1 4.000000
2016 1 4.000000
2015 1 4.000000
2014 2 8.000000
2013 3 12.000000
2012 1 4.000000
2011 1 4.000000
2010 3 12.000000
2009 2 8.000000
2008 5 20.000000
========================================
=== Fitur: Sales_growth ===
Total Baris Null : 902
Jumlah Null Persentase (%)
Year
2024 81 8.980000
2023 60 6.650000
2022 58 6.430000
2021 53 5.880000
2020 56 6.210000
2019 54 5.990000
2018 37 4.100000
2017 20 2.220000
2016 23 2.550000
2015 25 2.770000
2014 28 3.100000
2013 26 2.880000
2012 25 2.770000
2011 24 2.660000
2010 14 1.550000
2009 16 1.770000
2008 302 33.480000
========================================
=== Fitur: NI_growth ===
Total Baris Null : 853
Jumlah Null Persentase (%)
Year
2024 79 9.260000
2023 57 6.680000
2022 56 6.570000
2021 50 5.860000
2020 52 6.100000
2019 51 5.980000
2018 33 3.870000
2017 15 1.760000
2016 19 2.230000
2015 23 2.700000
2014 26 3.050000
2013 25 2.930000
2012 23 2.700000
2011 19 2.230000
2010 10 1.170000
2009 13 1.520000
2008 302 35.400000
========================================
=== Fitur: TA_growth ===
Total Baris Null : 853
Jumlah Null Persentase (%)
Year
2024 79 9.260000
2023 57 6.680000
2022 56 6.570000
2021 50 5.860000
2020 52 6.100000
2019 51 5.980000
2018 33 3.870000
2017 15 1.760000
2016 19 2.230000
2015 23 2.700000
2014 26 3.050000
2013 25 2.930000
2012 23 2.700000
2011 19 2.230000
2010 10 1.170000
2009 13 1.520000
2008 302 35.400000
========================================
=== Fitur: Equity_growth ===
Total Baris Null : 853
Jumlah Null Persentase (%)
Year
2024 79 9.260000
2023 57 6.680000
2022 56 6.570000
2021 50 5.860000
2020 52 6.100000
2019 51 5.980000
2018 33 3.870000
2017 15 1.760000
2016 19 2.230000
2015 23 2.700000
2014 26 3.050000
2013 25 2.930000
2012 23 2.700000
2011 19 2.230000
2010 10 1.170000
2009 13 1.520000
2008 302 35.400000
========================================
=== Fitur: CFO_growth ===
Total Baris Null : 857
Jumlah Null Persentase (%)
Year
2024 79 9.220000
2023 57 6.650000
2022 56 6.530000
2021 50 5.830000
2020 52 6.070000
2019 51 5.950000
2018 33 3.850000
2017 15 1.750000
2016 19 2.220000
2015 23 2.680000
2014 26 3.030000
2013 25 2.920000
2012 24 2.800000
2011 21 2.450000
2010 11 1.280000
2009 13 1.520000
2008 302 35.240000
========================================
# Kolom fitur numerik (sesuaikan bila perlu)
non_feature = {
'ticker', 'Year', 'Entity Name', 'Industry Group',
'target_distress_ppk', 'tahun_distress_ppk',
'target_distress_neg_equity', 'tahun_distress_neg_equity',
'target_distress_consecutive_loss', 'tahun_distress_consecutive_loss'
}
num_feature_cols = [c for c in df_fitur.columns
if c not in non_feature
and pd.api.types.is_numeric_dtype(df_fitur[c])
and not c.startswith('industry_')]
# Hanya fitur dengan null cukup berarti, agar heatmap ringkas
null_share = df_fitur[num_feature_cols].isnull().mean()
features = null_share[null_share > 0.002].sort_values(ascending=False).index.tolist()
# --- Heatmap 1: persentase null tiap fitur di tiap sektor ---
heat_sector = pd.DataFrame({
c: df_fitur.groupby('Industry Group')[c].apply(lambda s: s.isnull().mean() * 100)
for c in features
}).T # baris = fitur, kolom = sektor
plt.figure(figsize=(14, 9))
sns.heatmap(heat_sector, cmap='Reds', linewidths=0.3,
cbar_kws={'label': 'Persentase Nilai Kosong dalam Sektor (%)'})
plt.title('Persentase Nilai Kosong Tiap Fitur di Tiap Sektor')
plt.xlabel('Industry Group'); plt.ylabel('Fitur')
plt.tight_layout(); plt.show()
# --- Heatmap 2: persentase null tiap fitur per tahun ---
heat_year = pd.DataFrame({
c: df_fitur.groupby('Year')[c].apply(lambda s: s.isnull().mean() * 100)
for c in features
}).T
plt.figure(figsize=(12, 9))
sns.heatmap(heat_year, cmap='Blues', linewidths=0.3,
cbar_kws={'label': 'Persentase Nilai Kosong per Tahun (%)'})
plt.title('Persentase Nilai Kosong Tiap Fitur per Tahun Fiskal')
plt.xlabel('Tahun'); plt.ylabel('Fitur')
plt.tight_layout(); plt.show()

Potensi Fitur Missing Indicator
features_with_null = [
col for col in df_fitur.columns[df_fitur.isnull().any()]
if col not in ['ticker', 'Year', 'Entity Name']
and not col.startswith(('target', 'tahun'))
]
features_with_null['Parent Percent Owned (%)',
'Percent Owned - All Institutions (%)',
'Percent Owned - Insiders (%)',
'CR',
'QR',
'Age_When_IPO',
'WC_Sales',
'Inventory_CA',
'Prepaid_CA',
'Debt_TA',
'Debt_Equity',
'NetDebt_EBITDA',
'PPE_TA',
'Intang_TA',
'Altman_X3_EBIT_TA',
'Altman_X4_MVE_TL',
'Altman_X5_SalesTA_AssetTurnover',
'EBITDA_TA',
'GrossMargin',
'OpMargin',
'NetMargin',
'CFO_TA',
'CFO_TL',
'CFO_Sales',
'NetCash_TA',
'EPS_proxy',
'Sales_per_share',
'CFO_per_share',
'log_Sales',
'log_MktCap',
'PB',
'Sales_growth',
'NI_growth',
'TA_growth',
'Equity_growth',
'CFO_growth']
Jaccard Similarity Global
Dihitung secara global tanpa memisahkan jenis target
if len(features_with_null) == 0:
print("Tidak ada fitur yang memiliki nilai null untuk dianalisis.")
else:
# 1. Membuat DataFrame berisi missing indicator
df_missing = df_fitur[features_with_null].isnull()
num_features = len(features_with_null)
jaccard_matrix = np.zeros((num_features, num_features))
# 2. Menghitung Jaccard Similarity
for i in range(num_features):
for j in range(num_features):
col_i = df_missing.iloc[:, i]
col_j = df_missing.iloc[:, j]
intersection = (col_i & col_j).sum()
union = (col_i | col_j).sum()
if union == 0:
jaccard_matrix[i, j] = 1.0
else:
jaccard_matrix[i, j] = intersection / union
df_jaccard = pd.DataFrame(jaccard_matrix, index=features_with_null, columns=features_with_null)
# 3. Membuat mask untuk memotong segitiga atas
mask = np.triu(np.ones_like(df_jaccard, dtype=bool), k=1)
# 4. Memvisualisasikan Heatmap
plt.figure(figsize=(max(6, num_features * 0.4), max(6, num_features * 0.4)))
sns.heatmap(df_jaccard,
mask=mask,
annot=False, # Tetap tanpa angka agar compact
cmap='coolwarm', # Kembali ke palet biru-merah kontras
vmin=0, vmax=1, # Rentang mutlak Jaccard 0 hingga 1
linewidths=0.5,
square=True,
cbar_kws={"shrink": .8, "label": "Skor Jaccard"})
plt.title('Heatmap Jaccard Similarity antar Missing Indicator',
fontsize=14, fontweight='bold', pad=20)
plt.xticks(rotation=90, fontsize=9)
plt.yticks(rotation=0, fontsize=9)
plt.tight_layout()
plt.show()
if len(features_with_null) == 0:
print("Tidak ada fitur yang memiliki nilai null untuk dianalisis.")
else:
# 1. Membuat DataFrame boolean untuk missing indicator
df_missing = df_fitur[features_with_null].isnull()
# 2. Inisialisasi dictionary untuk mengelompokkan kolom
# Key: tuple pola boolean (True/False) dari atas ke bawah
# Value: list berisi nama-nama kolom yang memiliki pola tersebut
kelompok_null = {}
for col in features_with_null:
# Mengonversi array boolean menjadi tuple agar dapat menjadi key dictionary
pola = tuple(df_missing[col].values)
if pola not in kelompok_null:
kelompok_null[pola] = []
kelompok_null[pola].append(col)
# 3. Menampilkan hasil pengelompokan (hanya untuk kelompok yang berisi > 1 kolom)
print("=== Kelompok Kolom dengan Missing Indicator Sama Persis (Jaccard = 1.0) ===")
kelompok_ditemukan = False
nomor_kelompok = 1
for pola, daftar_kolom in kelompok_null.items():
# Hanya mencetak jika ada lebih dari 1 kolom dalam kelompok tersebut
if len(daftar_kolom) > 1:
kelompok_ditemukan = True
print(f"Kelompok {nomor_kelompok} ({len(daftar_kolom)} kolom tergabung):")
# Mencetak nama-nama kolom yang tergabung dalam satu baris agar rapi
print(f" -> {', '.join(daftar_kolom)}\n")
nomor_kelompok += 1
if not kelompok_ditemukan:
print("Tidak ditemukan kelompok kolom yang pola kekosongannya sama persis (Jaccard = 1.0).")=== Kelompok Kolom dengan Missing Indicator Sama Persis (Jaccard = 1.0) ===
Kelompok 1 (5 kolom tergabung):
-> WC_Sales, Altman_X5_SalesTA_AssetTurnover, OpMargin, NetMargin, log_Sales
Kelompok 2 (2 kolom tergabung):
-> Debt_TA, Debt_Equity
Kelompok 3 (3 kolom tergabung):
-> Altman_X4_MVE_TL, log_MktCap, PB
Kelompok 4 (3 kolom tergabung):
-> CFO_TA, CFO_TL, NetCash_TA
Kelompok 5 (3 kolom tergabung):
-> NI_growth, TA_growth, Equity_growth
missing_indicators_used_global = features_with_null.copy()
if len(missing_indicators_used_global) == 0:
print("Tidak ada fitur yang memiliki nilai null untuk diproses.")
else:
# 2. Membuat DataFrame boolean (True jika null, False jika tidak)
# Hal ini dilakukan di luar loop agar perhitungan Jaccard jauh lebih cepat
df_missing = df_fitur[missing_indicators_used_global].isnull()
# Varians untuk masing-masing missing indicator
# True/False diubah ke 1/0 agar varians bisa dihitung
varians_missing = df_missing.astype(int).var()
# 3. Inisialisasi variabel untuk filter
kolom_dibuang = set()
threshold = 0.90
print(f"=== Proses Eliminasi Missing Indicator ===")
print(f"Jumlah kolom awal : {len(missing_indicators_used_global)}")
print(f"Threshold Jaccard : {threshold} (Inklusif)\n")
# 4. Hitung semua pasangan yang memiliki Jaccard >= threshold
pasangan_mirip = []
for i in range(len(missing_indicators_used_global)):
kolom_i = missing_indicators_used_global[i]
col_i = df_missing[kolom_i]
for j in range(i + 1, len(missing_indicators_used_global)):
kolom_j = missing_indicators_used_global[j]
col_j = df_missing[kolom_j]
# Perhitungan Jaccard Similarity
intersection = (col_i & col_j).sum()
union = (col_i | col_j).sum()
if union == 0:
jaccard_sim = 1.0
else:
jaccard_sim = intersection / union
# Simpan hanya pasangan yang lolos threshold
if jaccard_sim >= threshold:
pasangan_mirip.append((kolom_i, kolom_j, jaccard_sim))
# 5. Eliminasi iteratif berdasarkan:
# - pasangan aktif dengan Jaccard tertinggi
# - varians lebih rendah
# - degree aktif lebih kecil
# - nama kolom
while True:
# Ambil hanya pasangan yang kedua kolomnya belum dibuang
pasangan_aktif = [
(kolom_a, kolom_b, skor_jaccard)
for kolom_a, kolom_b, skor_jaccard in pasangan_mirip
if kolom_a not in kolom_dibuang and kolom_b not in kolom_dibuang
]
# Jika sudah tidak ada pasangan aktif, selesai
if len(pasangan_aktif) == 0:
break
# Hitung degree aktif (jumlah pasangan aktif) tiap kolom
degree_aktif = {}
for kolom_a, kolom_b, _ in pasangan_aktif:
degree_aktif[kolom_a] = degree_aktif.get(kolom_a, 0) + 1
degree_aktif[kolom_b] = degree_aktif.get(kolom_b, 0) + 1
# Ambil pasangan aktif dengan Jaccard tertinggi
# Tie-breaker tambahan pada nama kolom agar hasil deterministik
kolom_utama, kolom_pembanding, jaccard_sim = max(
pasangan_aktif,
key=lambda x: (x[2], x[0], x[1])
)
var_utama = varians_missing[kolom_utama]
var_pembanding = varians_missing[kolom_pembanding]
degree_utama = degree_aktif[kolom_utama]
degree_pembanding = degree_aktif[kolom_pembanding]
# Tentukan kolom yang dieliminasi
# 1. Buang yang variansnya lebih rendah
if var_utama < var_pembanding:
kolom_dieliminasi = kolom_utama
kolom_dipertahankan = kolom_pembanding
elif var_pembanding < var_utama:
kolom_dieliminasi = kolom_pembanding
kolom_dipertahankan = kolom_utama
# 2. Jika varians sama, buang yang degree aktifnya lebih kecil
elif degree_utama < degree_pembanding:
kolom_dieliminasi = kolom_utama
kolom_dipertahankan = kolom_pembanding
elif degree_pembanding < degree_utama:
kolom_dieliminasi = kolom_pembanding
kolom_dipertahankan = kolom_utama
# 3. Jika masih sama, pakai nama kolom sebagai tie-breaker
# Di sini: buang nama kolom yang lebih besar secara alfabetis
else:
if kolom_utama > kolom_pembanding:
kolom_dieliminasi = kolom_utama
kolom_dipertahankan = kolom_pembanding
else:
kolom_dieliminasi = kolom_pembanding
kolom_dipertahankan = kolom_utama
kolom_dibuang.add(kolom_dieliminasi)
print(
f"[-] Eliminasi: '{kolom_dieliminasi}' "
f"(Mirip dengan '{kolom_dipertahankan}' | Skor: {jaccard_sim:.4f})"
)
# 6. Memperbarui list missing_indicators_used_global hanya dengan kolom yang dipertahankan
missing_indicators_used_global = [
col for col in missing_indicators_used_global
if col not in kolom_dibuang
]
# 7. Menampilkan ringkasan hasil akhir
print("\n=== Ringkasan Hasil Akhir ===")
print(f"Jumlah kolom yang dieliminasi : {len(kolom_dibuang)}")
print(f"Jumlah kolom yang tersisa : {len(missing_indicators_used_global)}")
print("\nDaftar kolom yang akan digunakan untuk membuat missing indicator:")
for col in missing_indicators_used_global:
print(f" -> {col}")=== Proses Eliminasi Missing Indicator ===
Jumlah kolom awal : 36
Threshold Jaccard : 0.9 (Inklusif)
[-] Eliminasi: 'log_MktCap' (Mirip dengan 'PB' | Skor: 1.0000)
[-] Eliminasi: 'log_Sales' (Mirip dengan 'WC_Sales' | Skor: 1.0000)
[-] Eliminasi: 'WC_Sales' (Mirip dengan 'OpMargin' | Skor: 1.0000)
[-] Eliminasi: 'TA_growth' (Mirip dengan 'Equity_growth' | Skor: 1.0000)
[-] Eliminasi: 'OpMargin' (Mirip dengan 'NetMargin' | Skor: 1.0000)
[-] Eliminasi: 'NI_growth' (Mirip dengan 'Equity_growth' | Skor: 1.0000)
[-] Eliminasi: 'Debt_TA' (Mirip dengan 'Debt_Equity' | Skor: 1.0000)
[-] Eliminasi: 'NetCash_TA' (Mirip dengan 'CFO_TL' | Skor: 1.0000)
[-] Eliminasi: 'CFO_TL' (Mirip dengan 'CFO_TA' | Skor: 1.0000)
[-] Eliminasi: 'NetMargin' (Mirip dengan 'Altman_X5_SalesTA_AssetTurnover' | Skor: 1.0000)
[-] Eliminasi: 'PB' (Mirip dengan 'Altman_X4_MVE_TL' | Skor: 1.0000)
[-] Eliminasi: 'QR' (Mirip dengan 'CR' | Skor: 0.9972)
[-] Eliminasi: 'Equity_growth' (Mirip dengan 'CFO_growth' | Skor: 0.9953)
[-] Eliminasi: 'Altman_X3_EBIT_TA' (Mirip dengan 'EBITDA_TA' | Skor: 0.9937)
[-] Eliminasi: 'EBITDA_TA' (Mirip dengan 'NetDebt_EBITDA' | Skor: 0.9822)
[-] Eliminasi: 'Altman_X5_SalesTA_AssetTurnover' (Mirip dengan 'Sales_per_share' | Skor: 0.9688)
[-] Eliminasi: 'CFO_growth' (Mirip dengan 'Sales_growth' | Skor: 0.9415)
[-] Eliminasi: 'Sales_per_share' (Mirip dengan 'CFO_Sales' | Skor: 0.9118)
=== Ringkasan Hasil Akhir ===
Jumlah kolom yang dieliminasi : 18
Jumlah kolom yang tersisa : 18
Daftar kolom yang akan digunakan untuk membuat missing indicator:
-> Parent Percent Owned (%)
-> Percent Owned - All Institutions (%)
-> Percent Owned - Insiders (%)
-> CR
-> Age_When_IPO
-> Inventory_CA
-> Prepaid_CA
-> Debt_Equity
-> NetDebt_EBITDA
-> PPE_TA
-> Intang_TA
-> Altman_X4_MVE_TL
-> GrossMargin
-> CFO_TA
-> CFO_Sales
-> EPS_proxy
-> CFO_per_share
-> Sales_growth
if len(missing_indicators_used_global) == 0:
print("Tidak ada fitur dalam daftar missing_indicators_used_global untuk dianalisis.")
else:
# 1. Membuat DataFrame berisi missing indicator hanya untuk kolom yang tersisa
df_missing = df_fitur[missing_indicators_used_global].isnull()
# 2. Menginisialisasi matriks kosong dengan dimensi sesuai jumlah kolom yang difilter
num_features = len(missing_indicators_used_global)
jaccard_matrix = np.zeros((num_features, num_features))
# 3. Menghitung Jaccard Similarity untuk setiap pasangan fitur yang tersisa
for i in range(num_features):
for j in range(num_features):
col_i = df_missing.iloc[:, i]
col_j = df_missing.iloc[:, j]
# Intersection: Jumlah baris di mana KEDUA fitur bernilai null
intersection = (col_i & col_j).sum()
# Union: Jumlah baris di mana SALAH SATU atau KEDUA fitur bernilai null
union = (col_i | col_j).sum()
# Menghitung Jaccard Similarity (mencegah pembagian dengan nol)
if union == 0:
jaccard_matrix[i, j] = 1.0
else:
jaccard_matrix[i, j] = intersection / union
# 4. Mengubah matriks numpy menjadi DataFrame agar memiliki nama baris dan kolom yang tepat
df_jaccard = pd.DataFrame(jaccard_matrix,
index=missing_indicators_used_global,
columns=missing_indicators_used_global)
# 5. Memvisualisasikan matriks menggunakan Heatmap dari Seaborn
# Ukuran gambar disesuaikan secara dinamis dengan jumlah fitur yang tersisa
plt.figure(figsize=(max(8, num_features * 0.8), max(8, num_features * 0.8)))
sns.heatmap(df_jaccard,
annot=True, # Menampilkan angka kemiripan di dalam kotak
fmt=".2f", # Membulatkan angka menjadi 2 desimal
cmap='coolwarm', # Skema warna (merah = tinggi, biru = rendah)
vmin=0, vmax=1, # Rentang nilai Jaccard (0 hingga 1)
linewidths=0.5, # Garis pembatas antar kotak
square=True, # Memaksa bentuk sel menjadi persegi
cbar_kws={"shrink": .8}) # Menyesuaikan ukuran colorbar (legenda warna)
# Memperbarui judul agar mencerminkan bahwa data telah difilter
plt.title('Heatmap Jaccard Similarity (Setelah Reduksi Dimensi)',
fontsize=14, fontweight='bold', pad=20)
plt.xticks(rotation=90)
plt.yticks(rotation=0)
plt.tight_layout()
plt.show()
if len(missing_indicators_used_global) == 0:
print("Tidak ada fitur dalam daftar missing_indicators_used_global untuk dianalisis.")
else:
# 1. Membuat DataFrame berisi missing indicator hanya untuk kolom yang tersisa
df_missing = df_fitur[missing_indicators_used_global].isnull()
# 2. Menginisialisasi matriks kosong dengan dimensi sesuai jumlah kolom yang difilter
num_features = len(missing_indicators_used_global)
jaccard_matrix = np.zeros((num_features, num_features))
# 3. Menghitung Jaccard Similarity untuk setiap pasangan fitur yang tersisa
for i in range(num_features):
for j in range(num_features):
col_i = df_missing.iloc[:, i]
col_j = df_missing.iloc[:, j]
# Intersection: Jumlah baris di mana KEDUA fitur bernilai null
intersection = (col_i & col_j).sum()
# Union: Jumlah baris di mana SALAH SATU atau KEDUA fitur bernilai null
union = (col_i | col_j).sum()
# Menghitung Jaccard Similarity (mencegah pembagian dengan nol)
if union == 0:
jaccard_matrix[i, j] = 1.0
else:
jaccard_matrix[i, j] = intersection / union
# 4. Mengubah matriks numpy menjadi DataFrame
df_jaccard = pd.DataFrame(jaccard_matrix,
index=missing_indicators_used_global,
columns=missing_indicators_used_global)
# ==========================================
# MEMBUAT MASKING UNTUK SEGITIGA ATAS
# ==========================================
mask = np.triu(np.ones_like(df_jaccard, dtype=bool), k=1)
# 5. Memvisualisasikan matriks menggunakan Heatmap dari Seaborn
# Ukuran gambar dibuat lebih padat (pengali 0.4) karena tidak ada teks di dalam kotak
plt.figure(figsize=(max(6, num_features * 0.4), max(6, num_features * 0.4)))
sns.heatmap(df_jaccard,
mask=mask, # Menerapkan topeng untuk menyembunyikan sisi atas
annot=False, # Mematikan teks angka di dalam kotak
cmap='coolwarm', # Tetap menggunakan skema warna biru-merah
vmin=0, vmax=1, # Rentang nilai Jaccard (0 hingga 1)
linewidths=0.5, # Garis pembatas antar kotak
square=True, # Memaksa bentuk sel menjadi persegi
cbar_kws={"shrink": .8, "label": "Skor Jaccard"}) # Label pada colorbar
plt.title('Heatmap Jaccard Similarity (Setelah Reduksi Dimensi)',
fontsize=11, fontweight='bold', pad=20)
# Membesarkan sedikit font pada sumbu agar mudah dibaca
plt.xticks(rotation=90, fontsize=10)
plt.yticks(rotation=0, fontsize=10)
plt.tight_layout()
plt.show()
Filter Persentase Fitur Null thd Tiap Target
if len(missing_indicators_used_global) == 0:
print("Daftar missing_indicators_used_global kosong. Tidak ada yang perlu dihitung.")
else:
total_baris = len(df_fitur)
hasil_perhitungan = []
# Menghitung jumlah dan persentase null untuk setiap kolom
for col in missing_indicators_used_global:
jumlah_null = df_fitur[col].isnull().sum()
persentase_null = (jumlah_null / total_baris) * 100
hasil_perhitungan.append({
'Nama Fitur': col,
'Jumlah Null': jumlah_null,
'Persentase (%)': persentase_null
})
# Mengonversi list dictionary menjadi DataFrame
df_rekap_null = pd.DataFrame(hasil_perhitungan)
# Mengurutkan data dari persentase terbesar ke terkecil
df_rekap_null = df_rekap_null.sort_values(by='Persentase (%)', ascending=False).reset_index(drop=True)
# Membulatkan nilai persentase agar tampilannya lebih rapi (2 angka di belakang koma)
df_rekap_null['Persentase (%)'] = df_rekap_null['Persentase (%)'].round(2)
# Mencetak hasil
print(f"=== Rekapitulasi Missing Values Scr Global ===")
print(f"Total Baris Data (df_fitur): {total_baris}\n")
print(df_rekap_null.to_string())=== Rekapitulasi Missing Values Scr Global ===
Total Baris Data (df_fitur): 8489
Nama Fitur Jumlah Null Persentase (%)
0 Intang_TA 6726 79.230000
1 Percent Owned - All Institutions (%) 3947 46.500000
2 Parent Percent Owned (%) 3641 42.890000
3 Percent Owned - Insiders (%) 2551 30.050000
4 Prepaid_CA 1462 17.220000
5 Inventory_CA 1206 14.210000
6 NetDebt_EBITDA 1126 13.260000
7 Sales_growth 902 10.630000
8 CR 706 8.320000
9 GrossMargin 198 2.330000
10 CFO_Sales 33 0.390000
11 Altman_X4_MVE_TL 25 0.290000
12 Age_When_IPO 21 0.250000
13 Debt_Equity 21 0.250000
14 PPE_TA 19 0.220000
15 CFO_per_share 3 0.040000
16 CFO_TA 2 0.020000
17 EPS_proxy 1 0.010000
# df_fitur dan missing_indicators_used_global telah terdefinisi sebelumnya
# Mendapatkan list kolom target yang diawali dengan 'target'
kolom_target = [col for col in df_fitur.columns if col.startswith('target')]
if len(missing_indicators_used_global) == 0:
print("Daftar missing_indicators_used_global kosong. Tidak ada yang perlu dihitung.")
elif len(kolom_target) == 0:
print("Tidak ditemukan kolom yang namanya diawali dengan 'target' pada df_fitur.")
else:
# Melakukan iterasi untuk setiap kolom target
for target in kolom_target:
print(f"=== Rekapitulasi Missing Values untuk Target: '{target}' ===")
# Mengambil subset data di mana kolom target saat ini tidak bernilai null
df_subset = df_fitur[df_fitur[target].notnull()]
total_baris_subset = len(df_subset)
# Dikonversi ke int agar tampilannya tidak berupa desimal (misal 15.0)
target_positif = int(df_subset[target].sum())
print(f"Total Baris Data (Target '{target}' tidak null): {total_baris_subset}")
print(f"Total Baris Bertarget Positif: {target_positif} atau {target_positif/total_baris_subset*100:.2f}% dari total baris tipe target tsb\n")
if total_baris_subset == 0:
print("Tidak ada data yang valid (semua baris pada target ini null).\n")
print("="*60 + "\n")
continue
hasil_perhitungan = []
# Menghitung jumlah dan persentase null untuk setiap kolom pada df_subset
for col in missing_indicators_used_global:
# Masking boolean untuk fitur yang null
is_null_mask = df_subset[col].isnull()
# Perhitungan 1: Total Null
jumlah_null = is_null_mask.sum()
persentase_null = (jumlah_null / total_baris_subset) * 100
# Perhitungan 2: Null DAN Target = 1
# Menggunakan operator & untuk mencari irisan kedua kondisi
jumlah_null_target_1 = (is_null_mask & (df_subset[target] == 1)).sum()
persentase_null_target_1 = (jumlah_null_target_1 / total_baris_subset) * 100
hasil_perhitungan.append({
'Nama Fitur': col,
'Jumlah Null': jumlah_null,
'Persentase (%)': persentase_null,
'Null & Target=1': jumlah_null_target_1,
'% Null & Target=1': persentase_null_target_1
})
# Mengonversi list dictionary menjadi DataFrame
df_rekap_null = pd.DataFrame(hasil_perhitungan)
# Mengurutkan data dari persentase terbesar ke terkecil
df_rekap_null = df_rekap_null.sort_values(by='Persentase (%)', ascending=False).reset_index(drop=True)
# Membulatkan nilai persentase agar tampilannya lebih rapi (2 angka di belakang koma)
df_rekap_null['Persentase (%)'] = df_rekap_null['Persentase (%)'].round(2)
df_rekap_null['% Null & Target=1'] = df_rekap_null['% Null & Target=1'].round(2)
# Mencetak hasil
print(df_rekap_null.to_string())
print("\n" + "="*60 + "\n")=== Rekapitulasi Missing Values untuk Target: 'target_distress_ppk' ===
Total Baris Data (Target 'target_distress_ppk' tidak null): 2583
Total Baris Bertarget Positif: 510 atau 19.74% dari total baris tipe target tsb
Nama Fitur Jumlah Null Persentase (%) Null & Target=1 % Null & Target=1
0 Intang_TA 1579 61.130000 342 13.240000
1 Percent Owned - All Institutions (%) 1280 49.550000 335 12.970000
2 Parent Percent Owned (%) 1137 44.020000 280 10.840000
3 Percent Owned - Insiders (%) 697 26.980000 171 6.620000
4 Prepaid_CA 447 17.310000 114 4.410000
5 Inventory_CA 357 13.820000 73 2.830000
6 NetDebt_EBITDA 300 11.610000 41 1.590000
7 Sales_growth 213 8.250000 38 1.470000
8 CR 207 8.010000 21 0.810000
9 GrossMargin 52 2.010000 2 0.080000
10 Age_When_IPO 11 0.430000 0 0.000000
11 Debt_Equity 9 0.350000 0 0.000000
12 PPE_TA 6 0.230000 0 0.000000
13 CFO_Sales 5 0.190000 0 0.000000
14 Altman_X4_MVE_TL 2 0.080000 2 0.080000
15 CFO_TA 0 0.000000 0 0.000000
16 EPS_proxy 0 0.000000 0 0.000000
17 CFO_per_share 0 0.000000 0 0.000000
============================================================
=== Rekapitulasi Missing Values untuk Target: 'target_distress_neg_equity' ===
Total Baris Data (Target 'target_distress_neg_equity' tidak null): 6403
Total Baris Bertarget Positif: 116 atau 1.81% dari total baris tipe target tsb
Nama Fitur Jumlah Null Persentase (%) Null & Target=1 % Null & Target=1
0 Intang_TA 5348 83.520000 92 1.440000
1 Percent Owned - All Institutions (%) 2773 43.310000 79 1.230000
2 Parent Percent Owned (%) 2638 41.200000 76 1.190000
3 Percent Owned - Insiders (%) 1947 30.410000 38 0.590000
4 Prepaid_CA 1109 17.320000 25 0.390000
5 Inventory_CA 941 14.700000 14 0.220000
6 NetDebt_EBITDA 923 14.420000 7 0.110000
7 Sales_growth 720 11.240000 14 0.220000
8 CR 567 8.860000 1 0.020000
9 GrossMargin 152 2.370000 5 0.080000
10 Debt_Equity 21 0.330000 0 0.000000
11 Altman_X4_MVE_TL 21 0.330000 4 0.060000
12 CFO_Sales 15 0.230000 1 0.020000
13 Age_When_IPO 15 0.230000 0 0.000000
14 PPE_TA 14 0.220000 0 0.000000
15 CFO_per_share 3 0.050000 0 0.000000
16 CFO_TA 2 0.030000 0 0.000000
17 EPS_proxy 1 0.020000 0 0.000000
============================================================
=== Rekapitulasi Missing Values untuk Target: 'target_distress_consecutive_loss' ===
Total Baris Data (Target 'target_distress_consecutive_loss' tidak null): 5160
Total Baris Bertarget Positif: 565 atau 10.95% dari total baris tipe target tsb
Nama Fitur Jumlah Null Persentase (%) Null & Target=1 % Null & Target=1
0 Intang_TA 4367 84.630000 469 9.090000
1 Percent Owned - All Institutions (%) 2082 40.350000 336 6.510000
2 Parent Percent Owned (%) 1980 38.370000 293 5.680000
3 Percent Owned - Insiders (%) 1540 29.840000 185 3.590000
4 Prepaid_CA 809 15.680000 110 2.130000
5 NetDebt_EBITDA 798 15.470000 51 0.990000
6 Inventory_CA 778 15.080000 61 1.180000
7 Sales_growth 722 13.990000 128 2.480000
8 CR 520 10.080000 25 0.480000
9 GrossMargin 110 2.130000 13 0.250000
10 Altman_X4_MVE_TL 17 0.330000 4 0.080000
11 Age_When_IPO 15 0.290000 0 0.000000
12 Debt_Equity 14 0.270000 2 0.040000
13 CFO_Sales 10 0.190000 5 0.100000
14 PPE_TA 6 0.120000 0 0.000000
15 CFO_per_share 2 0.040000 0 0.000000
16 CFO_TA 1 0.020000 0 0.000000
17 EPS_proxy 1 0.020000 0 0.000000
============================================================
# Mendapatkan list kolom target
kolom_target = [col for col in df_fitur.columns if col.startswith('target')]
# Dictionary untuk menyimpan hasil lolos Tahap 1 saja
fitur_lolos_tahap1 = dict()
if len(missing_indicators_used_global) == 0:
print("Daftar missing_indicators_used_global kosong.")
elif len(kolom_target) == 0:
print("Tidak ditemukan kolom target.")
else:
for target in kolom_target:
print(f"\n{'='*60}")
print(f"TAHAP 1: FILTER JUMLAH NULL (MIN. 20) UNTUK '{target}'")
print(f"{'='*60}")
# Subset data di mana target valid
df_subset = df_fitur[df_fitur[target].notnull()]
if len(df_subset) == 0:
print("Tidak ada data valid untuk target ini.")
continue
lolos = []
ditolak = []
# Evaluasi satu per satu fitur global
for col in missing_indicators_used_global:
jumlah_null = df_subset[col].isnull().sum()
if jumlah_null >= 20:
lolos.append((col, jumlah_null))
else:
ditolak.append((col, jumlah_null))
# Simpan nama fitur yang lolos ke dictionary
fitur_lolos_tahap1[target] = [item[0] for item in lolos]
# Menampilkan ringkasan hasil
print(f"Total fitur dievaluasi: {len(missing_indicators_used_global)}")
print(f"Lolos (>= 20 null) : {len(lolos)}")
print(f"Ditolak (< 20 null) : {len(ditolak)}\n")
print("--- DAFTAR FITUR DITOLAK (< 20 Null) ---")
if len(ditolak) > 0:
for col, n in ditolak:
print(f"[-] {col:<35} : {n} null")
else:
print("Tidak ada fitur yang ditolak.")
print("\n--- DAFTAR FITUR LOLOS (>= 20 Null) ---")
if len(lolos) > 0:
for col, n in lolos:
print(f"[+] {col:<35} : {n} null")
else:
print("Tidak ada fitur yang lolos.")
============================================================
TAHAP 1: FILTER JUMLAH NULL (MIN. 20) UNTUK 'target_distress_ppk'
============================================================
Total fitur dievaluasi: 18
Lolos (>= 20 null) : 10
Ditolak (< 20 null) : 8
--- DAFTAR FITUR DITOLAK (< 20 Null) ---
[-] Age_When_IPO : 11 null
[-] Debt_Equity : 9 null
[-] PPE_TA : 6 null
[-] Altman_X4_MVE_TL : 2 null
[-] CFO_TA : 0 null
[-] CFO_Sales : 5 null
[-] EPS_proxy : 0 null
[-] CFO_per_share : 0 null
--- DAFTAR FITUR LOLOS (>= 20 Null) ---
[+] Parent Percent Owned (%) : 1137 null
[+] Percent Owned - All Institutions (%) : 1280 null
[+] Percent Owned - Insiders (%) : 697 null
[+] CR : 207 null
[+] Inventory_CA : 357 null
[+] Prepaid_CA : 447 null
[+] NetDebt_EBITDA : 300 null
[+] Intang_TA : 1579 null
[+] GrossMargin : 52 null
[+] Sales_growth : 213 null
============================================================
TAHAP 1: FILTER JUMLAH NULL (MIN. 20) UNTUK 'target_distress_neg_equity'
============================================================
Total fitur dievaluasi: 18
Lolos (>= 20 null) : 12
Ditolak (< 20 null) : 6
--- DAFTAR FITUR DITOLAK (< 20 Null) ---
[-] Age_When_IPO : 15 null
[-] PPE_TA : 14 null
[-] CFO_TA : 2 null
[-] CFO_Sales : 15 null
[-] EPS_proxy : 1 null
[-] CFO_per_share : 3 null
--- DAFTAR FITUR LOLOS (>= 20 Null) ---
[+] Parent Percent Owned (%) : 2638 null
[+] Percent Owned - All Institutions (%) : 2773 null
[+] Percent Owned - Insiders (%) : 1947 null
[+] CR : 567 null
[+] Inventory_CA : 941 null
[+] Prepaid_CA : 1109 null
[+] Debt_Equity : 21 null
[+] NetDebt_EBITDA : 923 null
[+] Intang_TA : 5348 null
[+] Altman_X4_MVE_TL : 21 null
[+] GrossMargin : 152 null
[+] Sales_growth : 720 null
============================================================
TAHAP 1: FILTER JUMLAH NULL (MIN. 20) UNTUK 'target_distress_consecutive_loss'
============================================================
Total fitur dievaluasi: 18
Lolos (>= 20 null) : 10
Ditolak (< 20 null) : 8
--- DAFTAR FITUR DITOLAK (< 20 Null) ---
[-] Age_When_IPO : 15 null
[-] Debt_Equity : 14 null
[-] PPE_TA : 6 null
[-] Altman_X4_MVE_TL : 17 null
[-] CFO_TA : 1 null
[-] CFO_Sales : 10 null
[-] EPS_proxy : 1 null
[-] CFO_per_share : 2 null
--- DAFTAR FITUR LOLOS (>= 20 Null) ---
[+] Parent Percent Owned (%) : 1980 null
[+] Percent Owned - All Institutions (%) : 2082 null
[+] Percent Owned - Insiders (%) : 1540 null
[+] CR : 520 null
[+] Inventory_CA : 778 null
[+] Prepaid_CA : 809 null
[+] NetDebt_EBITDA : 798 null
[+] Intang_TA : 4367 null
[+] GrossMargin : 110 null
[+] Sales_growth : 722 null
fitur_lolos_tahap1{'target_distress_ppk': ['Parent Percent Owned (%)',
'Percent Owned - All Institutions (%)',
'Percent Owned - Insiders (%)',
'CR',
'Inventory_CA',
'Prepaid_CA',
'NetDebt_EBITDA',
'Intang_TA',
'GrossMargin',
'Sales_growth'],
'target_distress_neg_equity': ['Parent Percent Owned (%)',
'Percent Owned - All Institutions (%)',
'Percent Owned - Insiders (%)',
'CR',
'Inventory_CA',
'Prepaid_CA',
'Debt_Equity',
'NetDebt_EBITDA',
'Intang_TA',
'Altman_X4_MVE_TL',
'GrossMargin',
'Sales_growth'],
'target_distress_consecutive_loss': ['Parent Percent Owned (%)',
'Percent Owned - All Institutions (%)',
'Percent Owned - Insiders (%)',
'CR',
'Inventory_CA',
'Prepaid_CA',
'NetDebt_EBITDA',
'Intang_TA',
'GrossMargin',
'Sales_growth']}
Fisher’s Exact Test
# Pastikan fitur_lolos_tahap1 sudah terisi dari eksekusi kode sebelumnya
if 'fitur_lolos_tahap1' not in locals() or len(fitur_lolos_tahap1) == 0:
print("Variabel 'fitur_lolos_tahap1' kosong. Jalankan dulu kode filter Tahap 1 (Batas Null >= 20).")
else:
# Loop per target
for target, fitur_list in fitur_lolos_tahap1.items():
print(f"\n{'='*70}")
print(f"VISUALISASI FISHER'S EXACT TEST: '{target}'")
print(f"{'='*70}")
if len(fitur_list) == 0:
print(f"Tidak ada fitur yang lolos evaluasi Tahap 1 untuk target {target}. Lewati visualisasi.")
continue
# Filter baris yang targetnya valid (tidak null)
df_subset = df_fitur[df_fitur[target].notnull()].copy()
results = []
for col in fitur_list:
is_null = df_subset[col].isnull()
target_0 = df_subset[target] == 0
target_1 = df_subset[target] == 1
# Hitung matriks kontingensi 2x2
a = (~is_null & target_0).sum()
b = (~is_null & target_1).sum()
c = (is_null & target_0).sum()
d = (is_null & target_1).sum()
# Eksekusi Fisher's Exact Test
odds_ratio, p_value = fisher_exact([[a, b], [c, d]])
# Hitung -Log10 p-value (tambah 1e-100 agar tidak error log(0) jika p sangat kecil)
log_p = -np.log10(p_value + 1e-100)
results.append({
'Fitur': col,
'P-Value': p_value,
'Log_P': log_p,
'Odds_Ratio': odds_ratio,
'Signifikan': 'Ya' if p_value < 0.05 else 'Tidak'
})
# Konversi ke DataFrame & urutkan dari yang paling signifikan
df_results = pd.DataFrame(results).sort_values(by='Log_P', ascending=True)
# ==========================================
# BAGIAN PLOTTING (VISUALISASI)
# ==========================================
# Tinggi figur menyesuaikan jumlah fitur (minimal 4 inci agar tidak terlalu sempit)
tinggi_fig = max(4, len(fitur_list) * 0.4)
plt.figure(figsize=(12, tinggi_fig))
# Warna batang: Hijau untuk signifikan (<0.05), Abu-abu untuk tidak signifikan
colors = ['#2ecc71' if x == 'Ya' else '#95a5a6' for x in df_results['Signifikan']]
bars = plt.barh(df_results['Fitur'], df_results['Log_P'], color=colors)
# Garis batas signifikansi merah ( p = 0.05 => -log10(0.05) ~ 1.301 )
threshold_val = -np.log10(0.05)
plt.axvline(x=threshold_val, color='#e74c3c', linestyle='--', linewidth=2,
label=f'Threshold Signifikansi (p=0.05)')
# Pengaturan label dan judul
plt.title(f"Fisher's Exact Test - Signifikansi Null terhadap {target}\n(Melewati Garis Merah = Signifikan)",
fontsize=14, fontweight='bold', pad=15)
plt.xlabel('-Log10(P-Value)', fontsize=12, fontweight='bold')
plt.ylabel('Nama Fitur (Hanya yang Null >= 20)', fontsize=12, fontweight='bold')
# Tambahkan area abu-abu transparan untuk zona "Ditolak" (kiri garis merah)
plt.axvspan(0, threshold_val, color='gray', alpha=0.1)
plt.legend(loc='lower right', fontsize=10)
# Menambahkan teks p-value asli di ujung tiap batang
for bar, p_val in zip(bars, df_results['P-Value']):
width = bar.get_width()
# Format teks: jika p sangat kecil, tampilkan < 0.0001
teks_label = f" p = {p_val:.4f}" if p_val >= 0.0001 else " p < 0.0001"
plt.text(width + 0.05, bar.get_y() + bar.get_height()/2, teks_label,
va='center', fontsize=10, color='black', fontweight='bold')
plt.tight_layout()
plt.show()
# Cetak Ringkasan Angka
print(f"Ringkasan {target}:")
jml_lolos = (df_results['P-Value'] < 0.05).sum()
jml_gagal = len(df_results) - jml_lolos
print(f"- Fitur Signifikan (Hijau) : {jml_lolos}")
print(f"- Fitur Tidak Signifikan (Abu-abu) : {jml_gagal}\n")
======================================================================
VISUALISASI FISHER'S EXACT TEST: 'target_distress_ppk'
======================================================================

Ringkasan target_distress_ppk:
- Fitur Signifikan (Hijau) : 8
- Fitur Tidak Signifikan (Abu-abu) : 2
======================================================================
VISUALISASI FISHER'S EXACT TEST: 'target_distress_neg_equity'
======================================================================

Ringkasan target_distress_neg_equity:
- Fitur Signifikan (Hijau) : 5
- Fitur Tidak Signifikan (Abu-abu) : 7
======================================================================
VISUALISASI FISHER'S EXACT TEST: 'target_distress_consecutive_loss'
======================================================================

Ringkasan target_distress_consecutive_loss:
- Fitur Signifikan (Hijau) : 7
- Fitur Tidak Signifikan (Abu-abu) : 3
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import fisher_exact
if 'fitur_lolos_tahap1' in locals() and len(fitur_lolos_tahap1) > 0:
n_targets = len(fitur_lolos_tahap1)
# 1. PRE-KALKULASI TINGGI KANVAS
# Mencari jumlah fitur terbanyak di antara semua target untuk menentukan tinggi kanvas yang proporsional
max_features = max([len(fitur) for fitur in fitur_lolos_tahap1.values() if len(fitur) > 0], default=0)
if max_features > 0:
tinggi_fig = max(5, max_features * 0.4)
# 2. INISIALISASI KANVAS TUNGGAL (1 Baris, n_targets Kolom)
fig, axes = plt.subplots(nrows=1, ncols=n_targets, figsize=(8 * n_targets, tinggi_fig))
# Penyesuaian jika hanya ada 1 target agar iterable
if n_targets == 1:
axes = [axes]
for i, (target, fitur_list) in enumerate(fitur_lolos_tahap1.items()):
ax = axes[i]
if len(fitur_list) == 0:
ax.set_title(f"Target: {target}\n(Tidak ada fitur lolos)", fontsize=12, fontweight='bold')
ax.axis('off') # Sembunyikan garis axis jika kosong
continue
df_subset = df_fitur[df_fitur[target].notnull()].copy()
results = []
# 3. PERHITUNGAN FISHER'S EXACT (Diam tanpa print)
for col in fitur_list:
is_null = df_subset[col].isnull()
target_0 = df_subset[target] == 0
target_1 = df_subset[target] == 1
a = (~is_null & target_0).sum()
b = (~is_null & target_1).sum()
c = (is_null & target_0).sum()
d = (is_null & target_1).sum()
odds_ratio, p_value = fisher_exact([[a, b], [c, d]])
log_p = -np.log10(p_value + 1e-100)
results.append({
'Fitur': col,
'P-Value': p_value,
'Log_P': log_p,
'Signifikan': 'Ya' if p_value < 0.05 else 'Tidak'
})
df_results = pd.DataFrame(results).sort_values(by='Log_P', ascending=True)
# 4. PLOTTING KE DALAM SUBPLOT (ax)
colors = ['#2ecc71' if x == 'Ya' else '#95a5a6' for x in df_results['Signifikan']]
bars = ax.barh(df_results['Fitur'], df_results['Log_P'], color=colors)
# Garis batas signifikansi (p=0.05)
threshold_val = -np.log10(0.05)
ax.axvline(x=threshold_val, color='#e74c3c', linestyle='--', linewidth=2,
label='Threshold (p=0.05)')
ax.axvspan(0, threshold_val, color='gray', alpha=0.1)
# Teks dan Labeling
ax.set_title(f"Signifikansi Null thdp {target}", fontsize=13, fontweight='bold', pad=15)
ax.set_xlabel('-Log10(P-Value)', fontsize=11, fontweight='bold')
# Agar kanvas rapi, label Y 'Nama Fitur' hanya ditaruh di grafik paling kiri
if i == 0:
ax.set_ylabel('Nama Fitur (Null >= 20)', fontsize=11, fontweight='bold')
ax.legend(loc='lower right', fontsize=9)
# 5. PELEBARAN SUMBU X UNTUK TEKS (Anti-Terpotong)
# Karena kita akan menaruh teks p-value di ujung batang, kita harus memperlebar batas X-axis
max_x = df_results['Log_P'].max()
ax.set_xlim(0, max(max_x * 1.3, threshold_val * 1.5))
# Injeksi nilai p-value di ujung setiap batang
for bar, p_val in zip(bars, df_results['P-Value']):
width = bar.get_width()
teks_label = f" p = {p_val:.4f}" if p_val >= 0.0001 else " p < 0.0001"
# Menambahkan sedikit jarak padding (max_x * 0.02) agar teks tidak menempel keras di bar
ax.text(width + (max_x * 0.02), bar.get_y() + bar.get_height()/2, teks_label,
va='center', fontsize=9, color='black', fontweight='bold')
plt.tight_layout()
plt.show()
# 1. PERSIAPAN DICTIONARY PENYIMPANAN FINAL
fitur_lolos_tahap2_fisher = dict()
# Pastikan fitur_lolos_tahap1 sudah ada dari eksekusi sebelumnya
if 'fitur_lolos_tahap1' not in locals() or len(fitur_lolos_tahap1) == 0:
print("Variabel 'fitur_lolos_tahap1' tidak ditemukan. Jalankan filter Tahap 1 terlebih dahulu.")
else:
for target, fitur_list in fitur_lolos_tahap1.items():
print(f"\n{'='*70}")
print(f"TAHAP 2: EKSEKUSI FISHER'S EXACT TEST UNTUK '{target}'")
print(f"{'='*70}")
if len(fitur_list) == 0:
print(f"Tidak ada fitur yang dievaluasi untuk {target}.")
fitur_lolos_tahap2_fisher[target] = []
continue
df_subset = df_fitur[df_fitur[target].notnull()]
lolos_fisher = []
# Eksekusi uji statistik
for col in fitur_list:
is_null = df_subset[col].isnull()
target_0 = df_subset[target] == 0
target_1 = df_subset[target] == 1
a = (~is_null & target_0).sum()
b = (~is_null & target_1).sum()
c = (is_null & target_0).sum()
d = (is_null & target_1).sum()
odds_ratio, p_value = fisher_exact([[a, b], [c, d]])
# SIMPAN HANYA JIKA P-VALUE < 0.05
if p_value < 0.05:
lolos_fisher.append(col)
# Simpan list fitur yang lolos ke dalam dictionary final
fitur_lolos_tahap2_fisher[target] = lolos_fisher
print(f"Dari {len(fitur_list)} fitur yang masuk, {len(lolos_fisher)} fitur LOLOS (p < 0.05).")
print(f"Daftar tersimpan di: fitur_lolos_tahap2_fisher['{target}']")
======================================================================
TAHAP 2: EKSEKUSI FISHER'S EXACT TEST UNTUK 'target_distress_ppk'
======================================================================
Dari 10 fitur yang masuk, 8 fitur LOLOS (p < 0.05).
Daftar tersimpan di: fitur_lolos_tahap2_fisher['target_distress_ppk']
======================================================================
TAHAP 2: EKSEKUSI FISHER'S EXACT TEST UNTUK 'target_distress_neg_equity'
======================================================================
Dari 12 fitur yang masuk, 5 fitur LOLOS (p < 0.05).
Daftar tersimpan di: fitur_lolos_tahap2_fisher['target_distress_neg_equity']
======================================================================
TAHAP 2: EKSEKUSI FISHER'S EXACT TEST UNTUK 'target_distress_consecutive_loss'
======================================================================
Dari 10 fitur yang masuk, 7 fitur LOLOS (p < 0.05).
Daftar tersimpan di: fitur_lolos_tahap2_fisher['target_distress_consecutive_loss']
Kolom Terpilih untuk Missing Indicator
kolom_missing_indicator = fitur_lolos_tahap2_fisher.copy()
# cek hasil
for key_target in kolom_missing_indicator:
print(f"Key: {key_target}")
print('Kolom yang akan dibuatkan missing indicatornya:')
print(kolom_missing_indicator[key_target])
print()Key: target_distress_ppk
Kolom yang akan dibuatkan missing indicatornya:
['Parent Percent Owned (%)', 'Percent Owned - All Institutions (%)', 'Percent Owned - Insiders (%)', 'CR', 'Prepaid_CA', 'NetDebt_EBITDA', 'Intang_TA', 'GrossMargin']
Key: target_distress_neg_equity
Kolom yang akan dibuatkan missing indicatornya:
['Parent Percent Owned (%)', 'Percent Owned - All Institutions (%)', 'CR', 'NetDebt_EBITDA', 'Altman_X4_MVE_TL']
Key: target_distress_consecutive_loss
Kolom yang akan dibuatkan missing indicatornya:
['Parent Percent Owned (%)', 'Percent Owned - All Institutions (%)', 'CR', 'Inventory_CA', 'Prepaid_CA', 'NetDebt_EBITDA', 'Sales_growth']
# # kolom_missing_indicator harus sudah terdefinisi
# if 'kolom_missing_indicator' not in locals() or len(kolom_missing_indicator) == 0:
# print("Variabel 'kolom_missing_indicator' tidak ditemukan. Jalankan kode sebelumnya terlebih dahulu.")
# else:
# # TAHAP FINAL: PEMBUATAN KOLOM BOOLEAN DI DF_FITUR
# print(f"\n{'='*70}")
# print("TAHAP FINAL: INJEKSI KOLOM '_is_missing' KE DF_FITUR")
# print(f"{'='*70}")
# # Mengumpulkan SEMUA fitur unik yang lolos dari setidaknya satu target
# # Menggunakan 'set' untuk otomatis menghapus duplikat antar target
# fitur_global_final = set()
# for fitur_list in kolom_missing_indicator.values():
# fitur_global_final.update(fitur_list)
# fitur_global_final = list(fitur_global_final)
# if len(fitur_global_final) > 0:
# print(f"Ditemukan total {len(fitur_global_final)} fitur unik secara global.")
# print("Memulai pembuatan kolom missing indicator...\n")
# kolom_dibuat = 0
# for col in fitur_global_final:
# nama_kolom_baru = f"{col}_is_missing"
# # Pengecekan agar tidak membuat kolom ganda jika kode dijalankan 2x
# if nama_kolom_baru not in df_fitur.columns:
# # Membuat kolom biner (1 untuk null, 0 untuk ada isinya)
# df_fitur[nama_kolom_baru] = df_fitur[col].isnull().astype(int)
# print(f"[+] Dibuat: {nama_kolom_baru}")
# kolom_dibuat += 1
# else:
# print(f"[-] Terlewati: {nama_kolom_baru} (Kolom sudah ada)")
# print(f"\nSelesai! {kolom_dibuat} kolom baru berhasil ditambahkan ke df_fitur secara permanen.")
# else:
# print("Tidak ada satu pun fitur yang lolos uji Fisher dari seluruh target. Tidak ada kolom baru dibuat.")# df_fiturCross Tab Terhadap Target
Digunakan untuk menganalisis perbedaan jika suatu fitur null atau tidak null memang menghasilkan perubahan proporsi distress.
pd.crosstab(df_fitur['Parent Percent Owned (%)'].isnull(), df_fitur['target_distress_consecutive_loss'])| target_distress_consecutive_loss | 0 | 1 |
|---|---|---|
| Parent Percent Owned (%) | ||
| False | 2908 | 272 |
| True | 1687 | 293 |
pd.crosstab(df_fitur['Equity_TA'].isnull(), df_fitur['target_distress_consecutive_loss'])| target_distress_consecutive_loss | 0 | 1 |
|---|---|---|
| Equity_TA | ||
| False | 4595 | 565 |
# Cek hasil crosstab kalau ada yang tanpa null
ct = pd.crosstab(df_fitur['Equity_TA'].isnull(), df_fitur['target_distress_consecutive_loss'])
ct = ct.reindex([False, True], fill_value=0) # TAMBAHAN AGAR ketika suatu col null semua, maka tetap terbuat 2 index (not null dan null)
ct.index = ['Not Null', 'Null']
ct| target_distress_consecutive_loss | 0 | 1 |
|---|---|---|
| Not Null | 4595 | 565 |
| Null | 0 | 0 |
TODO: buat visualisasi dengan mosaic plot
# Tempat menyimpan hasil summary (untuk EDA/Analisis)
null_ct_summaries = dict()
# Pastikan fitur_lolos_tahap2_fisher sudah terdefinisi dari tahap sebelumnya
if 'fitur_lolos_tahap2_fisher' not in locals() or len(fitur_lolos_tahap2_fisher) == 0:
print("Dictionary fitur_lolos_tahap2_fisher belum terdefinisi. Silakan jalankan tahap eksekusi Fisher's Exact Test sebelumnya.")
else:
# Loop per target berdasarkan key dan list fitur yang ada di dictionary
for target, fitur_terpilih in fitur_lolos_tahap2_fisher.items():
print(f"\n{'='*60}")
print(f"ANALISIS PROPORSI DISTRESS {target} TERHADAP KETERISIAN DATA")
print(f"{'='*60}")
if len(fitur_terpilih) == 0:
print(f"Tidak ada fitur missing indicator yang lolos uji Fisher untuk {target}. Melewati visualisasi.")
continue
# Filter baris yang targetnya tidak null
df_check = df_fitur[df_fitur[target].notna()].copy()
summary_list = []
# Menyesuaikan jumlah fitur dengan list fitur yang lolos per target
num_features = len(fitur_terpilih)
rows = (num_features // 4) + (1 if num_features % 4 != 0 else 0)
if rows == 0:
continue
fig, axes = plt.subplots(rows, 4, figsize=(20, 5 * rows))
# Penanganan dimensi axes
if num_features == 1:
axes = [axes]
else:
axes = axes.flatten()
# Iterasi hanya pada fitur yang lolos untuk target saat ini
for i, col in enumerate(fitur_terpilih):
# Hitung jumlah Null vs Not Null
is_null = df_check[col].isnull()
n_null = int(is_null.sum())
n_notnull = int((~is_null).sum())
total = n_null + n_notnull
prop_null = (n_null / total) if total > 0 else 0
# Hitung Crosstab proporsi
ct = pd.crosstab(is_null, df_check[target], normalize='index')
# Reindex agar matriks tetap 2x2
ct = ct.reindex(index=[False, True], fill_value=0)
ct = ct.reindex(columns=[0, 1], fill_value=0)
ct.index = ['Not Null', 'Null']
# Delta = P(1|Null) - P(1|Not Null)
delta = ct.loc['Null', 1] - ct.loc['Not Null', 1]
summary_list.append({
'Fitur': col,
'Total_Null': n_null,
'Prop_Null (%)': prop_null * 100,
'Delta_Prob_Target_1 (%)': delta * 100
})
# Plotting
ax = axes[i]
ct.plot(kind='bar', stacked=True, ax=ax, color=['#3498db', '#e74c3c'], legend=True)
ax.set_title(f"{col}\nNull: {prop_null:.2%}\nDelta Target 1: {delta:+.1%}",
fontsize=10, fontweight='bold')
ax.set_xticklabels([f'Ada\n(n={n_notnull})', f'Null\n(n={n_null})'], rotation=0)
ax.set_xlabel('')
# Label persen pada bagian merah (Target=1)
if len(ax.containers) > 1:
for bar in ax.containers[1]:
height = bar.get_height()
if height > 0.05:
ax.text(bar.get_x() + bar.get_width()/2, bar.get_y() + height/2,
f'{height:.1%}', ha='center', va='center',
color='white', fontweight='bold', fontsize=9)
# Hapus axes sisa
for j in range(i + 1, len(axes)):
fig.delaxes(axes[j])
plt.tight_layout()
plt.show()
# =========================
# RINGKASAN SELURUH FITUR
# =========================
df_summary = pd.DataFrame(summary_list)
# Urutkan berdasarkan besarnya pengaruh (Absolute Delta)
df_summary = df_summary.sort_values(by='Delta_Prob_Target_1 (%)', key=abs, ascending=False)
null_ct_summaries[target] = df_summary
print(f"\nRingkasan Pengaruh Seluruh Missing Indicator yang Lolos terhadap {target}:")
print(df_summary.to_string(index=False))
print("\n" + "-"*60)
============================================================
ANALISIS PROPORSI DISTRESS target_distress_ppk TERHADAP KETERISIAN DATA
============================================================

Ringkasan Pengaruh Seluruh Missing Indicator yang Lolos terhadap target_distress_ppk:
Fitur Total_Null Prop_Null (%) Delta_Prob_Target_1 (%)
GrossMargin 52 2.013163 -16.224964
Percent Owned - All Institutions (%) 1280 49.554781 12.741330
CR 207 8.013937 -10.435881
Parent Percent Owned (%) 1137 44.018583 8.720262
Prepaid_CA 447 17.305459 6.964030
NetDebt_EBITDA 300 11.614402 -6.876478
Percent Owned - Insiders (%) 697 26.984127 6.559167
Intang_TA 1579 61.130468 4.926210
------------------------------------------------------------
============================================================
ANALISIS PROPORSI DISTRESS target_distress_neg_equity TERHADAP KETERISIAN DATA
============================================================

Ringkasan Pengaruh Seluruh Missing Indicator yang Lolos terhadap target_distress_neg_equity:
Fitur Total_Null Prop_Null (%) Delta_Prob_Target_1 (%)
Altman_X4_MVE_TL 21 0.327971 17.292683
Percent Owned - All Institutions (%) 2773 43.307824 1.829616
Parent Percent Owned (%) 2638 41.199438 1.818553
CR 567 8.855224 -1.794161
NetDebt_EBITDA 923 14.415118 -1.230655
------------------------------------------------------------
============================================================
ANALISIS PROPORSI DISTRESS target_distress_consecutive_loss TERHADAP KETERISIAN DATA
============================================================

Ringkasan Pengaruh Seluruh Missing Indicator yang Lolos terhadap target_distress_consecutive_loss:
Fitur Total_Null Prop_Null (%) Delta_Prob_Target_1 (%)
Percent Owned - All Institutions (%) 2082 40.348837 8.698432
Sales_growth 722 13.992248 7.881754
CR 520 10.077519 -6.830239
Parent Percent Owned (%) 1980 38.372093 6.244521
NetDebt_EBITDA 798 15.465116 -5.392608
Inventory_CA 778 15.077519 -3.660980
Prepaid_CA 809 15.678295 3.139667
------------------------------------------------------------
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import pandas as pd
import numpy as np
import math
from statsmodels.graphics.mosaicplot import mosaic
# Tempat menyimpan ringkasan
null_ct_summaries = dict()
cols_per_row = 4
if 'fitur_lolos_tahap2_fisher' in locals() and len(fitur_lolos_tahap2_fisher) > 0:
# 1. LOOPING UTAMA BERDASARKAN TARGET
for target, fitur_terpilih in fitur_lolos_tahap2_fisher.items():
if len(fitur_terpilih) == 0:
continue
# Hitung kebutuhan baris hanya untuk target ini
target_rows = math.ceil(len(fitur_terpilih) / cols_per_row)
# 2. MEMBUAT KANVAS BARU UNTUK TIAP TARGET
# Tinggi disesuaikan dengan jumlah baris spesifik di target ini
fig = plt.figure(figsize=(20, 5.5 * target_rows))
# GridSpec tunggal untuk mengatur jarak antar baris di dalam gambar ini
gs = gridspec.GridSpec(target_rows, cols_per_row, hspace=0.35, wspace=0.3)
df_check = df_fitur[df_fitur[target].notna()].copy()
summary_list = []
# 3. PROSES PLOTTING MOSAIK
for i, col in enumerate(fitur_terpilih):
r = i // cols_per_row
c = i % cols_per_row
# Tambahkan kotak grafik pada posisi spesifik
ax = fig.add_subplot(gs[r, c])
is_null = df_check[col].isnull()
status_keterisian = np.where(is_null, 'Null', 'Ada')
status_target = df_check[target].map({0.0: '0', 1.0: '1'}).fillna('0')
n_null = int(is_null.sum())
n_notnull = int((~is_null).sum())
total = n_null + n_notnull
prop_null = (n_null / total) if total > 0 else 0
ct_stats = pd.crosstab(is_null, df_check[target], normalize='index')
ct_stats = ct_stats.reindex(index=[False, True], fill_value=0)
ct_stats = ct_stats.reindex(columns=[0, 1], fill_value=0)
delta = ct_stats.loc[True, 1] - ct_stats.loc[False, 1]
summary_list.append({
'Fitur': col,
'Total_Null': n_null,
'Prop_Null (%)': prop_null * 100,
'Delta_Prob_Target_1 (%)': delta * 100
})
ct_mosaic = pd.crosstab(status_keterisian, status_target)
data_dict = {}
for status in ['Ada', 'Null']:
for tgt in ['0', '1']:
try:
data_dict[(status, tgt)] = ct_mosaic.loc[status, tgt]
except KeyError:
data_dict[(status, tgt)] = 0
props = lambda key: {
'facecolor': '#FF8A80' if key[1] == '1' else '#A8E6CF',
'edgecolor': 'none'
}
def labelizer(key):
count = data_dict.get(key, 0)
status = key[0]
total_status = data_dict.get((status, '0'), 0) + data_dict.get((status, '1'), 0)
if total_status == 0 or count == 0:
return ""
pct = (count / total_status) * 100
return f"{pct:.1f}%" if pct > 3 else ""
# Gambar mosaik
mosaic(data_dict, ax=ax, properties=props, labelizer=labelizer, gap=0.025)
# --- PENYESUAIAN TEKS & FONT ---
# Judul Nama Fitur (Font 16)
ax.set_title(col, fontsize=16, fontweight='bold', pad=10)
# Angka Persentase di dalam Kotak Mosaik (Font 14)
for text in ax.texts:
text.set_fontsize(14)
text.set_fontweight('bold')
text.set_color('#111111')
# Label Sumbu Axis (Font 14)
ax.tick_params(axis='both', labelsize=14)
# INJEKSI JUDUL SEKSI TARGET (Hanya di plot pojok kiri atas tiap gambar)
if i == 0:
ax.text(
x=0.0, y=1.30,
s=f"Perubahan Proporsi Target {target} terhadap Missing Indicator",
transform=ax.transAxes,
fontsize=20,
fontweight='bold',
ha='left',
va='bottom',
color='#2c3e50'
)
# Simpan ringkasan ke dalam dictionary
df_summary = pd.DataFrame(summary_list)
if not df_summary.empty:
df_summary = df_summary.sort_values(by='Delta_Prob_Target_1 (%)', key=abs, ascending=False)
null_ct_summaries[target] = df_summary
# 4. KONTROL SPASI & RENDER GAMBAR
# top=0.88 sedikit lebih rendah dari sebelumnya (0.95) untuk menjamin
# teks judul raksasa (Font 20) tidak terpotong oleh batas atas gambar.
plt.subplots_adjust(left=0.05, right=0.95, top=0.88, bottom=0.05)
# Eksekusi plt.show() DI DALAM loop! Ini yang akan mencetak gambar baru per target
plt.show()


Crosstab Perubahan Proporsi Distress tidak digunakan untuk filtering karena sudah terevaluasi dengan Fisher’s Exact Test, hanya untuk kebutuhan visualisasi saja.
Phi Coef Visualization
if 'kolom_missing_indicator' not in locals() or len(kolom_missing_indicator) == 0:
print("Dictionary 'kolom_missing_indicator' kosong atau belum terdefinisi.")
else:
# Loop untuk setiap target
for target, fitur_terpilih in kolom_missing_indicator.items():
print(f"\n{'='*70}")
print(f"VISUALISASI PHI COEFFICIENT (EFFECT SIZE): '{target}'")
print(f"{'='*70}")
if len(fitur_terpilih) == 0:
print(f"Tidak ada fitur yang lolos untuk target {target}. Lewati visualisasi.")
continue
# Filter baris yang targetnya valid (tidak null)
df_subset = df_fitur[df_fitur[target].notnull()].copy()
# Ekstrak label target (1/0)
y_true = df_subset[target].astype(int)
results = []
# Hitung Phi untuk masing-masing fitur
for col in fitur_terpilih:
# Buat array biner: 1 jika Null, 0 jika ada datanya
is_missing_array = df_subset[col].isnull().astype(int)
# Hitung Phi Coefficient (MCC)
phi_score = matthews_corrcoef(y_true, is_missing_array)
results.append({
'Fitur': col,
'Phi_Coefficient': phi_score,
'Abs_Phi': abs(phi_score)
})
# ==========================================
# PERUBAHAN DI SINI: Urutkan berdasarkan Phi asli
# ascending=True agar di bar chart, nilai positif tertinggi ada di atas
# ==========================================
df_phi = pd.DataFrame(results).sort_values(by='Phi_Coefficient', ascending=True)
# ==========================================
# BAGIAN PLOTTING (VISUALISASI)
# ==========================================
tinggi_fig = max(4, len(fitur_terpilih) * 0.4)
plt.figure(figsize=(10, tinggi_fig))
colors = ['#ff9999' if val > 0 else '#87ceeb' for val in df_phi['Phi_Coefficient']]
bars = plt.barh(df_phi['Fitur'], df_phi['Phi_Coefficient'], color=colors)
# Tambahkan garis vertikal di titik 0
plt.axvline(x=0, color='black', linestyle='-', linewidth=1.5)
# Pengaturan label dan judul
plt.title(f"Arah & Kekuatan Hubungan (Phi Coefficient) terhadap {target}\n(Diurutkan dari Paling Negatif ke Paling Positif)",
fontsize=12, fontweight='bold', pad=15)
plt.xlabel('Phi Coefficient (\u03C6)', fontsize=11, fontweight='bold')
plt.ylabel('Missing Indicator', fontsize=11, fontweight='bold')
# Menambahkan nilai teks di ujung setiap batang
for bar, phi_val in zip(bars, df_phi['Phi_Coefficient']):
if phi_val > 0:
pos_x = bar.get_width() + 0.005
alignment = 'left'
else:
pos_x = bar.get_width() - 0.005
alignment = 'right'
plt.text(pos_x, bar.get_y() + bar.get_height()/2, f"{phi_val:+.3f}",
va='center', ha=alignment, fontsize=10, color='black')
# Limit sumbu X agar simetris
max_val = df_phi['Abs_Phi'].max()
plt.xlim(-(max_val + 0.05), (max_val + 0.05))
# Legenda Kustom
legend_elements = [
Patch(facecolor='#ff9999', label='Positif (+): Jika Null, Risiko Distress Naik'),
Patch(facecolor='#87ceeb', label='Negatif (-): Jika Null, Risiko Distress Turun (Aman)')
]
plt.legend(handles=legend_elements, loc='best', fontsize=9)
plt.tight_layout()
plt.show()
# Cetak Ringkasan Angka
print(f"Hasil Phi Coeff untuk {target}:")
df_top = df_phi.sort_values(by='Phi_Coefficient', ascending=False)
print(df_top[['Fitur', 'Phi_Coefficient']].to_string(index=False))
print("\n")
======================================================================
VISUALISASI PHI COEFFICIENT (EFFECT SIZE): 'target_distress_ppk'
======================================================================

Hasil Phi Coeff untuk target_distress_ppk:
Fitur Phi_Coefficient
Percent Owned - All Institutions (%) 0.160032
Parent Percent Owned (%) 0.108745
Percent Owned - Insiders (%) 0.073139
Prepaid_CA 0.066181
Intang_TA 0.060323
NetDebt_EBITDA -0.055347
GrossMargin -0.057246
CR -0.071179
======================================================================
VISUALISASI PHI COEFFICIENT (EFFECT SIZE): 'target_distress_neg_equity'
======================================================================

Hasil Phi Coeff untuk target_distress_neg_equity:
Fitur Phi_Coefficient
Altman_X4_MVE_TL 0.074131
Percent Owned - All Institutions (%) 0.067973
Parent Percent Owned (%) 0.067111
NetDebt_EBITDA -0.032410
CR -0.038217
======================================================================
VISUALISASI PHI COEFFICIENT (EFFECT SIZE): 'target_distress_consecutive_loss'
======================================================================

Hasil Phi Coeff untuk target_distress_consecutive_loss:
Fitur Phi_Coefficient
Percent Owned - All Institutions (%) 0.136662
Parent Percent Owned (%) 0.097247
Sales_growth 0.087562
Prepaid_CA 0.036558
Inventory_CA -0.041952
NetDebt_EBITDA -0.062442
CR -0.065846
if 'kolom_missing_indicator' in locals() and len(kolom_missing_indicator) > 0:
# 1. FILTER TARGET VALID & PRE-KALKULASI DIMENSI
valid_targets = {k: v for k, v in kolom_missing_indicator.items() if len(v) > 0}
n_targets = len(valid_targets)
if n_targets > 0:
# Cari jumlah fitur terbanyak untuk menyesuaikan tinggi figur
max_features = max([len(v) for v in valid_targets.values()], default=0)
tinggi_fig = max(5, max_features * 0.5)
# 2. INISIALISASI KANVAS YANG LEBIH PADAT (Lebar diubah dari 8 ke 5.5 per target)
fig, axes = plt.subplots(nrows=1, ncols=n_targets, figsize=(5.5 * n_targets, tinggi_fig))
if n_targets == 1:
axes = [axes]
# 3. PROSES PERHITUNGAN DAN PLOTTING
for i, (target_key, fitur_terpilih) in enumerate(valid_targets.items()):
ax = axes[i]
# Proteksi KeyError nama kolom target
nama_kolom_target = target_key
if target_key not in df_fitur.columns:
nama_kolom_target = f"target_distress_{target_key}"
if nama_kolom_target not in df_fitur.columns:
continue
df_subset = df_fitur[df_fitur[nama_kolom_target].notnull()].copy()
y_true = df_subset[nama_kolom_target].astype(int)
results = []
for col in fitur_terpilih:
is_missing_array = df_subset[col].isnull().astype(int)
phi_score = matthews_corrcoef(y_true, is_missing_array)
results.append({
'Fitur': col,
'Phi_Coefficient': phi_score,
'Abs_Phi': abs(phi_score)
})
df_phi = pd.DataFrame(results).sort_values(by='Phi_Coefficient', ascending=True)
colors = ['#ff9999' if val > 0 else '#87ceeb' for val in df_phi['Phi_Coefficient']]
bars = ax.barh(df_phi['Fitur'], df_phi['Phi_Coefficient'], color=colors)
ax.axvline(x=0, color='black', linestyle='-', linewidth=1.5)
# Judul dan Label
ax.set_title(f"Target:\n{target_key}", fontsize=14, fontweight='bold', pad=15)
ax.set_xlabel('Phi Coefficient (\u03C6)', fontsize=12, fontweight='bold')
if i == 0:
ax.set_ylabel('Missing Indicator', fontsize=12, fontweight='bold')
# Berikan ruang batas X sedikit lebih longgar (1.4) karena lebar plot sekarang lebih kecil
max_val = df_phi['Abs_Phi'].max()
ax.set_xlim(-(max_val * 1.4), (max_val * 1.4))
# Injeksi nilai teks di ujung setiap batang
for bar, phi_val in zip(bars, df_phi['Phi_Coefficient']):
padding = max_val * 0.04
if phi_val > 0:
pos_x = bar.get_width() + padding
alignment = 'left'
else:
pos_x = bar.get_width() - padding
alignment = 'right'
ax.text(pos_x, bar.get_y() + bar.get_height()/2, f"{phi_val:+.3f}",
va='center', ha=alignment, fontsize=11, color='black', fontweight='bold')
# 4. LEGENDA GLOBAL & JUDUL UTAMA
legend_elements = [
Patch(facecolor='#ff9999', label='Positif (+): Jika Null, Risiko Distress Naik'),
Patch(facecolor='#87ceeb', label='Negatif (-): Jika Null, Risiko Distress Turun (Aman)')
]
fig.legend(handles=legend_elements, loc='lower center', bbox_to_anchor=(0.5, 0.02), ncol=2, fontsize=12)
fig.suptitle('Arah & Kekuatan Hubungan (Phi Coefficient) terhadap Seluruh Target',
fontsize=18, fontweight='bold', y=0.98)
# 5. MERAPIKAN TATA LETAK (w_pad dirapatkan dari 3.0 ke 1.5)
plt.tight_layout(rect=[0, 0.08, 1, 0.92], w_pad=1.5)
plt.show()
kolom_missing_indicator{'target_distress_ppk': ['Parent Percent Owned (%)',
'Percent Owned - All Institutions (%)',
'Percent Owned - Insiders (%)',
'CR',
'Prepaid_CA',
'NetDebt_EBITDA',
'Intang_TA',
'GrossMargin'],
'target_distress_neg_equity': ['Parent Percent Owned (%)',
'Percent Owned - All Institutions (%)',
'CR',
'NetDebt_EBITDA',
'Altman_X4_MVE_TL'],
'target_distress_consecutive_loss': ['Parent Percent Owned (%)',
'Percent Owned - All Institutions (%)',
'CR',
'Inventory_CA',
'Prepaid_CA',
'NetDebt_EBITDA',
'Sales_growth']}
Eksplorasi Data Lainnya
# 1. Menghitung jumlah nilai unik dari Parent Percent Owned (%) untuk setiap ticker
cek_konsistensi = df_fitur.groupby('ticker')['Parent Percent Owned (%)'].nunique()
# 2. Mendapatkan daftar ticker yang memiliki lebih dari 1 nilai unik
ticker_berubah = cek_konsistensi[cek_konsistensi > 1].index.tolist()
# 3. Evaluasi dan Tampilkan Hasil
if len(ticker_berubah) > 0:
print(f"Ditemukan {len(ticker_berubah)} perusahaan yang persentase kepemilikan induknya berubah.\n")
# Memfilter dataframe asli untuk melihat detail perubahan dari tahun ke tahun
df_detail_perubahan = df_fitur[df_fitur['ticker'].isin(ticker_berubah)][
['ticker', 'Year', 'Parent Percent Owned (%)']
].sort_values(by=['ticker', 'Year'])
# Menampilkan hasilnya
display(df_detail_perubahan)
else:
print("Persentase kepemilikan induk semua perusahaan konsisten (tidak berubah) sepanjang tahun.")Persentase kepemilikan induk semua perusahaan konsisten (tidak berubah) sepanjang tahun.
Sebaran Tahun Fiskal
targets = [col for col in df_fitur.columns if col.startswith('target')]
for target in targets:
print('\nSebaran Baris Data pada Tiap Tahun Fiskal untuk Target', target, ':')
print(df_fitur[df_fitur[target].notna()]['Year'].value_counts().sort_index())
Sebaran Baris Data pada Tiap Tahun Fiskal untuk Target target_distress_ppk :
Year
2020 611
2021 654
2022 667
2023 651
Name: count, dtype: Int64
Sebaran Baris Data pada Tiap Tahun Fiskal untuk Target target_distress_neg_equity :
Year
2008 291
2009 302
2010 310
2011 328
2012 347
2013 370
2014 395
2015 417
2016 432
2017 440
2018 469
2019 514
2020 557
2021 596
2022 635
Name: count, dtype: Int64
Sebaran Baris Data pada Tiap Tahun Fiskal untuk Target target_distress_consecutive_loss :
Year
2008 302
2009 287
2010 289
2011 303
2012 315
2013 331
2014 344
2015 346
2016 340
2017 336
2018 354
2019 386
2020 401
2021 399
2022 427
Name: count, dtype: Int64
# --- 1. Persiapan Target ---
targets = [col for col in df_fitur.columns if col.startswith('target')]
# --- 2. Setup Canvas (1 Baris, 3 Kolom) ---
# figsize=(18, 6) memberikan rasio yang pas untuk 3 chart berdampingan
fig, axes = plt.subplots(1, len(targets), figsize=(18, 6))
# Handle jika target hanya 1 (bungkus jadi list agar loop aman)
if len(targets) == 1:
axes = [axes]
# --- 3. Loop Visualisasi ---
for i, target in enumerate(targets):
ax = axes[i]
# A. Olah Data
# Filter notna -> ambil kolom Year -> hitung -> urutkan tahun
data_counts = df_fitur[df_fitur[target].notna()]['Year'].value_counts().sort_index()
# Ubah index tahun menjadi string agar jarak antar bar rapi
x = data_counts.index.astype(str)
y = data_counts.values
# B. Plot Bar (Volume)
bars = ax.bar(x, y, color='#3498db', alpha=0.6, label='Jumlah Data', zorder=2)
# C. Plot Line (Tren)
ax.plot(x, y, color='#e74c3c', marker='o', linewidth=2, markersize=6, label='Tren', zorder=3)
# D. Anotasi Angka (Label)
# Menambahkan angka di atas bar/titik
for j, val in enumerate(y):
ax.text(j, val + (val * 0.02), # Posisi sedikit di atas nilai asli
f'{val}',
ha='center', va='bottom', fontsize=10, fontweight='normal') # Font tidak bold
# E. Kosmetik & Layout
target_name = ""
if target == 'target_distress_ppk':
target_name = 'Papan Pemantauan Khusus'
elif target == 'target_distress_neg_equity':
target_name = 'Ekuitas Negatif'
elif target == 'target_distress_consecutive_loss':
target_name = 'Rugi Bersih 2 Tahun Berturut-turut'
else:
target_name = target
ax.set_title(f'Sebaran Data Point pada Tahun Fiskal untuk Target\nDistress {target_name}', fontsize=12, fontweight='bold')
ax.set_xlabel('Tahun Fiskal')
ax.set_ylabel('Jumlah Baris Data')
# Grid di belakang
ax.grid(axis='y', linestyle='--', alpha=0.5, zorder=0)
# [PERBAIKAN 1] Rotasi Label Sumbu X Vertikal (90 derajat)
ax.tick_params(axis='x', rotation=90)
# [PERBAIKAN 2] Menambah Headroom Sumbu Y
# Kita ambil nilai maksimum y, lalu tambah 15% agar angka tidak mentok atas
y_max = max(y)
ax.set_ylim(0, y_max * 1.15)
# Agar layout rapi dan judul tidak saling tabrak
plt.tight_layout()
plt.show()
# --- 1. Persiapan Target ---
targets = [col for col in df_fitur.columns if col.startswith('target')]
# --- 2. Setup Canvas ---
fig, axes = plt.subplots(1, len(targets), figsize=(18, 7))
if len(targets) == 1:
axes = [axes]
# --- 3. Loop Visualisasi ---
for i, target in enumerate(targets):
ax = axes[i]
# A. Mapping Nama Target
target_name = ""
if target == 'target_distress_ppk':
target_name = 'Papan Pemantauan Khusus'
elif target == 'target_distress_neg_equity':
target_name = 'Ekuitas Negatif'
elif target == 'target_distress_consecutive_loss':
target_name = 'Rugi Bersih 2 Tahun Berturut-turut'
else:
target_name = target
# B. Olah Data (Kumulatif)
counts = df_fitur[df_fitur[target].notna()]['Year'].value_counts().sort_index()
cumsum_values = counts.cumsum()
total_data = counts.sum()
cumsum_pct = (cumsum_values / total_data) * 100
x = counts.index.astype(str)
y = cumsum_values.values
pcts = cumsum_pct.values
# C. Logika Pewarnaan (Train vs Test)
# Jika persentase kumulatif <= 80%, anggap sebagai Train (Kuning)
# Sisanya adalah Test (Biru)
colors = []
for pct in pcts:
if pct <= 80.0:
colors.append('#f1c40f') # Kuning Emas (Train)
else:
colors.append('#3498db') # Biru (Test)
# D. Plot Bar dengan List Warna
bars = ax.bar(x, y, color=colors, alpha=0.85, edgecolor='black', linewidth=0.5)
# E. Anotasi Angka
for j, (val, pct) in enumerate(zip(y, pcts)):
label_text = f"{val}\n({pct:.1f}%)"
ax.text(j, val + (total_data * 0.02),
label_text,
ha='center', va='bottom', fontsize=9, fontweight='normal', color='black')
# F. Garis Ambang Batas
limit_70 = total_data * 0.70
limit_80 = total_data * 0.80
ax.axhline(limit_70, color='#e67e22', linestyle='--', linewidth=1.5, alpha=0.7) # Garis 70%
ax.axhline(limit_80, color='#c0392b', linestyle='--', linewidth=2) # Garis 80% (Batas Split)
# G. Legenda Manual (Kuning vs Biru)
legend_elements = [
Patch(facecolor='#f1c40f', edgecolor='black', label='Dataset Train'),
Patch(facecolor='#3498db', edgecolor='black', label='Dataset Test'),
plt.Line2D([0], [0], color='#e67e22', linestyle='--', label='Threshold 70%'),
plt.Line2D([0], [0], color='#c0392b', linestyle='--', label='Threshold 80%')
]
ax.legend(handles=legend_elements, loc='upper left', fontsize='small')
# H. Kosmetik
ax.set_title(f'Simulasi Time-Based Split (Train vs Test)\nTarget: {target_name}', fontsize=12, fontweight='bold')
ax.set_xlabel('Tahun Fiskal')
ax.set_ylabel('Total Data Point Terakumulasi')
ax.grid(axis='y', linestyle=':', alpha=0.4)
ax.tick_params(axis='x', rotation=90)
ax.set_ylim(0, total_data * 1.35) # Headroom
plt.tight_layout()
plt.show()
Sebaran Label Target (Terprediksi Akan Distress)
targets = [col for col in df_fitur.columns if col.startswith('target')]
for target in targets:
distress_count = (df_fitur[target]==1).sum()
all_count_per_indicator = df_fitur[target].notna().sum()
prop = round(distress_count/all_count_per_indicator*100, 2)
print('\nSebaran Target Positif pada Tiap Tahun Fiskal', target, ':')
print(df_fitur[(df_fitur[target].notna()) & (df_fitur[target]==1)]['Year'].value_counts().sort_index())
print('Total ada',distress_count,'baris data yang dilabeli distress dari',all_count_per_indicator,'total data (',prop,'% )')
Sebaran Target Positif pada Tiap Tahun Fiskal target_distress_ppk :
Year
2020 44
2021 92
2022 168
2023 206
Name: count, dtype: Int64
Total ada 510 baris data yang dilabeli distress dari 2583 total data ( 19.74 % )
Sebaran Target Positif pada Tiap Tahun Fiskal target_distress_neg_equity :
Year
2008 4
2009 3
2010 4
2011 5
2012 3
2013 2
2014 5
2015 11
2016 10
2017 9
2018 13
2019 13
2020 14
2021 9
2022 11
Name: count, dtype: Int64
Total ada 116 baris data yang dilabeli distress dari 6403 total data ( 1.81 % )
Sebaran Target Positif pada Tiap Tahun Fiskal target_distress_consecutive_loss :
Year
2008 36
2009 13
2010 16
2011 19
2012 21
2013 33
2014 43
2015 42
2016 32
2017 29
2018 49
2019 74
2020 63
2021 42
2022 53
Name: count, dtype: Int64
Total ada 565 baris data yang dilabeli distress dari 5160 total data ( 10.95 % )
# --- 1. Persiapan Target ---
targets = [col for col in df_fitur.columns if col.startswith('target')]
# --- 2. Setup Canvas (1 Baris, 3 Kolom) ---
# figsize=(18, 6) memberikan rasio yang pas untuk 3 chart berdampingan
fig, axes = plt.subplots(1, len(targets), figsize=(18, 6))
# Handle jika target hanya 1 (bungkus jadi list agar loop aman)
if len(targets) == 1:
axes = [axes]
# --- 3. Loop Visualisasi ---
for i, target in enumerate(targets):
ax = axes[i]
# A. Olah Data
# Filter notna -> ambil kolom Year -> hitung -> urutkan tahun
data_counts = df_fitur[(df_fitur[target].notna()) & (df_fitur[target]==1)]['Year'].value_counts().sort_index()
# Ubah index tahun menjadi string agar jarak antar bar rapi
x = data_counts.index.astype(str)
y = data_counts.values
# B. Plot Bar (Volume)
bars = ax.bar(x, y, color='#3498db', alpha=0.6, label='Jumlah Label Distress', zorder=2)
# C. Plot Line (Tren)
ax.plot(x, y, color='#e74c3c', marker='o', linewidth=2, markersize=6, label='Tren', zorder=3)
# D. Anotasi Angka (Label)
# Menambahkan angka di atas bar/titik
for j, val in enumerate(y):
ax.text(j, val + (val * 0.02), # Posisi sedikit di atas nilai asli
f'{val}',
ha='center', va='bottom', fontsize=10, fontweight='normal') # Font tidak bold
# E. Kosmetik & Layout
target_name = ""
if target == 'target_distress_ppk':
target_name = 'Papan Pemantauan Khusus'
elif target == 'target_distress_neg_equity':
target_name = 'Ekuitas Negatif'
elif target == 'target_distress_consecutive_loss':
target_name = 'Rugi Bersih 2 Tahun Berturut-turut'
else:
target_name = target
ax.set_title(f'Sebaran Label Distress (Target = 1)\nTarget Distress : {target_name}', fontsize=12, fontweight='bold')
ax.set_xlabel('Tahun Fiskal')
ax.set_ylabel('Jumlah Baris dengan Label Distress')
# Grid di belakang
ax.grid(axis='y', linestyle='--', alpha=0.5, zorder=0)
# [PERBAIKAN 1] Rotasi Label Sumbu X Vertikal (90 derajat)
ax.tick_params(axis='x', rotation=90)
# [PERBAIKAN 2] Menambah Headroom Sumbu Y
# Kita ambil nilai maksimum y, lalu tambah 15% agar angka tidak mentok atas
y_max = max(y)
ax.set_ylim(0, y_max * 1.15)
# Agar layout rapi dan judul tidak saling tabrak
plt.tight_layout()
plt.show()
# --- 1. Persiapan Target ---
targets = [col for col in df_fitur.columns if col.startswith('target')]
# --- 2. Setup Canvas ---
fig, axes = plt.subplots(1, len(targets), figsize=(18, 7))
# Handle jika target hanya 1
if len(targets) == 1:
axes = [axes]
# --- 3. Loop Visualisasi ---
for i, target in enumerate(targets):
ax = axes[i]
# A. Mapping Nama Target
target_name = ""
if target == 'target_distress_ppk':
target_name = 'Papan Pemantauan Khusus'
elif target == 'target_distress_neg_equity':
target_name = 'Ekuitas Negatif'
elif target == 'target_distress_consecutive_loss':
target_name = 'Rugi Bersih 2 Tahun Berturut-turut'
else:
target_name = target
# B. Olah Data (FILTER HANYA TARGET == 1)
# Filter notna DAN target==1 -> ambil kolom Year -> hitung -> urutkan tahun
counts = df_fitur[(df_fitur[target].notna()) & (df_fitur[target] == 1)]['Year'].value_counts().sort_index()
# Hitung Kumulatif
cumsum_values = counts.cumsum()
total_distress = counts.sum() # Total kasus distress
cumsum_pct = (cumsum_values / total_distress) * 100
x = counts.index.astype(str)
y = cumsum_values.values
pcts = cumsum_pct.values
# C. Logika Pewarnaan (Train vs Test berdasarkan akumulasi Distress)
colors = []
for pct in pcts:
if pct <= 80.0:
colors.append('#f1c40f') # Kuning Emas (Train)
else:
colors.append('#3498db') # Biru (Test)
# D. Plot Bar
bars = ax.bar(x, y, color=colors, alpha=0.85, edgecolor='black', linewidth=0.5)
# E. Anotasi Angka
for j, (val, pct) in enumerate(zip(y, pcts)):
label_text = f"{val}\n({pct:.1f}%)"
ax.text(j, val + (total_distress * 0.02),
label_text,
ha='center', va='bottom', fontsize=9, fontweight='normal', color='black')
# F. Garis Ambang Batas (Berdasarkan Total Distress)
limit_70 = total_distress * 0.70
limit_80 = total_distress * 0.80
ax.axhline(limit_70, color='#e67e22', linestyle='--', linewidth=1.5, alpha=0.7)
ax.axhline(limit_80, color='#c0392b', linestyle='--', linewidth=2)
# G. Legenda Manual
legend_elements = [
Patch(facecolor='#f1c40f', edgecolor='black', label='Train'),
Patch(facecolor='#3498db', edgecolor='black', label='Test'),
plt.Line2D([0], [0], color='#e67e22', linestyle='--', label='Threshold 70%'),
plt.Line2D([0], [0], color='#c0392b', linestyle='--', label='Threshold 80%')
]
ax.legend(handles=legend_elements, loc='upper left', fontsize='small')
# H. Kosmetik
ax.set_title(f'Akumulasi Label Distress (Target=1)\nTarget: {target_name}', fontsize=12, fontweight='bold')
ax.set_xlabel('Tahun Fiskal')
ax.set_ylabel('Total Kumulatif Label Distress')
ax.grid(axis='y', linestyle=':', alpha=0.4)
ax.tick_params(axis='x', rotation=90)
# Headroom
ax.set_ylim(0, total_distress * 1.35)
plt.tight_layout()
plt.show()
Semuanya selaras dengan proporsi split train test berdasarkan jumlah kumulatif baris data per tahun fiskal pada bagian sebelumnya. Untuk itu, split dataset berlabel distress papan pemantauan khusus dilakukan pada tahun 2022, sedangkan untuk label distress ekuitas negatif dan rugi 2 tahun berturut-turut pada tahun 2019.
Untuk label target papan pemantauan khusus, perbedaan proporsi sebaran label antara train dan test cukup signifikan dari threshold : - Ada 74.6% baris data yang masuk train untuk label papan pemantauan khusus. - Namun, hanya 58.7% saja baris berlabel distress yang masuk ke train.
Tapi tidak masalah karena split di tahun tersebut sudah yang paling mendekati.
DatPrep : Lanjutan FeatEng
Hapus Kolom Industri
Kolom Industri dipertahankan karena untuk keperluan imputasi di dalam tiap fold pada saat cross validation.
# df_fitur = df_fitur.drop(columns=['Industry Group'])
# df_fitur.head(3)Ubah Penamaan Key Dict Missing Indicator
# Membuat key baru sambil ambil & hapus isi dari key lama
kolom_missing_indicator['ppk'] = kolom_missing_indicator.pop('target_distress_ppk')
kolom_missing_indicator['negeq'] = kolom_missing_indicator.pop('target_distress_neg_equity')
kolom_missing_indicator['conloss'] = kolom_missing_indicator.pop('target_distress_consecutive_loss')
# cek hasil
for key_target in kolom_missing_indicator:
print(f"Key: {key_target}")
print('Kolom yang akan dibuatkan missing indicatornya:')
print(kolom_missing_indicator[key_target])
print()Key: ppk
Kolom yang akan dibuatkan missing indicatornya:
['Parent Percent Owned (%)', 'Percent Owned - All Institutions (%)', 'Percent Owned - Insiders (%)', 'CR', 'Prepaid_CA', 'NetDebt_EBITDA', 'Intang_TA', 'GrossMargin']
Key: negeq
Kolom yang akan dibuatkan missing indicatornya:
['Parent Percent Owned (%)', 'Percent Owned - All Institutions (%)', 'CR', 'NetDebt_EBITDA', 'Altman_X4_MVE_TL']
Key: conloss
Kolom yang akan dibuatkan missing indicatornya:
['Parent Percent Owned (%)', 'Percent Owned - All Institutions (%)', 'CR', 'Inventory_CA', 'Prepaid_CA', 'NetDebt_EBITDA', 'Sales_growth']
Split Jenis Target Financial Distress
cols_distress = [col for col in df_fitur.columns if col.startswith('target') or col.startswith('tahun')]
cols_distress['target_distress_ppk',
'tahun_distress_ppk',
'target_distress_neg_equity',
'tahun_distress_neg_equity',
'target_distress_consecutive_loss',
'tahun_distress_consecutive_loss']
[col for col in cols_distress if not col.endswith('ppk')] # tes aja['target_distress_neg_equity',
'tahun_distress_neg_equity',
'target_distress_consecutive_loss',
'tahun_distress_consecutive_loss']
Pisahkan dataset untuk masing-masing jenis indikator distress dengan melakukan :
- Pilih kolom label target yang bersesuaian, pilih yang tidak null - Drop kolom-kolom dari indikator lain - Rename kolom target dan tahun distress agar selaras semua
# Papan Pemantauan Khusus
df_fitur_ppk = (df_fitur[df_fitur['target_distress_ppk'].notna()]
.drop([col for col in cols_distress if not col.endswith('ppk')], axis=1)
.rename(columns=lambda x: x.removesuffix('_ppk'))
).reset_index(drop=True)
# Ekuitas Negatif
df_fitur_negeq = (df_fitur[df_fitur['target_distress_neg_equity'].notna()]
.drop([col for col in cols_distress if not col.endswith('neg_equity')], axis=1)
.rename(columns=lambda x: x.removesuffix('_neg_equity'))
).reset_index(drop=True)
# Rugi 2 Tahun Berturut-turut
df_fitur_conloss = (df_fitur[df_fitur['target_distress_consecutive_loss'].notna()]
.drop([col for col in cols_distress if not col.endswith('consecutive_loss')], axis=1)
.rename(columns=lambda x: x.removesuffix('_consecutive_loss'))
).reset_index(drop=True)print('=== Dataset Fitur dengan Label Distress Papan Pemantauan Khusus yang berukuran',df_fitur_ppk.shape,'===')
df_fitur_ppk.head(3)=== Dataset Fitur dengan Label Distress Papan Pemantauan Khusus yang berukuran (2583, 75) ===
| ticker | Year | Entity Name | target_distress | tahun_distress | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | CR | QR | Age_When_IPO | Years_Since_IPO | WC_Sales | CA_TA | CL_TA | CashST_TA | CashST_CL | Inventory_CA | Prepaid_CA | TL_TA | Debt_TA | Debt_Equity | Equity_TA | NetDebt_EBITDA | PPE_TA | Intang_TA | Altman_X1_WC_TA | Altman_X3_EBIT_TA | Altman_X4_MVE_TL | Altman_X5_SalesTA_AssetTurnover | ROA | ROE | EBITDA_TA | GrossMargin | OpMargin | NetMargin | CFO_TA | CFO_TL | CFO_Sales | NetCash_TA | EPS_proxy | Sales_per_share | CFO_per_share | log_TA | log_Sales | log_MktCap | PB | Sales_growth | NI_growth | TA_growth | Equity_growth | CFO_growth | industry_Banks | industry_Capital Goods | industry_Commercial and Professional Services | industry_Consumer Discretionary Distribution and Retail | industry_Consumer Durables and Apparel | industry_Consumer Services | industry_Consumer Staples Distribution and Retail | industry_Energy | industry_Financial Services | industry_Food Beverage and Tobacco | industry_Health Care Equipment and Services | industry_Household and Personal Products | industry_Insurance | industry_Materials | industry_Media and Entertainment | industry_Pharmaceuticals Biotechnology and Life Sciences | industry_Real Estate Management and Development | industry_Software and Services | industry_Technology Hardware and Equipment | industry_Telecommunication Services | industry_Transportation | industry_Utilities | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | AALI | 2020 | PT Astra Agro Lestari Tbk (IDX:AALI) | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 3.313000 | 1.322000 | 9 | 23 | 0.220417 | 0.213737 | 0.064522 | 0.035236 | 0.546102 | 0.421875 | NaN | 0.307166 | 0.210873 | 0.304362 | 0.692834 | 1.609740 | 0.638614 | NaN | 0.149215 | 0.061495 | 2.779863 | 0.676969 | 0.032172 | 0.046435 | 0.109109 | 0.159371 | 0.090839 | 0.047524 | 0.083588 | 0.272125 | 0.123473 | 0.021436 | 464.375964 | 9771.474517 | 1206.514302 | 17.139871 | 16.749742 | 16.981904 | 1.232442 | 0.077599 | 2.668607 | 0.029922 | 0.014188 | 0.796850 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 1 | AALI | 2021 | PT Astra Agro Lestari Tbk (IDX:AALI) | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1.579000 | 0.752000 | 9 | 24 | 0.142003 | 0.309679 | 0.196066 | 0.128159 | 0.653652 | 0.359192 | NaN | 0.303578 | 0.191385 | 0.274811 | 0.696422 | 0.445443 | 0.584911 | NaN | 0.113613 | 0.100867 | 1.981262 | 0.800070 | 0.068006 | 0.097650 | 0.141938 | 0.200637 | 0.126072 | 0.085000 | 0.161024 | 0.530422 | 0.201263 | 0.095959 | 1074.128192 | 12636.876102 | 2543.330739 | 17.229950 | 17.006894 | 16.721566 | 0.863653 | 0.293241 | 1.313057 | 0.094261 | 0.099927 | 1.107999 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 2 | AALI | 2022 | PT Astra Agro Lestari Tbk (IDX:AALI) | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 3.600000 | 1.227000 | 9 | 25 | 0.244527 | 0.252676 | 0.070188 | 0.055373 | 0.788926 | 0.464748 | NaN | 0.239531 | 0.138593 | 0.182247 | 0.760469 | 0.656024 | 0.615273 | NaN | 0.182489 | 0.083667 | 2.204591 | 0.746293 | 0.061268 | 0.080566 | 0.126856 | 0.167314 | 0.112109 | 0.082096 | 0.062750 | 0.261971 | 0.084082 | -0.077828 | 931.085812 | 11341.364015 | 953.607381 | 17.191368 | 16.898731 | 16.552836 | 0.694397 | -0.102518 | -0.133171 | -0.037848 | 0.050637 | -0.625056 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
print('=== Dataset Fitur dengan Label Distress Ekuitas Negatif yang berukuran',df_fitur_negeq.shape,'===')
df_fitur_negeq.head(3)=== Dataset Fitur dengan Label Distress Ekuitas Negatif yang berukuran (6403, 75) ===
| ticker | Year | Entity Name | target_distress | tahun_distress | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | CR | QR | Age_When_IPO | Years_Since_IPO | WC_Sales | CA_TA | CL_TA | CashST_TA | CashST_CL | Inventory_CA | Prepaid_CA | TL_TA | Debt_TA | Debt_Equity | Equity_TA | NetDebt_EBITDA | PPE_TA | Intang_TA | Altman_X1_WC_TA | Altman_X3_EBIT_TA | Altman_X4_MVE_TL | Altman_X5_SalesTA_AssetTurnover | ROA | ROE | EBITDA_TA | GrossMargin | OpMargin | NetMargin | CFO_TA | CFO_TL | CFO_Sales | NetCash_TA | EPS_proxy | Sales_per_share | CFO_per_share | log_TA | log_Sales | log_MktCap | PB | Sales_growth | NI_growth | TA_growth | Equity_growth | CFO_growth | industry_Banks | industry_Capital Goods | industry_Commercial and Professional Services | industry_Consumer Discretionary Distribution and Retail | industry_Consumer Durables and Apparel | industry_Consumer Services | industry_Consumer Staples Distribution and Retail | industry_Energy | industry_Financial Services | industry_Food Beverage and Tobacco | industry_Health Care Equipment and Services | industry_Household and Personal Products | industry_Insurance | industry_Materials | industry_Media and Entertainment | industry_Pharmaceuticals Biotechnology and Life Sciences | industry_Real Estate Management and Development | industry_Software and Services | industry_Technology Hardware and Equipment | industry_Telecommunication Services | industry_Transportation | industry_Utilities | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | AALI | 2008 | PT Astra Agro Lestari Tbk (IDX:AALI) | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1.944000 | 0.878000 | 9 | 11 | 0.117567 | 0.303024 | 0.155859 | 0.133083 | 0.853871 | 0.395495 | 0.018945 | 0.181481 | 0.000000 | 0.000000 | 0.818519 | -0.239907 | 0.632481 | NaN | 0.147166 | 0.517036 | 13.042854 | 1.251761 | 0.416504 | 0.508850 | 0.554729 | 0.466033 | 0.413047 | 0.332734 | 0.320168 | 1.764201 | 0.255774 | -0.022255 | 1724.417604 | 5182.564161 | 1325.566362 | 15.690353 | 15.914904 | 16.551986 | 2.891836 | NaN | NaN | NaN | NaN | NaN | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 1 | AALI | 2009 | PT Astra Agro Lestari Tbk (IDX:AALI) | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1.826000 | 1.007000 | 9 | 12 | 0.104448 | 0.226435 | 0.124016 | 0.104148 | 0.839797 | 0.355822 | 0.021493 | 0.151198 | 0.000000 | 0.000000 | 0.848802 | -0.272069 | 0.692401 | NaN | 0.102418 | 0.343831 | 31.294532 | 0.980570 | 0.228445 | 0.269138 | 0.382801 | 0.417789 | 0.350644 | 0.232972 | 0.262157 | 1.733860 | 0.267352 | -0.010451 | 1098.367037 | 4714.593791 | 1260.454232 | 15.839888 | 15.820267 | 17.394169 | 5.574543 | -0.090297 | -0.363050 | 0.161295 | 0.204258 | -0.049120 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 2 | AALI | 2010 | PT Astra Agro Lestari Tbk (IDX:AALI) | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1.932000 | 1.262000 | 9 | 13 | 0.111868 | 0.233306 | 0.120778 | 0.141129 | 1.168507 | 0.304554 | 0.010879 | 0.151794 | 0.000000 | 0.000000 | 0.848206 | -0.371938 | 0.694187 | NaN | 0.112528 | 0.340407 | 30.915714 | 1.005906 | 0.239274 | 0.282095 | 0.379443 | 0.408126 | 0.338409 | 0.237870 | 0.335160 | 2.207991 | 0.333192 | 0.051438 | 1335.868347 | 5615.970205 | 1871.196289 | 15.989330 | 15.995218 | 17.535363 | 5.532640 | 0.191189 | 0.216231 | 0.161186 | 0.160371 | 0.484541 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
print('=== Dataset Fitur dengan Label Distress Rugi 2 Tahun Berturut-turut yang berukuran',df_fitur_conloss.shape,'===')
df_fitur_conloss.head(3)=== Dataset Fitur dengan Label Distress Rugi 2 Tahun Berturut-turut yang berukuran (5160, 75) ===
| ticker | Year | Entity Name | target_distress | tahun_distress | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | CR | QR | Age_When_IPO | Years_Since_IPO | WC_Sales | CA_TA | CL_TA | CashST_TA | CashST_CL | Inventory_CA | Prepaid_CA | TL_TA | Debt_TA | Debt_Equity | Equity_TA | NetDebt_EBITDA | PPE_TA | Intang_TA | Altman_X1_WC_TA | Altman_X3_EBIT_TA | Altman_X4_MVE_TL | Altman_X5_SalesTA_AssetTurnover | ROA | ROE | EBITDA_TA | GrossMargin | OpMargin | NetMargin | CFO_TA | CFO_TL | CFO_Sales | NetCash_TA | EPS_proxy | Sales_per_share | CFO_per_share | log_TA | log_Sales | log_MktCap | PB | Sales_growth | NI_growth | TA_growth | Equity_growth | CFO_growth | industry_Banks | industry_Capital Goods | industry_Commercial and Professional Services | industry_Consumer Discretionary Distribution and Retail | industry_Consumer Durables and Apparel | industry_Consumer Services | industry_Consumer Staples Distribution and Retail | industry_Energy | industry_Financial Services | industry_Food Beverage and Tobacco | industry_Health Care Equipment and Services | industry_Household and Personal Products | industry_Insurance | industry_Materials | industry_Media and Entertainment | industry_Pharmaceuticals Biotechnology and Life Sciences | industry_Real Estate Management and Development | industry_Software and Services | industry_Technology Hardware and Equipment | industry_Telecommunication Services | industry_Transportation | industry_Utilities | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | AALI | 2008 | PT Astra Agro Lestari Tbk (IDX:AALI) | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1.944000 | 0.878000 | 9 | 11 | 0.117567 | 0.303024 | 0.155859 | 0.133083 | 0.853871 | 0.395495 | 0.018945 | 0.181481 | 0.000000 | 0.000000 | 0.818519 | -0.239907 | 0.632481 | NaN | 0.147166 | 0.517036 | 13.042854 | 1.251761 | 0.416504 | 0.508850 | 0.554729 | 0.466033 | 0.413047 | 0.332734 | 0.320168 | 1.764201 | 0.255774 | -0.022255 | 1724.417604 | 5182.564161 | 1325.566362 | 15.690353 | 15.914904 | 16.551986 | 2.891836 | NaN | NaN | NaN | NaN | NaN | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 1 | AALI | 2009 | PT Astra Agro Lestari Tbk (IDX:AALI) | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1.826000 | 1.007000 | 9 | 12 | 0.104448 | 0.226435 | 0.124016 | 0.104148 | 0.839797 | 0.355822 | 0.021493 | 0.151198 | 0.000000 | 0.000000 | 0.848802 | -0.272069 | 0.692401 | NaN | 0.102418 | 0.343831 | 31.294532 | 0.980570 | 0.228445 | 0.269138 | 0.382801 | 0.417789 | 0.350644 | 0.232972 | 0.262157 | 1.733860 | 0.267352 | -0.010451 | 1098.367037 | 4714.593791 | 1260.454232 | 15.839888 | 15.820267 | 17.394169 | 5.574543 | -0.090297 | -0.363050 | 0.161295 | 0.204258 | -0.049120 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 2 | AALI | 2010 | PT Astra Agro Lestari Tbk (IDX:AALI) | 0 | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1.932000 | 1.262000 | 9 | 13 | 0.111868 | 0.233306 | 0.120778 | 0.141129 | 1.168507 | 0.304554 | 0.010879 | 0.151794 | 0.000000 | 0.000000 | 0.848206 | -0.371938 | 0.694187 | NaN | 0.112528 | 0.340407 | 30.915714 | 1.005906 | 0.239274 | 0.282095 | 0.379443 | 0.408126 | 0.338409 | 0.237870 | 0.335160 | 2.207991 | 0.333192 | 0.051438 | 1335.868347 | 5615.970205 | 1871.196289 | 15.989330 | 15.995218 | 17.535363 | 5.532640 | 0.191189 | 0.216231 | 0.161186 | 0.160371 | 0.484541 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
all_dfs = {'ppk': df_fitur_ppk, 'negeq': df_fitur_negeq, 'conloss': df_fitur_conloss}
all_dfs.keys()dict_keys(['ppk', 'negeq', 'conloss'])
Modelling
# limit()Split Train Test dan X y
# all_dfs = {'ppk': df_fitur_ppk, 'negeq': df_fitur_negeq, 'conloss': df_fitur_conloss}
# kolom_missing_indicator = {'ppk': ['fitur_A', ...], 'negeq': [...], 'conloss': [...]}
metadata_cols = ['ticker', 'Year', 'Entity Name', 'tahun_distress', 'Industry Group']
target_col = 'target_distress'
split_years = {
'ppk': 2023,
'negeq': 2020,
'conloss': 2020
} # tahun disini & setelah-setelahnya masuk test
# Dictionary utama untuk menyimpan split data mentah
dict_data_splits = {}
for target_name, df_target in all_dfs.items():
# data diurutkan berdasarkan tahun untuk mencegah leakage urutan
df_target = df_target.sort_values(by=['Year', 'ticker']).reset_index(drop=True)
year_limit = split_years[target_name]
# Pisahkan X dan y (metadata masih ada di X)
X = df_target.drop(columns=[target_col])
y = df_target[target_col]
# Split time-based
train_mask = X['Year'] < year_limit
test_mask = X['Year'] >= year_limit
dict_data_splits[target_name] = {
'X_train': X[train_mask].copy().reset_index(drop=True),
'X_test': X[test_mask].copy().reset_index(drop=True),
'y_train': y[train_mask].copy().reset_index(drop=True),
'y_test': y[test_mask].copy().reset_index(drop=True)
}
print(f"[{target_name.upper()}] Train size: {len(X[train_mask])}, Test size: {len(X[test_mask])}")[PPK] Train size: 1932, Test size: 651
[NEGEQ] Train size: 4615, Test size: 1788
[CONLOSS] Train size: 3933, Test size: 1227
def print_dict_structure(d, indent=0):
for key, value in d.items():
# Cetak key dengan spasi sesuai tingkatannya
print(' ' * indent + f"├── {key}")
# Jika isinya adalah dictionary lagi, telusuri lebih dalam
if isinstance(value, dict):
print_dict_structure(value, indent + 1)
else:
# Jika sudah mentok (misalnya isinya DataFrame), cetak tipe datanya
print(' ' * (indent + 1) + f"└── (Isi: {type(value).__name__})")
print_dict_structure(dict_data_splits)├── ppk
├── X_train
└── (Isi: DataFrame)
├── X_test
└── (Isi: DataFrame)
├── y_train
└── (Isi: Series)
├── y_test
└── (Isi: Series)
├── negeq
├── X_train
└── (Isi: DataFrame)
├── X_test
└── (Isi: DataFrame)
├── y_train
└── (Isi: Series)
├── y_test
└── (Isi: Series)
├── conloss
├── X_train
└── (Isi: DataFrame)
├── X_test
└── (Isi: DataFrame)
├── y_train
└── (Isi: Series)
├── y_test
└── (Isi: Series)
dict_data_splits['ppk']['X_test'].head(3)| ticker | Year | Entity Name | tahun_distress | Industry Group | Parent Percent Owned (%) | Percent Owned - All Institutions (%) | Percent Owned - Insiders (%) | CR | QR | Age_When_IPO | Years_Since_IPO | WC_Sales | CA_TA | CL_TA | CashST_TA | CashST_CL | Inventory_CA | Prepaid_CA | TL_TA | Debt_TA | Debt_Equity | Equity_TA | NetDebt_EBITDA | PPE_TA | Intang_TA | Altman_X1_WC_TA | Altman_X3_EBIT_TA | Altman_X4_MVE_TL | Altman_X5_SalesTA_AssetTurnover | ROA | ROE | EBITDA_TA | GrossMargin | OpMargin | NetMargin | CFO_TA | CFO_TL | CFO_Sales | NetCash_TA | EPS_proxy | Sales_per_share | CFO_per_share | log_TA | log_Sales | log_MktCap | PB | Sales_growth | NI_growth | TA_growth | Equity_growth | CFO_growth | industry_Banks | industry_Capital Goods | industry_Commercial and Professional Services | industry_Consumer Discretionary Distribution and Retail | industry_Consumer Durables and Apparel | industry_Consumer Services | industry_Consumer Staples Distribution and Retail | industry_Energy | industry_Financial Services | industry_Food Beverage and Tobacco | industry_Health Care Equipment and Services | industry_Household and Personal Products | industry_Insurance | industry_Materials | industry_Media and Entertainment | industry_Pharmaceuticals Biotechnology and Life Sciences | industry_Real Estate Management and Development | industry_Software and Services | industry_Technology Hardware and Equipment | industry_Telecommunication Services | industry_Transportation | industry_Utilities | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | AALI | 2023 | PT Astra Agro Lestari Tbk (IDX:AALI) | <NA> | Food, Beverage and Tobacco | 79.680000 | 2.100000 | NaN | 1.834000 | 0.765000 | 9 | 26 | 0.155989 | 0.246764 | 0.134580 | 0.072436 | 0.538236 | 0.438658 | NaN | 0.217714 | 0.138841 | 0.177482 | 0.782286 | 0.636827 | 0.627661 | NaN | 0.112183 | 0.057966 | 2.152934 | 0.719174 | 0.037723 | 0.048222 | 0.104275 | 0.134720 | 0.080600 | 0.052453 | 0.088009 | 0.404242 | 0.122376 | 0.016290 | 565.374654 | 10778.614202 | 1319.038494 | 17.177490 | 16.847839 | 16.419750 | 0.599173 | -0.049619 | -0.392779 | -0.013781 | 0.014512 | 0.383209 | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False | False | False | False |
| 1 | ABBA | 2023 | PT Mahaka Media Tbk (IDX:ABBA) | 2024 | Media and Entertainment | 51.160000 | NaN | 0.010000 | 0.551000 | 0.377000 | 10 | 21 | -0.510978 | 0.464570 | 0.842947 | 0.116017 | 0.137633 | 0.035783 | 0.014761 | 1.390180 | 0.751819 | -1.926851 | -0.390180 | -4.774142 | 0.106254 | 0.056911 | -0.378377 | -0.169481 | 0.542985 | 0.740497 | -0.193300 | 0.495411 | -0.133176 | 0.290642 | -0.228875 | -0.261041 | -0.215986 | -0.155365 | -0.291677 | -0.158563 | -13.316065 | 51.011484 | -14.878874 | 12.510378 | 12.209944 | 12.229137 | -1.934610 | 0.179525 | 0.692448 | -0.302784 | -4.596127 | -0.391259 | False | False | False | False | False | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False |
| 2 | ABDA | 2023 | PT Asuransi Bina Dana Arta Tbk (IDX:ABDA) | 2025 | Insurance | 87.760000 | NaN | 0.000000 | 4.143000 | 3.843000 | 7 | 34 | 2.161418 | 0.871290 | 0.210323 | 0.764564 | 3.635184 | NaN | 0.000571 | 0.418694 | 0.000000 | 0.000000 | 0.581306 | -24.397668 | 0.025011 | NaN | 0.660966 | 0.027747 | 3.227599 | 0.305802 | 0.031742 | 0.054605 | 0.031338 | 0.460030 | 0.090734 | 0.103800 | -0.011484 | -0.027428 | -0.037554 | 0.104000 | 136.235201 | 1312.478131 | -49.288315 | 14.795508 | 13.610692 | 15.096633 | 2.324728 | 0.046444 | -0.072007 | 0.077806 | 0.028809 | -0.165972 | False | False | False | False | False | False | False | False | False | False | False | False | True | False | False | False | False | False | False | False | False | False |
Simpan Sebaran Fitur untuk Commentary
print_dict_structure(dict_data_splits['ppk'])├── X_train
└── (Isi: DataFrame)
├── X_test
└── (Isi: DataFrame)
├── y_train
└── (Isi: Series)
├── y_test
└── (Isi: Series)
for indikator, dict_datasets in dict_data_splits.items():
print(dict_datasets['X_train'].head(2)) ticker Year Entity Name tahun_distress Industry Group Parent Percent Owned (%) Percent Owned - All Institutions (%) Percent Owned - Insiders (%) CR QR \
0 AALI 2020 PT Astra Agro Lestari Tbk (IDX:AALI) <NA> Food, Beverage and Tobacco 79.680000 2.100000 NaN 3.313000 1.322000
1 ABBA 2020 PT Mahaka Media Tbk (IDX:ABBA) 2024 Media and Entertainment 51.160000 NaN 0.010000 0.395000 0.331000
Age_When_IPO Years_Since_IPO WC_Sales CA_TA CL_TA CashST_TA CashST_CL Inventory_CA Prepaid_CA TL_TA Debt_TA Debt_Equity Equity_TA NetDebt_EBITDA PPE_TA Intang_TA Altman_X1_WC_TA \
0 9 23 0.220417 0.213737 0.064522 0.035236 0.546102 0.421875 NaN 0.307166 0.210873 0.304362 0.692834 1.609740 0.638614 NaN 0.149215
1 10 18 -0.493552 0.230787 0.583961 0.120570 0.206470 0.091745 0.009467 1.467895 0.799858 -1.709483 -0.467895 -3.523342 0.125893 0.098273 -0.353174
Altman_X3_EBIT_TA Altman_X4_MVE_TL Altman_X5_SalesTA_AssetTurnover ROA ROE EBITDA_TA GrossMargin OpMargin NetMargin CFO_TA CFO_TL CFO_Sales NetCash_TA EPS_proxy Sales_per_share \
0 0.061495 2.779863 0.676969 0.032172 0.046435 0.109109 0.159371 0.090839 0.047524 0.083588 0.272125 0.123473 0.021436 464.375964 9771.474517
1 -0.236487 0.694370 0.715576 -0.263170 0.562456 -0.192796 0.456651 -0.330485 -0.367774 -0.227670 -0.155099 -0.318163 0.015107 -21.172133 57.568309
CFO_per_share log_TA log_Sales log_MktCap PB Sales_growth NI_growth TA_growth Equity_growth CFO_growth industry_Banks industry_Capital Goods industry_Commercial and Professional Services \
0 1206.514302 17.139871 16.749742 16.981904 1.232442 0.077599 2.668607 0.029922 0.014188 0.796850 False False False
1 -18.316095 12.308858 11.974190 12.327937 -2.178401 -0.368317 0.317128 -0.463199 -2.126583 3.146363 False False False
industry_Consumer Discretionary Distribution and Retail industry_Consumer Durables and Apparel industry_Consumer Services industry_Consumer Staples Distribution and Retail industry_Energy \
0 False False False False False
1 False False False False False
industry_Financial Services industry_Food Beverage and Tobacco industry_Health Care Equipment and Services industry_Household and Personal Products industry_Insurance industry_Materials \
0 False True False False False False
1 False False False False False False
industry_Media and Entertainment industry_Pharmaceuticals Biotechnology and Life Sciences industry_Real Estate Management and Development industry_Software and Services \
0 False False False False
1 True False False False
industry_Technology Hardware and Equipment industry_Telecommunication Services industry_Transportation industry_Utilities
0 False False False False
1 False False False False
ticker Year Entity Name tahun_distress Industry Group Parent Percent Owned (%) Percent Owned - All Institutions (%) Percent Owned - Insiders (%) CR QR \
0 AALI 2008 PT Astra Agro Lestari Tbk (IDX:AALI) <NA> Food, Beverage and Tobacco 79.680000 2.100000 NaN 1.944000 0.878000
1 ABBA 2008 PT Mahaka Media Tbk (IDX:ABBA) 2020 Media and Entertainment 51.160000 NaN 0.010000 2.188000 1.794000
Age_When_IPO Years_Since_IPO WC_Sales CA_TA CL_TA CashST_TA CashST_CL Inventory_CA Prepaid_CA TL_TA Debt_TA Debt_Equity Equity_TA NetDebt_EBITDA PPE_TA Intang_TA Altman_X1_WC_TA \
0 9 11 0.117567 0.303024 0.155859 0.133083 0.853871 0.395495 0.018945 0.181481 0.000000 0.000000 0.818519 -0.239907 0.632481 NaN 0.147166
1 10 6 0.373814 0.437741 0.200019 0.081366 0.406792 0.016806 0.007812 0.288351 0.071997 0.101169 0.711649 -0.101827 0.222390 NaN 0.237722
Altman_X3_EBIT_TA Altman_X4_MVE_TL Altman_X5_SalesTA_AssetTurnover ROA ROE EBITDA_TA GrossMargin OpMargin NetMargin CFO_TA CFO_TL CFO_Sales NetCash_TA EPS_proxy Sales_per_share \
0 0.517036 13.042854 1.251761 0.416504 0.508850 0.554729 0.466033 0.413047 0.332734 0.320168 1.764201 0.255774 -0.022255 1724.417604 5182.564161
1 0.048964 3.051410 0.635936 0.016276 0.022872 0.092011 0.425306 0.076994 0.025595 0.005176 0.017950 0.008139 0.063200 2.589808 101.185871
CFO_per_share log_TA log_Sales log_MktCap PB Sales_growth NI_growth TA_growth Equity_growth CFO_growth industry_Banks industry_Capital Goods industry_Commercial and Professional Services \
0 1325.566362 15.690353 15.914904 16.551986 2.891836 NaN NaN NaN NaN NaN False False False
1 0.823541 12.329436 11.876779 12.201462 1.236390 NaN NaN NaN NaN NaN False False False
industry_Consumer Discretionary Distribution and Retail industry_Consumer Durables and Apparel industry_Consumer Services industry_Consumer Staples Distribution and Retail industry_Energy \
0 False False False False False
1 False False False False False
industry_Financial Services industry_Food Beverage and Tobacco industry_Health Care Equipment and Services industry_Household and Personal Products industry_Insurance industry_Materials \
0 False True False False False False
1 False False False False False False
industry_Media and Entertainment industry_Pharmaceuticals Biotechnology and Life Sciences industry_Real Estate Management and Development industry_Software and Services \
0 False False False False
1 True False False False
industry_Technology Hardware and Equipment industry_Telecommunication Services industry_Transportation industry_Utilities
0 False False False False
1 False False False False
ticker Year Entity Name tahun_distress Industry Group Parent Percent Owned (%) Percent Owned - All Institutions (%) Percent Owned - Insiders (%) CR QR \
0 AALI 2008 PT Astra Agro Lestari Tbk (IDX:AALI) <NA> Food, Beverage and Tobacco 79.680000 2.100000 NaN 1.944000 0.878000
1 ABBA 2008 PT Mahaka Media Tbk (IDX:ABBA) 2016 Media and Entertainment 51.160000 NaN 0.010000 2.188000 1.794000
Age_When_IPO Years_Since_IPO WC_Sales CA_TA CL_TA CashST_TA CashST_CL Inventory_CA Prepaid_CA TL_TA Debt_TA Debt_Equity Equity_TA NetDebt_EBITDA PPE_TA Intang_TA Altman_X1_WC_TA \
0 9 11 0.117567 0.303024 0.155859 0.133083 0.853871 0.395495 0.018945 0.181481 0.000000 0.000000 0.818519 -0.239907 0.632481 NaN 0.147166
1 10 6 0.373814 0.437741 0.200019 0.081366 0.406792 0.016806 0.007812 0.288351 0.071997 0.101169 0.711649 -0.101827 0.222390 NaN 0.237722
Altman_X3_EBIT_TA Altman_X4_MVE_TL Altman_X5_SalesTA_AssetTurnover ROA ROE EBITDA_TA GrossMargin OpMargin NetMargin CFO_TA CFO_TL CFO_Sales NetCash_TA EPS_proxy Sales_per_share \
0 0.517036 13.042854 1.251761 0.416504 0.508850 0.554729 0.466033 0.413047 0.332734 0.320168 1.764201 0.255774 -0.022255 1724.417604 5182.564161
1 0.048964 3.051410 0.635936 0.016276 0.022872 0.092011 0.425306 0.076994 0.025595 0.005176 0.017950 0.008139 0.063200 2.589808 101.185871
CFO_per_share log_TA log_Sales log_MktCap PB Sales_growth NI_growth TA_growth Equity_growth CFO_growth industry_Banks industry_Capital Goods industry_Commercial and Professional Services \
0 1325.566362 15.690353 15.914904 16.551986 2.891836 NaN NaN NaN NaN NaN False False False
1 0.823541 12.329436 11.876779 12.201462 1.236390 NaN NaN NaN NaN NaN False False False
industry_Consumer Discretionary Distribution and Retail industry_Consumer Durables and Apparel industry_Consumer Services industry_Consumer Staples Distribution and Retail industry_Energy \
0 False False False False False
1 False False False False False
industry_Financial Services industry_Food Beverage and Tobacco industry_Health Care Equipment and Services industry_Household and Personal Products industry_Insurance industry_Materials \
0 False True False False False False
1 False False False False False False
industry_Media and Entertainment industry_Pharmaceuticals Biotechnology and Life Sciences industry_Real Estate Management and Development industry_Software and Services \
0 False False False False
1 True False False False
industry_Technology Hardware and Equipment industry_Telecommunication Services industry_Transportation industry_Utilities
0 False False False False
1 False False False False
import json
# rasio yang interpretable & actionable untuk user manajemen/investor
# arah: "high_good" = makin tinggi makin sehat, "low_good" = makin rendah makin sehat
commentary_ratios = {
"TL_TA": "low_good", "Debt_Equity": "low_good", "Equity_TA": "high_good",
"NetDebt_EBITDA": "low_good",
"CR": "high_good", "QR": "high_good", "CashST_CL": "high_good",
"ROA": "high_good", "ROE": "high_good", "NetMargin": "high_good",
"OpMargin": "high_good", "GrossMargin": "high_good",
"CFO_TA": "high_good", "CFO_Sales": "high_good",
"Sales_growth": "high_good", "NI_growth": "high_good",
}
# train_sets = {"loss_two_years": (X_train, y_train), "negative_equity": (...), "watchlist_board": (...)}
out = {}
for indicator, dict_datasets in dict_data_splits.items():
Xtr = dict_datasets['X_train']
ytr = dict_datasets['y_train']
d = Xtr.copy()
d["_y"] = ytr.values
stats = {}
for r, direction in commentary_ratios.items():
if r not in d.columns:
continue
healthy = d.loc[d["_y"] == 0, r].dropna()
stats[r] = {
"direction": direction,
"healthy_p25": float(healthy.quantile(.25)),
"healthy_median": float(healthy.median()),
"healthy_p75": float(healthy.quantile(.75)),
}
out[indicator] = stats
json.dump(out, open("commentary_stats.json", "w"), indent=2)Inisialisasi dan Konfigurasi Model
Konfigurasi Variabel, Model, dan Compute Type
# List konfigurasi
targets = ['ppk', 'negeq', 'conloss']
null_strategies = ['keep', 'impute']
feat_strategies = ['all', 'corr', 'pca']# # NOTE : CONFIG / KONFIGURASI !!!
# compute_type = 'gpu'
## ((sudah dipindah di paling awal notebook))
if compute_type == 'cpu':
model_names = ['RandomForest', 'AdaBoost']
n_jobs_optuna = -1 # seluruh core dipakai untuk paralel optuna
else: # gpu
model_names = ['XGBoost', 'LightGBM', 'CatBoost']
n_jobs_optuna = 1 # DILARANG paralel agar VRAM GPU tidak crash# Dictionary untuk menyimpan hasil akhir Optuna dan CV
dict_master_results = {}Fungsi Cross Validation
def get_custom_cv_indices(X_train_df):
"""
Menghasilkan list tuple (train_indices, val_indices) berdasarkan window-expanding 'Year'.
"""
years = sorted(X_train_df['Year'].unique())
cv_indices = []
# Mulai dari tahun kedua agar fold pertama punya minimal 1 tahun train
for i in range(1, len(years)):
val_year = years[i]
# Train: Semua tahun sebelum val_year
train_idx = X_train_df.index[X_train_df['Year'] < val_year].tolist()
# Validation: Hanya tahun val_year
val_idx = X_train_df.index[X_train_df['Year'] == val_year].tolist()
cv_indices.append((train_idx, val_idx))
return cv_indicesFungsi Imputasi dan Low Corr Feat Selection
def apply_custom_imputation(df_train, df_test, fitur_cols):
# 1. GABUNGKAN SEMENTARA (Temporal Concat)
# Tujuannya agar FFILL bisa mengalir mulus dari akhir Train ke awal Test,
# dan juga mengalir di dalam Test itu sendiri.
df_train_temp = df_train.copy()
df_test_temp = df_test.copy()
df_train_temp['is_test'] = 0
df_test_temp['is_test'] = 1
combined = pd.concat([df_train_temp, df_test_temp], axis=0)
for col in fitur_cols:
combined[col] = pd.to_numeric(combined[col], errors='coerce').astype(float)
combined = combined.sort_values(by=['ticker', 'Year'])
# 2. LOGIKA FFILL YANG KETAT (Mencegah loncat jika baris tahun bolong)
# Buat dataframe bergeser (shift 1 ke bawah) per ticker
shifted = combined.groupby('ticker')[fitur_cols + ['Year']].shift(1)
# Masking: Cek apakah baris sebelumnya persis berjarak 1 tahun
is_gap_one_year = ((combined['Year'] - shifted['Year']) == 1).fillna(False).astype(bool)
# Lakukan ffill hanya pada sel yang jaraknya 1 tahun
for col in fitur_cols:
# Jika nilai sekarang NaN, dan jarak dengan baris sebelumnya 1 tahun,
# maka ambil nilai baris sebelumnya. Jika tidak, biarkan tetap NaN.
combined[col] = np.where(
combined[col].isna() & is_gap_one_year,
shifted[col],
combined[col]
)
# 3. PISAHKAN KEMBALI TRAIN DAN TEST
train_imp = combined[combined['is_test'] == 0].drop(columns=['is_test']).copy()
test_imp = combined[combined['is_test'] == 1].drop(columns=['is_test']).copy()
# 4. EXTREME VALUE IMPUTATION (cuma dari train untuk mencegah Leakage)
for col in fitur_cols:
min_train = train_imp[col].min()
if pd.isna(min_train):
min_train = 0.0
std_train = train_imp[col].std()
if pd.isna(std_train) or std_train == 0:
std_train = 1.0
# Rumus custom nilai ekstrim menyesuaikan distribusi data
extreme_val = min_train - (std_train * 10) - 100000.0
train_imp[col] = train_imp[col].fillna(extreme_val)
test_imp[col] = test_imp[col].fillna(extreme_val)
return train_imp, test_imp
#######
# def apply_custom_imputation(df_train, df_test, fitur_cols):
# """
# Fungsi untuk melakukan imputasi custom: FFILL -> Group Median -> Global Median.
# Proses 'fit' (perhitungan median) murni hanya dilakukan pada df_train untuk mencegah leakage.
# """
# # Gunakan copy agar tidak mengubah dataframe asli di luar fungsi (mencegah SettingWithCopyWarning)
# train_imp = df_train.copy()
# test_imp = df_test.copy()
# # ---> [PERBAIKAN ERROR INT64 & NATYPE] <---
# # Lakukan casting tipe data ke FLOAT paling awal sebelum operasi matematis apapun
# for col in fitur_cols:
# train_imp[col] = pd.to_numeric(train_imp[col], errors='coerce').astype(float)
# test_imp[col] = pd.to_numeric(test_imp[col], errors='coerce').astype(float)
# # --- TAHAP 1: Forward Fill (Berdasarkan Ticker) ---
# # Pastikan Train terurut berdasarkan waktu
# train_imp = train_imp.sort_values(by=['ticker', 'Year'])
# train_imp[fitur_cols] = train_imp.groupby('ticker')[fitur_cols].ffill()
# # Untuk Test/Val, kita ambil nilai TERAKHIR dari Train untuk tiap ticker (Mencegah leakage)
# last_known_train = train_imp.groupby('ticker')[fitur_cols].last()
# for col in fitur_cols:
# test_imp[col] = test_imp[col].fillna(test_imp['ticker'].map(last_known_train[col]))
# # --- TAHAP 2: Group Median (Industry Group + Size Bin) ---
# # Binning log_TA (Membagi ukuran perusahaan menjadi Small, Medium, Large)
# # Pastikan nama kolom 'log_TA' sesuai dengan nama asli di datasetmu
# bins = pd.qcut(train_imp['log_TA'], q=3, retbins=True, duplicates='drop')[1]
# bins[0], bins[-1] = -np.inf, np.inf # Perlebar batas untuk antisipasi nilai ekstrem di Test
# train_imp['Size_Bin'] = pd.cut(train_imp['log_TA'], bins=bins, labels=['Small', 'Medium', 'Large'])
# test_imp['Size_Bin'] = pd.cut(test_imp['log_TA'], bins=bins, labels=['Small', 'Medium', 'Large'])
# # Hitung median grup HANYA dari Train
# group_medians = train_imp.groupby(['Industry Group', 'Size_Bin'], observed=False)[fitur_cols].median()
# # Eksekusi pengisian di Train
# train_imp[fitur_cols] = train_imp.groupby(['Industry Group', 'Size_Bin'], observed=False)[fitur_cols].transform(lambda x: x.fillna(x.median()))
# # Eksekusi pengisian di Test (Mapping dari tabel group_medians milik Train)
# val_keys = pd.MultiIndex.from_arrays([test_imp['Industry Group'], test_imp['Size_Bin']])
# mapped_medians = group_medians.reindex(val_keys).values
# test_imp[fitur_cols] = test_imp[fitur_cols].fillna(pd.DataFrame(mapped_medians, index=test_imp.index, columns=fitur_cols))
# # --- TAHAP 3: Global Median (Fallback) ---
# # Berjaga-jaga jika ada grup yang isinya kosong semua sehingga mediannya tetap NaN
# global_medians = train_imp[fitur_cols].median()
# train_imp[fitur_cols] = train_imp[fitur_cols].fillna(global_medians)
# test_imp[fitur_cols] = test_imp[fitur_cols].fillna(global_medians)
# # ---> TAHAP 4 PERBAIKAN ERROR PCA (Extreme Fallback) <---
# # Jika fitur 100% kosong, median global juga NaN. Kita paksa jadi 0 agar PCA tidak crash.
# train_imp[fitur_cols] = train_imp[fitur_cols].fillna(0)
# test_imp[fitur_cols] = test_imp[fitur_cols].fillna(0)
# return train_imp, test_imp
def get_low_corr_features_target_aware(X_train_fold, y_train_fold, threshold=0.85):
X_num = X_train_fold.select_dtypes(include=[np.number])
# Cari kolom konstan
constant_cols = [col for col in X_num.columns if X_num[col].nunique() <= 1]
# Buang dari perhitungan korelasi agar tidak ada warning (tidak terpakai juga)
X_num = X_num.drop(columns=constant_cols)
# Masukkan fitur konstan ke dalam daftar buang agar ikut dihapus dari dataset akhir
to_drop = set(constant_cols)
# Hitung korelasi antar fitur tersisa dan thd target
with warnings.catch_warnings():
warnings.simplefilter("ignore") # Abaikan semua warning di dalam blok ini
corr_matrix = X_num.corr(method='spearman').abs()
target_corr = X_num.apply(lambda col: col.corr(y_train_fold, method='spearman')).abs()
cols = corr_matrix.columns
for i in range(len(cols)):
for j in range(i+1, len(cols)):
col_i, col_j = cols[i], cols[j]
# Lewati jika salah satu sudah masuk daftar drop
if col_i in to_drop or col_j in to_drop:
continue
# cek korelasi antar fitur yg melebihi threshold
if corr_matrix.loc[col_i, col_j] > threshold:
# perbaikan jika target_corr bernilai NaN maka dianggap 0 (tidak berkorelasi dengan target)
corr_i = target_corr[col_i] if pd.notna(target_corr[col_i]) else 0
corr_j = target_corr[col_j] if pd.notna(target_corr[col_j]) else 0
# Bandingkan korelasi terhadap target, drop yang lebih kecil
if corr_i > corr_j:
to_drop.add(col_j)
else:
to_drop.add(col_i)
return list(to_drop)
# def get_low_corr_features(X_train_fold, threshold=0.85):
# """
# Mencari fitur yang harus di-drop agar tidak ada fitur yang saling berkorelasi > threshold.
# Metadata diabaikan dalam perhitungan.
# """
# # Ambil hanya kolom numerik dan abaikan metadata
# X_num = X_train_fold.drop(columns=metadata_cols, errors='ignore').select_dtypes(include=[np.number])
# corr_matrix = X_num.corr().abs()
# # Ambil segitiga atas dari matriks korelasi
# upper = corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k=1).astype(bool))
# # Cari nama kolom yang korelasinya di atas threshold
# to_drop = [column for column in upper.columns if any(upper[column] > threshold)]
# return to_dropFungsi Objective Optuna untuk Hyperparameter Tuning
# --- DEFINISI FUNGSI OPTUNA ---
def objective(trial, m_name, n_strat, f_strat, X_train_full, y_train_full, cv_indices):
# Hyperparameter Space Search
if m_name == 'RandomForest':
params = {
# 'n_estimators': trial.suggest_int('n_estimators', 50, 300),
# 'max_depth': trial.suggest_int('max_depth', 3, 15),
# 'class_weight': 'balanced',
# 'n_jobs': -1,
'random_state': 42,
'n_estimators': trial.suggest_int('n_estimators', 100, 500),
'max_depth': trial.suggest_int('max_depth', 5, 20),
'min_samples_split': trial.suggest_int('min_samples_split', 2, 10),
'min_samples_leaf': trial.suggest_int('min_samples_leaf', 1, 10),
'max_features': trial.suggest_categorical('max_features', ['sqrt', 'log2', None]),
'n_jobs': -1 # Memaksimalkan 4 core CPU kaggle
}
model = RandomForestClassifier(**params)
elif m_name == 'XGBoost':
params = {
# 'n_estimators': trial.suggest_int('n_estimators', 50, 300),
# 'max_depth': trial.suggest_int('max_depth', 3, 10),
# 'learning_rate': trial.suggest_float('learning_rate', 1e-3, 0.3, log=True),
# 'scale_pos_weight': trial.suggest_float('scale_pos_weight', 1, 20), # Berguna untuk imbalance
# 'tree_method': 'hist',
# 'device': 'cuda', # GPU
'random_state': 42,
'n_estimators': trial.suggest_int('n_estimators', 100, 1000),
'max_depth': trial.suggest_int('max_depth', 3, 7), # Jangan lebih dari 7 untuk data <10k baris
'learning_rate': trial.suggest_float('learning_rate', 1e-3, 0.1, log=True),
'subsample': trial.suggest_float('subsample', 0.6, 1.0),
'colsample_bytree': trial.suggest_float('colsample_bytree', 0.6, 1.0),
'gamma': trial.suggest_float('gamma', 0, 5), # Penalti kompleksitas node (penting!)
'reg_alpha': trial.suggest_float('reg_alpha', 1e-3, 10.0, log=True), # L1 Regularization
'reg_lambda': trial.suggest_float('reg_lambda', 1e-3, 10.0, log=True), # L2 Regularization
'tree_method': 'hist',
'device': 'cuda' # GPU
}
model = XGBClassifier(**params)
elif m_name == 'LightGBM':
params = {
# 'n_estimators': trial.suggest_int('n_estimators', 50, 300),
# 'max_depth': trial.suggest_int('max_depth', 3, 15),
# 'learning_rate': trial.suggest_float('learning_rate', 1e-3, 0.3, log=True),
# 'class_weight': 'balanced',
# 'device_type': 'gpu', # Gunakan GPU
'random_state': 42,
'verbose': -1,
'n_estimators': trial.suggest_int('n_estimators', 100, 1000),
'num_leaves': trial.suggest_int('num_leaves', 15, 63), # Rule of thumb: < 2^max_depth
'max_depth': trial.suggest_int('max_depth', 3, 7),
'learning_rate': trial.suggest_float('learning_rate', 1e-3, 0.1, log=True),
'min_child_samples': trial.suggest_int('min_child_samples', 10, 50), # Kunci anti-overfitting
'subsample': trial.suggest_float('subsample', 0.6, 1.0),
'colsample_bytree': trial.suggest_float('colsample_bytree', 0.6, 1.0),
'reg_alpha': trial.suggest_float('reg_alpha', 1e-3, 10.0, log=True),
'reg_lambda': trial.suggest_float('reg_lambda', 1e-3, 10.0, log=True),
'device': 'gpu'
}
model = LGBMClassifier(**params)
elif m_name == 'AdaBoost':
params = {
# 'n_estimators': trial.suggest_int('n_estimators', 50, 300),
# 'learning_rate': trial.suggest_float('learning_rate', 1e-3, 0.5, log=True),
'random_state': 42,
'n_estimators': trial.suggest_int('n_estimators', 50, 300),
'learning_rate': trial.suggest_float('learning_rate', 1e-3, 1.0, log=True),
}
model = AdaBoostClassifier(**params)
elif m_name == 'CatBoost':
params = {
# 'iterations': trial.suggest_int('iterations', 50, 300),
# 'depth': trial.suggest_int('depth', 3, 10),
# 'learning_rate': trial.suggest_float('learning_rate', 1e-3, 0.3, log=True),
# 'auto_class_weights': 'Balanced',
# 'task_type': 'GPU', # Gunakan GPU
# 'verbose': 0,
'random_seed': 42,
'iterations': trial.suggest_int('iterations', 100, 500),
'depth': trial.suggest_int('depth', 3, 6),
'learning_rate': trial.suggest_float('learning_rate', 5e-3, 0.2, log=True),
'l2_leaf_reg': trial.suggest_float('l2_leaf_reg', 0.1, 10.0), # CatBoost sangat sensitif pada L2
'border_count': trial.suggest_int('border_count', 32, 128),
'task_type': 'GPU',
'verbose': 0
}
model = CatBoostClassifier(**params)
# 2. Proses Custom CV tiap fold
fold_pr_auc = []
for train_idx, val_idx in cv_indices:
# Split data di fold ini
X_tr_fold = X_train_full.iloc[train_idx].copy()
X_val_fold = X_train_full.iloc[val_idx].copy()
y_tr_fold = y_train_full.iloc[train_idx].copy()
y_val_fold = y_train_full.iloc[val_idx].copy()
# -- TAHAP PREPROCESSING DI DALAM FOLD --
# A. Imputasi
if n_strat == 'impute':
# Ambil kolom fitur saja (tanpa metadata)
fitur_cols = [c for c in X_tr_fold.columns if c not in metadata_cols]
# Panggil fungsi custom imputation
X_tr_fold, X_val_fold = apply_custom_imputation(
df_train=X_tr_fold,
df_test=X_val_fold,
fitur_cols=fitur_cols
)
# Penyimpanan metadata untuk nanti digabung kembali jika diperlukan (misal untuk PCA yang butuh fitur asli)
meta_tr = X_tr_fold[metadata_cols].reset_index(drop=True)
meta_val = X_val_fold[metadata_cols].reset_index(drop=True)
# B. Feature Strategy (Semua fitur yang ada sudah dipastikan tidak null di sini jika strat impute)
if f_strat == 'corr':
cols_to_drop = get_low_corr_features_target_aware(X_tr_fold, y_tr_fold, threshold=0.85)
X_tr_fold = X_tr_fold.drop(columns=cols_to_drop)
X_val_fold = X_val_fold.drop(columns=cols_to_drop)
elif f_strat == 'pca':
fitur_cols = [c for c in X_tr_fold.columns if c not in metadata_cols]
# --- Buang Fitur Konstan Sebelum PCA ---
constant_cols = [col for col in fitur_cols if X_tr_fold[col].nunique() <= 1]
if constant_cols:
X_tr_fold = X_tr_fold.drop(columns=constant_cols)
X_val_fold = X_val_fold.drop(columns=constant_cols)
# Perbarui daftar fitur setelah kolom konstan dibuang
fitur_cols = [c for c in fitur_cols if c not in constant_cols]
scaler = StandardScaler()
pca = PCA(n_components=0.95, random_state=42) # Retain 95% variance
# Scaler & PCA
X_tr_pca = pca.fit_transform(scaler.fit_transform(X_tr_fold[fitur_cols]))
X_val_pca = pca.transform(scaler.transform(X_val_fold[fitur_cols]))
# # Rekonstruksi DataFrame (simpan kembali metadata)
# meta_tr = X_tr_fold[metadata_cols].reset_index(drop=True)
# meta_val = X_val_fold[metadata_cols].reset_index(drop=True)
### Sudah dipindah di atas ###
pca_cols = [f"PC_{i}" for i in range(X_tr_pca.shape[1])]
X_tr_fold = pd.concat([meta_tr, pd.DataFrame(X_tr_pca, columns=pca_cols)], axis=1)
X_val_fold = pd.concat([meta_val, pd.DataFrame(X_val_pca, columns=pca_cols)], axis=1)
# C. Drop Metadata Sesaat Sebelum Training
X_tr_final = X_tr_fold.drop(columns=metadata_cols, errors='ignore')
X_val_final = X_val_fold.drop(columns=metadata_cols, errors='ignore')
# D. Training & Scoring
model.fit(X_tr_final, y_tr_fold)
# Ambil probabilitas kelas positif (index 1)
y_val_probs = model.predict_proba(X_val_final)[:, 1]
# Hitung PR-AUC (Average Precision)
fold_score = average_precision_score(y_val_fold, y_val_probs)
fold_pr_auc.append(fold_score)
# Return rata-rata PR-AUC dari seluruh CV window
return np.mean(fold_pr_auc)Model Training (Find Best Params)
# limit()# import warnings
# warnings.filterwarnings('ignore') # Matikan warning untuk output yang bersih
# =====================================================================
# KONFIGURASI BACKUP
# =====================================================================
# True : Lanjutkan eksperimen dari file pickle yang ada di GDrive.
# False : Mulai dari awal (WARNING: Akan menimpa file backup di GDrive!)
resume_from_backup = True
# =====================================================================
backup_file_path = prefix_path + f'pickle/eksperimen_skripsi_backup_{compute_type}.pkl'
if resume_from_backup:
if os.path.exists(backup_file_path):
with open(backup_file_path, 'rb') as f:
dict_master_results = pickle.load(f)
print(f"✅ Berhasil me-load backup {compute_type.upper()}!")
print(f"Terdapat {len(dict_master_results)} eksperimen yang sudah selesai.")
print("Sistem akan melewati eksperimen tersebut dan melanjutkan sisanya...\n")
else:
print(f"⚠️ File backup tidak ditemukan di {backup_file_path}")
print("Memulai dict_master_results kosong...\n")
dict_master_results = {}
else:
print("⚠️ Memulai ulang eksperimen dari AWAL (dict_master_results kosong).")
print("File backup di GDrive akan ditimpa saat eksperimen pertama selesai!\n")
dict_master_results = {}
# NESTED LOOP SEMUA KONFIGURASI TRAINING
for t_name in targets:
# Ambil data spesifik target ini
X_train_full = dict_data_splits[t_name]['X_train'].copy()
y_train_full = dict_data_splits[t_name]['y_train'].copy()
# Ekstrak indeks Custom CV
cv_indices = get_custom_cv_indices(X_train_full)
for n_strat in null_strategies:
# pembuatan missing indicator
# di luar fold
# dan hanya jika strateginya impute
if n_strat == 'impute':
cols_to_flag = kolom_missing_indicator[t_name]
for col in cols_to_flag:
# tambah missing indicator (astype int agar jadi 1/0)
X_train_full[f"{col}_is_missing"] = X_train_full[col].isnull().astype(int)
for f_strat in feat_strategies:
# PCA tidak bisa pakai data null, jadi diloncati
if f_strat == 'pca' and n_strat == 'keep':
continue # loncat ke next loop
for m_name in model_names:
# loncati model yang tidak bisa menerima data null
if n_strat == 'keep' and m_name in ['AdaBoost']:
continue
exp_id = f"{t_name}_{n_strat}_{f_strat}_{m_name}"
# ---> [TAMBAHAN AGAR TIDAK MENGULANG DARI AWAL] <---
if exp_id in dict_master_results:
print(f"Melewati {exp_id} karena sudah selesai dan tersimpan.")
continue
print(f"Mulai eksperimen: {exp_id}")
# --- JALANKAN OPTUNA ---
study = optuna.create_study(direction='maximize')
study.optimize(lambda trial: objective(trial, m_name, n_strat, f_strat, X_train_full, y_train_full, cv_indices), n_trials=100, n_jobs=n_jobs_optuna)
# Simpan Hasil ke Master Dictionary
dict_master_results[exp_id] = {
'best_params': study.best_params,
'best_cv_pr_auc': study.best_value
}
# ---> [TAMBAHAN MODIFIKASI 1: AUTO-SAVE] <---
with open(save_path + f'pickle/eksperimen_skripsi_backup_{compute_type}.pkl', 'wb') as f:
pickle.dump(dict_master_results, f)
print(f"Eksperimen {exp_id} berhasil di-save ke backup {compute_type}!")
# Contoh melihat hasil
print("\n=== EKSPERIMEN SELESAI ===")
for key, val in dict_master_results.items():
print(f"{key} -> Best CV PR-AUC: {val['best_cv_pr_auc']:.4f}")✅ Berhasil me-load backup CPU!
Terdapat 24 eksperimen yang sudah selesai.
Sistem akan melewati eksperimen tersebut dan melanjutkan sisanya...
Melewati ppk_keep_all_RandomForest karena sudah selesai dan tersimpan.
Melewati ppk_keep_corr_RandomForest karena sudah selesai dan tersimpan.
Melewati ppk_impute_all_RandomForest karena sudah selesai dan tersimpan.
Melewati ppk_impute_all_AdaBoost karena sudah selesai dan tersimpan.
Melewati ppk_impute_corr_RandomForest karena sudah selesai dan tersimpan.
Melewati ppk_impute_corr_AdaBoost karena sudah selesai dan tersimpan.
Melewati ppk_impute_pca_RandomForest karena sudah selesai dan tersimpan.
Melewati ppk_impute_pca_AdaBoost karena sudah selesai dan tersimpan.
Melewati negeq_keep_all_RandomForest karena sudah selesai dan tersimpan.
Melewati negeq_keep_corr_RandomForest karena sudah selesai dan tersimpan.
Melewati negeq_impute_all_RandomForest karena sudah selesai dan tersimpan.
Melewati negeq_impute_all_AdaBoost karena sudah selesai dan tersimpan.
Melewati negeq_impute_corr_RandomForest karena sudah selesai dan tersimpan.
Melewati negeq_impute_corr_AdaBoost karena sudah selesai dan tersimpan.
Melewati negeq_impute_pca_RandomForest karena sudah selesai dan tersimpan.
Melewati negeq_impute_pca_AdaBoost karena sudah selesai dan tersimpan.
Melewati conloss_keep_all_RandomForest karena sudah selesai dan tersimpan.
Melewati conloss_keep_corr_RandomForest karena sudah selesai dan tersimpan.
Melewati conloss_impute_all_RandomForest karena sudah selesai dan tersimpan.
Melewati conloss_impute_all_AdaBoost karena sudah selesai dan tersimpan.
Melewati conloss_impute_corr_RandomForest karena sudah selesai dan tersimpan.
Melewati conloss_impute_corr_AdaBoost karena sudah selesai dan tersimpan.
Melewati conloss_impute_pca_RandomForest karena sudah selesai dan tersimpan.
Melewati conloss_impute_pca_AdaBoost karena sudah selesai dan tersimpan.
=== EKSPERIMEN SELESAI ===
ppk_keep_all_RandomForest -> Best CV PR-AUC: 0.5010
ppk_keep_corr_RandomForest -> Best CV PR-AUC: 0.5161
ppk_impute_all_RandomForest -> Best CV PR-AUC: 0.3698
ppk_impute_all_AdaBoost -> Best CV PR-AUC: 0.3105
ppk_impute_corr_RandomForest -> Best CV PR-AUC: 0.3433
ppk_impute_corr_AdaBoost -> Best CV PR-AUC: 0.3210
ppk_impute_pca_RandomForest -> Best CV PR-AUC: 0.3397
ppk_impute_pca_AdaBoost -> Best CV PR-AUC: 0.3101
negeq_keep_all_RandomForest -> Best CV PR-AUC: 0.4171
negeq_keep_corr_RandomForest -> Best CV PR-AUC: 0.4463
negeq_impute_all_RandomForest -> Best CV PR-AUC: 0.1105
negeq_impute_all_AdaBoost -> Best CV PR-AUC: 0.0901
negeq_impute_corr_RandomForest -> Best CV PR-AUC: 0.1090
negeq_impute_corr_AdaBoost -> Best CV PR-AUC: 0.0845
negeq_impute_pca_RandomForest -> Best CV PR-AUC: 0.1197
negeq_impute_pca_AdaBoost -> Best CV PR-AUC: 0.0847
conloss_keep_all_RandomForest -> Best CV PR-AUC: 0.5440
conloss_keep_corr_RandomForest -> Best CV PR-AUC: 0.5423
conloss_impute_all_RandomForest -> Best CV PR-AUC: 0.2272
conloss_impute_all_AdaBoost -> Best CV PR-AUC: 0.1912
conloss_impute_corr_RandomForest -> Best CV PR-AUC: 0.2365
conloss_impute_corr_AdaBoost -> Best CV PR-AUC: 0.1855
conloss_impute_pca_RandomForest -> Best CV PR-AUC: 0.1935
conloss_impute_pca_AdaBoost -> Best CV PR-AUC: 0.1858
Asesmen Model (Find Best Threshold & Fit Predict)
Threshold Finding
# 1. Inisialisasi dictionary
if platform == "gcolab":
dict_master_results = {} # Mulai dengan dictionary kosong di Colab
print("Inisialisasi dict_master_results kosong di Google Colab.")
for c_type in ["cpu","gpu"]:
file_path = prefix_path + f'pickle/eksperimen_skripsi_backup_{c_type}.pkl'
if os.path.exists(file_path):
with open(file_path, 'rb') as f:
data = pickle.load(f)
dict_master_results.update(data) # Menggabungkan dictionary
print(f"Berhasil me-load data backup {c_type.upper()} ({len(data)} eksperimen).")
else:
print(f"File backup {c_type.upper()} tidak ditemukan di: {file_path}")
elif platform == "kaggle": # Kaggle
# kalau pakai cpu yang dibuka gpu karena cpu sudah ada di var
# begitu pula sebaliknya
if compute_type == 'cpu':
file_path = prefix_path + f'pickle/eksperimen_skripsi_backup_gpu.pkl'
elif compute_type == 'gpu':
file_path = prefix_path + f'pickle/eksperimen_skripsi_backup_cpu.pkl'
if os.path.exists(file_path):
with open(file_path, 'rb') as f:
data = pickle.load(f)
dict_master_results.update(data) # Menggabungkan dictionary
print(f"Berhasil me-load backup SELAIN {compute_type.upper()} dengan {len(data)} eksperimen.")
else:
print(f"File backup {compute_type.upper()} tidak ditemukan di: {file_path}")
print(f"Total seluruh eksperimen yang siap dicari threshold-nya: {len(dict_master_results)}")
# Mapping string nama model ke Class Modelnya
dict_model_classes = {
'RandomForest': RandomForestClassifier,
'XGBoost': XGBClassifier,
'LightGBM': LGBMClassifier,
'AdaBoost': AdaBoostClassifier,
'CatBoost': CatBoostClassifier
}
# dict untuk menyimpan best threshold dari OOF predict
dict_best_thresholds = {}
threshold_pkl_path = save_path + 'best_thresholds_skripsi.pkl'
if resume_from_backup:
try:
with open(threshold_pkl_path, 'rb') as f:
dict_best_thresholds = pickle.load(f)
print("✅ Berhasil me-load Model dan Threshold dari backup.")
except FileNotFoundError:
print("⚠️ File backup model/threshold tidak ditemukan. Memaksa fit ulang...")
resume_from_backup = False
print("\n========== MEMULAI PROSES PENCARIAN THRESHOLD OPTIMAL (OOF-CV) ==========")
# Looping ke seluruh hasil eksperimen yang ada di master
for exp_id, result_data in dict_master_results.items():
# KONDISI RESUME: Jika threshold sudah ada di dict, skip eksperimen ini
if resume_from_backup and (exp_id in dict_best_thresholds):
print(f"⏩ Skip OOF-CV Threshold Finding untuk: {exp_id} (Sudah ada di backup)")
continue
print(f"⚙️ Mencari Threshold untuk: {exp_id}")
# Parsing Konfigurasi dari exp_id (Format: target_nstrat_fstrat_model)
parts = exp_id.split('_')
t_name, n_strat, f_strat, m_name = parts[0], parts[1], parts[2], parts[3]
# Ambil parameter terbaik & Class Model
best_params = result_data['best_params']
model_class = dict_model_classes[m_name]
# Atur deterministic parameter tambahan agar hasil konsisten
if model_class is RandomForestClassifier:
best_params['n_jobs'] = 1
best_params['random_state'] = 42
elif model_class is XGBClassifier:
best_params['n_jobs'] = 1
elif model_class is LGBMClassifier:
best_params['deterministic'] = True
best_params['force_col_wise'] = True
best_params['n_jobs'] = 1
best_params['verbose'] = -1
elif model_class is CatBoostClassifier:
best_params['thread_count'] = 1
best_params['random_seed'] = 42
best_params['verbose'] = False
# Data Raw whole train
X_train_final = dict_data_splits[t_name]['X_train'].copy()
y_train_final = dict_data_splits[t_name]['y_train'].copy()
# X_test_final = dict_data_splits[t_name]['X_test'].copy()
# y_test_final = dict_data_splits[t_name]['y_test'].copy()
# --- 4. REPLIKASI PREPROCESSING (SESUAI KONFIGURASI) ---
# A. Missing Indicator & Imputasi
if n_strat == 'impute':
# Tambah missing indicator (Train dan Test)
cols_to_flag = kolom_missing_indicator[t_name]
for col in cols_to_flag:
X_train_final[f"{col}_is_missing"] = X_train_final[col].isnull().astype(int)
# X_test_final[f"{col}_is_missing"] = X_test_final[col].isnull().astype(int)
# Terapkan Custom Imputation
fitur_cols = [c for c in X_train_final.columns if c not in metadata_cols]
X_train_final, _ = apply_custom_imputation(X_train_final, X_test_final, fitur_cols)
# Simpan metadata untuk nanti digabung kembali jika diperlukan (misal untuk PCA yang butuh fitur asli)
meta_tr = X_train_final[metadata_cols].reset_index(drop=True)
# meta_ts = X_test_final[metadata_cols].reset_index(drop=True)
# B. Strategi Fitur (Correlation Drop / PCA)
if f_strat == 'corr':
# Cari fitur korelasi tinggi HANYA di Train, drop di Train dan Test
cols_to_drop = get_low_corr_features_target_aware(X_train_final, y_train_final, threshold=0.85)
X_train_final = X_train_final.drop(columns=cols_to_drop)
# X_test_final = X_test_final.drop(columns=cols_to_drop)
elif f_strat == 'pca':
fitur_cols = [c for c in X_train_final.columns if c not in metadata_cols]
# Buang Fitur Konstan dulu Sebelum PCA (agar PCA tidak error)
constant_cols = [col for col in fitur_cols if X_train_final[col].nunique() <= 1]
if constant_cols:
X_train_final = X_train_final.drop(columns=constant_cols)
# X_test_final = X_test_final.drop(columns=constant_cols)
fitur_cols = [c for c in fitur_cols if c not in constant_cols]
scaler = StandardScaler()
pca = PCA(n_components=0.95, random_state=42)
# Fit Transform di Train, Transform di Test
X_tr_pca = pca.fit_transform(scaler.fit_transform(X_train_final[fitur_cols]))
# X_ts_pca = pca.transform(scaler.transform(X_test_final[fitur_cols]))
# Rekonstruksi DataFrame (tempelkan metadata lagi)
pca_cols = [f"PC_{i}" for i in range(X_tr_pca.shape[1])]
X_train_final = pd.concat([meta_tr, pd.DataFrame(X_tr_pca, columns=pca_cols)], axis=1)
# X_test_final = pd.concat([meta_ts, pd.DataFrame(X_ts_pca, columns=pca_cols)], axis=1)
# C. Drop Metadata sebelum masuk ke model
X_train_clean = X_train_final.drop(columns=metadata_cols, errors='ignore')
# X_test_clean = X_test_final.drop(columns=metadata_cols, errors='ignore')
# Ambil index custom CV dari data train (sebelum urutannya berubah)
cv_indices_eval = get_custom_cv_indices(X_train_final)
# Loop Manual untuk Out-of-Fold Probabilities (Mengatasi ValueError)
# Buat array kosong diisi NaN sepanjang data train
y_train_oof_probs = np.full(len(X_train_clean), np.nan)
for train_idx, val_idx in cv_indices_eval:
clone_model = model_class(**best_params)
# Potong data sesuai indeks Custom CV
X_tr_fold = X_train_clean.iloc[train_idx]
y_tr_fold = y_train_final.iloc[train_idx]
X_val_fold = X_train_clean.iloc[val_idx]
# Fit dan Prediksi pada lipatan ini
clone_model.fit(X_tr_fold, y_tr_fold)
y_train_oof_probs[val_idx] = clone_model.predict_proba(X_val_fold)[:, 1]
# Saring array, buang indeks yang tidak pernah masuk ke lipatan validasi (NaN)
valid_mask = ~np.isnan(y_train_oof_probs)
y_train_oof_probs_clean = y_train_oof_probs[valid_mask]
# Gunakan to_numpy() agar tidak ada masalah pergeseran index bawaan Pandas
y_train_true_clean = y_train_final.to_numpy()[valid_mask]
# Cari threshold optimal MURNI DARI DATA TRAIN
precisions_tr, recalls_tr, thresholds_tr = precision_recall_curve(y_train_true_clean, y_train_oof_probs_clean)
# precisions_tr, recalls_tr, thresholds_tr = precision_recall_curve(y_train_final, y_train_oof_probs)
target_recall = 0.70
# Proses cari best threshold
try:
optimal_idx = np.where(recalls_tr >= target_recall)[0][-1]
if optimal_idx >= len(thresholds_tr):
optimal_idx = len(thresholds_tr) - 1
best_threshold = thresholds_tr[optimal_idx]
except IndexError:
best_threshold = 0.5
dict_best_thresholds[exp_id] = best_threshold
with open(threshold_pkl_path, 'wb') as f:
pickle.dump(dict_best_thresholds, f)
print("\n=== PROSES PENCARIAN THRESHOLD SELESAI ===")Inisialisasi dict_master_results kosong di Google Colab.
Berhasil me-load data backup CPU (24 eksperimen).
Berhasil me-load data backup GPU (45 eksperimen).
Total seluruh eksperimen yang siap dicari threshold-nya: 69
✅ Berhasil me-load Model dan Threshold dari backup.
========== MEMULAI PROSES PENCARIAN THRESHOLD OPTIMAL (OOF-CV) ==========
⏩ Skip OOF-CV Threshold Finding untuk: ppk_keep_all_RandomForest (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_keep_corr_RandomForest (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_impute_all_RandomForest (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_impute_all_AdaBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_impute_corr_RandomForest (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_impute_corr_AdaBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_impute_pca_RandomForest (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_impute_pca_AdaBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_keep_all_RandomForest (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_keep_corr_RandomForest (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_impute_all_RandomForest (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_impute_all_AdaBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_impute_corr_RandomForest (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_impute_corr_AdaBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_impute_pca_RandomForest (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_impute_pca_AdaBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_keep_all_RandomForest (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_keep_corr_RandomForest (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_impute_all_RandomForest (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_impute_all_AdaBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_impute_corr_RandomForest (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_impute_corr_AdaBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_impute_pca_RandomForest (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_impute_pca_AdaBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_keep_all_XGBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_keep_all_LightGBM (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_keep_all_CatBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_keep_corr_XGBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_keep_corr_LightGBM (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_keep_corr_CatBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_impute_all_XGBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_impute_all_LightGBM (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_impute_all_CatBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_impute_corr_XGBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_impute_corr_LightGBM (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_impute_corr_CatBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_impute_pca_XGBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_impute_pca_LightGBM (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: ppk_impute_pca_CatBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_keep_all_XGBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_keep_all_LightGBM (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_keep_all_CatBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_keep_corr_XGBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_keep_corr_LightGBM (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_keep_corr_CatBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_impute_all_XGBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_impute_all_LightGBM (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_impute_all_CatBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_impute_corr_XGBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_impute_corr_LightGBM (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_impute_corr_CatBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_impute_pca_XGBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_impute_pca_LightGBM (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: negeq_impute_pca_CatBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_keep_all_XGBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_keep_all_LightGBM (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_keep_all_CatBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_keep_corr_XGBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_keep_corr_LightGBM (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_keep_corr_CatBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_impute_all_XGBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_impute_all_LightGBM (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_impute_all_CatBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_impute_corr_XGBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_impute_corr_LightGBM (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_impute_corr_CatBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_impute_pca_XGBoost (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_impute_pca_LightGBM (Sudah ada di backup)
⏩ Skip OOF-CV Threshold Finding untuk: conloss_impute_pca_CatBoost (Sudah ada di backup)
=== PROSES PENCARIAN THRESHOLD SELESAI ===
dict_best_thresholds{'ppk_keep_all_RandomForest': np.float64(0.1026076616985708),
'ppk_keep_corr_RandomForest': np.float64(0.09740742823844975),
'ppk_impute_all_RandomForest': np.float64(0.07135664391305577),
'ppk_impute_all_AdaBoost': np.float64(0.35292381769287745),
'ppk_impute_corr_RandomForest': np.float64(0.06876373959258938),
'ppk_impute_corr_AdaBoost': np.float64(0.2706962006722341),
'ppk_impute_pca_RandomForest': np.float64(0.11050689486787096),
'ppk_impute_pca_AdaBoost': np.float64(0.4295207244500669),
'negeq_keep_all_RandomForest': np.float64(0.059480461812911195),
'negeq_keep_corr_RandomForest': np.float64(0.05046650879984213),
'negeq_impute_all_RandomForest': np.float64(0.008311750428088816),
'negeq_impute_all_AdaBoost': np.float64(0.3452967440652882),
'negeq_impute_corr_RandomForest': np.float64(0.008418424698410443),
'negeq_impute_corr_AdaBoost': np.float64(0.39055291556829064),
'negeq_impute_pca_RandomForest': np.float64(0.009023368159728705),
'negeq_impute_pca_AdaBoost': np.float64(0.24982568084045162),
'conloss_keep_all_RandomForest': np.float64(0.10313216101867166),
'conloss_keep_corr_RandomForest': np.float64(0.09407666229288404),
'conloss_impute_all_RandomForest': np.float64(0.07689151930468276),
'conloss_impute_all_AdaBoost': np.float64(0.16221623091726856),
'conloss_impute_corr_RandomForest': np.float64(0.07632841380226682),
'conloss_impute_corr_AdaBoost': np.float64(0.18046469323195244),
'conloss_impute_pca_RandomForest': np.float64(0.07417635658914729),
'conloss_impute_pca_AdaBoost': np.float64(0.3950771133294802),
'ppk_keep_all_XGBoost': np.float64(0.015902331098914146),
'ppk_keep_all_LightGBM': np.float64(0.0025382668280570612),
'ppk_keep_all_CatBoost': np.float64(0.01813688630925825),
'ppk_keep_corr_XGBoost': np.float64(0.03040345199406147),
'ppk_keep_corr_LightGBM': np.float64(0.008526471325700261),
'ppk_keep_corr_CatBoost': np.float64(0.009480744680310961),
'ppk_impute_all_XGBoost': np.float64(0.012296569533646107),
'ppk_impute_all_LightGBM': np.float64(3.415264436267724e-05),
'ppk_impute_all_CatBoost': np.float64(0.004135391656362598),
'ppk_impute_corr_XGBoost': np.float64(0.07499814033508301),
'ppk_impute_corr_LightGBM': np.float64(0.004446654234049561),
'ppk_impute_corr_CatBoost': np.float64(0.00401453094057985),
'ppk_impute_pca_XGBoost': np.float64(0.06325820833444595),
'ppk_impute_pca_LightGBM': np.float64(0.001057382472860725),
'ppk_impute_pca_CatBoost': np.float64(0.036930656688580636),
'negeq_keep_all_XGBoost': np.float64(0.011342362500727177),
'negeq_keep_all_LightGBM': np.float64(0.0008399380078398809),
'negeq_keep_all_CatBoost': np.float64(0.012688657018005924),
'negeq_keep_corr_XGBoost': np.float64(0.01108479779213667),
'negeq_keep_corr_LightGBM': np.float64(0.00013188201136054325),
'negeq_keep_corr_CatBoost': np.float64(0.007393605695564846),
'negeq_impute_all_XGBoost': np.float64(0.008673484437167645),
'negeq_impute_all_LightGBM': np.float64(2.6023672887597216e-05),
'negeq_impute_all_CatBoost': np.float64(0.020205113489548995),
'negeq_impute_corr_XGBoost': np.float64(0.00426494050770998),
'negeq_impute_corr_LightGBM': np.float64(4.513603303341643e-07),
'negeq_impute_corr_CatBoost': np.float64(0.11106818494619851),
'negeq_impute_pca_XGBoost': np.float64(0.008357434533536434),
'negeq_impute_pca_LightGBM': np.float64(0.007491902745421547),
'negeq_impute_pca_CatBoost': np.float64(0.027811120171266263),
'conloss_keep_all_XGBoost': np.float64(0.06068401038646698),
'conloss_keep_all_LightGBM': np.float64(0.023755998304761973),
'conloss_keep_all_CatBoost': np.float64(0.04315520980250645),
'conloss_keep_corr_XGBoost': np.float64(0.06285607069730759),
'conloss_keep_corr_LightGBM': np.float64(0.0026665631522638364),
'conloss_keep_corr_CatBoost': np.float64(0.05833520459810593),
'conloss_impute_all_XGBoost': np.float64(0.07634969055652618),
'conloss_impute_all_LightGBM': np.float64(0.07720331008273537),
'conloss_impute_all_CatBoost': np.float64(0.0819846305283936),
'conloss_impute_corr_XGBoost': np.float64(0.07830634713172913),
'conloss_impute_corr_LightGBM': np.float64(0.07801975274385842),
'conloss_impute_corr_CatBoost': np.float64(0.08380675600146752),
'conloss_impute_pca_XGBoost': np.float64(0.024094466120004654),
'conloss_impute_pca_LightGBM': np.float64(0.0014064269861669264),
'conloss_impute_pca_CatBoost': np.float64(0.07269169798521348)}
Final Test (Fit & Predict)
# Load atau inisialisasi backup model final
dict_fitted_models = {}
model_pkl_path = save_path + 'fitted_models_skripsi.pkl'
print(f"Total seluruh eksperimen yang siap dievaluasi: {len(dict_master_results)}")
# if resume_from_backup:
# try:
# with open(model_pkl_path, 'rb') as f:
# dict_fitted_models = pickle.load(f)
# print(f"✅ Berhasil me-load {len(dict_fitted_models)} Model Ter-fit dari backup.")
# except FileNotFoundError:
# print("⚠️ File backup model final tidak ditemukan. Akan melakukan fitting dari awal...")
# # resume_from_backup = False
# List untuk menyimpan rekapan performa Test dari SEMUA eksperimen
final_evaluation_results = []
# Mnampung metrik kurva PR per target
pr_auc_plot_data = {'ppk': [], 'negeq': [], 'conloss': []}
# dict menampung detail prediksi tiap baris data
dict_detailed_predictions = {}
print("========== MEMULAI EVALUASI FINAL SELURUH EKSPERIMEN ==========")
# Looping ke seluruh hasil eksperimen yang ada di master
for exp_id, result_data in dict_master_results.items():
print(f"\nMengevaluasi Hasil: {exp_id}")
# Parsing Konfigurasi dari exp_id (Format: target_nstrat_fstrat_model)
parts = exp_id.split('_')
t_name, n_strat, f_strat, m_name = parts[0], parts[1], parts[2], parts[3]
# Ambil parameter terbaik, threshold terbaik & Class Model
best_params = result_data['best_params']
model_class = dict_model_classes[m_name]
best_threshold = dict_best_thresholds[exp_id]
# Atur deterministic parameter tambahan agar hasil konsisten
if model_class is RandomForestClassifier:
best_params['n_jobs'] = 1
best_params['random_state'] = 42
elif model_class is XGBClassifier:
best_params['n_jobs'] = 1
elif model_class is LGBMClassifier:
best_params['deterministic'] = True
best_params['force_col_wise'] = True
best_params['n_jobs'] = 1
best_params['verbose'] = -1
elif model_class is CatBoostClassifier:
best_params['thread_count'] = 1
best_params['random_seed'] = 42
best_params['verbose'] = False
# 3. Tarik Data Raw (Train utuh dan Test masa depan)
X_train_final = dict_data_splits[t_name]['X_train'].copy()
y_train_final = dict_data_splits[t_name]['y_train'].copy()
X_test_final = dict_data_splits[t_name]['X_test'].copy()
y_test_final = dict_data_splits[t_name]['y_test'].copy()
# --- 4. REPLIKASI PREPROCESSING (SESUAI KONFIGURASI) ---
# A. Missing Indicator & Imputasi
if n_strat == 'impute':
# Tambah missing indicator (Train dan Test)
cols_to_flag = kolom_missing_indicator[t_name]
for col in cols_to_flag:
X_train_final[f"{col}_is_missing"] = X_train_final[col].isnull().astype(int)
X_test_final[f"{col}_is_missing"] = X_test_final[col].isnull().astype(int)
# Terapkan Custom Imputation
fitur_cols = [c for c in X_train_final.columns if c not in metadata_cols]
X_train_final, X_test_final = apply_custom_imputation(X_train_final, X_test_final, fitur_cols)
# Simpan metadata untuk nanti digabung kembali jika diperlukan (misal untuk PCA yang butuh fitur asli)
meta_tr = X_train_final[metadata_cols].reset_index(drop=True)
meta_ts = X_test_final[metadata_cols].reset_index(drop=True) # cek kepake di cell ini atau ga
# B. Strategi Fitur (Correlation Drop / PCA)
if f_strat == 'corr':
# Cari fitur korelasi tinggi HANYA di Train, drop di Train dan Test
cols_to_drop = get_low_corr_features_target_aware(X_train_final, y_train_final, threshold=0.85)
X_train_final = X_train_final.drop(columns=cols_to_drop)
X_test_final = X_test_final.drop(columns=cols_to_drop)
elif f_strat == 'pca':
fitur_cols = [c for c in X_train_final.columns if c not in metadata_cols]
# Buang Fitur Konstan dulu Sebelum PCA (agar PCA tidak error)
constant_cols = [col for col in fitur_cols if X_train_final[col].nunique() <= 1]
if constant_cols:
X_train_final = X_train_final.drop(columns=constant_cols)
X_test_final = X_test_final.drop(columns=constant_cols)
fitur_cols = [c for c in fitur_cols if c not in constant_cols]
scaler = StandardScaler()
pca = PCA(n_components=0.95, random_state=42)
# Fit Transform di Train, Transform di Test
X_tr_pca = pca.fit_transform(scaler.fit_transform(X_train_final[fitur_cols]))
X_ts_pca = pca.transform(scaler.transform(X_test_final[fitur_cols]))
# Rekonstruksi DataFrame (tempelkan metadata lagi)
pca_cols = [f"PC_{i}" for i in range(X_tr_pca.shape[1])]
X_train_final = pd.concat([meta_tr, pd.DataFrame(X_tr_pca, columns=pca_cols)], axis=1)
X_test_final = pd.concat([meta_ts, pd.DataFrame(X_ts_pca, columns=pca_cols)], axis=1)
# C. Drop Metadata sebelum masuk ke model
X_train_clean = X_train_final.drop(columns=metadata_cols, errors='ignore')
X_test_clean = X_test_final.drop(columns=metadata_cols, errors='ignore')
print(f"⚙️ Melakukan Fitting Model Final ke Whole Train Set...")
# Latih model final sesungguhnya ke SELURUH data Train
model = model_class(**best_params)
model.fit(X_train_clean, y_train_final)
# simpan model dan threshold
dict_fitted_models[exp_id] = model
with open(model_pkl_path, 'wb') as f:
pickle.dump(dict_fitted_models, f)
# Prediksi probabilitas pada Test Set
y_pred_probs = model.predict_proba(X_test_clean)[:, 1]
# Hitung Test PR-AUC
test_pr_auc = average_precision_score(y_test_final, y_pred_probs)
# Terapkan Threshold yang sudah didapat sebelumnya
y_pred_final = (y_pred_probs >= best_threshold).astype(int)
# Hitung Performa Real di Dunia Nyata
achieved_recall = recall_score(y_test_final, y_pred_final)
expected_precision = precision_score(y_test_final, y_pred_final, zero_division=0)
cm = confusion_matrix(y_test_final, y_pred_final)
tn, fp, fn, tp = cm.ravel()
# [METRIK TAMBAHAN BARU]
test_f1 = f1_score(y_test_final, y_pred_final, zero_division=0)
test_accuracy = accuracy_score(y_test_final, y_pred_final)
test_specificity = tn / (tn + fp) if (tn + fp) > 0 else 0.0
# Simpan detail prediksi dan metadata ke dalam keranjang
dict_detailed_predictions[exp_id] = {
'metadata': meta_ts.copy(),
'y_true': y_test_final.reset_index(drop=True),
'y_pred': pd.Series(y_pred_final),
'y_prob': pd.Series(y_pred_probs),
'X_test_features': X_test_clean.reset_index(drop=True) # simpan fitur
}
# --- 7. SIMPAN HASIL KE REKAPAN ---
final_evaluation_results.append({
'Experiment_ID': exp_id,
'Target': t_name,
'Null_Strat': n_strat,
'Feat_Strat': f_strat,
'Model': m_name,
'Optuna_PR_AUC': result_data['best_cv_pr_auc'], # Dari Optuna
'Test_PR_AUC': test_pr_auc, # Di Test Set riil
'Best_Threshold': best_threshold,
'Test_Recall': achieved_recall,
'Test_Precision': expected_precision,
'Confusion_Matrix': cm.tolist(),
'Test_Specificity': test_specificity, # Masuk ke tabel
'Test_F1': test_f1, # Masuk ke tabel
'Test_Accuracy': test_accuracy, # Masuk ke tabel
})
# Simpan kurva Test Set murni untuk plotting (cuma untuk visualisasi kurva, bukan untuk cari threshold)
precisions_test, recalls_test, _ = precision_recall_curve(y_test_final, y_pred_probs)
# Simpan metrik kurva untuk diplot di luar loop
if t_name in pr_auc_plot_data:
pr_auc_plot_data[t_name].append({
'label': f"{n_strat}_{f_strat}_{m_name} (AUC={test_pr_auc:.3f})",
'recalls': recalls_test,
'precisions': precisions_test,
'auc': test_pr_auc
})
# Jadikan DataFrame agar mudah diurutkan dan dianalisis
df_final_summary = pd.DataFrame(final_evaluation_results)
df_final_summary = df_final_summary.sort_values(by=['Target', 'Test_PR_AUC'], ascending=[True, False]).reset_index(drop=True)
# print("\n========== REKAPAN PERINGKAT MODEL TERBAIK TIAP TARGET ==========")
# display(df_final_summary.head(3))
# # Hanya overwrite/simpan ulang jika kita sedang tidak menggunakan backup (artinya ada eksperimen baru yang di-fit)
# if not resume_from_backup:
# with open(model_pkl_path, 'wb') as f:
# pickle.dump(dict_fitted_models, f)
# with open(threshold_pkl_path, 'wb') as f:
# pickle.dump(dict_best_thresholds, f)
# Simpan Dataframe Evaluasi Final ke CSV di GDrive
csv_path = save_path + 'hasil_evaluasi_final_skripsi.csv'
df_final_summary.to_csv(csv_path, index=False)
print(f"\n✅ Evaluasi Selesai. Hasil evaluasi final diekspor ke: {csv_path}")Total seluruh eksperimen yang siap dievaluasi: 69
========== MEMULAI EVALUASI FINAL SELURUH EKSPERIMEN ==========
Mengevaluasi Hasil: ppk_keep_all_RandomForest
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_keep_corr_RandomForest
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_impute_all_RandomForest
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_impute_all_AdaBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_impute_corr_RandomForest
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_impute_corr_AdaBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_impute_pca_RandomForest
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_impute_pca_AdaBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_keep_all_RandomForest
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_keep_corr_RandomForest
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_impute_all_RandomForest
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_impute_all_AdaBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_impute_corr_RandomForest
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_impute_corr_AdaBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_impute_pca_RandomForest
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_impute_pca_AdaBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_keep_all_RandomForest
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_keep_corr_RandomForest
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_impute_all_RandomForest
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_impute_all_AdaBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_impute_corr_RandomForest
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_impute_corr_AdaBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_impute_pca_RandomForest
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_impute_pca_AdaBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_keep_all_XGBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_keep_all_LightGBM
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_keep_all_CatBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_keep_corr_XGBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_keep_corr_LightGBM
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_keep_corr_CatBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_impute_all_XGBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_impute_all_LightGBM
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_impute_all_CatBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_impute_corr_XGBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_impute_corr_LightGBM
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_impute_corr_CatBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_impute_pca_XGBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_impute_pca_LightGBM
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: ppk_impute_pca_CatBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_keep_all_XGBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_keep_all_LightGBM
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_keep_all_CatBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_keep_corr_XGBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_keep_corr_LightGBM
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_keep_corr_CatBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_impute_all_XGBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_impute_all_LightGBM
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_impute_all_CatBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_impute_corr_XGBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_impute_corr_LightGBM
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_impute_corr_CatBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_impute_pca_XGBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_impute_pca_LightGBM
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: negeq_impute_pca_CatBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_keep_all_XGBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_keep_all_LightGBM
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_keep_all_CatBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_keep_corr_XGBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_keep_corr_LightGBM
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_keep_corr_CatBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_impute_all_XGBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_impute_all_LightGBM
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_impute_all_CatBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_impute_corr_XGBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_impute_corr_LightGBM
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_impute_corr_CatBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_impute_pca_XGBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_impute_pca_LightGBM
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
Mengevaluasi Hasil: conloss_impute_pca_CatBoost
⚙️ Melakukan Fitting Model Final ke Whole Train Set...
✅ Evaluasi Selesai. Hasil evaluasi final diekspor ke: /content/drive/MyDrive/SKRIPSI/Dataset/hasil_evaluasi_final_skripsi.csv
# resume_fit_from_backup = True
# # 1. Inisialisasi dictionary
# if platform == "gcolab":
# dict_master_results = {} # Mulai dengan dictionary kosong di Colab
# print("Inisialisasi dict_master_results kosong di Google Colab.")
# for c_type in ["cpu","gpu"]:
# file_path = prefix_path + f'pickle/eksperimen_skripsi_backup_{c_type}.pkl'
# if os.path.exists(file_path):
# with open(file_path, 'rb') as f:
# data = pickle.load(f)
# dict_master_results.update(data) # Menggabungkan dictionary
# print(f"Berhasil me-load data backup {c_type.upper()} ({len(data)} eksperimen).")
# else:
# print(f"File backup {c_type.upper()} tidak ditemukan di: {file_path}")
# elif platform == "kaggle": # Kaggle
# # kalau pakai cpu yang dibuka gpu karena cpu sudah ada di var
# # begitu pula sebaliknya
# if compute_type == 'cpu':
# file_path = prefix_path + f'pickle/eksperimen_skripsi_backup_gpu.pkl'
# elif compute_type == 'gpu':
# file_path = prefix_path + f'pickle/eksperimen_skripsi_backup_cpu.pkl'
# if os.path.exists(file_path):
# with open(file_path, 'rb') as f:
# data = pickle.load(f)
# dict_master_results.update(data) # Menggabungkan dictionary
# print(f"Berhasil me-load backup SELAIN {compute_type.upper()} dengan {len(data)} eksperimen.")
# else:
# print(f"File backup {compute_type.upper()} tidak ditemukan di: {file_path}")
# print(f"Total seluruh eksperimen yang siap dievaluasi: {len(dict_master_results)}")
# # Mapping string nama model ke Class Modelnya
# dict_model_classes = {
# 'RandomForest': RandomForestClassifier,
# 'XGBoost': XGBClassifier,
# 'LightGBM': LGBMClassifier,
# 'AdaBoost': AdaBoostClassifier,
# 'CatBoost': CatBoostClassifier
# }
# # List untuk menyimpan rekapan performa Test dari SEMUA eksperimen
# final_evaluation_results = []
# # Mnampung metrik kurva PR per target
# pr_auc_plot_data = {'ppk': [], 'negeq': [], 'conloss': []}
# # dict menampung detail prediksi tiap baris data
# dict_detailed_predictions = {}
# # dict untuk menyimpan model yang udah difit
# dict_fitted_models = {}
# # dict untuk menyimpan best threshold dari OOF predict
# dict_best_thresholds = {}
# model_pkl_path = save_path + 'fitted_models_skripsi.pkl'
# threshold_pkl_path = save_path + 'best_thresholds_skripsi.pkl'
# if resume_from_backup:
# try:
# with open(model_pkl_path, 'rb') as f:
# dict_fitted_models = pickle.load(f)
# with open(threshold_pkl_path, 'rb') as f:
# dict_best_thresholds = pickle.load(f)
# print("✅ Berhasil me-load Model dan Threshold dari backup.")
# except FileNotFoundError:
# print("⚠️ File backup model/threshold tidak ditemukan. Memaksa fit ulang...")
# resume_from_backup = False
# print("========== MEMULAI EVALUASI FINAL SELURUH EKSPERIMEN ==========")
# # Looping ke seluruh hasil eksperimen yang ada di master
# for exp_id, result_data in dict_master_results.items():
# print(f"\nMengevaluasi: {exp_id}")
# # 1. Parsing Konfigurasi dari exp_id (Format: target_nstrat_fstrat_model)
# parts = exp_id.split('_')
# t_name, n_strat, f_strat, m_name = parts[0], parts[1], parts[2], parts[3]
# # 2. Ambil parameter terbaik & Class Model
# best_params = result_data['best_params']
# model_class = dict_model_classes[m_name]
# # Atur deterministic parameter tambahan agar hasil konsisten
# if model_class is RandomForestClassifier:
# best_params['n_jobs'] = 1
# best_params['random_state'] = 42
# elif model_class is XGBClassifier:
# best_params['n_jobs'] = 1
# elif model_class is LGBMClassifier:
# best_params['deterministic'] = True
# best_params['force_col_wise'] = True
# best_params['n_jobs'] = 1
# elif model_class is CatBoostClassifier:
# best_params['thread_count'] = 1
# best_params['random_seed'] = 42
# # 3. Tarik Data Raw (Train utuh dan Test masa depan)
# X_train_final = dict_data_splits[t_name]['X_train'].copy()
# y_train_final = dict_data_splits[t_name]['y_train'].copy()
# X_test_final = dict_data_splits[t_name]['X_test'].copy()
# y_test_final = dict_data_splits[t_name]['y_test'].copy()
# # --- 4. REPLIKASI PREPROCESSING (SESUAI KONFIGURASI) ---
# # A. Missing Indicator & Imputasi
# if n_strat == 'impute':
# # Tambah missing indicator (Train dan Test)
# cols_to_flag = kolom_missing_indicator[t_name]
# for col in cols_to_flag:
# X_train_final[f"{col}_is_missing"] = X_train_final[col].isnull().astype(int)
# X_test_final[f"{col}_is_missing"] = X_test_final[col].isnull().astype(int)
# # Terapkan Custom Imputation
# fitur_cols = [c for c in X_train_final.columns if c not in metadata_cols]
# X_train_final, X_test_final = apply_custom_imputation(X_train_final, X_test_final, fitur_cols)
# # Simpan metadata untuk nanti digabung kembali jika diperlukan (misal untuk PCA yang butuh fitur asli)
# meta_tr = X_train_final[metadata_cols].reset_index(drop=True)
# meta_ts = X_test_final[metadata_cols].reset_index(drop=True)
# # B. Strategi Fitur (Correlation Drop / PCA)
# if f_strat == 'corr':
# # Cari fitur korelasi tinggi HANYA di Train, drop di Train dan Test
# cols_to_drop = get_low_corr_features_target_aware(X_train_final, y_train_final, threshold=0.85)
# X_train_final = X_train_final.drop(columns=cols_to_drop)
# X_test_final = X_test_final.drop(columns=cols_to_drop)
# elif f_strat == 'pca':
# fitur_cols = [c for c in X_train_final.columns if c not in metadata_cols]
# # Buang Fitur Konstan dulu Sebelum PCA (agar PCA tidak error)
# constant_cols = [col for col in fitur_cols if X_train_final[col].nunique() <= 1]
# if constant_cols:
# X_train_final = X_train_final.drop(columns=constant_cols)
# X_test_final = X_test_final.drop(columns=constant_cols)
# fitur_cols = [c for c in fitur_cols if c not in constant_cols]
# scaler = StandardScaler()
# pca = PCA(n_components=0.95, random_state=42)
# # Fit Transform di Train, Transform di Test
# X_tr_pca = pca.fit_transform(scaler.fit_transform(X_train_final[fitur_cols]))
# X_ts_pca = pca.transform(scaler.transform(X_test_final[fitur_cols]))
# # Rekonstruksi DataFrame (tempelkan metadata lagi)
# pca_cols = [f"PC_{i}" for i in range(X_tr_pca.shape[1])]
# X_train_final = pd.concat([meta_tr, pd.DataFrame(X_tr_pca, columns=pca_cols)], axis=1)
# X_test_final = pd.concat([meta_ts, pd.DataFrame(X_ts_pca, columns=pca_cols)], axis=1)
# # C. Drop Metadata sebelum masuk ke model
# X_train_clean = X_train_final.drop(columns=metadata_cols, errors='ignore')
# X_test_clean = X_test_final.drop(columns=metadata_cols, errors='ignore')
# # =======================================================
# # 2. BLOK BYPASS (CEK APAKAH PERLU FIT ULANG ATAU TIDAK)
# # =======================================================
# if resume_from_backup and (exp_id in dict_fitted_models) and (exp_id in dict_best_thresholds):
# print(f"⏩ Bypass CV & Fit. Menggunakan model dan threshold dari backup.")
# # Langsung tarik dari dictionary hasil load pickle
# model = dict_fitted_models[exp_id]
# best_threshold = dict_best_thresholds[exp_id]
# else:
# print(f" ⚙️ Melakukan Cross-Validation & Fitting Model...")
# # Ambil index custom CV dari data train (sebelum urutannya berubah)
# cv_indices_eval = get_custom_cv_indices(X_train_final)
# # Loop Manual untuk Out-of-Fold Probabilities (Mengatasi ValueError)
# # Buat array kosong diisi NaN sepanjang data train
# y_train_oof_probs = np.full(len(X_train_clean), np.nan)
# for train_idx, val_idx in cv_indices_eval:
# clone_model = model_class(**best_params)
# # Potong data sesuai indeks Custom CV
# X_tr_fold = X_train_clean.iloc[train_idx]
# y_tr_fold = y_train_final.iloc[train_idx]
# X_val_fold = X_train_clean.iloc[val_idx]
# # Fit dan Prediksi pada lipatan ini
# clone_model.fit(X_tr_fold, y_tr_fold)
# y_train_oof_probs[val_idx] = clone_model.predict_proba(X_val_fold)[:, 1]
# # Saring array, buang indeks yang tidak pernah masuk ke lipatan validasi (NaN)
# valid_mask = ~np.isnan(y_train_oof_probs)
# y_train_oof_probs_clean = y_train_oof_probs[valid_mask]
# # Gunakan to_numpy() agar tidak ada masalah pergeseran index bawaan Pandas
# y_train_true_clean = y_train_final.to_numpy()[valid_mask]
# # Cari threshold optimal MURNI DARI DATA TRAIN
# precisions_tr, recalls_tr, thresholds_tr = precision_recall_curve(y_train_true_clean, y_train_oof_probs_clean)
# # precisions_tr, recalls_tr, thresholds_tr = precision_recall_curve(y_train_final, y_train_oof_probs)
# target_recall = 0.70
# # Proses cari best threshold
# try:
# optimal_idx = np.where(recalls_tr >= target_recall)[0][-1]
# if optimal_idx >= len(thresholds_tr):
# optimal_idx = len(thresholds_tr) - 1
# best_threshold = thresholds_tr[optimal_idx]
# except IndexError:
# best_threshold = 0.5
# # Latih model final sesungguhnya ke SELURUH data Train
# model = model_class(**best_params)
# model.fit(X_train_clean, y_train_final)
# # simpan model dan threshold
# dict_fitted_models[exp_id] = model
# dict_best_thresholds[exp_id] = best_threshold
# with open(model_pkl_path, 'wb') as f:
# pickle.dump(dict_fitted_models, f)
# with open(threshold_pkl_path, 'wb') as f:
# pickle.dump(dict_best_thresholds, f)
# # Prediksi probabilitas pada Test Set
# y_pred_probs = model.predict_proba(X_test_clean)[:, 1]
# # Hitung Test PR-AUC
# test_pr_auc = average_precision_score(y_test_final, y_pred_probs)
# # Terapkan Threshold yang sudah didapat sebelumnya
# y_pred_final = (y_pred_probs >= best_threshold).astype(int)
# # Hitung Performa Real di Dunia Nyata
# achieved_recall = recall_score(y_test_final, y_pred_final)
# expected_precision = precision_score(y_test_final, y_pred_final, zero_division=0)
# cm = confusion_matrix(y_test_final, y_pred_final)
# tn, fp, fn, tp = cm.ravel()
# # [METRIK TAMBAHAN BARU]
# test_f1 = f1_score(y_test_final, y_pred_final, zero_division=0)
# test_accuracy = accuracy_score(y_test_final, y_pred_final)
# test_specificity = tn / (tn + fp) if (tn + fp) > 0 else 0.0
# # Simpan detail prediksi dan metadata ke dalam keranjang
# dict_detailed_predictions[exp_id] = {
# 'metadata': meta_ts.copy(),
# 'y_true': y_test_final.reset_index(drop=True),
# 'y_pred': pd.Series(y_pred_final),
# 'y_prob': pd.Series(y_pred_probs),
# 'X_test_features': X_test_clean.reset_index(drop=True) # simpan fitur
# }
# # --- 7. SIMPAN HASIL KE REKAPAN ---
# final_evaluation_results.append({
# 'Experiment_ID': exp_id,
# 'Target': t_name,
# 'Null_Strat': n_strat,
# 'Feat_Strat': f_strat,
# 'Model': m_name,
# 'Optuna_PR_AUC': result_data['best_cv_pr_auc'], # Dari Optuna
# 'Test_PR_AUC': test_pr_auc, # Di Test Set riil
# 'Best_Threshold': best_threshold,
# 'Test_Recall': achieved_recall,
# 'Test_Precision': expected_precision,
# 'Confusion_Matrix': cm.tolist(),
# 'Test_Specificity': test_specificity, # Masuk ke tabel
# 'Test_F1': test_f1, # Masuk ke tabel
# 'Test_Accuracy': test_accuracy, # Masuk ke tabel
# })
# # Simpan kurva Test Set murni untuk plotting (cuma untuk visualisasi kurva, bukan untuk cari threshold)
# precisions_test, recalls_test, _ = precision_recall_curve(y_test_final, y_pred_probs)
# # Simpan metrik kurva untuk diplot di luar loop
# if t_name in pr_auc_plot_data:
# pr_auc_plot_data[t_name].append({
# 'label': f"{n_strat}_{f_strat}_{m_name} (AUC={test_pr_auc:.3f})",
# 'recalls': recalls_test,
# 'precisions': precisions_test,
# 'auc': test_pr_auc
# })
# # Jadikan DataFrame agar mudah diurutkan dan dianalisis
# df_final_summary = pd.DataFrame(final_evaluation_results)
# df_final_summary = df_final_summary.sort_values(by=['Target', 'Test_Precision'], ascending=[True, False]).reset_index(drop=True)
# print("\n========== REKAPAN PERINGKAT MODEL TERBAIK TIAP TARGET ==========")
# display(df_final_summary.head(3))
# # # Hanya overwrite/simpan ulang jika kita sedang tidak menggunakan backup (artinya ada eksperimen baru yang di-fit)
# # if not resume_from_backup:
# # with open(model_pkl_path, 'wb') as f:
# # pickle.dump(dict_fitted_models, f)
# # with open(threshold_pkl_path, 'wb') as f:
# # pickle.dump(dict_best_thresholds, f)
# # Simpan Dataframe Evaluasi Final ke CSV di GDrive
# csv_path = save_path + 'hasil_evaluasi_final_skripsi.csv'
# df_final_summary.to_csv(csv_path, index=False)
# print(f"\n✅ Proses Selesai. Hasil evaluasi final diekspor ke: {csv_path}")
# if not resume_from_backup:
# print(f"✅ Model & Threshold disimpan di: {model_pkl_path} dan {threshold_pkl_path}")# # 1. Inisialisasi dictionary
# if platform == "gcolab":
# dict_master_results = {} # Mulai dengan dictionary kosong di Colab
# print("Inisialisasi dict_master_results kosong di Google Colab.")
# for c_type in ["cpu","gpu"]:
# file_path = prefix_path + f'pickle/eksperimen_skripsi_backup_{c_type}.pkl'
# if os.path.exists(file_path):
# with open(file_path, 'rb') as f:
# data = pickle.load(f)
# dict_master_results.update(data) # Menggabungkan dictionary
# print(f"Berhasil me-load data backup {c_type.upper()} ({len(data)} eksperimen).")
# else:
# print(f"File backup {c_type.upper()} tidak ditemukan di: {file_path}")
# elif platform == "kaggle": # Kaggle
# # kalau pakai cpu yang dibuka gpu karena cpu sudah ada di var
# # begitu pula sebaliknya
# if compute_type == 'cpu':
# file_path = prefix_path + f'pickle/eksperimen_skripsi_backup_gpu.pkl'
# elif compute_type == 'gpu':
# file_path = prefix_path + f'pickle/eksperimen_skripsi_backup_cpu.pkl'
# if os.path.exists(file_path):
# with open(file_path, 'rb') as f:
# data = pickle.load(f)
# dict_master_results.update(data) # Menggabungkan dictionary
# print(f"Berhasil me-load backup SELAIN {compute_type.upper()} dengan {len(data)} eksperimen.")
# else:
# print(f"File backup {compute_type.upper()} tidak ditemukan di: {file_path}")
# print(f"Total seluruh eksperimen yang siap dievaluasi: {len(dict_master_results)}")
# # Mapping string nama model ke Class Modelnya
# dict_model_classes = {
# 'RandomForest': RandomForestClassifier,
# 'XGBoost': XGBClassifier,
# 'LightGBM': LGBMClassifier,
# 'AdaBoost': AdaBoostClassifier,
# 'CatBoost': CatBoostClassifier
# }
# # List untuk menyimpan rekapan performa Test dari SEMUA eksperimen
# final_evaluation_results = []
# # Mnampung metrik kurva PR per target
# pr_auc_plot_data = {'ppk': [], 'negeq': [], 'conloss': []}
# # dict menampung detail prediksi tiap baris data
# dict_detailed_predictions = {}
# # dict untuk menyimpan model yang udah difit
# dict_fitted_models = {}
# print("========== MEMULAI EVALUASI FINAL SELURUH EKSPERIMEN ==========")
# # Looping ke seluruh hasil eksperimen yang ada di master
# for exp_id, result_data in dict_master_results.items():
# print(f"\nMengevaluasi: {exp_id}")
# # 1. Parsing Konfigurasi dari exp_id (Format: target_nstrat_fstrat_model)
# parts = exp_id.split('_')
# t_name, n_strat, f_strat, m_name = parts[0], parts[1], parts[2], parts[3]
# # 2. Ambil parameter terbaik & Class Model
# best_params = result_data['best_params']
# model_class = dict_model_classes[m_name]
# # Atur deterministic parameter tambahan agar hasil konsisten
# if model_class is RandomForestClassifier:
# best_params['n_jobs'] = 1
# best_params['random_state'] = 42
# elif model_class is XGBClassifier:
# best_params['n_jobs'] = 1
# elif model_class is LGBMClassifier:
# best_params['deterministic'] = True
# best_params['force_col_wise'] = True
# best_params['n_jobs'] = 1
# elif model_class is CatBoostClassifier:
# best_params['thread_count'] = 1
# best_params['random_seed'] = 42
# # 3. Tarik Data Raw (Train utuh dan Test masa depan)
# X_train_final = dict_data_splits[t_name]['X_train'].copy()
# y_train_final = dict_data_splits[t_name]['y_train'].copy()
# X_test_final = dict_data_splits[t_name]['X_test'].copy()
# y_test_final = dict_data_splits[t_name]['y_test'].copy()
# # --- 4. REPLIKASI PREPROCESSING (SESUAI KONFIGURASI) ---
# # A. Missing Indicator & Imputasi
# if n_strat == 'impute':
# # Tambah missing indicator (Train dan Test)
# cols_to_flag = kolom_missing_indicator[t_name]
# for col in cols_to_flag:
# X_train_final[f"{col}_is_missing"] = X_train_final[col].isnull().astype(int)
# X_test_final[f"{col}_is_missing"] = X_test_final[col].isnull().astype(int)
# # Terapkan Custom Imputation
# fitur_cols = [c for c in X_train_final.columns if c not in metadata_cols]
# X_train_final, X_test_final = apply_custom_imputation(X_train_final, X_test_final, fitur_cols)
# # Simpan metadata untuk nanti digabung kembali jika diperlukan (misal untuk PCA yang butuh fitur asli)
# meta_tr = X_train_final[metadata_cols].reset_index(drop=True)
# meta_ts = X_test_final[metadata_cols].reset_index(drop=True)
# # B. Strategi Fitur (Correlation Drop / PCA)
# if f_strat == 'corr':
# # Cari fitur korelasi tinggi HANYA di Train, drop di Train dan Test
# cols_to_drop = get_low_corr_features_target_aware(X_train_final, y_train_final, threshold=0.85)
# X_train_final = X_train_final.drop(columns=cols_to_drop)
# X_test_final = X_test_final.drop(columns=cols_to_drop)
# elif f_strat == 'pca':
# fitur_cols = [c for c in X_train_final.columns if c not in metadata_cols]
# # Buang Fitur Konstan dulu Sebelum PCA (agar PCA tidak error)
# constant_cols = [col for col in fitur_cols if X_train_final[col].nunique() <= 1]
# if constant_cols:
# X_train_final = X_train_final.drop(columns=constant_cols)
# X_test_final = X_test_final.drop(columns=constant_cols)
# fitur_cols = [c for c in fitur_cols if c not in constant_cols]
# scaler = StandardScaler()
# pca = PCA(n_components=0.95, random_state=42)
# # Fit Transform di Train, Transform di Test
# X_tr_pca = pca.fit_transform(scaler.fit_transform(X_train_final[fitur_cols]))
# X_ts_pca = pca.transform(scaler.transform(X_test_final[fitur_cols]))
# # Rekonstruksi DataFrame (tempelkan metadata lagi)
# pca_cols = [f"PC_{i}" for i in range(X_tr_pca.shape[1])]
# X_train_final = pd.concat([meta_tr, pd.DataFrame(X_tr_pca, columns=pca_cols)], axis=1)
# X_test_final = pd.concat([meta_ts, pd.DataFrame(X_ts_pca, columns=pca_cols)], axis=1)
# # C. Drop Metadata sebelum masuk ke model
# X_train_clean = X_train_final.drop(columns=metadata_cols, errors='ignore')
# X_test_clean = X_test_final.drop(columns=metadata_cols, errors='ignore')
# # Ambil index custom CV dari data train (sebelum urutannya berubah)
# cv_indices_eval = get_custom_cv_indices(X_train_final)
# # Loop Manual untuk Out-of-Fold Probabilities (Mengatasi ValueError)
# # Buat array kosong diisi NaN sepanjang data train
# y_train_oof_probs = np.full(len(X_train_clean), np.nan)
# for train_idx, val_idx in cv_indices_eval:
# clone_model = model_class(**best_params)
# # Potong data sesuai indeks Custom CV
# X_tr_fold = X_train_clean.iloc[train_idx]
# y_tr_fold = y_train_final.iloc[train_idx]
# X_val_fold = X_train_clean.iloc[val_idx]
# # Fit dan Prediksi pada lipatan ini
# clone_model.fit(X_tr_fold, y_tr_fold)
# y_train_oof_probs[val_idx] = clone_model.predict_proba(X_val_fold)[:, 1]
# # Saring array, buang indeks yang tidak pernah masuk ke lipatan validasi (NaN)
# valid_mask = ~np.isnan(y_train_oof_probs)
# y_train_oof_probs_clean = y_train_oof_probs[valid_mask]
# # Gunakan to_numpy() agar tidak ada masalah pergeseran index bawaan Pandas
# y_train_true_clean = y_train_final.to_numpy()[valid_mask]
# # Cari threshold optimal MURNI DARI DATA TRAIN
# precisions_tr, recalls_tr, thresholds_tr = precision_recall_curve(y_train_true_clean, y_train_oof_probs_clean)
# # precisions_tr, recalls_tr, thresholds_tr = precision_recall_curve(y_train_final, y_train_oof_probs)
# target_recall = 0.70
# # Proses cari best threshold
# try:
# optimal_idx = np.where(recalls_tr >= target_recall)[0][-1]
# if optimal_idx >= len(thresholds_tr):
# optimal_idx = len(thresholds_tr) - 1
# best_threshold = thresholds_tr[optimal_idx]
# except IndexError:
# best_threshold = 0.5
# # Latih model final sesungguhnya ke SELURUH data Train
# model = model_class(**best_params)
# model.fit(X_train_clean, y_train_final)
# # simpan model
# dict_fitted_models[exp_id] = model
# # Prediksi probabilitas pada Test Set
# y_pred_probs = model.predict_proba(X_test_clean)[:, 1]
# # Hitung Test PR-AUC
# test_pr_auc = average_precision_score(y_test_final, y_pred_probs)
# # Terapkan Threshold yang sudah didapat sebelumnya
# y_pred_final = (y_pred_probs >= best_threshold).astype(int)
# # Hitung Performa Real di Dunia Nyata
# achieved_recall = recall_score(y_test_final, y_pred_final)
# expected_precision = precision_score(y_test_final, y_pred_final, zero_division=0)
# cm = confusion_matrix(y_test_final, y_pred_final)
# tn, fp, fn, tp = cm.ravel()
# # [METRIK TAMBAHAN BARU]
# test_f1 = f1_score(y_test_final, y_pred_final, zero_division=0)
# test_accuracy = accuracy_score(y_test_final, y_pred_final)
# test_specificity = tn / (tn + fp) if (tn + fp) > 0 else 0.0
# # Simpan detail prediksi dan metadata ke dalam keranjang
# dict_detailed_predictions[exp_id] = {
# 'metadata': meta_ts.copy(),
# 'y_true': y_test_final.reset_index(drop=True),
# 'y_pred': pd.Series(y_pred_final),
# 'y_prob': pd.Series(y_pred_probs),
# 'X_test_features': X_test_clean.reset_index(drop=True) # simpan fitur
# }
# # --- 7. SIMPAN HASIL KE REKAPAN ---
# final_evaluation_results.append({
# 'Experiment_ID': exp_id,
# 'Target': t_name,
# 'Null_Strat': n_strat,
# 'Feat_Strat': f_strat,
# 'Model': m_name,
# 'Optuna_PR_AUC': result_data['best_cv_pr_auc'], # Dari Optuna
# 'Test_PR_AUC': test_pr_auc, # Di Test Set riil
# 'Best_Threshold': best_threshold,
# 'Test_Recall': achieved_recall,
# 'Test_Precision': expected_precision,
# 'Confusion_Matrix': cm.tolist(),
# 'Test_Specificity': test_specificity, # Masuk ke tabel
# 'Test_F1': test_f1, # Masuk ke tabel
# 'Test_Accuracy': test_accuracy, # Masuk ke tabel
# })
# # Simpan kurva Test Set murni untuk plotting (cuma untuk visualisasi kurva, bukan untuk cari threshold)
# precisions_test, recalls_test, _ = precision_recall_curve(y_test_final, y_pred_probs)
# # Simpan metrik kurva untuk diplot di luar loop
# if t_name in pr_auc_plot_data:
# pr_auc_plot_data[t_name].append({
# 'label': f"{n_strat}_{f_strat}_{m_name} (AUC={test_pr_auc:.3f})",
# 'recalls': recalls_test,
# 'precisions': precisions_test,
# 'auc': test_pr_auc
# })
# # Jadikan DataFrame agar mudah diurutkan dan dianalisis
# df_final_summary = pd.DataFrame(final_evaluation_results)
# df_final_summary = df_final_summary.sort_values(by=['Target', 'Test_Precision'], ascending=[True, False]).reset_index(drop=True)
# print("\n========== REKAPAN PERINGKAT MODEL TERBAIK TIAP TARGET ==========")
# display(df_final_summary.head(3))
# # SIMPAN DICTIONARY OBJEK MODEL KE DRIVE
# model_pkl_path = save_path + 'fitted_models_skripsi.pkl'
# with open(model_pkl_path, 'wb') as f:
# pickle.dump(dict_fitted_models, f)
# # Simpan Dataframe Evaluasi Final ke CSV di GDrive
# csv_path = save_path + 'hasil_evaluasi_final_skripsi.csv'
# df_final_summary.to_csv(csv_path, index=False)
# print(f"\n✅ Model dan rekap evaluasi final berhasil disimpan di: {model_pkl_path} dan {csv_path}")# # 1. Inisialisasi dictionary
# if platform == "gcolab":
# dict_master_results = {} # Mulai dengan dictionary kosong di Colab
# print("Inisialisasi dict_master_results kosong di Google Colab.")
# for c_type in ["cpu","gpu"]:
# file_path = prefix_path + f'pickle/eksperimen_skripsi_backup_{c_type}.pkl'
# if os.path.exists(file_path):
# with open(file_path, 'rb') as f:
# data = pickle.load(f)
# dict_master_results.update(data) # Menggabungkan dictionary
# print(f"Berhasil me-load data backup {c_type.upper()} ({len(data)} eksperimen).")
# else:
# print(f"File backup {c_type.upper()} tidak ditemukan di: {file_path}")
# elif platform == "kaggle": # Kaggle
# # kalau pakai cpu yang dibuka gpu karena cpu sudah ada di var
# # begitu pula sebaliknya
# if compute_type == 'cpu':
# file_path = prefix_path + f'pickle/eksperimen_skripsi_backup_gpu.pkl'
# elif compute_type == 'gpu':
# file_path = prefix_path + f'pickle/eksperimen_skripsi_backup_cpu.pkl'
# if os.path.exists(file_path):
# with open(file_path, 'rb') as f:
# data = pickle.load(f)
# dict_master_results.update(data) # Menggabungkan dictionary
# print(f"Berhasil me-load backup SELAIN {compute_type.upper()} dengan {len(data)} eksperimen.")
# else:
# print(f"File backup {compute_type.upper()} tidak ditemukan di: {file_path}")
# print(f"Total seluruh eksperimen yang siap dievaluasi: {len(dict_master_results)}")
# # Mapping string nama model ke Class Modelnya
# dict_model_classes = {
# 'RandomForest': RandomForestClassifier,
# 'XGBoost': XGBClassifier,
# 'LightGBM': LGBMClassifier,
# 'AdaBoost': AdaBoostClassifier,
# 'CatBoost': CatBoostClassifier
# }
# # List untuk menyimpan rekapan performa Test dari SEMUA eksperimen
# final_evaluation_results = []
# # Mnampung metrik kurva PR per target
# pr_auc_plot_data = {'ppk': [], 'negeq': [], 'conloss': []}
# # dict menampung detail prediksi tiap baris data
# dict_detailed_predictions = {}
# # dict untuk menyimpan model yang udah difit
# dict_fitted_models = {}
# print("========== MEMULAI EVALUASI FINAL SELURUH EKSPERIMEN ==========")
# # Looping ke seluruh hasil eksperimen yang ada di master
# for exp_id, result_data in dict_master_results.items():
# print(f"\nMengevaluasi: {exp_id}")
# # 1. Parsing Konfigurasi dari exp_id (Format: target_nstrat_fstrat_model)
# parts = exp_id.split('_')
# t_name, n_strat, f_strat, m_name = parts[0], parts[1], parts[2], parts[3]
# # 2. Ambil parameter terbaik & Class Model
# best_params = result_data['best_params']
# model_class = dict_model_classes[m_name]
# # Atur deterministic parameter tambahan agar hasil konsisten
# if model_class is RandomForestClassifier:
# best_params['n_jobs'] = 1
# best_params['random_state'] = 42
# elif model_class is XGBClassifier:
# best_params['n_jobs'] = 1
# elif model_class is LGBMClassifier:
# best_params['deterministic'] = True
# best_params['force_col_wise'] = True
# best_params['n_jobs'] = 1
# elif model_class is CatBoostClassifier:
# best_params['thread_count'] = 1
# best_params['random_seed'] = 42
# # 3. Tarik Data Raw (Train utuh dan Test masa depan)
# X_train_final = dict_data_splits[t_name]['X_train'].copy()
# y_train_final = dict_data_splits[t_name]['y_train'].copy()
# X_test_final = dict_data_splits[t_name]['X_test'].copy()
# y_test_final = dict_data_splits[t_name]['y_test'].copy()
# # --- 4. REPLIKASI PREPROCESSING (SESUAI KONFIGURASI) ---
# # A. Missing Indicator & Imputasi
# if n_strat == 'impute':
# # Tambah missing indicator (Train dan Test)
# cols_to_flag = kolom_missing_indicator[t_name]
# for col in cols_to_flag:
# X_train_final[f"{col}_is_missing"] = X_train_final[col].isnull().astype(int)
# X_test_final[f"{col}_is_missing"] = X_test_final[col].isnull().astype(int)
# # Terapkan Custom Imputation
# fitur_cols = [c for c in X_train_final.columns if c not in metadata_cols]
# X_train_final, X_test_final = apply_custom_imputation(X_train_final, X_test_final, fitur_cols)
# # Simpan metadata untuk nanti digabung kembali jika diperlukan (misal untuk PCA yang butuh fitur asli)
# meta_tr = X_train_final[metadata_cols].reset_index(drop=True)
# meta_ts = X_test_final[metadata_cols].reset_index(drop=True)
# # B. Strategi Fitur (Correlation Drop / PCA)
# if f_strat == 'corr':
# # Cari fitur korelasi tinggi HANYA di Train, drop di Train dan Test
# cols_to_drop = get_low_corr_features_target_aware(X_train_final, y_train_final, threshold=0.85)
# X_train_final = X_train_final.drop(columns=cols_to_drop)
# X_test_final = X_test_final.drop(columns=cols_to_drop)
# elif f_strat == 'pca':
# fitur_cols = [c for c in X_train_final.columns if c not in metadata_cols]
# # Buang Fitur Konstan dulu Sebelum PCA (agar PCA tidak error)
# constant_cols = [col for col in fitur_cols if X_train_final[col].nunique() <= 1]
# if constant_cols:
# X_train_final = X_train_final.drop(columns=constant_cols)
# X_test_final = X_test_final.drop(columns=constant_cols)
# fitur_cols = [c for c in fitur_cols if c not in constant_cols]
# scaler = StandardScaler()
# pca = PCA(n_components=0.95, random_state=42)
# # Fit Transform di Train, Transform di Test
# X_tr_pca = pca.fit_transform(scaler.fit_transform(X_train_final[fitur_cols]))
# X_ts_pca = pca.transform(scaler.transform(X_test_final[fitur_cols]))
# # Rekonstruksi DataFrame (tempelkan metadata lagi)
# pca_cols = [f"PC_{i}" for i in range(X_tr_pca.shape[1])]
# X_train_final = pd.concat([meta_tr, pd.DataFrame(X_tr_pca, columns=pca_cols)], axis=1)
# X_test_final = pd.concat([meta_ts, pd.DataFrame(X_ts_pca, columns=pca_cols)], axis=1)
# # C. Drop Metadata sebelum masuk ke model
# X_train_clean = X_train_final.drop(columns=metadata_cols, errors='ignore')
# X_test_clean = X_test_final.drop(columns=metadata_cols, errors='ignore')
# # --- 5. TRAINING MODEL FINAL & PREDIKSI ---
# model = model_class(**best_params)
# model.fit(X_train_clean, y_train_final)
# # simpan model
# dict_fitted_models[exp_id] = model
# # Prediksi probabilitas pada Test Set
# y_pred_probs = model.predict_proba(X_test_clean)[:, 1]
# # Hitung Test PR-AUC
# test_pr_auc = average_precision_score(y_test_final, y_pred_probs)
# # --- 6. PENENTUAN THRESHOLD (PRIORITAS RECALL) ---
# precisions, recalls, thresholds = precision_recall_curve(y_test_final, y_pred_probs)
# target_recall = 0.80
# try:
# # Cari threshold tertinggi yang recall-nya masih >= 80%
# optimal_idx = np.where(recalls >= target_recall)[0][-1]
# if optimal_idx >= len(thresholds):
# optimal_idx = len(thresholds) - 1
# best_threshold = thresholds[optimal_idx]
# expected_precision = precisions[optimal_idx]
# achieved_recall = recalls[optimal_idx]
# except IndexError:
# # Fallback jika model sangat buruk dan tidak bisa mencapai 80% recall
# best_threshold = 0.5
# expected_precision = 0
# achieved_recall = 0
# # Terapkan Threshold
# y_pred_final = (y_pred_probs >= best_threshold).astype(int)
# cm = confusion_matrix(y_test_final, y_pred_final)
# # Simpan detail prediksi dan metadata ke dalam keranjang
# dict_detailed_predictions[exp_id] = {
# 'metadata': meta_ts.copy(),
# 'y_true': y_test_final.reset_index(drop=True),
# 'y_pred': pd.Series(y_pred_final),
# 'y_prob': pd.Series(y_pred_probs),
# 'X_test_features': X_test_clean.reset_index(drop=True) # simpan fitur
# }
# # --- 7. SIMPAN HASIL KE REKAPAN ---
# final_evaluation_results.append({
# 'Experiment_ID': exp_id,
# 'Target': t_name,
# 'Null_Strat': n_strat,
# 'Feat_Strat': f_strat,
# 'Model': m_name,
# 'Optuna_PR_AUC': result_data['best_optuna_pr_auc'], # Dari Optuna
# 'Test_PR_AUC': test_pr_auc, # Di Test Set riil
# 'Best_Threshold': best_threshold,
# 'Test_Recall': achieved_recall,
# 'Test_Precision': expected_precision,
# 'Confusion_Matrix': cm.tolist()
# })
# # # Cetak matriks hanya untuk model dengan PR_AUC Test > 0.40 agar layar tidak penuh
# # if test_pr_auc > 0.40:
# # print(f"-> Test PR-AUC = {test_pr_auc:.4f} | Threshold = {best_threshold:.4f}")
# # cm = confusion_matrix(y_test_final, y_pred_final)
# # plt.figure(figsize=(4,3))
# # sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
# # plt.title(f'{exp_id}')
# # plt.show()
# # Simpan metrik kurva untuk diplot di luar loop
# if t_name in pr_auc_plot_data:
# pr_auc_plot_data[t_name].append({
# 'label': f"{n_strat}_{f_strat}_{m_name} (AUC={test_pr_auc:.3f})",
# 'recalls': recalls,
# 'precisions': precisions,
# 'auc': test_pr_auc
# })
# # Jadikan DataFrame agar mudah diurutkan dan dianalisis
# df_final_summary = pd.DataFrame(final_evaluation_results)
# df_final_summary = df_final_summary.sort_values(by=['Target', 'Test_Precision'], ascending=[True, False]).reset_index(drop=True)
# print("\n========== REKAPAN PERINGKAT MODEL TERBAIK TIAP TARGET ==========")
# display(df_final_summary.head(3))
# # SIMPAN DICTIONARY OBJEK MODEL KE DRIVE
# model_pkl_path = save_path + 'fitted_models_skripsi.pkl'
# with open(model_pkl_path, 'wb') as f:
# pickle.dump(dict_fitted_models, f)
# # Simpan Dataframe Evaluasi Final ke CSV di GDrive
# csv_path = save_path + 'hasil_evaluasi_final_skripsi.csv'
# df_final_summary.to_csv(csv_path, index=False)
# print(f"\n✅ Model dan rekap evaluasi final berhasil disimpan di: {model_pkl_path} dan {csv_path}")PR AUC
import matplotlib.cm as mpl_cm
import numpy as np
import matplotlib.pyplot as plt
# ======================================================================
# --- VISUALISASI KURVA PR-AUC GABUNGAN PER TARGET (HORIZONTAL R-TO-L) ---
# ======================================================================
# 1. Filter target yang valid & memiliki data curves
valid_targets = [(t_name, curves) for t_name, curves in pr_auc_plot_data.items() if len(curves) > 0][::-1]
n_targets = len(valid_targets)
if n_targets > 0:
# 2. Inisialisasi Kanvas Multi-Plot
# Lebar diturunkan menjadi 8 per plot agar jarak antar kotak merapat
# Tinggi dinaikkan sedikit ke 15 agar seimbang dengan legenda di bawahnya
fig, axes = plt.subplots(nrows=1, ncols=n_targets, figsize=(8 * n_targets, 15))
if n_targets == 1:
axes = [axes]
# 3. Looping untuk mengisi plot dari kanan ke kiri
for i, (target_name, curves) in enumerate(valid_targets):
ax = axes[n_targets - 1 - i]
# Urutkan berdasarkan AUC tertinggi agar garis dan legend rapi dari atas ke bawah
curves = sorted(curves, key=lambda x: x['auc'], reverse=True)
# Hitung jumlah konfigurasi masing-masing
keep_count = sum(1 for c in curves if 'keep' in c['label'].lower())
impute_count = sum(1 for c in curves if 'impute' in c['label'].lower())
# Rentang dibalik (0.9, 0.4) agar list dimulai dari warna paling tua ke muda
green_colors = mpl_cm.Greens(np.linspace(0.9, 0.4, keep_count)) if keep_count > 0 else []
red_colors = mpl_cm.Reds(np.linspace(0.9, 0.4, impute_count)) if impute_count > 0 else []
green_iter = iter(green_colors)
red_iter = iter(red_colors)
# Plot seluruh konfigurasi
for curve in curves:
label_lower = curve['label'].lower()
if 'keep' in label_lower:
line_color = next(green_iter)
elif 'impute' in label_lower:
line_color = next(red_iter)
else:
line_color = 'gray'
ax.plot(curve['recalls'], curve['precisions'], label=curve['label'],
color=line_color, alpha=0.8, linewidth=2.5)
# 4. Mengatur Judul & Label (Tetap diukuran raksasa)
ax.set_title(f'Target: {target_name.upper()}', fontsize=24, fontweight='bold', pad=10)
ax.set_xlabel('Recall', fontsize=20)
ax.set_ylabel('Precision', fontsize=20)
ax.tick_params(axis='both', labelsize=16)
ax.grid(True, linestyle='--', alpha=0.5)
# Memaksa kotak plot untuk selalu berbentuk persegi (1:1)
ax.set_box_aspect(1)
# Menggunakan ncol=1 untuk mengembalikan layout legenda menjadi 1 kolom vertikal
ax.legend(bbox_to_anchor=(0.5, -0.15), loc='upper center', borderaxespad=0., fontsize=16, ncol=1)
# 5. Mengatur Judul Utama Global
fig.suptitle('Precision-Recall Curve', fontsize=32, fontweight='bold', y=0.97)
# --- PERBAIKAN TATA LETAK ---
# Tambahkan w_pad=1.0 untuk memastikan jarak horizontal antar plot saling merapat
plt.tight_layout(rect=[0, 0.0, 1, 0.95], w_pad=1.0)
plt.show()
# ======================================================================
# --- VISUALISASI KURVA PR-AUC GABUNGAN PER TARGET ---
# ======================================================================
for target_name, curves in pr_auc_plot_data.items():
if len(curves) == 0:
continue
# Urutkan berdasarkan AUC tertinggi agar legend rapi
curves = sorted(curves, key=lambda x: x['auc'], reverse=True)
plt.figure(figsize=(10, 7))
for curve in curves:
# Menambahkan transparansi (alpha) agar garis yang bertumpuk tetap terlihat
plt.plot(curve['recalls'], curve['precisions'], label=curve['label'], alpha=0.7, linewidth=1.5)
plt.title(f'Precision-Recall Curve (All Configurations) - Target: {target_name.upper()}')
plt.xlabel('Recall')
plt.ylabel('Precision')
# Memaksa legend berada di luar grafik (sebelah kanan) agar tidak menutupi kurva
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0., fontsize=8)
plt.grid(True, linestyle='--', alpha=0.5)
plt.tight_layout()
plt.show()


ROC AUC
import matplotlib.cm as mpl_cm
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve, roc_auc_score
# 1. Definisikan list target dan siapkan wadah untuk data yang sudah diproses
daftar_target = ['conloss', 'negeq', 'ppk']
valid_roc_data = []
# --- TAHAP 1: PENGUMPULAN & PERHITUNGAN DATA ---
for target_name in daftar_target:
curves_roc = []
# Kumpulkan seluruh eksperimen yang termasuk dalam target ini
for exp_id, data in dict_detailed_predictions.items():
parts = exp_id.split('_')
t_name = parts[0]
if t_name == target_name:
y_true = data['y_true']
y_prob = data['y_prob']
# Hitung metrik ROC secara dinamis
fpr, tpr, _ = roc_curve(y_true, y_prob)
roc_auc = roc_auc_score(y_true, y_prob)
# Format label agar hanya menampilkan konfigurasi
config_label = "_".join(parts[1:])
curves_roc.append({
'label': f"{config_label} (AUC={roc_auc:.3f})",
'fpr': fpr,
'tpr': tpr,
'auc': roc_auc
})
# Jika ada data, urutkan dan simpan ke list utama
if len(curves_roc) > 0:
curves_roc = sorted(curves_roc, key=lambda x: x['auc'], reverse=True)
valid_roc_data.append((target_name, curves_roc))
# --- TAHAP 2: VISUALISASI ---
n_targets = len(valid_roc_data)
if n_targets > 0:
# 2. Inisialisasi Kanvas Multi-Plot
# Lebar diset 8 per plot, Tinggi diset 15 agar seimbang dengan legenda
fig, axes = plt.subplots(nrows=1, ncols=n_targets, figsize=(8 * n_targets, 15))
if n_targets == 1:
axes = [axes]
# 3. Looping untuk mengisi plot dari kanan ke kiri
for i, (target_name, curves_roc) in enumerate(valid_roc_data):
# Trik Alur Kanan ke Kiri: Indeks dibalik
ax = axes[n_targets - 1 - i]
# --- LOGIKA PEWARNAAN GARIS ---
# Hitung jumlah konfigurasi masing-masing
keep_count = sum(1 for c in curves_roc if 'keep' in c['label'].lower())
impute_count = sum(1 for c in curves_roc if 'impute' in c['label'].lower())
# Rentang dibalik (0.9, 0.4) agar list dimulai dari warna paling tua ke muda
green_colors = mpl_cm.Greens(np.linspace(0.9, 0.4, keep_count)) if keep_count > 0 else []
red_colors = mpl_cm.Reds(np.linspace(0.9, 0.4, impute_count)) if impute_count > 0 else []
green_iter = iter(green_colors)
red_iter = iter(red_colors)
# Plot seluruh konfigurasi
for curve in curves_roc:
label_lower = curve['label'].lower()
if 'keep' in label_lower:
line_color = next(green_iter)
elif 'impute' in label_lower:
line_color = next(red_iter)
else:
line_color = 'gray'
ax.plot(curve['fpr'], curve['tpr'], label=curve['label'],
color=line_color, alpha=0.8, linewidth=2.5)
# Tambahkan garis diagonal baseline (Random Guessing) disamakan ketebalannya
ax.plot([0, 1], [0, 1], color='black', linestyle='--', alpha=0.5, label='Baseline (AUC=0.500)', linewidth=2.5)
# 4. Mengatur Judul Kecil & Label per Sub-Plot (UKURAN RAKSASA)
ax.set_title(f'Target: {target_name.upper()}', fontsize=24, fontweight='bold', pad=10)
ax.set_xlabel('False Positive Rate', fontsize=20)
ax.set_ylabel('True Positive Rate', fontsize=20)
ax.tick_params(axis='both', labelsize=16)
ax.grid(True, linestyle='--', alpha=0.5)
# Memaksa kotak plot untuk selalu berbentuk persegi (1:1)
ax.set_box_aspect(1)
# Meletakkan legenda di tengah bawah dengan 1 kolom
ax.legend(bbox_to_anchor=(0.5, -0.15), loc='upper center', borderaxespad=0., fontsize=16, ncol=1)
# 5. Mengatur Judul Utama Global
fig.suptitle('ROC-AUC Curve', fontsize=32, fontweight='bold', y=0.97)
# --- PERBAIKAN TATA LETAK ---
plt.tight_layout(rect=[0, 0.0, 1, 0.95], w_pad=1.0)
plt.show()
# 1. Definisikan list target yang dimiliki
daftar_target = ['ppk', 'negeq', 'conloss']
# 2. Iterasi per tipe target (Satu target = Satu Kanvas Gambar)
for target_name in daftar_target:
curves_roc = []
# Kumpulkan seluruh eksperimen yang termasuk dalam target ini
for exp_id, data in dict_detailed_predictions.items():
parts = exp_id.split('_')
t_name = parts[0] # Mengambil 'ppk', 'negeq', atau 'conloss'
if t_name == target_name:
y_true = data['y_true']
y_prob = data['y_prob']
# Hitung metrik ROC secara dinamis
fpr, tpr, _ = roc_curve(y_true, y_prob)
roc_auc = roc_auc_score(y_true, y_prob)
# Format label agar hanya menampilkan konfigurasi (misal: impute_corr_XGBoost)
config_label = "_".join(parts[1:])
curves_roc.append({
'label': f"{config_label} (AUC={roc_auc:.3f})",
'fpr': fpr,
'tpr': tpr,
'auc': roc_auc
})
# Jika tidak ada eksperimen untuk target ini, lewati
if len(curves_roc) == 0:
continue
# Urutkan dari nilai AUC tertinggi agar urutan legenda rapi
curves_roc = sorted(curves_roc, key=lambda x: x['auc'], reverse=True)
# 3. Mulai Plotting per Target
plt.figure(figsize=(10, 7))
for curve in curves_roc:
# Menampilkan tiap kombinasi konfigurasi sebagai garis berbeda di kanvas yang sama
plt.plot(curve['fpr'], curve['tpr'], label=curve['label'], alpha=0.7, linewidth=1.5)
# Tambahkan garis diagonal baseline (Random Guessing)
plt.plot([0, 1], [0, 1], color='black', linestyle='--', alpha=0.5, label='Baseline (AUC=0.500)')
plt.title(f'ROC-AUC Curve (All Configurations) - Target: {target_name.upper()}', fontsize=12, fontweight='bold')
plt.xlabel('False Positive Rate (1 - Specificity)')
plt.ylabel('True Positive Rate (Sensitivity / Recall)')
# Memaksa legend berada di luar grafik sebelah kanan (sama seperti PR-AUC)
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0., fontsize=8)
plt.grid(True, linestyle='--', alpha=0.5)
plt.tight_layout()
plt.show()


print("========== REKAPAN NILAI ROC AUC ==========")
for exp_id, data in dict_detailed_predictions.items():
y_true = data['y_true']
y_prob = data['y_prob']
# Hitung nilai ROC AUC
roc_auc = roc_auc_score(y_true, y_prob)
# Cetak sesuai format yang diminta (dibulatkan 3 angka di belakang koma)
print(f"- ROC AUC {exp_id} = {roc_auc:.3f}")========== REKAPAN NILAI ROC AUC ==========
- ROC AUC ppk_keep_all_RandomForest = 0.848
- ROC AUC ppk_keep_corr_RandomForest = 0.843
- ROC AUC ppk_impute_all_RandomForest = 0.512
- ROC AUC ppk_impute_all_AdaBoost = 0.447
- ROC AUC ppk_impute_corr_RandomForest = 0.491
- ROC AUC ppk_impute_corr_AdaBoost = 0.481
- ROC AUC ppk_impute_pca_RandomForest = 0.523
- ROC AUC ppk_impute_pca_AdaBoost = 0.525
- ROC AUC negeq_keep_all_RandomForest = 0.931
- ROC AUC negeq_keep_corr_RandomForest = 0.900
- ROC AUC negeq_impute_all_RandomForest = 0.392
- ROC AUC negeq_impute_all_AdaBoost = 0.490
- ROC AUC negeq_impute_corr_RandomForest = 0.395
- ROC AUC negeq_impute_corr_AdaBoost = 0.508
- ROC AUC negeq_impute_pca_RandomForest = 0.464
- ROC AUC negeq_impute_pca_AdaBoost = 0.595
- ROC AUC conloss_keep_all_RandomForest = 0.913
- ROC AUC conloss_keep_corr_RandomForest = 0.912
- ROC AUC conloss_impute_all_RandomForest = 0.525
- ROC AUC conloss_impute_all_AdaBoost = 0.479
- ROC AUC conloss_impute_corr_RandomForest = 0.524
- ROC AUC conloss_impute_corr_AdaBoost = 0.480
- ROC AUC conloss_impute_pca_RandomForest = 0.528
- ROC AUC conloss_impute_pca_AdaBoost = 0.517
- ROC AUC ppk_keep_all_XGBoost = 0.832
- ROC AUC ppk_keep_all_LightGBM = 0.819
- ROC AUC ppk_keep_all_CatBoost = 0.839
- ROC AUC ppk_keep_corr_XGBoost = 0.830
- ROC AUC ppk_keep_corr_LightGBM = 0.822
- ROC AUC ppk_keep_corr_CatBoost = 0.834
- ROC AUC ppk_impute_all_XGBoost = 0.442
- ROC AUC ppk_impute_all_LightGBM = 0.464
- ROC AUC ppk_impute_all_CatBoost = 0.432
- ROC AUC ppk_impute_corr_XGBoost = 0.469
- ROC AUC ppk_impute_corr_LightGBM = 0.459
- ROC AUC ppk_impute_corr_CatBoost = 0.458
- ROC AUC ppk_impute_pca_XGBoost = 0.513
- ROC AUC ppk_impute_pca_LightGBM = 0.490
- ROC AUC ppk_impute_pca_CatBoost = 0.493
- ROC AUC negeq_keep_all_XGBoost = 0.894
- ROC AUC negeq_keep_all_LightGBM = 0.855
- ROC AUC negeq_keep_all_CatBoost = 0.931
- ROC AUC negeq_keep_corr_XGBoost = 0.890
- ROC AUC negeq_keep_corr_LightGBM = 0.835
- ROC AUC negeq_keep_corr_CatBoost = 0.903
- ROC AUC negeq_impute_all_XGBoost = 0.444
- ROC AUC negeq_impute_all_LightGBM = 0.464
- ROC AUC negeq_impute_all_CatBoost = 0.425
- ROC AUC negeq_impute_corr_XGBoost = 0.470
- ROC AUC negeq_impute_corr_LightGBM = 0.431
- ROC AUC negeq_impute_corr_CatBoost = 0.470
- ROC AUC negeq_impute_pca_XGBoost = 0.519
- ROC AUC negeq_impute_pca_LightGBM = 0.526
- ROC AUC negeq_impute_pca_CatBoost = 0.486
- ROC AUC conloss_keep_all_XGBoost = 0.918
- ROC AUC conloss_keep_all_LightGBM = 0.916
- ROC AUC conloss_keep_all_CatBoost = 0.910
- ROC AUC conloss_keep_corr_XGBoost = 0.915
- ROC AUC conloss_keep_corr_LightGBM = 0.905
- ROC AUC conloss_keep_corr_CatBoost = 0.917
- ROC AUC conloss_impute_all_XGBoost = 0.506
- ROC AUC conloss_impute_all_LightGBM = 0.497
- ROC AUC conloss_impute_all_CatBoost = 0.501
- ROC AUC conloss_impute_corr_XGBoost = 0.480
- ROC AUC conloss_impute_corr_LightGBM = 0.484
- ROC AUC conloss_impute_corr_CatBoost = 0.494
- ROC AUC conloss_impute_pca_XGBoost = 0.508
- ROC AUC conloss_impute_pca_LightGBM = 0.502
- ROC AUC conloss_impute_pca_CatBoost = 0.481
Evaluasi
Hasil Asesmen Semua Konfigurasi Model
with pd.option_context('display.max_rows', None, 'display.max_columns', None):
display(df_final_summary)| Experiment_ID | Target | Null_Strat | Feat_Strat | Model | Optuna_PR_AUC | Test_PR_AUC | Best_Threshold | Test_Recall | Test_Precision | Confusion_Matrix | Test_Specificity | Test_F1 | Test_Accuracy | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | conloss_keep_corr_CatBoost | conloss | keep | corr | CatBoost | 0.579363 | 0.624740 | 0.058335 | 0.949367 | 0.307377 | [[731, 338], [8, 150]] | 0.683817 | 0.464396 | 0.718011 |
| 1 | conloss_keep_all_XGBoost | conloss | keep | all | XGBoost | 0.571422 | 0.623831 | 0.060684 | 0.943038 | 0.299197 | [[720, 349], [9, 149]] | 0.673527 | 0.454268 | 0.708231 |
| 2 | conloss_keep_corr_LightGBM | conloss | keep | corr | LightGBM | 0.567439 | 0.623410 | 0.002667 | 0.936709 | 0.273063 | [[675, 394], [10, 148]] | 0.631431 | 0.422857 | 0.670742 |
| 3 | conloss_keep_corr_XGBoost | conloss | keep | corr | XGBoost | 0.566969 | 0.617837 | 0.062856 | 0.949367 | 0.295276 | [[711, 358], [8, 150]] | 0.665108 | 0.450450 | 0.701711 |
| 4 | conloss_keep_all_LightGBM | conloss | keep | all | LightGBM | 0.566194 | 0.609190 | 0.023756 | 0.936709 | 0.282983 | [[694, 375], [10, 148]] | 0.649205 | 0.434655 | 0.686227 |
| 5 | conloss_keep_all_CatBoost | conloss | keep | all | CatBoost | 0.583181 | 0.599378 | 0.043155 | 0.943038 | 0.272395 | [[671, 398], [9, 149]] | 0.627689 | 0.422695 | 0.668297 |
| 6 | conloss_keep_all_RandomForest | conloss | keep | all | RandomForest | 0.544012 | 0.593263 | 0.103132 | 0.962025 | 0.298039 | [[711, 358], [6, 152]] | 0.665108 | 0.455090 | 0.703341 |
| 7 | conloss_keep_corr_RandomForest | conloss | keep | corr | RandomForest | 0.542281 | 0.560232 | 0.094077 | 0.962025 | 0.294004 | [[704, 365], [6, 152]] | 0.658559 | 0.450370 | 0.697637 |
| 8 | conloss_impute_pca_AdaBoost | conloss | impute | pca | AdaBoost | 0.185754 | 0.159655 | 0.395077 | 0.164557 | 0.189781 | [[958, 111], [132, 26]] | 0.896165 | 0.176271 | 0.801956 |
| 9 | conloss_impute_corr_RandomForest | conloss | impute | corr | RandomForest | 0.236535 | 0.154020 | 0.076328 | 0.968354 | 0.128788 | [[34, 1035], [5, 153]] | 0.031805 | 0.227340 | 0.152404 |
| 10 | conloss_impute_all_RandomForest | conloss | impute | all | RandomForest | 0.227192 | 0.152983 | 0.076892 | 0.974684 | 0.129086 | [[30, 1039], [4, 154]] | 0.028064 | 0.227979 | 0.149959 |
| 11 | conloss_impute_pca_RandomForest | conloss | impute | pca | RandomForest | 0.193522 | 0.147872 | 0.074176 | 0.829114 | 0.126692 | [[166, 903], [27, 131]] | 0.155285 | 0.219799 | 0.242054 |
| 12 | conloss_impute_all_XGBoost | conloss | impute | all | XGBoost | 0.216323 | 0.145872 | 0.076350 | 1.000000 | 0.128769 | [[0, 1069], [0, 158]] | 0.000000 | 0.228159 | 0.128769 |
| 13 | conloss_impute_pca_CatBoost | conloss | impute | pca | CatBoost | 0.178360 | 0.138323 | 0.072692 | 0.898734 | 0.126447 | [[88, 981], [16, 142]] | 0.082320 | 0.221702 | 0.187449 |
| 14 | conloss_impute_corr_CatBoost | conloss | impute | corr | CatBoost | 0.212818 | 0.135268 | 0.083807 | 0.968354 | 0.127076 | [[18, 1051], [5, 153]] | 0.016838 | 0.224670 | 0.139364 |
| 15 | conloss_impute_all_CatBoost | conloss | impute | all | CatBoost | 0.205887 | 0.135089 | 0.081985 | 0.974684 | 0.127907 | [[19, 1050], [4, 154]] | 0.017774 | 0.226138 | 0.140994 |
| 16 | conloss_impute_pca_XGBoost | conloss | impute | pca | XGBoost | 0.173743 | 0.133132 | 0.024094 | 0.829114 | 0.129832 | [[191, 878], [27, 131]] | 0.178672 | 0.224507 | 0.262429 |
| 17 | conloss_impute_pca_LightGBM | conloss | impute | pca | LightGBM | 0.171470 | 0.130777 | 0.001406 | 0.968354 | 0.130324 | [[48, 1021], [5, 153]] | 0.044902 | 0.229730 | 0.163814 |
| 18 | conloss_impute_corr_LightGBM | conloss | impute | corr | LightGBM | 0.210543 | 0.130056 | 0.078020 | 1.000000 | 0.128769 | [[0, 1069], [0, 158]] | 0.000000 | 0.228159 | 0.128769 |
| 19 | conloss_impute_all_AdaBoost | conloss | impute | all | AdaBoost | 0.191228 | 0.129227 | 0.162216 | 0.803797 | 0.126747 | [[194, 875], [31, 127]] | 0.181478 | 0.218966 | 0.261614 |
| 20 | conloss_impute_all_LightGBM | conloss | impute | all | LightGBM | 0.207482 | 0.129117 | 0.077203 | 1.000000 | 0.128769 | [[0, 1069], [0, 158]] | 0.000000 | 0.228159 | 0.128769 |
| 21 | conloss_impute_corr_XGBoost | conloss | impute | corr | XGBoost | 0.218250 | 0.127453 | 0.078306 | 1.000000 | 0.128769 | [[0, 1069], [0, 158]] | 0.000000 | 0.228159 | 0.128769 |
| 22 | conloss_impute_corr_AdaBoost | conloss | impute | corr | AdaBoost | 0.185537 | 0.125277 | 0.180465 | 0.848101 | 0.127863 | [[155, 914], [24, 134]] | 0.144995 | 0.222222 | 0.235534 |
| 23 | negeq_keep_all_RandomForest | negeq | keep | all | RandomForest | 0.417147 | 0.467442 | 0.059480 | 0.852941 | 0.166667 | [[1609, 145], [5, 29]] | 0.917332 | 0.278846 | 0.916107 |
| 24 | negeq_keep_corr_RandomForest | negeq | keep | corr | RandomForest | 0.446320 | 0.458193 | 0.050467 | 0.852941 | 0.122881 | [[1547, 207], [5, 29]] | 0.881984 | 0.214815 | 0.881432 |
| 25 | negeq_keep_all_CatBoost | negeq | keep | all | CatBoost | 0.529702 | 0.404039 | 0.012689 | 0.823529 | 0.142132 | [[1585, 169], [6, 28]] | 0.903649 | 0.242424 | 0.902125 |
| 26 | negeq_keep_corr_CatBoost | negeq | keep | corr | CatBoost | 0.498662 | 0.381612 | 0.007394 | 0.794118 | 0.120536 | [[1557, 197], [7, 27]] | 0.887685 | 0.209302 | 0.885906 |
| 27 | negeq_keep_all_LightGBM | negeq | keep | all | LightGBM | 0.514100 | 0.303942 | 0.000840 | 0.735294 | 0.058005 | [[1348, 406], [9, 25]] | 0.768529 | 0.107527 | 0.767897 |
| 28 | negeq_keep_corr_LightGBM | negeq | keep | corr | LightGBM | 0.495929 | 0.299926 | 0.000132 | 0.764706 | 0.052953 | [[1289, 465], [8, 26]] | 0.734892 | 0.099048 | 0.735459 |
| 29 | negeq_keep_corr_XGBoost | negeq | keep | corr | XGBoost | 0.428743 | 0.269452 | 0.011085 | 0.705882 | 0.114286 | [[1568, 186], [10, 24]] | 0.893957 | 0.196721 | 0.890380 |
| 30 | negeq_keep_all_XGBoost | negeq | keep | all | XGBoost | 0.426746 | 0.265196 | 0.011342 | 0.676471 | 0.113861 | [[1575, 179], [11, 23]] | 0.897948 | 0.194915 | 0.893736 |
| 31 | negeq_impute_all_AdaBoost | negeq | impute | all | AdaBoost | 0.090062 | 0.026135 | 0.345297 | 0.441176 | 0.018007 | [[936, 818], [19, 15]] | 0.533637 | 0.034602 | 0.531879 |
| 32 | negeq_impute_pca_AdaBoost | negeq | impute | pca | AdaBoost | 0.084748 | 0.025583 | 0.249826 | 0.000000 | 0.000000 | [[1753, 1], [34, 0]] | 0.999430 | 0.000000 | 0.980425 |
| 33 | negeq_impute_pca_XGBoost | negeq | impute | pca | XGBoost | 0.081999 | 0.020723 | 0.008357 | 0.970588 | 0.019550 | [[99, 1655], [1, 33]] | 0.056442 | 0.038328 | 0.073826 |
| 34 | negeq_impute_pca_LightGBM | negeq | impute | pca | LightGBM | 0.094666 | 0.020156 | 0.007492 | 1.000000 | 0.019016 | [[0, 1754], [0, 34]] | 0.000000 | 0.037322 | 0.019016 |
| 35 | negeq_impute_corr_AdaBoost | negeq | impute | corr | AdaBoost | 0.084543 | 0.020000 | 0.390553 | 0.764706 | 0.019160 | [[423, 1331], [8, 26]] | 0.241163 | 0.037383 | 0.251119 |
| 36 | negeq_impute_corr_CatBoost | negeq | impute | corr | CatBoost | 0.098251 | 0.018241 | 0.111068 | 1.000000 | 0.019016 | [[0, 1754], [0, 34]] | 0.000000 | 0.037322 | 0.019016 |
| 37 | negeq_impute_pca_CatBoost | negeq | impute | pca | CatBoost | 0.108857 | 0.018042 | 0.027811 | 0.970588 | 0.019053 | [[55, 1699], [1, 33]] | 0.031357 | 0.037373 | 0.049217 |
| 38 | negeq_impute_pca_RandomForest | negeq | impute | pca | RandomForest | 0.119677 | 0.017961 | 0.009023 | 1.000000 | 0.019016 | [[0, 1754], [0, 34]] | 0.000000 | 0.037322 | 0.019016 |
| 39 | negeq_impute_all_LightGBM | negeq | impute | all | LightGBM | 0.089864 | 0.017722 | 0.000026 | 0.676471 | 0.018639 | [[543, 1211], [11, 23]] | 0.309578 | 0.036278 | 0.316555 |
| 40 | negeq_impute_all_XGBoost | negeq | impute | all | XGBoost | 0.110072 | 0.017494 | 0.008673 | 1.000000 | 0.019080 | [[6, 1748], [0, 34]] | 0.003421 | 0.037445 | 0.022371 |
| 41 | negeq_impute_corr_XGBoost | negeq | impute | corr | XGBoost | 0.106669 | 0.017146 | 0.004265 | 1.000000 | 0.019551 | [[49, 1705], [0, 34]] | 0.027936 | 0.038353 | 0.046421 |
| 42 | negeq_impute_all_CatBoost | negeq | impute | all | CatBoost | 0.097000 | 0.016889 | 0.020205 | 1.000000 | 0.019518 | [[46, 1708], [0, 34]] | 0.026226 | 0.038288 | 0.044743 |
| 43 | negeq_impute_corr_LightGBM | negeq | impute | corr | LightGBM | 0.107772 | 0.016014 | 0.000000 | 0.647059 | 0.017028 | [[484, 1270], [12, 22]] | 0.275941 | 0.033183 | 0.282998 |
| 44 | negeq_impute_corr_RandomForest | negeq | impute | corr | RandomForest | 0.109019 | 0.014907 | 0.008418 | 0.970588 | 0.018571 | [[10, 1744], [1, 33]] | 0.005701 | 0.036444 | 0.024049 |
| 45 | negeq_impute_all_RandomForest | negeq | impute | all | RandomForest | 0.110482 | 0.014780 | 0.008312 | 1.000000 | 0.019048 | [[3, 1751], [0, 34]] | 0.001710 | 0.037383 | 0.020694 |
| 46 | ppk_keep_all_RandomForest | ppk | keep | all | RandomForest | 0.500955 | 0.754347 | 0.102608 | 0.864078 | 0.504249 | [[270, 175], [28, 178]] | 0.606742 | 0.636852 | 0.688172 |
| 47 | ppk_keep_corr_CatBoost | ppk | keep | corr | CatBoost | 0.571880 | 0.747768 | 0.009481 | 0.883495 | 0.490566 | [[256, 189], [24, 182]] | 0.575281 | 0.630849 | 0.672811 |
| 48 | ppk_keep_corr_RandomForest | ppk | keep | corr | RandomForest | 0.516061 | 0.744832 | 0.097407 | 0.873786 | 0.520231 | [[279, 166], [26, 180]] | 0.626966 | 0.652174 | 0.705069 |
| 49 | ppk_keep_all_CatBoost | ppk | keep | all | CatBoost | 0.574503 | 0.739800 | 0.018137 | 0.898058 | 0.456790 | [[225, 220], [21, 185]] | 0.505618 | 0.605565 | 0.629800 |
| 50 | ppk_keep_all_XGBoost | ppk | keep | all | XGBoost | 0.548289 | 0.716612 | 0.015902 | 0.898058 | 0.469543 | [[236, 209], [21, 185]] | 0.530337 | 0.616667 | 0.646697 |
| 51 | ppk_keep_corr_LightGBM | ppk | keep | corr | LightGBM | 0.556769 | 0.710270 | 0.008526 | 0.941748 | 0.425439 | [[183, 262], [12, 194]] | 0.411236 | 0.586103 | 0.579109 |
| 52 | ppk_keep_corr_XGBoost | ppk | keep | corr | XGBoost | 0.542239 | 0.706544 | 0.030403 | 0.849515 | 0.513196 | [[279, 166], [31, 175]] | 0.626966 | 0.639854 | 0.697389 |
| 53 | ppk_keep_all_LightGBM | ppk | keep | all | LightGBM | 0.562097 | 0.705019 | 0.002538 | 0.898058 | 0.417607 | [[187, 258], [21, 185]] | 0.420225 | 0.570108 | 0.571429 |
| 54 | ppk_impute_all_RandomForest | ppk | impute | all | RandomForest | 0.369765 | 0.364025 | 0.071357 | 0.961165 | 0.316294 | [[17, 428], [8, 198]] | 0.038202 | 0.475962 | 0.330261 |
| 55 | ppk_impute_corr_RandomForest | ppk | impute | corr | RandomForest | 0.343327 | 0.349207 | 0.068764 | 1.000000 | 0.316436 | [[0, 445], [0, 206]] | 0.000000 | 0.480747 | 0.316436 |
| 56 | ppk_impute_pca_RandomForest | ppk | impute | pca | RandomForest | 0.339715 | 0.345245 | 0.110507 | 0.563107 | 0.324022 | [[203, 242], [90, 116]] | 0.456180 | 0.411348 | 0.490015 |
| 57 | ppk_impute_pca_XGBoost | ppk | impute | pca | XGBoost | 0.378230 | 0.331170 | 0.063258 | 0.655340 | 0.327670 | [[168, 277], [71, 135]] | 0.377528 | 0.436893 | 0.465438 |
| 58 | ppk_impute_pca_AdaBoost | ppk | impute | pca | AdaBoost | 0.310142 | 0.330281 | 0.429521 | 0.233010 | 0.317881 | [[342, 103], [158, 48]] | 0.768539 | 0.268908 | 0.599078 |
| 59 | ppk_impute_pca_CatBoost | ppk | impute | pca | CatBoost | 0.391594 | 0.329774 | 0.036931 | 0.713592 | 0.307531 | [[114, 331], [59, 147]] | 0.256180 | 0.429825 | 0.400922 |
| 60 | ppk_impute_pca_LightGBM | ppk | impute | pca | LightGBM | 0.383868 | 0.320410 | 0.001057 | 0.757282 | 0.317719 | [[110, 335], [50, 156]] | 0.247191 | 0.447633 | 0.408602 |
| 61 | ppk_impute_corr_XGBoost | ppk | impute | corr | XGBoost | 0.373046 | 0.312541 | 0.074998 | 1.000000 | 0.316436 | [[0, 445], [0, 206]] | 0.000000 | 0.480747 | 0.316436 |
| 62 | ppk_impute_all_XGBoost | ppk | impute | all | XGBoost | 0.381881 | 0.303941 | 0.012297 | 0.995146 | 0.315871 | [[1, 444], [1, 205]] | 0.002247 | 0.479532 | 0.316436 |
| 63 | ppk_impute_corr_LightGBM | ppk | impute | corr | LightGBM | 0.382905 | 0.303882 | 0.004447 | 0.995146 | 0.315385 | [[0, 445], [1, 205]] | 0.000000 | 0.478972 | 0.314900 |
| 64 | ppk_impute_all_LightGBM | ppk | impute | all | LightGBM | 0.401347 | 0.303398 | 0.000034 | 0.849515 | 0.303819 | [[44, 401], [31, 175]] | 0.098876 | 0.447570 | 0.336406 |
| 65 | ppk_impute_corr_CatBoost | ppk | impute | corr | CatBoost | 0.407648 | 0.301576 | 0.004015 | 0.975728 | 0.317035 | [[12, 433], [5, 201]] | 0.026966 | 0.478571 | 0.327189 |
| 66 | ppk_impute_corr_AdaBoost | ppk | impute | corr | AdaBoost | 0.320983 | 0.295450 | 0.270696 | 0.631068 | 0.323383 | [[173, 272], [76, 130]] | 0.388764 | 0.427632 | 0.465438 |
| 67 | ppk_impute_all_CatBoost | ppk | impute | all | CatBoost | 0.409952 | 0.294545 | 0.004135 | 0.985437 | 0.315217 | [[4, 441], [3, 203]] | 0.008989 | 0.477647 | 0.317972 |
| 68 | ppk_impute_all_AdaBoost | ppk | impute | all | AdaBoost | 0.310547 | 0.282853 | 0.352924 | 0.233010 | 0.247423 | [[299, 146], [158, 48]] | 0.671910 | 0.240000 | 0.533026 |
print(df_final_summary.to_csv(index=False))Experiment_ID,Target,Null_Strat,Feat_Strat,Model,Optuna_PR_AUC,Test_PR_AUC,Best_Threshold,Test_Recall,Test_Precision,Confusion_Matrix,Test_Specificity,Test_F1,Test_Accuracy
conloss_keep_corr_CatBoost,conloss,keep,corr,CatBoost,0.5793629045077039,0.6247402413159118,0.05833520459810593,0.9493670886075949,0.3073770491803279,"[[731, 338], [8, 150]]",0.6838166510757717,0.46439628482972134,0.7180114099429503
conloss_keep_all_XGBoost,conloss,keep,all,XGBoost,0.5714215738607572,0.6238310558219972,0.06068401038646698,0.9430379746835443,0.2991967871485944,"[[720, 349], [9, 149]]",0.6735266604303087,0.45426829268292684,0.7082314588427058
conloss_keep_corr_LightGBM,conloss,keep,corr,LightGBM,0.5674394091033492,0.6234100557004398,0.0026665631522638364,0.9367088607594937,0.2730627306273063,"[[675, 394], [10, 148]]",0.6314312441534145,0.4228571428571429,0.6707416462917686
conloss_keep_corr_XGBoost,conloss,keep,corr,XGBoost,0.5669690192635685,0.6178372289627286,0.06285607069730759,0.9493670886075949,0.2952755905511811,"[[711, 358], [8, 150]]",0.6651075771749299,0.45045045045045046,0.7017114914425427
conloss_keep_all_LightGBM,conloss,keep,all,LightGBM,0.566194207254477,0.6091903176556698,0.023755998304761973,0.9367088607594937,0.2829827915869981,"[[694, 375], [10, 148]]",0.6492048643592142,0.434654919236417,0.6862265688671557
conloss_keep_all_CatBoost,conloss,keep,all,CatBoost,0.5831806230254756,0.5993776393815425,0.04315520980250645,0.9430379746835443,0.27239488117001825,"[[671, 398], [9, 149]]",0.627689429373246,0.4226950354609929,0.6682966585167074
conloss_keep_all_RandomForest,conloss,keep,all,RandomForest,0.5440121230786213,0.5932626457275421,0.10313216101867166,0.9620253164556962,0.2980392156862745,"[[711, 358], [6, 152]]",0.6651075771749299,0.4550898203592814,0.7033414832925835
conloss_keep_corr_RandomForest,conloss,keep,corr,RandomForest,0.5422811197304024,0.5602321293019342,0.09407666229288404,0.9620253164556962,0.2940038684719536,"[[704, 365], [6, 152]]",0.6585594013096352,0.45037037037037037,0.6976365118174409
conloss_impute_pca_AdaBoost,conloss,impute,pca,AdaBoost,0.1857544125047856,0.15965537559102566,0.3950771133294802,0.16455696202531644,0.1897810218978102,"[[958, 111], [132, 26]]",0.8961646398503275,0.17627118644067796,0.8019559902200489
conloss_impute_corr_RandomForest,conloss,impute,corr,RandomForest,0.23653467800639058,0.15402029646853876,0.07632841380226682,0.9683544303797469,0.12878787878787878,"[[34, 1035], [5, 153]]",0.031805425631431246,0.22734026745913818,0.15240423797881011
conloss_impute_all_RandomForest,conloss,impute,all,RandomForest,0.22719221241109172,0.15298346799248047,0.07689151930468276,0.9746835443037974,0.12908633696563285,"[[30, 1039], [4, 154]]",0.02806361085126286,0.22797927461139897,0.14995925020374898
conloss_impute_pca_RandomForest,conloss,impute,pca,RandomForest,0.19352154293774204,0.1478716269958704,0.07417635658914729,0.8291139240506329,0.12669245647969052,"[[166, 903], [27, 131]]",0.15528531337698784,0.2197986577181208,0.24205378973105135
conloss_impute_all_XGBoost,conloss,impute,all,XGBoost,0.21632282201956754,0.14587163460566516,0.07634969055652618,1.0,0.12876935615321924,"[[0, 1069], [0, 158]]",0.0,0.22815884476534296,0.12876935615321924
conloss_impute_pca_CatBoost,conloss,impute,pca,CatBoost,0.17835978340452197,0.13832338638815062,0.07269169798521348,0.8987341772151899,0.12644701691896706,"[[88, 981], [16, 142]]",0.0823199251637044,0.22170179547228727,0.18744906275468623
conloss_impute_corr_CatBoost,conloss,impute,corr,CatBoost,0.21281794433885776,0.13526834102997842,0.08380675600146752,0.9683544303797469,0.1270764119601329,"[[18, 1051], [5, 153]]",0.01683816651075772,0.22466960352422907,0.1393643031784841
conloss_impute_all_CatBoost,conloss,impute,all,CatBoost,0.20588719720895562,0.13508852089090406,0.0819846305283936,0.9746835443037974,0.12790697674418605,"[[19, 1050], [4, 154]]",0.01777362020579981,0.2261380323054332,0.14099429502852487
conloss_impute_pca_XGBoost,conloss,impute,pca,XGBoost,0.17374300825837827,0.1331315465042961,0.024094466120004654,0.8291139240506329,0.12983151635282458,"[[191, 878], [27, 131]]",0.17867165575304023,0.22450728363324765,0.2624286878565607
conloss_impute_pca_LightGBM,conloss,impute,pca,LightGBM,0.1714697473030024,0.13077675434049707,0.0014064269861669264,0.9683544303797469,0.1303236797274276,"[[48, 1021], [5, 153]]",0.04490177736202058,0.22972972972972974,0.16381418092909536
conloss_impute_corr_LightGBM,conloss,impute,corr,LightGBM,0.21054268309709537,0.1300563382162916,0.07801975274385842,1.0,0.12876935615321924,"[[0, 1069], [0, 158]]",0.0,0.22815884476534296,0.12876935615321924
conloss_impute_all_AdaBoost,conloss,impute,all,AdaBoost,0.19122785498322162,0.1292274359717539,0.16221623091726856,0.8037974683544303,0.12674650698602793,"[[194, 875], [31, 127]]",0.1814780168381665,0.2189655172413793,0.2616136919315403
conloss_impute_all_LightGBM,conloss,impute,all,LightGBM,0.20748179522070567,0.12911705586371353,0.07720331008273537,1.0,0.12876935615321924,"[[0, 1069], [0, 158]]",0.0,0.22815884476534296,0.12876935615321924
conloss_impute_corr_XGBoost,conloss,impute,corr,XGBoost,0.21824966351127129,0.12745308222418245,0.07830634713172913,1.0,0.12876935615321924,"[[0, 1069], [0, 158]]",0.0,0.22815884476534296,0.12876935615321924
conloss_impute_corr_AdaBoost,conloss,impute,corr,AdaBoost,0.1855365946860566,0.12527718659682988,0.18046469323195244,0.8481012658227848,0.12786259541984732,"[[155, 914], [24, 134]]",0.1449953227315248,0.2222222222222222,0.23553382233088835
negeq_keep_all_RandomForest,negeq,keep,all,RandomForest,0.4171470873591485,0.4674422502757218,0.059480461812911195,0.8529411764705882,0.16666666666666666,"[[1609, 145], [5, 29]]",0.9173318129988598,0.27884615384615385,0.9161073825503355
negeq_keep_corr_RandomForest,negeq,keep,corr,RandomForest,0.4463202572195174,0.4581926619607342,0.05046650879984213,0.8529411764705882,0.1228813559322034,"[[1547, 207], [5, 29]]",0.8819840364880274,0.21481481481481482,0.8814317673378076
negeq_keep_all_CatBoost,negeq,keep,all,CatBoost,0.5297020015536803,0.4040386068967264,0.012688657018005924,0.8235294117647058,0.14213197969543148,"[[1585, 169], [6, 28]]",0.9036488027366021,0.24242424242424243,0.9021252796420581
negeq_keep_corr_CatBoost,negeq,keep,corr,CatBoost,0.49866201483402844,0.3816117551233172,0.007393605695564846,0.7941176470588235,0.12053571428571429,"[[1557, 197], [7, 27]]",0.8876852907639681,0.20930232558139536,0.8859060402684564
negeq_keep_all_LightGBM,negeq,keep,all,LightGBM,0.5141002064384389,0.30394234176699225,0.0008399380078398809,0.7352941176470589,0.058004640371229696,"[[1348, 406], [9, 25]]",0.7685290763968073,0.10752688172043011,0.7678970917225951
negeq_keep_corr_LightGBM,negeq,keep,corr,LightGBM,0.49592921187427663,0.29992591793856643,0.00013188201136054325,0.7647058823529411,0.05295315682281059,"[[1289, 465], [8, 26]]",0.7348916761687572,0.09904761904761905,0.7354586129753915
negeq_keep_corr_XGBoost,negeq,keep,corr,XGBoost,0.4287427051530059,0.2694523037878131,0.01108479779213667,0.7058823529411765,0.11428571428571428,"[[1568, 186], [10, 24]]",0.8939566704675028,0.19672131147540983,0.8903803131991052
negeq_keep_all_XGBoost,negeq,keep,all,XGBoost,0.42674578858055773,0.26519646712233774,0.011342362500727177,0.6764705882352942,0.11386138613861387,"[[1575, 179], [11, 23]]",0.8979475484606614,0.19491525423728814,0.8937360178970917
negeq_impute_all_AdaBoost,negeq,impute,all,AdaBoost,0.09006153985655257,0.026135443349151966,0.3452967440652882,0.4411764705882353,0.01800720288115246,"[[936, 818], [19, 15]]",0.5336374002280502,0.03460207612456748,0.5318791946308725
negeq_impute_pca_AdaBoost,negeq,impute,pca,AdaBoost,0.08474752808297803,0.025583227790239964,0.24982568084045162,0.0,0.0,"[[1753, 1], [34, 0]]",0.999429874572406,0.0,0.9804250559284117
negeq_impute_pca_XGBoost,negeq,impute,pca,XGBoost,0.08199934661052383,0.020722971300160164,0.008357434533536434,0.9705882352941176,0.019549763033175356,"[[99, 1655], [1, 33]]",0.056442417331812995,0.03832752613240418,0.0738255033557047
negeq_impute_pca_LightGBM,negeq,impute,pca,LightGBM,0.09466597699153358,0.020156313564060845,0.007491902745421547,1.0,0.01901565995525727,"[[0, 1754], [0, 34]]",0.0,0.03732162458836443,0.01901565995525727
negeq_impute_corr_AdaBoost,negeq,impute,corr,AdaBoost,0.0845433379733001,0.019999861145556137,0.39055291556829064,0.7647058823529411,0.01915991156963891,"[[423, 1331], [8, 26]]",0.2411630558722919,0.037383177570093455,0.2511185682326622
negeq_impute_corr_CatBoost,negeq,impute,corr,CatBoost,0.09825068374924294,0.01824076491843875,0.11106818494619851,1.0,0.01901565995525727,"[[0, 1754], [0, 34]]",0.0,0.03732162458836443,0.01901565995525727
negeq_impute_pca_CatBoost,negeq,impute,pca,CatBoost,0.10885720738286374,0.018041963090630178,0.027811120171266263,0.9705882352941176,0.01905311778290993,"[[55, 1699], [1, 33]]",0.03135689851767389,0.03737259343148358,0.049217002237136466
negeq_impute_pca_RandomForest,negeq,impute,pca,RandomForest,0.11967706515387207,0.01796070089458172,0.009023368159728705,1.0,0.01901565995525727,"[[0, 1754], [0, 34]]",0.0,0.03732162458836443,0.01901565995525727
negeq_impute_all_LightGBM,negeq,impute,all,LightGBM,0.08986413743270867,0.017722029444499264,2.6023672887597216e-05,0.6764705882352942,0.018638573743922204,"[[543, 1211], [11, 23]]",0.30957810718358036,0.03627760252365931,0.31655480984340045
negeq_impute_all_XGBoost,negeq,impute,all,XGBoost,0.11007164005631398,0.017493939834249878,0.008673484437167645,1.0,0.019079685746352413,"[[6, 1748], [0, 34]]",0.0034207525655644243,0.037444933920704845,0.02237136465324385
negeq_impute_corr_XGBoost,negeq,impute,corr,XGBoost,0.1066687061960185,0.01714568745360183,0.00426494050770998,1.0,0.019551466359977,"[[49, 1705], [0, 34]]",0.027936145952109463,0.03835307388606881,0.046420581655480984
negeq_impute_all_CatBoost,negeq,impute,all,CatBoost,0.09700003338965547,0.016888990127705138,0.020205113489548995,1.0,0.01951779563719862,"[[46, 1708], [0, 34]]",0.026225769669327253,0.038288288288288286,0.0447427293064877
negeq_impute_corr_LightGBM,negeq,impute,corr,LightGBM,0.1077721254433791,0.016013899660255144,4.513603303341643e-07,0.6470588235294118,0.017027863777089782,"[[484, 1270], [12, 22]]",0.2759407069555302,0.033182503770739065,0.2829977628635347
negeq_impute_corr_RandomForest,negeq,impute,corr,RandomForest,0.10901927863599276,0.014907448215334046,0.008418424698410443,0.9705882352941176,0.018570624648283626,"[[10, 1744], [1, 33]]",0.005701254275940707,0.036443953616786304,0.024049217002237135
negeq_impute_all_RandomForest,negeq,impute,all,RandomForest,0.11048189870483469,0.014780235261005113,0.008311750428088816,1.0,0.01904761904761905,"[[3, 1751], [0, 34]]",0.0017103762827822121,0.037383177570093455,0.02069351230425056
ppk_keep_all_RandomForest,ppk,keep,all,RandomForest,0.5009549492844173,0.7543469006969028,0.1026076616985708,0.8640776699029126,0.5042492917847026,"[[270, 175], [28, 178]]",0.6067415730337079,0.6368515205724508,0.6881720430107527
ppk_keep_corr_CatBoost,ppk,keep,corr,CatBoost,0.5718798405170432,0.7477682949046598,0.009480744680310961,0.883495145631068,0.49056603773584906,"[[256, 189], [24, 182]]",0.5752808988764045,0.6308492201039861,0.6728110599078341
ppk_keep_corr_RandomForest,ppk,keep,corr,RandomForest,0.5160614227882753,0.7448321467620256,0.09740742823844975,0.8737864077669902,0.5202312138728323,"[[279, 166], [26, 180]]",0.6269662921348315,0.6521739130434783,0.7050691244239631
ppk_keep_all_CatBoost,ppk,keep,all,CatBoost,0.5745031086299868,0.7397997241048739,0.01813688630925825,0.8980582524271845,0.4567901234567901,"[[225, 220], [21, 185]]",0.5056179775280899,0.6055646481178396,0.6298003072196621
ppk_keep_all_XGBoost,ppk,keep,all,XGBoost,0.548289360120827,0.7166120551596885,0.015902331098914146,0.8980582524271845,0.46954314720812185,"[[236, 209], [21, 185]]",0.5303370786516854,0.6166666666666667,0.6466973886328725
ppk_keep_corr_LightGBM,ppk,keep,corr,LightGBM,0.556768921077627,0.7102702588981185,0.008526471325700261,0.941747572815534,0.42543859649122806,"[[183, 262], [12, 194]]",0.41123595505617977,0.5861027190332326,0.5791090629800307
ppk_keep_corr_XGBoost,ppk,keep,corr,XGBoost,0.5422394181887022,0.7065437167917236,0.03040345199406147,0.8495145631067961,0.5131964809384164,"[[279, 166], [31, 175]]",0.6269662921348315,0.6398537477148081,0.6973886328725039
ppk_keep_all_LightGBM,ppk,keep,all,LightGBM,0.5620967724601429,0.7050192204622268,0.0025382668280570612,0.8980582524271845,0.417607223476298,"[[187, 258], [21, 185]]",0.4202247191011236,0.5701078582434514,0.5714285714285714
ppk_impute_all_RandomForest,ppk,impute,all,RandomForest,0.36976494064866383,0.3640249704273563,0.07135664391305577,0.9611650485436893,0.31629392971246006,"[[17, 428], [8, 198]]",0.038202247191011236,0.47596153846153844,0.3302611367127496
ppk_impute_corr_RandomForest,ppk,impute,corr,RandomForest,0.3433272032810414,0.34920716385216366,0.06876373959258938,1.0,0.31643625192012287,"[[0, 445], [0, 206]]",0.0,0.4807467911318553,0.31643625192012287
ppk_impute_pca_RandomForest,ppk,impute,pca,RandomForest,0.33971520610202244,0.3452447976393893,0.11050689486787096,0.5631067961165048,0.3240223463687151,"[[203, 242], [90, 116]]",0.45617977528089887,0.41134751773049644,0.49001536098310294
ppk_impute_pca_XGBoost,ppk,impute,pca,XGBoost,0.3782298655516516,0.33117039734293896,0.06325820833444595,0.6553398058252428,0.3276699029126214,"[[168, 277], [71, 135]]",0.3775280898876405,0.4368932038834951,0.46543778801843316
ppk_impute_pca_AdaBoost,ppk,impute,pca,AdaBoost,0.3101419855232404,0.3302806030032098,0.4295207244500669,0.23300970873786409,0.31788079470198677,"[[342, 103], [158, 48]]",0.7685393258426966,0.2689075630252101,0.5990783410138248
ppk_impute_pca_CatBoost,ppk,impute,pca,CatBoost,0.39159352562580385,0.3297741572053883,0.036930656688580636,0.7135922330097088,0.3075313807531381,"[[114, 331], [59, 147]]",0.25617977528089886,0.4298245614035088,0.4009216589861751
ppk_impute_pca_LightGBM,ppk,impute,pca,LightGBM,0.3838678068846172,0.3204096798568804,0.001057382472860725,0.7572815533980582,0.31771894093686354,"[[110, 335], [50, 156]]",0.24719101123595505,0.44763271162123386,0.40860215053763443
ppk_impute_corr_XGBoost,ppk,impute,corr,XGBoost,0.3730458741905497,0.3125408709685088,0.07499814033508301,1.0,0.31643625192012287,"[[0, 445], [0, 206]]",0.0,0.4807467911318553,0.31643625192012287
ppk_impute_all_XGBoost,ppk,impute,all,XGBoost,0.38188111933357916,0.3039408264470023,0.012296569533646107,0.9951456310679612,0.31587057010785824,"[[1, 444], [1, 205]]",0.0022471910112359553,0.47953216374269003,0.31643625192012287
ppk_impute_corr_LightGBM,ppk,impute,corr,LightGBM,0.38290476992270606,0.3038816889339783,0.004446654234049561,0.9951456310679612,0.3153846153846154,"[[0, 445], [1, 205]]",0.0,0.47897196261682246,0.31490015360983103
ppk_impute_all_LightGBM,ppk,impute,all,LightGBM,0.4013469597133256,0.3033984859011807,3.415264436267724e-05,0.8495145631067961,0.3038194444444444,"[[44, 401], [31, 175]]",0.09887640449438202,0.4475703324808184,0.33640552995391704
ppk_impute_corr_CatBoost,ppk,impute,corr,CatBoost,0.40764813662598715,0.30157573733219123,0.00401453094057985,0.9757281553398058,0.31703470031545744,"[[12, 433], [5, 201]]",0.02696629213483146,0.4785714285714286,0.3271889400921659
ppk_impute_corr_AdaBoost,ppk,impute,corr,AdaBoost,0.3209833269534381,0.29544979984519526,0.2706962006722341,0.6310679611650486,0.32338308457711445,"[[173, 272], [76, 130]]",0.3887640449438202,0.4276315789473684,0.46543778801843316
ppk_impute_all_CatBoost,ppk,impute,all,CatBoost,0.40995220407316535,0.2945449162873124,0.004135391656362598,0.9854368932038835,0.31521739130434784,"[[4, 441], [3, 203]]",0.008988764044943821,0.4776470588235294,0.31797235023041476
ppk_impute_all_AdaBoost,ppk,impute,all,AdaBoost,0.3105474759887357,0.28285294542946227,0.35292381769287745,0.23300970873786409,0.24742268041237114,"[[299, 146], [158, 48]]",0.6719101123595506,0.24,0.533026113671275
Performa vs Baseline
# 1. Persiapan Data
indikator = ['Papan Pemantauan\nKhusus', 'Ekuitas\nNegatif', 'Rugi\nBerturut-turut']
baseline = [31.6, 1.9, 12.9]
performa = [75.4, 46.7, 62.5]
# Menentukan lokasi koordinat sumbu X
x = np.arange(len(indikator))
# 2. Inisialisasi Kanvas Plot
fig, ax = plt.subplots(figsize=(10, 7))
# 3. Pembuatan Bar Overlay (Tumpang Tindih)
ax.bar(x, baseline, width=0.6, color='gray', alpha=0.4, label='Baseline PR-AUC')
ax.bar(x, performa, width=0.3, color='seagreen', alpha=0.9, label='Performa Aktual')
# 4. Anotasi Angka Persentase & Multiplier
for i in range(len(x)):
# Membagi performa aktual dengan baseline untuk mencari tahu berapa kali lipat kenaikannya
kelipatan = performa[i] / baseline[i]
# Mencetak persentase baseline
ax.text(x[i] - 0.32, baseline[i] + 0.5, f"{baseline[i]}%",
ha='right', va='bottom', fontsize=11, fontweight='bold', color='dimgray')
# Mencetak persentase performa aktual
ax.text(x[i], performa[i] + 1.5, f"{performa[i]}%",
ha='center', va='bottom', fontsize=12, fontweight='bold', color='darkgreen')
# --- PERBAIKAN UKURAN BADGE KELIPATAN ---
# 1. Jarak vertikal dinaikkan (+ 8.0) agar tidak menabrak teks persentase
# 2. fontsize dinaikkan dari 10 menjadi 14
# 3. pad (bantalan kotak) ditebalkan dari 0.3 menjadi 0.6
ax.text(x[i], performa[i] + 8.0, f" {kelipatan:.1f}x Lipat ",
ha='center', va='bottom', fontsize=14, fontweight='bold', color='white',
bbox=dict(facecolor='darkorange', edgecolor='none', boxstyle='round,pad=0.6', alpha=0.9))
# 5. Konfigurasi Kosmetik Sumbu dan Judul
ax.set_ylabel('Skor PR-AUC (%)', fontsize=14, fontweight='bold', labelpad=10)
ax.set_title('Perbandingan Performa Model terhadap Baseline', fontsize=18, fontweight='bold', pad=20)
ax.set_xticks(x)
ax.set_xticklabels(indikator, fontsize=12, fontweight='bold')
# --- PERBAIKAN LIMIT SUMBU Y ---
# Batas atas dinaikkan menjadi 110 agar kotak kelipatan yang sekarang menjadi raksasa tidak terpotong
ax.set_ylim(0, 110)
ax.grid(axis='y', linestyle='--', alpha=0.4)
# Menempatkan kotak legenda di pojok kanan atas
ax.legend(loc='upper right', fontsize=12, framealpha=0.9)
# Merapikan tata letak
plt.tight_layout()
plt.show()
Feature Importance
FI Target Distress Papan Pemantauan Khusus
ppk_best_configs = ['ppk_keep_all_RandomForest','ppk_keep_corr_CatBoost']# [FIX] Sembunyikan warning tight_layout (tidak berbahaya, hanya informasi)
warnings.filterwarnings("ignore", message="This figure includes Axes that are not compatible with tight_layout")
# [FIX] Set DPI tinggi & font dasar lebih besar
plt.rcParams['figure.dpi'] = 150
plt.rcParams['savefig.dpi'] = 300
plt.rcParams['font.size'] = 16
n_rows = len(ppk_best_configs)
n_cols = 3
# [FIX] Kanvas diperlebar agar plot kanan punya ruang untuk digeser ke kanan
fig, axes = plt.subplots(nrows=n_rows, ncols=n_cols, figsize=(33, 8 * n_rows),
gridspec_kw={'width_ratios': [1.3, 1.55, 1.05], 'wspace': 0.18})
fig.suptitle("Pengaruh dan Kontribusi Fitur\nIndikator Papan Pemantauan Khusus",
fontsize=26, fontweight='bold', y=0.97)
axes = np.atleast_2d(axes)
for i, chosen_id in enumerate(ppk_best_configs):
ax_shap = axes[i, 0]
ax_spearman = axes[i, 1]
ax_imp = axes[i, 2]
chosen_model = dict_fitted_models[chosen_id]
X_test_clean_chosen = dict_detailed_predictions[chosen_id]['X_test_features'].copy()
X_test_clean_chosen = X_test_clean_chosen.fillna(np.nan).astype(float)
explainer = shap.TreeExplainer(chosen_model)
shap_values = explainer.shap_values(X_test_clean_chosen)
if isinstance(shap_values, list):
shap_values_plot = shap_values[1]
elif len(shap_values.shape) == 3:
shap_values_plot = shap_values[:, :, 1]
else:
shap_values_plot = shap_values
mean_abs_shap = np.abs(shap_values_plot).mean(axis=0)
df_shap_order = pd.DataFrame({'Feature': X_test_clean_chosen.columns, 'Mean_Abs_SHAP': mean_abs_shap})
top_15_features_shap = df_shap_order.sort_values(by='Mean_Abs_SHAP', ascending=False)['Feature'].head(15).tolist()
# ----------------------------------------------------------
# KOLOM 1: SHAP PLOT
# ----------------------------------------------------------
plt.sca(ax_shap)
shap.summary_plot(shap_values_plot, X_test_clean_chosen, max_display=15,
show=False, plot_size=None)
ax_shap.set_title("SHAP Values Summary", fontsize=20, fontweight='bold', pad=15)
if "RandomForest" in chosen_model.__class__.__name__:
ax_shap.set_xlabel("SHAP value (Impact on Probability)", fontsize=18)
else:
ax_shap.set_xlabel("SHAP value (Impact on Log-Odds)", fontsize=18)
ax_shap.tick_params(axis='y', labelsize=18)
ax_shap.tick_params(axis='x', labelsize=16)
for fig_ax in fig.axes:
if fig_ax.get_ylabel() == "Feature value":
fig_ax.set_ylabel("Feature value", fontsize=17)
fig_ax.tick_params(labelsize=16)
ax_shap.annotate(chosen_id,
xy=(-0.85, 0.5), xycoords='axes fraction',
rotation=90, va='center', ha='center',
fontsize=20, fontweight='bold', color='black',
clip_on=False)
# ----------------------------------------------------------
# KOLOM 2: ARAH KORELASI SPEARMAN
# ----------------------------------------------------------
spearman_rows = []
for j, col in enumerate(X_test_clean_chosen.columns):
feature_vals = X_test_clean_chosen.iloc[:, j]
shap_vals = pd.Series(shap_values_plot[:, j], index=feature_vals.index)
if feature_vals.nunique() > 1 and shap_vals.nunique() > 1:
corr = feature_vals.corr(shap_vals, method='spearman')
corr = 0.0 if pd.isna(corr) else corr
else:
corr = 0.0
spearman_rows.append({'Feature': col, 'Spearman_Corr': corr})
df_spearman = pd.DataFrame(spearman_rows)
df_spearman = df_spearman.set_index('Feature').loc[top_15_features_shap].reset_index()
colors = ['#c62828' if val > 0 else '#2e7d32' for val in df_spearman['Spearman_Corr']]
sns.barplot(x='Spearman_Corr', y='Feature', data=df_spearman,
hue='Feature', palette=colors, legend=False, ax=ax_spearman)
ax_spearman.axvline(0, color='black', linewidth=1.2)
ax_spearman.set_title("Arah Pengaruh (Spearman Correlation)", fontsize=20, fontweight='bold', pad=15)
ax_spearman.set_ylabel('')
ax_spearman.set_yticks([])
ax_spearman.set_xlabel("Spearman r (Positif = Memperparah Distress)", fontsize=18)
ax_spearman.tick_params(axis='x', labelsize=16)
ax_spearman.locator_params(axis='x', nbins=8)
max_abs_corr = df_spearman['Spearman_Corr'].abs().max()
xlim_val = max(max_abs_corr * 1.15, 0.1)
ax_spearman.set_xlim(-xlim_val, xlim_val)
ax_spearman.margins(x=0)
# ----------------------------------------------------------
# KOLOM 3: FEATURE IMPORTANCE
# ----------------------------------------------------------
importance = chosen_model.feature_importances_
df_imp = pd.DataFrame({'Feature': X_test_clean_chosen.columns, 'Importance': importance})
df_imp = df_imp.sort_values(by='Importance', ascending=False).head(15)
sns.barplot(x='Importance', y='Feature', data=df_imp, hue='Feature',
palette='viridis', legend=False, ax=ax_imp)
ax_imp.set_title("Top 15 Feature Importance (Model)", fontsize=20, fontweight='bold', pad=15)
ax_imp.set_ylabel('')
ax_imp.set_xlabel("Importance", fontsize=18)
ax_imp.tick_params(axis='y', labelsize=18)
ax_imp.tick_params(axis='x', labelsize=16)
# ==========================================================
# PENYESUAIAN MARGIN (kembali ke tight_layout seperti sebelumnya)
# ==========================================================
plt.tight_layout(rect=[0.08, 0, 1, 0.93])
# [FIX] Geser hanya plot kanan (Feature Importance) lebih ke kanan,
# tanpa mengubah posisi plot SHAP & Spearman
fig.canvas.draw()
SHIFT = 0.035 # seberapa jauh plot kanan digeser ke kanan (dalam koordinat figure 0-1)
for i in range(n_rows):
pos_imp = axes[i, 2].get_position()
new_pos_imp = [pos_imp.x0 + SHIFT, pos_imp.y0, pos_imp.width, pos_imp.height]
axes[i, 2].set_position(new_pos_imp)
# [FIX] Gambar garis pembatas vertikal di celah antara Spearman & Feature Importance
# (posisi dihitung ulang setelah pergeseran di atas)
for i in range(n_rows):
bbox_spearman = axes[i, 1].get_position()
bbox_imp = axes[i, 2].get_position()
x_sep = (bbox_spearman.x1 + bbox_imp.x0) / 2
y0 = bbox_imp.y0 - 0.01
y1 = bbox_imp.y1 + 0.01
line = Line2D([x_sep, x_sep], [y0, y1], transform=fig.transFigure,
color='gray', linestyle='--', linewidth=1.5, alpha=0.7)
line.set_clip_on(False)
fig.add_artist(line)
plt.savefig("ppk_shap_summary.png", dpi=300, bbox_inches='tight')
plt.show()
for chosen_id in ppk_best_configs:
chosen_model = dict_fitted_models[chosen_id]
X_test_clean_chosen = dict_detailed_predictions[chosen_id]['X_test_features'].copy()
X_test_clean_chosen = X_test_clean_chosen.fillna(np.nan).astype(float)
explainer = shap.TreeExplainer(chosen_model)
shap_values = explainer.shap_values(X_test_clean_chosen)
if isinstance(shap_values, list):
shap_values_plot = shap_values[1]
elif len(shap_values.shape) == 3:
shap_values_plot = shap_values[:, :, 1]
else:
shap_values_plot = shap_values
# ----------------------------------------------------------
# DATAFRAME 1: Mean |SHAP| (Top 15) + Arah Pengaruh (Spearman)
# ----------------------------------------------------------
mean_abs_shap = np.abs(shap_values_plot).mean(axis=0)
df_shap_order = pd.DataFrame({
'Feature': X_test_clean_chosen.columns,
'Mean_Abs_SHAP': mean_abs_shap
})
top_15_shap = df_shap_order.sort_values(by='Mean_Abs_SHAP', ascending=False).head(15).reset_index(drop=True)
spearman_rows = []
for feat in top_15_shap['Feature']:
j = list(X_test_clean_chosen.columns).index(feat)
feature_vals = X_test_clean_chosen.iloc[:, j]
shap_vals = pd.Series(shap_values_plot[:, j], index=feature_vals.index)
if feature_vals.nunique() > 1 and shap_vals.nunique() > 1:
corr = feature_vals.corr(shap_vals, method='spearman')
corr = 0.0 if pd.isna(corr) else corr
else:
corr = 0.0
spearman_rows.append(corr)
top_15_shap['Spearman_Corr'] = spearman_rows
top_15_shap['Arah_Pengaruh'] = top_15_shap['Spearman_Corr'].apply(
lambda x: 'Positif (Memperparah Distress)' if x > 0
else ('Negatif (Mengurangi Distress)' if x < 0 else 'Netral')
)
df1 = top_15_shap[['Feature', 'Mean_Abs_SHAP', 'Spearman_Corr', 'Arah_Pengaruh']]
# ----------------------------------------------------------
# DATAFRAME 2: Feature Importance Model (Top 15)
# ----------------------------------------------------------
importance = chosen_model.feature_importances_
df_imp = pd.DataFrame({
'Feature': X_test_clean_chosen.columns,
'Importance': importance
})
df2 = df_imp.sort_values(by='Importance', ascending=False).head(15).reset_index(drop=True)
# ----------------------------------------------------------
# CETAK DALAM FORMAT CSV
# ----------------------------------------------------------
print(f"=== {chosen_id} - SHAP & Arah Pengaruh (Top 15) ===")
print(df1.to_csv(index=False))
print(f"=== {chosen_id} - Feature Importance (Top 15) ===")
print(df2.to_csv(index=False))=== ppk_keep_all_RandomForest - SHAP & Arah Pengaruh (Top 15) ===
Feature,Mean_Abs_SHAP,Spearman_Corr,Arah_Pengaruh
Sales_per_share,0.018086309141178362,-0.38384339191384514,Negatif (Mengurangi Distress)
log_MktCap,0.014268786710354983,-0.8890226335896925,Negatif (Mengurangi Distress)
EPS_proxy,0.013631205671255605,-0.637823597340716,Negatif (Mengurangi Distress)
log_TA,0.011865081811796682,-0.9071374420157321,Negatif (Mengurangi Distress)
CashST_TA,0.009411639257299135,-0.6333483101106295,Negatif (Mengurangi Distress)
log_Sales,0.008419484779264423,-0.8726748335772678,Negatif (Mengurangi Distress)
Parent Percent Owned (%),0.008161288728794585,0.3050614663579201,Positif (Memperparah Distress)
Sales_growth,0.007634299295551004,0.894366490572696,Positif (Memperparah Distress)
ROE,0.006183140409879697,-0.6037439188557336,Negatif (Mengurangi Distress)
NetMargin,0.006055307454870257,-0.8059455486132707,Negatif (Mengurangi Distress)
CashST_CL,0.0059698796557021965,-0.6291640479053897,Negatif (Mengurangi Distress)
Debt_Equity,0.005013153922554357,0.008124215965337342,Positif (Memperparah Distress)
CFO_per_share,0.004481925279623213,-0.686046152541304,Negatif (Mengurangi Distress)
CR,0.0044782199144499865,-0.826807235742521,Negatif (Mengurangi Distress)
ROA,0.004445465228575167,-0.57771090180352,Negatif (Mengurangi Distress)
=== ppk_keep_all_RandomForest - Feature Importance (Top 15) ===
Feature,Importance
Sales_per_share,0.03331554881196363
Sales_growth,0.03190100747862269
EPS_proxy,0.028094634551687953
CashST_TA,0.02580235904107479
log_MktCap,0.024996616645981375
NetDebt_EBITDA,0.024289307273134025
log_TA,0.024170724940866587
log_Sales,0.024168798593961992
Inventory_CA,0.02416842954461248
Prepaid_CA,0.02401141857529439
Parent Percent Owned (%),0.023334035986800297
CashST_CL,0.022305652759122403
NI_growth,0.022192215843909253
Debt_Equity,0.022071828941036584
Altman_X3_EBIT_TA,0.02184114394584633
=== ppk_keep_corr_CatBoost - SHAP & Arah Pengaruh (Top 15) ===
Feature,Mean_Abs_SHAP,Spearman_Corr,Arah_Pengaruh
Sales_per_share,0.4568988577297765,-0.6640751332577971,Negatif (Mengurangi Distress)
Sales_growth,0.3435495824134642,0.9068938597472925,Positif (Memperparah Distress)
Parent Percent Owned (%),0.2752592854519673,0.07771335190990666,Positif (Memperparah Distress)
log_Sales,0.21420615900594095,-0.8775068232776526,Negatif (Mengurangi Distress)
log_TA,0.21169342712398878,-0.8484704331884225,Negatif (Mengurangi Distress)
log_MktCap,0.18661485481868267,-0.9007935977625477,Negatif (Mengurangi Distress)
Years_Since_IPO,0.17945489253947436,0.7924165837498359,Positif (Memperparah Distress)
CashST_TA,0.14352596380794094,-0.7954335472562268,Negatif (Mengurangi Distress)
EPS_proxy,0.11901717782178424,-0.7986215130604603,Negatif (Mengurangi Distress)
Debt_Equity,0.11514953297867045,-0.40588294125013075,Negatif (Mengurangi Distress)
PPE_TA,0.10965199304196346,-0.08709612967677485,Negatif (Mengurangi Distress)
OpMargin,0.10647990876797471,-0.7224074045882872,Negatif (Mengurangi Distress)
Percent Owned - All Institutions (%),0.10060927808983898,-0.5341527830399403,Negatif (Mengurangi Distress)
Intang_TA,0.09737482079305851,-0.6781619865398005,Negatif (Mengurangi Distress)
GrossMargin,0.09484705214836112,-0.47020386097134037,Negatif (Mengurangi Distress)
=== ppk_keep_corr_CatBoost - Feature Importance (Top 15) ===
Feature,Importance
Sales_growth,5.395527823659915
Sales_per_share,4.6003206272365835
Parent Percent Owned (%),4.344947203390322
Prepaid_CA,3.4537502212256275
GrossMargin,3.208472588903182
log_Sales,3.1071012551659005
log_TA,2.9234321016382596
Inventory_CA,2.8022457339167346
log_MktCap,2.7227591028485145
PB,2.674316475030797
CA_TA,2.580351402955231
PPE_TA,2.5539363343931605
TA_growth,2.4902256663287323
NetDebt_EBITDA,2.4855775773300057
NI_growth,2.4672570524756376
for chosen_id in ppk_best_configs:
print(f"\n========== MENGANALISIS: {chosen_id} ==========")
chosen_model = dict_fitted_models[chosen_id]
X_test_clean_chosen = dict_detailed_predictions[chosen_id]['X_test_features'].copy()
X_test_clean_chosen = X_test_clean_chosen.fillna(np.nan).astype(float)
# ---------- SHAP ----------
explainer = shap.TreeExplainer(chosen_model)
shap_values = explainer.shap_values(X_test_clean_chosen)
if isinstance(shap_values, list):
shap_values_plot = shap_values[1]
elif len(shap_values.shape) == 3:
shap_values_plot = shap_values[:, :, 1]
else:
shap_values_plot = shap_values
feature_names = list(X_test_clean_chosen.columns)
mean_abs_shap = np.abs(shap_values_plot).mean(axis=0)
tree_importance = chosen_model.feature_importances_
industry_idx = [i for i, c in enumerate(feature_names) if c.startswith('industry_')]
other_idx = [i for i, c in enumerate(feature_names) if not c.startswith('industry_')]
rows = []
for i in other_idx:
feature_vals = X_test_clean_chosen.iloc[:, i]
shap_vals = pd.Series(shap_values_plot[:, i], index=feature_vals.index)
rows.append({
'Feature': feature_names[i],
'Mean_Abs_SHAP': mean_abs_shap[i],
'Tree_Importance': tree_importance[i],
'SHAP_Dir_Spearman': feature_vals.corr(shap_vals, method='spearman')
})
# ---------- Agregasi Industry Group ----------
if industry_idx:
industry_shap_rowsum = shap_values_plot[:, industry_idx].sum(axis=1)
rows.append({
'Feature': 'Industry Group (agregat)',
'Mean_Abs_SHAP': np.abs(industry_shap_rowsum).mean(),
'Tree_Importance': tree_importance[industry_idx].sum(),
'SHAP_Dir_Spearman': np.nan
})
df_importance = pd.DataFrame(rows).sort_values(
by='Mean_Abs_SHAP', ascending=False).reset_index(drop=True)
print(df_importance.to_csv(index=False))
========== MENGANALISIS: ppk_keep_all_RandomForest ==========
Feature,Mean_Abs_SHAP,Tree_Importance,SHAP_Dir_Spearman
Sales_per_share,0.018086309141178362,0.03331554881196363,-0.38384339191384514
log_MktCap,0.014268786710354983,0.024996616645981375,-0.8890226335896925
EPS_proxy,0.013631205671255605,0.028094634551687953,-0.637823597340716
log_TA,0.011865081811796682,0.024170724940866587,-0.9071374420157321
CashST_TA,0.009411639257299135,0.02580235904107479,-0.6333483101106295
log_Sales,0.008419484779264423,0.024168798593961992,-0.8726748335772678
Parent Percent Owned (%),0.008161288728794585,0.023334035986800297,0.3050614663579201
Sales_growth,0.007634299295551004,0.03190100747862269,0.894366490572696
ROE,0.006183140409879697,0.021834770589707484,-0.6037439188557336
NetMargin,0.006055307454870257,0.020272541730230628,-0.8059455486132707
CashST_CL,0.0059698796557021965,0.022305652759122403,-0.6291640479053897
Debt_Equity,0.005013153922554357,0.022071828941036584,0.008124215965337342
CFO_per_share,0.004481925279623213,0.02025783575642112,-0.686046152541304
CR,0.0044782199144499865,0.01978030876919815,-0.826807235742521
ROA,0.004445465228575167,0.0203004909662637,-0.57771090180352
Prepaid_CA,0.004360598822409726,0.02401141857529439,-0.4096175945284868
Debt_TA,0.004283306935427938,0.018876955776698765,0.1667954846529983
Years_Since_IPO,0.004099501598835011,0.016470873918940337,0.6303940449967198
NetDebt_EBITDA,0.0037926879341788106,0.024289307273134025,0.5638168069545686
PB,0.0037242212592953528,0.020715212069070434,0.17774643721605923
OpMargin,0.0037213597320908567,0.018455874048958626,-0.5644633696009117
GrossMargin,0.0035763292533206865,0.02010577506047018,-0.6477292119853809
Age_When_IPO,0.0035344789231686923,0.01672962281638334,-0.5691350414272497
TL_TA,0.00347276659835204,0.019608628123669383,-0.2960872770609561
Inventory_CA,0.0034613671950798224,0.02416842954461248,0.07788302210892036
CFO_growth,0.0033533593472055995,0.02008573642518434,-0.29041503221761444
Equity_TA,0.0033349828479915826,0.020661664497623307,0.10067973981292802
CA_TA,0.0032629989222235705,0.02079646346898236,0.013898739297512305
Percent Owned - All Institutions (%),0.003242532155906701,0.01580165475484876,-0.010643514236265177
QR,0.002984941083517711,0.019035510425341188,-0.6724598597370334
Altman_X3_EBIT_TA,0.002905041148237055,0.02184114394584633,-0.5385481753535514
Altman_X4_MVE_TL,0.002858715161499423,0.01977258034698885,-0.3273401287016961
EBITDA_TA,0.0027856843998968904,0.02123438306704055,-0.4203827446784283
Altman_X5_SalesTA_AssetTurnover,0.0027755997890706804,0.019458642501764024,0.3800515850664278
Industry Group (agregat),0.0027141401576190505,0.022460465722608555,
Intang_TA,0.0025330360810353865,0.012920873486184464,-0.42282266352816367
Equity_growth,0.0025024300318317117,0.01976764575012234,-0.4479080557449185
Percent Owned - Insiders (%),0.0024834921993119384,0.01889055293049523,-0.1045389413253252
TA_growth,0.0024377131249507114,0.021779129198522467,-0.24646389455479922
NI_growth,0.0023469232716228894,0.022192215843909253,-0.46206415160012326
Altman_X1_WC_TA,0.0021538497279937276,0.018059550691629133,-0.3145550353070638
NetCash_TA,0.002043258460940024,0.015984947847918818,-0.13271397907455695
WC_Sales,0.0020106927050525247,0.01631812381189096,-0.6556910811333927
CFO_Sales,0.0019363556360147552,0.017996715527145445,0.27753292027584525
CL_TA,0.0018686363310112494,0.01736705083019999,-0.4102551633998299
CFO_TA,0.0018658565386384966,0.016361184642392654,-0.07195429545716504
PPE_TA,0.00186404424341517,0.016652050584519008,0.46647360294161827
CFO_TL,0.0017623289929889557,0.018522460928670468,-0.07977408698564448
========== MENGANALISIS: ppk_keep_corr_CatBoost ==========
Feature,Mean_Abs_SHAP,Tree_Importance,SHAP_Dir_Spearman
Sales_per_share,0.4568988577297765,4.6003206272365835,-0.6640751332577971
Sales_growth,0.3435495824134642,5.395527823659915,0.9068938597472925
Parent Percent Owned (%),0.2752592854519673,4.344947203390322,0.07771335190990666
log_Sales,0.21420615900594095,3.1071012551659005,-0.8775068232776526
log_TA,0.21169342712398878,2.9234321016382596,-0.8484704331884225
log_MktCap,0.18661485481868267,2.7227591028485145,-0.9007935977625477
Years_Since_IPO,0.17945489253947436,2.298320240787829,0.7924165837498359
CashST_TA,0.14352596380794094,2.440960345494906,-0.7954335472562268
EPS_proxy,0.11901717782178424,2.18246145043081,-0.7986215130604603
Debt_Equity,0.11514953297867045,2.2812914278113943,-0.40588294125013075
PPE_TA,0.10965199304196346,2.5539363343931605,-0.08709612967677485
OpMargin,0.10647990876797471,1.6365973454534706,-0.7224074045882872
Percent Owned - All Institutions (%),0.10060927808983898,1.7484806025787807,-0.5341527830399403
Intang_TA,0.09737482079305851,1.8540711686019387,-0.6781619865398005
GrossMargin,0.09484705214836112,3.208472588903182,-0.47020386097134037
Age_When_IPO,0.08868723897191338,2.165030439718456,-0.8528185935257897
Percent Owned - Insiders (%),0.0876870644252517,1.9596661201469083,-0.7928384582179576
PB,0.0841211670656311,2.674316475030797,0.4977301700871857
Altman_X3_EBIT_TA,0.08395005271005772,1.903655415292499,-0.6864083103938363
Prepaid_CA,0.08334913309042867,3.4537502212256275,-0.2963204867093475
QR,0.08222387917903641,2.030750382901075,0.0017419766978667899
NetDebt_EBITDA,0.08101290532066484,2.4855775773300057,0.5322239834775373
CashST_CL,0.08057640367462898,2.1893680150746393,-0.771300478662442
Debt_TA,0.08009739936171516,2.0003435391782096,0.42486680646201064
NetMargin,0.07866334315337326,1.5619428309479266,-0.8440126309471253
CA_TA,0.07338747377541263,2.580351402955231,-0.4412437394388711
CFO_per_share,0.07282600168292785,1.8656567952246417,-0.6264755786465662
Inventory_CA,0.07244679400915192,2.8022457339167346,0.2511640658361944
CFO_growth,0.06957912236266213,2.353092377655193,-0.13416226451160917
Altman_X4_MVE_TL,0.06950280443989977,2.0038940881035585,-0.6562375957705465
WC_Sales,0.06654176505229885,1.5128700465348077,-0.7900350787150708
ROE,0.06389917087855973,2.061383292404402,-0.5419256105066516
CFO_TL,0.06374947087007647,1.96473060709826,-0.635302888285275
TA_growth,0.0636232803401504,2.4902256663287323,-0.2942924425735969
NI_growth,0.06036453479774485,2.4672570524756376,-0.6313136420726092
Equity_growth,0.05958541493863396,1.538132587234043,-0.4576320474641444
CR,0.05331244199674574,1.3413231651256114,-0.7965993915122525
CFO_Sales,0.05068120140885691,1.5047121513716637,0.5712648562599086
Equity_TA,0.05031493531020322,1.5965299147908134,-0.38511040117610473
Altman_X5_SalesTA_AssetTurnover,0.04825185115865816,2.125025286510304,0.3685367195638322
CL_TA,0.04099657263514009,1.8581461831147768,-0.09599289291749216
NetCash_TA,0.039211175576788465,1.4720644441323183,-0.11546930014375098
Industry Group (agregat),0.028782340898690677,0.7392785697821841,
FI Target Distress Ekuitas Negatif
negeq_best_configs = ['negeq_keep_all_RandomForest', 'negeq_keep_corr_RandomForest']# [FIX] Sembunyikan warning tight_layout (tidak berbahaya, hanya informasi)
warnings.filterwarnings("ignore", message="This figure includes Axes that are not compatible with tight_layout")
# [FIX] Set DPI tinggi & font dasar lebih besar
plt.rcParams['figure.dpi'] = 150
plt.rcParams['savefig.dpi'] = 300
plt.rcParams['font.size'] = 16
n_rows = len(negeq_best_configs)
n_cols = 3
# [FIX] Kanvas diperlebar agar plot kanan punya ruang untuk digeser ke kanan
fig, axes = plt.subplots(nrows=n_rows, ncols=n_cols, figsize=(33, 8 * n_rows),
gridspec_kw={'width_ratios': [1.3, 1.55, 1.05], 'wspace': 0.18})
fig.suptitle("Pengaruh dan Kontribusi Fitur\nIndikator Ekuitas Negatif",
fontsize=26, fontweight='bold', y=0.97)
axes = np.atleast_2d(axes)
for i, chosen_id in enumerate(negeq_best_configs):
ax_shap = axes[i, 0]
ax_spearman = axes[i, 1]
ax_imp = axes[i, 2]
chosen_model = dict_fitted_models[chosen_id]
X_test_clean_chosen = dict_detailed_predictions[chosen_id]['X_test_features'].copy()
X_test_clean_chosen = X_test_clean_chosen.fillna(np.nan).astype(float)
explainer = shap.TreeExplainer(chosen_model)
shap_values = explainer.shap_values(X_test_clean_chosen)
if isinstance(shap_values, list):
shap_values_plot = shap_values[1]
elif len(shap_values.shape) == 3:
shap_values_plot = shap_values[:, :, 1]
else:
shap_values_plot = shap_values
mean_abs_shap = np.abs(shap_values_plot).mean(axis=0)
df_shap_order = pd.DataFrame({'Feature': X_test_clean_chosen.columns, 'Mean_Abs_SHAP': mean_abs_shap})
top_15_features_shap = df_shap_order.sort_values(by='Mean_Abs_SHAP', ascending=False)['Feature'].head(15).tolist()
# ----------------------------------------------------------
# KOLOM 1: SHAP PLOT
# ----------------------------------------------------------
plt.sca(ax_shap)
shap.summary_plot(shap_values_plot, X_test_clean_chosen, max_display=15,
show=False, plot_size=None)
ax_shap.set_title("SHAP Values Summary", fontsize=20, fontweight='bold', pad=15)
if "RandomForest" in chosen_model.__class__.__name__:
ax_shap.set_xlabel("SHAP value (Impact on Probability)", fontsize=18)
else:
ax_shap.set_xlabel("SHAP value (Impact on Log-Odds)", fontsize=18)
ax_shap.tick_params(axis='y', labelsize=18)
ax_shap.tick_params(axis='x', labelsize=16)
for fig_ax in fig.axes:
if fig_ax.get_ylabel() == "Feature value":
fig_ax.set_ylabel("Feature value", fontsize=17)
fig_ax.tick_params(labelsize=16)
ax_shap.annotate(chosen_id,
xy=(-0.85, 0.5), xycoords='axes fraction',
rotation=90, va='center', ha='center',
fontsize=20, fontweight='bold', color='black',
clip_on=False)
# ----------------------------------------------------------
# KOLOM 2: ARAH KORELASI SPEARMAN
# ----------------------------------------------------------
spearman_rows = []
for j, col in enumerate(X_test_clean_chosen.columns):
feature_vals = X_test_clean_chosen.iloc[:, j]
shap_vals = pd.Series(shap_values_plot[:, j], index=feature_vals.index)
if feature_vals.nunique() > 1 and shap_vals.nunique() > 1:
corr = feature_vals.corr(shap_vals, method='spearman')
corr = 0.0 if pd.isna(corr) else corr
else:
corr = 0.0
spearman_rows.append({'Feature': col, 'Spearman_Corr': corr})
df_spearman = pd.DataFrame(spearman_rows)
df_spearman = df_spearman.set_index('Feature').loc[top_15_features_shap].reset_index()
colors = ['#c62828' if val > 0 else '#2e7d32' for val in df_spearman['Spearman_Corr']]
sns.barplot(x='Spearman_Corr', y='Feature', data=df_spearman,
hue='Feature', palette=colors, legend=False, ax=ax_spearman)
ax_spearman.axvline(0, color='black', linewidth=1.2)
ax_spearman.set_title("Arah Pengaruh (Spearman Correlation)", fontsize=20, fontweight='bold', pad=15)
ax_spearman.set_ylabel('')
ax_spearman.set_yticks([])
ax_spearman.set_xlabel("Spearman r (Positif = Memperparah Distress)", fontsize=18)
ax_spearman.tick_params(axis='x', labelsize=16)
ax_spearman.locator_params(axis='x', nbins=8)
max_abs_corr = df_spearman['Spearman_Corr'].abs().max()
xlim_val = max(max_abs_corr * 1.15, 0.1)
ax_spearman.set_xlim(-xlim_val, xlim_val)
ax_spearman.margins(x=0)
# ----------------------------------------------------------
# KOLOM 3: FEATURE IMPORTANCE
# ----------------------------------------------------------
importance = chosen_model.feature_importances_
df_imp = pd.DataFrame({'Feature': X_test_clean_chosen.columns, 'Importance': importance})
df_imp = df_imp.sort_values(by='Importance', ascending=False).head(15)
sns.barplot(x='Importance', y='Feature', data=df_imp, hue='Feature',
palette='viridis', legend=False, ax=ax_imp)
ax_imp.set_title("Top 15 Feature Importance (Model)", fontsize=20, fontweight='bold', pad=15)
ax_imp.set_ylabel('')
ax_imp.set_xlabel("Importance", fontsize=18)
ax_imp.tick_params(axis='y', labelsize=18)
ax_imp.tick_params(axis='x', labelsize=16)
# ==========================================================
# PENYESUAIAN MARGIN (kembali ke tight_layout seperti sebelumnya)
# ==========================================================
plt.tight_layout(rect=[0.08, 0, 1, 0.93])
# [FIX] Geser hanya plot kanan (Feature Importance) lebih ke kanan,
# tanpa mengubah posisi plot SHAP & Spearman
fig.canvas.draw()
SHIFT = 0.035 # seberapa jauh plot kanan digeser ke kanan (dalam koordinat figure 0-1)
for i in range(n_rows):
pos_imp = axes[i, 2].get_position()
new_pos_imp = [pos_imp.x0 + SHIFT, pos_imp.y0, pos_imp.width, pos_imp.height]
axes[i, 2].set_position(new_pos_imp)
# [FIX] Gambar garis pembatas vertikal di celah antara Spearman & Feature Importance
# (posisi dihitung ulang setelah pergeseran di atas)
for i in range(n_rows):
bbox_spearman = axes[i, 1].get_position()
bbox_imp = axes[i, 2].get_position()
x_sep = (bbox_spearman.x1 + bbox_imp.x0) / 2
y0 = bbox_imp.y0 - 0.01
y1 = bbox_imp.y1 + 0.01
line = Line2D([x_sep, x_sep], [y0, y1], transform=fig.transFigure,
color='gray', linestyle='--', linewidth=1.5, alpha=0.7)
line.set_clip_on(False)
fig.add_artist(line)
plt.savefig("negeq_shap_summary.png", dpi=300, bbox_inches='tight')
plt.show()
for chosen_id in negeq_best_configs:
chosen_model = dict_fitted_models[chosen_id]
X_test_clean_chosen = dict_detailed_predictions[chosen_id]['X_test_features'].copy()
X_test_clean_chosen = X_test_clean_chosen.fillna(np.nan).astype(float)
explainer = shap.TreeExplainer(chosen_model)
shap_values = explainer.shap_values(X_test_clean_chosen)
if isinstance(shap_values, list):
shap_values_plot = shap_values[1]
elif len(shap_values.shape) == 3:
shap_values_plot = shap_values[:, :, 1]
else:
shap_values_plot = shap_values
# ----------------------------------------------------------
# DATAFRAME 1: Mean |SHAP| (Top 15) + Arah Pengaruh (Spearman)
# ----------------------------------------------------------
mean_abs_shap = np.abs(shap_values_plot).mean(axis=0)
df_shap_order = pd.DataFrame({
'Feature': X_test_clean_chosen.columns,
'Mean_Abs_SHAP': mean_abs_shap
})
top_15_shap = df_shap_order.sort_values(by='Mean_Abs_SHAP', ascending=False).head(15).reset_index(drop=True)
spearman_rows = []
for feat in top_15_shap['Feature']:
j = list(X_test_clean_chosen.columns).index(feat)
feature_vals = X_test_clean_chosen.iloc[:, j]
shap_vals = pd.Series(shap_values_plot[:, j], index=feature_vals.index)
if feature_vals.nunique() > 1 and shap_vals.nunique() > 1:
corr = feature_vals.corr(shap_vals, method='spearman')
corr = 0.0 if pd.isna(corr) else corr
else:
corr = 0.0
spearman_rows.append(corr)
top_15_shap['Spearman_Corr'] = spearman_rows
top_15_shap['Arah_Pengaruh'] = top_15_shap['Spearman_Corr'].apply(
lambda x: 'Positif (Memperparah Distress)' if x > 0
else ('Negatif (Mengurangi Distress)' if x < 0 else 'Netral')
)
df1 = top_15_shap[['Feature', 'Mean_Abs_SHAP', 'Spearman_Corr', 'Arah_Pengaruh']]
# ----------------------------------------------------------
# DATAFRAME 2: Feature Importance Model (Top 15)
# ----------------------------------------------------------
importance = chosen_model.feature_importances_
df_imp = pd.DataFrame({
'Feature': X_test_clean_chosen.columns,
'Importance': importance
})
df2 = df_imp.sort_values(by='Importance', ascending=False).head(15).reset_index(drop=True)
# ----------------------------------------------------------
# CETAK DALAM FORMAT CSV
# ----------------------------------------------------------
print(f"=== {chosen_id} - SHAP & Arah Pengaruh (Top 15) ===")
print(df1.to_csv(index=False))
print(f"=== {chosen_id} - Feature Importance (Top 15) ===")
print(df2.to_csv(index=False))=== negeq_keep_all_RandomForest - SHAP & Arah Pengaruh (Top 15) ===
Feature,Mean_Abs_SHAP,Spearman_Corr,Arah_Pengaruh
ROE,0.004625663056664717,-0.09028384156427355,Negatif (Mengurangi Distress)
Debt_Equity,0.003457320624640768,0.6055624353738713,Positif (Memperparah Distress)
EPS_proxy,0.0032708919749361246,-0.3306482822525756,Negatif (Mengurangi Distress)
NetMargin,0.003133286125230754,-0.6847803201121454,Negatif (Mengurangi Distress)
Equity_growth,0.0030112070131665183,-0.2819757070574467,Negatif (Mengurangi Distress)
ROA,0.002487984435032106,-0.5983583138075316,Negatif (Mengurangi Distress)
Equity_TA,0.0024697348061009285,-0.5822281351509317,Negatif (Mengurangi Distress)
Debt_TA,0.002347278445749217,0.740499807604793,Positif (Memperparah Distress)
CR,0.0020296119037564245,-0.48617636181032436,Negatif (Mengurangi Distress)
Altman_X3_EBIT_TA,0.0017821558608417658,0.18625940544908162,Positif (Memperparah Distress)
TL_TA,0.001777735423909399,0.6285698598388582,Positif (Memperparah Distress)
NetDebt_EBITDA,0.0016997844944600766,0.6033004773841287,Positif (Memperparah Distress)
OpMargin,0.0013898848494798453,-0.43185576323494523,Negatif (Mengurangi Distress)
Altman_X4_MVE_TL,0.0013209701057985072,-0.5787067949370781,Negatif (Mengurangi Distress)
GrossMargin,0.0010911677334193087,-0.47606274023409656,Negatif (Mengurangi Distress)
=== negeq_keep_all_RandomForest - Feature Importance (Top 15) ===
Feature,Importance
ROE,0.07100069611755966
Equity_growth,0.06043605623912042
Debt_Equity,0.05122967599484337
Equity_TA,0.050442958734987414
EPS_proxy,0.04009828086623649
TL_TA,0.033178643467025405
ROA,0.031915538384382
Debt_TA,0.03044449608401384
Altman_X3_EBIT_TA,0.02948647844209178
NetMargin,0.028301225673961216
Altman_X4_MVE_TL,0.02809476182860804
CR,0.02413421155883497
TA_growth,0.02388509213588187
Prepaid_CA,0.020550104908736416
NetDebt_EBITDA,0.0203525744947967
=== negeq_keep_corr_RandomForest - SHAP & Arah Pengaruh (Top 15) ===
Feature,Mean_Abs_SHAP,Spearman_Corr,Arah_Pengaruh
Equity_growth,0.0042040743561464625,-0.22707310613110623,Negatif (Mengurangi Distress)
NetMargin,0.00405017940099328,-0.7033373939453705,Negatif (Mengurangi Distress)
Debt_Equity,0.003993514402583375,0.5704869177255164,Positif (Memperparah Distress)
EPS_proxy,0.0034986253571296926,-0.28961897528504504,Negatif (Mengurangi Distress)
ROA,0.0034105522555389683,-0.545023371078781,Negatif (Mengurangi Distress)
Equity_TA,0.003094239129336745,-0.6155995622722693,Negatif (Mengurangi Distress)
Altman_X3_EBIT_TA,0.0025747218178525377,0.06652745322802145,Positif (Memperparah Distress)
NetDebt_EBITDA,0.002534515635427185,0.6810238979072052,Positif (Memperparah Distress)
CR,0.0022594287380385755,-0.43786851642349445,Negatif (Mengurangi Distress)
Altman_X4_MVE_TL,0.0016697428793688374,-0.5017384335362053,Negatif (Mengurangi Distress)
TA_growth,0.0016380741644945907,-0.3833115067297223,Negatif (Mengurangi Distress)
OpMargin,0.0014961122887564317,-0.28073922221711606,Negatif (Mengurangi Distress)
Age_When_IPO,0.0013613086317569828,-0.14030367907810512,Negatif (Mengurangi Distress)
CashST_CL,0.0013459002523823309,-0.7532677982576168,Negatif (Mengurangi Distress)
Parent Percent Owned (%),0.0013092594160215058,-0.13934981499767857,Negatif (Mengurangi Distress)
=== negeq_keep_corr_RandomForest - Feature Importance (Top 15) ===
Feature,Importance
Equity_growth,0.07422492609876975
Debt_Equity,0.07177956532372345
Equity_TA,0.06640534694356909
EPS_proxy,0.04123080744789114
ROA,0.037993153554151296
Altman_X3_EBIT_TA,0.0343105863536803
TA_growth,0.03296366159712036
Altman_X4_MVE_TL,0.0328021605942236
NetMargin,0.032223565549263866
CR,0.02793690781785807
NetDebt_EBITDA,0.02623796488689963
CashST_CL,0.024938653402126292
CA_TA,0.023471534710224003
PB,0.023466937967508213
Percent Owned - Insiders (%),0.02314906508943251
for chosen_id in negeq_best_configs:
print(f"\n========== MENGANALISIS: {chosen_id} ==========")
chosen_model = dict_fitted_models[chosen_id]
X_test_clean_chosen = dict_detailed_predictions[chosen_id]['X_test_features'].copy()
X_test_clean_chosen = X_test_clean_chosen.fillna(np.nan).astype(float)
# ---------- SHAP ----------
explainer = shap.TreeExplainer(chosen_model)
shap_values = explainer.shap_values(X_test_clean_chosen)
if isinstance(shap_values, list):
shap_values_plot = shap_values[1]
elif len(shap_values.shape) == 3:
shap_values_plot = shap_values[:, :, 1]
else:
shap_values_plot = shap_values
feature_names = list(X_test_clean_chosen.columns)
mean_abs_shap = np.abs(shap_values_plot).mean(axis=0)
tree_importance = chosen_model.feature_importances_
industry_idx = [i for i, c in enumerate(feature_names) if c.startswith('industry_')]
other_idx = [i for i, c in enumerate(feature_names) if not c.startswith('industry_')]
rows = []
for i in other_idx:
feature_vals = X_test_clean_chosen.iloc[:, i]
shap_vals = pd.Series(shap_values_plot[:, i], index=feature_vals.index)
rows.append({
'Feature': feature_names[i],
'Mean_Abs_SHAP': mean_abs_shap[i],
'Tree_Importance': tree_importance[i],
'SHAP_Dir_Spearman': feature_vals.corr(shap_vals, method='spearman')
})
# ---------- Agregasi Industry Group ----------
if industry_idx:
industry_shap_rowsum = shap_values_plot[:, industry_idx].sum(axis=1)
rows.append({
'Feature': 'Industry Group (agregat)',
'Mean_Abs_SHAP': np.abs(industry_shap_rowsum).mean(),
'Tree_Importance': tree_importance[industry_idx].sum(),
'SHAP_Dir_Spearman': np.nan
})
df_importance = pd.DataFrame(rows).sort_values(
by='Mean_Abs_SHAP', ascending=False).reset_index(drop=True)
print(df_importance.to_csv(index=False))
========== MENGANALISIS: negeq_keep_all_RandomForest ==========
Feature,Mean_Abs_SHAP,Tree_Importance,SHAP_Dir_Spearman
ROE,0.004625663056664717,0.07100069611755966,-0.09028384156427355
Debt_Equity,0.003457320624640768,0.05122967599484337,0.6055624353738713
EPS_proxy,0.0032708919749361246,0.04009828086623649,-0.3306482822525756
NetMargin,0.003133286125230754,0.028301225673961216,-0.6847803201121454
Equity_growth,0.0030112070131665183,0.06043605623912042,-0.2819757070574467
ROA,0.002487984435032106,0.031915538384382,-0.5983583138075316
Equity_TA,0.0024697348061009285,0.050442958734987414,-0.5822281351509317
Debt_TA,0.002347278445749217,0.03044449608401384,0.740499807604793
CR,0.0020296119037564245,0.02413421155883497,-0.48617636181032436
Altman_X3_EBIT_TA,0.0017821558608417658,0.02948647844209178,0.18625940544908162
TL_TA,0.001777735423909399,0.033178643467025405,0.6285698598388582
NetDebt_EBITDA,0.0016997844944600766,0.0203525744947967,0.6033004773841287
OpMargin,0.0013898848494798453,0.018022980143280003,-0.43185576323494523
Altman_X4_MVE_TL,0.0013209701057985072,0.02809476182860804,-0.5787067949370781
GrossMargin,0.0010911677334193087,0.016999030495405947,-0.47606274023409656
TA_growth,0.001072358251766405,0.02388509213588187,-0.3663479238497489
Age_When_IPO,0.00096299780583648,0.013741149767947007,-0.15780429416891392
EBITDA_TA,0.0009587759534408155,0.020008440365970916,-0.13557727916342216
Prepaid_CA,0.0009063775314650194,0.020550104908736416,-0.3909773867663814
QR,0.0008828069193341019,0.015716850249881367,-0.4390809198438103
Altman_X1_WC_TA,0.0008656981967813959,0.016574445059414182,-0.2530568553402044
log_Sales,0.0008512441906341653,0.01515378146191709,0.5191429964679022
Parent Percent Owned (%),0.0008387158308222245,0.011353885864986668,-0.19198877225219743
Industry Group (agregat),0.0007922169666708516,0.010635665409170075,
Percent Owned - Insiders (%),0.0007651616476443461,0.01338875579548235,-0.7864543097926102
CashST_CL,0.0007446557362443204,0.018438563801091816,-0.8052323903464262
Years_Since_IPO,0.0007338809206590943,0.014286760550338862,0.13138470720073542
WC_Sales,0.0006595009522298618,0.014620876129339022,-0.35980634358272157
Sales_per_share,0.0006517935302728322,0.014681211782639274,0.5572900332581694
Intang_TA,0.0006515533942359876,0.002613545749472631,0.16388968132915327
CL_TA,0.0006475386202304971,0.01426576083611413,0.7397415837105475
Sales_growth,0.0005532338450319876,0.015980749979910013,0.26655219065620356
NetCash_TA,0.0005526101452252547,0.010689431481777638,-0.6892431111838138
CashST_TA,0.0005467564551435648,0.01611305992924166,-0.3481127200234514
log_MktCap,0.00048370966896798997,0.017150364922742665,0.2963524478500768
log_TA,0.0004699602301179252,0.013196268832719316,0.5576627064999407
CA_TA,0.00044942084707276416,0.014318112443437609,0.20738234523191423
PPE_TA,0.0004372879093330641,0.015234898980818413,0.0418459443173421
Percent Owned - All Institutions (%),0.0004318164571666195,0.005919099931534951,-0.09577271936244595
Altman_X5_SalesTA_AssetTurnover,0.00042331276620900316,0.012643929163821274,0.6247031899465028
CFO_per_share,0.0003996605682910822,0.012927448408991299,0.5586631127038482
Inventory_CA,0.000378170053803852,0.015093618708843048,0.13746925532967466
NI_growth,0.0003549713739680101,0.013135977335408791,0.0598280159363355
CFO_growth,0.00034223895622820466,0.013583897392081181,-0.3341164846285187
CFO_Sales,0.00033636689201467485,0.01148769104846697,0.20610677646770445
CFO_TA,0.0003193117622166426,0.012801972407512985,0.27791702235373944
PB,0.0003134618951120944,0.014520599050253536,0.48899041676940236
CFO_TL,0.00031295324911258325,0.01115038158890777,0.05355277036919835
========== MENGANALISIS: negeq_keep_corr_RandomForest ==========
Feature,Mean_Abs_SHAP,Tree_Importance,SHAP_Dir_Spearman
Equity_growth,0.0042040743561464625,0.07422492609876975,-0.22707310613110623
NetMargin,0.00405017940099328,0.032223565549263866,-0.7033373939453705
Debt_Equity,0.003993514402583375,0.07177956532372345,0.5704869177255164
EPS_proxy,0.0034986253571296926,0.04123080744789114,-0.28961897528504504
ROA,0.0034105522555389683,0.037993153554151296,-0.545023371078781
Equity_TA,0.003094239129336745,0.06640534694356909,-0.6155995622722693
Altman_X3_EBIT_TA,0.0025747218178525377,0.0343105863536803,0.06652745322802145
NetDebt_EBITDA,0.002534515635427185,0.02623796488689963,0.6810238979072052
CR,0.0022594287380385755,0.02793690781785807,-0.43786851642349445
Altman_X4_MVE_TL,0.0016697428793688374,0.0328021605942236,-0.5017384335362053
TA_growth,0.0016380741644945907,0.03296366159712036,-0.3833115067297223
Industry Group (agregat),0.0015353125440069704,0.017312081068830948,
OpMargin,0.0014961122887564317,0.019172155916588784,-0.28073922221711606
Age_When_IPO,0.0013613086317569828,0.020880065720676706,-0.14030367907810512
CashST_CL,0.0013459002523823309,0.024938653402126292,-0.7532677982576168
Parent Percent Owned (%),0.0013092594160215058,0.012194492078831387,-0.13934981499767857
Percent Owned - Insiders (%),0.0012137615429222742,0.02314906508943251,-0.6252127281579721
GrossMargin,0.0011129381201764078,0.02079232612012233,-0.49690315712418703
QR,0.0011065717203374665,0.01915557528541538,-0.4232540544700148
log_Sales,0.001099384642699087,0.018949190660420938,0.6457135967734653
Prepaid_CA,0.0010069424205873655,0.021328792906561728,-0.3396681333204922
CL_TA,0.0009840885698557723,0.015120715217818265,0.7775134436472045
log_TA,0.0008925339422002186,0.01612310379661396,0.7329312291198521
Intang_TA,0.0008890484746971554,0.004986456980321322,-0.20781374781222237
Years_Since_IPO,0.0008544236436557729,0.015958806752374773,0.18986098868003295
Sales_growth,0.0007440883585414027,0.016637224589518947,0.30081991902017463
Altman_X5_SalesTA_AssetTurnover,0.0007230630170794479,0.021231124061383975,0.36377814677326153
CFO_growth,0.0007090481339836096,0.01628767835390425,-0.5360196478693756
Percent Owned - All Institutions (%),0.0007072504729634266,0.009159910768907218,0.00557173024390607
NetCash_TA,0.0006917277965492807,0.013915276682557699,-0.6136503133854758
CFO_per_share,0.0006868883456319708,0.017609153213909916,0.6733420125279201
CFO_TL,0.0006860698973694815,0.015992060397351546,-0.5424094855784702
Sales_per_share,0.0006805641658287826,0.01469472900682485,0.6288881377331633
Inventory_CA,0.0006777304286360188,0.01431133154054633,0.1923265823819316
CFO_Sales,0.0006542972042818198,0.01657146597793686,0.49564651741854193
CA_TA,0.0006508596287036028,0.023471534710224003,0.16076441498673563
CashST_TA,0.0006483634300863354,0.018717242873413857,-0.29652366496167026
log_MktCap,0.0006368742568308768,0.016435305385496545,0.07118306941874974
PB,0.0006194434309161633,0.023466937967508213,0.3605438703534683
PPE_TA,0.0005949782009590698,0.020042985801808064,0.07306067007117537
NI_growth,0.0005667111544413911,0.013285911505421993,0.08512811125881825
FI Target Distress Rugi Berturut-turut
conloss_best_configs = ['conloss_keep_corr_CatBoost','conloss_keep_all_XGBoost']# [FIX] Sembunyikan warning tight_layout (tidak berbahaya, hanya informasi)
warnings.filterwarnings("ignore", message="This figure includes Axes that are not compatible with tight_layout")
# [FIX] Set DPI tinggi & font dasar lebih besar
plt.rcParams['figure.dpi'] = 150
plt.rcParams['savefig.dpi'] = 300
plt.rcParams['font.size'] = 16
n_rows = len(conloss_best_configs)
n_cols = 3
# [FIX] Kanvas diperlebar agar plot kanan punya ruang untuk digeser ke kanan
fig, axes = plt.subplots(nrows=n_rows, ncols=n_cols, figsize=(33, 8 * n_rows),
gridspec_kw={'width_ratios': [1.3, 1.55, 1.05], 'wspace': 0.18})
fig.suptitle("Pengaruh dan Kontribusi Fitur\nIndikator Rugi Berturut-turut",
fontsize=26, fontweight='bold', y=0.97)
axes = np.atleast_2d(axes)
for i, chosen_id in enumerate(conloss_best_configs):
ax_shap = axes[i, 0]
ax_spearman = axes[i, 1]
ax_imp = axes[i, 2]
chosen_model = dict_fitted_models[chosen_id]
X_test_clean_chosen = dict_detailed_predictions[chosen_id]['X_test_features'].copy()
X_test_clean_chosen = X_test_clean_chosen.fillna(np.nan).astype(float)
explainer = shap.TreeExplainer(chosen_model)
shap_values = explainer.shap_values(X_test_clean_chosen)
if isinstance(shap_values, list):
shap_values_plot = shap_values[1]
elif len(shap_values.shape) == 3:
shap_values_plot = shap_values[:, :, 1]
else:
shap_values_plot = shap_values
mean_abs_shap = np.abs(shap_values_plot).mean(axis=0)
df_shap_order = pd.DataFrame({'Feature': X_test_clean_chosen.columns, 'Mean_Abs_SHAP': mean_abs_shap})
top_15_features_shap = df_shap_order.sort_values(by='Mean_Abs_SHAP', ascending=False)['Feature'].head(15).tolist()
# ----------------------------------------------------------
# KOLOM 1: SHAP PLOT
# ----------------------------------------------------------
plt.sca(ax_shap)
shap.summary_plot(shap_values_plot, X_test_clean_chosen, max_display=15,
show=False, plot_size=None)
ax_shap.set_title("SHAP Values Summary", fontsize=20, fontweight='bold', pad=15)
if "RandomForest" in chosen_model.__class__.__name__:
ax_shap.set_xlabel("SHAP value (Impact on Probability)", fontsize=18)
else:
ax_shap.set_xlabel("SHAP value (Impact on Log-Odds)", fontsize=18)
ax_shap.tick_params(axis='y', labelsize=18)
ax_shap.tick_params(axis='x', labelsize=16)
for fig_ax in fig.axes:
if fig_ax.get_ylabel() == "Feature value":
fig_ax.set_ylabel("Feature value", fontsize=17)
fig_ax.tick_params(labelsize=16)
ax_shap.annotate(chosen_id,
xy=(-0.85, 0.5), xycoords='axes fraction',
rotation=90, va='center', ha='center',
fontsize=20, fontweight='bold', color='black',
clip_on=False)
# ----------------------------------------------------------
# KOLOM 2: ARAH KORELASI SPEARMAN
# ----------------------------------------------------------
spearman_rows = []
for j, col in enumerate(X_test_clean_chosen.columns):
feature_vals = X_test_clean_chosen.iloc[:, j]
shap_vals = pd.Series(shap_values_plot[:, j], index=feature_vals.index)
if feature_vals.nunique() > 1 and shap_vals.nunique() > 1:
corr = feature_vals.corr(shap_vals, method='spearman')
corr = 0.0 if pd.isna(corr) else corr
else:
corr = 0.0
spearman_rows.append({'Feature': col, 'Spearman_Corr': corr})
df_spearman = pd.DataFrame(spearman_rows)
df_spearman = df_spearman.set_index('Feature').loc[top_15_features_shap].reset_index()
colors = ['#c62828' if val > 0 else '#2e7d32' for val in df_spearman['Spearman_Corr']]
sns.barplot(x='Spearman_Corr', y='Feature', data=df_spearman,
hue='Feature', palette=colors, legend=False, ax=ax_spearman)
ax_spearman.axvline(0, color='black', linewidth=1.2)
ax_spearman.set_title("Arah Pengaruh (Spearman Correlation)", fontsize=20, fontweight='bold', pad=15)
ax_spearman.set_ylabel('')
ax_spearman.set_yticks([])
ax_spearman.set_xlabel("Spearman r (Positif = Memperparah Distress)", fontsize=18)
ax_spearman.tick_params(axis='x', labelsize=16)
ax_spearman.locator_params(axis='x', nbins=8)
max_abs_corr = df_spearman['Spearman_Corr'].abs().max()
xlim_val = max(max_abs_corr * 1.15, 0.1)
ax_spearman.set_xlim(-xlim_val, xlim_val)
ax_spearman.margins(x=0)
# ----------------------------------------------------------
# KOLOM 3: FEATURE IMPORTANCE
# ----------------------------------------------------------
importance = chosen_model.feature_importances_
df_imp = pd.DataFrame({'Feature': X_test_clean_chosen.columns, 'Importance': importance})
df_imp = df_imp.sort_values(by='Importance', ascending=False).head(15)
sns.barplot(x='Importance', y='Feature', data=df_imp, hue='Feature',
palette='viridis', legend=False, ax=ax_imp)
ax_imp.set_title("Top 15 Feature Importance (Model)", fontsize=20, fontweight='bold', pad=15)
ax_imp.set_ylabel('')
ax_imp.set_xlabel("Importance", fontsize=18)
ax_imp.tick_params(axis='y', labelsize=18)
ax_imp.tick_params(axis='x', labelsize=16)
# ==========================================================
# PENYESUAIAN MARGIN (kembali ke tight_layout seperti sebelumnya)
# ==========================================================
plt.tight_layout(rect=[0.08, 0, 1, 0.93])
# [FIX] Geser hanya plot kanan (Feature Importance) lebih ke kanan,
# tanpa mengubah posisi plot SHAP & Spearman
fig.canvas.draw()
SHIFT = 0.035 # seberapa jauh plot kanan digeser ke kanan (dalam koordinat figure 0-1)
for i in range(n_rows):
pos_imp = axes[i, 2].get_position()
new_pos_imp = [pos_imp.x0 + SHIFT, pos_imp.y0, pos_imp.width, pos_imp.height]
axes[i, 2].set_position(new_pos_imp)
# [FIX] Gambar garis pembatas vertikal di celah antara Spearman & Feature Importance
# (posisi dihitung ulang setelah pergeseran di atas)
for i in range(n_rows):
bbox_spearman = axes[i, 1].get_position()
bbox_imp = axes[i, 2].get_position()
x_sep = (bbox_spearman.x1 + bbox_imp.x0) / 2
y0 = bbox_imp.y0 - 0.01
y1 = bbox_imp.y1 + 0.01
line = Line2D([x_sep, x_sep], [y0, y1], transform=fig.transFigure,
color='gray', linestyle='--', linewidth=1.5, alpha=0.7)
line.set_clip_on(False)
fig.add_artist(line)
plt.savefig("conloss_shap_summary.png", dpi=300, bbox_inches='tight')
plt.show()
for chosen_id in conloss_best_configs:
chosen_model = dict_fitted_models[chosen_id]
X_test_clean_chosen = dict_detailed_predictions[chosen_id]['X_test_features'].copy()
X_test_clean_chosen = X_test_clean_chosen.fillna(np.nan).astype(float)
explainer = shap.TreeExplainer(chosen_model)
shap_values = explainer.shap_values(X_test_clean_chosen)
if isinstance(shap_values, list):
shap_values_plot = shap_values[1]
elif len(shap_values.shape) == 3:
shap_values_plot = shap_values[:, :, 1]
else:
shap_values_plot = shap_values
# ----------------------------------------------------------
# DATAFRAME 1: Mean |SHAP| (Top 15) + Arah Pengaruh (Spearman)
# ----------------------------------------------------------
mean_abs_shap = np.abs(shap_values_plot).mean(axis=0)
df_shap_order = pd.DataFrame({
'Feature': X_test_clean_chosen.columns,
'Mean_Abs_SHAP': mean_abs_shap
})
top_15_shap = df_shap_order.sort_values(by='Mean_Abs_SHAP', ascending=False).head(15).reset_index(drop=True)
spearman_rows = []
for feat in top_15_shap['Feature']:
j = list(X_test_clean_chosen.columns).index(feat)
feature_vals = X_test_clean_chosen.iloc[:, j]
shap_vals = pd.Series(shap_values_plot[:, j], index=feature_vals.index)
if feature_vals.nunique() > 1 and shap_vals.nunique() > 1:
corr = feature_vals.corr(shap_vals, method='spearman')
corr = 0.0 if pd.isna(corr) else corr
else:
corr = 0.0
spearman_rows.append(corr)
top_15_shap['Spearman_Corr'] = spearman_rows
top_15_shap['Arah_Pengaruh'] = top_15_shap['Spearman_Corr'].apply(
lambda x: 'Positif (Memperparah Distress)' if x > 0
else ('Negatif (Mengurangi Distress)' if x < 0 else 'Netral')
)
df1 = top_15_shap[['Feature', 'Mean_Abs_SHAP', 'Spearman_Corr', 'Arah_Pengaruh']]
# ----------------------------------------------------------
# DATAFRAME 2: Feature Importance Model (Top 15)
# ----------------------------------------------------------
importance = chosen_model.feature_importances_
df_imp = pd.DataFrame({
'Feature': X_test_clean_chosen.columns,
'Importance': importance
})
df2 = df_imp.sort_values(by='Importance', ascending=False).head(15).reset_index(drop=True)
# ----------------------------------------------------------
# CETAK DALAM FORMAT CSV
# ----------------------------------------------------------
print(f"=== {chosen_id} - SHAP & Arah Pengaruh (Top 15) ===")
print(df1.to_csv(index=False))
print(f"=== {chosen_id} - Feature Importance (Top 15) ===")
print(df2.to_csv(index=False))=== conloss_keep_corr_CatBoost - SHAP & Arah Pengaruh (Top 15) ===
Feature,Mean_Abs_SHAP,Spearman_Corr,Arah_Pengaruh
ROE,0.3192689554432316,-0.8952229046183492,Negatif (Mengurangi Distress)
NetMargin,0.2373379568824317,-0.9435595324985789,Negatif (Mengurangi Distress)
Altman_X3_EBIT_TA,0.22213192867238066,-0.9627948631943598,Negatif (Mengurangi Distress)
Percent Owned - All Institutions (%),0.16776538964882504,-0.5198438911560657,Negatif (Mengurangi Distress)
NetDebt_EBITDA,0.15207264527761338,0.8415742661145861,Positif (Memperparah Distress)
EPS_proxy,0.1479449232133265,-0.8174231548219381,Negatif (Mengurangi Distress)
ROA,0.14079531854817653,-0.9512575181143631,Negatif (Mengurangi Distress)
NI_growth,0.1290547993123681,-0.802806912946197,Negatif (Mengurangi Distress)
Years_Since_IPO,0.1213380976819955,-0.816997763187094,Negatif (Mengurangi Distress)
Altman_X5_SalesTA_AssetTurnover,0.10294381176679801,-0.7030643800875506,Negatif (Mengurangi Distress)
log_MktCap,0.09891725291081024,0.1908580690421838,Positif (Memperparah Distress)
Debt_TA,0.09099207539146018,0.8958160029127759,Positif (Memperparah Distress)
CFO_growth,0.08901102062332121,-0.23898014755267694,Negatif (Mengurangi Distress)
PPE_TA,0.08485142283635062,0.9009417732751318,Positif (Memperparah Distress)
CFO_TL,0.08244002475332127,-0.9224849321072246,Negatif (Mengurangi Distress)
=== conloss_keep_corr_CatBoost - Feature Importance (Top 15) ===
Feature,Importance
NetMargin,6.037625911004771
ROE,5.763679919701531
EPS_proxy,3.8768890456041323
Years_Since_IPO,3.827685140479139
Altman_X3_EBIT_TA,3.731992848718549
ROA,3.7155374071722242
Age_When_IPO,3.5891166830543133
NetDebt_EBITDA,3.403282666506999
Debt_TA,3.3585462442856215
log_MktCap,3.2865612655214806
Percent Owned - All Institutions (%),3.259183293903597
Altman_X5_SalesTA_AssetTurnover,3.0476374269120967
NI_growth,2.885804311365791
OpMargin,2.5292780190769526
Inventory_CA,2.3960990758577188
=== conloss_keep_all_XGBoost - SHAP & Arah Pengaruh (Top 15) ===
Feature,Mean_Abs_SHAP,Spearman_Corr,Arah_Pengaruh
ROE,0.4316161,-0.8800784886405073,Negatif (Mengurangi Distress)
Altman_X3_EBIT_TA,0.27861458,-0.9386388248497058,Negatif (Mengurangi Distress)
ROA,0.26279235,-0.8486209109277847,Negatif (Mengurangi Distress)
NetMargin,0.24395414,-0.89332692210077,Negatif (Mengurangi Distress)
EPS_proxy,0.21610238,-0.8602264211128476,Negatif (Mengurangi Distress)
NI_growth,0.20682013,-0.7581126741019758,Negatif (Mengurangi Distress)
NetDebt_EBITDA,0.14913501,0.8130785804095254,Positif (Memperparah Distress)
Percent Owned - All Institutions (%),0.13340995,0.20476863161164088,Positif (Memperparah Distress)
Debt_TA,0.12957084,0.8029082235944924,Positif (Memperparah Distress)
Parent Percent Owned (%),0.1223252,0.5858002055997794,Positif (Memperparah Distress)
Years_Since_IPO,0.12201359,-0.6081864318512272,Negatif (Mengurangi Distress)
Inventory_CA,0.10511425,-0.8890527287382054,Negatif (Mengurangi Distress)
TA_growth,0.09546359,-0.6552825594653804,Negatif (Mengurangi Distress)
log_MktCap,0.0877707,-0.11755728448771045,Negatif (Mengurangi Distress)
CFO_TA,0.07471729,-0.3362505130075218,Negatif (Mengurangi Distress)
=== conloss_keep_all_XGBoost - Feature Importance (Top 15) ===
Feature,Importance
ROA,0.10206569
ROE,0.050763775
NetMargin,0.04683309
EPS_proxy,0.046610106
industry_Capital Goods,0.042293403
Altman_X3_EBIT_TA,0.022063222
EBITDA_TA,0.017023806
Debt_TA,0.016601881
Debt_Equity,0.016467342
Equity_growth,0.016464878
CashST_CL,0.016215578
NI_growth,0.016118083
GrossMargin,0.0154362405
NetDebt_EBITDA,0.015100167
CL_TA,0.015058582
for chosen_id in conloss_best_configs:
print(f"\n========== MENGANALISIS: {chosen_id} ==========")
chosen_model = dict_fitted_models[chosen_id]
X_test_clean_chosen = dict_detailed_predictions[chosen_id]['X_test_features'].copy()
X_test_clean_chosen = X_test_clean_chosen.fillna(np.nan).astype(float)
# ---------- SHAP ----------
explainer = shap.TreeExplainer(chosen_model)
shap_values = explainer.shap_values(X_test_clean_chosen)
if isinstance(shap_values, list):
shap_values_plot = shap_values[1]
elif len(shap_values.shape) == 3:
shap_values_plot = shap_values[:, :, 1]
else:
shap_values_plot = shap_values
feature_names = list(X_test_clean_chosen.columns)
mean_abs_shap = np.abs(shap_values_plot).mean(axis=0)
tree_importance = chosen_model.feature_importances_
industry_idx = [i for i, c in enumerate(feature_names) if c.startswith('industry_')]
other_idx = [i for i, c in enumerate(feature_names) if not c.startswith('industry_')]
rows = []
for i in other_idx:
feature_vals = X_test_clean_chosen.iloc[:, i]
shap_vals = pd.Series(shap_values_plot[:, i], index=feature_vals.index)
rows.append({
'Feature': feature_names[i],
'Mean_Abs_SHAP': mean_abs_shap[i],
'Tree_Importance': tree_importance[i],
'SHAP_Dir_Spearman': feature_vals.corr(shap_vals, method='spearman')
})
# ---------- Agregasi Industry Group ----------
if industry_idx:
industry_shap_rowsum = shap_values_plot[:, industry_idx].sum(axis=1)
rows.append({
'Feature': 'Industry Group (agregat)',
'Mean_Abs_SHAP': np.abs(industry_shap_rowsum).mean(),
'Tree_Importance': tree_importance[industry_idx].sum(),
'SHAP_Dir_Spearman': np.nan
})
df_importance = pd.DataFrame(rows).sort_values(
by='Mean_Abs_SHAP', ascending=False).reset_index(drop=True)
print(df_importance.to_csv(index=False))
========== MENGANALISIS: conloss_keep_corr_CatBoost ==========
Feature,Mean_Abs_SHAP,Tree_Importance,SHAP_Dir_Spearman
ROE,0.3192689554432316,5.763679919701531,-0.8952229046183492
NetMargin,0.2373379568824317,6.037625911004771,-0.9435595324985789
Altman_X3_EBIT_TA,0.22213192867238066,3.731992848718549,-0.9627948631943598
Percent Owned - All Institutions (%),0.16776538964882504,3.259183293903597,-0.5198438911560657
NetDebt_EBITDA,0.15207264527761338,3.403282666506999,0.8415742661145861
EPS_proxy,0.1479449232133265,3.8768890456041323,-0.8174231548219381
ROA,0.14079531854817653,3.7155374071722242,-0.9512575181143631
NI_growth,0.1290547993123681,2.885804311365791,-0.802806912946197
Years_Since_IPO,0.1213380976819955,3.827685140479139,-0.816997763187094
Altman_X5_SalesTA_AssetTurnover,0.10294381176679801,3.0476374269120967,-0.7030643800875506
log_MktCap,0.09891725291081024,3.2865612655214806,0.1908580690421838
Debt_TA,0.09099207539146018,3.3585462442856215,0.8958160029127759
CFO_growth,0.08901102062332121,2.3409274711567254,-0.23898014755267694
PPE_TA,0.08485142283635062,2.180472887780791,0.9009417732751318
CFO_TL,0.08244002475332127,2.1790389695776953,-0.9224849321072246
Parent Percent Owned (%),0.08072765208012775,2.1722411277249116,-0.772896316086723
OpMargin,0.0764415239155485,2.5292780190769526,-0.8860624666567034
Equity_growth,0.07390574119168808,1.4694937235361858,-0.908026329474901
Inventory_CA,0.07141082093973754,2.3960990758577188,-0.5336843138463242
Age_When_IPO,0.06222695774740363,3.5891166830543133,-0.39621451882091596
TA_growth,0.06200531341097385,2.3603542566171285,-0.6713826001367498
CashST_CL,0.05967316622299798,1.5657743322192281,-0.6433215400283528
CashST_TA,0.057680932965038526,1.2566718809770105,-0.8774946352882733
CL_TA,0.05733519148380391,1.7617903188767938,-0.8522335869914557
Industry Group (agregat),0.05108101273762465,2.399768603489172,
Sales_per_share,0.04890856935668006,2.106615419133713,0.7575253911196369
CFO_per_share,0.043965296534763464,1.4783948300223175,-0.46090024233949795
log_TA,0.043565420091639315,1.6291003514201758,-0.7575893266943587
CFO_Sales,0.041514208680477366,1.4059646598096232,-0.8134293127440791
log_Sales,0.03722292302281421,1.2159258247822857,-0.8136583834787535
Intang_TA,0.03627352522085023,0.46218669436723164,0.1575413253279351
Percent Owned - Insiders (%),0.035134232141018656,1.6762910694682036,0.1358865277773194
GrossMargin,0.03420233937295071,1.8209861259140958,-0.31349617272009356
NetCash_TA,0.03269045663763407,1.5368259812917853,-0.4447097880359094
Sales_growth,0.03182455507260002,1.1129212648422864,-0.7393389055975017
PB,0.028345282650559325,1.7963560029930044,0.6123520609341918
QR,0.02820503551036768,1.6304430389360658,-0.2163807976769637
Altman_X4_MVE_TL,0.0267483705970156,1.40087678132163,0.3763154788439101
CR,0.0263171112896359,1.4005265172557855,-0.36445856018002987
WC_Sales,0.025180403835318037,1.231547006368211,0.11841034051149121
Prepaid_CA,0.020882543582918658,1.448516451712597,-0.13096032900595733
CA_TA,0.020047968145997516,1.466246217921986,-0.014229593434823712
Equity_TA,0.01390946369029179,0.7848229313184015,-0.2536934321039363
========== MENGANALISIS: conloss_keep_all_XGBoost ==========
Feature,Mean_Abs_SHAP,Tree_Importance,SHAP_Dir_Spearman
ROE,0.4316161,0.050763775,-0.8800784886405073
Altman_X3_EBIT_TA,0.27861458,0.022063222,-0.9386388248497058
ROA,0.26279235,0.10206569,-0.8486209109277847
NetMargin,0.24395414,0.04683309,-0.89332692210077
EPS_proxy,0.21610238,0.046610106,-0.8602264211128476
NI_growth,0.20682013,0.016118083,-0.7581126741019758
NetDebt_EBITDA,0.14913501,0.015100167,0.8130785804095254
Percent Owned - All Institutions (%),0.13340995,0.0123126805,0.20476863161164088
Debt_TA,0.12957084,0.016601881,0.8029082235944924
Parent Percent Owned (%),0.1223252,0.0124470955,0.5858002055997794
Years_Since_IPO,0.12201359,0.014892352,-0.6081864318512272
Inventory_CA,0.10511425,0.014580792,-0.8890527287382054
TA_growth,0.09546359,0.013834398,-0.6552825594653804
log_MktCap,0.0877707,0.012732381,-0.11755728448771045
CFO_TA,0.07471729,0.012783064,-0.3362505130075218
EBITDA_TA,0.07147601,0.017023806,-0.3355893286689183
Equity_growth,0.070282534,0.016464878,-0.7616637508156372
OpMargin,0.06992085,0.01478731,-0.9102186849938481
CFO_growth,0.068744965,0.013122952,-0.3334368566395375
Altman_X5_SalesTA_AssetTurnover,0.06794302,0.014147317,-0.6436615292227884
CashST_CL,0.05771726,0.016215578,-0.853047135083681
Percent Owned - Insiders (%),0.056751896,0.01242601,0.6513684278141695
Intang_TA,0.055529505,0.012313991,-0.2621616760442048
PPE_TA,0.054736733,0.012742097,0.7375466098131811
PB,0.05009698,0.014093289,0.8478996700005101
Debt_Equity,0.049957793,0.016467342,0.7761299520554691
WC_Sales,0.049925398,0.013046193,0.807391339748351
Industry Group (agregat),0.04668503,0.15102921,
CashST_TA,0.04609975,0.011933475,-0.7600641165697042
TL_TA,0.042747002,0.014053993,-0.8764548551626897
Prepaid_CA,0.0402919,0.013951697,-0.22968063564815547
NetCash_TA,0.037167843,0.013336075,-0.5737072525508242
Altman_X1_WC_TA,0.037121613,0.014401976,0.49531502608174477
Age_When_IPO,0.036031056,0.013671853,0.5992003227947154
Sales_growth,0.0357716,0.012587108,-0.7729751233853595
Sales_per_share,0.034883704,0.012403338,0.6532586886087574
GrossMargin,0.03321539,0.0154362405,0.010650029818728913
CFO_TL,0.032796174,0.013836142,-0.2660556104895719
CR,0.03105291,0.01333415,0.11841758147087618
CA_TA,0.02863456,0.0121469535,-0.0176961854796148
CFO_Sales,0.027253969,0.012090952,-0.3836491794952272
CFO_per_share,0.02665546,0.012733933,-0.09109293438925303
log_TA,0.025196731,0.011968922,-0.17522892227826747
Altman_X4_MVE_TL,0.021575313,0.013938266,0.825447794877386
QR,0.021166923,0.014942494,-0.27330286552938704
Equity_TA,0.01922914,0.012268942,0.33077153994456526
log_Sales,0.018563671,0.012286092,0.2791326759530095
CL_TA,0.0167992,0.015058582,-0.3807544782410951
Confusion Matrix Visualisasi
chosen_id_list = [
# --- BARIS 1 (Diisi berurutan dari kiri ke kanan) ---
'ppk_keep_all_RandomForest', # Masuk ke Kotak 1 (Kiri Atas)
'negeq_keep_all_RandomForest', # Masuk ke Kotak 2 (Tengah Atas)
'conloss_keep_all_XGBoost', # Masuk ke Kotak 3 (Kanan Atas)
# --- BARIS 2 (Turun ke bawah, diisi dari kiri ke kanan) ---
'ppk_keep_corr_CatBoost', # Masuk ke Kotak 4 (Kiri Bawah)
'negeq_keep_corr_RandomForest', # Masuk ke Kotak 5 (Tengah Bawah)
'conloss_keep_corr_CatBoost' # Masuk ke Kotak 6 (Kanan Bawah)
]# Gunakan constrained_layout=True agar susunan otomatis rapi
fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(16, 9), constrained_layout=True)
fig.suptitle('Evaluasi Confusion Matrix Model Final per Indikator Financial Distress',
fontsize=18, fontweight='bold')
axes = axes.flatten()
# Penamaan judul kolom secara manual
kolom_titles = [
"Papan Pemantauan Khusus",
"Ekuitas Negatif",
"Rugi Berturut-turut"
]
for idx, chosen_id in enumerate(chosen_id_list):
# Tarik data prediksi
y_true = dict_detailed_predictions[chosen_id]['y_true']
y_pred = dict_detailed_predictions[chosen_id]['y_pred']
# Hitung Confusion Matrix
cm = confusion_matrix(y_true, y_pred)
# Plot Heatmap
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=['Sehat (0)', 'Distress (1)'],
yticklabels=['Sehat (0)', 'Distress (1)'],
annot_kws={"size": 14},
ax=axes[idx])
# Penggabungan Judul dengan Modifikasi Jarak
if idx < 3:
target_name = kolom_titles[idx]
axes[idx].set_title(f"\nIndikator : {target_name}\n\n{chosen_id}", fontsize=11, fontweight='bold')
else:
# Untuk baris kedua, tidak perlu diberi \n di awal
axes[idx].set_title(chosen_id, fontsize=11, fontweight='bold')
axes[idx].set_ylabel('Aktual')
axes[idx].set_xlabel('Prediksi')
plt.show()
Analisis Sebaran Prediksi Distress Papan Pemantauan Khusus
ppk_keep_all_RandomForest
chosen_id = 'ppk_keep_all_RandomForest'dict_detailed_predictions[chosen_id].keys()dict_keys(['metadata', 'y_true', 'y_pred', 'y_prob', 'X_test_features'])
data_pred = dict_detailed_predictions[chosen_id]
# gabung jadi 1 df
df_analyze = data_pred['metadata'].copy()
df_analyze['True_Label'] = data_pred['y_true']
df_analyze['Predicted_Label'] = data_pred['y_pred']
df_analyze['Prediction_Prob'] = data_pred['y_prob']
df_analyze = pd.concat([df_analyze, data_pred['X_test_features']], axis=1)
# FN
df_fn = df_analyze[(df_analyze['True_Label'] == 1) & (df_analyze['Predicted_Label'] == 0)]
df_fn = df_fn.sort_values(by='Prediction_Prob', ascending=False)
print(f"Total False Negative ({chosen_id}): {len(df_fn)} emiten")
display(df_fn[['ticker', 'Year', 'True_Label', 'Predicted_Label', 'Prediction_Prob']].head(10))
# FP
df_fp = df_analyze[(df_analyze['True_Label'] == 0) & (df_analyze['Predicted_Label'] == 1)]
df_fp = df_fp.sort_values(by='Prediction_Prob', ascending=False)
print(f"\nTotal False Positive ({chosen_id}): {len(df_fp)} emiten")
display(df_fp[['ticker', 'Year', 'True_Label', 'Predicted_Label', 'Prediction_Prob']].head(10))Total False Negative (ppk_keep_all_RandomForest): 28 emiten
| ticker | Year | True_Label | Predicted_Label | Prediction_Prob | |
|---|---|---|---|---|---|
| 545 | SKLT | 2023 | 1 | 0 | 0.102213 |
| 636 | WIFI | 2023 | 1 | 0 | 0.101544 |
| 399 | MGLV | 2023 | 1 | 0 | 0.096362 |
| 503 | PYFA | 2023 | 1 | 0 | 0.092915 |
| 563 | SOSS | 2023 | 1 | 0 | 0.092372 |
| 551 | SMDM | 2023 | 1 | 0 | 0.092151 |
| 130 | BTON | 2023 | 1 | 0 | 0.090695 |
| 189 | DMND | 2023 | 1 | 0 | 0.090122 |
| 2 | ABDA | 2023 | 1 | 0 | 0.090054 |
| 481 | POLU | 2023 | 1 | 0 | 0.089773 |
Total False Positive (ppk_keep_all_RandomForest): 175 emiten
| ticker | Year | True_Label | Predicted_Label | Prediction_Prob | |
|---|---|---|---|---|---|
| 501 | PURI | 2023 | 0 | 1 | 0.351597 |
| 436 | NATO | 2023 | 0 | 1 | 0.345191 |
| 491 | PSDN | 2023 | 0 | 1 | 0.326002 |
| 123 | BRMS | 2023 | 0 | 1 | 0.324656 |
| 455 | PAMG | 2023 | 0 | 1 | 0.322508 |
| 489 | PRIM | 2023 | 0 | 1 | 0.317136 |
| 185 | DIVA | 2023 | 0 | 1 | 0.315250 |
| 606 | TRGU | 2023 | 0 | 1 | 0.313993 |
| 374 | LUCY | 2023 | 0 | 1 | 0.308776 |
| 69 | BAPA | 2023 | 0 | 1 | 0.298206 |
detail = dict_detailed_predictions[chosen_id]
X_test = detail['X_test_features'].copy().fillna(np.nan).astype(float)
y_true = pd.Series(np.asarray(detail['y_true']), index=X_test.index) # sesuaikan nama key label asli
y_pred = pd.Series(np.asarray(detail['y_pred']), index=X_test.index) # sesuaikan nama key kelas akhir setelah threshold
# Kelompokkan menjadi TN / FP / FN / TP
group = np.where((y_true==0) & (y_pred==0), 'TN',
np.where((y_true==0) & (y_pred==1), 'FP',
np.where((y_true==1) & (y_pred==0), 'FN', 'TP')))
group = pd.Series(group, index=X_test.index, name='group')
# Bandingkan median rasio kunci antar kelompok (sesuaikan dengan fitur dominan tiap indikator)
key_ratios = ['ROE','NetMargin','ROA','Altman_X3_EBIT_TA','NetDebt_EBITDA',
'Debt_TA','CR','log_MktCap','Sales_growth']
key_ratios = [c for c in key_ratios if c in X_test.columns]
summary = X_test[key_ratios].groupby(group).median().T
print(f"{chosen_id} :")
print(summary.to_csv())
print(group.value_counts().to_csv()) # jumlah tiap kelompokppk_keep_all_RandomForest :
,FN,FP,TN,TP
ROE,0.06729980021381832,0.012779070173507958,0.09899950850764239,0.012311284126411488
NetMargin,0.08545301006744334,0.015416713079188772,0.11281632748782558,0.006069147527492284
ROA,0.0373286219082203,0.007687880597593954,0.0545393249490186,0.002300934205812785
Altman_X3_EBIT_TA,0.06799366819128755,0.020966144615435098,0.08732108511965313,0.011865895257493377
NetDebt_EBITDA,0.3697371800428746,2.1265031297108754,0.28439978762948187,0.6903017626390058
Debt_TA,0.13421265127934168,0.1863485439635218,0.14066510661028264,0.22171117497587162
CR,2.107,1.6165,1.8330000000000002,1.3435000000000001
log_MktCap,14.705208754988682,13.231576462379438,15.242326335419186,12.952733626363845
Sales_growth,0.010634273318872012,0.08185789455747872,0.019928757002864894,-0.025013078631246688
group,count
TN,270
TP,178
FP,175
FN,28
GROUP_ORDER = ['TN', 'FP', 'FN', 'TP']
# Dictionary konfigurasi warna & pola strip
GROUP_CONFIG = {
'TN': {'facecolor': '#2e7d32', 'edgecolor': '#2e7d32', 'hatch': ''}, # Hijau Polos
'TP': {'facecolor': '#c62828', 'edgecolor': '#c62828', 'hatch': ''}, # Merah Polos
# FP: Dasar Hijau, Strip Merah
'FP': {'facecolor': '#2e7d32', 'edgecolor': '#c62828', 'hatch': r'\\'},
# FN: Dasar Merah, Strip Hijau
'FN': {'facecolor': '#c62828', 'edgecolor': '#2e7d32', 'hatch': r'//'}
}
# [PERBAIKAN] Nilai n_cols diubah default-nya menjadi 5
def plot_group_profiles(summary, title, n_cols=5):
# summary: DataFrame dengan index = rasio dan kolom memuat TN/TP/FP/FN
cols = [g for g in GROUP_ORDER if g in summary.columns]
ratios = list(summary.index)
n = len(ratios)
# n_rows akan otomatis menyesuaikan (misal: 15 rasio / 5 kolom = 3 baris)
n_rows = int(np.ceil(n / n_cols))
# Ukuran figsize otomatis melebar ke samping (5 * 3.3 = 16.5 inci)
fig, axes = plt.subplots(n_rows, n_cols, figsize=(n_cols * 3.3, n_rows * 2.7))
axes = np.array(axes).reshape(-1)
for i, ratio in enumerate(ratios):
ax = axes[i]
vals = [summary.loc[ratio, g] for g in cols]
# Plot bar standar
bars = ax.bar(cols, vals)
# Kustomisasi warna dasar dan pola strip per boks grafik
for b, g in zip(bars, cols):
b.set_facecolor(GROUP_CONFIG[g]['facecolor'])
b.set_edgecolor(GROUP_CONFIG[g]['edgecolor'])
b.set_hatch(GROUP_CONFIG[g]['hatch'])
b.set_linewidth(1.2)
ax.set_title(ratio, fontsize=9)
ax.axhline(0, color='gray', linewidth=0.6)
ax.tick_params(axis='both', labelsize=8)
for b, v in zip(bars, vals):
ax.annotate(f"{v:.3f}", (b.get_x() + b.get_width() / 2, v),
ha='center', va='bottom' if v >= 0 else 'top', fontsize=10)
# Menghapus sumbu koordinat untuk kotak grid yang kosong di akhir
for j in range(n, len(axes)):
axes[j].axis('off')
# Kustomisasi Kotak Legend
handles = [
plt.Rectangle((0, 0), 1, 1,
facecolor=GROUP_CONFIG[g]['facecolor'],
edgecolor=GROUP_CONFIG[g]['edgecolor'],
hatch=GROUP_CONFIG[g]['hatch'])
for g in cols
]
fig.legend(handles, cols, loc='lower center', ncol=len(cols), fontsize=10)
fig.suptitle(title, fontsize=14)
fig.tight_layout(rect=[0, 0.05, 1, 0.97])
plt.show()
return fig
group_summaries = {} # buat sebelum loop
# di dalam loop, setelah baris summary = X_test[key_ratios].groupby(group).median().T
group_summaries[chosen_id] = summary
# setelah loop selesai
for cid, s in group_summaries.items():
plot_group_profiles(s, f"Perbandingan Rerata Fitur Tiap Kuadran Confusion Matrix\n{cid}")
ppk_keep_corr_CatBoost
chosen_id = 'ppk_keep_corr_CatBoost'data_pred = dict_detailed_predictions[chosen_id]
# gabung jadi 1 df
df_analyze = data_pred['metadata'].copy()
df_analyze['True_Label'] = data_pred['y_true']
df_analyze['Predicted_Label'] = data_pred['y_pred']
df_analyze['Prediction_Prob'] = data_pred['y_prob']
df_analyze = pd.concat([df_analyze, data_pred['X_test_features']], axis=1)
# FN
df_fn = df_analyze[(df_analyze['True_Label'] == 1) & (df_analyze['Predicted_Label'] == 0)]
df_fn = df_fn.sort_values(by='Prediction_Prob', ascending=False)
print(f"Total False Negative ({chosen_id}): {len(df_fn)} emiten")
display(df_fn[['ticker', 'Year', 'True_Label', 'Predicted_Label', 'Prediction_Prob']].head(10))
# FP
df_fp = df_analyze[(df_analyze['True_Label'] == 0) & (df_analyze['Predicted_Label'] == 1)]
df_fp = df_fp.sort_values(by='Prediction_Prob', ascending=False)
print(f"\nTotal False Positive ({chosen_id}): {len(df_fp)} emiten")
display(df_fp[['ticker', 'Year', 'True_Label', 'Predicted_Label', 'Prediction_Prob']].head(10))Total False Negative (ppk_keep_corr_CatBoost): 24 emiten
| ticker | Year | True_Label | Predicted_Label | Prediction_Prob | |
|---|---|---|---|---|---|
| 150 | CBUT | 2023 | 1 | 0 | 0.009317 |
| 503 | PYFA | 2023 | 1 | 0 | 0.007977 |
| 330 | KDSI | 2023 | 1 | 0 | 0.007216 |
| 156 | CITA | 2023 | 1 | 0 | 0.006434 |
| 615 | UANG | 2023 | 1 | 0 | 0.006121 |
| 447 | NRCA | 2023 | 1 | 0 | 0.006096 |
| 84 | BCIC | 2023 | 1 | 0 | 0.005454 |
| 233 | GDST | 2023 | 1 | 0 | 0.005378 |
| 109 | BMAS | 2023 | 1 | 0 | 0.005265 |
| 467 | PGUN | 2023 | 1 | 0 | 0.004901 |
Total False Positive (ppk_keep_corr_CatBoost): 189 emiten
| ticker | Year | True_Label | Predicted_Label | Prediction_Prob | |
|---|---|---|---|---|---|
| 224 | FIRE | 2023 | 0 | 1 | 0.274268 |
| 69 | BAPA | 2023 | 0 | 1 | 0.221099 |
| 501 | PURI | 2023 | 0 | 1 | 0.217655 |
| 489 | PRIM | 2023 | 0 | 1 | 0.203752 |
| 123 | BRMS | 2023 | 0 | 1 | 0.178601 |
| 148 | CASH | 2023 | 0 | 1 | 0.175944 |
| 180 | DFAM | 2023 | 0 | 1 | 0.173372 |
| 631 | WAPO | 2023 | 0 | 1 | 0.173192 |
| 450 | OILS | 2023 | 0 | 1 | 0.172687 |
| 436 | NATO | 2023 | 0 | 1 | 0.169980 |
detail = dict_detailed_predictions[chosen_id]
X_test = detail['X_test_features'].copy().fillna(np.nan).astype(float)
y_true = pd.Series(np.asarray(detail['y_true']), index=X_test.index) # sesuaikan nama key label asli
y_pred = pd.Series(np.asarray(detail['y_pred']), index=X_test.index) # sesuaikan nama key kelas akhir setelah threshold
# Kelompokkan menjadi TN / FP / FN / TP
group = np.where((y_true==0) & (y_pred==0), 'TN',
np.where((y_true==0) & (y_pred==1), 'FP',
np.where((y_true==1) & (y_pred==0), 'FN', 'TP')))
group = pd.Series(group, index=X_test.index, name='group')
# Bandingkan median rasio kunci antar kelompok (sesuaikan dengan fitur dominan tiap indikator)
key_ratios = ['ROE','NetMargin','ROA','Altman_X3_EBIT_TA','NetDebt_EBITDA',
'Debt_TA','CR','log_MktCap','Sales_growth']
key_ratios = [c for c in key_ratios if c in X_test.columns]
summary = X_test[key_ratios].groupby(group).median().T
print(f"{chosen_id} :")
print(summary.to_csv())
print(group.value_counts().to_csv()) # jumlah tiap kelompokppk_keep_corr_CatBoost :
,FN,FP,TN,TP
ROE,0.0737876732128985,0.024918503599246248,0.09409720005512201,0.013487727488502679
NetMargin,0.08294989586052003,0.03154538916724116,0.10904804856248175,0.008568794964422415
Altman_X3_EBIT_TA,0.05608328409660638,0.029782958843770236,0.0808543970531425,0.01551875119097589
NetDebt_EBITDA,0.3697371800428746,1.6911939680814774,0.3010230064922693,0.6683013036180658
Debt_TA,0.13421265127934168,0.1863485439635218,0.14066510661028264,0.21197304602592348
CR,1.801,1.589,1.872,1.4845000000000002
log_MktCap,15.273112231471357,13.548624083703452,15.36946819508624,12.86321784992093
Sales_growth,-0.09335639346925134,0.09955861183635883,-0.0010163012901684976,0.002483406827388346
group,count
TN,256
FP,189
TP,182
FN,24
Analisis Sebaran Prediksi Distress Ekuitas Negatif
negeq_keep_all_RandomForest
chosen_id = 'negeq_keep_all_RandomForest'data_pred = dict_detailed_predictions[chosen_id]
# gabung jadi 1 df
df_analyze = data_pred['metadata'].copy()
df_analyze['True_Label'] = data_pred['y_true']
df_analyze['Predicted_Label'] = data_pred['y_pred']
df_analyze['Prediction_Prob'] = data_pred['y_prob']
df_analyze = pd.concat([df_analyze, data_pred['X_test_features']], axis=1)
# FN
df_fn = df_analyze[(df_analyze['True_Label'] == 1) & (df_analyze['Predicted_Label'] == 0)]
df_fn = df_fn.sort_values(by='Prediction_Prob', ascending=False)
print(f"Total False Negative ({chosen_id}): {len(df_fn)} emiten")
display(df_fn[['ticker', 'Year', 'True_Label', 'Predicted_Label', 'Prediction_Prob']].head(10))
# FP
df_fp = df_analyze[(df_analyze['True_Label'] == 0) & (df_analyze['Predicted_Label'] == 1)]
df_fp = df_fp.sort_values(by='Prediction_Prob', ascending=False)
print(f"\nTotal False Positive ({chosen_id}): {len(df_fp)} emiten")
display(df_fp[['ticker', 'Year', 'True_Label', 'Predicted_Label', 'Prediction_Prob']].head(10))Total False Negative (negeq_keep_all_RandomForest): 5 emiten
| ticker | Year | True_Label | Predicted_Label | Prediction_Prob | |
|---|---|---|---|---|---|
| 238 | INAF | 2020 | 1 | 0 | 0.016615 |
| 1591 | PBRX | 2022 | 1 | 0 | 0.015860 |
| 811 | INAF | 2021 | 1 | 0 | 0.010583 |
| 1678 | SINI | 2022 | 1 | 0 | 0.006428 |
| 1050 | SINI | 2021 | 1 | 0 | 0.005528 |
Total False Positive (negeq_keep_all_RandomForest): 145 emiten
| ticker | Year | True_Label | Predicted_Label | Prediction_Prob | |
|---|---|---|---|---|---|
| 1454 | JAWA | 2022 | 0 | 1 | 0.436186 |
| 356 | MPPA | 2020 | 0 | 1 | 0.410476 |
| 1552 | MPPA | 2022 | 0 | 1 | 0.399255 |
| 610 | ATIC | 2021 | 0 | 1 | 0.374170 |
| 1402 | HELI | 2022 | 0 | 1 | 0.370407 |
| 968 | PDES | 2021 | 0 | 1 | 0.358411 |
| 1603 | PKPK | 2022 | 0 | 1 | 0.344816 |
| 34 | ARII | 2020 | 0 | 1 | 0.344791 |
| 684 | BULL | 2021 | 0 | 1 | 0.344670 |
| 550 | WSKT | 2020 | 0 | 1 | 0.343143 |
detail = dict_detailed_predictions[chosen_id]
X_test = detail['X_test_features'].copy().fillna(np.nan).astype(float)
y_true = pd.Series(np.asarray(detail['y_true']), index=X_test.index) # sesuaikan nama key label asli
y_pred = pd.Series(np.asarray(detail['y_pred']), index=X_test.index) # sesuaikan nama key kelas akhir setelah threshold
# Kelompokkan menjadi TN / FP / FN / TP
group = np.where((y_true==0) & (y_pred==0), 'TN',
np.where((y_true==0) & (y_pred==1), 'FP',
np.where((y_true==1) & (y_pred==0), 'FN', 'TP')))
group = pd.Series(group, index=X_test.index, name='group')
# Bandingkan median rasio kunci antar kelompok (sesuaikan dengan fitur dominan tiap indikator)
key_ratios = ['ROE','NetMargin','ROA','Altman_X3_EBIT_TA','NetDebt_EBITDA',
'Debt_TA','CR','log_MktCap','Sales_growth']
key_ratios = [c for c in key_ratios if c in X_test.columns]
summary = X_test[key_ratios].groupby(group).median().T
print(f"{chosen_id} :")
print(summary.to_csv())
print(group.value_counts().to_csv()) # jumlah tiap kelompoknegeq_keep_all_RandomForest :
,FN,FP,TN,TP
ROE,0.00655221648461087,-0.31780350980816624,0.04880420791817566,-1.2164644547600976
NetMargin,0.0033858418593652457,-0.21143220540852398,0.05248763223726913,-0.43555411960998003
ROA,0.003083477014625799,-0.08407876265522239,0.021723854675538962,-0.16809272696915364
Altman_X3_EBIT_TA,0.04413040869536313,-0.036905096394676984,0.04537896012869113,-0.07603731604254854
NetDebt_EBITDA,3.8983060533868716,-0.24730026745581324,0.9298757432311806,-1.8241009048130288
Debt_TA,0.3397132285383936,0.46540483566166735,0.1577316703887841,0.49102897766222076
CR,1.35,0.784,1.8295,0.547
log_MktCap,13.458343958881528,13.501860203904133,14.40421758037468,12.92067831698373
Sales_growth,0.2622269674201323,-0.16629620504781423,0.08129965435234521,-0.14904697345265228
group,count
TN,1609
FP,145
TP,29
FN,5
negeq_keep_corr_RandomForest
chosen_id = 'negeq_keep_corr_RandomForest'data_pred = dict_detailed_predictions[chosen_id]
# gabung jadi 1 df
df_analyze = data_pred['metadata'].copy()
df_analyze['True_Label'] = data_pred['y_true']
df_analyze['Predicted_Label'] = data_pred['y_pred']
df_analyze['Prediction_Prob'] = data_pred['y_prob']
df_analyze = pd.concat([df_analyze, data_pred['X_test_features']], axis=1)
# FN
df_fn = df_analyze[(df_analyze['True_Label'] == 1) & (df_analyze['Predicted_Label'] == 0)]
df_fn = df_fn.sort_values(by='Prediction_Prob', ascending=False)
print(f"Total False Negative ({chosen_id}): {len(df_fn)} emiten")
display(df_fn[['ticker', 'Year', 'True_Label', 'Predicted_Label', 'Prediction_Prob']].head(10))
# FP
df_fp = df_analyze[(df_analyze['True_Label'] == 0) & (df_analyze['Predicted_Label'] == 1)]
df_fp = df_fp.sort_values(by='Prediction_Prob', ascending=False)
print(f"\nTotal False Positive ({chosen_id}): {len(df_fp)} emiten")
display(df_fp[['ticker', 'Year', 'True_Label', 'Predicted_Label', 'Prediction_Prob']].head(10))Total False Negative (negeq_keep_corr_RandomForest): 5 emiten
| ticker | Year | True_Label | Predicted_Label | Prediction_Prob | |
|---|---|---|---|---|---|
| 811 | INAF | 2021 | 1 | 0 | 0.018610 |
| 238 | INAF | 2020 | 1 | 0 | 0.006540 |
| 1678 | SINI | 2022 | 1 | 0 | 0.005010 |
| 1591 | PBRX | 2022 | 1 | 0 | 0.003880 |
| 1050 | SINI | 2021 | 1 | 0 | 0.000926 |
Total False Positive (negeq_keep_corr_RandomForest): 207 emiten
| ticker | Year | True_Label | Predicted_Label | Prediction_Prob | |
|---|---|---|---|---|---|
| 1454 | JAWA | 2022 | 0 | 1 | 0.427964 |
| 1552 | MPPA | 2022 | 0 | 1 | 0.374975 |
| 610 | ATIC | 2021 | 0 | 1 | 0.360179 |
| 1603 | PKPK | 2022 | 0 | 1 | 0.326559 |
| 356 | MPPA | 2020 | 0 | 1 | 0.321963 |
| 1402 | HELI | 2022 | 0 | 1 | 0.313309 |
| 1000 | PSDN | 2021 | 0 | 1 | 0.302539 |
| 968 | PDES | 2021 | 0 | 1 | 0.299183 |
| 1624 | PSDN | 2022 | 0 | 1 | 0.298423 |
| 34 | ARII | 2020 | 0 | 1 | 0.285318 |
detail = dict_detailed_predictions[chosen_id]
X_test = detail['X_test_features'].copy().fillna(np.nan).astype(float)
y_true = pd.Series(np.asarray(detail['y_true']), index=X_test.index) # sesuaikan nama key label asli
y_pred = pd.Series(np.asarray(detail['y_pred']), index=X_test.index) # sesuaikan nama key kelas akhir setelah threshold
# Kelompokkan menjadi TN / FP / FN / TP
group = np.where((y_true==0) & (y_pred==0), 'TN',
np.where((y_true==0) & (y_pred==1), 'FP',
np.where((y_true==1) & (y_pred==0), 'FN', 'TP')))
group = pd.Series(group, index=X_test.index, name='group')
# Bandingkan median rasio kunci antar kelompok (sesuaikan dengan fitur dominan tiap indikator)
key_ratios = ['ROE','NetMargin','ROA','Altman_X3_EBIT_TA','NetDebt_EBITDA',
'Debt_TA','CR','log_MktCap','Sales_growth']
key_ratios = [c for c in key_ratios if c in X_test.columns]
summary = X_test[key_ratios].groupby(group).median().T
print(f"{chosen_id} :")
print(summary.to_csv())
print(group.value_counts().to_csv()) # jumlah tiap kelompoknegeq_keep_corr_RandomForest :
,FN,FP,TN,TP
NetMargin,0.0033858418593652457,-0.18398862501132704,0.05542375368151475,-0.43555411960998003
ROA,0.003083477014625799,-0.07264230501754582,0.02418591882874731,-0.16809272696915364
Altman_X3_EBIT_TA,0.04413040869536313,-0.03836857971202155,0.04834008115888791,-0.07603731604254854
NetDebt_EBITDA,3.8983060533868716,1.481162864455563,0.8701563192272355,-1.8241009048130288
CR,1.35,0.8725,1.8495,0.547
log_MktCap,13.458343958881528,13.61608070123334,14.409578775981627,12.92067831698373
Sales_growth,0.2622269674201323,-0.16289992615988558,0.08651037088095137,-0.14904697345265228
group,count
TN,1547
FP,207
TP,29
FN,5
Analisis Sebaran Prediksi Distress Rugi Berturut-turut
conloss_keep_corr_CatBoost
chosen_id = 'conloss_keep_corr_CatBoost'data_pred = dict_detailed_predictions[chosen_id]
# gabung jadi 1 df
df_analyze = data_pred['metadata'].copy()
df_analyze['True_Label'] = data_pred['y_true']
df_analyze['Predicted_Label'] = data_pred['y_pred']
df_analyze['Prediction_Prob'] = data_pred['y_prob']
df_analyze = pd.concat([df_analyze, data_pred['X_test_features']], axis=1)
# FN
df_fn = df_analyze[(df_analyze['True_Label'] == 1) & (df_analyze['Predicted_Label'] == 0)]
df_fn = df_fn.sort_values(by='Prediction_Prob', ascending=False)
print(f"Total False Negative ({chosen_id}): {len(df_fn)} emiten")
display(df_fn[['ticker', 'Year', 'True_Label', 'Predicted_Label', 'Prediction_Prob']].head(10))
# FP
df_fp = df_analyze[(df_analyze['True_Label'] == 0) & (df_analyze['Predicted_Label'] == 1)]
df_fp = df_fp.sort_values(by='Prediction_Prob', ascending=False)
print(f"\nTotal False Positive ({chosen_id}): {len(df_fp)} emiten")
display(df_fp[['ticker', 'Year', 'True_Label', 'Predicted_Label', 'Prediction_Prob']].head(10))Total False Negative (conloss_keep_corr_CatBoost): 8 emiten
| ticker | Year | True_Label | Predicted_Label | Prediction_Prob | |
|---|---|---|---|---|---|
| 132 | FIRE | 2020 | 1 | 0 | 0.057245 |
| 1093 | PGLI | 2022 | 1 | 0 | 0.041237 |
| 48 | BBYB | 2020 | 1 | 0 | 0.038582 |
| 655 | NICK | 2021 | 1 | 0 | 0.035563 |
| 164 | IKBI | 2020 | 1 | 0 | 0.027424 |
| 768 | TPIA | 2021 | 1 | 0 | 0.024245 |
| 1038 | LUCY | 2022 | 1 | 0 | 0.021578 |
| 986 | INDR | 2022 | 1 | 0 | 0.017618 |
Total False Positive (conloss_keep_corr_CatBoost): 338 emiten
| ticker | Year | True_Label | Predicted_Label | Prediction_Prob | |
|---|---|---|---|---|---|
| 21 | ARKA | 2020 | 0 | 1 | 0.952964 |
| 767 | TOYS | 2021 | 0 | 1 | 0.921327 |
| 75 | BOLT | 2020 | 0 | 1 | 0.907221 |
| 948 | FLMC | 2022 | 0 | 1 | 0.886973 |
| 1081 | NPGF | 2022 | 0 | 1 | 0.879342 |
| 418 | AMIN | 2021 | 0 | 1 | 0.865227 |
| 311 | RISE | 2020 | 0 | 1 | 0.842084 |
| 771 | TRIN | 2021 | 0 | 1 | 0.826556 |
| 240 | MGRO | 2020 | 0 | 1 | 0.821270 |
| 207 | KKGI | 2020 | 0 | 1 | 0.797002 |
detail = dict_detailed_predictions[chosen_id]
X_test = detail['X_test_features'].copy().fillna(np.nan).astype(float)
y_true = pd.Series(np.asarray(detail['y_true']), index=X_test.index) # sesuaikan nama key label asli
y_pred = pd.Series(np.asarray(detail['y_pred']), index=X_test.index) # sesuaikan nama key kelas akhir setelah threshold
# Kelompokkan menjadi TN / FP / FN / TP
group = np.where((y_true==0) & (y_pred==0), 'TN',
np.where((y_true==0) & (y_pred==1), 'FP',
np.where((y_true==1) & (y_pred==0), 'FN', 'TP')))
group = pd.Series(group, index=X_test.index, name='group')
# Bandingkan median rasio kunci antar kelompok (sesuaikan dengan fitur dominan tiap indikator)
key_ratios = ['ROE','NetMargin','ROA','Altman_X3_EBIT_TA','NetDebt_EBITDA',
'Debt_TA','CR','log_MktCap','Sales_growth']
key_ratios = [c for c in key_ratios if c in X_test.columns]
summary = X_test[key_ratios].groupby(group).median().T
print(f"{chosen_id} :")
print(summary.to_csv())
print(group.value_counts().to_csv()) # jumlah tiap kelompokconloss_keep_corr_CatBoost :
,FN,FP,TN,TP
ROE,0.06973346472475316,0.013818175197601413,0.10642121740162772,-0.021012988674344306
NetMargin,0.06561682167705439,0.022098578640617778,0.12129330947292344,-0.053001829489680956
ROA,0.03865848800501101,0.006478717218521261,0.05761557462599317,-0.013737448470650032
Altman_X3_EBIT_TA,0.061140744489493616,0.020776397229527455,0.09543227819932194,-0.006566356588855474
NetDebt_EBITDA,-0.6522820015736247,3.0126958621639,0.12739919640942127,3.431945781488151
Debt_TA,0.1625305941549952,0.18870753274333193,0.11710562177318196,0.24667656877397628
CR,2.641,1.62,2.0084999999999997,1.439
log_MktCap,13.789160423497503,13.951596122757346,15.176876458371567,13.333281658216443
Sales_growth,-0.003497763784430008,-0.04027502829524687,0.12257603914504323,-0.09611319659319512
group,count
TN,731
FP,338
TP,150
FN,8
conloss_keep_all_XGBoost
chosen_id = 'conloss_keep_all_XGBoost'data_pred = dict_detailed_predictions[chosen_id]
# gabung jadi 1 df
df_analyze = data_pred['metadata'].copy()
df_analyze['True_Label'] = data_pred['y_true']
df_analyze['Predicted_Label'] = data_pred['y_pred']
df_analyze['Prediction_Prob'] = data_pred['y_prob']
df_analyze = pd.concat([df_analyze, data_pred['X_test_features']], axis=1)
# FN
df_fn = df_analyze[(df_analyze['True_Label'] == 1) & (df_analyze['Predicted_Label'] == 0)]
df_fn = df_fn.sort_values(by='Prediction_Prob', ascending=False)
print(f"Total False Negative ({chosen_id}): {len(df_fn)} emiten")
display(df_fn[['ticker', 'Year', 'True_Label', 'Predicted_Label', 'Prediction_Prob']].head(10))
# FP
df_fp = df_analyze[(df_analyze['True_Label'] == 0) & (df_analyze['Predicted_Label'] == 1)]
df_fp = df_fp.sort_values(by='Prediction_Prob', ascending=False)
print(f"\nTotal False Positive ({chosen_id}): {len(df_fp)} emiten")
display(df_fp[['ticker', 'Year', 'True_Label', 'Predicted_Label', 'Prediction_Prob']].head(10))Total False Negative (conloss_keep_all_XGBoost): 9 emiten
| ticker | Year | True_Label | Predicted_Label | Prediction_Prob | |
|---|---|---|---|---|---|
| 132 | FIRE | 2020 | 1 | 0 | 0.058509 |
| 1181 | TGRA | 2022 | 1 | 0 | 0.056347 |
| 1011 | KBAG | 2022 | 1 | 0 | 0.051167 |
| 48 | BBYB | 2020 | 1 | 0 | 0.043425 |
| 655 | NICK | 2021 | 1 | 0 | 0.043004 |
| 1038 | LUCY | 2022 | 1 | 0 | 0.036423 |
| 164 | IKBI | 2020 | 1 | 0 | 0.033780 |
| 986 | INDR | 2022 | 1 | 0 | 0.017504 |
| 768 | TPIA | 2021 | 1 | 0 | 0.013567 |
Total False Positive (conloss_keep_all_XGBoost): 349 emiten
| ticker | Year | True_Label | Predicted_Label | Prediction_Prob | |
|---|---|---|---|---|---|
| 21 | ARKA | 2020 | 0 | 1 | 0.907378 |
| 767 | TOYS | 2021 | 0 | 1 | 0.875710 |
| 771 | TRIN | 2021 | 0 | 1 | 0.849894 |
| 1081 | NPGF | 2022 | 0 | 1 | 0.837772 |
| 131 | FILM | 2020 | 0 | 1 | 0.834883 |
| 400 | ZONE | 2020 | 0 | 1 | 0.827859 |
| 75 | BOLT | 2020 | 0 | 1 | 0.814995 |
| 128 | ESSA | 2020 | 0 | 1 | 0.813899 |
| 418 | AMIN | 2021 | 0 | 1 | 0.805717 |
| 965 | HELI | 2022 | 0 | 1 | 0.805020 |
detail = dict_detailed_predictions[chosen_id]
X_test = detail['X_test_features'].copy().fillna(np.nan).astype(float)
y_true = pd.Series(np.asarray(detail['y_true']), index=X_test.index) # sesuaikan nama key label asli
y_pred = pd.Series(np.asarray(detail['y_pred']), index=X_test.index) # sesuaikan nama key kelas akhir setelah threshold
# Kelompokkan menjadi TN / FP / FN / TP
group = np.where((y_true==0) & (y_pred==0), 'TN',
np.where((y_true==0) & (y_pred==1), 'FP',
np.where((y_true==1) & (y_pred==0), 'FN', 'TP')))
group = pd.Series(group, index=X_test.index, name='group')
# Bandingkan median rasio kunci antar kelompok (sesuaikan dengan fitur dominan tiap indikator)
key_ratios = ['ROE','NetMargin','ROA','Altman_X3_EBIT_TA','NetDebt_EBITDA',
'Debt_TA','CR','log_MktCap','Sales_growth']
key_ratios = [c for c in key_ratios if c in X_test.columns]
summary = X_test[key_ratios].groupby(group).median().T
print(f"{chosen_id} :")
print(summary.to_csv())
print(group.value_counts().to_csv()) # jumlah tiap kelompokconloss_keep_all_XGBoost :
,FN,FP,TN,TP
ROE,0.0391288162018532,0.01557552316677661,0.1071394545586764,-0.021598812200488975
NetMargin,0.07237938485018018,0.02241751236404642,0.12475275948013481,-0.05337829003929229
ROA,0.027331009244866512,0.007876164183510045,0.0584392967320556,-0.013772726153386183
Altman_X3_EBIT_TA,0.05657245925955019,0.02343737441022285,0.09704772804843081,-0.0075738046907378054
NetDebt_EBITDA,0.5056435143553407,2.9515924835737026,0.08390103135401727,2.920838191339234
Debt_TA,0.1513808856078739,0.21723667543184766,0.11209597726655329,0.25099671833206216
CR,2.402,1.6955,1.999,1.47
log_MktCap,13.098813236946116,13.926523435169825,15.221189484207997,13.384727641873358
Sales_growth,-0.0311681686020146,-0.04316301141169804,0.12456022216722484,-0.08736368319417454
group,count
TN,720
FP,349
TP,149
FN,9
Rancangan Deployment
Deployment
!pip install session_info -qimport session_info
session_info.show()Click to view session information
----- catboost 1.2.10 google NA imblearn 0.14.2 lightgbm 4.6.0 matplotlib 3.10.0 numpy 2.0.2 optuna 4.9.0 pandas 2.2.2 plotly 5.24.1 scipy 1.16.3 seaborn 0.13.2 session_info v1.0.1 shap 0.52.0 sklearn 1.6.1 statsmodels 0.14.6 xgboost 3.2.0 -----
Click to view modules imported as dependencies
81d243bd2c585b0f4821__mypyc NA Cython 3.0.12 PIL 11.3.0 ac51d50a4f4b6d748b8c__mypyc NA anywidget 0.9.21 arrow 1.4.0 attr 26.1.0 attrs 26.1.0 backcall 0.2.0 bottleneck 1.4.2 certifi 2026.05.20 cffi 2.0.0 charset_normalizer 3.4.7 cloudpickle 3.1.2 colorlog NA cramjam 2.11.0 cv2 4.13.0 cvxopt 1.3.2 cycler 0.12.1 cython 3.0.12 cython_runtime NA dask 2026.3.0 dateutil 2.9.0.post0 debugpy 1.8.15 decorator 4.4.2 defusedxml 0.7.1 entrypoints 0.4 fastjsonschema NA fqdn NA fsspec 2025.3.0 graphviz 0.21 httplib2 0.31.2 idna 3.18 iniconfig NA ipykernel 6.17.1 ipython_genutils 0.2.0 ipywidgets 7.7.1 isoduration NA jinja2 3.1.6 joblib 1.5.3 jsonpointer 3.1.1 jsonschema 4.26.0 jsonschema_specifications NA kaleido 0.2.1 kiwisolver 1.5.0 lark 1.3.1 llvmlite 0.43.0 lxml 6.1.1 markupsafe 3.0.3 matplotlib_inline 0.2.2 mmh3 NA mpl_toolkits NA nbformat 5.10.4 numba 0.60.0 numexpr 2.14.1 openpyxl 3.1.5 orjson 3.11.9 packaging 26.2 patsy 1.0.2 pexpect 4.9.0 pickleshare 0.7.5 platformdirs 4.10.0 pluggy 1.6.0 polars 1.35.2 prompt_toolkit 3.0.52 psutil 5.9.5 psygnal 0.15.1 ptyprocess 0.7.0 py NA py4j 0.10.9.9 pyarrow 18.1.0 pycparser 3.00 pydev_ipython NA pydevconsole NA pydevd 3.2.3 pydevd_file_utils NA pydevd_plugins NA pydevd_tracing NA pygments 2.20.0 pyparsing 3.3.2 pyspark 4.0.2 pytest 8.4.2 pytz 2025.2 referencing NA regex 2025.11.3 rfc3339_validator 0.1.4 rfc3986_validator 0.1.1 rfc3987_syntax NA rpds NA sitecustomize NA six 1.17.0 sklearn_compat 0.1.6 slicer NA snappy 0.7.3 socks 1.7.1 sphinxcontrib NA storemagic NA tenacity NA threadpoolctl 3.6.0 tlz 0.12.1 toolz 0.12.1 tornado 6.5.7 tqdm 4.67.3 traitlets 5.7.1 typing_extensions NA uri_template NA vscode NA wcwidth 0.8.1 webcolors NA xarray 2025.12.0 xlrd 2.0.2 xxhash NA yaml 6.0.3 zmq 26.2.1 zoneinfo NA zstandard 0.25.0
----- IPython 7.34.0 jupyter_client 7.4.9 jupyter_core 5.9.1 notebook 6.5.7 ----- Python 3.12.13 (main, Mar 4 2026, 09:23:07) [GCC 11.4.0] Linux-6.6.122+-x86_64-with-glibc2.35 ----- Session information updated at 2026-06-13 14:15