hedtorresca commited on
Commit
22c3db0
Β·
verified Β·
1 Parent(s): d52c998

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +85 -70
app.py CHANGED
@@ -1,95 +1,111 @@
1
- ################################################################################
2
- # DASHBOARD BANCAMÍA – Q1-2025 Β· Gradio v4
3
- ################################################################################
4
- import os, json, pathlib, datetime as dt
5
  import pandas as pd
6
  import plotly.express as px
7
  import gradio as gr
8
- from geopy.geocoders import Nominatim
9
- from geopy.extra.rate_limiter import RateLimiter
10
-
11
- DATA_XLSX = "Base de Datos Prueba.xlsx" # <- ponlo junto a app.py
12
- COORDS_JSON = "office_coords.json" # cachΓ© de coordenadas
13
 
14
  # ──────────────────────────────────────────────────────────────────────────────
15
- # 1. Cargar datos
16
  # ──────────────────────────────────────────────────────────────────────────────
 
17
  df = pd.read_excel(DATA_XLSX, parse_dates=["FECHA_APERTURA"])
18
  df["MES"] = df["FECHA_APERTURA"].dt.to_period("M").dt.to_timestamp()
19
- oficinas = sorted(df["OFICINA"].dropna().unique().tolist())
20
- productos = sorted(df["TIPO PRODUCTO"].dropna().unique().tolist())
21
  min_amt, max_amt = int(df["MONTO_I"].min()), int(df["MONTO_I"].max())
22
 
23
  # ──────────────────────────────────────────────────────────────────────────────
24
- # 2. GeorreferenciaciΓ³n (1Βͺ vez usa Nominatim, luego lee cachΓ©)
25
  # ──────────────────────────────────────────────────────────────────────────────
26
- if pathlib.Path(COORDS_JSON).is_file():
27
- office_coords = json.loads(open(COORDS_JSON).read())
28
- else:
29
- geolocator = Nominatim(user_agent="bancamia_dash")
30
- geocode = RateLimiter(geolocator.geocode, min_delay_seconds=1)
31
- office_coords = {}
32
- for ofi in oficinas:
33
- loc = geocode(f"{ofi}, Colombia")
34
- office_coords[ofi] = (loc.latitude, loc.longitude) if loc else (None, None)
35
- with open(COORDS_JSON, "w") as fh:
36
- json.dump(office_coords, fh)
 
 
 
 
 
 
 
 
 
 
 
37
 
38
  # ──────────────────────────────────────────────────────────────────────────────
39
- # 3. FunciΓ³n principal
40
  # ──────────────────────────────────────────────────────────────────────────────
41
- def dashboard(start_date, end_date, tipos, ofis, monto):
42
  d = df.copy()
43
- if start_date: d = d[d["FECHA_APERTURA"] >= pd.to_datetime(start_date)]
44
- if end_date: d = d[d["FECHA_APERTURA"] <= pd.to_datetime(end_date)]
45
- if tipos: d = d[d["TIPO PRODUCTO"].isin(tipos)]
46
- if ofis: d = d[d["OFICINA"].isin(ofis)]
47
- d = d[(d["MONTO_I"] >= monto[0]) & (d["MONTO_I"] <= monto[1])]
 
 
 
 
 
 
 
 
48
 
49
- # ── Fig 1: barras mensuales
50
- f1 = px.bar(d.groupby("MES")["MONTO_I"].sum().reset_index(),
51
- x="MES", y="MONTO_I",
52
- labels={"MES":"Mes","MONTO_I":"Monto (COP)"},
53
- title="Monto desembolsado por mes")
54
 
55
- # ── Fig 2: pie de distribuciΓ³n
56
- pie_df = d["TIPO PRODUCTO"].value_counts().reset_index()
57
- pie_df.columns = ["TIPO PRODUCTO","CANT"]
58
- f2 = px.pie(pie_df, names="TIPO PRODUCTO", values="CANT",
59
- title="DistribuciΓ³n por producto")
 
60
 
61
- # ── Fig 3: boxplot tasas
62
- f3 = px.box(d, x="TIPO PRODUCTO", y="TASA",
63
- labels={"TASA":"Tasa (%)","TIPO PRODUCTO":"Producto"},
64
- title="Tasas de interΓ©s")
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
- # ── Fig 4: mapa
67
- g = d.copy()
68
- g[["lat","lon"]] = g["OFICINA"].apply(
69
- lambda x: pd.Series(office_coords.get(x,(None,None))))
70
- g = g.dropna(subset=["lat","lon"])
71
- f4 = px.scatter_mapbox(g, lat="lat", lon="lon",
72
- color="TIPO PRODUCTO", size="MONTO_I", size_max=15,
73
- hover_name="OFICINA", zoom=4,
74
- mapbox_style="open-street-map",
75
- title="Mapa de aperturas")
76
- return f1, f2, f3, f4
77
 
