Capítulo 3 Data Frames

3.1 Introducción a los Data Frames

En los capítulos anteriores, exploramos diferentes tipos de objetos en R, como variables, vectores, listas y matrices. Estos objetos nos permiten almacenar información de maneras más eficientes. Ahora, en este capítulo, nos adentraremos en el mundo de los data frames, una herramienta esencial para organizar y analizar la información que te ayudará a tomar la mejor decisión sobre tu mudanza a Estados Unidos.

3.1.1 ¿Qué son los data frames?

Imagina una hoja de cálculo, con filas y columnas que organizan la información de manera tabular. En R, un data frame es precisamente eso: una estructura de datos que almacena información en un formato tabular, con filas que representan observaciones (por ejemplo, cada ciudad de Estados Unidos) y columnas que representan variables (como población, costo de vida, tasa de criminalidad).

Cada columna de un data frame puede contener un tipo de dato diferente: numérico, carácter, lógico, factor, etc. Esto hace que los data frames sean muy versátiles para almacenar información diversa.

Por ejemplo, un data frame sobre ciudades de Estados Unidos podría contener las siguientes columnas:

  • ciudad: Nombre de la ciudad (carácter).
  • estado: Estado al que pertenece la ciudad (carácter).
  • poblacion: Población de la ciudad (numérico).
  • area: Área de la ciudad en kilómetros cuadrados (numérico).
  • tiene_playa: Valor lógico que indica si la ciudad tiene playa (TRUE o FALSE).

3.1.2 ¿Por qué data frames?

En R, existen diversas estructuras para organizar datos, como vectores, listas y matrices. Sin embargo, los data frames se destacan como una herramienta fundamental en el análisis de datos. ¿Por qué?

Los data frames ofrecen una combinación única de características que los hacen ideales para representar y manipular información compleja:

  • Estructura tabular: Organizan los datos en filas y columnas, como una hoja de cálculo, lo que facilita su visualización y comprensión.
  • Flexibilidad en los tipos de datos: Cada columna puede contener un tipo de dato diferente (números, texto, fechas, etc.), lo que permite representar la diversidad de información del mundo real.
  • Eficiencia en el análisis: La mayoría de las funciones y paquetes de análisis de datos en R están diseñados para trabajar con data frames.

En resumen, los data frames son una estructura de datos versátil y poderosa que se adapta a las necesidades del análisis de datos moderno.

3.1.3 Data Frames en acción: explorando información sobre Estados Unidos

En el contexto de tu mudanza a Estados Unidos, los data frames serán esenciales para organizar y analizar la información que necesitas para tomar la mejor decisión. Podemos usar data frames para almacenar información sobre:

  • Criminalidad: Tasas de criminalidad en diferentes estados.
  • Costo de vida: Costo de vivienda, alimentación, transporte en diferentes ciudades.
  • Clima: Temperaturas promedio, precipitaciones, días de sol en diferentes regiones.
  • Demografía: Población, edad promedio, nivel educativo en diferentes estados.

Con esta información organizada en data frames, podrás realizar análisis más profundos y tomar decisiones más informadas sobre tu mudanza.

3.2 Creando Data Frames: Construyendo tu base de datos para la mudanza

Ahora que ya sabes qué son los data frames y por qué son tan importantes en el análisis de datos, es hora de aprender a crearlos. En R, podemos crear data frames de diferentes maneras: importando datos desde archivos externos o creándolos manualmente.

3.2.1 Importando datos desde archivos: CSV, Excel

Una forma común de crear data frames es importando datos desde archivos externos, como archivos CSV (Comma Separated Values) o archivos de Excel. R nos ofrece funciones para leer datos de diferentes formatos.

  • Importando datos desde archivos CSV: Para importar datos desde un archivo CSV, usamos la función read.csv().

    url <- "https://dparedesi.github.io/DS-con-R/notas-estudiantes.csv"
    
    # Importar datos desde un archivo CSV llamado "notas-estudiantes.csv"
    ciudades <- read.csv(url)
    
    ciudades
    #>        inicio genero                 tipo P1 P2 P3 P4 P5 P6
    #> 1  03/05/2020  mujer Trabajo individual 1  5  5  5  5  5  5
    #> 2  03/05/2020 hombre Trabajo individual 1  5  5  5  5  4  5
    #> 3  03/05/2020  mujer Trabajo individual 1  5  5  4  5  5  5
    #> 4  03/05/2020 hombre Trabajo individual 1  5  5  5  5  5  5
    #> 5  03/05/2020 hombre Trabajo individual 1  2  5  5  5  5  5
    #> 6  03/05/2020 hombre Trabajo individual 1  5  4  5  1  5  5
    #> 7  03/05/2020 hombre Trabajo individual 1  2  1  5  5  2  5
    #> 8  03/05/2020 hombre Trabajo individual 1  5  5  5  5  5  5
    #> 9  03/05/2020 hombre Trabajo individual 1  4  5  5  5  5  5
    #> 10 03/05/2020 hombre Trabajo individual 1  3  4  5  5  5  5
    #> 11 03/05/2020 hombre Trabajo individual 1  2  5  5  5  5  5
    #> 12 03/05/2020  mujer Trabajo individual 1  1  1  5  5  5  1
    #> 13 03/05/2020 hombre Trabajo individual 1  5  5  5  5  5  5
    #> 14 03/05/2020  mujer Trabajo individual 1  3  5  5  1  1  1
    #> 15 03/05/2020 hombre Trabajo individual 1  4  5  5  5  5  5
    #> 16 03/05/2020  mujer Trabajo individual 1  5  5  5  5  5  5
    #> 17 03/05/2020 hombre Trabajo individual 1  5  1  5  5  1  1
    #> 18 03/05/2020  mujer Trabajo individual 1  4  5  5  5  5  5
    #> 19 03/05/2020 hombre Trabajo individual 1  5  3  5  5  5  2
    #> 20 03/05/2020 hombre Trabajo individual 1  3  5  5  5  5  5
    #> 21 03/05/2020 hombre Trabajo individual 1  1  4  4  4  5  5

    La función read.csv() tiene varios argumentos opcionales que nos permiten personalizar la importación de datos. Algunos de los argumentos más comunes son:

    • header: Indica si el archivo tiene una fila de encabezado (TRUE o FALSE).
    • sep: Especifica el carácter que se usa para separar las columnas (por defecto, la coma “,”).
    • dec: Especifica el carácter que se usa para separar los decimales (por defecto, el punto “.”).
  • Importando datos desde archivos de Excel: Para importar datos desde un archivo de Excel, podemos usar la función read_excel() del paquete readxl.

    # Instalar el paquete readxl (si no lo tienes instalado)
    install.packages("readxl")
    
    # Cargar el paquete readxl
    library(readxl)
    
    # Importar datos desde un archivo de Excel llamado "estados.xlsx"
    estados <- read_excel("estados.xlsx")

    La función read_excel() tiene varios argumentos opcionales, como sheet para especificar la hoja de cálculo que se quiere importar.

