Asesinos Seriales

Author

Halena Ruiz

Introducción

Este documento presenta un análisis exploratorio de un conjunto de datos sobre asesinos seriales, obtenido de https://www.kaggle.com/datasets/vesuvius13/serial-killers-dataset?utm_source=chatgpt.com. El objetivo es limpiar, transformar y visualizar la información para comprender mejor las características de los perfiles de asesinos.

1. Carga, Limpieza y Preparación de los Datos

En esta sección, se realiza la carga inicial de los datos desde múltiples archivos CSV y se lleva a cabo un proceso de limpieza y transformación para estandarizar los nombres de las columnas y convertir las variables de años y víctimas en formatos numéricos utilizables para el análisis.

Code
# Primero verificamos la ruta de trabajo del presente archivo
getwd()
# Ubicamos la ruta de trabajo de los archivos csv a trabajar
setwd("C:/Users/DELL LATITUDE 3500/Documents/Libros/R/CDV - CD/archive")
# Creamos una variable por cada archivo csv descomprimido
datos_1 <- read.csv("5_to_14_victim_count.csv", stringsAsFactors = FALSE)
datos_2 <- read.csv("15_to_30_victim_count.csv", stringsAsFactors = FALSE)
datos_3 <- read.csv("Highest_victim_count.csv", stringsAsFactors = FALSE)
datos_4 <- read.csv("Lessthan_5_victim_count.csv", stringsAsFactors = FALSE)
# Juntamos todos los datos en una única tabla

serial_killers_datos <- rbind(datos_1, datos_2, datos_3, datos_4)
# En este caso podemos usar la función "rbin" porque cada tabla individual contaba con el mismo número, nombre y orden de columnas

# Limpiamos el formato de los nombres de las columnas a un formato más adecuado
names(serial_killers_datos)                    
serial_killers_datos <- serial_killers_datos %>% 
  janitor::clean_names() 

# Verificamos las dimensiones y vemos la clase que le asignó a cada columna
dim(serial_killers_datos)
apply(serial_killers_datos,2, class)
# Hay que hacer tres modificaciones de clases de "charater" a "numeric"

# Realizamos las transformaciones
df_transformacion <- serial_killers_datos %>%
  # Primero la transformación para los años  
  mutate(rango_limpio = str_replace_all(years_active, "[^0-9to\\s]", "")) %>%
  mutate(rango_limpio = str_squish(rango_limpio))%>%
  mutate(año_inicio = as.numeric(str_extract(rango_limpio, "\\d{4}"))) %>%
  mutate(año_fin = case_when(
    str_detect(rango_limpio, " to | \\- ") ~ as.numeric(str_extract(rango_limpio, "\\d{4}$")), 
    TRUE ~ año_inicio)) %>%
# Ahora la transformación para los "proven_victims"
  mutate(
    rango_limpio_kills = str_replace_all(proven_victims, "[^0-9–+-]", ""),
    rango_limpio_kills = str_squish(rango_limpio_kills),
    victimas_min = as.numeric(str_extract(rango_limpio_kills, "^\\d+")),
    victimas_max = case_when(
      str_detect(rango_limpio_kills, "[–-]") ~ {
        segundo_num_str <- str_split(rango_limpio_kills, "[–-]", simplify = TRUE)[,2]
        as.numeric(str_remove(segundo_num_str, "\\+"))
      },
      str_detect(rango_limpio_kills, "\\d+\\+$") ~ as.numeric(str_remove(rango_limpio, "\\+")),
      str_detect(rango_limpio_kills, "^\\d+$") ~ victimas_min,
      TRUE ~ NA_real_
    )) %>%
# Ahora la transformación para los "possible_victims"
    mutate(
      rango_limpio_p = str_replace_all(possible_victims, "[^0-9–+-]", ""),
      rango_limpio_p = str_squish(rango_limpio_p),
      p_victimas_min = as.numeric(str_extract(rango_limpio_p, "^\\d+")),
      p_victimas_max = case_when(
        str_detect(rango_limpio_p, "[–-]") ~ {
          tercero_num_str <- str_split(rango_limpio_p, "[–-]", simplify = TRUE)[,2]
          as.numeric(str_remove(tercero_num_str, "\\+"))
        },
        str_detect(rango_limpio_p, "\\d+\\+$") ~ as.numeric(str_remove(rango_limpio_p, "\\+")),
        str_detect(rango_limpio_p, "^\\d+$") ~ p_victimas_min,
        TRUE ~ NA_real_
      ))
# Esta extensa transformación fue resultado de interacción de códigos propios corregidos por Gemini iteradamente.

# Selecciono los datos que quiero en la tabla
df_transformacion <- df_transformacion %>%
  dplyr::select(name, country, año_inicio, año_fin,victimas_min, victimas_max, p_victimas_min, p_victimas_max, 
                notes)

