import%20marimo%0A%0A__generated_with%20%3D%20%220.23.13%22%0Aapp%20%3D%20marimo.App()%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Data%20Science%0A%0A%20%20%20%20This%20lesson%20applies%20two%20common%20data%20science%20techniques%20to%20urban%20data%3A%20dimensionality%20reduction%20with%20Principal%20Component%20Analysis%20(PCA)%20and%20prediction%20with%20a%20Random%20Forest%20model%2C%20both%20using%20%60scikit-learn%60.%20We%20first%20use%20%60cityseer%60%20to%20generate%20street%20network%20centrality%20and%20land%20use%20accessibility%20metrics%2C%20which%20then%20serve%20as%20the%20inputs%20for%20the%20analysis.%0A%0A%20%20%20%20Network%20data%20%C2%A9%20%5BOpenStreetMap%5D(https%3A%2F%2Fwww.openstreetmap.org%2Fcopyright)%20contributors%2C%20available%20under%20the%20Open%20Database%20Licence.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20osmnx%20as%20ox%0A%20%20%20%20from%20cityseer.network%20import%20CityNetwork%0A%20%20%20%20from%20cityseer.tools%20import%20io%0A%0A%20%20%20%20return%20CityNetwork%2C%20io%2C%20ox%2C%20plt%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%3E%20**Note%3A**%0A%20%20%20%20%3E%20The%20code%20below%20uses%20the%20high-level%20%5B%60CityNetwork%60%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fapi%2Fnetwork)%20class%2C%20which%20wraps%20network%20preparation%20and%20metric%20computation%20behind%20a%20lean%20interface.%20For%20a%20full%20explanation%20of%20the%20%60cityseer%60%20workflow%2C%20see%20the%20%5Bquickstart%20recipe%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fexamples%2Frecipes%2Fquickstart).%0A%0A%20%20%20%20First%2C%20we%20define%20the%20geographical%20area%20of%20interest%20(Nicosia%2C%20Cyprus)%20using%20longitude%20and%20latitude%20coordinates%2C%20and%20a%20buffer%20radius.%20Then%2C%20we%20use%20%60cityseer%60%20to%3A%0A%0A%20%20%20%201.%20Create%20a%20buffered%20polygon%20area%20around%20the%20center%20point.%0A%20%20%20%202.%20Download%20and%20simplify%20the%20street%20network%20within%20this%20polygon%20from%20OpenStreetMap%20with%20%60CityNetwork.from_osm%60%2C%20which%20also%20builds%20the%20dual%20representation%20so%20that%20calculations%20are%20expressed%20per%20street%20segment%20(the%20%5Bquickstart%20recipe%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fexamples%2Frecipes%2Fquickstart)%20explains%20primal%20vs.%20dual%20graphs).%0A%20%20%20%203.%20Calculate%20centrality%20measures%20(density%2C%20harmonic%20closeness%2C%20betweenness)%20at%20multiple%20distance%20thresholds%20using%20angular%20analysis%3B%20%60density%60%20is%20requested%20through%20the%20%60closeness%60%20expression%20dictionary%20since%20the%20default%20computes%20only%20harmonic%20closeness%20and%20betweenness.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(CityNetwork%2C%20io)%3A%0A%20%20%20%20lng%2C%20lat%20%3D%2033.36402%2C%2035.17526%0A%20%20%20%20buffer%20%3D%202000%0A%0A%20%20%20%20poly_wgs%2C%20_epsg_code%20%3D%20io.buffered_point_poly(lng%2C%20lat%2C%20buffer)%0A%20%20%20%20cn%20%3D%20CityNetwork.from_osm(poly_wgs%2C%20to_crs_code%3D3035)%0A%20%20%20%20cn.centrality_simplest(%0A%20%20%20%20%20%20%20%20distances%3D%5B500%2C%201000%2C%202000%2C%205000%5D%2C%0A%20%20%20%20%20%20%20%20closeness%3D%7B%22density%22%3A%20%221%22%2C%20%22harmonic%22%3A%20%221%20%2F%20(1%20%2B%20c%20%2F%2090)%22%7D%2C%0A%20%20%20%20)%0A%20%20%20%20cn.nodes_gdf.head()%0A%20%20%20%20return%20cn%2C%20poly_wgs%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Next%2C%20we%20use%20%60osmnx%60%20to%20acquire%20data%20about%20restaurants%20within%20the%20previously%20defined%20polygonal%20area%20(%60poly_wgs%60).%0A%0A%20%20%20%201.%20%60ox.features_from_polygon%60%20queries%20OpenStreetMap%20for%20features%20tagged%20with%20%60%22amenity%22%3A%20%22restaurant%22%60.%0A%20%20%20%202.%20%60.to_crs(epsg%3D3035)%60%20reprojects%20the%20downloaded%20restaurant%20data%20to%20the%20ETRS89%20%2F%20LAEA%20Europe%20projected%20coordinate%20system%20(EPSG%3A3035)%20to%20ensure%20consistency%20with%20the%20street%20network%20data%20and%20enable%20accurate%20spatial%20calculations.%0A%20%20%20%203.%20%60gdf_rest%5B%5B%22amenity%22%2C%20%22geometry%22%5D%5D%60%20filters%20out%20the%20%60amenity%60%20and%20%60geometry%60%20columns.%0A%20%20%20%204.%20%60.reset_index(drop%3DTrue)%60%20resets%20the%20DataFrame%20index%20for%20cleaner%20data%20handling.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(ox%2C%20poly_wgs)%3A%0A%20%20%20%20gdf_rest%20%3D%20ox.features_from_polygon(poly_wgs%2C%20tags%3D%7B%22amenity%22%3A%20%22restaurant%22%7D)%0A%20%20%20%20gdf_rest%20%3D%20gdf_rest.to_crs(epsg%3D3035)%0A%20%20%20%20gdf_rest%20%3D%20gdf_rest%5B%5B%22amenity%22%2C%20%22geometry%22%5D%5D%0A%20%20%20%20gdf_rest%20%3D%20gdf_rest.reset_index(drop%3DTrue)%0A%20%20%20%20gdf_rest.head()%0A%20%20%20%20return%20(gdf_rest%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20We%20can%20then%20use%20%60cityseer%60%20once%20again%20to%20calculate%20accessibility%20to%20restaurants%20from%20each%20node%20in%20the%20street%20network.%0A%0A%20%20%20%201.%20%60cn.compute_accessibilities%60%20takes%20the%20restaurant%20locations%20(%60gdf_rest%60)%20as%20input.%0A%20%20%20%202.%20%60landuse_column_label%3D%22amenity%22%60%20specifies%20that%20the%20'amenity'%20column%20in%20%60gdf_rest%60%20identifies%20the%20type%20of%20feature%20(restaurants%20in%20this%20case).%0A%20%20%20%203.%20%60accessibility_keys%3D%5B%22restaurant%22%5D%60%20tells%20the%20method%20to%20calculate%20accessibility%20specifically%20for%20features%20labelled%20as%20'restaurant'.%0A%20%20%20%204.%20%60distances%3D%5B200%2C%20400%2C%20800%5D%60%20specifies%20the%20distance%20thresholds%20(in%20meters)%20at%20which%20accessibility%20should%20be%20measured.%20For%20each%20node%2C%20this%20will%20count%20how%20many%20restaurants%20are%20reachable%20within%20200m%2C%20400m%2C%20and%20800m%20along%20the%20network.%0A%0A%20%20%20%20The%20results%20are%20written%20to%20the%20network's%20nodes%20GeoDataFrame%20as%20new%20columns%20for%20these%20accessibility%20scores%20(e.g.%20%60cc_restaurant_200%60%20for%20the%20count%20of%20restaurants%20within%20200m)%2C%20and%20the%20method%20also%20returns%20the%20land%20use%20GeoDataFrame%20with%20assignment%20information.%20The%20snapshot%20taken%20with%20%60cn.to_geopandas()%60%20(here%20assigned%20to%20%60nodes_gdf_acc%60)%20joins%20all%20computed%20columns%20to%20the%20street%20geometries.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(cn%2C%20gdf_rest)%3A%0A%20%20%20%20_cn%2C%20_gdf_rest%20%3D%20cn.compute_accessibilities(%0A%20%20%20%20%20%20%20%20gdf_rest%2C%0A%20%20%20%20%20%20%20%20landuse_column_label%3D%22amenity%22%2C%0A%20%20%20%20%20%20%20%20accessibility_keys%3D%5B%22restaurant%22%5D%2C%0A%20%20%20%20%20%20%20%20distances%3D%5B200%2C%20400%2C%20800%5D%2C%0A%20%20%20%20)%0A%20%20%20%20nodes_gdf_acc%20%3D%20cn.to_geopandas()%0A%20%20%20%20return%20(nodes_gdf_acc%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20We%20now%20arrive%20at%20the%20Data%20Science%20section%2C%20which%20performs%20Principal%20Component%20Analysis%20(PCA)%2C%20a%20dimensionality%20reduction%20technique%2C%20on%20the%20street%20network%20centrality%20measures.%20The%20goal%20is%20to%20identify%20underlying%20patterns%20or%20latent%20dimensions%20within%20these%20(often%20correlated)%20centrality%20metrics.%0A%0A%20%20%20%201.%20%60StandardScaler()%60%20is%20initialised%20to%20standardise%20the%20data.%20PCA%20is%20sensitive%20to%20the%20scale%20of%20variables%2C%20so%20standardisation%20(transforming%20data%20to%20have%20zero%20mean%20and%20unit%20variance)%20is%20a%20necessary%20preprocessing%20step.%0A%20%20%20%202.%20%60X_scaled%20%3D%20scaler.fit_transform(...)%60%20selects%20the%20twelve%20centrality%20columns%20(density%2C%20harmonic%2C%20and%20betweenness%20at%20four%20different%20distances)%20from%20%60nodes_gdf_acc%60%20and%20applies%20the%20standardisation.%0A%20%20%20%203.%20%60pca%20%3D%20PCA(n_components%3D4)%60%20initialises%20PCA%20to%20extract%20the%20top%204%20principal%20components.%20These%20components%20are%20new%2C%20uncorrelated%20variables%20that%20capture%20the%20maximum%20possible%20variance%20from%20the%20original%20data.%0A%20%20%20%204.%20%60X_pca%20%3D%20pca.fit_transform(X_scaled)%60%20applies%20PCA%20to%20the%20scaled%20centrality%20data.%0A%20%20%20%205.%20The%20next%20four%20lines%20add%20these%20four%20principal%20components%20as%20new%20columns%20(%60pca_1%60%2C%20%60pca_2%60%2C%20%60pca_3%60%2C%20%60pca_4%60)%20to%20the%20%60nodes_gdf_acc%60%20GeoDataFrame.%0A%20%20%20%206.%20The%20subsequent%20code%20sets%20up%20a%203x1%20plot%20to%20visualise%20the%20spatial%20distribution%20of%20the%20first%20three%20principal%20components.%20Each%20subplot%20shows%20the%20nodes%20coloured%20by%20their%20score%20on%20a%20principal%20component.%20The%20title%20of%20each%20subplot%20indicates%20the%20percentage%20of%20the%20original%20data's%20variance%20that%20is%20explained%20by%20that%20component.%20This%20helps%20in%20understanding%20how%20much%20information%20is%20retained%20by%20each%20component.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(nodes_gdf_acc%2C%20plt)%3A%0A%20%20%20%20from%20sklearn.decomposition%20import%20PCA%0A%20%20%20%20from%20sklearn.preprocessing%20import%20StandardScaler%0A%0A%20%20%20%20scaler%20%3D%20StandardScaler()%0A%20%20%20%20X_scaled%20%3D%20scaler.fit_transform(%0A%20%20%20%20%20%20%20%20nodes_gdf_acc%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22cc_density_500_ang%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22cc_density_1000_ang%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22cc_density_2000_ang%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22cc_density_5000_ang%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22cc_harmonic_500_ang%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22cc_harmonic_1000_ang%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22cc_harmonic_2000_ang%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22cc_harmonic_5000_ang%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22cc_betweenness_500_ang%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22cc_betweenness_1000_ang%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22cc_betweenness_2000_ang%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22cc_betweenness_5000_ang%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20)%0A%20%20%20%20pca%20%3D%20PCA(n_components%3D4)%0A%20%20%20%20X_pca%20%3D%20pca.fit_transform(X_scaled)%0A%20%20%20%20nodes_gdf_acc%5B%22pca_1%22%5D%20%3D%20X_pca%5B%3A%2C%200%5D%0A%20%20%20%20nodes_gdf_acc%5B%22pca_2%22%5D%20%3D%20X_pca%5B%3A%2C%201%5D%0A%20%20%20%20nodes_gdf_acc%5B%22pca_3%22%5D%20%3D%20X_pca%5B%3A%2C%202%5D%0A%20%20%20%20nodes_gdf_acc%5B%22pca_4%22%5D%20%3D%20X_pca%5B%3A%2C%203%5D%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(3%2C%201%2C%20figsize%3D(8%2C%2024)%2C%20dpi%3D150)%0A%20%20%20%20nodes_gdf_acc.plot(%0A%20%20%20%20%20%20%20%20column%3D%22pca_1%22%2C%20cmap%3D%22Reds%22%2C%20legend%3DTrue%2C%20legend_kwds%3D%7B%22label%22%3A%20%22Component%201%20score%22%2C%20%22shrink%22%3A%200.6%7D%2C%20ax%3D_ax%5B0%5D%0A%20%20%20%20)%0A%20%20%20%20_ax%5B0%5D.set_xlim(6433800%2C%206433800%20%2B%202700)%0A%20%20%20%20_ax%5B0%5D.set_ylim(1669400%2C%201669400%20%2B%202700)%0A%20%20%20%20_ax%5B0%5D.set_axis_off()%0A%20%20%20%20_ax%5B0%5D.set_title(f%22Principal%20component%201%20-%20explained%20variance%3A%20%7Bpca.explained_variance_ratio_%5B0%5D%3A.0%25%7D%22)%0A%20%20%20%20nodes_gdf_acc.plot(%0A%20%20%20%20%20%20%20%20column%3D%22pca_2%22%2C%20cmap%3D%22Reds%22%2C%20legend%3DTrue%2C%20legend_kwds%3D%7B%22label%22%3A%20%22Component%202%20score%22%2C%20%22shrink%22%3A%200.6%7D%2C%20ax%3D_ax%5B1%5D%0A%20%20%20%20)%0A%20%20%20%20_ax%5B1%5D.set_xlim(6433800%2C%206433800%20%2B%202700)%0A%20%20%20%20_ax%5B1%5D.set_ylim(1669400%2C%201669400%20%2B%202700)%0A%20%20%20%20_ax%5B1%5D.set_axis_off()%0A%20%20%20%20_ax%5B1%5D.set_title(f%22Principal%20component%202%20-%20explained%20variance%3A%20%7Bpca.explained_variance_ratio_%5B1%5D%3A.0%25%7D%22)%0A%20%20%20%20nodes_gdf_acc.plot(%0A%20%20%20%20%20%20%20%20column%3D%22pca_3%22%2C%20cmap%3D%22Reds%22%2C%20legend%3DTrue%2C%20legend_kwds%3D%7B%22label%22%3A%20%22Component%203%20score%22%2C%20%22shrink%22%3A%200.6%7D%2C%20ax%3D_ax%5B2%5D%0A%20%20%20%20)%0A%20%20%20%20_ax%5B2%5D.set_xlim(6433800%2C%206433800%20%2B%202700)%0A%20%20%20%20_ax%5B2%5D.set_ylim(1669400%2C%201669400%20%2B%202700)%0A%20%20%20%20_ax%5B2%5D.set_axis_off()%0A%20%20%20%20_ax%5B2%5D.set_title(f%22Principal%20component%203%20-%20explained%20variance%3A%20%7Bpca.explained_variance_ratio_%5B2%5D%3A.0%25%7D%22)%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20_fig%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20next%20cell%20uses%20%60seaborn%60%2C%20a%20statistical%20data%20visualisation%20library%2C%20to%20create%20a%20histogram%20and%20a%20joint%20plot.%0A%0A%20%20%20%20Histograms%20are%20useful%20for%20visualising%20the%20distribution%20of%20a%20single%20variable%2C%20while%20joint%20plots%20allow%20for%20the%20visualisation%20of%20the%20relationship%20between%20two%20variables%20along%20with%20their%20individual%20distributions.%0A%0A%20%20%20%20The%20histogram%20shows%20the%20distribution%20of%20restaurant%20accessibility%20within%20800%20meters%20(%60cc_restaurant_800%60).%20The%20x-axis%20represents%20the%20accessibility%20values%2C%20while%20the%20y-axis%20shows%20the%20frequency%20of%20these%20values.%20The%20%60bins%3D50%60%20argument%20specifies%20that%20the%20data%20should%20be%20divided%20into%2050%20bins%20for%20the%20histogram.%0A%0A%20%20%20%20The%20joint%20plot%20visualises%20the%20relationship%20between%20the%20first%20principal%20component%20(%60pca_1%60)%20and%20restaurant%20accessibility%20within%20800%20meters%20(%60cc_restaurant_800%60).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(nodes_gdf_acc)%3A%0A%20%20%20%20import%20seaborn%20as%20sns%0A%0A%20%20%20%20_hist_ax%20%3D%20sns.histplot(data%3Dnodes_gdf_acc%2C%20x%3D%22cc_restaurant_800%22%2C%20bins%3D50)%0A%20%20%20%20_hist_ax.set_xlabel(%22Restaurant%20accessibility%2C%20800%20m%22)%0A%20%20%20%20_joint_grid%20%3D%20sns.jointplot(data%3Dnodes_gdf_acc%2C%20x%3D%22pca_1%22%2C%20y%3D%22cc_restaurant_800%22%2C%20kind%3D%22kde%22)%0A%20%20%20%20_joint_grid.set_axis_labels(%22Principal%20component%201%20score%22%2C%20%22Restaurant%20accessibility%2C%20800%20m%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20This%20cell%20demonstrates%20use%20of%20a%20Random%20Forest%20Regressor%20model%20to%20predict%20restaurant%20accessibility%20(%60cc_restaurant_800%60)%20based%20on%20the%20four%20principal%20components%20(%60pca_1%60%20to%20%60pca_4%60)%20derived%20from%20the%20network%20centrality%20measures.%20This%20is%20an%20example%20of%20supervised%20learning.%0A%0A%20%20%20%201.%20%60X%60%20is%20defined%20as%20the%20DataFrame%20containing%20the%20predictor%20variables%20(the%20four%20PCA%20components).%0A%20%20%20%202.%20%60y%60%20is%20defined%20as%20the%20target%20variable%20(restaurant%20accessibility%20at%20800m).%0A%20%20%20%203.%20%60train_test_split(X%2C%20y%2C%20...)%60%20splits%20the%20data%20into%20training%20(80%25)%20and%20testing%20(20%25)%20sets.%20The%20model%20will%20be%20trained%20on%20the%20training%20set%20and%20evaluated%20on%20the%20unseen%20testing%20set.%20%60random_state%3D42%60%20ensures%20reproducibility%20of%20the%20split.%0A%20%20%20%204.%20%60regressor%20%3D%20RandomForestRegressor(...)%60%20initialises%20a%20Random%20Forest%20Regressor%20model.%20%60n_estimators%3D100%60%20means%20it%20will%20use%20100%20decision%20trees.%20%60criterion%3D%22squared_error%22%60%20specifies%20the%20function%20to%20measure%20the%20quality%20of%20a%20split.%0A%20%20%20%205.%20%60regressor.fit(X_train%2C%20y_train)%60%20trains%20the%20model%20using%20the%20training%20data.%0A%20%20%20%206.%20%60y_pred%20%3D%20regressor.predict(X_test)%60%20makes%20predictions%20on%20the%20test%20set.%0A%20%20%20%207.%20%60r2%20%3D%20r2_score(y_test%2C%20y_pred)%60%20calculates%20the%20R-squared%20score%2C%20a%20measure%20of%20how%20well%20the%20model's%20predictions%20fit%20the%20actual%20values%20in%20the%20test%20set.%20An%20R2%20score%20closer%20to%201%20indicates%20a%20better%20fit.%20Increasing%20the%20size%20of%20the%20training%20set%20generally%20improves%20the%20model's%20performance%2C%20as%20it%20has%20more%20data%20to%20learn%20from.%0A%20%20%20%208.%20The%20model%20then%20predicts%20accessibility%20for%20all%20nodes%20using%20%60regressor.predict(X)%60%20and%20stores%20these%20predictions%20in%20a%20new%20column%20%60cc_restaurant_800_pred%60.%0A%20%20%20%209.%20%60nodes_gdf_acc%5B%22cc_restaurant_800_residuals%22%5D%60%20calculates%20the%20residuals%20(the%20difference%20between%20predicted%20and%20actual%20accessibility%20values).%0A%20%20%20%2010.%20The%20final%20part%20of%20the%20cell%20sets%20up%20a%203x1%20plot%20to%20visualise%3A%0A%20%20%20%20%20%20%20%20-%20The%20actual%20restaurant%20accessibility.%0A%20%20%20%20%20%20%20%20-%20The%20predicted%20restaurant%20accessibility%20(with%20the%20R2%20score%20in%20the%20title).%0A%20%20%20%20%20%20%20%20-%20The%20residuals%20of%20the%20regression%2C%20showing%20where%20the%20model%20over%20or%20under-predicts%20accessibility.%20A%20good%20model%20would%20have%20residuals%20randomly%20scattered%20around%20zero.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(nodes_gdf_acc%2C%20plt)%3A%0A%20%20%20%20from%20sklearn.ensemble%20import%20RandomForestRegressor%0A%20%20%20%20from%20sklearn.metrics%20import%20r2_score%0A%20%20%20%20from%20sklearn.model_selection%20import%20train_test_split%0A%0A%20%20%20%20X%20%3D%20nodes_gdf_acc%5B%5B%22pca_1%22%2C%20%22pca_2%22%2C%20%22pca_3%22%2C%20%22pca_4%22%5D%5D%0A%20%20%20%20y%20%3D%20nodes_gdf_acc%5B%22cc_restaurant_800%22%5D%0A%20%20%20%20X_train%2C%20X_test%2C%20y_train%2C%20y_test%20%3D%20train_test_split(X%2C%20y%2C%20test_size%3D0.2%2C%20random_state%3D42)%0A%20%20%20%20regressor%20%3D%20RandomForestRegressor(n_estimators%3D100%2C%20random_state%3D42%2C%20criterion%3D%22squared_error%22)%0A%20%20%20%20regressor.fit(X_train%2C%20y_train)%0A%20%20%20%20y_pred%20%3D%20regressor.predict(X_test)%0A%20%20%20%20r2%20%3D%20r2_score(y_test%2C%20y_pred)%0A%20%20%20%20print(%22R2%20score%3A%20%22%2C%20r2)%0A%20%20%20%20nodes_gdf_acc%5B%22cc_restaurant_800_pred%22%5D%20%3D%20regressor.predict(X)%0A%20%20%20%20nodes_gdf_acc%5B%22cc_restaurant_800_residuals%22%5D%20%3D%20(%0A%20%20%20%20%20%20%20%20nodes_gdf_acc%5B%22cc_restaurant_800_pred%22%5D%20-%20nodes_gdf_acc%5B%22cc_restaurant_800%22%5D%0A%20%20%20%20)%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(3%2C%201%2C%20figsize%3D(8%2C%2024)%2C%20dpi%3D150)%0A%20%20%20%20nodes_gdf_acc.plot(%0A%20%20%20%20%20%20%20%20column%3D%22cc_restaurant_800%22%2C%0A%20%20%20%20%20%20%20%20cmap%3D%22magma%22%2C%0A%20%20%20%20%20%20%20%20legend%3DTrue%2C%0A%20%20%20%20%20%20%20%20legend_kwds%3D%7B%22label%22%3A%20%22Restaurants%20within%20800%20m%22%2C%20%22shrink%22%3A%200.6%7D%2C%0A%20%20%20%20%20%20%20%20ax%3D_ax%5B0%5D%2C%0A%20%20%20%20)%0A%20%20%20%20_ax%5B0%5D.set_xlim(6433800%2C%206433800%20%2B%202700)%0A%20%20%20%20_ax%5B0%5D.set_ylim(1669400%2C%201669400%20%2B%202700)%0A%20%20%20%20_ax%5B0%5D.set_axis_off()%0A%20%20%20%20_ax%5B0%5D.set_title(%22Restaurant%20accessibility%2C%20800%20m%22)%0A%20%20%20%20nodes_gdf_acc.plot(%0A%20%20%20%20%20%20%20%20column%3D%22cc_restaurant_800_pred%22%2C%0A%20%20%20%20%20%20%20%20cmap%3D%22magma%22%2C%0A%20%20%20%20%20%20%20%20legend%3DTrue%2C%0A%20%20%20%20%20%20%20%20legend_kwds%3D%7B%22label%22%3A%20%22Predicted%20restaurants%20within%20800%20m%22%2C%20%22shrink%22%3A%200.6%7D%2C%0A%20%20%20%20%20%20%20%20ax%3D_ax%5B1%5D%2C%0A%20%20%20%20)%0A%20%20%20%20_ax%5B1%5D.set_xlim(6433800%2C%206433800%20%2B%202700)%0A%20%20%20%20_ax%5B1%5D.set_ylim(1669400%2C%201669400%20%2B%202700)%0A%20%20%20%20_ax%5B1%5D.set_axis_off()%0A%20%20%20%20_ax%5B1%5D.set_title(f%22Predicted%20restaurant%20accessibility%2C%20800%20m%20-%20R2%20score%3A%20%7Br2%3A.2f%7D%22)%0A%20%20%20%20%23%20coolwarm%20suits%20the%20residuals%20because%20they%20diverge%20around%20zero%0A%20%20%20%20nodes_gdf_acc.plot(%0A%20%20%20%20%20%20%20%20column%3D%22cc_restaurant_800_residuals%22%2C%0A%20%20%20%20%20%20%20%20cmap%3D%22coolwarm%22%2C%0A%20%20%20%20%20%20%20%20vmax%3D4%2C%0A%20%20%20%20%20%20%20%20vmin%3D-4%2C%0A%20%20%20%20%20%20%20%20legend%3DTrue%2C%0A%20%20%20%20%20%20%20%20legend_kwds%3D%7B%22label%22%3A%20%22Residual%20(predicted%20minus%20actual)%22%2C%20%22shrink%22%3A%200.6%7D%2C%0A%20%20%20%20%20%20%20%20ax%3D_ax%5B2%5D%2C%0A%20%20%20%20)%0A%20%20%20%20_ax%5B2%5D.set_xlim(6433800%2C%206433800%20%2B%202700)%0A%20%20%20%20_ax%5B2%5D.set_ylim(1669400%2C%201669400%20%2B%202700)%0A%20%20%20%20_ax%5B2%5D.set_axis_off()%0A%20%20%20%20_ax%5B2%5D.set_title(%22Residuals%20of%20the%20Random%20Forest%20regression%22)%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20_fig%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Exercises%0A%0A%20%20%20%20-%20Re-run%20the%20PCA%20with%20%60n_components%3D2%60%20instead%20of%204.%20How%20much%20total%20variance%20is%20explained%20by%20just%20two%20components%3F%20Plot%20the%20results.%0A%20%20%20%20-%20Experiment%20with%20the%20Random%20Forest%20parameters%3A%20try%20changing%20%60n_estimators%60%20to%2050%20or%20200%2C%20or%20adjust%20%60test_size%60%20to%200.3.%20How%20does%20the%20R%C2%B2%20score%20change%3F%0A%20%20%20%20-%20Replace%20the%20%60RandomForestRegressor%60%20with%20a%20different%20%60scikit-learn%60%20model%20(e.g.%2C%20%60GradientBoostingRegressor%60%20or%20%60LinearRegression%60).%20Compare%20the%20R%C2%B2%20scores%20%E2%80%94%20which%20model%20performs%20best%3F%0A%0A%20%20%20%20%23%23%20Summary%0A%0A%20%20%20%20-%20PCA%20reduces%20many%20correlated%20variables%20into%20a%20smaller%20set%20of%20uncorrelated%20principal%20components%2C%20each%20capturing%20a%20share%20of%20the%20original%20variance.%0A%20%20%20%20-%20Standardising%20data%20before%20PCA%20is%20essential%20because%20PCA%20is%20sensitive%20to%20variable%20scales.%0A%20%20%20%20-%20Supervised%20learning%20(e.g.%2C%20Random%20Forest)%20trains%20on%20labelled%20data%20to%20predict%20outcomes%20for%20new%20observations.%0A%20%20%20%20-%20R%C2%B2%20scores%20measure%20how%20well%20predictions%20match%20actual%20values%20(1.0%20%3D%20perfect%2C%200.0%20%3D%20no%20better%20than%20the%20mean).%0A%20%20%20%20-%20Residual%20maps%20reveal%20where%20a%20model%20over-%20or%20under-predicts%2C%20helping%20identify%20spatial%20patterns%20the%20model%20misses.%0A%0A%20%20%20%20These%20techniques%20combine%20well%20with%20%60cityseer%60%20network%20metrics%3B%20see%20the%20%5BCityseer%20Recipes%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fexamples)%20section%20for%20practical%20analysis%20workflows.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
00a74e37f5a3216e72f4aad59c36af82