3.2.2 Creando data frames manualmente

También podemos crear data frames manualmente, combinando vectores con la función data.frame().

# Crear vectores con información sobre ciudades
ciudades <- c("Nueva York", "Los Ángeles", "Chicago")
estados <- c("Nueva York", "California", "Illinois")
poblacion <- c(8.4e6, 3.9e6, 2.7e6)

# Crear un data frame con la información de las ciudades
df_ciudades <- data.frame(ciudad = ciudades, estado = estados, poblacion = poblacion)

df_ciudades
#>        ciudad     estado poblacion
#> 1  Nueva York Nueva York   8400000
#> 2 Los Ángeles California   3900000
#> 3     Chicago   Illinois   2700000

En este ejemplo, creamos un data frame llamado df_ciudades con tres columnas: ciudad, estado y poblacion. Cada columna se crea a partir de un vector. Observa que los vectores deben tener la misma longitud para que se puedan combinar en un data frame.

3.2.3 Ejemplos

Podemos usar data frames para organizar información diversa sobre nuestra mudanza a Estados Unidos. Por ejemplo, podríamos crear un data frame con información sobre diferentes ciudades, incluyendo su costo de vida, tasa de criminalidad, y clima. También podríamos crear un data frame con información sobre los diferentes estados, incluyendo su población, producto interno bruto (PIB), y sistema educativo.

# Crear un data frame con información sobre ciudades
df_ciudades <- data.frame(
  ciudad = c("Nueva York", "Los Ángeles", "Chicago", "Houston"),
  estado = c("Nueva York", "California", "Illinois", "Texas"),
  costo_vida = c(3.5, 2.8, 2.5, 2.0),  # En miles de dólares
  tasa_criminalidad = c(400, 350, 500, 450),  # Por cada 100,000 habitantes
  clima = c("Templado", "Mediterráneo", "Continental", "Subtropical")
)

df_ciudades
#>        ciudad     estado costo_vida tasa_criminalidad        clima
#> 1  Nueva York Nueva York        3.5               400     Templado
#> 2 Los Ángeles California        2.8               350 Mediterráneo
#> 3     Chicago   Illinois        2.5               500  Continental
#> 4     Houston      Texas        2.0               450  Subtropical

# Crear un data frame con información sobre estados
df_estados <- data.frame(
  estado = c("California", "Texas", "Florida", "Nueva York"),
  poblacion = c(39.2e6, 29.0e6, 21.4e6, 19.4e6),
  pib = c(3.2e12, 1.8e12, 1.1e12, 1.7e12),  # En dólares
  sistema_educativo = c("Bueno", "Regular", "Bueno", "Excelente")
)

df_estados
#>       estado poblacion     pib sistema_educativo
#> 1 California  39200000 3.2e+12             Bueno
#> 2      Texas  29000000 1.8e+12           Regular
#> 3    Florida  21400000 1.1e+12             Bueno
#> 4 Nueva York  19400000 1.7e+12         Excelente

Estos data frames nos permitirán analizar la información de forma más eficiente y tomar decisiones más informadas sobre nuestra mudanza.

3.3 Explorando Data Frames: Descubriendo los secretos de tus datos

Ya hemos aprendido a crear data frames, ahora es momento de explorar su contenido y descubrir la información que esconden. R nos ofrece diversas herramientas para examinar y comprender nuestros datos.

3.3.1 Accediendo a filas, columnas y celdas

Un data frame es como un mapa organizado en filas y columnas. Para acceder a la información que necesitamos, debemos saber cómo navegar por este mapa. R nos proporciona diferentes maneras de acceder a las filas, columnas y celdas de un data frame.

  • Accediendo a columnas: Podemos acceder a una columna de un data frame usando el operador $ seguido del nombre de la columna.

    # Acceder a la columna "estado" del data frame "df_ciudades"
    df_ciudades$estado
    #> [1] "Nueva York" "California" "Illinois"   "Texas"

    También podemos acceder a una columna usando el nombre del data frame seguido de corchetes y el nombre de la columna entre comillas.

    # Acceder a la columna "poblacion" del data frame "df_estados"
    df_estados["poblacion"]
    #>   poblacion
    #> 1  39200000
    #> 2  29000000
    #> 3  21400000
    #> 4  19400000
  • Accediendo a filas: Podemos acceder a una fila de un data frame usando corchetes y el número de la fila.

    # Acceder a la tercera fila del data frame "df_ciudades"
    df_ciudades[3, ]
    #>    ciudad   estado costo_vida tasa_criminalidad       clima
    #> 3 Chicago Illinois        2.5               500 Continental
  • Accediendo a celdas: Podemos acceder a una celda específica de un data frame usando corchetes y especificando la fila y la columna.

    # Acceder a la celda en la fila 2, columna 3 del data frame "df_estados"
    df_estados[2, 3]
    #> [1] 1.8e+12
  • Filtrando filas con condiciones: Podemos usar condiciones lógicas para filtrar las filas de un data frame. Por ejemplo, si queremos obtener las ciudades con un costo de vida menor a 3:

    df_ciudades[df_ciudades$costo_vida < 3, ]
    #>        ciudad     estado costo_vida tasa_criminalidad        clima
    #> 2 Los Ángeles California        2.8               350 Mediterráneo
    #> 3     Chicago   Illinois        2.5               500  Continental
    #> 4     Houston      Texas        2.0               450  Subtropical

3.3.2 Funciones para explorar data frames