# Ahora, jugando un poco con los datos, queremos sacar el promedio de las columnas por cada fila
df_con_promedios <- df_transformacion %>%
  rowwise() %>%
  mutate(
    promedio_victimas = mean(c_across(c(victimas_min, victimas_max)), na.rm = TRUE)
  ) %>%
  mutate(
    promedio_victimas_estimadas = mean(c_across(c(p_victimas_min, p_victimas_max)), na.rm = TRUE)
  )
# La función "rowwise" fue recomendada por Gemini

# Organizamos la df resultante
df_serial_killer_final <- df_con_promedios %>%
  dplyr::select(name, country, año_inicio, año_fin, promedio_victimas, promedio_victimas_estimadas, notes)

Esta fue la parte más complicada de todo el trabajo, sinceramente después de pasar horas en la limpieza de los datos no me quedaba creatividad para el resto del proyecto.

2. Valores Estadísticos y Tabla Resumen

Para comprender las características principales de las variables numéricas en el conjunto de datos, se presentan medidas de tendencia central, dispersión y rango. Para un análisis más específico, se presenta una tabla con el promedio de víctimas estimadas y el número total de asesinos por cada país.

Promedio de Víctimas Estimadas por País
Número de víctimas estimadas promedio por asesino serial, agrupado por país de actividad.
País Promedio Víctimas Estimadas Mediana Víctimas Estimadas N° Asesinos
Colombia Peru Ecuador 300.00 300.00 1
Colombia Ecuador Venezuela 236.00 236.00 1
Colombia Ecuador Brazil (alleged) 180.00 180.00 1
France Germany (suspected) Switzerland (suspected) 150.00 150.00 1
Pakistan 100.00 100.00 2
German Empire Germany 79.00 79.00 1
Soviet Union Ukraine 71.00 71.00 2
Germany 56.33 42.00 3
Italy 50.80 16.00 6
Brazil 50.20 42.00 8
Spain France (claimed) Italy (claimed) 48.00 48.00 1
Mexico 47.50 34.50 4
Swaziland 45.00 45.00 1
Indonesia 44.50 44.50 3
Bangladesh 43.00 43.00 2
Soviet Union 37.33 33.00 13
Morocco 36.00 36.00 1
Kingdom of Romania Yugoslavia Hungary (alleged) 35.00 35.00 1
China 33.36 25.00 9
Ottoman Empire Iraq Iran 33.00 33.00 1
Egypt 32.00 32.00 1
India 30.67 25.00 10
United States 30.10 20.00 92
Canada 29.50 29.50 5
Latvia 28.50 28.50 2
Russia 28.29 16.50 21
Greece 27.00 27.00 1
South Africa 26.62 18.00 19
Soviet Union Russia 26.33 20.00 4
Colombia 26.25 22.50 4
Romania 26.00 26.00 1
South Korea 26.00 26.00 4
Poland 25.50 25.50 2
Peru 25.00 25.00 1
United States Canada 25.00 25.00 1
West Germany 25.00 25.00 2
Austria-Hungary 24.00 24.00 1
Ecuador 23.00 23.00 1
Soviet Union Russia Tajikistan 22.00 22.00 1
Turkey 22.00 22.00 4
United States Portuguese Angola 22.00 22.00 1
Australia 21.50 21.50 5
Iran 21.00 20.00 3
Afghanistan 20.00 20.00 1
East Germany Germany France Spain Czech Republic (suspected) Italy (suspected) 19.00 19.00 1
France Belgium 19.00 19.00 1
Ukraine 19.00 19.00 2
Austria 18.00 18.00 1
Canada United States 18.00 18.00 1
United Kingdom Ireland (suspected) West Germany (suspected) Netherlands (suspected) France (suspected) 18.00 18.00 1
United States Mexico 18.00 18.00 1
France 17.00 18.50 6
Allied-occupied Germany West Germany 16.00 16.00 1
Japan 16.00 16.00 4
Spain 16.00 16.00 3
Austria United States Czechoslovakia 15.00 15.00 1
Costa Rica 15.00 15.00 2
Denmark 15.00 15.00 1
United Kingdom 14.75 12.75 12
Argentina 14.00 14.00 3
Belgium 14.00 14.00 2
Thailand Nepal India Malaysia 13.00 13.00 1
Belarus 12.00 12.00 1
Netherlands 12.00 12.00 1
Switzerland France United States Yugoslavia (suspected) Italy (suspected) 11.00 11.00 1
Czechoslovakia 10.00 10.00 1
Chile NaN NA 1
China Japan NaN NA 1
Cyprus NaN NA 1
Singapore NaN NA 1
Soviet Union Russia Ukraine NaN NA 1
Tunisia NaN NA 1
Venezuela NaN NA 1
Yemen NaN NA 1

TOP’s 10

TOP 10 Asesinos con Más Víctimas Estimadas

TOP 10 Países con Mayor Número de Asesinos

Tabla de Búsqueda Interactiva