TemporalGNN / tgn_data.py
Danielfonseca1212's picture
Create tgn_data.py
9b35f8c verified
# 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,
}