R nos ofrece varias funciones útiles para explorar data frames:

  • head(): Muestra las primeras 6 filas del data frame.

    head(df_ciudades)
    #>        ciudad     estado costo_vida tasa_criminalidad        clima
    #> 1  Nueva York Nueva York        3.5               400     Templado
    #> 2 Los Ángeles California        2.8               350 Mediterráneo
    #> 3     Chicago   Illinois        2.5               500  Continental
    #> 4     Houston      Texas        2.0               450  Subtropical
  • tail(): Muestra las últimas 6 filas del data frame.

    tail(df_estados)
    #>       estado poblacion     pib sistema_educativo
    #> 1 California  39200000 3.2e+12             Bueno
    #> 2      Texas  29000000 1.8e+12           Regular
    #> 3    Florida  21400000 1.1e+12             Bueno
    #> 4 Nueva York  19400000 1.7e+12         Excelente
  • str(): Muestra la estructura del data frame, incluyendo el nombre de las columnas, el tipo de dato de cada columna y los primeros valores de cada columna.

    str(df_ciudades)
    #> 'data.frame':    4 obs. of  5 variables:
    #>  $ ciudad           : chr  "Nueva York" "Los Ángeles" "Chicago" "Houston"
    #>  $ estado           : chr  "Nueva York" "California" "Illinois" "Texas"
    #>  $ costo_vida       : num  3.5 2.8 2.5 2
    #>  $ tasa_criminalidad: num  400 350 500 450
    #>  $ clima            : chr  "Templado" "Mediterráneo" "Continental" "Subtropical"
  • summary(): Proporciona estadísticas descriptivas de cada columna del data frame, como la media, la mediana, los valores mínimo y máximo, etc.

    summary(df_estados)
    #>     estado            poblacion             pib           sistema_educativo 
    #>  Length:4           Min.   :19400000   Min.   :1.10e+12   Length:4          
    #>  Class :character   1st Qu.:20900000   1st Qu.:1.55e+12   Class :character  
    #>  Mode  :character   Median :25200000   Median :1.75e+12   Mode  :character  
    #>                     Mean   :27250000   Mean   :1.95e+12                     
    #>                     3rd Qu.:31550000   3rd Qu.:2.15e+12                     
    #>                     Max.   :39200000   Max.   :3.20e+12
  • View(): Abre una ventana con una vista interactiva del data frame, similar a una hoja de cálculo.

    View(df_ciudades)

3.3.3 Ejemplos: explorando data frames con información sobre la mudanza

Al explorar los data frames que creamos en la sección anterior, podemos obtener información valiosa sobre las ciudades y estados de Estados Unidos. Por ejemplo, podríamos usar summary() para obtener estadísticas descriptivas del costo de vida en diferentes ciudades, o View() para examinar en detalle la información sobre cada estado.

# Obtener estadísticas descriptivas del costo de vida en diferentes ciudades
summary(df_ciudades$costo_vida)
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>   2.000   2.375   2.650   2.700   2.975   3.500
# Examinar en detalle la información sobre cada estado
View(df_estados)

Además de las funciones mencionadas, podemos usar otras herramientas para explorar nuestros data frames. Por ejemplo, podemos usar la función table() para obtener la frecuencia de cada valor en una columna categórica, como la columna clima en el data frame df_ciudades.

table(df_ciudades$clima)
#> 
#>  Continental Mediterráneo  Subtropical     Templado 
#>            1            1            1            1

También podemos usar la función hist() para crear un histograma de una columna numérica, como la columna poblacion en el data frame df_estados.

hist(df_estados$poblacion)

Estas son solo algunas ideas de cómo podemos explorar nuestros data frames. A medida que te familiarices con R, descubrirás nuevas funciones y técnicas para analizar y visualizar tus datos.

3.4 Manipulando Data Frames: Transformando tus datos

En la sección anterior, aprendimos a explorar data frames y acceder a la información que contienen. Ahora, daremos un paso más allá y aprenderemos a manipular data frames, transformando los datos para responder preguntas específicas y obtener información relevante para nuestra mudanza.

3.4.1 Introducción al operador pipeline (%>%)

Antes de manipular data frames, introduciremos una herramienta para escribir código más legible y eficiente: el operador pipeline (%>%). Este operador se encuentra en el paquete tidyverse, una colección de paquetes de R diseñados para la ciencia de datos. Uno de los paquetes incluidos en tidyverse es dplyr, que contiene muchas funciones útiles para trabajar con data frames.

Un paquete en R es como una caja de herramientas con funciones y datos adicionales para realizar tareas específicas. Para usar las funciones de un paquete, primero debemos instalarlo y luego cargarlo en nuestro entorno de trabajo.

Para instalar el paquete tidyverse, podemos usar la siguiente instrucción en la consola de R:

install.packages("tidyverse")

Esto instalará tidyverse y todos los paquetes que contiene, incluyendo dplyr. Una vez instalado el paquete, podemos cargarlo con la función library():

library(tidyverse)

Ahora ya podemos usar el operador pipeline (%>%) y las demás funciones de dplyr.

El operador pipeline nos permite encadenar varias operaciones de forma secuencial. En lugar de escribir código anidado, podemos usar el operador pipeline para “pasar” el resultado de una operación a la siguiente.

Por ejemplo, si queremos visualizar solo los datos estado, población y total del data frame murders (del paquete dslabs), podemos usar un pipeline:

install.packages("dslabs")
# Cargar la librería y el dataset
library(dslabs)
data(murders)

# Pipeline
murders %>% select(state, population, total)
#>                   state population total
#> 1               Alabama    4779736   135
#> 2                Alaska     710231    19
#> 3               Arizona    6392017   232
#> 4              Arkansas    2915918    93
#> 5            California   37253956  1257
#> 6              Colorado    5029196    65
#> 7           Connecticut    3574097    97
#> 8              Delaware     897934    38
#> 9  District of Columbia     601723    99
#> 10              Florida   19687653   669
#> 11              Georgia    9920000   376
#> 12               Hawaii    1360301     7
#> 13                Idaho    1567582    12
#> 14             Illinois   12830632   364
#> 15              Indiana    6483802   142
#> 16                 Iowa    3046355    21
#> 17               Kansas    2853118    63
#> 18             Kentucky    4339367   116
#> 19            Louisiana    4533372   351
#> 20                Maine    1328361    11
#> 21             Maryland    5773552   293
#> 22        Massachusetts    6547629   118
#> 23             Michigan    9883640   413
#> 24            Minnesota    5303925    53
#> 25          Mississippi    2967297   120
#> 26             Missouri    5988927   321
#> 27              Montana     989415    12
#> 28             Nebraska    1826341    32
#> 29               Nevada    2700551    84
#> 30        New Hampshire    1316470     5
#> 31           New Jersey    8791894   246
#> 32           New Mexico    2059179    67
#> 33             New York   19378102   517
#> 34       North Carolina    9535483   286
#> 35         North Dakota     672591     4
#> 36                 Ohio   11536504   310
#> 37             Oklahoma    3751351   111
#> 38               Oregon    3831074    36
#> 39         Pennsylvania   12702379   457
#> 40         Rhode Island    1052567    16
#> 41       South Carolina    4625364   207
#> 42         South Dakota     814180     8
#> 43            Tennessee    6346105   219
#> 44                Texas   25145561   805
#> 45                 Utah    2763885    22
#> 46              Vermont     625741     2
#> 47             Virginia    8001024   250
#> 48           Washington    6724540    93
#> 49        West Virginia    1852994    27
#> 50            Wisconsin    5686986    97
#> 51              Wyoming     563626     5

