File size: 11,733 Bytes
bbe788d
 
 
897ea2d
bbe788d
9fb5d19
 
 
ea90ced
eb69c0e
 
 
 
 
 
a580030
 
 
 
 
0bc1a23
 
 
 
a580030
 
 
 
 
 
0bc1a23
a580030
 
 
 
0697975
 
 
 
 
 
 
f2e7a61
9e1bee4
4324c06
cbc8d85
0697975
 
 
994bcfa
0697975
 
 
 
 
 
 
 
 
 
 
 
f573577
bbe788d
63a0d92
bbe788d
a580030
 
38a9e29
ff1b036
 
2737b30
a580030
bbe788d
81d3eef
6c40417
81d3eef
5cdc637
02c8225
f573577
9e1bee4
 
 
5cdc637
 
 
 
de141c7
 
 
f573577
02c8225
5cdc637
703e3e3
5cdc637
9e1bee4
 
 
5cdc637
 
 
 
fac9697
af3bf18
 
 
703e3e3
 
5cdc637
703e3e3
5cdc637
9e1bee4
 
 
5cdc637
 
 
 
fac9697
af3bf18
a998bd1
 
99d8e2d
5cdc637
703e3e3
6eac004
 
ab64c26
 
b5290a2
 
ab64c26
f573577
2f0dc3c
9837004
2f0dc3c
 
f573577
2f0dc3c
db72775
02c8225
6c40417
 
9fb5d19
 
 
 
 
 
 
 
ba96f19
9fb5d19
b5290a2
6c40417
bbe788d
6262455
 
58b3cb4
bbe788d
6eac004
72d1967
502ed02
72d1967
84326e9
72d1967
1a07133
84248e4
 
72d1967
db72775
13ffc08
84326e9
502ed02
db72775
13ffc08
84326e9
 
6eac004
 
 
 
 
 
db72775
 
 
 
1a07133
 
 
 
 
db72775
 
 
84326e9
 
 
db72775
 
1a07133
ab64c26
 
 
1a07133
6c40417
 
fd31ef9
efca6f0
 
 
 
 
 
 
 
38a9e29
9c4dcb3
38a9e29
87c4391
 
 
 
 
 
 
 
897ea2d
4e8c737
897ea2d
6c40417
 
d9189c2
2c61227
 
02052b7
 
 
 
 
 
 
 
 
 
 
 
706edaf
 
02052b7
 
 
 
 
 
0bc1a23
02052b7
f96846f
0bc1a23
 
f96846f
 
 
02052b7
 
 
c903a84
bc7b20c
4eb55de
 
d9189c2
b7d2bd1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
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

    
# Function to calculate distance in meters between two coordinates
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):
    # Separate features and target variable
    X = df[features_columns]
    y = df[target_column]

    # Check if there is enough data for prediction
    if len(X) < k:
        return np.zeros(len(X))  # Return an array of zeros if there isn't enough data

    # Create KNN regressor
    knn = KNeighborsRegressor(n_neighbors=k)

    # Fit the model
    knn.fit(X, y)

    # Use the model to predict target_column for the filtered_data
    predictions = knn.predict(df[features_columns])

    return predictions

# Set wide mode
st.set_page_config(layout="wide")

# Set dark theme
st.markdown(
    """
    <style>
        @font-face {font-family: 'Quicksand';
        src: url('font/Quicksand-VariableFont_wght.ttf') format('truetype');

        }
        body {
            color: white;
            background-color: #1e1e1e;
            font-family: 'Quicksand', sans-serif;
        }
        .st-df-header, .st-df-body, .st-df-caption {
            color: #f8f9fa;  /* Bootstrap table header text color */
        }
        .st-eb {
            background-color: #343a40;  /* Streamlit exception box background color */
        }
    </style>
    """,
    unsafe_allow_html=True
)


# Create a DataFrame with sample data
data = pd.read_excel('data_nexus.xlsx')

# Initialize variables to avoid NameError
radius_visible = True
custom_address_initial = 'Centro, Lajeado - RS, Brazil'  # Initial custom address
custom_lat = data['latitude'].median()
custom_lon = data['longitude'].median()
radius_in_meters = 1500
filtered_data = data  # Initialize with the entire dataset

# Calculate a zoom level based on the maximum distance
zoom_level = 14

# Set font to 'Quicksand' for title_html
title_html = """
        <style>
        @font-face {font-family: 'Quicksand';
        src: url('font/Quicksand-VariableFont_wght.ttf') format('truetype');
        }
        body {{
            font-family: 'Quicksand', sans-serif;
        }}
    </style>
    <span style='color: gray; font-size: 50px;'>aval</span>
    <span style='color: white; font-size: 50px;'>ia</span>
    <span style='color: gray; font-size: 50px;'>.NEXUS</span>
    """