78
  # ──────────────────────────────────────────────────────────────────────────────
79
- # 4. Interfaz Gradio v4
80
  # ──────────────────────────────────────────────────────────────────────────────
81
- with gr.Blocks(theme=gr.themes.Monochrome()) as demo:
82
- gr.Markdown("### Dashboard BancamΓ­a β€” Apertura de Productos Q1-2025")
83
  with gr.Row():
84
  with gr.Column(scale=1):
85
- dp1 = gr.Date(label="Desde", value="2025-01-01")
86
- dp2 = gr.Date(label="Hasta", value="2025-03-31")
87
- dd1 = gr.Dropdown(tipos, multiselect=True, label="Producto")
88
- dd2 = gr.Dropdown(oficinas, multiselect=True, label="Oficina")
89
- rs = gr.RangeSlider(min_amt, max_amt,
90
- value=[min_amt, max_amt],
91
- label="Rango de monto (COP)")
92
- btn = gr.Button("Actualizar")
93
  with gr.Column(scale=3):
94
  with gr.Tab("Monto"):
95
  out1 = gr.Plot()
@@ -98,9 +114,8 @@ with gr.Blocks(theme=gr.themes.Monochrome()) as demo:
98
  with gr.Tab("Tasas"):
99
  out3 = gr.Plot()
100
  with gr.Tab("Mapa"):
101
- out4 = gr.Plot()
102
  btn.click(dashboard, [dp1, dp2, dd1, dd2, rs], [out1, out2, out3, out4])
103
 
104
  if __name__ == "__main__":
105
- demo.launch(server_name="0.0.0.0",
106
- server_port=int(os.environ.get("PORT", 7860)))
 
1
+ import os, json, pathlib
 
 
 
2
  import pandas as pd
3
  import plotly.express as px
4
  import gradio as gr
5
+ import folium
6
+ from folium.plugins import MarkerCluster
 
 
 
7
 
8
  # ──────────────────────────────────────────────────────────────────────────────
9
+ # 1. Cargar y preparar datos
10
  # ──────────────────────────────────────────────────────────────────────────────
11
+ DATA_XLSX = "Base de Datos Prueba.xlsx" # Debe estar junto a este script
12
  df = pd.read_excel(DATA_XLSX, parse_dates=["FECHA_APERTURA"])
13
  df["MES"] = df["FECHA_APERTURA"].dt.to_period("M").dt.to_timestamp()
14
+ oficinas = sorted(df["OFICINA"].dropna().unique())
15
+ productos = sorted(df["TIPO PRODUCTO"].dropna().unique())
16
  min_amt, max_amt = int(df["MONTO_I"].min()), int(df["MONTO_I"].max())
17
 
18
  # ──────────────────────────────────────────────────────────────────────────────
19
+ # 2. Coordenadas hard-coded de oficinas
20
  # ──────────────────────────────────────────────────────────────────────────────
21
+ office_coords = {
22
+ "MonterΓ­a Centro": (8.74733, -75.88145),
23
+ "Monteria": (8.74733, -75.88145),
24
+ "Bucaramanga Centro": (7.119349, -73.122741),
25
+ "Manizales": (5.068887, -75.51739),
26
+ "Caucasia": (7.98666, -75.18959),
27
+ "Pasto": (1.213607, -77.281104),
28
+ "Bello": (6.33734, -75.55835),
29
+ "Fusagasuga": (4.3370, -74.3544),
30
+ "Itagui": (6.1630, -75.6056),
31
+ "Sincelejo": (9.3040, -75.3978),
32
+ "Centro MedellΓ­n": (6.2442, -75.5812),
33
+ "Soacha": (4.5833, -74.2167),
34
+ "Principal": (4.7110, -74.0721),
35
+ "San Juan": (7.02093, -75.62913),
36
+ "Perdomo": (1.1906, -77.5803),
37
+ "Ibague": (4.4389, -75.2322),
38
+ "Santa Marta Av. Libertador": (11.24079, -74.19904),
39
+ "Valledupar": (10.45924, -73.25321),
40
+ "Funza": (4.7570, -74.1188),
41
+ "Buenos Aires": (2.28889, -76.14583),
42
+ }
43
 