El código con pipeline es más fácil de leer y entender, ya que sigue el flujo natural de las operaciones. Pipeline crea una vista; no estamos editando el data frame murders.

Podemos mostrar las primeras filas usando la función head():

head(murders %>% select(state, population, total))
#>        state population total
#> 1    Alabama    4779736   135
#> 2     Alaska     710231    19
#> 3    Arizona    6392017   232
#> 4   Arkansas    2915918    93
#> 5 California   37253956  1257
#> 6   Colorado    5029196    65

También podemos usar el operador pipeline para mostrar las primeras filas:

murders %>% select(state, population, total) %>% head()
#>        state population total
#> 1    Alabama    4779736   135
#> 2     Alaska     710231    19
#> 3    Arizona    6392017   232
#> 4   Arkansas    2915918    93
#> 5 California   37253956  1257
#> 6   Colorado    5029196    65

Para una mejor legibilidad, usaremos una función por línea, obteniendo el mismo resultado:

murders %>%
  select(state, population, total) %>% # Seleccionamos columnas
  head() # Mostramos las 6 primeras filas
#>        state population total
#> 1    Alabama    4779736   135
#> 2     Alaska     710231    19
#> 3    Arizona    6392017   232
#> 4   Arkansas    2915918    93
#> 5 California   37253956  1257
#> 6   Colorado    5029196    65

3.4.2 Transformando una tabla con mutate()

Podemos crear nuevas columnas o modificar existentes usando la función mutate(). Por ejemplo, para agregar una columna con el ratio de homicidios por cada 100 mil habitantes al data frame murders:

murders %>%
  mutate(ratio = total / population * 100000) %>%
  head()
#>        state abb region population total    ratio
#> 1    Alabama  AL  South    4779736   135 2.824424
#> 2     Alaska  AK   West     710231    19 2.675186
#> 3    Arizona  AZ   West    6392017   232 3.629527
#> 4   Arkansas  AR  South    2915918    93 3.189390
#> 5 California  CA   West   37253956  1257 3.374138
#> 6   Colorado  CO   West    5029196    65 1.292453

Esto crea una vista con la columna adicional ratio.

Si queremos modificar el data frame murders directamente, usamos el operador de asignación <-:

murders <- murders %>%
  mutate(ratio = total / population * 100000)

3.4.3 Filtrando datos: seleccionando las ciudades que te interesan

Podemos filtrar filas que cumplen una condición usando la función filter(). Por ejemplo, para obtener los estados con menos de 1 homicidio por cada 100 mil habitantes:

# Cargar el dataset
data(murders)

murders %>%
  mutate(ratio = total / population * 100000) %>%
  filter(ratio < 1)
#>            state abb        region population total     ratio
#> 1         Hawaii  HI          West    1360301     7 0.5145920
#> 2          Idaho  ID          West    1567582    12 0.7655102
#> 3           Iowa  IA North Central    3046355    21 0.6893484
#> 4          Maine  ME     Northeast    1328361    11 0.8280881
#> 5      Minnesota  MN North Central    5303925    53 0.9992600
#> 6  New Hampshire  NH     Northeast    1316470     5 0.3798036
#> 7   North Dakota  ND North Central     672591     4 0.5947151
#> 8         Oregon  OR          West    3831074    36 0.9396843
#> 9   South Dakota  SD North Central     814180     8 0.9825837
#> 10          Utah  UT          West    2763885    22 0.7959810
#> 11       Vermont  VT     Northeast     625741     2 0.3196211
#> 12       Wyoming  WY          West     563626     5 0.8871131

Podemos usar diferentes operadores para crear nuestras condiciones:

  • >: mayor que
  • <: menor que
  • >=: mayor o igual que
  • <=: menor o igual que
  • ==: igual a
  • !=: diferente a

También podemos combinar condiciones usando operadores lógicos:

  • &: AND (y)
  • |: OR (o)
  • !: NOT (no)

Por ejemplo, para filtrar por ratio menor a 1 y región Oeste:

murders %>%
  mutate(ratio = total / population * 100000) %>%
  filter(ratio < 1 & region == "West")
#>     state abb region population total     ratio
#> 1  Hawaii  HI   West    1360301     7 0.5145920
#> 2   Idaho  ID   West    1567582    12 0.7655102
#> 3  Oregon  OR   West    3831074    36 0.9396843
#> 4    Utah  UT   West    2763885    22 0.7959810
#> 5 Wyoming  WY   West     563626     5 0.8871131

3.4.4 Ordenando datos: encontrando las ciudades más seguras

La función arrange() del paquete dplyr nos permite ordenar las filas de un data frame según una o varias columnas. Imagina que tienes un data frame con información sobre diferentes ciudades, y quieres ordenarlas de la más segura a la menos segura, basándote en su tasa de criminalidad. O tal vez quieres ordenarlas por costo de vida, de la más barata a la más cara. arrange() te permite hacer esto de forma sencilla.

Por ejemplo, para ordenar los estados por tasa de homicidios (de menor a mayor):

murders %>%
  mutate(ratio = total / population * 100000) %>%
  arrange(ratio) %>%
  head()
#>           state abb        region population total     ratio
#> 1       Vermont  VT     Northeast     625741     2 0.3196211
#> 2 New Hampshire  NH     Northeast    1316470     5 0.3798036
#> 3        Hawaii  HI          West    1360301     7 0.5145920
#> 4  North Dakota  ND North Central     672591     4 0.5947151
#> 5          Iowa  IA North Central    3046355    21 0.6893484
#> 6         Idaho  ID          West    1567582    12 0.7655102

Si queremos ordenar de forma descendente, usamos la función desc():

murders %>%
  mutate(ratio = total / population * 100000) %>%
  arrange(desc(ratio)) %>%
  head()
#>                  state abb        region population total     ratio
#> 1 District of Columbia  DC         South     601723    99 16.452753
#> 2            Louisiana  LA         South    4533372   351  7.742581
#> 3             Missouri  MO North Central    5988927   321  5.359892
#> 4             Maryland  MD         South    5773552   293  5.074866
#> 5       South Carolina  SC         South    4625364   207  4.475323
#> 6             Delaware  DE         South     897934    38  4.231937

