Série Analytics Engineering na Prática
Uma análise confiável de segurança pública depende de uma base consistente: identificadores únicos, datas válidas, categorias padronizadas e coordenadas dentro da região esperada. Neste estudo, preparei 150.500 ocorrências e 13 colunas para responder perguntas sobre categoria, distrito, dia da semana, resolução e distribuição geográfica dos incidentes em São Francisco.
Entendendo o esquema
O conjunto contém os seguintes campos principais:
| Campo | Papel no conjunto de dados |
|---|---|
IncidntNum | identificador do incidente |
Category e Descript | classificação geral e descrição detalhada |
Date, Time e DayOfWeek | dimensões temporais |
PdDistrict | distrito policial |
Resolution | desfecho registrado |
Address | endereço informado |
X, Y e Location | representação geográfica |
PdId | identificador atribuído pelo departamento de polícia |
Esse inventário funciona como o início de um contrato de dados. Antes de agregar, eu validaria unicidade, tipos, datas, categorias desconhecidas e limites de latitude e longitude. Também verificaria se DayOfWeek corresponde à data, evitando manter duas versões conflitantes da mesma informação.
Preparação da camada analítica
As transformações podem ser organizadas em um fluxo simples e reproduzível:
required = [
"IncidntNum", "Category", "Date", "Time",
"PdDistrict", "Resolution", "X", "Y"
]
missing = set(required) - set(df.columns)
if missing:
raise ValueError(f"Colunas obrigatórias ausentes: {sorted(missing)}")
df["occurred_at"] = pd.to_datetime(
df["Date"] + " " + df["Time"],
errors="coerce"
)
df["Category"] = df["Category"].str.strip().str.upper()
df["PdDistrict"] = df["PdDistrict"].str.strip().str.upper()Na sequência, uma tabela fato de ocorrências poderia se relacionar a dimensões de tempo, categoria e distrito. Isso evita repetir atributos descritivos e facilita o consumo por dashboards.
Modelo dimensional
A granularidade de fct_ocorrencia seria uma linha por incidente registrado. As dimensões compartilhadas manteriam descrições consistentes entre dashboards:
dim_tempo: data, dia da semana, mês, trimestre e ano;dim_categoria_crime: categoria e descrição padronizadas;dim_distrito_policial: código e nome do distrito;dim_resolucao: situação e agrupamento do desfecho;dim_localizacao: endereço e coordenadas validadas.
Sobre esse modelo, marts específicos poderiam publicar ocorrências por dia, distrito e categoria. Métricas como total_ocorrencias e percentual_resolvido precisariam de definição explícita, período de cobertura e data da última atualização.
Indicadores publicados
LARCENY/THEFT foi a categoria mais frequente, com 40.409 ocorrências, seguida por OTHER OFFENSES, com 19.599, e NON-CRIMINAL, com 17.866.

Por distrito policial, SOUTHERN concentrou 28.445 registros, seguido por NORTHERN, com 20.100, e MISSION, com 19.503.

O cruzamento entre categoria e distrito mostra que o mesmo indicador geral pode esconder perfis locais distintos. Em TENDERLOIN, por exemplo, LARCENY/THEFT aparece com 1.825 ocorrências, NON-CRIMINAL com 1.379 e OTHER OFFENSES com 1.237.

A dimensão temporal permite comparar categorias por dia da semana. LARCENY/THEFT registrou 6.477 ocorrências na sexta-feira, 6.384 no sábado e 5.538 na quinta-feira.

Por fim, as coordenadas permitem construir mapas de densidade. Antes dessa etapa, pontos ausentes ou fora dos limites de São Francisco devem ser isolados para não distorcer a visualização.

Cuidados de interpretação
Contagem de registros não é sinônimo de taxa de criminalidade. Para comparar regiões de forma justa, seria necessário incluir população, área, fluxo de pessoas e mudanças no processo de registro. Da mesma forma, uma ocorrência não resolvida no conjunto pode representar atraso de atualização, e não necessariamente ausência de ação.
Essas limitações devem acompanhar a tabela ou o dashboard como metadados. Um consumidor precisa saber o período de cobertura, a data da última atualização e as regras aplicadas aos registros.
Evolução para um pipeline recorrente
Eu implementaria a ingestão incremental por data de atualização, mantendo três camadas:
- Raw: cópia imutável da fonte;
- Trusted: datas, categorias e coordenadas validadas, com duplicidades tratadas;
- Curated: agregações por tempo, categoria e distrito, prontas para visualização.
Testes automáticos acompanhariam volume, unicidade de IDs, percentual de nulos e limites geográficos. A publicação usaria uma chave composta ou estratégia de upsert para que reprocessamentos não duplicassem incidentes.
Tecnologias
Python, Pandas, NumPy, Matplotlib, Plotly e Folium foram utilizados na exploração e visualização. O estudo tomou como referência o artigo San Francisco Crime Analysis with Data Science.
Aprendizados
O maior ganho veio de separar a pergunta analítica da preparação do dado. Quando esquema, granularidade e regras de qualidade estão explícitos, os mesmos registros podem alimentar mapas, séries temporais e indicadores sem criar uma transformação diferente para cada gráfico.