aunghlaing commited on
Commit
c74465e
·
verified ·
1 Parent(s): ea1f2b0
src/Adjusted_Resale_Prices_2025_with_coords.csv DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:a5d0cfae8fa43b5c33f94d73a0b4aa93b24f422322608b3b666103631a163aac
3
- size 40738143
 
 
 
 
src/MasterPlan2019PlanningAreaBoundaryNoSea.geojson DELETED
The diff for this file is too large to render. See raw diff
 
src/streamlit1_app.py DELETED
@@ -1,40 +0,0 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
- import streamlit as st
5
-
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/streamlit_app.py DELETED
@@ -1,210 +0,0 @@
1
- import streamlit as st
2
- import pandas as pd
3
- import plotly.express as px
4
- import geopandas as gpd
5
-
6
- st.set_page_config(layout="wide", page_title="Singapore Housing Data Dashboard", page_icon=":house:")
7
-
8
- @st.cache_data
9
- def load_data():
10
- # Use relative paths for deployment
11
- df = pd.read_csv("Adjusted_Resale_Prices_2025_with_coords.csv")
12
- gdf = gpd.read_file("planning_area_boundaries.geojson")
13
- return df, gdf
14
-
15
- try:
16
- df, gdf = load_data()
17
- except FileNotFoundError as e:
18
- st.error(f"Data file not found: {e}")
19
- st.stop()
20
-
21
- st.title("🏠 Singapore HDB Resale Price Dashboard")
22
- st.markdown("---")
23
-
24
- # Sidebar Filters
25
- st.sidebar.header("🔍 Filters")
26
-
27
- # Check if required columns exist
28
- required_columns = ["planning_area", "flat_type", "storey_range", "lease_commence_year", "resale_price"]
29
- missing_columns = [col for col in required_columns if col not in df.columns]
30
-
31
- if missing_columns:
32
- st.error(f"Missing required columns: {missing_columns}")
33
- st.write("Available columns:", list(df.columns))
34
- st.stop()
35
-
36
- all_planning_areas = sorted(df["planning_area"].unique())
37
- selected_planning_area = st.sidebar.selectbox(
38
- "Planning Area",
39
- ["All"] + all_planning_areas
40
- )
41
-
42
- all_flat_types = sorted(df["flat_type"].unique())
43
- selected_flat_types = st.sidebar.multiselect(
44
- "Flat Type",
45
- all_flat_types,
46
- default=all_flat_types
47
- )
48
-
49
- all_storey_ranges = sorted(df["storey_range"].unique())
50
- selected_storey_ranges = st.sidebar.multiselect(
51
- "Storey Range",
52
- all_storey_ranges,
53
- default=all_storey_ranges
54
- )
55
-
56
- min_year = int(df["lease_commence_year"].min())
57
- max_year = int(df["lease_commence_year"].max())
58
- selected_year_range = st.sidebar.slider(
59
- "Lease Commencement Year",
60
- min_year, max_year, (min_year, max_year)
61
- )
62
-
63
- # Apply filters
64
- filtered_df = df[
65
- (df["flat_type"].isin(selected_flat_types)) &
66
- (df["storey_range"].isin(selected_storey_ranges)) &
67
- (df["lease_commence_year"] >= selected_year_range[0]) &
68
- (df["lease_commence_year"] <= selected_year_range[1])
69
- ]
70
-
71
- if selected_planning_area != "All":
72
- filtered_df = filtered_df[filtered_df["planning_area"] == selected_planning_area]
73
-
74
- # Display filter summary
75
- st.sidebar.markdown("---")
76
- st.sidebar.write(f"**Records shown:** {len(filtered_df):,}")
77
- st.sidebar.write(f"**Total records:** {len(df):,}")
78
-
79
- # Main content
80
- col1, col2 = st.columns([2, 1])
81
-
82
- with col2:
83
- if not filtered_df.empty:
84
- avg_price = filtered_df["resale_price"].mean()
85
- median_price = filtered_df["resale_price"].median()
86
- max_price = filtered_df["resale_price"].max()
87
- min_price = filtered_df["resale_price"].min()
88
-
89
- st.metric("Average Price", f"${avg_price:,.0f}")
90
- st.metric("Median Price", f"${median_price:,.0f}")
91
- st.metric("Price Range", f"${min_price:,.0f} - ${max_price:,.0f}")
92
-
93
- with col1:
94
- st.header("📊 Key Statistics")
95
-
96
- # Choropleth Map
97
- st.header("🗺️ Average Resale Price by Planning Area")
98
- if not filtered_df.empty:
99
- avg_price_by_planning_area = filtered_df.groupby("planning_area")["resale_price"].mean().reset_index()
100
-
101
- # Try to merge with GeoDataFrame
102
- try:
103
- # Check if the GeoDataFrame has the expected column
104
- if "PLN_AREA_N" in gdf.columns:
105
- gdf_merged = gdf.merge(avg_price_by_planning_area, left_on="PLN_AREA_N", right_on="planning_area", how="left")
106
- else:
107
- # Try other common column names
108
- possible_columns = [col for col in gdf.columns if "area" in col.lower() or "name" in col.lower()]
109
- if possible_columns:
110
- gdf_merged = gdf.merge(avg_price_by_planning_area, left_on=possible_columns[0], right_on="planning_area", how="left")
111
- else:
112
- st.error("Could not find matching column in GeoJSON for planning areas")
113
- st.write("GeoJSON columns:", list(gdf.columns))
114
- gdf_merged = None
115
-
116
- if gdf_merged is not None:
117
- fig_map = px.choropleth_mapbox(
118
- gdf_merged,
119
- geojson=gdf_merged.geometry,
120
- locations=gdf_merged.index,
121
- color="resale_price",
122
- color_continuous_scale="Viridis",
123
- mapbox_style="carto-positron",
124
- zoom=9.5,
125
- center={"lat": 1.3521, "lon": 103.8198},
126
- opacity=0.7,
127
- labels={
128
- "resale_price": "Avg Resale Price (SGD)",
129
- },
130
- hover_name=gdf_merged.columns[0] if "PLN_AREA_N" not in gdf_merged.columns else "PLN_AREA_N",
131
- hover_data={
132
- "resale_price": ":$,.0f",
133
- }
134
- )
135
- fig_map.update_layout(margin={"r":0,"t":0,"l":0,"b":0}, height=500)
136
- st.plotly_chart(fig_map, use_container_width=True)
137
- else:
138
- st.warning("Could not create choropleth map due to data structure mismatch.")
139
- except Exception as e:
140
- st.error(f"Error creating map: {e}")
141
- st.write("Showing data table instead:")
142
- st.dataframe(avg_price_by_planning_area)
143
- else:
144
- st.warning("No data to display for the selected filters on the map.")
145
-
146
- # Line Chart
147
- st.header("📈 Resale Price Trends Over Lease Commencement Year")
148
- if not filtered_df.empty:
149
- avg_price_by_year = filtered_df.groupby("lease_commence_year")["resale_price"].mean().reset_index()
150
- fig_line = px.line(
151
- avg_price_by_year,
152
- x="lease_commence_year",
153
- y="resale_price",
154
- title="Average Resale Price by Lease Commencement Year",
155
- labels={
156
- "lease_commence_year": "Lease Commencement Year",
157
- "resale_price": "Average Resale Price (SGD)"
158
- },
159
- markers=True
160
- )
161
- fig_line.update_traces(mode="lines+markers", line=dict(width=3))
162
- fig_line.update_layout(
163
- hovermode="x unified",
164
- height=400,
165
- xaxis_title="Lease Commencement Year",
166
- yaxis_title="Average Resale Price (SGD)"
167
- )
168
- st.plotly_chart(fig_line, use_container_width=True)
169
- else:
170
- st.warning("No data to display for the selected filters on the line chart.")
171
-
172
- # Additional Charts
173
- if not filtered_df.empty:
174
- col1, col2 = st.columns(2)
175
-
176
- with col1:
177
- st.subheader("📊 Price Distribution by Flat Type")
178
- fig_box = px.box(
179
- filtered_df,
180
- x="flat_type",
181
- y="resale_price",
182
- title="Price Distribution by Flat Type"
183
- )
184
- fig_box.update_layout(height=400)
185
- st.plotly_chart(fig_box, use_container_width=True)
186
-
187
- with col2:
188
- st.subheader("🏢 Average Price by Storey Range")
189
- avg_by_storey = filtered_df.groupby("storey_range")["resale_price"].mean().reset_index()
190
- fig_bar = px.bar(
191
- avg_by_storey,
192
- x="storey_range",
193
- y="resale_price",
194
- title="Average Price by Storey Range"
195
- )
196
- fig_bar.update_layout(height=400)
197
- st.plotly_chart(fig_bar, use_container_width=True)
198
-
199
- # Toggle for Data Table
200
- st.header("📋 Filtered Data Table")
201
- show_data_table = st.checkbox("Show filtered data table")
202
- if show_data_table:
203
- if not filtered_df.empty:
204
- st.dataframe(filtered_df, use_container_width=True)
205
- else:
206
- st.info("No data to display in the table for the selected filters.")
207
-
208
- # Footer
209
- st.markdown("---")
210
- st.markdown("*Data source: Singapore HDB Resale Prices*")