También podemos ordenar por varias columnas. Por ejemplo, si queremos ordenar primero por region y luego por estado (en orden alfabético):

murders %>% 
  arrange(region, state) %>%
  head()
#>           state abb    region population total
#> 1   Connecticut  CT Northeast    3574097    97
#> 2         Maine  ME Northeast    1328361    11
#> 3 Massachusetts  MA Northeast    6547629   118
#> 4 New Hampshire  NH Northeast    1316470     5
#> 5    New Jersey  NJ Northeast    8791894   246
#> 6      New York  NY Northeast   19378102   517

3.4.5 Agregando y resumiendo datos: obteniendo información general

La función summarize() del paquete dplyr nos permite calcular estadísticas descriptivas de una o varias columnas de un data frame. Es como si pudiéramos resumir la información de nuestro data frame en un solo número o en un conjunto de números.

Por ejemplo, para calcular la media de la población de los estados:

murders %>%
  summarize(media_poblacion = mean(population))
#>   media_poblacion
#> 1         6075769

Podemos combinar summarize() con group_by() para calcular estadísticas por grupos. Por ejemplo, para calcular la población promedio por región:

murders %>%
  group_by(region) %>%
  summarize(media_poblacion = mean(population))
#> # A tibble: 4 × 2
#>   region        media_poblacion
#>   <fct>                   <dbl>
#> 1 Northeast            6146360 
#> 2 South                6804378.
#> 3 North Central        5577250.
#> 4 West                 5534273.

3.4.6 Uniendo data frames: combinando información

Imagina que tienes dos data frames: uno con información sobre ciudades (nombre, población, etc.) y otro con información sobre los estados a los que pertenecen esas ciudades (nombre del estado, gobernador, etc.). Si quieres combinar la información de ambos data frames para tener un único data frame con toda la información de las ciudades y sus estados, puedes usar las funciones de unión de dplyr.

dplyr ofrece varias funciones para unir data frames, como left_join(), right_join(), inner_join() y full_join(). Cada función realiza un tipo de unión diferente, dependiendo de cómo se combinen las filas de los data frames.

La función left_join() une dos data frames manteniendo todas las filas del primer data frame (el de la izquierda) y añadiendo las columnas del segundo data frame que coinciden con las filas del primer data frame. Si una fila del primer data frame no tiene una coincidencia en el segundo data frame, las nuevas columnas tendrán valores NA.

Por ejemplo, si tenemos un data frame con información sobre ciudades y otro data frame con información sobre estados, podemos unirlos por la columna estado:

df_ciudades_estados <- left_join(df_ciudades, df_estados, by = "estado")

El data frame resultante df_ciudades_estados contendrá la información de ambos data frames combinada. Si una ciudad en df_ciudades no tiene un estado correspondiente en df_estados, las columnas de df_estados tendrán valores NA para esa ciudad.

Veamos un ejemplo concreto. Supongamos que tenemos los siguientes data frames:

df_ciudades <- data.frame(
  ciudad = c("Nueva York", "Los Ángeles", "Chicago", "Houston"),
  estado = c("Nueva York", "California", "Illinois", "Texas")
)

df_estados <- data.frame(
  estado = c("California", "Texas", "Florida"),
  gobernador = c("Gavin Newsom", "Greg Abbott", "Ron DeSantis")
)

# Unir los data frames por la columna "estado"
df_ciudades_estados <- left_join(df_ciudades, df_estados, by = "estado")

df_ciudades_estados
#>        ciudad     estado   gobernador
#> 1  Nueva York Nueva York         <NA>
#> 2 Los Ángeles California Gavin Newsom
#> 3     Chicago   Illinois         <NA>
#> 4     Houston      Texas  Greg Abbott

En este ejemplo, left_join() combina los data frames df_ciudades y df_estados por la columna estado. Observa que las ciudades “Nueva York” y “Chicago” tienen valores NA en la columna gobernador, ya que sus estados (“Nueva York” e “Illinois”) no están presentes en el data frame df_estados.

Las otras funciones de unión (right_join(), inner_join() y full_join()) funcionan de manera similar, pero con diferentes criterios para combinar las filas de los data frames.

  • right_join() hace lo opuesto a left_join(): mantiene todas las filas del data frame de la derecha y solo las filas del data frame de la izquierda que coinciden.
  • inner_join() solo mantiene las filas que tienen coincidencias en ambos data frames.
  • full_join() mantiene todas las filas de ambos data frames, añadiendo NA donde no hay coincidencias.

Puedes consultar la documentación de dplyr para obtener más información sobre estas funciones.

3.4.7 Ejemplos

Las funciones de dplyr que hemos visto nos permiten realizar transformaciones de datos complejas para responder preguntas específicas sobre nuestra mudanza a Estados Unidos. Veamos algunos ejemplos con código en R:

  • Filtrar las ciudades con un buen sistema educativo y un bajo costo de vida:

    # Supongamos que tenemos un data frame "df_ciudades" con información 
    # sobre ciudades, incluyendo columnas "sistema_educativo" y "costo_vida"
    
    df_ciudades %>% 
      filter(sistema_educativo == "Bueno" & costo_vida < 2.5)
  • Ordenar los estados por su PIB per cápita:

    # Supongamos que tenemos un data frame "df_estados" con información 
    # sobre estados, incluyendo columnas "pib" y "poblacion"
    
    df_estados %>% 
      mutate(pib_per_capita = pib / poblacion) %>% 
      arrange(desc(pib_per_capita))
  • Unir un data frame de ciudades con un data frame de información climática:

    # Supongamos que tenemos un data frame "df_ciudades" con información 
    # sobre ciudades, incluyendo la columna "ciudad", y un data frame 
    # "df_clima" con información climática, incluyendo la columna "ciudad"
    
    df_ciudades_clima <- left_join(df_ciudades, df_clima, by = "ciudad")

Con estas herramientas, podrás explorar y analizar la información sobre Estados Unidos para tomar la mejor decisión sobre tu mudanza.

3.5 Ejercicios

  1. Reporta las columnas abreviación del estado abb y la población population del data frame murders
Solución
murders %>%
  select(abb, population)
  1. Reporta todos los datos de data frame que no sean de la región sur (“South” en inglés).
Solución
murders %>%
  filter(region != "South")

