SPB Git

spb/wp5_uqo Public

UQO Working Paper No. 5 — Airbnb, residential rents and housing market pressure.

TeX 53.4% Python 46.5%

Add extended robustness: clustered SEs, size control, log form, ring exposure, leave-one-city-out

- scripts/11_extended_robustness.py -> Table 12 (extended_robustness.tex)
  and Figure 9 (leave_one_city_out.pdf); existing results untouched
- Paper: new §6.5-6.6, clustered-SE result replaces the speculative
  inference caveat; recompiled clean (43 pages, 0 unresolved refs)
- Tighter layout on the three widest tables (overfull hboxes fixed)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed 5 days ago (Aug 5, 2026) parent 627dc7b

Showing 10 changed files with +314 and −4

modified CHANGES.md +27 −0
@@ -133,6 +133,33 @@ Sections 1 (introduction), 2 (literature) and most of 7–8 needed only the
133 133 consistency fixes above; the prose was already in polished academic English and
134 134 was otherwise preserved.
135 135
136 +## 4bis. Extended robustness added on request ("make paper more robust")
137 +
138 +New analyses (2026-08-05, after the initial restructuring commit) — the existing
139 +tables and figures are untouched; everything below is **additive**:
140 +
141 +- **`scripts/11_extended_robustness.py`**`results/tables/extended_robustness.tex`
142 + (Table 12) and `figures/leave_one_city_out.pdf` (Figure 9):
143 + - (R1) baseline (replicates Model 1c exactly: β = 0.0039, HC1);
144 + - (R2) **city-clustered standard errors** (153 clusters) — clustering *tightens*
145 + the SE on the Airbnb coefficient (0.0001 vs 0.0002), so baseline significance
146 + is conservative on this dimension;
147 + - (R3) **interior-size control** (per 100 sq ft, N = 3,531) — β = 0.0032***;
148 + - (R4) **log(1+count) functional form** — 0.048***: doubling the nearby count
149 + ≈ +3.4% rent; mild concavity;
150 + - (R5) **ring decomposition** (500m count + 500m–1km annulus jointly) — both
151 + positive and significant (0.0013 / 0.0018); reported honestly: the
152 + association extends to the kilometre scale rather than being confined to
153 + 500m;
154 + - **leave-one-city-out** over the 8 largest cities — β ranges 0.0038–0.0040;
155 + excluding Montreal (half the sample) leaves it at 0.0038.
156 +- Paper: new §6.5 "Additional Specification Checks" and §6.6 "Leave-One-City-Out
157 + Sensitivity"; the speculative "Inference Caveats" subsection was replaced by
158 + the actual clustered-SE result; §4 (Standard Errors) and §6.7 updated.
159 +- Cosmetic: `\small` + tighter `\tabcolsep` on the three widest tables removed
160 + the pre-existing overfull-hbox warnings (1 minor one remains, in a text line).
161 +- PDF: 43 pages, 0 unresolved references/citations.
162 +
136 163 ## 5. Items requiring your review
137 164
138 165 - **Raw data missing** (`airbnb.csv`, `rent.json`): absent from the original
modified README.md +5 −0
@@ -46,6 +46,8 @@ python3 scripts/07_spatial_models.py # Model 4 (SAR/SEM via spreg) + bu
46 46 python3 scripts/08_quantile_models.py # Model 5 (quantile regressions)
47 47 python3 scripts/09_ml_robustness.py # Model 6 (LASSO/ENet/RF/GBM + SHAP)
48 48 python3 scripts/10_robustness_tables_figures.py # robustness tables + extra figures
49 +python3 scripts/11_extended_robustness.py # clustered SEs, size control, log form,
50 + # ring exposure, leave-one-city-out
49 51
50 52 cd paper && latexmk # builds paper/main.pdf
51 53 ```
@@ -62,6 +64,9 @@ be run from any working directory.
62 64 the upper quantiles of the rent distribution.
63 65 - SAR/SEM spatial models confirm substantial spatial autocorrelation in rents;
64 66 the Airbnb coefficient survives with mild attenuation.
67 +- The estimate is stable under city-clustered standard errors, an interior-size
68 + control, a log(1+count) functional form, ring-exposure decomposition, and
69 + leave-one-city-out exclusions (including Montreal).
65 70 - ML benchmarks (random forest, gradient boosting) confirm the predictive
66 71 relevance of Airbnb exposure; the linear specification remains adequate.
67 72 - Cross-sectional design — associations, not causal effects.
added figures/leave_one_city_out.pdf +0 −0

Binary file not shown.

modified paper/main.pdf +0 −0

Binary file not shown.

modified paper/sections/03_data.tex +2 −0
@@ -48,6 +48,7 @@ Tables~\ref{tab:summary_airbnb} and~\ref{tab:summary_rent} present summary stati
48 48 \centering
49 49 \caption{Summary Statistics --- Airbnb Listings}
50 50 \label{tab:summary_airbnb}
51 + \small\setlength{\tabcolsep}{4pt}
51 52 \input{../results/tables/summary_stats_airbnb.tex}
52 53 \end{table}
53 54
@@ -55,6 +56,7 @@ Tables~\ref{tab:summary_airbnb} and~\ref{tab:summary_rent} present summary stati
55 56 \centering
56 57 \caption{Summary Statistics --- Residential Rental Listings}
57 58 \label{tab:summary_rent}
59 + \small\setlength{\tabcolsep}{4pt}
58 60 \input{../results/tables/summary_stats_rent.tex}
59 61 \end{table}
60 62
modified paper/sections/04_methodology.tex +1 −1
@@ -87,4 +87,4 @@ Our city fixed effects control for all time-invariant, city-level confounders, i
87 87
88 88 \subsection{Standard Errors}
89 89
90 Throughout the analysis, we report heteroskedasticity-robust standard errors (HC1) as our baseline. For the quantile regressions, we report the asymptotic standard errors produced by the kernel-based estimator in \texttt{statsmodels}. For the machine-learning models, we report held-out test-set performance metrics, with regularisation parameters selected by 5-fold cross-validation.
90 +Throughout the analysis, we report heteroskedasticity-robust standard errors (HC1) as our baseline; Section~\ref{sec:robustness} re-computes the preferred specification with standard errors clustered at the city level. For the quantile regressions, we report the asymptotic standard errors produced by the kernel-based estimator in \texttt{statsmodels}. For the machine-learning models, we report held-out test-set performance metrics, with regularisation parameters selected by 5-fold cross-validation.
modified paper/sections/05_results.tex +1 −0
@@ -87,6 +87,7 @@ Table~\ref{tab:quantile} and Figure~\ref{fig:quantile_plot} present the quantile
87 87 \centering
88 88 \caption{Quantile Regression Results --- Coefficient on Airbnb Count (500m)}
89 89 \label{tab:quantile}
90 + \small\setlength{\tabcolsep}{4pt}
90 91 \input{../results/tables/quantile_regression.tex}
91 92 \end{table}
92 93
modified paper/sections/06_robustness.tex +32 −3
@@ -76,9 +76,38 @@ To assess the sensitivity of the results to extreme values, we re-estimate the b
76 76
77 77 The results are stable across the outlier-handling approaches. Trimming the rent distribution at the 5th and 95th percentiles reduces the point estimate by roughly a fifth (from 0.0039 to 0.0031)---consistent with the quantile-regression finding that the association is strongest in the tails---while trimming extreme Airbnb counts slightly increases it (0.0042). In every case the coefficient remains positive and significant at the 1\% level, providing confidence that the baseline results are not driven by a small number of influential observations.
78 78
79 \subsection{Inference Caveats}
79 +\subsection{Additional Specification Checks}
80 80
81 Our baseline reports HC1 (heteroskedasticity-robust) standard errors, which are consistent under arbitrary forms of heteroskedasticity but assume independence across observations. In a cross-sectional setting with spatially concentrated observations, standard errors clustered at the city level---or corrected for spatial correlation more generally---would likely be larger than the HC1 estimates we report. The very high $t$-statistics on the Airbnb coefficient in the baseline model suggest that the finding would survive a substantial widening of the confidence intervals, but readers should bear this caveat in mind when interpreting the reported significance levels.
81 +Table~\ref{tab:extended_robustness} subjects the preferred specification to four further checks. Column~(R1) reproduces the baseline for reference.
82 +
83 +\begin{table}[htbp]
84 + \centering
85 + \caption{Extended Robustness --- Preferred Hedonic Specification}
86 + \label{tab:extended_robustness}
87 + \small
88 + \input{../results/tables/extended_robustness.tex}
89 +\end{table}
90 +
91 +\paragraph{Clustered standard errors.} Column~(R2) re-computes the baseline standard errors clustering by city (153 clusters). HC1 standard errors assume independence across observations, which is questionable when listings are spatially concentrated. In our data, city-level clustering in fact \textit{tightens} the standard error on the Airbnb coefficient (0.0001 versus 0.0002 under HC1), so the baseline significance levels are, if anything, conservative on this dimension. Clustering does widen the confidence interval on some controls (bathrooms), as expected.
92 +
93 +\paragraph{Interior size.} Column~(R3) adds the interior floor area (per 100 sq ft) as a control on the subsample of 3{,}531 listings that report it. Size enters positively and significantly, and the Airbnb coefficient remains positive and highly significant at 0.0032---about a fifth smaller than the baseline, consistent with size absorbing some quality variation, but leaving the qualitative conclusion intact.
94 +
95 +\paragraph{Functional form.} Column~(R4) replaces the linear count with $\ln(1 + \texttt{airbnb\_count\_500m})$. The elasticity-style coefficient (0.048) is positive and highly significant: doubling the nearby Airbnb count is associated with roughly 3.4\% higher rent. The fit improves marginally over the linear form, indicating mild concavity in the exposure--rent relationship.
96 +
97 +\paragraph{Ring exposure.} Column~(R5) includes the 500\,m count jointly with the count in the 500\,m--1\,km annulus. Both enter positively and significantly. Splitting the exposure this way attenuates the inner-buffer coefficient (0.0013), and the annulus coefficient (0.0018) is of comparable magnitude per listing: the association is therefore not confined to the immediate 500\,m but extends to the kilometre scale, consistent with the buffer-radius results above, where the total (not per-listing) association accumulates over wider areas. Because the two counts are strongly correlated, the individual coefficients in this specification should be read as a decomposition of a common neighbourhood signal rather than as separate causal gradients.
98 +
99 +\subsection{Leave-One-City-Out Sensitivity}
100 +
101 +Figure~\ref{fig:leave_one_out} re-estimates the preferred specification excluding, in turn, each of the eight cities with the most rental listings.
102 +
103 +\begin{figure}[htbp]
104 + \centering
105 + \includegraphics[width=0.80\textwidth]{leave_one_city_out.pdf}
106 + \caption{Leave-One-City-Out Sensitivity of the Airbnb Coefficient}
107 + \label{fig:leave_one_out}
108 +\end{figure}
109 +
110 +The coefficient is remarkably stable, ranging from 0.0038 to 0.0040 across exclusions. Notably, excluding Montreal---which accounts for more than half of the estimation sample---leaves the point estimate essentially unchanged (0.0038), albeit with a wider confidence interval; the association is thus not an artefact of any single market.
82 111
83 112 \subsection{Summary of Robustness}
84 113
@@ -91,4 +120,4 @@ Figure~\ref{fig:robustness_summary} presents a coefficient plot summarising the
91 120 \label{fig:robustness_summary}
92 121 \end{figure}
93 122
94 Across all specifications---varying the buffer radius, the exposure measure, the sample, and the outlier treatment---the estimated association between Airbnb presence and residential rents is consistently positive. While the magnitude varies across specifications, the qualitative conclusion is robust: higher Airbnb density in the immediate vicinity of a rental listing is associated with higher monthly rents, conditional on observable dwelling characteristics and city-level heterogeneity.
123 +Across all specifications---varying the buffer radius, the exposure measure, the functional form, the control set, the sample, the outlier treatment, and the standard-error computation---the estimated association between Airbnb presence and residential rents is consistently positive. While the magnitude varies across specifications, the qualitative conclusion is robust: higher Airbnb density in the immediate vicinity of a rental listing is associated with higher monthly rents, conditional on observable dwelling characteristics and city-level heterogeneity.
added results/tables/extended_robustness.tex +25 −0
@@ -0,0 +1,25 @@
1 +\begin{tabular}{lccccc}
2 +\toprule
3 + & \textbf{(R1)} & \textbf{(R2)} & \textbf{(R3)} & \textbf{(R4)} & \textbf{(R5)} \\
4 +Dep.\ var: & \textit{log\_rent} & \textit{log\_rent} & \textit{log\_rent} & \textit{log\_rent} & \textit{log\_rent} \\
5 +\midrule
6 +Airbnb count (500m) & 0.0039*** & 0.0039*** & 0.0032*** & & 0.0013*** \\
7 + & (0.0002) & (0.0001) & (0.0003) & & (0.0003) \\[4pt]
8 +log(1 + Airbnb count 500m) & & & & 0.0484*** & \\
9 + & & & & (0.0025) & \\[4pt]
10 +Airbnb count (500m--1km ring) & & & & & 0.0018*** \\
11 + & & & & & (0.0002) \\[4pt]
12 +Bedrooms & 0.1276*** & 0.1276*** & 0.1271*** & 0.1296*** & 0.1322*** \\
13 + & (0.0046) & (0.0043) & (0.0076) & (0.0046) & (0.0047) \\[4pt]
14 +Bathrooms & 0.2287*** & 0.2287*** & 0.2103*** & 0.2274*** & 0.2246*** \\
15 + & (0.0126) & (0.0291) & (0.0202) & (0.0125) & (0.0125) \\[4pt]
16 +Interior size (100 sq ft) & & & 0.0044*** & & \\
17 + & & & (0.0017) & & \\[4pt]
18 +\midrule
19 +Standard errors & HC1 & City cluster & HC1 & HC1 & HC1 \\
20 +Building type \& city FE & Yes & Yes & Yes & Yes & Yes \\
21 +Observations & 7,925 & 7,925 & 3,531 & 7,925 & 7,925 \\
22 +R$^2$ & 0.5697 & 0.5697 & 0.6044 & 0.5726 & 0.5786 \\
23 +\bottomrule
24 +\end{tabular}
25 +\parbox{\textwidth}{\footnotesize Standard errors in parentheses: HC1 except column (R2), which clusters by city. All columns control for bedrooms, bathrooms, building type, and city fixed effects; column (R3) adds interior size (per 100 sq ft) on the subsample reporting it. Column (R5) includes the 500m count and the count in the 500m--1km annulus jointly. $^{***}p<0.01$; $^{**}p<0.05$; $^{*}p<0.10$.}
added scripts/11_extended_robustness.py +221 −0
@@ -0,0 +1,221 @@
1 +# Author: Simon-Pierre Boucher — contact@spboucher.ai
2 +"""
3 +11_extended_robustness.py
4 +-------------------------
5 +Extended robustness checks for the preferred hedonic specification
6 +(log_rent ~ airbnb_count_500m + bedrooms + bathrooms + building type + city FE):
7 +
8 + (R1) Baseline with HC1 standard errors (reference column)
9 + (R2) Same coefficients with standard errors clustered by city
10 + (R3) Adding interior size (in 100 sq ft) on the subsample reporting it
11 + (R4) log(1 + count) functional form for the exposure variable
12 + (R5) Ring exposure: count in the 500m-1km annulus alongside the 500m count
13 +
14 +plus a leave-one-city-out sensitivity figure (dropping each of the largest
15 +cities in turn).
16 +
17 +Outputs:
18 + results/tables/extended_robustness.tex
19 + figures/leave_one_city_out.pdf
20 +"""
21 +
22 +import sys
23 +from pathlib import Path
24 +
25 +sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
26 +
27 +import matplotlib
28 +matplotlib.use("Agg")
29 +import matplotlib.pyplot as plt # noqa: E402
30 +import numpy as np # noqa: E402
31 +import pandas as pd # noqa: E402
32 +import statsmodels.api as sm # noqa: E402
33 +
34 +from src.config import MERGED_ANALYSIS, TABLE_DIR, FIG_DIR, require # noqa: E402
35 +from src.latex_tables import significance_star as _star # noqa: E402
36 +
37 +PROCESSED_HINT = "Run scripts/04_merge_data.py first."
38 +
39 +CONTROL_LABELS = {
40 + "airbnb_count_500m": r"Airbnb count (500m)",
41 + "log1p_airbnb_500m": r"log(1 + Airbnb count 500m)",
42 + "airbnb_ring_500m_1km": r"Airbnb count (500m--1km ring)",
43 + "bedrooms": "Bedrooms",
44 + "bathrooms": "Bathrooms",
45 + "size_100sqft": r"Interior size (100 sq ft)",
46 +}
47 +
48 +
49 +def prepare(df: pd.DataFrame) -> tuple[pd.DataFrame, list[str]]:
50 + """Add derived regressors and building-type dummies; return (frame, bt_cols)."""
51 + d = df.copy()
52 + d["log1p_airbnb_500m"] = np.log1p(d["airbnb_count_500m"])
53 + d["airbnb_ring_500m_1km"] = d["airbnb_count_1000m"] - d["airbnb_count_500m"]
54 + d["size_100sqft"] = d["size_interior_sqft"] / 100.0
55 +
56 + bt = pd.get_dummies(d["building_type"], prefix="bt", drop_first=True, dtype=float)
57 + d = pd.concat([d, bt], axis=1)
58 + return d, list(bt.columns)
59 +
60 +
61 +def fit(d: pd.DataFrame, x_main: list[str], bt_cols: list[str],
62 + label: str, cluster: bool = False, extra_controls: list[str] | None = None):
63 + """
64 + OLS of log_rent on x_main + bedrooms/bathrooms (+extras) + building type
65 + + city FE, with HC1 or city-clustered standard errors.
66 + """
67 + controls = ["bedrooms", "bathrooms"] + (extra_controls or [])
68 + needed = ["log_rent", "city"] + x_main + controls
69 + sub = d[needed + bt_cols].dropna().reset_index(drop=True)
70 +
71 + city_fe = pd.get_dummies(sub["city"], prefix="city", drop_first=True, dtype=float)
72 + X = sm.add_constant(pd.concat(
73 + [sub[x_main + controls + bt_cols].astype(float), city_fe], axis=1
74 + ).astype(float))
75 + y = sub["log_rent"].astype(float)
76 +
77 + if cluster:
78 + res = sm.OLS(y, X).fit(cov_type="cluster",
79 + cov_kwds={"groups": sub["city"]})
80 + else:
81 + res = sm.OLS(y, X).fit(cov_type="HC1")
82 +
83 + print(f" [{label}] N={int(res.nobs):,} R2={res.rsquared:.4f} "
84 + f"beta({x_main[0]})={res.params[x_main[0]]:.6f} "
85 + f"SE={res.bse[x_main[0]]:.6f}")
86 + return res
87 +
88 +
89 +def build_table(results: list, col_headers: list[str], se_row: list[str],
90 + display_vars: list[str]) -> None:
91 + """Write the extended-robustness table fragment."""
92 + n = len(results)
93 + lines: list[str] = []
94 + lines.append(r"\begin{tabular}{l" + "c" * n + "}")
95 + lines.append(r"\toprule")
96 + lines.append(" & ".join([""] + [f"\\textbf{{{h}}}" for h in col_headers]) + r" \\")
97 + lines.append(
98 + " & ".join(["Dep.\\ var:"] + [r"\textit{log\_rent}"] * n) + r" \\"
99 + )
100 + lines.append(r"\midrule")
101 +
102 + for var in display_vars:
103 + cells_c, cells_s = [], []
104 + for res in results:
105 + if var in res.params.index:
106 + b, se, p = res.params[var], res.bse[var], res.pvalues[var]
107 + cells_c.append(f"{b:.4f}{_star(p)}")
108 + cells_s.append(f"({se:.4f})")
109 + else:
110 + cells_c.append("")
111 + cells_s.append("")
112 + label = CONTROL_LABELS.get(var, var.replace("_", r"\_"))
113 + lines.append(f"{label} & " + " & ".join(cells_c) + r" \\")
114 + lines.append(" & " + " & ".join(cells_s) + r" \\[4pt]")
115 +
116 + lines.append(r"\midrule")
117 + lines.append("Standard errors & " + " & ".join(se_row) + r" \\")
118 + lines.append("Building type \\& city FE & " + " & ".join(["Yes"] * n) + r" \\")
119 + lines.append("Observations & "
120 + + " & ".join(f"{int(r.nobs):,}" for r in results) + r" \\")
121 + lines.append("R$^2$ & "
122 + + " & ".join(f"{r.rsquared:.4f}" for r in results) + r" \\")
123 + lines.append(r"\bottomrule")
124 + lines.append(r"\end{tabular}")
125 + lines.append(
126 + r"\parbox{\textwidth}{\footnotesize Standard errors in parentheses: "
127 + r"HC1 except column (R2), which clusters by city. All columns control "
128 + r"for bedrooms, bathrooms, building type, and city fixed effects; "
129 + r"column (R3) adds interior size (per 100 sq ft) on the subsample "
130 + r"reporting it. Column (R5) includes the 500m count and the count in "
131 + r"the 500m--1km annulus jointly. "
132 + r"$^{***}p<0.01$; $^{**}p<0.05$; $^{*}p<0.10$.}"
133 + )
134 +
135 + out = TABLE_DIR / "extended_robustness.tex"
136 + out.write_text("\n".join(lines) + "\n", encoding="utf-8")
137 + print(f" -> saved {out}")
138 +
139 +
140 +def leave_one_city_out(d: pd.DataFrame, bt_cols: list[str], n_cities: int = 8) -> None:
141 + """Coefficient on the 500m count when each large city is excluded in turn."""
142 + print("\n--- Leave-one-city-out ---")
143 + top = d["city"].value_counts().head(n_cities).index.tolist()
144 +
145 + res_full = fit(d, ["airbnb_count_500m"], bt_cols, "Full sample")
146 + beta_full = res_full.params["airbnb_count_500m"]
147 + se_full = res_full.bse["airbnb_count_500m"]
148 +
149 + labels, betas, ses = [], [], []
150 + for city in top:
151 + res = fit(d[d["city"] != city], ["airbnb_count_500m"], bt_cols,
152 + f"excl. {city}")
153 + labels.append(f"excl. {city}")
154 + betas.append(res.params["airbnb_count_500m"])
155 + ses.append(res.bse["airbnb_count_500m"])
156 +
157 + betas = np.array(betas)
158 + ses = np.array(ses)
159 +
160 + fig, ax = plt.subplots(figsize=(7, 0.45 * len(labels) + 2))
161 + y_pos = np.arange(len(labels))
162 + ax.errorbar(betas, y_pos, xerr=1.96 * ses,
163 + fmt="o", color="#2166ac", ecolor="#92c5de", capsize=4,
164 + markersize=6, elinewidth=1.5)
165 + ax.axvline(beta_full, color="firebrick", linestyle="--", linewidth=1.2,
166 + label=f"Full sample ({beta_full:.4f})")
167 + ax.axvspan(beta_full - 1.96 * se_full, beta_full + 1.96 * se_full,
168 + color="firebrick", alpha=0.08)
169 + ax.axvline(0, color="grey", linestyle=":", linewidth=0.8)
170 + ax.set_yticks(y_pos)
171 + ax.set_yticklabels(labels)
172 + ax.set_xlabel(r"Coefficient on Airbnb count within 500m ($\beta$)")
173 + ax.set_title("Leave-One-City-Out Sensitivity")
174 + ax.legend(fontsize=8, loc="best")
175 + ax.invert_yaxis()
176 + fig.tight_layout()
177 +
178 + out = FIG_DIR / "leave_one_city_out.pdf"
179 + fig.savefig(out, dpi=300)
180 + plt.close(fig)
181 + print(f" -> saved {out}")
182 +
183 +
184 +def main() -> None:
185 + print("=" * 72)
186 + print("11 EXTENDED ROBUSTNESS")
187 + print("=" * 72)
188 +
189 + df = pd.read_parquet(require(MERGED_ANALYSIS, PROCESSED_HINT))
190 + d, bt_cols = prepare(df)
191 + print(f"\nSample: {len(d):,} rows | "
192 + f"interior size non-missing: {d['size_100sqft'].notna().sum():,}")
193 +
194 + print("\n--- Specifications R1-R5 ---")
195 + res_r1 = fit(d, ["airbnb_count_500m"], bt_cols, "R1 baseline HC1")
196 + res_r2 = fit(d, ["airbnb_count_500m"], bt_cols, "R2 clustered", cluster=True)
197 + res_r3 = fit(d, ["airbnb_count_500m"], bt_cols, "R3 + size",
198 + extra_controls=["size_100sqft"])
199 + res_r4 = fit(d, ["log1p_airbnb_500m"], bt_cols, "R4 log(1+count)")
200 + res_r5 = fit(d, ["airbnb_count_500m", "airbnb_ring_500m_1km"], bt_cols,
201 + "R5 ring")
202 +
203 + build_table(
204 + [res_r1, res_r2, res_r3, res_r4, res_r5],
205 + ["(R1)", "(R2)", "(R3)", "(R4)", "(R5)"],
206 + ["HC1", "City cluster", "HC1", "HC1", "HC1"],
207 + display_vars=[
208 + "airbnb_count_500m", "log1p_airbnb_500m", "airbnb_ring_500m_1km",
209 + "bedrooms", "bathrooms", "size_100sqft",
210 + ],
211 + )
212 +
213 + leave_one_city_out(d, bt_cols)
214 +
215 + print("\n" + "=" * 72)
216 + print("11 DONE")
217 + print("=" * 72)
218 +
219 +
220 +if __name__ == "__main__":
221 + main()
222