# -*- coding: utf-8 -*- import pandas as pd import numpy as np import gradio as gr # Sample data generation def generate_sample_data(): np.random.seed(42) n_samples = 1000 towns = ['ANG MO KIO', 'BEDOK', 'CLEMENTI', 'QUEENSTOWN', 'TAMPINES'] flat_types = ['2 ROOM', '3 ROOM', '4 ROOM', '5 ROOM', 'EXECUTIVE'] flat_models = ['Improved', 'New Generation', 'Model A', 'Standard', 'Premium'] data = { 'town': np.random.choice(towns, n_samples), 'flat_type': np.random.choice(flat_types, n_samples), 'flat_model': np.random.choice(flat_models, n_samples), 'floor_area_sqm': np.random.uniform(60, 150, n_samples), 'storey_level': np.random.randint(1, 25, n_samples), 'flat_age': np.random.randint(0, 50, n_samples), 'resale_price': np.random.uniform(200000, 800000, n_samples) } return pd.DataFrame(data) # Load or create sample data data = generate_sample_data() # Create encoders for categorical variables towns_list = data['town'].unique().tolist() flat_types = data['flat_type'].unique().tolist() flat_models = data['flat_model'].unique().tolist() def simple_xgboost_emulation(input_data): """Emulate XGBoost with a slightly different formula""" # Different arbitrary weights to simulate a different model weights = { 'floor_area_sqm': 5200, 'storey_level': 1800, 'flat_age': -2800, 'remaining_lease': 1200, 'town_factor': 9500, 'flat_type_factor': 14500, 'flat_model_factor': 8500, 'base_price': 220000, 'interaction_factor': 500 # Simulate tree interactions } # Calculate factors town_factor = towns_list.index(input_data['town']) * weights['town_factor'] flat_type_factor = flat_types.index(input_data['flat_type']) * weights['flat_type_factor'] flat_model_factor = flat_models.index(input_data['flat_model']) * weights['flat_model_factor'] # Simulate tree interactions interaction = (input_data['floor_area_sqm'] * input_data['storey_level']) / 100 * weights['interaction_factor'] # Calculate price price = (weights['base_price'] + input_data['floor_area_sqm'] * weights['floor_area_sqm'] + input_data['storey_level'] * weights['storey_level'] + input_data['flat_age'] * weights['flat_age'] + input_data['remaining_lease'] * weights['remaining_lease'] + town_factor + flat_type_factor + flat_model_factor + interaction) return max(price, 100000) # Ensure price is at least 100,000 def preprocess_input(town, flat_type, flat_model, floor_area_sqm, storey_level, flat_age): """Preprocess user input into a format suitable for the models.""" input_data = { 'town': town, 'flat_type': flat_type, 'flat_model': flat_model, 'floor_area_sqm': float(floor_area_sqm), 'storey_level': int(storey_level), 'flat_age': int(flat_age), 'remaining_lease': 99 - int(flat_age) } return input_data def create_market_insights_chart(data, town, flat_type, predicted_price): """ Generate a simple text-based market insight since we can't use Plotly. """ # Filter data for the specific town and flat type filtered_data = data[(data['town'] == town) & (data['flat_type'] == flat_type)] if filtered_data.empty: return "No historical data available for this town and flat type combination." # Calculate some basic statistics avg_price = filtered_data['resale_price'].mean() min_price = filtered_data['resale_price'].min() max_price = filtered_data['resale_price'].max() count = len(filtered_data) insight_text = f""" ## Market Insights for {town} - {flat_type} - Historical transactions: {count} - Average price: ${avg_price:,.2f} - Price range: ${min_price:,.2f} - ${max_price:,.2f} ### Prediction: - Predicted Price: ${predicted_price:,.2f} *Note: These insights are based on simulated data.* """ return insight_text def predict_hdb_price(town, flat_type, flat_model, floor_area_sqm, storey_level, flat_age): """Predict the HDB resale price using the selected model.""" # Validate inputs try: floor_area_sqm = float(floor_area_sqm) storey_level = int(storey_level) flat_age = int(flat_age) except ValueError: return "Please enter valid numbers for floor area, storey level, and flat age.", "Invalid input", "Invalid input" # Preprocess the user input input_data = preprocess_input(town, flat_type, flat_model, floor_area_sqm, storey_level, flat_age) # Make prediction predicted_price = simple_xgboost_emulation(input_data) # Generate insights insights = create_market_insights_chart( data=data, town=town, flat_type=flat_type, predicted_price=predicted_price ) summary = f""" ### Property Details 🏡 - **Town:** {town} - **Flat Type:** {flat_type} - **Flat Model:** {flat_model} - **Floor Area:** {floor_area_sqm} sqm - **Storey Level:** {storey_level} - **Flat Age:** {flat_age} years --- ### Prediction Summary The predicted price is **${predicted_price:,.2f}**. *Note: This is a demo with simulated data and simple prediction models.* """ return f"${predicted_price:,.2f}", insights, summary # Create the Gradio interface with gr.Blocks(title="HDB Resale Price Predictor", theme=gr.themes.Soft()) as demo: gr.Markdown("# 🏘️ HDB Resale Price Predictor") gr.Markdown("Estimate the resale price of HDB flats in Singapore based on property features.") with gr.Row(): with gr.Column(): town = gr.Dropdown(choices=towns_list, label="Town", value="ANG MO KIO") flat_type = gr.Dropdown(choices=flat_types, label="Flat Type", value="4 ROOM") flat_model = gr.Dropdown(choices=flat_models, label="Flat Model", value="Improved") floor_area_sqm = gr.Number(label="Floor Area (sqm)", value=100) storey_level = gr.Slider(minimum=1, maximum=50, step=1, label="Storey Level", value=5) flat_age = gr.Slider(minimum=0, maximum=99, step=1, label="Flat Age (years)", value=10) predict_btn = gr.Button("Predict Price", variant="primary") with gr.Column(): price_output = gr.Label(label="Predicted Resale Price") insights_output = gr.Markdown() summary_output = gr.Markdown() predict_btn.click( fn=predict_hdb_price, inputs=[town, flat_type, flat_model, floor_area_sqm, storey_level, flat_age], outputs=[price_output, insights_output, summary_output] ) gr.Examples( examples=[ ["ANG MO KIO", "4 ROOM", "Improved", 100, 5, 10], ["BEDOK", "3 ROOM", "New Generation", 80, 8, 5], ["TAMPINES", "5 ROOM", "Model A", 120, 12, 15] ], inputs=[town, flat_type, flat_model, floor_area_sqm, storey_level, flat_age] ) # Launch the application if __name__ == "__main__": demo.launch(server_name="0.0.0.0", server_port=7860)