{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "provenance": [] }, "kernelspec": { "name": "python3", "display_name": "Python 3" }, "language_info": { "name": "python" } }, "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "import os\nfrom pathlib import Path\n\nPY_FIG_DIR = Path(\"artifacts/py/figures\")\nPY_TAB_DIR = Path(\"artifacts/py/tables\")\nPY_FIG_DIR.mkdir(parents=True, exist_ok=True)\nPY_TAB_DIR.mkdir(parents=True, exist_ok=True)\nprint(\"Artifact directories ready.\")\n" }, { "cell_type": "code", "execution_count": 2, "metadata": { "id": "LqEg38a305oW" }, "outputs": [], "source": [ "import pandas as pd\n", "import plotly.express as px\n", "\n", "df = pd.read_csv(\"FINAL_merged_clean_synthetic.csv\")\n", "df[\"city\"] = df[\"city\"].astype(str).str.strip().str.lower()" ] }, { "cell_type": "code", "source": [ "rating_city = (\n", " df.groupby(\"city\", as_index=False)[\"guest_satisfaction_overall\"]\n", " .mean()\n", " .rename(columns={\"guest_satisfaction_overall\": \"avg_rating\"})\n", " .sort_values(\"avg_rating\", ascending=False)\n", ")\n", "\n", "fig = px.bar(\n", " rating_city,\n", " x=\"city\",\n", " y=\"avg_rating\",\n", " text=rating_city[\"avg_rating\"].round(2),\n", " title=\"Average Rating by City\"\n", ")\n", "\n", "fig.update_layout(xaxis_tickangle=-45)\n", "fig.update_traces(textposition=\"outside\")\n", "fig.show()" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "kFbGxd9w248V", "outputId": "ab77c8a9-cafa-44ef-c685-d24f3626f403" }, "execution_count": 3, "outputs": [ { "output_type": "display_data", "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "price_city = (\n", " df.groupby(\"city\", as_index=False)[\"realsum\"]\n", " .mean()\n", " .rename(columns={\"realsum\": \"avg_price\"})\n", " .sort_values(\"avg_price\", ascending=False)\n", ")\n", "\n", "fig = px.bar(\n", " price_city,\n", " x=\"city\",\n", " y=\"avg_price\",\n", " text=price_city[\"avg_price\"].round(2),\n", " title=\"Average Price by City\"\n", ")\n", "\n", "fig.update_layout(xaxis_tickangle=-45)\n", "fig.update_traces(textposition=\"outside\")\n", "fig.show()" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "2ELY3yjQ3BuD", "outputId": "8d0d42fd-9a96-4d13-d5ea-6cbf60bc08f7" }, "execution_count": 4, "outputs": [ { "output_type": "display_data", "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "tmp = df.copy()\n", "tmp[\"bedrooms\"] = pd.to_numeric(tmp[\"bedrooms\"], errors=\"coerce\")\n", "tmp = tmp.dropna(subset=[\"bedrooms\"])\n", "tmp[\"bedrooms\"] = tmp[\"bedrooms\"].astype(int)\n", "\n", "price_bed_city = (\n", " tmp.groupby([\"city\", \"bedrooms\"], as_index=False)[\"realsum\"]\n", " .mean()\n", " .rename(columns={\"realsum\": \"avg_price\"})\n", ")\n", "\n", "fig = px.bar(\n", " price_bed_city,\n", " x=\"bedrooms\",\n", " y=\"avg_price\",\n", " color=\"city\",\n", " barmode=\"group\",\n", " title=\"Average Price by Bedrooms and City\"\n", ")\n", "\n", "fig.show()" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "naJjOUsp3GJa", "outputId": "8ab3a1a5-8fd0-4e5f-fc7b-fdcff0f34b14" }, "execution_count": 5, "outputs": [ { "output_type": "display_data", "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "import pandas as pd\n", "import plotly.express as px\n", "\n", "df = pd.read_csv(\"FINAL_merged_clean_synthetic.csv\")\n", "df[\"city\"] = df[\"city\"].astype(str).str.strip().str.title()\n", "\n", "# Count listings by city and room_type (keep only the two shown in the example)\n", "counts = (\n", " df[df[\"room_type\"].isin([\"Entire home/apt\", \"Private room\"])]\n", " .groupby([\"city\", \"room_type\"], as_index=False)\n", " .size()\n", " .rename(columns={\"size\": \"count\"})\n", ")\n", "\n", "# Order cities by total listings (descending), like typical dashboards\n", "city_order = (\n", " counts.groupby(\"city\", as_index=False)[\"count\"]\n", " .sum()\n", " .sort_values(\"count\", ascending=False)[\"city\"]\n", " .tolist()\n", ")\n", "\n", "fig = px.bar(\n", " counts,\n", " y=\"city\",\n", " x=\"count\",\n", " color=\"room_type\",\n", " orientation=\"h\",\n", " barmode=\"stack\",\n", " category_orders={\"city\": city_order},\n", " text=\"count\",\n", " title=\"Number of available apartments/rooms\",\n", " labels={\"count\": \"Count\", \"city\": \"City\", \"room_type\": \"Room type\"},\n", ")\n", "\n", "fig.update_traces(textposition=\"inside\")\n", "fig.update_layout(yaxis_title=\"\", xaxis_title=\"\")\n", "fig.show()" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "Wcsk8_Jl3TtO", "outputId": "65d6c483-d68a-4866-fbfe-85bd5dcfe7e8" }, "execution_count": 6, "outputs": [ { "output_type": "display_data", "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "import pandas as pd\n", "import plotly.graph_objects as go\n", "\n", "# Load dataset\n", "df = pd.read_csv(\"FINAL_merged_clean_synthetic.csv\")\n", "\n", "# Clean city names\n", "df[\"city\"] = df[\"city\"].astype(str).str.strip().str.title()\n", "\n", "# Compute average price by city and room_type\n", "avg_price = (\n", " df.groupby([\"city\", \"room_type\"], as_index=False)[\"realsum\"]\n", " .mean()\n", ")\n", "\n", "# Keep only the three main room types\n", "room_types = [\"Entire home/apt\", \"Private room\", \"Shared room\"]\n", "avg_price = avg_price[avg_price[\"room_type\"].isin(room_types)]\n", "\n", "# Ensure consistent city order\n", "city_order = sorted(avg_price[\"city\"].unique())\n", "\n", "fig = go.Figure()\n", "\n", "for room in room_types:\n", " subset = avg_price[avg_price[\"room_type\"] == room]\n", " subset = subset.set_index(\"city\").reindex(city_order).reset_index()\n", "\n", " fig.add_trace(go.Scatterpolar(\n", " r=subset[\"realsum\"],\n", " theta=subset[\"city\"],\n", " fill='none',\n", " name=room\n", " ))\n", "\n", "fig.update_layout(\n", " polar=dict(\n", " radialaxis=dict(\n", " visible=True\n", " )\n", " ),\n", " title=\"Average Price by City and Room Type\",\n", " showlegend=True\n", ")\n", "\n", "fig.show()" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "f7wZ1R753vLA", "outputId": "fed5218f-7e26-4d0e-bb28-9813ca18a58b" }, "execution_count": 7, "outputs": [ { "output_type": "display_data", "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "import pandas as pd\n", "import plotly.express as px\n", "\n", "df = pd.read_csv(\"FINAL_merged_clean_synthetic.csv\")\n", "df[\"city\"] = df[\"city\"].astype(str).str.strip().str.title()\n", "df[\"day\"] = df[\"day\"].astype(str).str.strip().str.lower()\n", "\n", "# Average price by city and day type\n", "avg_price_day = (\n", " df.groupby([\"city\", \"day\"], as_index=False)[\"realsum\"]\n", " .mean()\n", " .rename(columns={\"realsum\": \"avg_price\"})\n", ")\n", "\n", "# Keep only the two categories and order them\n", "avg_price_day = avg_price_day[avg_price_day[\"day\"].isin([\"weekdays\", \"weekend\"])]\n", "avg_price_day[\"day\"] = avg_price_day[\"day\"].map({\"weekdays\": \"Weekday\", \"weekend\": \"Weekend\"})\n", "\n", "# Order cities by overall average price (descending)\n", "city_order = (\n", " df.groupby(\"city\", as_index=False)[\"realsum\"]\n", " .mean()\n", " .sort_values(\"realsum\", ascending=False)[\"city\"]\n", " .tolist()\n", ")\n", "\n", "fig = px.scatter(\n", " avg_price_day,\n", " x=\"city\",\n", " y=\"avg_price\",\n", " color=\"day\",\n", " symbol=\"day\",\n", " category_orders={\"city\": city_order, \"day\": [\"Weekday\", \"Weekend\"]},\n", " title=\"Average Price by City: Weekday vs Weekend\",\n", " labels={\"city\": \"City\", \"avg_price\": \"Average price\", \"day\": \"\"},\n", ")\n", "\n", "fig.update_traces(marker=dict(size=12))\n", "fig.update_layout(xaxis_tickangle=-45)\n", "fig.show()" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "FiJVaG-A4t-b", "outputId": "9aa185a3-bc9a-4c7b-90d7-d2a6bcea53f5" }, "execution_count": 8, "outputs": [ { "output_type": "display_data", "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "import pandas as pd\n", "import plotly.express as px\n", "\n", "# Load dataset\n", "df = pd.read_csv(\"FINAL_merged_clean_synthetic.csv\")\n", "\n", "# Ensure numeric format\n", "df[\"booking_window_days\"] = pd.to_numeric(df[\"booking_window_days\"], errors=\"coerce\")\n", "df[\"guest_satisfaction_overall\"] = pd.to_numeric(df[\"guest_satisfaction_overall\"], errors=\"coerce\")\n", "\n", "# Remove missing values\n", "df = df.dropna(subset=[\"booking_window_days\", \"guest_satisfaction_overall\"])\n", "\n", "# Create interactive scatter plot with trendline\n", "fig = px.scatter(\n", " df,\n", " x=\"booking_window_days\",\n", " y=\"guest_satisfaction_overall\",\n", " trendline=\"lowess\",\n", " opacity=0.4,\n", " title=\"Booking Window vs Guest Satisfaction Rating\",\n", " labels={\n", " \"booking_window_days\": \"Booking Window (Days)\",\n", " \"guest_satisfaction_overall\": \"Guest Satisfaction Rating\"\n", " }\n", ")\n", "\n", "fig.update_traces(marker=dict(size=6))\n", "fig.show()" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "dBjpeGdh5bug", "outputId": "23044862-fc5d-42d9-fa1f-7f2053041918" }, "execution_count": 9, "outputs": [ { "output_type": "display_data", "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "import pandas as pd\n", "import plotly.express as px\n", "\n", "# Load dataset\n", "df = pd.read_csv(\"FINAL_merged_clean_synthetic.csv\")\n", "\n", "# Ensure correct types\n", "df[\"has_amenities_bundle\"] = pd.to_numeric(df[\"has_amenities_bundle\"], errors=\"coerce\")\n", "df[\"guest_satisfaction_overall\"] = pd.to_numeric(df[\"guest_satisfaction_overall\"], errors=\"coerce\")\n", "\n", "# Remove missing values\n", "df = df.dropna(subset=[\"has_amenities_bundle\", \"guest_satisfaction_overall\"])\n", "\n", "# Convert amenities to readable labels\n", "df[\"Amenities Included\"] = df[\"has_amenities_bundle\"].map({0: \"No\", 1: \"Yes\"})\n", "\n", "# Interactive boxplot\n", "fig = px.box(\n", " df,\n", " x=\"Amenities Included\",\n", " y=\"guest_satisfaction_overall\",\n", " color=\"Amenities Included\",\n", " points=\"all\",\n", " title=\"Guest Satisfaction by Amenities Inclusion\",\n", " labels={\n", " \"guest_satisfaction_overall\": \"Guest Satisfaction Rating\",\n", " \"Amenities Included\": \"Amenities Bundle Included\"\n", " }\n", ")\n", "\n", "fig.show()" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "Eux_3QKa5wWt", "outputId": "ad006e0c-5358-4863-dda6-291b1f4b1686" }, "execution_count": 10, "outputs": [ { "output_type": "display_data", "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "# ============================================================\n", "# STEP 9: MACHINE LEARNING - PREDICTIVE PRICING MODEL\n", "# ============================================================\n", "\n", "import pandas as pd\n", "import plotly.express as px\n", "from sklearn.model_selection import train_test_split\n", "from sklearn.ensemble import RandomForestRegressor\n", "\n", "# Ensure data is clean and formatted for Machine Learning\n", "df = pd.read_csv(\"FINAL_merged_clean_synthetic.csv\")\n", "df[\"city\"] = df[\"city\"].astype(str).str.strip().str.title()\n", "df[\"day\"] = df[\"day\"].astype(str).str.strip().str.lower()\n", "\n", "# Select logical features for predicting the target variable (realsum/price)\n", "features = [\n", " 'realsum', 'person_capacity', 'bedrooms', 'distance_from_center_km',\n", " 'cleanliness_rating', 'guest_satisfaction_overall',\n", " 'city', 'room_type', 'day'\n", "]\n", "df_ml = df[features].dropna().copy()\n", "\n", "# Apply One-Hot Encoding to categorical variables (city, room_type, day)\n", "df_ml = pd.get_dummies(df_ml, drop_first=True)\n", "\n", "X = df_ml.drop('realsum', axis=1)\n", "y = df_ml['realsum']\n", "\n", "# Train-Test Split and Random Forest Training (using 100 decision trees)\n", "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n", "rf = RandomForestRegressor(n_estimators=100, random_state=42, n_jobs=-1)\n", "rf.fit(X_train, y_train)\n", "\n", "# Extract Feature Importance (Identifying key price drivers)\n", "importances = rf.feature_importances_\n", "feat_imp = pd.DataFrame({'Feature': X.columns, 'Importance': importances})\n", "\n", "# Logically group features for a clean, strategic business chart\n", "summary_imp = {\n", " 'Size (Capacity & Bedrooms)': feat_imp[feat_imp['Feature'].str.contains('capacity|bedrooms')]['Importance'].sum(),\n", " 'Location (Center Dist)': feat_imp[feat_imp['Feature'].str.contains('dist')]['Importance'].sum(),\n", " 'Quality (Satisfaction & Cleanliness)': feat_imp[feat_imp['Feature'].str.contains('cleanliness|satisfaction')]['Importance'].sum(),\n", " 'City Geography': feat_imp[feat_imp['Feature'].str.contains('city')]['Importance'].sum(),\n", " 'Room Type': feat_imp[feat_imp['Feature'].str.contains('room_type')]['Importance'].sum(),\n", " 'Day (Weekend vs Weekday)': feat_imp[feat_imp['Feature'].str.contains('day')]['Importance'].sum()\n", "}\n", "final_imp = pd.DataFrame(list(summary_imp.items()), columns=['Factor', 'Impact']).sort_values('Impact', ascending=True)\n", "\n", "# Generate an interactive Plotly chart consistent with the rest of the dashboard\n", "fig = px.bar(\n", " final_imp, x=\"Impact\", y=\"Factor\", orientation='h',\n", " title=\"Strategic Determinants of Airbnb Price (Random Forest AI)\",\n", " text_auto='.1%', color=\"Impact\", color_continuous_scale='Blues',\n", " labels={\"Impact\": \"Relative Impact on Price\", \"Factor\": \"Market Factor\"}\n", ")\n", "fig.update_traces(textposition='inside')\n", "fig.show()" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "id": "iCABvmAFoQAt", "outputId": "771ea390-498d-4e5f-94ee-43a667f6ae2e" }, "execution_count": 12, "outputs": [ { "output_type": "display_data", "data": { "text/html": [ "\n", "\n", "\n", "
\n", "
\n", "\n", "" ] }, "metadata": {} } ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# ============================================================\n# ARTIFACT EXPORT \u2014 save all figures + tables\n# ============================================================\nimport os\nfrom pathlib import Path\nimport pandas as pd\nimport plotly.express as px\nimport plotly.graph_objects as go\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.ensemble import RandomForestRegressor\n\nPY_FIG_DIR = Path(\"artifacts/py/figures\")\nPY_TAB_DIR = Path(\"artifacts/py/tables\")\nPY_FIG_DIR.mkdir(parents=True, exist_ok=True)\nPY_TAB_DIR.mkdir(parents=True, exist_ok=True)\n\ndf = pd.read_csv(\"FINAL_merged_clean_synthetic.csv\")\ndf[\"city\"] = df[\"city\"].astype(str).str.strip().str.title()\ndf[\"day\"] = df[\"day\"].astype(str).str.strip().str.lower()\n\n# 1. Avg rating by city\nrating_city = (df.groupby(\"city\", as_index=False)[\"guest_satisfaction_overall\"]\n .mean().rename(columns={\"guest_satisfaction_overall\": \"avg_rating\"})\n .sort_values(\"avg_rating\", ascending=False))\nfig = px.bar(rating_city, x=\"city\", y=\"avg_rating\", text=rating_city[\"avg_rating\"].round(1),\n title=\"Average Guest Rating by City\", color=\"avg_rating\", color_continuous_scale=\"Blues\")\nfig.write_image(str(PY_FIG_DIR / \"01_avg_rating_by_city.png\"), width=900, height=500)\nrating_city.to_csv(str(PY_TAB_DIR / \"avg_rating_by_city.csv\"), index=False)\nprint(\"Saved: 01_avg_rating_by_city.png\")\n\n# 2. Avg price by city\nprice_city = (df.groupby(\"city\", as_index=False)[\"realsum\"]\n .mean().rename(columns={\"realsum\": \"avg_price\"})\n .sort_values(\"avg_price\", ascending=False))\nfig = px.bar(price_city, x=\"city\", y=\"avg_price\", text=price_city[\"avg_price\"].round(0),\n title=\"Average Price by City (\u20ac)\", color=\"avg_price\", color_continuous_scale=\"Reds\")\nfig.write_image(str(PY_FIG_DIR / \"02_avg_price_by_city.png\"), width=900, height=500)\nprice_city.to_csv(str(PY_TAB_DIR / \"avg_price_by_city.csv\"), index=False)\nprint(\"Saved: 02_avg_price_by_city.png\")\n\n# 3. Room type distribution\ncounts = (df[df[\"room_type\"].isin([\"Entire home/apt\", \"Private room\"])]\n .groupby([\"city\", \"room_type\"], as_index=False).size().rename(columns={\"size\": \"count\"}))\nfig = px.bar(counts, x=\"city\", y=\"count\", color=\"room_type\", barmode=\"group\",\n title=\"Room Type Distribution by City\", color_discrete_sequence=[\"#2196F3\", \"#FF9800\"])\nfig.write_image(str(PY_FIG_DIR / \"03_room_type_distribution.png\"), width=900, height=500)\ncounts.to_csv(str(PY_TAB_DIR / \"room_type_distribution.csv\"), index=False)\nprint(\"Saved: 03_room_type_distribution.png\")\n\n# 4. Weekday vs weekend price\navg_price_day = (df.groupby([\"city\", \"day\"], as_index=False)[\"realsum\"]\n .mean().rename(columns={\"realsum\": \"avg_price\"}))\nfig = px.bar(avg_price_day, x=\"city\", y=\"avg_price\", color=\"day\", barmode=\"group\",\n title=\"Weekday vs Weekend Average Price by City\",\n color_discrete_map={\"weekdays\": \"#42A5F5\", \"weekend\": \"#EF5350\"})\nfig.write_image(str(PY_FIG_DIR / \"04_weekday_vs_weekend.png\"), width=900, height=500)\navg_price_day.to_csv(str(PY_TAB_DIR / \"weekday_vs_weekend_price.csv\"), index=False)\nprint(\"Saved: 04_weekday_vs_weekend.png\")\n\n# 5. Bedrooms vs price\ntmp = df.copy()\ntmp[\"bedrooms\"] = pd.to_numeric(tmp[\"bedrooms\"], errors=\"coerce\")\ntmp = tmp.dropna(subset=[\"bedrooms\"])\ntmp[\"bedrooms\"] = tmp[\"bedrooms\"].astype(int)\nprice_bed = tmp.groupby([\"city\", \"bedrooms\"], as_index=False)[\"realsum\"].mean().rename(columns={\"realsum\": \"avg_price\"})\nfig = px.line(price_bed, x=\"bedrooms\", y=\"avg_price\", color=\"city\",\n title=\"Bedrooms vs Average Price by City\", markers=True)\nfig.write_image(str(PY_FIG_DIR / \"05_bedrooms_vs_price.png\"), width=900, height=500)\nprice_bed.to_csv(str(PY_TAB_DIR / \"bedrooms_vs_price.csv\"), index=False)\nprint(\"Saved: 05_bedrooms_vs_price.png\")\n\n# 6. Booking window vs satisfaction\ndf2 = df.copy()\ndf2[\"booking_window_days\"] = pd.to_numeric(df2[\"booking_window_days\"], errors=\"coerce\")\ndf2[\"guest_satisfaction_overall\"] = pd.to_numeric(df2[\"guest_satisfaction_overall\"], errors=\"coerce\")\ndf2 = df2.dropna(subset=[\"booking_window_days\", \"guest_satisfaction_overall\"])\ndf2[\"booking_bin\"] = pd.cut(df2[\"booking_window_days\"], bins=[0,7,30,90,180,365], labels=[\"1-7d\",\"8-30d\",\"31-90d\",\"91-180d\",\"181-365d\"])\nsat_bw = df2.groupby(\"booking_bin\", as_index=False)[\"guest_satisfaction_overall\"].mean()\nfig = px.bar(sat_bw, x=\"booking_bin\", y=\"guest_satisfaction_overall\",\n title=\"Booking Window vs Guest Satisfaction\", color=\"guest_satisfaction_overall\", color_continuous_scale=\"Greens\")\nfig.write_image(str(PY_FIG_DIR / \"06_booking_window_satisfaction.png\"), width=900, height=500)\nsat_bw.to_csv(str(PY_TAB_DIR / \"booking_window_satisfaction.csv\"), index=False)\nprint(\"Saved: 06_booking_window_satisfaction.png\")\n\n# 7. ML Feature Importance\nfeatures = ['realsum','person_capacity','bedrooms','distance_from_center_km',\n 'cleanliness_rating','guest_satisfaction_overall','city','room_type','day']\ndf_ml = df[features].dropna().copy()\ndf_ml = pd.get_dummies(df_ml, drop_first=True)\nX = df_ml.drop('realsum', axis=1)\ny = df_ml['realsum']\nX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\nrf = RandomForestRegressor(n_estimators=100, random_state=42, n_jobs=-1)\nrf.fit(X_train, y_train)\nimportances = rf.feature_importances_\nfeat_imp = pd.DataFrame({'Feature': X.columns, 'Importance': importances})\nsummary_imp = {\n 'Size (Capacity & Bedrooms)': feat_imp[feat_imp['Feature'].str.contains('capacity|bedrooms')]['Importance'].sum(),\n 'Location (Center Dist)': feat_imp[feat_imp['Feature'].str.contains('dist')]['Importance'].sum(),\n 'Quality (Satisfaction & Cleanliness)': feat_imp[feat_imp['Feature'].str.contains('cleanliness|satisfaction')]['Importance'].sum(),\n 'City Geography': feat_imp[feat_imp['Feature'].str.contains('city')]['Importance'].sum(),\n 'Room Type': feat_imp[feat_imp['Feature'].str.contains('room_type')]['Importance'].sum(),\n 'Day (Weekend vs Weekday)': feat_imp[feat_imp['Feature'].str.contains('day')]['Importance'].sum()\n}\nfinal_imp = pd.DataFrame(list(summary_imp.items()), columns=['Factor','Impact']).sort_values('Impact', ascending=True)\nfig = px.bar(final_imp, x=\"Impact\", y=\"Factor\", orientation='h',\n title=\"Strategic Determinants of Airbnb Price (Random Forest)\", text_auto='.1%',\n color=\"Impact\", color_continuous_scale='Blues',\n labels={\"Impact\": \"Relative Impact on Price\", \"Factor\": \"Market Factor\"})\nfig.update_traces(textposition='inside')\nfig.write_image(str(PY_FIG_DIR / \"07_ml_feature_importance.png\"), width=900, height=500)\nfinal_imp.to_csv(str(PY_TAB_DIR / \"ml_feature_importance.csv\"), index=False)\nfeat_imp.to_csv(str(PY_TAB_DIR / \"ml_feature_importance_full.csv\"), index=False)\nprint(\"Saved: 07_ml_feature_importance.png\")\n\n# KPIs summary\nkpis = {\n \"n_cities\": int(df[\"city\"].nunique()),\n \"total_listings\": int(len(df)),\n \"avg_price_eur\": round(float(df[\"realsum\"].mean()), 2),\n \"avg_rating\": round(float(df[\"guest_satisfaction_overall\"].mean()), 2),\n \"weekend_premium_pct\": round(float(\n (df[df[\"day\"]==\"weekend\"][\"realsum\"].mean() - df[df[\"day\"]==\"weekdays\"][\"realsum\"].mean())\n / df[df[\"day\"]==\"weekdays\"][\"realsum\"].mean() * 100), 2)\n}\nimport json\nwith open(str(PY_TAB_DIR / \"kpis.json\"), \"w\") as f:\n json.dump(kpis, f, indent=2)\nprint(\"Saved: kpis.json\")\nprint(\"All artifacts exported successfully!\")\n" } ] }