Si queremos filtrar todos los registros que sean de la región South y West usaremos %in% en vez de == para comparar versus un vector

  1. Crea el vector sur_y_oeste que contenga los valores “South” y “West”. Luego filtra los registros que sean de esas dos regiones.
Solución
sur_y_oeste <- c("South", "West")
  
murders %>%
  filter(region %in% sur_y_oeste)
  1. Agrega la columna ratio al data frame murders con el ratio de asesinatos por 100 mil habitantes. Luego, filtra los que tengan un ratio menor a 0.5 y sean de las regiones “South” y “West”. Reporta las columnas state, abb y ratio.
Solución
data(murders)

sur_y_oeste <- c("South", "West")
  
murders <- murders %>%
  mutate(ratio = total/population*100000) %>%
  filter(ratio < 0.9 & region %in% sur_y_oeste) %>%
  select(state, abb, ratio)

murders

Para ordenar usando pipeline utilizamos la función arrange(x), donde xes el nombre de la columna que queremos tomar tomo referencia la cual ordenará de forma ascendente o arrange(desc(x)) para ordenar en forma descendente.

  1. Modifica el código generado en el ejercicio anterior para ordenar el resultado por el campo ratio.
Solución
data(murders)

sur_y_oeste <- c("South", "West")
  
murders <- murders %>%
  mutate(ratio = total/population*100000) %>%
  filter(ratio < 0.9 & region %in% sur_y_oeste) %>%
  select(state, abb, ratio) %>%
  arrange(ratio)

murders
Así, finalmente podemos saber qué opciones de estados tenemos para poder mudarnos y resolver el caso presentado.

3.6 Data frames en gráficos

Ahora veremos algunas funciones que nos permiten visualizar nuestra data. Poco a poco iremos construyendo gráficos más complejos y visualmente más estéticos para presentar. Primero veamos las funciones más básicas que R nos presenta. En el siguiente capítulo veremos más a detalle los tipos de gráficos y en qué situaciones es recomendable usar uno u otro gráfico.

3.6.1 Gráficos de dispersión

Uno de los gráficos más utilizados en R es el gráfico o diagrama de dispersión, el cual es un tipo de diagrama matemático que utiliza las coordenadas cartesianas para mostrar los valores de dos variables para un conjunto de datos (Jarrell 1994, 492). Por defecto asumimos que las variables a analizar son independientes. Así, el diagrama de dispersión mostrará el grado de correlación (no causalidad) entre las dos variables.

La manera más sencilla de graficar un gráfico de dispersión es con la función plot(x,y), donde x y y son vectores que indican las coordenas del eje-x y las coordenadas del eje-y de cada punto que queremos graficar. Por ejemplo, veamos la relación entre el tamaño de la población y el total de asesinatos.

# Almacenemos en el objeto eje_x los datos de población
eje_x <- murders$population

# Almacenemos en el objeto eje_x los datos de población
eje_y <- murders$total

# Con este código creamos el gráfico de dispersión
plot(eje_x, eje_y)

Podemos ver una correlación entre la población y el número de casos. Transformemos el eje_x dividiendo por un millón (\({10}^6\)). Así tendremos el eje x expresado en millones.

eje_x <- murders$population/10^6
eje_y <- murders$total

plot(eje_x, eje_y)

3.6.2 Histogramas

También podemos crear histogramas a partir de un vector con la función hist.

data(murders)

murders <- murders %>%
  mutate(ratio = total/population*100000)

hist(murders$ratio)

La facilidad que nos da R para crear gráficos nos ahorrará tiempo para el análisis. De aquí podemos ver rápidamente que la mayoría de estados tiene un ratio < 5.

3.6.3 Diagrama de cajas

Finalmente, R nos permite crear diagramas de cajas de forma sencilla con la función boxplot. Asi, si quisiéramos analizar la distribución del ratio usaríamos el siguiente código:

boxplot(murders$ratio)

3.7 Interpretación de datos

Hemos visto gráficos que se pueden generar con una línea de código, pero necesitamos interpretarlos. Para ello, necesitamos aprender o recordar algunos estadísticos. Vamos a aprender a lo largo de este libro concepto estadísticos no entrando a profundidad en la parte matemática, sino desde la parte práctica y aprovechando que ya existen las funciones en R.

Recordemos nuestro caso/problemática. Tenemos un listado de asesinatos en cada uno de los 51 estados. Si los ordenemos por la columna total tendríamos:

murders %>% 
  arrange(total) %>%
  head()
#>           state abb        region population total     ratio
#> 1       Vermont  VT     Northeast     625741     2 0.3196211
#> 2  North Dakota  ND North Central     672591     4 0.5947151
#> 3 New Hampshire  NH     Northeast    1316470     5 0.3798036
#> 4       Wyoming  WY          West     563626     5 0.8871131
#> 5        Hawaii  HI          West    1360301     7 0.5145920
#> 6  South Dakota  SD North Central     814180     8 0.9825837

R nos provee con la función summary(), la cual nos da un resumen de los datos de un vector.

summary(murders$total)
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>     2.0    24.5    97.0   184.4   268.0  1257.0
  1. Min.: Mínimo valor del vector
  2. 1st Qu.: Primer cuartil
  3. Median: Mediana o segundo cuartil
  4. Mean: Promedio
  5. 3rd Qu.: Tercer cuartil
  6. Max.: Máximo valor del vector

3.7.1 Cuartiles

Para comprender los cuartiles visualicemos el total de los datos de forma ordenada. Para solo obtener una sola columna en pipeline usaremos .$ antes del nombre de la variable:

murders %>% 
  arrange(total) %>%
  .$total
#>  [1]    2    4    5    5    7    8   11   12   12   16   19   21   22   27   32
#> [16]   36   38   53   63   65   67   84   93   93   97   97   99  111  116  118
#> [31]  120  135  142  207  219  232  246  250  286  293  310  321  351  364  376
#> [46]  413  457  517  669  805 1257

Los cuartiles dividen nuestro vector en 4 partes con la misma cantidad de datos. Dado que tenemos 51 valores, tendríamos grupos de 51/4 = 12.75. Tendríamos grupos de 13 valores (3 grupos de 13 elementos y uno de 12 elementos).

Por ejemplo, el primer grupo estaría compuesto por estos números:

#>  [1]  2  4  5  5  7  8 11 12 12 16 19 21 22

El segundo grupo estaría compuesto por estos números:

#>  [1] 27 32 36 38 53 63 65 67 84 93 93 97 97

Y así sucesivamente. En total 4 grupos conformado por el 25% de los datos c/u.

