Skip to content

Instantly share code, notes, and snippets.

@gabanox
Created October 1, 2025 16:29
Show Gist options
  • Select an option

  • Save gabanox/0db12b3f7d1fe8cf078af8ae5474b3ed to your computer and use it in GitHub Desktop.

Select an option

Save gabanox/0db12b3f7d1fe8cf078af8ae5474b3ed to your computer and use it in GitHub Desktop.
Generador de Transacciones de Tarjetas de Crédito - Script Python para generar datos sintéticos con indicadores de fraude
"""
Generador de Transacciones de Tarjetas de Crédito
Genera datos sintéticos de transacciones para proyectos de machine learning
"""
import random
import pandas as pd
from datetime import datetime, timedelta
import json
class GeneradorTransaccionesTarjetas:
"""Clase para generar transacciones sintéticas de tarjetas de crédito"""
def __init__(self, num_registros=1000):
self.num_registros = num_registros
# Categorías de comercios
self.categorias = [
'Supermercado', 'Gasolina', 'Restaurante', 'Farmacia',
'Ropa', 'Electrónica', 'Entretenimiento', 'Viajes',
'Servicios', 'Telecomunicaciones', 'Educación', 'Salud'
]
# Comercios por categoría
self.comercios = {
'Supermercado': ['Walmart', 'Soriana', 'Chedraui', 'La Comer', 'Costco'],
'Gasolina': ['Pemex', 'BP', 'Shell', 'Mobil', 'Chevron'],
'Restaurante': ['Starbucks', 'McDonald\'s', 'Subway', 'Domino\'s', 'KFC', 'Italiannis'],
'Farmacia': ['Farmacias del Ahorro', 'Farmacia Guadalajara', 'Farmacias Similares', 'San Pablo'],
'Ropa': ['Zara', 'H&M', 'Liverpool', 'Palacio de Hierro', 'Coppel'],
'Electrónica': ['Best Buy', 'RadioShack', 'Office Depot', 'Amazon', 'Mercado Libre'],
'Entretenimiento': ['Cinépolis', 'Cinemex', 'Netflix', 'Spotify', 'Xbox Store'],
'Viajes': ['Volaris', 'Aeroméxico', 'Booking.com', 'Airbnb', 'Despegar'],
'Servicios': ['Uber', 'DiDi', 'Rappi', 'Cornershop', 'Izzi'],
'Telecomunicaciones': ['Telcel', 'AT&T', 'Movistar', 'Totalplay'],
'Educación': ['Udemy', 'Coursera', 'Amazon Web Services', 'LinkedIn Learning'],
'Salud': ['Hospital ABC', 'Laboratorio Chopo', 'Farmacias Similares', 'Dentegra']
}
# Ciudades de México
self.ciudades = [
'Ciudad de México', 'Guadalajara', 'Monterrey', 'Puebla',
'Tijuana', 'León', 'Querétaro', 'Mérida', 'Cancún', 'Toluca'
]
# Tipos de tarjeta
self.tipos_tarjeta = ['Visa', 'Mastercard', 'American Express']
def generar_numero_tarjeta(self, tipo):
"""Genera un número de tarjeta enmascarado"""
prefijos = {
'Visa': '4',
'Mastercard': '5',
'American Express': '37'
}
prefijo = prefijos[tipo]
return f"{prefijo}{'*' * 12}{random.randint(1000, 9999)}"
def generar_fecha_transaccion(self, fecha_inicio, fecha_fin):
"""Genera una fecha aleatoria entre dos fechas"""
delta = fecha_fin - fecha_inicio
random_days = random.randint(0, delta.days)
random_seconds = random.randint(0, 86400)
return fecha_inicio + timedelta(days=random_days, seconds=random_seconds)
def es_transaccion_fraudulenta(self, probabilidad=0.05):
"""Determina si una transacción es fraudulenta (5% por defecto)"""
return random.random() < probabilidad
def generar_monto(self, categoria, es_fraude):
"""Genera un monto basado en la categoría y si es fraude"""
# Rangos normales por categoría
rangos_normales = {
'Supermercado': (100, 3000),
'Gasolina': (200, 1500),
'Restaurante': (150, 2000),
'Farmacia': (50, 800),
'Ropa': (300, 5000),
'Electrónica': (500, 15000),
'Entretenimiento': (100, 1500),
'Viajes': (1000, 20000),
'Servicios': (50, 1000),
'Telecomunicaciones': (200, 800),
'Educación': (300, 5000),
'Salud': (500, 10000)
}
rango = rangos_normales.get(categoria, (100, 1000))
if es_fraude:
# Transacciones fraudulentas tienden a ser mayores
monto = random.uniform(rango[1] * 0.8, rango[1] * 3)
else:
monto = random.uniform(rango[0], rango[1])
return round(monto, 2)
def generar_transacciones(self):
"""Genera el conjunto de transacciones"""
transacciones = []
# Fechas para las transacciones (últimos 90 días)
fecha_fin = datetime.now()
fecha_inicio = fecha_fin - timedelta(days=90)
# Generar IDs de clientes (simular 200 clientes diferentes)
num_clientes = 200
clientes = [f"CLI_{str(i).zfill(6)}" for i in range(1, num_clientes + 1)]
for i in range(self.num_registros):
# Datos básicos
transaccion_id = f"TXN_{str(i+1).zfill(8)}"
cliente_id = random.choice(clientes)
tipo_tarjeta = random.choice(self.tipos_tarjeta)
numero_tarjeta = self.generar_numero_tarjeta(tipo_tarjeta)
# Categoría y comercio
categoria = random.choice(self.categorias)
comercio = random.choice(self.comercios[categoria])
# Fecha y hora
fecha_transaccion = self.generar_fecha_transaccion(fecha_inicio, fecha_fin)
# Determinar si es fraude
es_fraude = self.es_transaccion_fraudulenta()
# Monto
monto = self.generar_monto(categoria, es_fraude)
# Ubicación
ciudad = random.choice(self.ciudades)
# Hora del día (puede ser indicador de fraude)
hora = fecha_transaccion.hour
es_horario_inusual = hora < 6 or hora > 22
# Distancia desde la transacción anterior (simulada)
distancia_km = random.uniform(0, 500) if es_fraude else random.uniform(0, 50)
# Estado de la transacción
estado = 'Rechazada' if es_fraude and random.random() > 0.3 else 'Aprobada'
transaccion = {
'transaccion_id': transaccion_id,
'cliente_id': cliente_id,
'fecha': fecha_transaccion.strftime('%Y-%m-%d'),
'hora': fecha_transaccion.strftime('%H:%M:%S'),
'fecha_hora': fecha_transaccion.strftime('%Y-%m-%d %H:%M:%S'),
'tipo_tarjeta': tipo_tarjeta,
'numero_tarjeta': numero_tarjeta,
'comercio': comercio,
'categoria': categoria,
'monto': monto,
'ciudad': ciudad,
'distancia_km': round(distancia_km, 2),
'es_horario_inusual': es_horario_inusual,
'estado': estado,
'es_fraude': es_fraude
}
transacciones.append(transaccion)
return transacciones
def guardar_csv(self, transacciones, nombre_archivo='transacciones_tarjetas.csv'):
"""Guarda las transacciones en formato CSV"""
df = pd.DataFrame(transacciones)
df.to_csv(nombre_archivo, index=False, encoding='utf-8-sig')
print(f"✓ Archivo CSV guardado: {nombre_archivo}")
return df
def guardar_json(self, transacciones, nombre_archivo='transacciones_tarjetas.json'):
"""Guarda las transacciones en formato JSON"""
with open(nombre_archivo, 'w', encoding='utf-8') as f:
json.dump(transacciones, f, ensure_ascii=False, indent=2)
print(f"✓ Archivo JSON guardado: {nombre_archivo}")
def generar_estadisticas(self, df):
"""Genera estadísticas del conjunto de datos"""
print("\n" + "="*60)
print("ESTADÍSTICAS DE TRANSACCIONES GENERADAS")
print("="*60)
print(f"\nTotal de transacciones: {len(df)}")
print(f"Transacciones fraudulentas: {df['es_fraude'].sum()} ({df['es_fraude'].mean()*100:.2f}%)")
print(f"Transacciones aprobadas: {(df['estado'] == 'Aprobada').sum()}")
print(f"Transacciones rechazadas: {(df['estado'] == 'Rechazada').sum()}")
print(f"\nMonto total: ${df['monto'].sum():,.2f}")
print(f"Monto promedio: ${df['monto'].mean():,.2f}")
print(f"Monto mínimo: ${df['monto'].min():,.2f}")
print(f"Monto máximo: ${df['monto'].max():,.2f}")
print(f"\nClientes únicos: {df['cliente_id'].nunique()}")
print(f"Comercios únicos: {df['comercio'].nunique()}")
print("\n" + "-"*60)
print("TRANSACCIONES POR CATEGORÍA:")
print("-"*60)
print(df['categoria'].value_counts().to_string())
print("\n" + "-"*60)
print("TRANSACCIONES POR TIPO DE TARJETA:")
print("-"*60)
print(df['tipo_tarjeta'].value_counts().to_string())
print("\n" + "-"*60)
print("MONTO PROMEDIO POR CATEGORÍA:")
print("-"*60)
print(df.groupby('categoria')['monto'].mean().sort_values(ascending=False).apply(lambda x: f"${x:,.2f}").to_string())
print("\n" + "="*60 + "\n")
def main():
"""Función principal"""
print("="*60)
print("GENERADOR DE TRANSACCIONES DE TARJETAS DE CRÉDITO")
print("="*60)
print("\nGenerando 1000 transacciones sintéticas...\n")
# Crear generador
generador = GeneradorTransaccionesTarjetas(num_registros=1000)
# Generar transacciones
transacciones = generador.generar_transacciones()
# Guardar en CSV y JSON
df = generador.guardar_csv(transacciones)
generador.guardar_json(transacciones)
# Mostrar estadísticas
generador.generar_estadisticas(df)
# Mostrar muestra de datos
print("MUESTRA DE DATOS (primeras 5 transacciones):")
print("-"*60)
print(df.head().to_string())
print("\n")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment