194 lines
5.5 KiB
Python
194 lines
5.5 KiB
Python
"""
|
|
Standarisasi Kolom Module
|
|
Memetakan kolom dari berbagai tahun ke kolom standar
|
|
"""
|
|
|
|
import pandas as pd
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Dict, List, Optional
|
|
|
|
|
|
class ColumnStandardizer:
|
|
"""
|
|
Kelas untuk menstandarisasi kolom dari berbagai file Excel
|
|
dengan struktur kolom yang berbeda-beda
|
|
"""
|
|
|
|
def __init__(self, mapping_file: str = None):
|
|
"""
|
|
Args:
|
|
mapping_file: Path ke file JSON mapping kolom
|
|
"""
|
|
if mapping_file is None:
|
|
# Default path
|
|
mapping_file = Path(__file__).parent.parent / "column_mapping.json"
|
|
|
|
self.mapping_file = Path(mapping_file)
|
|
self.mappings = self._load_mappings()
|
|
|
|
def _load_mappings(self) -> Dict:
|
|
"""Load mapping dari file JSON"""
|
|
if not self.mapping_file.exists():
|
|
raise FileNotFoundError(f"Mapping file tidak ditemukan: {self.mapping_file}")
|
|
|
|
with open(self.mapping_file, 'r', encoding='utf-8') as f:
|
|
return json.load(f)
|
|
|
|
def get_mapping(self, year: str) -> Dict:
|
|
"""
|
|
Ambil mapping untuk tahun tertentu
|
|
|
|
Args:
|
|
year: Tahun data (2022, 2023, 2024)
|
|
|
|
Returns:
|
|
Dictionary dengan mapping kolom
|
|
"""
|
|
if year not in self.mappings:
|
|
raise ValueError(f"Mapping untuk tahun {year} tidak ditemukan. "
|
|
f"Tahun yang tersedia: {list(self.mappings.keys())}")
|
|
return self.mappings[year]
|
|
|
|
def detect_year(self, df: pd.DataFrame, filename: str = None) -> Optional[str]:
|
|
"""
|
|
Deteksi tahun dari DataFrame atau filename
|
|
|
|
Args:
|
|
df: DataFrame untuk dicek kolomnya
|
|
filename: Nama file (opsional)
|
|
|
|
Returns:
|
|
Tahun yang terdeteksi atau None
|
|
"""
|
|
# Cek dari filename
|
|
if filename:
|
|
for year in self.mappings.keys():
|
|
if year in filename:
|
|
return year
|
|
|
|
# Cek dari kolom yang tersedia
|
|
available_years = []
|
|
for year, mapping in self.mappings.items():
|
|
match_count = sum(1 for col in mapping.values() if col in df.columns)
|
|
if match_count >= 2: # Minimal 2 kolom cocok
|
|
available_years.append((year, match_count))
|
|
|
|
if available_years:
|
|
# Pilih tahun dengan match terbanyak
|
|
return max(available_years, key=lambda x: x[1])[0]
|
|
|
|
return None
|
|
|
|
def standardize(self, df: pd.DataFrame, year: str = None, filename: str = None) -> pd.DataFrame:
|
|
"""
|
|
Standarisasi kolom DataFrame
|
|
|
|
Args:
|
|
df: DataFrame asli
|
|
year: Tahun data (auto-detect jika None)
|
|
filename: Nama file (opsional, untuk auto-detect)
|
|
|
|
Returns:
|
|
DataFrame dengan kolom standar
|
|
"""
|
|
# Deteksi tahun jika tidak specified
|
|
if year is None:
|
|
year = self.detect_year(df, filename)
|
|
|
|
if year is None:
|
|
raise ValueError("Tidak dapat mendeteksi tahun data. "
|
|
f"Pastikan kolom sesuai dengan mapping yang tersedia.")
|
|
|
|
# Ambil mapping
|
|
mapping = self.get_mapping(year)
|
|
|
|
# Buat DataFrame baru dengan kolom standar
|
|
result = pd.DataFrame()
|
|
for target_col, source_col in mapping.items():
|
|
if source_col in df.columns:
|
|
result[target_col] = df[source_col]
|
|
else:
|
|
result[target_col] = None
|
|
|
|
return result
|
|
|
|
def standardize_multiple(self, dfs: List[pd.DataFrame],
|
|
years: List[str] = None,
|
|
filenames: List[str] = None) -> List[pd.DataFrame]:
|
|
"""
|
|
Standarisasi multiple DataFrame sekaligus
|
|
|
|
Args:
|
|
dfs: List DataFrame
|
|
years: List tahun (auto-detect jika None)
|
|
filenames: List nama file (opsional)
|
|
|
|
Returns:
|
|
List DataFrame yang sudah distandarisasi
|
|
"""
|
|
results = []
|
|
|
|
for i, df in enumerate(dfs):
|
|
year = years[i] if years else None
|
|
filename = filenames[i] if filenames else None
|
|
|
|
standardized = self.standardize(df, year, filename)
|
|
results.append(standardized)
|
|
|
|
return results
|
|
|
|
|
|
def merge_datasets(dfs: List[pd.DataFrame]) -> pd.DataFrame:
|
|
"""
|
|
Gabungkan multiple DataFrame menjadi satu
|
|
|
|
Args:
|
|
dfs: List DataFrame
|
|
|
|
Returns:
|
|
DataFrame gabungan
|
|
"""
|
|
return pd.concat(dfs, ignore_index=True)
|
|
|
|
|
|
def clean_data(df: pd.DataFrame) -> pd.DataFrame:
|
|
"""
|
|
Basic data cleaning
|
|
|
|
Args:
|
|
df: DataFrame
|
|
|
|
Returns:
|
|
DataFrame yang sudah dibersihkan
|
|
"""
|
|
result = df.copy()
|
|
|
|
# Hapus baris dengan semua nilai kosong
|
|
result = result.dropna(how='all')
|
|
|
|
# Hapus duplikat
|
|
result = result.drop_duplicates()
|
|
|
|
# Isi nilai kosong dengan string kosong
|
|
result = result.fillna('')
|
|
|
|
return result
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Contoh penggunaan
|
|
standardizer = ColumnStandardizer()
|
|
|
|
# Sample data 2024
|
|
sample_2024 = pd.DataFrame({
|
|
"4. Nama Lengkap / nmmhsmsmh (Gunakan Huruf Besar)": ["Ali", "Budi"],
|
|
"9. Apa nama perusahaan/kantor tempat Anda bekerja? f5b": ["Google", "Tokopedia"],
|
|
"10. Bila Anda berwiraswasta, apa posisi/jabatan Anda saat ini ? f5c": ["Engineer", "Analyst"]
|
|
})
|
|
|
|
# Standarisasi
|
|
standardized = standardizer.standardize(sample_2024, year="2024")
|
|
print("Data Standarisasi:")
|
|
print(standardized)
|