3.7.1.1 Primer cuartil

Por lo tanto, cuando veamos el 1er cuartil, 1st Qu., pensemos que ese es el corte que nos indica hasta dónde puedo encontrar al 25% de los datos.

summary(murders$total)
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>     2.0    24.5    97.0   184.4   268.0  1257.0

En nuestro ejemplo 24.5 indica que todo número menor o igual que es número estará dentro del 25% de los primeros datos (25% de 51 datos = 12.75, redondeado a 13 datos).

Si listamos los números menores o iguales a 24.5 tendremos este listado:

murders %>%
  arrange(total) %>%
  filter(total <= 24.5) %>%
  .$total
#>  [1]  2  4  5  5  7  8 11 12 12 16 19 21 22

Que es exactamente el mismo listado que obtuvimos anteriormente para el primer grupo.

3.7.1.2 Segundo cuartil o mediana

El segundo cuartil, también llamado la mediana (Median), nos indica el corte del segundo grupo. El primer grupo contiene los primeros 25% datos, el segundo grupo tiene 25% adicionales. Así que este corte nos daría exactamente el valor que se encuentra al medio.

summary(murders$total)
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>     2.0    24.5    97.0   184.4   268.0  1257.0

En nuestro ejemplo 97 indica que debajo de es número encontraremos 50% del total de datos (50% de 51 datos = 25.5, redondeado a 26 datos).

murders %>%
  arrange(total) %>%
  filter(total <= 97) %>%
  .$total
#>  [1]  2  4  5  5  7  8 11 12 12 16 19 21 22 27 32 36 38 53 63 65 67 84 93 93 97
#> [26] 97

3.7.1.3 Tercer cuartil

El tercer cuartil es el corte del tercer grupo. Hasta la mediana ya teníamos 50%, si agregamos otro 25% de datos tendríamos 75%.

summary(murders$total)
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>     2.0    24.5    97.0   184.4   268.0  1257.0

En nuestro ejemplo 268 indica que debajo de es número encontraremos 75% del total de datos (75% de 51 datos = 26.01, redondeado a 26 datos).

3.7.2 Interpretación del gráfico de cajas

Ya estamos listos para crear un gráfico de cajas con el total de asesinatos e interpretar los resultados.

boxplot(murders$total)

La caja empieza en el valor 24.5 (primer cuartil) y termina en el valor de 268 (tercer cuartil). La línea ancha representa la mediana (segundo cuartil), 97 en nuestro ejemplo.

Entre el primer cuartil y el tercer cuartil (entre 24.5 y 97 para nuestro ejemplo) encontraremos 50% de la data, también llamado rango intercuartil or IQRpor sus siglas en inglés.

Fuera de la caja vemos una línea vertical hacia arriba y otra hacia abajo, mostrando el rango de nuestros datos. Fuera de esas líneas vemos unos puntos que son datos atípicos muy alejados de la media, conocidos como outliers.

Podemos encontrar rápidamente a qué estados pertenecen estos datos extremos si ordenamos la tabla descendentemente usando la función desc:

murders %>%
  arrange(desc(total)) %>%
  head()
#>          state abb        region population total    ratio
#> 1   California  CA          West   37253956  1257 3.374138
#> 2        Texas  TX         South   25145561   805 3.201360
#> 3      Florida  FL         South   19687653   669 3.398069
#> 4     New York  NY     Northeast   19378102   517 2.667960
#> 5 Pennsylvania  PA     Northeast   12702379   457 3.597751
#> 6     Michigan  MI North Central    9883640   413 4.178622

Vemos que en California se reportaron 1257 casos. Ese es uno de los datos extremo que vemos en el gráfico de cajas.

3.7.3 Ejemplos

  1. Crea la variable pob_log10 y almacena los datos del logaritmo 10 de la población (función log10()). Realiza la misma transformación a logaritmo 10 para el total de asesinatos y almacénalo en la variable tot_log10. Genera un gráfico de dispersión de estas dos variables.
pob_log10 <- log10(murders$population)
tot_log10 <- log10(murders$total)

plot(pob_log10, tot_log10)

  1. Crea un histograma de la población en millones (dividido por \({10}^6\)).
hist(murders$population/10^6)

  1. Crea un diagrama de cajas de la población.
boxplot(murders$population)

3.8 Ejercicios

A continuación, encontrarás una serie de ejercicios con diferentes niveles de dificultad. Es hora de poner en práctica lo que has aprendido en este capítulo. Recuerda que puedes usar las funciones de dplyr como filter(), arrange(), mutate(), summarize(), group_by() y left_join() para manipular los data frames.

  1. Crea un data frame llamado mis_gastos con las siguientes columnas:

    • categoria: Un factor con las categorías “Vivienda”, “Transporte”, “Alimentación” y “Entretenimiento”.
    • enero: Gastos en enero para cada categoría.
    • febrero: Gastos en febrero para cada categoría.
    • marzo: Gastos en marzo para cada categoría.
Solución
mis_gastos <- data.frame(
  categoria = factor(c("Vivienda", "Transporte", "Alimentación", "Entretenimiento")),
  enero = c(1500, 300, 500, 200),
  febrero = c(1500, 250, 400, 150),
  marzo = c(1500, 350, 550, 250)
)

mis_gastos
#>         categoria enero febrero marzo
#> 1        Vivienda  1500    1500  1500
#> 2      Transporte   300     250   350
#> 3    Alimentación   500     400   550
#> 4 Entretenimiento   200     150   250
  1. Usa las funciones head(), tail(), str() y summary() para explorar el data frame mis_gastos.
Solución
head(mis_gastos)
#>         categoria enero febrero marzo
#> 1        Vivienda  1500    1500  1500
#> 2      Transporte   300     250   350
#> 3    Alimentación   500     400   550
#> 4 Entretenimiento   200     150   250
tail(mis_gastos)
#>         categoria enero febrero marzo
#> 1        Vivienda  1500    1500  1500
#> 2      Transporte   300     250   350
#> 3    Alimentación   500     400   550
#> 4 Entretenimiento   200     150   250
str(mis_gastos)
#> 'data.frame':    4 obs. of  4 variables:
#>  $ categoria: Factor w/ 4 levels "Alimentación",..: 4 3 1 2
#>  $ enero    : num  1500 300 500 200
#>  $ febrero  : num  1500 250 400 150
#>  $ marzo    : num  1500 350 550 250
summary(mis_gastos)
#>            categoria     enero         febrero         marzo       
#>  Alimentación   :1   Min.   : 200   Min.   : 150   Min.   : 250.0  
#>  Entretenimiento:1   1st Qu.: 275   1st Qu.: 225   1st Qu.: 325.0  
#>  Transporte     :1   Median : 400   Median : 325   Median : 450.0  
#>  Vivienda       :1   Mean   : 625   Mean   : 575   Mean   : 662.5  
#>                      3rd Qu.: 750   3rd Qu.: 675   3rd Qu.: 787.5  
#>                      Max.   :1500   Max.   :1500   Max.   :1500.0
  1. Accede a la columna febrero del data frame mis_gastos usando el operador $. Luego, accede a la segunda fila del data frame usando corchetes.
