- Published on
Simulación y Análisis de Datos de un Hotel con Instagres, Python y Power BI
- Authors
- Name
- Jordan Rodriguez
Simulación de flujo de atención de un hospedaje con Python y Power BI
Este tutorial, pensado para un perfil de Data Scientist experto, cubre todo el proceso: desde el aprovisionamiento rápido de la base de datos efímera en Instagres.com, el modelado de tablas (con diagrama ER), la instalación de librerías, la definición y ejecución de funciones Python para generar y cargar 2 años de histórico de un hotel con 1 000 clientes, hasta la conexión final con Power BI.
1. Creación de la base de datos efímera en Instagres.com
Instagres.com permite aprovisionar en segundos una base de datos PostgreSQL que vive sólo 1 hora, con un solo clic y sin configuración adicional.
- Visita https://www.instagres.com
- Haz clic en New
- ¡Listo! Instagres crea tu BD efímera (1 hora de vida) y muestra la cadena de conexión.
# Ejemplo de Connection String generada automáticamente:
CONN_STR="postgresql://neondb_owner:npg_mL4hapw1PAIe@ep-round-glade-a2o8ygz2.eu-central-1.aws.neon.tech/neondb?sslmode=require"
2. Modelado del esquema de datos
Diseñamos un modelo relacional con tres tablas:
- clients: Datos sociodemográficos y nivel de lealtad.
- rooms: 30 habitaciones en 4 pisos, con tipo, capacidad y precio.
- stays: Historial de estancias (check-in, check-out, importe total).
Diagrama ER
erDiagram
CLIENTS ||--o{ STAYS : has
ROOMS ||--o{ STAYS : has
CLIENTS {
int client_id PK
string first_name
string last_name
string gender
date birth_date
string email
string phone
string nationality
string loyalty_level
}
ROOMS {
int room_id PK
int floor
string room_number
string room_type
int capacity
decimal price_per_night
}
STAYS {
int stay_id PK
int client_id FK
int room_id FK
date check_in_date
date check_out_date
decimal total_amount
}
3. Instalación de librerías Python
!pip install psycopg2-binary faker numpy pandas
4. Generación, simulación e ingestión de datos
A continuación el script completo, que incluye:
- Conexión y creación de tablas
- Generación de 1 000 clientes con Faker
- Generación de 30 habitaciones
- Simulación de 2 años de estancias (μ=15 llegadas/día, σ=12; estancia ~ exponencial(λ=1/3))
- Ingestión masiva en batches
- Validación de carga
- Impresión automática de parámetros para Power BI
import psycopg2
import psycopg2.extras
import numpy as np
import pandas as pd
import random
from datetime import datetime, timedelta
from faker import Faker
from urllib.parse import urlparse, parse_qs
# — Parámetros de simulación —
CONN_STR = "postgresql://usuario:password@host:puerto/hotel_demo?sslmode=require"
N_CLIENTS = 1000
START_DATE = datetime.today() - timedelta(days=365*2)
END_DATE = datetime.today()
MEAN_DAILY = 15
STD_DAILY = 12
MEAN_STAY = 3
fake = Faker("es_ES")
random.seed(42)
np.random.seed(42)
def connect_db():
return psycopg2.connect(CONN_STR)
def create_tables(conn):
cur = conn.cursor()
cur.execute(\"\"\"
CREATE TABLE IF NOT EXISTS clients (
client_id SERIAL PRIMARY KEY,
first_name VARCHAR(50),
last_name VARCHAR(50),
gender VARCHAR(10),
birth_date DATE,
email VARCHAR(100),
phone VARCHAR(20),
nationality VARCHAR(50),
loyalty_level VARCHAR(20)
);
CREATE TABLE IF NOT EXISTS rooms (
room_id SERIAL PRIMARY KEY,
floor INTEGER,
room_number VARCHAR(4),
room_type VARCHAR(10),
capacity INTEGER,
price_per_night NUMERIC(8,2)
);
CREATE TABLE IF NOT EXISTS stays (
stay_id SERIAL PRIMARY KEY,
client_id INTEGER REFERENCES clients(client_id),
room_id INTEGER REFERENCES rooms(room_id),
check_in_date DATE,
check_out_date DATE,
total_amount NUMERIC(10,2)
);
\"\"\")
conn.commit()
cur.close()
def generate_clients(n=N_CLIENTS):
records = []
for _ in range(n):
gender = random.choice(['Male','Female'])
first = fake.first_name_male() if gender=='Male' else fake.first_name_female()
last = fake.last_name()
birth = fake.date_between(start_date='-70y', end_date='-18y')
records.append((first, last, gender, birth,
fake.ascii_free_email(), fake.phone_number(),
fake.country(), random.choice(['Bronze','Silver','Gold','Platinum'])))
return pd.DataFrame(records, columns=[
'first_name','last_name','gender','birth_date',
'email','phone','nationality','loyalty_level'
])
def generate_rooms():
floor_sizes = [8,8,7,7]
types = ['Single','Double','Suite']
caps = {'Single':1,'Double':2,'Suite':4}
prices = {'Single':80.0,'Double':120.0,'Suite':250.0}
records = []
for floor, count in enumerate(floor_sizes, start=1):
for num in range(1, count+1):
rt = random.choice(types)
records.append((floor, f"{floor}{str(num).zfill(2)}", rt, caps[rt], prices[rt]))
return pd.DataFrame(records, columns=[
'floor','room_number','room_type','capacity','price_per_night'
])
def generate_stays(clients_df, rooms_df):
records = []
client_ids = clients_df.index + 1
room_ids = rooms_df.index + 1
current = START_DATE
while current <= END_DATE:
k = max(0, int(np.random.normal(MEAN_DAILY, STD_DAILY)))
for _ in range(k):
c = random.choice(client_ids)
length = max(1, int(np.random.exponential(MEAN_STAY)))
ci = current
co = current + timedelta(days=length)
r = random.choice(room_ids)
price = rooms_df.loc[r-1, 'price_per_night']
total = round(price * length, 2)
records.append((c, r, ci.date(), co.date(), total))
current += timedelta(days=1)
return pd.DataFrame(records, columns=[
'client_id','room_id','check_in_date','check_out_date','total_amount'
])
def ingest_df(conn, df, table):
cols = ",".join(df.columns)
vals = ",".join([f"%({c})s" for c in df.columns])
query = f"INSERT INTO {table} ({cols}) VALUES ({vals})"
cur = conn.cursor()
psycopg2.extras.execute_batch(cur, query, df.to_dict('records'), page_size=500)
conn.commit()
cur.close()
def check_data_exists(conn):
for tbl in ['clients','rooms','stays']:
cur = conn.cursor()
cur.execute(f"SELECT COUNT(*) FROM {tbl}")
cnt = cur.fetchone()[0]
status = "✅ OK" if cnt>0 else "❌ VACÍA"
print(f"{tbl:10s}: {cnt:6d} rows → {status}")
cur.close()
def parse_conn_str(conn_str: str) -> dict:
parsed = urlparse(conn_str)
params = {
'host': parsed.hostname,
'port': parsed.port or 5432,
'database': parsed.path.lstrip('/'),
'user': parsed.username,
'password': parsed.password,
}
qp = parse_qs(parsed.query)
if 'sslmode' in qp:
params['sslmode'] = qp['sslmode'][0]
return params
def print_powerbi_params(conn_str: str):
params = parse_conn_str(conn_str)
print("\n# Parámetros para Power BI")
for key, val in params.items():
print(f"{key}={val}")
def main():
conn = connect_db()
create_tables(conn)
clients = generate_clients()
rooms = generate_rooms()
stays = generate_stays(clients, rooms)
ingest_df(conn, clients, 'clients')
ingest_df(conn, rooms, 'rooms')
ingest_df(conn, stays, 'stays')
check_data_exists(conn)
print_powerbi_params(CONN_STR)
conn.close()
if __name__ == '__main__':
main()
5. Ejecución
En tu entorno (Colab, terminal o IDE):
python hotel_simulation.py
Deberías ver algo como:
clients : 1000 rows → ✅ OK
rooms : 30 rows → ✅ OK
stays : ~11000 rows → ✅ OK
# Parámetros para Power BI
host=ep-round-glade-a2o8ygz2.eu-central-1.aws.neon.tech
port=5432
database=neondb
user=neondb_owner
password=npg_mL4hapw1PAIe
sslmode=require
6. Conexión desde Power BI
Para Windows, asegúrate de instalar primero el conector de PostgreSQL: Link descarga
Reinicia Power BI Desktop tras la instalación.
- Abre Power BI Desktop → Obtener datos → Bases de datos → PostgreSQL.
- Servidor:
<host_de_Instagres>
- Base de datos:
<database_name>
- Modo: Importar o DirectQuery.
- Credenciales: Básico → usuario y contraseña de Instagres.
- SSL: Marca Requerir cifrado (SSL).
- Selecciona las tablas y pulsa Cargar.
¡Ya estás listo para construir dashboards de ocupación, ADR y LTV con tus datos simulados!