import gradio as gr import pandas as pd import numpy as np import plotly.graph_objects as go import plotly.express as px from datetime import datetime import json # Core pricing function matching your Excel formula def calculate_price(lot_size, base_size, base_price, adjustment_increment, over_adjustment, under350_adjustment, under250_adjustment, user_adjustment=0): """Calculate price based on lot size using tiered pricing model""" if lot_size >= base_size: price = base_price + ((lot_size - base_size) / adjustment_increment) * over_adjustment elif lot_size >= 250: price = base_price + ((lot_size - base_size) / adjustment_increment) * under350_adjustment else: price = base_price + ((lot_size - base_size) / adjustment_increment) * under250_adjustment price += user_adjustment return np.ceil(price / 1000) * 1000 # Enhanced ML-based price predictor def ml_price_suggestion(lot_size, location_score, market_trend, amenity_score): """Simple ML model for price suggestions based on additional factors""" # Base calculation base_suggestion = 255000 + (lot_size - 350) * 500 # Location multiplier (0.8 to 1.2) location_mult = 0.8 + (location_score / 10) * 0.4 # Market trend adjustment (-10% to +10%) trend_mult = 1 + (market_trend / 100) # Amenity bonus (0 to 15%) amenity_mult = 1 + (amenity_score / 10) * 0.15 final_price = base_suggestion * location_mult * trend_mult * amenity_mult return np.ceil(final_price / 1000) * 1000 # Generate pricing table def generate_pricing_table(base_size, base_price, adjustment_increment, over_adjustment, under350_adjustment, under250_adjustment): """Generate comprehensive pricing table""" lot_sizes = list(range(170, 580, 10)) prices = [] tiers = [] for size in lot_sizes: price = calculate_price(size, base_size, base_price, adjustment_increment, over_adjustment, under350_adjustment, under250_adjustment) prices.append(price) if size >= base_size: tiers.append("Premium (≥350)") elif size >= 250: tiers.append("Standard (250-349)") else: tiers.append("Compact (<250)") df = pd.DataFrame({ 'Lot Size (sqm)': lot_sizes, 'Price ($)': prices, 'Price per sqm': [p/s for p, s in zip(prices, lot_sizes)], 'Tier': tiers }) return df # Visualization functions def create_price_curve(df): """Create interactive price curve visualization""" fig = px.line(df, x='Lot Size (sqm)', y='Price ($)', color='Tier', markers=True, title='Price Curve by Lot Size') fig.update_layout( hovermode='x unified', xaxis_title="Lot Size (sqm)", yaxis_title="Price ($)", yaxis_tickformat='$,.0f' ) return fig def create_price_per_sqm(df): """Create price per sqm analysis""" fig = px.scatter(df, x='Lot Size (sqm)', y='Price per sqm', color='Tier', size='Price ($)', title='Price per Square Meter Analysis') fig.update_layout( yaxis_tickformat='$,.0f', xaxis_title="Lot Size (sqm)", yaxis_title="Price per sqm ($)" ) return fig def create_sensitivity_analysis(base_size, base_price, adjustment_increment): """Create sensitivity analysis for different adjustment rates""" lot_sizes = list(range(200, 500, 20)) scenarios = { 'Conservative': (15000, 30000, 35000), 'Current': (25000, 40000, 40000), 'Aggressive': (35000, 50000, 45000) } fig = go.Figure() for scenario_name, (over, under350, under250) in scenarios.items(): prices = [calculate_price(size, base_size, base_price, adjustment_increment, over, under350, under250) for size in lot_sizes] fig.add_trace(go.Scatter( x=lot_sizes, y=prices, mode='lines+markers', name=scenario_name )) fig.update_layout( title='Pricing Sensitivity Analysis', xaxis_title='Lot Size (sqm)', yaxis_title='Price ($)', yaxis_tickformat='$,.0f', hovermode='x unified' ) return fig # Main calculation function for Gradio def calculate_all(lot_size, base_size, base_price, adjustment_increment, over_adjustment, under350_adjustment, under250_adjustment, user_adjustment, location_score, market_trend, amenity_score): # Calculate traditional price traditional_price = calculate_price(lot_size, base_size, base_price, adjustment_increment, over_adjustment, under350_adjustment, under250_adjustment, user_adjustment) # Calculate ML-suggested price ml_price = ml_price_suggestion(lot_size, location_score, market_trend, amenity_score) # Generate pricing table df = generate_pricing_table(base_size, base_price, adjustment_increment, over_adjustment, under350_adjustment, under250_adjustment) # Create visualizations price_curve = create_price_curve(df) price_per_sqm = create_price_per_sqm(df) sensitivity = create_sensitivity_analysis(base_size, base_price, adjustment_increment) # Create detailed breakdown if lot_size >= base_size: tier = "Premium (≥350 sqm)" adjustment_rate = over_adjustment elif lot_size >= 250: tier = "Standard (250-349 sqm)" adjustment_rate = under350_adjustment else: tier = "Compact (<250 sqm)" adjustment_rate = under250_adjustment diff_from_base = lot_size - base_size increments = diff_from_base / adjustment_increment adjustment_amount = increments * adjustment_rate breakdown = f""" ## Price Calculation Breakdown **Lot Details:** - Size: {lot_size} sqm - Tier: {tier} - Difference from base: {diff_from_base:+.1f} sqm **Calculation:** - Base Price: ${base_price:,.0f} - Increments: {increments:.2f} × ${adjustment_rate:,.0f} - Adjustment: ${adjustment_amount:,.0f} - User Adjustment: ${user_adjustment:,.0f} - **Final Price: ${traditional_price:,.0f}** **ML-Enhanced Suggestion:** ${ml_price:,.0f} - Location Factor: {location_score}/10 - Market Trend: {market_trend:+.0f}% - Amenity Score: {amenity_score}/10 """ # Create comparison table comparison_df = pd.DataFrame({ 'Pricing Method': ['Formula-Based', 'ML-Enhanced', 'Difference'], 'Price': [f'${traditional_price:,.0f}', f'${ml_price:,.0f}', f'${abs(ml_price - traditional_price):,.0f}'] }) return (breakdown, comparison_df, price_curve, price_per_sqm, sensitivity) # Create Gradio interface with gr.Blocks(theme=gr.themes.Soft()) as demo: gr.Markdown(""" # 🏡 Real Estate Pricing Matrix Calculator This enhanced version of your Excel pricing formula includes: - **Traditional formula-based pricing** matching your Excel model - **ML-enhanced pricing** considering location, market trends, and amenities - **Interactive visualizations** for better decision making - **Sensitivity analysis** for different pricing strategies """) with gr.Row(): with gr.Column(scale=1): gr.Markdown("### Property Details") lot_size = gr.Slider(150, 600, 350, step=0.5, label="Lot Size (sqm)") gr.Markdown("### ML Enhancement Factors") location_score = gr.Slider(1, 10, 7, step=0.5, label="Location Score (1-10)") market_trend = gr.Slider(-20, 20, 0, step=1, label="Market Trend (%)") amenity_score = gr.Slider(1, 10, 5, step=0.5, label="Amenity Score (1-10)") with gr.Column(scale=1): gr.Markdown("### Pricing Parameters") base_size = gr.Number(350, label="Base Size (sqm)") base_price = gr.Number(255000, label="Base Price ($)") adjustment_increment = gr.Number(50, label="Adjustment Increment (sqm)") gr.Markdown("### Adjustment Rates ($)") over_adjustment = gr.Number(25000, label="Over Base Size") under350_adjustment = gr.Number(40000, label="250-349 sqm") under250_adjustment = gr.Number(40000, label="Under 250 sqm") user_adjustment = gr.Number(0, label="User Adjustment ($)") calculate_btn = gr.Button("Calculate Price", variant="primary") with gr.Row(): with gr.Column(): breakdown_output = gr.Markdown() comparison_output = gr.DataFrame() with gr.Row(): price_curve_output = gr.Plot() price_per_sqm_output = gr.Plot() sensitivity_output = gr.Plot() # Export functionality gr.Markdown("### Export Options") with gr.Row(): export_config = gr.JSON(label="Current Configuration") export_btn = gr.Button("Export Configuration") def export_configuration(base_size, base_price, adjustment_increment, over_adjustment, under350_adjustment, under250_adjustment): return { "base_size": base_size, "base_price": base_price, "adjustment_increment": adjustment_increment, "over_adjustment": over_adjustment, "under350_adjustment": under350_adjustment, "under250_adjustment": under250_adjustment, "exported_at": datetime.now().isoformat() } calculate_btn.click( calculate_all, inputs=[lot_size, base_size, base_price, adjustment_increment, over_adjustment, under350_adjustment, under250_adjustment, user_adjustment, location_score, market_trend, amenity_score], outputs=[breakdown_output, comparison_output, price_curve_output, price_per_sqm_output, sensitivity_output] ) export_btn.click( export_configuration, inputs=[base_size, base_price, adjustment_increment, over_adjustment, under350_adjustment, under250_adjustment], outputs=export_config ) # Load default calculation on start demo.load( calculate_all, inputs=[lot_size, base_size, base_price, adjustment_increment, over_adjustment, under350_adjustment, under250_adjustment, user_adjustment, location_score, market_trend, amenity_score], outputs=[breakdown_output, comparison_output, price_curve_output, price_per_sqm_output, sensitivity_output] ) if __name__ == "__main__": demo.launch()