Upload 2 files
Browse files- app.py +467 -0
- requirements.txt +9 -0
app.py
ADDED
@@ -0,0 +1,467 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import pandas as pd
|
3 |
+
import numpy as np
|
4 |
+
import streamlit.components.v1 as components
|
5 |
+
import matplotlib.pyplot as plt
|
6 |
+
import seaborn as sns
|
7 |
+
from sklearn.preprocessing import PowerTransformer
|
8 |
+
from sklearn.model_selection import train_test_split
|
9 |
+
from sklearn.linear_model import LinearRegression
|
10 |
+
from sklearn.tree import DecisionTreeRegressor, plot_tree
|
11 |
+
from sklearn.ensemble import RandomForestRegressor
|
12 |
+
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
|
13 |
+
import folium
|
14 |
+
from folium.plugins import HeatMap, MarkerCluster
|
15 |
+
import tempfile
|
16 |
+
import os
|
17 |
+
|
18 |
+
def folium_static(fig, height=500):
|
19 |
+
"""Render folium map by saving to HTML."""
|
20 |
+
temp = tempfile.NamedTemporaryFile(delete=False, suffix='.html')
|
21 |
+
temp_path = temp.name
|
22 |
+
temp.close()
|
23 |
+
fig.save(temp_path)
|
24 |
+
with open(temp_path, 'r', encoding='utf-8') as f:
|
25 |
+
html_data = f.read()
|
26 |
+
os.unlink(temp_path)
|
27 |
+
components.html(html_data, height=height)
|
28 |
+
|
29 |
+
|
30 |
+
st.set_page_config(page_title="Airbnb Fiyat Tahmini", page_icon="🏠", layout="wide")
|
31 |
+
|
32 |
+
st.markdown("""
|
33 |
+
<style>
|
34 |
+
.main-header {
|
35 |
+
font-size: 2.5rem;
|
36 |
+
color: #FF5A5F; /* Airbnb color */
|
37 |
+
text-align: center;
|
38 |
+
margin-bottom: 1rem;
|
39 |
+
}
|
40 |
+
.section-header {
|
41 |
+
font-size: 1.8rem;
|
42 |
+
color: #484848;
|
43 |
+
margin-top: 1.5rem;
|
44 |
+
margin-bottom: 1rem;
|
45 |
+
}
|
46 |
+
.subsection-header {
|
47 |
+
font-size: 1.3rem;
|
48 |
+
color: #767676;
|
49 |
+
margin-top: 1rem;
|
50 |
+
}
|
51 |
+
.description {
|
52 |
+
font-size: 1rem;
|
53 |
+
color: #484848;
|
54 |
+
}
|
55 |
+
.info-box {
|
56 |
+
background-color: #f8f9fa;
|
57 |
+
padding: 15px;
|
58 |
+
border-radius: 5px;
|
59 |
+
border-left: 5px solid #FF5A5F;
|
60 |
+
}
|
61 |
+
</style>
|
62 |
+
""", unsafe_allow_html=True)
|
63 |
+
|
64 |
+
st.sidebar.title("Navigasyon")
|
65 |
+
pages = ["Ana Sayfa", "Veri İnceleme", "Ön İşleme Sonuçları", "Model Sonuçları", "Harita Görselleştirme"]
|
66 |
+
selected_page = st.sidebar.radio("", pages)
|
67 |
+
|
68 |
+
@st.cache_data
|
69 |
+
def load_data():
|
70 |
+
try:
|
71 |
+
df = pd.read_csv("AB_NYC_2019.csv")
|
72 |
+
return df
|
73 |
+
except:
|
74 |
+
st.error("Lütfen AB_NYC_2019.csv dosyasını yükleyin veya doğru konumda olduğundan emin olun.")
|
75 |
+
return None
|
76 |
+
|
77 |
+
df = load_data()
|
78 |
+
|
79 |
+
if df is None:
|
80 |
+
st.warning("Devam etmek için veri dosyasını yükleyin.")
|
81 |
+
uploaded_file = st.file_uploader("AB_NYC_2019.csv dosyasını yükleyin", type="csv")
|
82 |
+
if uploaded_file is not None:
|
83 |
+
df = pd.read_csv(uploaded_file)
|
84 |
+
st.success("Veri başarıyla yüklendi!")
|
85 |
+
|
86 |
+
def preprocess_data(df):
|
87 |
+
|
88 |
+
processed_df = df.copy()
|
89 |
+
|
90 |
+
processed_df.drop(columns=["id", "name", "host_id", "host_name", "last_review"], inplace=True)
|
91 |
+
|
92 |
+
power_transformer = PowerTransformer(method='yeo-johnson')
|
93 |
+
reviews_temp = processed_df["reviews_per_month"].fillna(0)
|
94 |
+
processed_df["reviews_per_month"] = power_transformer.fit_transform(reviews_temp.values.reshape(-1, 1))
|
95 |
+
|
96 |
+
processed_df["reviews_per_month_original"] = power_transformer.inverse_transform(
|
97 |
+
processed_df["reviews_per_month"].values.reshape(-1, 1)
|
98 |
+
).flatten()
|
99 |
+
|
100 |
+
processed_df = pd.get_dummies(processed_df, columns=["neighbourhood_group", "room_type"], drop_first=True)
|
101 |
+
|
102 |
+
processed_df["neighbourhood_encoded"] = processed_df.groupby("neighbourhood")["price"].transform("mean")
|
103 |
+
processed_df.drop(columns=["neighbourhood"], inplace=True)
|
104 |
+
|
105 |
+
processed_df = processed_df[processed_df["price"] > 0]
|
106 |
+
|
107 |
+
processed_df["log_price"] = np.log1p(processed_df["price"])
|
108 |
+
processed_df["minimum_nights_log"] = np.log1p(processed_df["minimum_nights"])
|
109 |
+
processed_df["review_score"] = processed_df["reviews_per_month"] * processed_df["number_of_reviews"]
|
110 |
+
|
111 |
+
X = processed_df.drop(columns=["price"])
|
112 |
+
if "reviews_per_month_original" in X.columns:
|
113 |
+
X = X.drop(columns=["reviews_per_month_original"])
|
114 |
+
y = processed_df["price"]
|
115 |
+
|
116 |
+
return X, y, processed_df
|
117 |
+
|
118 |
+
if df is not None:
|
119 |
+
|
120 |
+
if selected_page == "Ana Sayfa":
|
121 |
+
st.markdown("<h1 class='main-header'>New York Airbnb Fiyat Tahmini</h1>", unsafe_allow_html=True)
|
122 |
+
|
123 |
+
col1, col2, col3 = st.columns([1, 3, 1])
|
124 |
+
with col2:
|
125 |
+
st.image("https://a0.muscache.com/im/pictures/fe7217ff-0b24-438d-8833-1dd45a298a6b.jpg", use_column_width=True)
|
126 |
+
|
127 |
+
st.markdown("<div class='info-box'>", unsafe_allow_html=True)
|
128 |
+
st.markdown("""
|
129 |
+
Bu veri seti ile New York City'deki Airbnb ilanlarının fiyatlarını tahmin etmeye çalışacağız.
|
130 |
+
Amacımız, bir evi kiralamak isteyen birinin fiyatının ne olacağını öngörmek.
|
131 |
+
Bunun için Regresyon, Karar Ağacı ve Random Forest modelleri uygulayacağız.
|
132 |
+
""")
|
133 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
134 |
+
|
135 |
+
st.markdown("<h2 class='section-header'>Veri Seti Genel Bakış</h2>", unsafe_allow_html=True)
|
136 |
+
st.dataframe(df.head())
|
137 |
+
|
138 |
+
st.markdown("<h2 class='section-header'>Veri Seti Sütunları</h2>", unsafe_allow_html=True)
|
139 |
+
st.markdown("""
|
140 |
+
- **id**: Airbnb ilanının benzersiz kimlik numarası
|
141 |
+
- **name**: İlanın adı veya açıklaması
|
142 |
+
- **host_id**: İlan sahibinin benzersiz kimlik numarası
|
143 |
+
- **host_name**: İlan sahibinin adı
|
144 |
+
- **neighbourhood_group**: İlanın bulunduğu büyük bölge (örneğin Manhattan, Brooklyn)
|
145 |
+
- **neighbourhood**: İlanın bulunduğu mahalle
|
146 |
+
- **latitude**: İlanın enlem (latitude) koordinatı
|
147 |
+
- **longitude**: İlanın boylam (longitude) koordinatı
|
148 |
+
- **room_type**: Konaklama türü (Örneğin: "Private room", "Entire home/apt", "Shared room")
|
149 |
+
- **price**: Gecelik konaklama ücreti (USD cinsinden)
|
150 |
+
- **minimum_nights**: Konaklama için belirlenen minimum gece sayısı
|
151 |
+
- **number_of_reviews**: İlanın aldığı toplam inceleme sayısı
|
152 |
+
- **last_review**: İlanın son inceleme tarihi
|
153 |
+
- **reviews_per_month**: Aylık ortalama inceleme sayısı
|
154 |
+
- **calculated_host_listings_count**: Aynı ev sahibinin toplam ilan sayısı
|
155 |
+
- **availability_365**: Yıl boyunca müsait olduğu gün sayısı (365 gün üzerinden)
|
156 |
+
""")
|
157 |
+
|
158 |
+
|
159 |
+
elif selected_page == "Veri İnceleme":
|
160 |
+
st.markdown("<h1 class='main-header'>Veri İnceleme</h1>", unsafe_allow_html=True)
|
161 |
+
|
162 |
+
st.markdown("<h2 class='section-header'>Aylık Yorum Sayısı Analizi</h2>", unsafe_allow_html=True)
|
163 |
+
|
164 |
+
col1, col2 = st.columns(2)
|
165 |
+
with col1:
|
166 |
+
st.metric("Ortalama", f"{df['reviews_per_month'].mean():.2f}")
|
167 |
+
with col2:
|
168 |
+
st.metric("Medyan", f"{df['reviews_per_month'].median():.2f}")
|
169 |
+
|
170 |
+
st.markdown("<div class='info-box'>", unsafe_allow_html=True)
|
171 |
+
st.markdown("""
|
172 |
+
Çarpık bir dağılım var. Çünkü ortalama > medyan olduğundan dağılım sağa çarpık.
|
173 |
+
Bu da birçok ilan çok az yorum alırken, az sayıda ilan aşırı fazla yorum aldığını gösteriyor.
|
174 |
+
""")
|
175 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
176 |
+
|
177 |
+
fig, ax = plt.subplots(figsize=(10, 5))
|
178 |
+
sns.histplot(df["reviews_per_month"].dropna(), bins=50, kde=True, ax=ax)
|
179 |
+
ax.axvline(df["reviews_per_month"].dropna().mean(), color='red', linestyle='dashed', linewidth=2, label="ortalama")
|
180 |
+
ax.axvline(df["reviews_per_month"].dropna().median(), color='blue', linestyle='dashed', linewidth=2, label="median")
|
181 |
+
ax.set_title("Aylık Yorum Sayısı Dağılımı")
|
182 |
+
ax.legend()
|
183 |
+
|
184 |
+
st.pyplot(fig)
|
185 |
+
|
186 |
+
st.markdown("""
|
187 |
+
Daha fazla yorum sayısına sahip ilanlar ortalamayı yukarı çekiyor.
|
188 |
+
Burada çarpıklığı azaltmam gerekiyor. Dağılımı dengeli hale getirelim.
|
189 |
+
""")
|
190 |
+
|
191 |
+
st.markdown("<h2 class='section-header'>Eksik Değer Analizi</h2>", unsafe_allow_html=True)
|
192 |
+
|
193 |
+
missing_vals = df.isnull().sum()
|
194 |
+
missing_cols = missing_vals[missing_vals > 0]
|
195 |
+
|
196 |
+
if len(missing_cols) > 0:
|
197 |
+
st.write("Eksik değer içeren sütunlar:")
|
198 |
+
st.write(missing_cols)
|
199 |
+
else:
|
200 |
+
st.success("Veri setinde eksik değer bulunmuyor.")
|
201 |
+
|
202 |
+
|
203 |
+
elif selected_page == "Ön İşleme Sonuçları":
|
204 |
+
st.markdown("<h1 class='main-header'>Ön İşleme Sonuçları</h1>", unsafe_allow_html=True)
|
205 |
+
|
206 |
+
|
207 |
+
st.markdown("<h2 class='section-header'>Yeo-Johnson Dönüşümü Sonuçları</h2>", unsafe_allow_html=True)
|
208 |
+
|
209 |
+
processed_df = df.copy()
|
210 |
+
|
211 |
+
try:
|
212 |
+
power_transformer = PowerTransformer(method='yeo-johnson')
|
213 |
+
reviews_temp = processed_df["reviews_per_month"].fillna(0)
|
214 |
+
reviews_transformed = power_transformer.fit_transform(reviews_temp.values.reshape(-1, 1))
|
215 |
+
reviews_original = power_transformer.inverse_transform(reviews_transformed)
|
216 |
+
|
217 |
+
|
218 |
+
fig, axes = plt.subplots(1, 2, figsize=(16, 5))
|
219 |
+
|
220 |
+
|
221 |
+
sns.histplot(reviews_temp, bins=50, kde=True, ax=axes[0], color='skyblue')
|
222 |
+
axes[0].axvline(reviews_temp.mean(), color='red', linestyle='dashed', linewidth=2, label="ortalama")
|
223 |
+
axes[0].axvline(reviews_temp.median(), color='blue', linestyle='dashed', linewidth=2, label="median")
|
224 |
+
axes[0].set_title("Orijinal Veride Aylık Yorum Dağılımı")
|
225 |
+
axes[0].legend()
|
226 |
+
|
227 |
+
|
228 |
+
sns.histplot(reviews_transformed, bins=50, kde=True, ax=axes[1], color='lightgreen')
|
229 |
+
axes[1].axvline(reviews_transformed.mean(), color='red', linestyle='dashed', linewidth=2, label="ortalama")
|
230 |
+
axes[1].axvline(np.median(reviews_transformed), color='blue', linestyle='dashed', linewidth=2, label="median")
|
231 |
+
axes[1].set_title("Yeo-Johnson Dönüşüm Sonrası Dağılım")
|
232 |
+
axes[1].legend()
|
233 |
+
|
234 |
+
plt.tight_layout()
|
235 |
+
st.pyplot(fig)
|
236 |
+
|
237 |
+
|
238 |
+
fig, ax = plt.subplots(1, 2, figsize=(12, 6))
|
239 |
+
|
240 |
+
|
241 |
+
sns.boxplot(x=reviews_transformed.flatten(), ax=ax[0])
|
242 |
+
ax[0].set_title("Aylık Yorum Sayısı Dağılımı (Boxplot)")
|
243 |
+
|
244 |
+
|
245 |
+
sns.histplot(reviews_transformed.flatten(), bins=30, kde=True, ax=ax[1])
|
246 |
+
ax[1].set_title("Aylık Yorum Sayısı Histogramı")
|
247 |
+
|
248 |
+
st.pyplot(fig)
|
249 |
+
|
250 |
+
st.markdown("<div class='info-box'>", unsafe_allow_html=True)
|
251 |
+
st.markdown("""
|
252 |
+
✓ Çünkü veri uç değerler içeriyor ve çarpık bir dağılım gösteriyor.
|
253 |
+
✓ Çoğu ilan çok az yorum alırken, birkaç ilan çok fazla yorum alıyor.
|
254 |
+
✓ mean() kullanırsak, az yorum alan ilanlar için yanlış tahmin yapabiliriz.
|
255 |
+
""")
|
256 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
257 |
+
except Exception as e:
|
258 |
+
st.error(f"Dönüşüm sırasında hata oluştu: {e}")
|
259 |
+
|
260 |
+
|
261 |
+
st.markdown("<h2 class='section-header'>Eksik Değer Doldurma Stratejisi</h2>", unsafe_allow_html=True)
|
262 |
+
|
263 |
+
st.markdown("""
|
264 |
+
Eksik değerleri şu stratejiye göre doldurdum:
|
265 |
+
|
266 |
+
1. Önce reviews_per_month sütununu float tipine çevirdim.
|
267 |
+
2. Aynı mahalle ve oda tipindeki medyan değerleri kullanarak eksik değerleri doldurdum.
|
268 |
+
3. Hala eksik değer varsa, mahalle bazında medyan değerleri kullandım.
|
269 |
+
4. Son olarak, kalan eksik değerleri 0 ile doldurdum.
|
270 |
+
""")
|
271 |
+
|
272 |
+
|
273 |
+
st.markdown("<h2 class='section-header'>Kategorik Değişken Dönüşümü ve Özellik Mühendisliği</h2>", unsafe_allow_html=True)
|
274 |
+
|
275 |
+
st.markdown("""
|
276 |
+
1. neighbourhood_group ve room_type kategorik değişkenlerini one-hot encoding ile sayısal değerlere dönüştürdüm.
|
277 |
+
2. neighbourhood değişkenini, her mahalle için ortalama fiyatı hesaplayarak kodladım.
|
278 |
+
3. Sıfır fiyatlı ilanları veri setinden çıkardım.
|
279 |
+
4. Logaritmik dönüşümler uygulayarak log_price ve minimum_nights_log değişkenlerini oluşturdum.
|
280 |
+
5. reviews_per_month ve number_of_reviews değişkenlerini birleştirerek review_score adlı yeni bir özellik oluşturdum.
|
281 |
+
""")
|
282 |
+
|
283 |
+
|
284 |
+
elif selected_page == "Model Sonuçları":
|
285 |
+
st.markdown("<h1 class='main-header'>Model Sonuçları</h1>", unsafe_allow_html=True)
|
286 |
+
|
287 |
+
|
288 |
+
if st.checkbox("Modelleri Göster", value=True):
|
289 |
+
with st.spinner("Modeller hazırlanıyor..."):
|
290 |
+
|
291 |
+
X, y, processed_df = preprocess_data(df)
|
292 |
+
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
|
293 |
+
|
294 |
+
|
295 |
+
st.markdown("<h2 class='section-header'>Doğrusal Regresyon Sonuçları</h2>", unsafe_allow_html=True)
|
296 |
+
|
297 |
+
lr_model = LinearRegression()
|
298 |
+
lr_model.fit(X_train, y_train)
|
299 |
+
lr_pred = lr_model.predict(X_test)
|
300 |
+
|
301 |
+
lr_mae = mean_absolute_error(y_test, lr_pred)
|
302 |
+
lr_mse = mean_squared_error(y_test, lr_pred)
|
303 |
+
lr_rmse = np.sqrt(lr_mse)
|
304 |
+
|
305 |
+
col1, col2, col3 = st.columns(3)
|
306 |
+
col1.metric("MAE", f"${lr_mae:.2f}")
|
307 |
+
col2.metric("RMSE", f"${lr_rmse:.2f}")
|
308 |
+
col3.metric("MSE", f"${lr_mse:.2f}")
|
309 |
+
|
310 |
+
fig, ax = plt.subplots(figsize=(8, 6))
|
311 |
+
sns.scatterplot(x=y_test, y=lr_pred, alpha=0.5, ax=ax)
|
312 |
+
ax.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], color="red", linestyle="--")
|
313 |
+
ax.set_xlabel("Gerçek Değerler (Fiyat)")
|
314 |
+
ax.set_ylabel("Tahmin Edilen Değerler (Fiyat)")
|
315 |
+
ax.set_title("Gerçek vs. Tahmin Edilen Fiyatlar")
|
316 |
+
|
317 |
+
|
318 |
+
|
319 |
+
st.pyplot(fig)
|
320 |
+
|
321 |
+
|
322 |
+
st.markdown("<h2 class='section-header'>Karar Ağacı Sonuçları</h2>", unsafe_allow_html=True)
|
323 |
+
|
324 |
+
dt_model = DecisionTreeRegressor(max_depth=4, random_state=42)
|
325 |
+
dt_model.fit(X_train, y_train)
|
326 |
+
dt_pred = dt_model.predict(X_test)
|
327 |
+
|
328 |
+
dt_mae = mean_absolute_error(y_test, dt_pred)
|
329 |
+
dt_mse = mean_squared_error(y_test, dt_pred)
|
330 |
+
dt_r2 = r2_score(y_test, dt_pred)
|
331 |
+
|
332 |
+
col1, col2, col3 = st.columns(3)
|
333 |
+
col1.metric("MAE", f"${dt_mae:.2f}")
|
334 |
+
col2.metric("MSE", f"${dt_mse:.2f}")
|
335 |
+
col3.metric("R²", f"{dt_r2:.4f}")
|
336 |
+
|
337 |
+
fig, ax = plt.subplots(figsize=(8, 6))
|
338 |
+
sns.scatterplot(x=y_test, y=dt_pred, alpha=0.5, ax=ax)
|
339 |
+
ax.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], color="brown", linestyle="--")
|
340 |
+
ax.set_xlabel("Gerçek Değerler (Fiyat)")
|
341 |
+
ax.set_ylabel("Tahmin Edilen Değerler (Fiyat)")
|
342 |
+
ax.set_title("Gerçek vs. Tahmin Edilen Fiyatlar")
|
343 |
+
|
344 |
+
|
345 |
+
|
346 |
+
st.pyplot(fig)
|
347 |
+
|
348 |
+
if st.checkbox("Karar Ağacını Görselleştir"):
|
349 |
+
fig, ax = plt.subplots(figsize=(20, 10))
|
350 |
+
plot_tree(dt_model, feature_names=X.columns, filled=True, rounded=True, ax=ax)
|
351 |
+
ax.set_title("Decision Tree")
|
352 |
+
st.pyplot(fig)
|
353 |
+
|
354 |
+
|
355 |
+
st.markdown("<h2 class='section-header'>Random Forest Sonuçları</h2>", unsafe_allow_html=True)
|
356 |
+
|
357 |
+
rf_model = RandomForestRegressor(n_estimators=100, random_state=42)
|
358 |
+
rf_model.fit(X_train, y_train)
|
359 |
+
rf_pred = rf_model.predict(X_test)
|
360 |
+
|
361 |
+
rf_mae = mean_absolute_error(y_test, rf_pred)
|
362 |
+
rf_mse = mean_squared_error(y_test, rf_pred)
|
363 |
+
rf_r2 = r2_score(y_test, rf_pred)
|
364 |
+
|
365 |
+
col1, col2, col3 = st.columns(3)
|
366 |
+
col1.metric("MAE", f"${rf_mae:.2f}")
|
367 |
+
col2.metric("MSE", f"${rf_mse:.2f}")
|
368 |
+
col3.metric("R²", f"{rf_r2:.4f}")
|
369 |
+
|
370 |
+
fig, ax = plt.subplots(figsize=(8, 6))
|
371 |
+
sns.scatterplot(x=y_test, y=rf_pred, alpha=0.5, ax=ax)
|
372 |
+
ax.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], color="red", linestyle="--")
|
373 |
+
ax.set_xlabel("Gerçek Değerler (Fiyat)")
|
374 |
+
ax.set_ylabel("Tahmin Edilen Değerler (Fiyat)")
|
375 |
+
ax.set_title("Gerçek vs. Tahmin Edilen Fiyatlar")
|
376 |
+
|
377 |
+
|
378 |
+
|
379 |
+
st.pyplot(fig)
|
380 |
+
|
381 |
+
st.markdown("<h2 class='section-header'>Model Karşılaştırması</h2>", unsafe_allow_html=True)
|
382 |
+
|
383 |
+
comparison_df = pd.DataFrame({
|
384 |
+
'Model': ['Doğrusal Regresyon', 'Karar Ağacı', 'Random Forest'],
|
385 |
+
'MAE': [lr_mae, dt_mae, rf_mae],
|
386 |
+
'MSE': [lr_mse, dt_mse, rf_mse],
|
387 |
+
'R²': [r2_score(y_test, lr_pred), dt_r2, rf_r2]
|
388 |
+
})
|
389 |
+
|
390 |
+
comparison_df = comparison_df.set_index('Model')
|
391 |
+
st.dataframe(comparison_df.style.highlight_min(subset=['MAE', 'MSE']).highlight_max(subset=['R²']))
|
392 |
+
|
393 |
+
st.markdown("<div class='info-box'>", unsafe_allow_html=True)
|
394 |
+
st.markdown("""
|
395 |
+
Random Forest modeli en iyi performansı göstermiştir.
|
396 |
+
R² değeri 1'e yakın olduğu için modelin açıklama gücü yüksektir.
|
397 |
+
""")
|
398 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
399 |
+
|
400 |
+
|
401 |
+
elif selected_page == "Harita Görselleştirme":
|
402 |
+
st.markdown("<h1 class='main-header'>Harita Görselleştirme</h1>", unsafe_allow_html=True)
|
403 |
+
|
404 |
+
try:
|
405 |
+
|
406 |
+
st.markdown("<h2 class='section-header'>Semtlere Göre Konum Dağılımı</h2>", unsafe_allow_html=True)
|
407 |
+
|
408 |
+
fig, ax = plt.subplots(figsize=(10, 6))
|
409 |
+
sns.scatterplot(x=df.longitude, y=df.latitude, hue=df.neighbourhood_group, ax=ax)
|
410 |
+
ax.set_title('Neighbourhood Group Location')
|
411 |
+
st.pyplot(fig)
|
412 |
+
|
413 |
+
|
414 |
+
st.markdown("<h2 class='section-header'>Oda Tiplerine Göre Konum Dağılımı</h2>", unsafe_allow_html=True)
|
415 |
+
|
416 |
+
fig, ax = plt.subplots(figsize=(10, 6))
|
417 |
+
sns.scatterplot(x=df.longitude, y=df.latitude, hue=df.room_type, ax=ax)
|
418 |
+
ax.set_title('Room type location per Neighbourhood Group')
|
419 |
+
st.pyplot(fig)
|
420 |
+
|
421 |
+
|
422 |
+
st.markdown("<h2 class='section-header'>İlan Yoğunluğu Haritası</h2>", unsafe_allow_html=True)
|
423 |
+
|
424 |
+
if st.button("Yoğunluk Haritasını Göster"):
|
425 |
+
|
426 |
+
m = folium.Map(location=[40.76586, -73.98436], tiles='cartodbpositron', zoom_start=11)
|
427 |
+
|
428 |
+
|
429 |
+
sample_df = df.sample(min(5000, len(df)))
|
430 |
+
|
431 |
+
|
432 |
+
HeatMap(data=sample_df[['latitude', 'longitude']].values.tolist(), radius=10).add_to(m)
|
433 |
+
|
434 |
+
|
435 |
+
folium_static(m, height=600)
|
436 |
+
|
437 |
+
|
438 |
+
st.markdown("<h2 class='section-header'>İlan Kümeleme Haritası</h2>", unsafe_allow_html=True)
|
439 |
+
|
440 |
+
if st.button("Kümeleme Haritasını Göster"):
|
441 |
+
|
442 |
+
m = folium.Map(location=[40.76586, -73.98436], tiles='cartodbpositron', zoom_start=11)
|
443 |
+
|
444 |
+
sample_df = df.sample(min(1000, len(df)))
|
445 |
+
|
446 |
+
|
447 |
+
sample_df["All"] = 'Room type: ' + sample_df['room_type'].astype(str) + ', ' + \
|
448 |
+
'Availability (365 days): ' + sample_df["availability_365"].astype(str) + ', ' + \
|
449 |
+
'Price: $' + sample_df["price"].astype(str)
|
450 |
+
|
451 |
+
|
452 |
+
marker_cluster = MarkerCluster().add_to(m)
|
453 |
+
|
454 |
+
|
455 |
+
for idx, row in sample_df.iterrows():
|
456 |
+
folium.Marker(
|
457 |
+
location=[row['latitude'], row['longitude']],
|
458 |
+
popup=row['All']
|
459 |
+
).add_to(marker_cluster)
|
460 |
+
|
461 |
+
folium_static(m, height=600)
|
462 |
+
except Exception as e:
|
463 |
+
st.error(f"Harita oluşturulurken bir hata oluştu: {e}")
|
464 |
+
|
465 |
+
if __name__ == "__main__":
|
466 |
+
|
467 |
+
pass
|
requirements.txt
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
streamlit>=1.25.0
|
2 |
+
pandas>=1.3.0
|
3 |
+
numpy>=1.21.0
|
4 |
+
matplotlib>=3.4.0
|
5 |
+
seaborn>=0.11.0
|
6 |
+
scikit-learn>=1.0.0
|
7 |
+
folium>=0.12.0
|
8 |
+
streamlit-folium>=0.11.0
|
9 |
+
plotly>=5.3.0
|