|
import streamlit as st |
|
import pandas as pd |
|
import numpy as np |
|
from sklearn.neighbors import KNeighborsRegressor |
|
from geopy.distance import geodesic |
|
import googlemaps |
|
from geopy.exc import GeocoderTimedOut |
|
from streamlit_folium import st_folium |
|
import folium |
|
from branca.colormap import LinearColormap |
|
import base64 |
|
from io import BytesIO |
|
import sys |
|
import pydeck as pdk |
|
|
|
|
|
print("Python version") |
|
print(sys.version) |
|
print("Version info.") |
|
print(sys.version_info) |
|
|
|
image1 = 'images/avalia-removebg-preview.png' |
|
|
|
|
|
def add_heatmap_layer(map_obj, data, column_name, colormap_name, radius=15): |
|
heat_data = data[['latitude', 'longitude', column_name]].dropna() |
|
heat_layer = folium.FeatureGroup(name=f'Variável - {column_name}') |
|
|
|
cmap = LinearColormap(colors=['blue', 'white', 'red'], vmin=heat_data[column_name].min(), vmax=heat_data[column_name].max()) |
|
|
|
for index, row in heat_data.iterrows(): |
|
folium.CircleMarker( |
|
location=[row['latitude'], row['longitude']], |
|
radius=radius, |
|
fill=True, |
|
fill_color=cmap(row[column_name]), |
|
fill_opacity=0.5, |
|
weight=0, |
|
popup=f"{column_name}: {row[column_name]:.2f}" |
|
).add_to(heat_layer) |
|
|
|
heat_layer.add_to(map_obj) |
|
|
|
|
|
def calculate_distance(lat1, lon1, lat2, lon2): |
|
coords_1 = (lat1, lon1) |
|
coords_2 = (lat2, lon2) |
|
return geodesic(coords_1, coords_2).meters |
|
|
|
def knn_predict(df, target_column, features_columns, k=5): |
|
|
|
X = df[features_columns] |
|
y = df[target_column] |
|
|
|
|
|
if len(X) < k: |
|
return np.zeros(len(X)) |
|
|
|
|
|
knn = KNeighborsRegressor(n_neighbors=k) |
|
|
|
|
|
knn.fit(X, y) |
|
|
|
|
|
predictions = knn.predict(df[features_columns]) |
|
|
|
return predictions |
|
|
|
|
|
st.set_page_config(layout="wide") |
|
|
|
|
|
data = pd.read_excel('data_nexus.xlsx') |
|
|
|
|
|
radius_visible = True |
|
custom_address_initial = 'Centro, Lajeado - RS, Brazil' |
|
|
|
custom_lat = -29.45880114339262 |
|
|
|
custom_lon = -51.97011580843118 |
|
radius_in_meters = 150000 |
|
filtered_data = data |
|
|
|
|
|
zoom_level = 13 |
|
|
|
font_url = "https://fonts.googleapis.com/css2?family=Quicksand:wght@300..700&display=swap" |
|
|
|
title_html = f""" |
|
<style> |
|
@import url('{font_url}'); |
|
h1 {{ |
|
font-family: 'Quicksand', sans-serif; |
|
}} |
|
</style> |
|
<span style='color: #566f71; font-size: 50px;'>aval</span> |
|
<span style='color: #edb600; font-size: 50px;'>ia</span> |
|
<span style='color: #566f71; font-size: 50px;'>.se</span> |
|
""" |
|
|
|
|
|
with st.sidebar: |
|
st.image(image1, width=200) |
|
|
|
|
|
|
|
selected_fonte = st.selectbox('Finalidade', data['Fonte'].unique(), index=data['Fonte'].unique().tolist().index('Venda')) |
|
data = data[data['Fonte'] == selected_fonte] |
|
|
|
|
|
selected_tipo = st.selectbox('Tipo de imóvel', data['Tipo'].unique(), index=data['Tipo'].unique().tolist().index('Apartamento')) |
|
data_tipo = data[data['Tipo'] == selected_tipo] |
|
|
|
custom_address = st.text_input('Informe o endereço', custom_address_initial) |
|
radius_visible = True |
|
|
|
gmaps = googlemaps.Client(key='AIzaSyDoJ6C7NE2CHqFcaHTnhreOfgJeTk4uSH0') |
|
|
|
try: |
|
|
|
custom_address = custom_address.strip() |
|
if not custom_address.endswith(" - RS, Brazil"): |
|
custom_address += " - RS, Brazil" |
|
|
|
location = gmaps.geocode(custom_address)[0]['geometry']['location'] |
|
custom_lat, custom_lon = location['lat'], location['lng'] |
|
except (IndexError, GeocoderTimedOut): |
|
st.error("Erro: Não foi possível geocodificar o endereço fornecido. Por favor, verifique e tente novamente.") |
|
|
|
|
|
if radius_visible: |
|
radius_in_meters = st.number_input('Selecione raio (em metros)', min_value=0, max_value=100000, value=2000) |
|
|
|
|
|
|
|
|
|
|
|
|
|
col1, col2 = st.columns(2) |
|
with col1: |
|
atotal_min = st.number_input('Área Total mínima', |
|
min_value=float(data_tipo['Atotal'].min()), |
|
max_value=float(data_tipo['Atotal'].max()), |
|
value=float(data_tipo['Atotal'].min()), |
|
step=0.1) |
|
with col2: |
|
atotal_max = st.number_input('Área Total máxima', |
|
min_value=float(data_tipo['Atotal'].min()), |
|
max_value=float(data_tipo['Atotal'].max()), |
|
value=float(data_tipo['Atotal'].max()), |
|
step=0.1) |
|
|
|
|
|
col3, col4 = st.columns(2) |
|
with col3: |
|
apriv_min = st.number_input('Área Privativa mínima', |
|
min_value=float(data_tipo['Apriv'].min()), |
|
max_value=float(data_tipo['Apriv'].max()), |
|
value=float(data_tipo['Apriv'].min()), |
|
step=0.1) |
|
with col4: |
|
apriv_max = st.number_input('Área Privativa máxima', |
|
min_value=float(data_tipo['Apriv'].min()), |
|
max_value=float(data_tipo['Apriv'].max()), |
|
value=float(data_tipo['Apriv'].max()), |
|
step=0.1) |
|
|
|
|
|
|
|
|
|
|
|
data_tipo = data_tipo[(data_tipo['Atotal'].between(atotal_min, atotal_max)) & |
|
(data_tipo['Apriv'].between(apriv_min, apriv_max))] |
|
|
|
|
|
|
|
|
|
|
|
filtered_data = data_tipo[data_tipo.apply(lambda x: calculate_distance(x['latitude'], x['longitude'], custom_lat, custom_lon), axis=1) <= radius_in_meters] |
|
filtered_data = filtered_data.dropna() |
|
|
|
|
|
st.markdown(f"""<style> |
|
.map {{ |
|
width: 100%; |
|
height: 100vh; |
|
}} |
|
</style>""", unsafe_allow_html=True) |
|
|
|
|
|
filtered_data['area_feature'] = np.where(filtered_data['Apriv'] != 0, filtered_data['Apriv'], filtered_data['Atotal']) |
|
|
|
|
|
filtered_data['target_column'] = np.where(filtered_data['Vunit_priv'] != 0, filtered_data['Vunit_priv'], filtered_data['Vunit_total']) |
|
|
|
|
|
predicted_target = knn_predict(filtered_data, 'target_column', ['latitude', 'longitude', 'area_feature']) |
|
|
|
|
|
filtered_data['Predicted_target'] = predicted_target |
|
|
|
|
|
with st.container(): |
|
|
|
view_state = pdk.ViewState(latitude=filtered_data['latitude'].mean(), longitude=filtered_data['longitude'].mean(), zoom=zoom_level) |
|
|
|
|
|
layer = pdk.Layer( |
|
"ScatterplotLayer", |
|
filtered_data, |
|
get_position=["longitude", "latitude"], |
|
get_color="[237, 181, 0, 160]", |
|
get_radius=100, |
|
) |
|
|
|
|
|
deck_map = pdk.Deck(layers=[layer], initial_view_state=view_state, map_style="mapbox://styles/mapbox/light-v9") |
|
|
|
|
|
st.pydeck_chart(deck_map) |
|
|
|
st.write("Dados:", filtered_data) |
|
|
|
if st.button('Baixar planilha'): |
|
st.write("Preparando...") |
|
|
|
output_df = filtered_data |
|
|
|
|
|
excel_buffer = BytesIO() |
|
|
|
|
|
with pd.ExcelWriter(excel_buffer, engine="xlsxwriter") as writer: |
|
output_df.to_excel(writer, index=False, sheet_name="Sheet1") |
|
|
|
|
|
excel_buffer.seek(0) |
|
|
|
|
|
b64 = base64.b64encode(excel_buffer.read()).decode() |
|
href = f'<a href="data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,{b64}" download="sample_data.xlsx">Clique aqui para baixar a planilha</a>' |
|
|
|
|
|
|
|
download_placeholder = st.empty() |
|
download_placeholder.markdown(href, unsafe_allow_html=True) |
|
|
|
|
|
folium_layermap = folium.Map(location=[custom_lat, custom_lon], tiles="Cartodb Positron", zoom_start=14) |
|
|
|
|
|
add_heatmap_layer(folium_layermap, filtered_data, 'Valor_Urb', 'RdBu_r') |
|
add_heatmap_layer(folium_layermap, filtered_data, 'Valor_Eqp', 'RdBu_r') |
|
add_heatmap_layer(folium_layermap, filtered_data, 'RENDA', 'RdBu_r') |
|
|
|
|
|
folium.LayerControl().add_to(folium_layermap) |
|
|
|
|
|
st_folium(folium_layermap, width=1200, height=400) |
|
|
|
k_threshold = 5 |
|
|
|
|
|
def bootstrap_stats(bound_data, num_samples=1000): |
|
|
|
bound_data = np.array(bound_data).reshape(-1, 1) |
|
|
|
|
|
bootstrapped_means = [] |
|
for _ in range(num_samples): |
|
bootstrap_sample = np.random.choice(bound_data.flatten(), len(bound_data), replace=True) |
|
bootstrapped_means.append(np.mean(bootstrap_sample)) |
|
|
|
|
|
lower_bound = np.percentile(bootstrapped_means, 16.) |
|
higher_bound = np.percentile(bootstrapped_means, 84.) |
|
|
|
return lower_bound, higher_bound |
|
|
|
|
|
predicted_target = knn_predict(filtered_data, 'Predicted_target', ['latitude', 'longitude', 'area_feature']) |
|
|
|
|
|
if 'Predicted_target' in filtered_data.columns and not np.all(predicted_target == 0): |
|
|
|
|
|
lower_bound, higher_bound = bootstrap_stats(filtered_data['target_column']) |
|
|
|
mean_value = np.mean(filtered_data['Predicted_target']) |
|
|
|
|
|
st.markdown("## **Algoritmo KNN (K-nearest neighbors)**") |
|
st.write(f"Valor médio (Reais/m²) para as características selecionadas: ${mean_value:.2f}$ Reais") |
|
st.write(f"Os valores podem variar entre ${lower_bound:.2f}$ e ${higher_bound:.2f}$ Reais, dependendo das características dos imóveis.") |
|
else: |
|
st.warning(f"**Dados insuficientes para inferência do valor. Mínimo necessário:** {k_threshold}") |