Análisis exploratorio y limpieza del dataset de ataques de tiburón (GSAF) para estudiar patrones por tipo de ataque, año, geografía y actividad. El trabajo se ha coordinado por secciones (Marlene, Marta, Alejandro) y se desarrolla en el notebook Project2_SharkAttacksInvestigation.ipynb.
- Fuente: Google Drive (CSV GSAF; separador
;). También se incluyeGSAF5.csven el repo. - Notebook principal:
Project2_SharkAttacksInvestigation.ipynb.
- Carga con
pandas.read_csv(..., sep=';', low_memory=False). - Eliminación de columnas completamente vacías:
dropna(axis=1, how='all'). - Normalización de texto y nombres de columnas con
standardize_text(df)del móduloFunctions.py:- Minúsculas y
strip()en columnas de tipo texto. - Nombres de columnas normalizados (sin espacios, consistentes).
- Minúsculas y
Limpieza (filtro temporal y de tipo de ataque):
- TYPE: se filtra a ataques de tipo
unprovokedpara centrar el análisis en incidentes no provocados. - YEAR: se eliminan
NaNenyeary se normaliza el tipo numérico:- Reemplazo de coma por punto cuando aplica (
, -> .). - Conversión a numérico y filtro de rango:
2000 ≤ year ≤ 2025. - Conversión a entero/cadena cuando es útil para visualización.
- Reemplazo de coma por punto cuando aplica (
Análisis principales:
- Distribución anual de ataques y comparación por fatalidad (
fatal_y/n). - Agrupaciones
groupby(['year', 'country', 'fatal_y/n'])para contar casos y visualizar:- Barras por año con facetas por país.
- Serie temporal comparando ataques fatales vs no fatales.
Resultados esperados:
- Tendencias anuales claras y comparables entre países.
- Reducción de ruido al acotar años recientes y tipo de ataque homogéneo.
Limpieza (foco geográfico y homogeneización de estados):
- COUNTRY: selección de países que concentran ~90% de los ataques:
usa, australia, south africa, bahamas, brazil, new zealand, new caledonia, egypt, reunion, french polynesia, mexico, reunion island. - Unificación de
reunion island → reunion. STATE: se eliminan filas constatenulo y se normalizan estados por país mediante diccionarios específicos (minúsculas,strip(), corrección de typos y abreviaturas). Se cubren mapeos para: USA, Australia, South Africa, Bahamas, Brazil, New Zealand, New Caledonia, Egypt, Reunion, French Polynesia y Mexico.- Caso especial Reunion: se agrupa todo lo que empieza por
saintbajosaint areastras normalizar y quitar guiones.
Análisis principales:
- Conteos por
countryy porstatedentro de cada país. - Visualizaciones:
- Heatmap de dispersión de ataques por año y país.
- Mapa geográfico interactivo por país/estado (
scatter_geo).
Resultados esperados:
- Geografía estandarizada que permite comparaciones por país/estado sin duplicidades por errores tipográficos.
Limpieza (categorías de actividad y calidad de sex):
activity: normalización a categorías canónicas agregando variantes tipográficas y sinónimos. Categorías trabajadas:- diving, swimming, study, paddle, fishing, surfing, kayaking (cada una con su lista de equivalencias y reemplazos).
- Relleno de vacíos:
activity == "" → NaN → 'unknown activity'.sex == "" → NaN → 'unknown sex'y corrección delli → 'unknown sex'.
Análisis principales:
- Distribución de ataques por actividad (top categorías) y cruces con
sex. - Heatmap de ataques por
country × activitypara entender patrones de práctica.
Resultados esperados:
- Reducción del número de categorías dispersas por errores de escritura.
- Mejor lectura de patrones por actividad y diferencias por sexo.
Este análisis estima el riesgo relativo (odds ratio) de ataque fatal por especie usando regresión logística.
- Variable dependiente
- Se filtra
fatal_y/na valores válidos ('y','n'). - Se crea
fatal_binary(1=fatal, 0=no fatal):shark_df = shark_df.copy() shark_df = shark_df[shark_df['fatal_y/n'].isin(['y','n'])] shark_df['fatal_binary'] = shark_df['fatal_y/n'].map({'y':1, 'n':0})
- Exclusión de especie desconocida
- Se elimina
species_clean == 'unknown':shark_df_clean = shark_df[shark_df['species_clean'] != 'unknown'].copy()
- Agrupar especies raras
- Especies con < 8 observaciones se agrupan en
Otherpara estabilidad:species_counts = shark_df_clean['species_clean'].value_counts() rare_species = species_counts[species_counts < 8].index shark_df_clean['species_grouped'] = shark_df_clean['species_clean'].replace(rare_species, 'Other')
- Modelo logit
- Fórmula:
fatal_binary ~ C(species_grouped)constatsmodels:model = logit("fatal_binary ~ C(species_grouped)", data=shark_df_clean).fit() print(model.summary())
-
Odds Ratios (OR) e intervalos de confianza (IC 95%)
params = model.params conf = model.conf_int() odds_ratios = pd.DataFrame({ "Species": params.index, "OR": params.apply(lambda x: round(np.exp(x),2)), "CI_lower": conf[0].apply(lambda x: round(np.exp(x),2)), "CI_upper": conf[1].apply(lambda x: round(np.exp(x),2)) }).sort_values("OR", ascending=False).reset_index(drop=True)
-
Interpretación
- OR > 1 → mayor probabilidad de fatalidad que la categoría de referencia (
Other). - OR < 1 → menor probabilidad relativa.
- Los IC muestran la incertidumbre estadística.
Notas y limitaciones
- Se agrupan especies raras y se excluyen desconocidas.
- El modelo no ajusta por otros factores (lugar, actividad, tamaño, etc.).
- Cuando no hay especie, se usaron categorías de tamaño (small/medium/large) en ciertas observaciones; si se tratan como especies, pueden introducir sesgos en la distribución.
- Abrir el notebook
Project2_SharkAttacksInvestigation.ipynby ejecutar las celdas en orden. - Asegurar el import de funciones de limpieza:
import importlib, Functions importlib.reload(Functions) from Functions import standardize_text
- Generar las visualizaciones para cada sección (YEAR/COUNTRY/STATE/ACTIVITY/SEX) y, opcionalmente, el modelo logístico por especie.