# Set font to 'Quicksand' for factor_html
factor_html = """
    <style>
        @font-face {font-family: 'Quicksand';
        src: url('font/Quicksand-VariableFont_wght.ttf') format('truetype');
        }
        body {{
            font-family: 'Quicksand', sans-serif;
        }}
    </style>
    <a href='https://huggingface.co/spaces/DavidSB/avaliaFACTOR' target='_blank' style='text-decoration: none; color: inherit;'>
    <span style='color: gray; font-size: 20px;'>aval</span>
    <span style='color: white; font-size: 20px;'>ia</span>
    <span style='color: gray; font-size: 20px;'>.FACTOR</span>
"""

# Set font to 'Quicksand' for evo_html
evo_html = """
    <style>
        @font-face {font-family: 'Quicksand';
        src: url('font/Quicksand-VariableFont_wght.ttf') format('truetype');
        }
        body {{
            font-family: 'Quicksand', sans-serif;
        }}
    </style>
    <a href='https://huggingface.co/spaces/DavidSB/avalia.EVO' target='_blank' style='text-decoration: none; color: inherit;'>
    <span style='color: gray; font-size: 20px;'>aval</span>
    <span style='color: white; font-size: 20px;'>ia</span>
    <span style='color: gray; font-size: 20px;'>.EVO</span>
"""


import streamlit as st

import streamlit as st

# Create a sidebar for controls
with st.sidebar:
    st.markdown(title_html, unsafe_allow_html=True)

    # Add a dropdown for filtering "Fonte"
    selected_fonte = st.selectbox('Finalidade', data['Fonte'].unique())
    data = data[data['Fonte'] == selected_fonte]

    # Add a dropdown for filtering "Tipo"
    selected_tipo = st.selectbox('Tipo de imóvel', data['Tipo'].unique())
    data_tipo = data[data['Tipo'] == selected_tipo]
    
    custom_address = st.text_input('Informe o endereço', custom_address_initial)
    radius_visible = True  # Show radius slider for custom coordinates

    # Geocode the custom address using the Google Maps API
    gmaps = googlemaps.Client(key='AIzaSyDoJ6C7NE2CHqFcaHTnhreOfgJeTk4uSH0')  # Replace with your API key

    try:
        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.")

    # Slider for setting the zoom level
    zoom_level = st.slider('Nível de zoom', min_value=1, max_value=15, value=zoom_level)

    # Conditionally render the radius slider
    if radius_visible:
        radius_in_meters = st.slider('Selecione raio (em metros)', min_value=100, max_value=20000, value=2000, step=500)

    # Initialize sliders variables
    dorm_range = (int(data_tipo['Dorm'].min()), int(data_tipo['Dorm'].max()))
    banho_range = (int(data_tipo['Banh'].min()), int(data_tipo['Banh'].max()))
    vaga_range = (int(data_tipo['Vaga'].min()), int(data_tipo['Vaga'].max()))
    test_range = (int(data_tipo['Test'].min()), int(data_tipo['Test'].max()))

    # Add sliders to filter data based
    atotal_range = st.slider('Área Total', float(data_tipo['Atotal'].min()), float(data_tipo['Atotal'].max()), (float(data_tipo['Atotal'].min()), float(data_tipo['Atotal'].max())), step=.1 if data_tipo['Atotal'].min() != data_tipo['Atotal'].max() else 0.1)
    apriv_range = st.slider('Área Privativa', float(data_tipo['Apriv'].min()), float(data_tipo['Apriv'].max()), (float(data_tipo['Apriv'].min()), float(data_tipo['Apriv'].max())), step=.1 if data_tipo['Apriv'].min() != data_tipo['Apriv'].max() else 0.1)

    if int(data_tipo['Dorm'].min()) != 0 and int(data_tipo['Dorm'].max()) != 0:
        dorm_range = st.slider('Dormitórios', int(data_tipo['Dorm'].min()), int(data_tipo['Dorm'].max()), (int(data_tipo['Dorm'].min()), int(data_tipo['Dorm'].max())), step=1 if data_tipo['Dorm'].min() != data_tipo['Dorm'].max() else 1)
    if int(data_tipo['Banh'].min()) != 0 and int(data_tipo['Banh'].max()) != 0:
        banho_range = st.slider('Banheiros', int(data_tipo['Banh'].min()), int(data_tipo['Banh'].max()), (int(data_tipo['Banh'].min()), int(data_tipo['Banh'].max())), step=1 if data_tipo['Banh'].min() != data_tipo['Banh'].max() else 1)
    if int(data_tipo['Vaga'].min()) != 0 and int(data_tipo['Vaga'].max()) != 0:
        vaga_range = st.slider('Vaga de estacionamento', int(data_tipo['Vaga'].min()), int(data_tipo['Vaga'].max()), (int(data_tipo['Vaga'].min()), int(data_tipo['Vaga'].max())), step=1 if data_tipo['Vaga'].min() != data_tipo['Vaga'].max() else 1)
    if int(data_tipo['Test'].min()) != 0 and int(data_tipo['Test'].max()) != 0:
        test_range = st.slider('Testada', int(data_tipo['Test'].min()), int(data_tipo['Test'].max()), (int(data_tipo['Test'].min()), int(data_tipo['Test'].max())), step=1 if data_tipo['Test'].min() != data_tipo['Test'].max() else 1)
    
    # Initialize checkbox variables
    elev_checkbox = False
    esq_checkbox = False
        
    # Add checkboxes for dummy features
    if int(data_tipo['Elevador'].min()) != 0 and int(data_tipo['Elevador'].max()) != 0:
        elev_checkbox = st.checkbox('Elevador')
    if int(data_tipo['Lot_pos'].min()) != 0 and int(data_tipo['Lot_pos'].max()) != 0:
        esq_checkbox = st.checkbox('Duas ou mais frentes')

    # Transform checkbox values into 1s and 0s
    elev_value = 1 if elev_checkbox else 0
    esq_value = 1 if esq_checkbox else 0

    data_tipo = data_tipo[(data_tipo['Atotal'].between(atotal_range[0], atotal_range[1])) &
            (data_tipo['Apriv'].between(apriv_range[0], apriv_range[1])) &
            (data_tipo['Dorm'].between(dorm_range[0], dorm_range[1])) &
            (data_tipo['Banh'].between(banho_range[0], banho_range[1])) &
            (data_tipo['Vaga'].between(vaga_range[0], vaga_range[1])) &
            (data_tipo['Test'].between(test_range[0], test_range[1])) &
            (data_tipo['Elevador'] == elev_value) &
            (data_tipo['Lot_pos'] == esq_value)]