Solución
mis_gastos$febrero
#> [1] 1500  250  400  150
mis_gastos[2, ]
#>    categoria enero febrero marzo
#> 2 Transporte   300     250   350
  1. Filtra el data frame mis_gastos para obtener solo las filas donde los gastos en enero sean mayores a 400.
Solución
mis_gastos %>%
  filter(enero > 400)
#>      categoria enero febrero marzo
#> 1     Vivienda  1500    1500  1500
#> 2 Alimentación   500     400   550
  1. Ordena el data frame mis_gastos de forma descendente según los gastos en marzo.
Solución
mis_gastos %>%
  arrange(desc(marzo))
#>         categoria enero febrero marzo
#> 1        Vivienda  1500    1500  1500
#> 2    Alimentación   500     400   550
#> 3      Transporte   300     250   350
#> 4 Entretenimiento   200     150   250
  1. Agrega una columna llamada total al data frame mis_gastos que contenga la suma de los gastos de enero, febrero y marzo para cada categoría.
Solución
mis_gastos <- mis_gastos %>% 
  mutate(total = enero + febrero + marzo)
  1. Calcula la media y la desviación estándar de los gastos totales para cada categoría en el data frame mis_gastos.
Solución
mis_gastos %>% 
  summarize(media_total = mean(total), 
            desviacion_total = sd(total))
#>   media_total desviacion_total
#> 1      1862.5         1793.216
  1. Agrupa el data frame mis_gastos por categoría y calcula la suma de los gastos para cada mes.
Solución
mis_gastos %>% 
  group_by(categoria) %>% 
  summarize(suma_enero = sum(enero), 
            suma_febrero = sum(febrero), 
            suma_marzo = sum(marzo))
#> # A tibble: 4 × 4
#>   categoria       suma_enero suma_febrero suma_marzo
#>   <fct>                <dbl>        <dbl>      <dbl>
#> 1 Alimentación           500          400        550
#> 2 Entretenimiento        200          150        250
#> 3 Transporte             300          250        350
#> 4 Vivienda              1500         1500       1500
  1. Analiza visualmente el siguiente gráfico que describe la distribución del total de asesionatos por regiones. Solo visualizándolo podrías señalar ¿qué región tiene el menor rango de datos, obviando outliers? ¿qué región tiene la mediana más alta?

Solución

El oeste (West) tiene el menor rango de datos y tiene dos outliers. El sur (South) tiene la mediana más alta entre todas las regiones.

Analizar únicamente viendo un gráfico nos permite colocarnos en los zapatos del observador final y comprender si solo con la información presentada se puede tomar decisiones.
  1. Crea el vector sur donde almacenes los datos filtrados del total de asesinatos que ocurrieron en la región sur. Luego, crea un histograma del vector sur.
Solución
sur <- murders %>%
  filter(region == "South") %>%
  .$total

hist(sur)
  1. Crea un nuevo data frame llamado df_ciudades_clima que combine la información de df_ciudades y df_clima (debes crear el data frame df_clima con información climática de las ciudades). Asegúrate de que el data frame resultante contenga todas las ciudades de df_ciudades, incluso si no tienen información climática en df_clima.
Solución
df_clima <- data.frame(
  ciudad = c("Nueva York", "Los Ángeles", "Chicago"),
  temperatura_promedio = c(12.8, 17.7, 10.7),  # En grados Celsius
  precipitacion_anual = c(1269, 373, 965)  # En milímetros
)

df_ciudades_clima <- left_join(df_ciudades, df_clima, by = "ciudad")
  1. Crea un data frame con algunos valores faltantes (NA). Luego, reemplaza los valores faltantes por la media de los valores no faltantes en la misma columna.
Solución
# Crear un data frame con valores faltantes
df_con_na <- data.frame(
  x = c(1, 2, NA, 4, 5),
  y = c(NA, 7, 8, NA, 10)
)

df_con_na
#>    x  y
#> 1  1 NA
#> 2  2  7
#> 3 NA  8
#> 4  4 NA
#> 5  5 10

# Reemplazar los valores faltantes por la media
df_con_na <- df_con_na %>% 
  mutate(x = ifelse(is.na(x), mean(x, na.rm = TRUE), x),
         y = ifelse(is.na(y), mean(y, na.rm = TRUE), y))

df_con_na
#>   x         y
#> 1 1  8.333333
#> 2 2  7.000000
#> 3 3  8.000000
#> 4 4  8.333333
#> 5 5 10.000000
  1. Crea una función llamada limpiar_data_frame() que reciba un data frame como argumento y reemplace los valores faltantes por la media de los valores no faltantes en cada columna.
Solución
limpiar_data_frame <- function(df) {
  for (col in names(df)) {
    if (is.numeric(df[[col]])) {
      df[[col]] <- ifelse(is.na(df[[col]]), mean(df[[col]], na.rm = TRUE), df[[col]])
    }
  }
  return(df)
}

## Probar la función creada
# Crear un data frame con valores faltantes para probar la función
df_prueba <- data.frame(
  edad = c(25, 30, NA, 28, 35),
  altura = c(1.75, 1.80, 1.65, NA, 1.70),
  peso = c(70, 80, 75, 65, NA)
)

df_prueba
#>   edad altura peso
#> 1   25   1.75   70
#> 2   30   1.80   80
#> 3   NA   1.65   75
#> 4   28     NA   65
#> 5   35   1.70   NA

# Aplicar la función al data frame de prueba
df_limpio <- limpiar_data_frame(df_prueba)

# Mostrar el data frame limpio
df_limpio
#>   edad altura peso
#> 1 25.0  1.750 70.0
#> 2 30.0  1.800 80.0
#> 3 29.5  1.650 75.0
#> 4 28.0  1.725 65.0
#> 5 35.0  1.700 72.5

Referencias

Jarrell, Stephen B. 1994. Basic Statistics. 1st ed. US: Brown (William C.) Co.