44
  # ──────────────────────────────────────────────────────────────────────────────
45
+ # 3. FunciΓ³n principal para generar grΓ‘ficos + mapa Folium
46
  # ──────────────────────────────────────────────────────────────────────────────
47
+ def dashboard(f_inicio, f_fin, tipos, ofis, rango_monto):
48
  d = df.copy()
49
+ if f_inicio: d = d[d["FECHA_APERTURA"] >= pd.to_datetime(f_inicio)]
50
+ if f_fin: d = d[d["FECHA_APERTURA"] <= pd.to_datetime(f_fin)]
51
+ if tipos: d = d[d["TIPO PRODUCTO"].isin(tipos)]
52
+ if ofis: d = d[d["OFICINA"].isin(ofis)]
53
+ d = d[(d["MONTO_I"] >= rango_monto[0]) & (d["MONTO_I"] <= rango_monto[1])]
54
+
55
+ # GrΓ‘fico 1: barras mensuales
56
+ fig1 = px.bar(
57
+ d.groupby("MES")["MONTO_I"].sum().reset_index(),
58
+ x="MES", y="MONTO_I",
59
+ labels={"MES":"Mes", "MONTO_I":"Monto (COP)"},
60
+ title="Monto desembolsado por mes"
61
+ )
62
 
63
+ # GrΓ‘fico 2: pastel de productos
64
+ df2 = d["TIPO PRODUCTO"].value_counts().reset_index()
65
+ df2.columns = ["TIPO PRODUCTO","CANT"]
66
+ fig2 = px.pie(df2, names="TIPO PRODUCTO", values="CANT",
67
+ title="DistribuciΓ³n por tipo de producto")
68
 
69
+ # GrΓ‘fico 3: boxplot de tasas
70
+ fig3 = px.box(
71
+ d, x="TIPO PRODUCTO", y="TASA",
72
+ labels={"TASA":"Tasa (%)","TIPO PRODUCTO":"Producto"},
73
+ title="DistribuciΓ³n de tasas de interΓ©s"
74
+ )
75
 
76
+ # Mapa Folium
77
+ m = folium.Map(location=[4.6, -74.1], zoom_start=6, tiles="OpenStreetMap")
78
+ mc = MarkerCluster().add_to(m)
79
+ for ofi, (lat, lon) in office_coords.items():
80
+ if (lat is None) or (lon is None): continue
81
+ sub = d[d["OFICINA"] == ofi]
82
+ if sub.empty: continue
83
+ total = sub["MONTO_I"].sum()
84
+ folium.CircleMarker(
85
+ location=(lat, lon),
86
+ radius=7,
87
+ color="blue",
88
+ fill=True,
89
+ fill_opacity=0.6,
90
+ popup=f"{ofi}<br>Total: {total:,.0f} COP"
91
+ ).add_to(mc)
92
+ map_html = m._repr_html_()
93
 
94
+ return fig1, fig2, fig3, map_html
 
 
 
 
 
 
 
 
 
 
95
 
96
  # ──────────────────────────────────────────────────────────────────────────────
97
+ # 4. Interfaz Gradio v4 – pestaΓ±as + HTML para Folium
98
  # ──────────────────────────────────────────────────────────────────────────────
99
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
100
+ gr.Markdown("## Dashboard BancamΓ­a – Aperturas Q1 2025")
101
  with gr.Row():
102
  with gr.Column(scale=1):
103
+ dp1 = gr.Date(label="Desde", value="2025-01-01")
104
+ dp2 = gr.Date(label="Hasta", value="2025-03-31")
105
+ dd1 = gr.Dropdown(productos, multiselect=True, label="Tipo de Producto")
106
+ dd2 = gr.Dropdown(oficinas, multiselect=True, label="Oficina")
107
+ rs = gr.RangeSlider(min_amt, max_amt, value=[min_amt, max_amt], label="Monto (COP)")
108
+ btn = gr.Button("Actualizar")
 
 
109
  with gr.Column(scale=3):
110
  with gr.Tab("Monto"):
111
  out1 = gr.Plot()
 
114
  with gr.Tab("Tasas"):
115
  out3 = gr.Plot()
116
  with gr.Tab("Mapa"):
117
+ out4 = gr.HTML()
118
  btn.click(dashboard, [dp1, dp2, dd1, dd2, rs], [out1, out2, out3, out4])
119
 
120
  if __name__ == "__main__":
121
+ demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))