import%20marimo%0A%0A__generated_with%20%3D%20%220.16.5%22%0Aapp%20%3D%20marimo.App(%0A%20%20%20%20width%3D%22full%22%2C%0A%20%20%20%20app_title%3D%22hdb-resale-prices%22%2C%0A%20%20%20%20auto_download%3D%5B%22html%22%5D%2C%0A%20%20%20%20sql_output%3D%22pandas%22%2C%0A)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(%0A%20%20%20%20clusters%2C%0A%20%20%20%20conclusion%2C%0A%20%20%20%20data%2C%0A%20%20%20%20elbow%2C%0A%20%20%20%20flats%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20summary1%2C%0A%20%20%20%20summary2%2C%0A%20%20%20%20summary3%2C%0A%20%20%20%20title%2C%0A%20%20%20%20trend_chart%2C%0A)%3A%0A%20%20%20%20mo.ui.tabs(%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Executive%20Summary%22%3A%20mo.vstack(%5Btitle%2C%20mo.hstack(%5Bsummary1%2C%20summary2%5D)%2C%20summary3%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Data%22%3A%20data%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Volume%22%3A%20flats%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Trends%22%3A%20trend_chart%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Clustering%22%3A%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mo.accordion(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Optimal%20Value%20of%20K%20(click%20to%20expand)%22%3A%20elbow%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D)%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20clusters%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Conclusion%22%3A%20conclusion%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(%0A%20%20%20%20df%2C%0A%20%20%20%20flat_obs%2C%0A%20%20%20%20flat_type_chart%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20reference%2C%0A%20%20%20%20total_flats_chart%2C%0A%20%20%20%20total_obs%2C%0A)%3A%0A%20%20%20%20data%20%3D%20mo.vstack(%5Bmo.callout(reference%2C%20kind%3D'info')%2C%20mo.md(%22%23%23%20Random%20Sample%20of%20Data%22)%2C%20mo.ui.table(df.sample(n%3D10%2C%20random_state%3D42)%0A%20%20%20%20)%5D)%0A%20%20%20%20flats%20%3D%20mo.hstack(%5Bmo.vstack(%5Bflat_obs%2C%20flat_type_chart%5D)%2C%20mo.vstack(%5Btotal_obs%2C%20total_flats_chart%5D)%5D)%0A%20%20%20%20return%20data%2C%20flats%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%0A%20%20%20%20title%20%3D%20mo.callout(mo.md(%22%23%20Clustering%20Analysis%20of%20HDB%20Resale%20Prices%20Across%20Singapore%20Towns%22))%0A%0A%20%20%20%20summary1%20%3D%20mo.callout(mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Project%20Overview%0A%0A%20%20%20%20This%20analysis%20examines%20Housing%20%26%20Development%20Board%20(HDB)%20resale%20flat%20prices%20across%2026%20residential%20towns%20in%20Singapore%20to%20identify%20affordability%20clusters%20and%20pricing%20patterns.%20Using%20publicly%20available%20data%20from%20Singapore's%20Open%20Data%20Portal%20spanning%20January%202017%20onwards%2C%20the%20study%20applies%20machine%20learning%20techniques%20to%20segment%20towns%20based%20on%20their%20housing%20price%20characteristics.%0A%0A%20%20%20%20%23%23%20Methodology%0A%0A%20%20%20%20The%20analysis%20employs%20a%20systematic%20approach%20to%20understand%20price%20dynamics%20and%20affordability%20segmentation%3A%0A%0A%20%20%20%20**Data%20Processing%20and%20Charting%20with%20SQL%2C%20Pandas%20and%20Altair**%0A%20%20%20%20-%20Examined%20distribution%20of%20flat%20types%20and%20total%20number%20of%20flats%20resold%20across%20all%20towns%0A%20%20%20%20-%20Monthly%20average%20resale%20prices%20were%20calculated%20for%20each%20town%20from%20the%20raw%20transaction%20data%0A%20%20%20%20-%20A%2012-month%20rolling%20average%20was%20computed%20(starting%20December%202017)%20to%20smooth%20short-term%20fluctuations%20and%20reveal%20underlying%20trends%0A%20%20%20%20-%20Two%20key%20statistical%20measures%20were%20derived%20for%20each%20town%3A%20mean%20resale%20price%20and%20standard%20deviation%20across%20the%20analysis%20period%0A%0A%20%20%20%20**Clustering%20Analysis%20with%20Scikit-learn**%0A%20%20%20%20-%20Feature%20standardization%20was%20performed%20using%20Scikit-learn's%20StandardScaler%20to%20normalize%20the%20mean%20and%20standard%20deviation%20values%2C%20ensuring%20equal%20weighting%20in%20the%20clustering%20algorithm%0A%20%20%20%20-%20The%20Elbow%20Method%20was%20applied%20to%20determine%20the%20optimal%20number%20of%20clusters%20(K)%20for%20K-means%20clustering%0A%20%20%20%20-%20K-means%20clustering%20algorithm%20was%20used%20to%20group%20the%2026%20towns%20based%20on%20their%20price%20characteristics%0A%20%20%20%20%22%22%22))%0A%0A%20%20%20%20summary2%20%3D%20mo.callout(mo.md(r%22%22%22%0A%0A%20%20%20%20%23%23%20Key%20Insights%0A%0A%20%20%20%20The%20clustering%20analysis%20reveals%20distinct%20affordability%20segments%20among%20Singapore's%20residential%20towns%2C%20with%20groupings%20determined%20by%20both%20average%20price%20levels%20and%20price%20volatility.%20These%20clusters%20provide%20insights%20into%3A%0A%0A%20%20%20%20-%20**Market%20segmentation**%3A%20Towns%20naturally%20group%20into%20affordability%20tiers%20based%20on%20historical%20pricing%20patterns%0A%20%20%20%20-%20**Price%20stability**%3A%20The%20standard%20deviation%20component%20captures%20which%20towns%20experience%20more%20volatile%20pricing%20versus%20those%20with%20stable%20markets%0A%20%20%20%20-%20**Geographic%20and%20demographic%20patterns**%3A%20The%20clustering%20reflects%20underlying%20factors%20such%20as%20location%2C%20maturity%20of%20estates%2C%20and%20amenity%20access%0A%0A%20%20%20%20%23%23%20Applications%0A%0A%20%20%20%20This%20analysis%20offers%20practical%20value%20for%20multiple%20stakeholders%3A%0A%0A%20%20%20%20-%20**Homebuyers**%3A%20Understanding%20which%20towns%20fall%20into%20similar%20affordability%20clusters%20aids%20in%20expanding%20housing%20search%20options%0A%20%20%20%20-%20**Policy%20makers**%3A%20Identifying%20pricing%20patterns%20across%20towns%20can%20inform%20housing%20policy%20and%20subsidy%20targeting%0A%20%20%20%20-%20**Market%20analysts**%3A%20The%20clustering%20provides%20a%20data-driven%20framework%20for%20market%20segmentation%20and%20trend%20analysis%0A%20%20%20%20-%20**Researchers**%3A%20The%20methodology%20demonstrates%20reproducible%20techniques%20for%20housing%20market%20analysis%20using%20open%20data%0A%20%20%20%20%22%22%22))%0A%0A%20%20%20%20summary3%20%3Dmo.callout(mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Data%20Source%0A%0A%20%20%20%20Analysis%20based%20on%20HDB%20Resale%20Flat%20Prices%20dataset%20from%20Singapore's%20Open%20Data%20Portal%2C%20covering%20monthly%20transactions%20by%20town%20and%20flat%20type%20from%20January%202017%20onwards.%0A%20%20%20%20Housing%20%26%20Development%20Board.%20(2021).%20Resale%20flat%20prices%20based%20on%20registration%20date%20from%20Jan-2017%20onwards%20(2025)%20%5BDataset%5D.%20data.gov.sg.%20Retrieved%20October%2005%2C%202025%20from%20https%3A%2F%2Fdata.gov.sg%2Fdatasets%2Fd_8b84c4ee58e3cfc0ece0d773c8ca6abc%2Fview%0A%20%20%20%20%22%22%22))%0A%20%20%20%20return%20summary1%2C%20summary2%2C%20summary3%2C%20title%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.outline(label%3D%22Table%20of%20Contents%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%23%20Import%20Python%20Modules%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20altair%20as%20alt%0A%20%20%20%20return%20alt%2C%20pd%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20from%20sklearn.preprocessing%20import%20StandardScaler%0A%20%20%20%20from%20sklearn.cluster%20import%20KMeans%0A%20%20%20%20return%20KMeans%2C%20StandardScaler%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%23%20Load%20and%20Transform%20the%20Data%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20reference%20%3D%20mo.md('Housing%20%26%20Development%20Board.%20(2021).%20Resale%20flat%20prices%20based%20on%20registration%20date%20from%20Jan-2017%20onwards%20(2025)%20%5BDataset%5D.%20data.gov.sg.%20Retrieved%20October%2005%2C%202025%20from%20https%3A%2F%2Fdata.gov.sg%2Fdatasets%2Fd_8b84c4ee58e3cfc0ece0d773c8ca6abc%2Fview')%0A%20%20%20%20return%20(reference%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20pd)%3A%0A%20%20%20%20%23%20define%20a%20function%20to%20read%20in%20the%20CSV%20file%20to%20a%20Pandas%20dataframe%20and%20convert%20the%20month%20column%20to%20'date'%20data%20type%0A%20%20%20%20%40mo.cache%0A%20%20%20%20def%20get_data()%3A%0A%20%20%20%20%20%20%20%20%23%20Read%20CSV%20file%20into%20a%20pandas%20DataFrame%0A%20%20%20%20%20%20%20%20df%20%3D%20pd.read_csv('ResaleflatpricesfromJan2017.csv')%0A%0A%20%20%20%20%20%20%20%20%23%20Convert%20'month'%20column%20to%20datetime%20(format%3A%20YYYY-MM)%0A%20%20%20%20%20%20%20%20df%5B'month'%5D%20%3D%20pd.to_datetime(df%5B'month'%5D%2C%20format%3D'%25Y-%25m'%2C%20errors%3D'coerce')%0A%20%20%20%20%20%20%20%20return%20df%0A%0A%0A%20%20%20%20df%20%3D%20get_data()%0A%20%20%20%20return%20(df%2C)%0A%0A%0A%40app.cell%0Adef%20_(df)%3A%0A%20%20%20%20df%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%23%23%20Count%20the%20number%20of%20flat%20types%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(df%2C%20mo)%3A%0A%20%20%20%20flat_df%20%3D%20mo.sql(%0A%20%20%20%20%20%20%20%20f%22%22%22%0A%20%20%20%20%20%20%20%20SELECT%0A%20%20%20%20%20%20%20%20%20%20%20%20town%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20flat_type%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20COUNT(*)%20AS%20flat_count%0A%20%20%20%20%20%20%20%20FROM%20df%20%0A%20%20%20%20%20%20%20%20GROUP%20BY%0A%20%20%20%20%20%20%20%20%20%20%20%20town%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20flat_type%2C%0A%20%20%20%20%20%20%20%20ORDER%20BY%0A%20%20%20%20%20%20%20%20%20%20%20%20town%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20flat_type%3B%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%20(flat_df%2C)%0A%0A%0A%40app.cell%0Adef%20_(town_avg_df)%3A%0A%20%20%20%20%23%201.%20Define%20the%2026-color%20fixed%20palette%20for%20the%2026%20towns%0A%20%20%20%20%23%20list%20of%20towns%0A%20%20%20%20all_town_domain%20%3D%20sorted(list(town_avg_df%5B%22town%22%5D.unique()))%0A%20%20%20%20all_color_range%20%3D%20%5B%0A%20%20%20%20%20%20%20%20'%234C78A8'%2C%20'%239ECADD'%2C%20'%23FF7F0E'%2C%20'%23FFBB78'%2C%20'%232CA02C'%2C%20'%2398DF8A'%2C%20'%23D62728'%2C%20'%23FF9896'%2C%20%0A%20%20%20%20%20%20%20%20'%239467BD'%2C%20'%23C5B0D5'%2C%20'%238C564B'%2C%20'%23C49C94'%2C%20'%23E377C2'%2C%20'%23F7B6D2'%2C%20'%237F7F7F'%2C%20'%23C7C7C7'%2C%20%0A%20%20%20%20%20%20%20%20'%23BCBD22'%2C%20'%23DBDB8D'%2C%20'%2317BECF'%2C%20'%239EDAE5'%2C%20'%23F08E35'%2C%20'%23C93F56'%2C%20'%23A8AECC'%2C%20'%236B8E23'%2C%20%0A%20%20%20%20%20%20%20%20'%23A54B86'%2C%20'%2342A8AE'%0A%20%20%20%20%5D%0A%20%20%20%20color_map%20%3D%20dict(zip(all_town_domain%2C%20all_color_range))%0A%20%20%20%20return%20all_color_range%2C%20all_town_domain%2C%20color_map%0A%0A%0A%40app.cell%0Adef%20_(alt%2C%20flat_df)%3A%0A%20%20%20%20%23%20chart%20the%20total%20number%20of%20flat%20types%20sold%20among%20the%20towns%20over%20the%20years%202017%20to%20Sep%202025%0A%20%20%20%20flat_type_chart%20%3D%20(%0A%20%20%20%20%20%20%20%20alt.Chart(flat_df)%0A%20%20%20%20%20%20%20%20.mark_bar()%0A%20%20%20%20%20%20%20%20.encode(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3Dalt.X(field%3D'flat_type'%2C%20type%3D'nominal')%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3Dalt.Y(field%3D'flat_count'%2C%20type%3D'quantitative')%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3Dalt.Color(field%3D'flat_type'%2C%20type%3D'nominal'%2C%20legend%3DNone)%2C%20%23scale%3Dalt.Scale(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23domain%3Dall_town_domain%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23range%3Dall_color_range)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20tooltip%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20alt.Tooltip(field%3D'flat_type')%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(field%3D'flat_count'%2C%20format%3D'%2C.0f')%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(field%3D'town')%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.properties(%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D'HDB%20flat%20types%20sold%20over%20the%20years%202017%20to%20Sep%202025'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20height%3D400%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3D'container'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20config%3D%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20'axis'%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20'grid'%3A%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20%23flat_type_chart%0A%20%20%20%20return%20(flat_type_chart%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20flat_obs%20%3D%20mo.callout(mo.md(%22Most%20of%20the%20units%20sold%20were%203-%2C%204-%2C%20or%205-room%20flats%22)%2C%20kind%3D%22warn%22)%0A%20%20%20%20return%20(flat_obs%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%23%23%20Count%20the%20total%20number%20of%20flats%20sold%20in%20each%20town%20over%20the%20entire%20period%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(df%2C%20mo)%3A%0A%20%20%20%20total_flats_df%20%3D%20mo.sql(%0A%20%20%20%20%20%20%20%20f%22%22%22%0A%20%20%20%20%20%20%20%20SELECT%0A%20%20%20%20%20%20%20%20%20%20%20%20town%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20COUNT(*)%20AS%20total_flats_sold%0A%20%20%20%20%20%20%20%20FROM%20df%0A%20%20%20%20%20%20%20%20GROUP%20BY%20town%0A%20%20%20%20%20%20%20%20ORDER%20BY%20town%3B%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%20(total_flats_df%2C)%0A%0A%0A%40app.cell%0Adef%20_(all_color_range%2C%20all_town_domain%2C%20alt%2C%20total_flats_df)%3A%0A%20%20%20%20%23%20Chart%20the%20total%20number%20of%20flats%20as%20a%20bar%20graph%0A%20%20%20%20total_flats_chart%20%3D%20(%0A%20%20%20%20%20%20%20%20alt.Chart(total_flats_df)%0A%20%20%20%20%20%20%20%20.mark_bar()%0A%20%20%20%20%20%20%20%20.encode(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3Dalt.X(field%3D'total_flats_sold'%2C%20type%3D'quantitative')%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3Dalt.Y(field%3D'town'%2C%20type%3D'nominal'%2C%20axis%3Dalt.Axis(title%3DNone))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3Dalt.Color(field%3D'town'%2C%20type%3D'nominal'%2C%20scale%3Dalt.Scale(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20domain%3Dall_town_domain%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20range%3Dall_color_range)%2C%20legend%3DNone)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20tooltip%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(field%3D'total_flats_sold'%2C%20format%3D'%2C.0f')%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(field%3D'town')%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.properties(%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D('Total%20Number%20of%20Flats%20Sold%20in%20Each%20Town')%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20height%3D400%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3D'container'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20config%3D%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20'axis'%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20'grid'%3A%20False%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20%23total_flats_chart%0A%20%20%20%20return%20(total_flats_chart%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20total_obs%20%3D%20mo.callout(mo.md(r%22%22%22%0A%20%20%20%20BUKIT%20TIMAH%20has%20the%20smallest%20number%20of%20flat%20resales%2C%20followed%20by%20MARINE%20PARADE%20and%20CENTRAL%20AREA%0A%20%20%20%20%22%22%22)%2C%20kind%3D'warn')%0A%20%20%20%20return%20(total_obs%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%23%23%20Monthly%20Average%20Resale%20Price%20by%20Town%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(df%2C%20mo)%3A%0A%20%20%20%20town_avg_df%20%3D%20mo.sql(%0A%20%20%20%20%20%20%20%20f%22%22%22%0A%20%20%20%20%20%20%20%20--%20Calculate%20monthly%20average%20resale%20price%20for%20each%20town%0A%20%20%20%20%20%20%20%20SELECT%0A%20%20%20%20%20%20%20%20%20%20%20%20town%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20month%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20AVG(resale_price)%20AS%20avg_resale_price%0A%20%20%20%20%20%20%20%20FROM%0A%20%20%20%20%20%20%20%20%20%20%20%20df%0A%20%20%20%20%20%20%20%20GROUP%20BY%0A%20%20%20%20%20%20%20%20%20%20%20%20town%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20month%3B%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%20(town_avg_df%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%20Rolling%2012-Month%20Average%20Resale%20Price%20per%20Town%0A%0A%20%20%20%20Rolling%2012%20month%20averages%20in%20SQL.%20The%20first%2012%20m%20average%20will%20be%20counted%20from%20Dec%202017%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20town_avg_df)%3A%0A%20%20%20%20roll_df%20%3D%20mo.sql(%0A%20%20%20%20%20%20%20%20f%22%22%22%0A%20%20%20%20%20%20%20%20WITH%20temp%20AS%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20SELECT%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20town%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20month%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20avg_resale_price%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20AVG(avg_resale_price)%20OVER%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20PARTITION%20BY%20town%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ORDER%20BY%20month%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ROWS%20BETWEEN%2011%20PRECEDING%20AND%20CURRENT%20ROW%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%20AS%20moving_avg_12m_price%0A%20%20%20%20%20%20%20%20%20%20%20%20FROM%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20town_avg_df%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20SELECT%20*%0A%20%20%20%20%20%20%20%20FROM%20temp%0A%20%20%20%20%20%20%20%20WHERE%20month%20%3E%3D%20'2017-12-01'%0A%20%20%20%20%20%20%20%20ORDER%20BY%20town%2C%20month%3B%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%20(roll_df%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20town_avg_df)%3A%0A%20%20%20%20%23%20create%20a%20marimo%20selection%20widget%20for%20towns%0A%20%20%20%20town_sel%20%3D%20mo.ui.multiselect.from_series(town_avg_df%5B%22town%22%5D%2C%20value%3D%5B%22BUKIT%20TIMAH%22%2C%20%22PUNGGOL%22%2C%20%22YISHUN%22%5D%2C%20label%3D%22Select%20Towns%22)%0A%20%20%20%20return%20(town_sel%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(roll_df%2C%20town_sel)%3A%0A%20%20%20%20%23%20Filter%20the%20DataFrame%20using%20the%20.isin()%20method%0A%20%20%20%20town_mask%20%3D%20roll_df%5B%22town%22%5D.isin(town_sel.value)%0A%20%20%20%20roll_df_filtered%20%3D%20roll_df%5Btown_mask%5D%0A%20%20%20%20return%20(roll_df_filtered%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(alt%2C%20color_map%2C%20roll_df_filtered%2C%20town_sel)%3A%0A%20%20%20%20%23%20Build%20the%20chart%20for%20the%20rolling%2012m%20average%20resale%20price%20for%20each%20town%0A%0A%20%20%20%20%23%20Build%20the%20domain%2Frange%20for%20selected%20towns%0A%20%20%20%20sel_domain%20%3D%20town_sel.value%0A%20%20%20%20sel_range%20%3D%20%5Bcolor_map%5Bt%5D%20for%20t%20in%20sel_domain%5D%0A%0A%20%20%20%20roll_avg_chart%20%3D%20(%0A%20%20%20%20%20%20%20%20alt.Chart(roll_df_filtered)%0A%20%20%20%20%20%20%20%20.mark_line()%0A%20%20%20%20%20%20%20%20.encode(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3Dalt.X('month%3AT')%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3Dalt.Y('moving_avg_12m_price%3AQ')%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3Dalt.Color('town%3AN'%2C%20scale%3Dalt.Scale(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20domain%3Dsel_domain%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20range%3Dsel_range%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20legend%20%3D%20alt.Legend(title%3D%22Selected%20Towns%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20row%3Dalt.Row(field%3D'town'%2C%20type%3D'nominal')%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20tooltip%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(field%3D'month'%2C%20timeUnit%3D'yearmonth')%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(field%3D'moving_avg_12m_price'%2C%20format%3D'%2C.2f')%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(field%3D'town')%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.properties(%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D'Average%20Monthly%20Resale%20Price%20over%20Rolling%2012M%20for%20HDB%20Flat%20in%20SGD'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20height%3D390%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3D'container'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20config%3D%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20'axis'%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20'grid'%3A%20False%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(all_color_range%2C%20all_town_domain%2C%20alt%2C%20roll_df)%3A%0A%20%20%20%20%23%20Altair%20selection%0A%20%20%20%20highlight%20%3D%20alt.selection_point(fields%3D%5B'town'%5D%2C%20bind%3D'legend')%0A%0A%20%20%20%20roll_chart%20%3D%20(%0A%20%20%20%20%20%20%20%20alt.Chart(roll_df)%0A%20%20%20%20%20%20%20%20.mark_line()%0A%20%20%20%20%20%20%20%20.encode(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3Dalt.X('month%3AT'%2C%20title%3D'Month')%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3Dalt.Y('moving_avg_12m_price%3AQ'%2C%20title%3D'Rolling%2012M%20Average%20Price%20(SGD)')%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3Dalt.Color(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20'town%3AN'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20scale%3Dalt.Scale(domain%3Dall_town_domain%2C%20range%3Dall_color_range)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20legend%3Dalt.Legend(title%3D%22Click%20on%20a%20town%20to%20filter%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20tooltip%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(field%3D'month'%2C%20timeUnit%3D'yearmonth')%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(field%3D'moving_avg_12m_price'%2C%20format%3D'%2C.2f')%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip(field%3D'town')%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20opacity%3Dalt.condition(highlight%2C%20alt.value(1)%2C%20alt.value(0.1))%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.add_params(highlight)%0A%20%20%20%20%20%20%20%20.properties(%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D'Rolling%2012-Month%20Average%20HDB%20Resale%20Prices%20(SGD)%20-%20Click%20on%20a%20Town%20in%20the%20Legend%20to%20Filter'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20height%3D400%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3D'container'%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20%23roll_chart%0A%20%20%20%20return%20(roll_chart%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20obs%20%3D%20mo.callout(mo.md(r%22%22%22%0A%20%20%20%20-%20BUKIT%20TIMAH%20is%20the%20most%20expensive%20town%20in%20Singapore%20for%20HDB%20flats%0A%20%20%20%20-%20YISHUN%20is%20the%20cheapest%0A%20%20%20%20%22%22%22)%2C%20kind%3D%22warn%22)%0A%20%20%20%20return%20(obs%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20obs%2C%20roll_chart)%3A%0A%20%20%20%20trend_chart%20%3D%20mo.vstack(%5Bobs%2C%20roll_chart%5D)%0A%20%20%20%20return%20(trend_chart%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%23%20Cluster%20Analysis%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%20Create%20a%20Features%20Dataframe%0A%0A%20%20%20%20Create%20a%20DataFrame%20with%20town%20as%20the%20index%20and%20Mean_Price%20and%20Std_Dev%20as%20columns%2C%20representing%20the%20mean%20and%20standard%20deviation%20of%20the%20rolling%2012-month%20average%20prices%20for%20each%20town.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20roll_df)%3A%0A%20%20%20%20features_df%20%3D%20mo.sql(%0A%20%20%20%20%20%20%20%20f%22%22%22%0A%20%20%20%20%20%20%20%20SELECT%0A%20%20%20%20%20%20%20%20%20%20%20%20town%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20AVG(moving_avg_12m_price)%20AS%20Mean_Price%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20STDDEV(moving_avg_12m_price)%20AS%20Std_Dev%0A%20%20%20%20%20%20%20%20FROM%0A%20%20%20%20%20%20%20%20%20%20%20%20roll_df%0A%20%20%20%20%20%20%20%20GROUP%20BY%0A%20%20%20%20%20%20%20%20%20%20%20%20town%0A%20%20%20%20%20%20%20%20ORDER%20BY%0A%20%20%20%20%20%20%20%20%20%20%20%20town%3B%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%20(features_df%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%20Scale%20Features%0A%0A%20%20%20%20Apply%20Standard%20Scaling%20to%20the%20Mean_Price%20and%20Std_Dev%20columns%20in%20the%20Features%20DataFrame.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(StandardScaler%2C%20features_df%2C%20pd)%3A%0A%20%20%20%20scaler%20%3D%20StandardScaler()%0A%20%20%20%20features_to_scale%20%3D%20features_df%5B%5B'Mean_Price'%2C%20'Std_Dev'%5D%5D%0A%20%20%20%20scaled_features%20%3D%20scaler.fit_transform(features_to_scale)%0A%20%20%20%20scaled_features_df%20%3D%20pd.DataFrame(scaled_features%2C%20columns%3D%5B'Mean_Price'%2C%20'Std_Dev'%5D%2C%20index%3Dfeatures_df.index)%0A%20%20%20%20features_df%5B%5B'Mean_Price'%2C%20'Std_Dev'%5D%5D%20%3D%20scaled_features_df%5B%5B'Mean_Price'%2C%20'Std_Dev'%5D%5D%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(features_df)%3A%0A%20%20%20%20features_df%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%20Determine%20Optimal%20Clusters%20(Elbow%20Method)%0A%0A%20%20%20%20Implement%20the%20Elbow%20Method%20to%20find%20the%20optimal%20number%20of%20clusters%20by%20calculating%20inertia%20for%20different%20values%20of%20K%20and%20plotting%20the%20results.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(KMeans%2C%20alt%2C%20features_df%2C%20pd)%3A%0A%20%20%20%20%23%20Calculate%20inertia%20values%20for%20K%20values%20between%201%20and%2010%0A%20%20%20%20inertia_values%20%3D%20%5B%5D%0A%20%20%20%20for%20k%20in%20range(1%2C%2011)%3A%0A%20%20%20%20%20%20%20%20kmeans%20%3D%20KMeans(n_clusters%3Dk%2C%20random_state%3D42%2C%20n_init%3D10)%0A%20%20%20%20%20%20%20%20kmeans.fit(features_df%5B%5B'Mean_Price'%2C%20'Std_Dev'%5D%5D)%0A%20%20%20%20%20%20%20%20inertia_values.append(kmeans.inertia_)%0A%0A%20%20%20%20%23%20Create%20DataFrame%20for%20Altair%0A%20%20%20%20elbow_df%20%3D%20pd.DataFrame(%7B%0A%20%20%20%20%20%20%20%20'k'%3A%20range(1%2C%2011)%2C%0A%20%20%20%20%20%20%20%20'inertia'%3A%20inertia_values%0A%20%20%20%20%7D)%0A%0A%20%20%20%20%23%20Create%20Altair%20chart%0A%20%20%20%20elbow_chart%20%3D%20(%0A%20%20%20%20%20%20%20%20alt.Chart(elbow_df)%0A%20%20%20%20%20%20%20%20.mark_line(point%3DTrue)%0A%20%20%20%20%20%20%20%20.encode(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3Dalt.X('k%3AQ'%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20scale%3Dalt.Scale(domain%3D%5B1%2C%2010%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20axis%3Dalt.Axis(tickMinStep%3D1%2C%20title%3D'Number%20of%20Clusters%20(K)'))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3Dalt.Y('inertia%3AQ'%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20axis%3Dalt.Axis(title%3D'Inertia'))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20tooltip%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip('k%3AQ'%2C%20title%3D'K')%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alt.Tooltip('inertia%3AQ'%2C%20format%3D'%2C.0f'%2C%20title%3D'Inertia')%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.properties(%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D'Elbow%20Method%20for%20Optimal%20K'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3D600%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20height%3D400%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20%23elbow_chart%0A%20%20%20%20return%20(elbow_chart%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20k_choice%20%3D%20mo.callout(%22K%20%3D%203%20appears%20to%20be%20optimal%22%2C%20kind%3D%22success%22)%0A%20%20%20%20return%20(k_choice%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20k_reason%20%3D%20mo.md(%22The%20elbow%20is%20most%20evident%20around%20K%3D3%2C%20where%20the%20rate%20of%20inertia%20reduction%20slows%20significantly.%20Beyond%20K%3D4%2C%20gains%20are%20minimal%2C%20suggesting%20over-clustering%20risks%20(e.g.%2C%20splitting%20noise%2C%20not%20structure)%22)%0A%20%20%20%20return%20(k_reason%2C)%0A%0A%0A%40app.cell%0Adef%20_(elbow_chart%2C%20k_choice%2C%20k_reason%2C%20mo)%3A%0A%20%20%20%20elbow%20%3D%20mo.hstack(%5Belbow_chart%2C%20mo.vstack(%5Bk_choice%2C%20k_reason%5D)%5D)%0A%20%20%20%20return%20(elbow%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%20Perform%20k-means%20clustering%0A%0A%20%20%20%20Run%20the%20KMeans%20algorithm%20with%20k%3D3%20and%20assign%20the%20cluster%20labels%20to%20the%20scaled%20features%20dataframe.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(KMeans%2C%20features_df)%3A%0A%20%20%20%20kmeans3%20%3D%20KMeans(n_clusters%3D3%2C%20random_state%3D42%2C%20n_init%3D10)%0A%20%20%20%20features_df%5B'Cluster_ID'%5D%20%3D%20kmeans3.fit_predict(features_df%5B%5B'Mean_Price'%2C%20'Std_Dev'%5D%5D)%0A%20%20%20%20features_df%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%20Visualise%20Clusters%0A%0A%20%20%20%20Create%20a%20scatter%20plot%20of%20the%20scaled%20Mean_Price%20and%20Std_Dev%2C%20colored%20by%20Cluster_ID%2C%20and%20add%20town%20labels%20to%20visualize%20the%20clusters.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(alt%2C%20features_df)%3A%0A%20%20%20%20%23%20Create%20scatter%20plot%0A%20%20%20%20scatter%20%3D%20alt.Chart(features_df).mark_circle(size%3D60).encode(%0A%20%20%20%20%20%20%20%20x%3Dalt.X('Mean_Price%3AQ'%2C%20title%3D'Scaled%20Mean%20Price')%2C%0A%20%20%20%20%20%20%20%20y%3Dalt.Y('Std_Dev%3AQ'%2C%20title%3D'Scaled%20Standard%20Deviation')%2C%0A%20%20%20%20%20%20%20%20color%3Dalt.Color('Cluster_ID%3AN'%2C%20title%3D'Cluster%20ID'%2C%20scale%3Dalt.Scale(scheme%3D'set2'))%2C%0A%20%20%20%20%20%20%20%20tooltip%3D%5B'town'%2C%20'Mean_Price'%2C%20'Std_Dev'%2C%20'Cluster_ID'%5D%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Add%20text%20labels%20for%20towns%0A%20%20%20%20text%20%3D%20scatter.mark_text(align%3D'left'%2C%20dx%3D5%2C%20dy%3D-5%2C%20fontSize%3D9).encode(%0A%20%20%20%20%20%20%20%20text%3D'town%3AN'%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Combine%20scatter%20and%20text%2C%20configure%20chart%0A%20%20%20%20cluster_chart%20%3D%20(scatter%20%2B%20text).properties(%0A%20%20%20%20%20%20%20%20title%3D'Town%20Clusters%20based%20on%20Mean%20and%20Standard%20Deviation%20of%20Rolling%20Resale%20Prices'%2C%0A%20%20%20%20%20%20%20%20width%3D1000%2C%0A%20%20%20%20%20%20%20%20height%3D1000%0A%20%20%20%20).configure_axis(%0A%20%20%20%20%20%20%20%20grid%3DTrue%0A%20%20%20%20).configure_view(%0A%20%20%20%20%20%20%20%20strokeWidth%3D0%0A%20%20%20%20)%0A%20%20%20%20%23%20cluster_chart%0A%20%20%20%20return%20(cluster_chart%2C)%0A%0A%0A%40app.cell%0Adef%20_(cluster_chart%2C%20cluster_table%2C%20mo)%3A%0A%20%20%20%20clusters%20%3D%20mo.hstack(%5Bcluster_chart%2C%20cluster_table%5D%2C%20justify%3D'center')%0A%20%20%20%20return%20(clusters%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20cluster_table%20%3D%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%7C%20Cluster%20ID%20%7C%20Name%20%7C%20Description%20%7C%0A%20%20%20%20%7C%3A---%3A%7C%3A---%3A%7C%3A---%7C%0A%20%20%20%20%7C%200%20%7C%20High%20Price%2C%20Moderate%20Volatility%20%7C%20Towns%20in%20this%20cluster%20have%20a%20significantly%20higher%20average%20scaled%20mean%20price%20and%20moderate%20standard%20deviation.%20%7C%0A%20%20%20%20%7C%201%20%7C%20Moderate%20Price%2C%20High%20Volatility%20%7C%20This%20cluster%20is%20characterized%20by%20moderate%20average%20scaled%20mean%20prices%20and%20higher%20scaled%20standard%20deviations%2C%20suggesting%20more%20price%20fluctuation.%20%7C%0A%20%20%20%20%7C%202%20%7C%20Low%20Price%2C%20Low%20Volatility%20%7C%20Towns%20in%20this%20cluster%20exhibit%20lower%20average%20scaled%20mean%20prices%20and%20lower%20scaled%20standard%20deviations%2C%20indicating%20more%20stable%20and%20affordable%20prices.%20%7C%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%20(cluster_table%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20cluster_names%20%3D%20%7B%0A%20%20%20%20%20%20%20%200%3A%20'High%20Price%2C%20Moderate%20Volatility'%2C%0A%20%20%20%20%20%20%20%201%3A%20'Moderate%20Price%2C%20High%20Volatility'%2C%0A%20%20%20%20%20%20%20%202%3A%20'Low%20Price%2C%20Low%20Volatility'%0A%20%20%20%20%7D%0A%0A%20%20%20%20cluster_0%20%3D%20mo.md(f%22Cluster%20%7Blist(cluster_names.keys())%5B0%5D%7D%20(%7Bcluster_names%5Blist(cluster_names.keys())%5B0%5D%5D%7D)%3A%20Towns%20in%20this%20cluster%20have%20a%20significantly%20higher%20average%20scaled%20mean%20price%20and%20moderate%20standard%20deviation.%20Examples%20include%20BISHAN%2C%20BUKIT%20TIMAH%2C%20and%20CENTRAL%20AREA.%22)%0A%20%20%20%20return%20(cluster_names%2C)%0A%0A%0A%40app.cell%0Adef%20_(cluster_names%2C%20mo)%3A%0A%20%20%20%20cluster_1%20%3D%20mo.md((f%22Cluster%20%7Blist(cluster_names.keys())%5B1%5D%7D%20(%7Bcluster_names%5Blist(cluster_names.keys())%5B1%5D%5D%7D)%3A%20This%20cluster%20is%20characterized%20by%20moderate%20average%20scaled%20mean%20prices%20and%20higher%20scaled%20standard%20deviations%2C%20suggesting%20more%20price%20fluctuation.%20Examples%20include%20BUKIT%20BATOK%2C%20CHOA%20CHU%20KANG%2C%20and%20KALLANG%2FWHAMPOA.%22))%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(cluster_names%2C%20mo)%3A%0A%20%20%20%20cluster_2%20%3D%20mo.md((f%22Cluster%20%7Blist(cluster_names.keys())%5B2%5D%7D%20(%7Bcluster_names%5Blist(cluster_names.keys())%5B2%5D%5D%7D)%3A%20Towns%20in%20this%20cluster%20exhibit%20lower%20average%20scaled%20mean%20prices%20and%20lower%20scaled%20standard%20deviations%2C%20indicating%20more%20stable%20and%20affordable%20prices.%20Examples%20include%20ANG%20MO%20KIO%2C%20BEDOK%2C%20and%20JURONG%20EAST.%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%23%20Interpret%20and%20Report%20Findings%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20conclusion%20%3D%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%20Summary%3A%0A%0A%20%20%20%20%23%23%23%20What%20the%20Z-scores%20Indicate%0A%0A%20%20%20%20%7C%20Scaled%20Value%20of%20Mean%20%7C%20Interpretation%20%7C%0A%20%20%20%20%7C---%7C---%7C%0A%20%20%20%20%7C%20Negative%20%7C%20The%20town's%20average%20price%20is%20below%20the%20average%20of%20all%2026%20towns.%20%7C%0A%20%20%20%20%7C%20Positive%20%7C%20The%20town's%20average%20price%20is%20above%20the%20average%20of%20all%2026%20towns.%20%7C%0A%20%20%20%20%7C%20Zero%20%7C%20The%20town's%20average%20price%20is%20exactly%20the%20average%20of%20all%2026%20towns.%20%7C%0A%0A%20%20%20%20%7C%20Scaled%20Value%20of%20Std%20Dev%20%7C%20Interpretation%20%7C%0A%20%20%20%20%7C---%7C---%7C%0A%20%20%20%20%7C%20Negative%20%7C%20The%20town's%20price%20volatility%20is%20lower%20than%20the%20average%20volatility%20of%20all%2026%20towns.%20%7C%0A%20%20%20%20%7C%20Positive%20%7C%20The%20town's%20price%20volatility%20is%20higher%20than%20the%20average%20volatility%20of%20all%2026%20towns.%20%7C%0A%0A%0A%20%20%20%20%23%23%23%20Data%20Analysis%20Key%20Findings%0A%0A%20%20%20%20*%20%20%20Cluster%200%20is%20characterized%20by%20a%20high%20average%20scaled%20mean%20price%20(average%20of%20scaled%20mean%20price%20is%201.49)%20and%20moderate%20scaled%20standard%20deviation%20(average%20of%20scaled%20standard%20deviation%20is%20-0.00).%0A%20%20%20%20*%20%20%20Cluster%201%20exhibits%20moderate%20average%20scaled%20mean%20prices%20(average%20of%20scaled%20mean%20price%20is%20-0.28)%20and%20high%20scaled%20standard%20deviations%20(average%20of%20scaled%20standard%20deviation%20is%201.74).%0A%20%20%20%20*%20%20%20Cluster%202%20shows%20low%20average%20scaled%20mean%20prices%20(average%20of%20scaled%20mean%20price%20is%20-0.78)%20and%20low%20scaled%20standard%20deviations%20(average%20of%20scaled%20standard%20deviation%20is%20-0.85).%0A%0A%20%20%20%20%23%23%23%20Insights%20or%20Next%20Steps%0A%0A%20%20%20%20*%20%20%20The%20clusters%20highlight%20distinct%20market%20segments%3A%20high-price%2Fmoderate-volatility%2C%20moderate-price%2Fhigh-volatility%2C%20and%20low-price%2Flow-volatility.%0A%20%20%20%20*%20%20%20This%20clustering%20can%20inform%20strategies%20for%20buyers%2C%20sellers%2C%20and%20policymakers%20based%20on%20their%20risk%20tolerance%20and%20price%20preferences.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%20(conclusion%2C)%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
f16e20465011fea28367be5db3c63abe13e31f74e7f4fb826c148d1d1e6f5bec