# tgn_data.py — Gerador de eventos temporais de e-commerce import numpy as np import torch import pandas as pd from datetime import datetime, timedelta import random def gerar_eventos_ecommerce( n_usuarios=300, n_comerciantes=80, n_eventos=3000, taxa_fraude=0.08, seed=42 ): """ Gera stream de transações de cartão de crédito e-commerce. Padrões de fraude embutidos: - Velocity attack: muitas transações em pouco tempo - Card testing: pequenas compras antes de compra grande - Geo anomaly: transações em locais impossíveis - Merchant anomaly: categorias incomuns para o usuário - Night transactions: compras em horário incomum """ random.seed(seed) np.random.seed(seed) # Perfis de usuário usuarios = [] for i in range(n_usuarios): is_fraudster = np.random.random() < 0.15 # 15% são alvos/fraudadores usuarios.append({ 'id': i, 'idade': np.random.randint(18, 75), 'limite': np.random.lognormal(8, 0.8), 'score_credito': np.random.beta(5, 2) if not is_fraudster else np.random.beta(2, 5), 'tempo_cliente_dias': np.random.exponential(500), 'regiao': np.random.choice(['SP', 'RJ', 'MG', 'RS', 'BA'], p=[0.35,0.25,0.15,0.15,0.10]), 'is_alvo': is_fraudster, 'ticket_medio': np.random.lognormal(5, 1), 'horario_tipico': np.random.randint(8, 22), # hora típica de compra }) # Perfis de comerciante categorias = ['eletronicos', 'viagem', 'alimentacao', 'roupas', 'jogos', 'farmacia'] comerciantes = [] for i in range(n_comerciantes): comerciantes.append({ 'id': i, 'categoria': np.random.choice(categorias, p=[0.20,0.15,0.25,0.20,0.10,0.10]), 'ticket_medio': np.random.lognormal(5.5, 1.2), 'risco_setor': np.random.beta(2, 8), }) # Gera eventos temporais inicio = datetime(2024, 1, 1) eventos = [] # Histórico por usuário para detectar padrões historico_usuario = {i: [] for i in range(n_usuarios)} for _ in range(n_eventos): usuario = random.choice(usuarios) uid = usuario['id'] comerciante = random.choice(comerciantes) mid = comerciante['id'] # Timestamp realista dias_offset = np.random.uniform(0, 180) hora_base = usuario['horario_tipico'] hora = int(np.clip(np.random.normal(hora_base, 3), 0, 23)) ts = inicio + timedelta(days=dias_offset, hours=hora, minutes=np.random.randint(0, 60)) timestamp = ts.timestamp() # Valor da transação valor_base = usuario['ticket_medio'] * comerciante['ticket_medio'] / 100 valor = max(1.0, np.random.lognormal(np.log(valor_base), 0.5)) # Features da transação n_tx_recentes = sum(1 for t in historico_usuario[uid] if timestamp - t['ts'] < 3600) # última hora # Determinar se é fraude prob_fraude = 0.0 fatores = [] if usuario['is_alvo']: prob_fraude += 0.15 # Velocity attack if n_tx_recentes >= 3: prob_fraude += 0.4 fatores.append('velocity') # Card testing (valor muito baixo seguido de alto) if len(historico_usuario[uid]) > 0: ultima = historico_usuario[uid][-1] if ultima['valor'] < 10 and valor > 500: prob_fraude += 0.35 fatores.append('card_testing') # Horário anômalo (madrugada) if hora < 4: prob_fraude += 0.25 fatores.append('night') # Categoria incomum cats_usuario = [h['categoria'] for h in historico_usuario[uid][-10:]] if cats_usuario and comerciante['categoria'] not in cats_usuario: if comerciante['categoria'] in ['eletronicos', 'viagem']: prob_fraude += 0.15 fatores.append('categoria_anomala') is_fraude = np.random.random() < min(prob_fraude, 0.95) evento = { 'evento_id': len(eventos), 'src': uid, # usuário 'dst': mid + n_usuarios, # comerciante (ids separados) 'timestamp': timestamp, 'valor': valor, 'valor_norm': np.log1p(valor) / 12.0, 'hora': hora, 'hora_norm': hora / 24.0, 'hora_anomala': 1.0 if hora < 6 else 0.0, 'categoria': comerciante['categoria'], 'cat_eletronicos': 1.0 if comerciante['categoria'] == 'eletronicos' else 0.0, 'cat_viagem': 1.0 if comerciante['categoria'] == 'viagem' else 0.0, 'n_tx_recentes_norm': min(n_tx_recentes / 10.0, 1.0), 'score_usuario': usuario['score_credito'], 'tempo_cliente_norm': min(usuario['tempo_cliente_dias'] / 1000.0, 1.0), 'risco_comerciante': comerciante['risco_setor'], 'fatores': fatores, 'label': int(is_fraude), } eventos.append(evento) historico_usuario[uid].append({ 'ts': timestamp, 'valor': valor, 'categoria': comerciante['categoria'] }) # Ordena por timestamp eventos.sort(key=lambda x: x['timestamp']) # Garante taxa de fraude aproximada df = pd.DataFrame(eventos) return df, usuarios, comerciantes, n_usuarios def df_para_tensores(df, n_usuarios, n_comerciantes): """Converte DataFrame de eventos para tensores PyTorch.""" n_nos = n_usuarios + n_comerciantes # Features de aresta (transação) edge_feats = torch.FloatTensor(df[[ 'valor_norm', 'hora_norm', 'hora_anomala', 'cat_eletronicos', 'cat_viagem', 'n_tx_recentes_norm', 'score_usuario', 'tempo_cliente_norm', 'risco_comerciante' ]].values) src = torch.LongTensor(df['src'].values) dst = torch.LongTensor(df['dst'].values) timestamps = torch.FloatTensor(df['timestamp'].values) labels = torch.LongTensor(df['label'].values) # Normaliza timestamps para [0, 1] t_min = timestamps.min() t_max = timestamps.max() timestamps_norm = (timestamps - t_min) / (t_max - t_min + 1e-8) # Split temporal: 60% train, 20% val, 20% test n = len(df) n_train = int(0.6 * n) n_val = int(0.8 * n) splits = { 'train': (0, n_train), 'val': (n_train, n_val), 'test': (n_val, n), } return { 'src': src, 'dst': dst, 'timestamps': timestamps_norm, 'timestamps_raw': timestamps, 'edge_feats': edge_feats, 'labels': labels, 'n_nos': n_nos, 'n_usuarios': n_usuarios, 'n_comerciantes': n_comerciantes, 'splits': splits, 'df': df, }