# Links to other apps at the bottom of the sidebar
st.sidebar.markdown(factor_html, unsafe_allow_html=True)
st.sidebar.markdown(evo_html, unsafe_allow_html=True)

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()  # Drop rows with NaN values

# Add a custom CSS class to the map container
st.markdown(f"""<style>
.map {{
  width: 100%;
  height: 100vh;
}}
</style>""", unsafe_allow_html=True)

# Determine which area feature to use for prediction
filtered_data['area_feature'] = np.where(filtered_data['Apriv'] != 0, filtered_data['Apriv'], filtered_data['Atotal'])

# Define the target column based on conditions
filtered_data['target_column'] = np.where(filtered_data['Vunit_priv'] != 0, filtered_data['Vunit_priv'], filtered_data['Vunit_total'])

# Apply KNN and get predicted target values
predicted_target = knn_predict(filtered_data, 'target_column', ['latitude', 'longitude', 'area_feature'])  # Update with your features

# Add predicted target values to filtered_data
filtered_data['Predicted_target'] = predicted_target

# Display the map and filtered_data
with st.container():
    st.map(filtered_data, zoom=zoom_level, use_container_width=True)
    st.write("Dados:", filtered_data)  # Debug: Print filtered_data

k_threshold = 5

# Function to perform bootstrap on the predicted target values
def bootstrap_stats(predicted_target, num_samples=1000):
    # Reshape the predicted_target array
    predicted_target = np.array(predicted_target).reshape(-1, 1)

    # Bootstrap resampling
    bootstrapped_means = []
    for _ in range(num_samples):
        bootstrap_sample = np.random.choice(predicted_target.flatten(), len(predicted_target), replace=True)
        bootstrapped_means.append(np.mean(bootstrap_sample))

    # Calculate lower and higher bounds
    lower_bound = np.percentile(bootstrapped_means, 5.)
    higher_bound = np.percentile(bootstrapped_means, 95.)

    # Calculate the mean value
    mean_value = np.mean(bootstrapped_means)

    return lower_bound, higher_bound, mean_value

# Apply KNN and get predicted Predicted_target values
predicted_target = knn_predict(filtered_data, 'Predicted_target', ['latitude', 'longitude', 'area_feature'])

# Check if there are predictions to display
if 'Predicted_target' in filtered_data.columns and not np.all(predicted_target == 0):
    # Add predicted Predicted_target values to filtered_data
    filtered_data['Predicted_target'] = predicted_target

    # Apply bootstrap on the predicted values
    lower_bound, higher_bound, mean_value = bootstrap_stats(predicted_target)

    # Display the results with custom styling
    st.markdown("## **Resultado da Análise Estatística**")
    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}")