Extraer el máximo valor de una fila

tipito
tip
análisis
dplyr
data wrangling
Author

Checho

Published

October 13, 2022

Extraer el máximo valor de una fila

En este pequeño tutorial (por eso el tag de tipito, o sea un tip chiquito), voy a contar cómo resolví un problema que me encontré en el trabajo y me trajo más de un dolor de cabeza.

Estaba trabajando con una tabla en la que tenía varios cursos, con sus fechas de finalización, y para el análisis que estaba haciendo necesitaba extraer la fecha del último curso completado por cada persona (el valor más alto).

El problema es que cuando estaba ejecutando la función max() en vez de obtener el valor más alto de la fila, obtenía el valor más alto de la columna. Así que en este post vamos a ver la función rowwise() que permite resolver este inconveniente.

En este ejemplo vamos a reemplazar las fechas por un número, que a los fines prácticos plantea el mismo problema.

Datos de ejemplo

Primero carguemos la librería dplyr que además de contener la función rowwise() nos permite trabajar con el pipe %>% para simplificar la lectura del código. Luego crearemos un data frame de ejemplo con datos inventados

# En caso que no esté instalado 'dplyr' primero ejecutar install.package("dplyr")
library(dplyr)

# Creación de datos
ejemplo <- data.frame("Nombre" = c("Carla", "Daniela", "Sergio", "Yanel"),
                    "Valor A" = c(12, 8, 300, 17),
                    "Valor B" = c(5, 21, 18, 400),
                    "Valor C" = c(39, 200, 26, 64), 
                    "Valor D" = c(100, 43, 86, 12))

Ahora veamos cómo quedan los datos

# Ejecutar para ver el contenido del data frame
ejemplo
   Nombre Valor.A Valor.B Valor.C Valor.D
1   Carla      12       5      39     100
2 Daniela       8      21     200      43
3  Sergio     300      18      26      86
4   Yanel      17     400      64      12

Lo que necesitaba lograr (en un archivo con muchas más columnas que en este ejemplo) es poner en una columna nueva el valor más alto para cada persona. Entonces para Carla esperaba que el resultado fuera 100, para Daniela 200 y así sucesivamente.

Instintivamente lo que hice para intentar obtener el valor más alto de cada caso, fue usar dentro de una función mutate() (para crear una columna nueva) la función max() a un vector con los nombres de las 4 columnas.

ejemplo %>% 
  mutate("Valor Máximo" = max(c(Valor.A, Valor.B, Valor.C, Valor.D)))
   Nombre Valor.A Valor.B Valor.C Valor.D Valor Máximo
1   Carla      12       5      39     100          400
2 Daniela       8      21     200      43          400
3  Sergio     300      18      26      86          400
4   Yanel      17     400      64      12          400

Claramente no es es el resultado que esperaba, así que mi reacción fue la siguiente:

La solución

El problema del enfoque anterior es que la función max() busca entre todos los datos que le pasamos, las 4 columnas con los valores numéricos, y lo que nos devuelve el valor máximo de entre todas las celdas. Este es un claro ejemplo de que R está haciendo lo que le dijimos que haga, no lo que estábamos queriendo que haga.

Para resolver esto, antes de crear una columna nueva con mutate(), usamos la función rowwise().

ejemplo %>% 
  rowwise() %>%   # Con esta función indicamos que queremos los cálculos sobre las filas
  mutate("Valor Máximo" = max(c(Valor.A, Valor.B, Valor.C, Valor.D)))
# A tibble: 4 × 6
# Rowwise: 
  Nombre  Valor.A Valor.B Valor.C Valor.D `Valor Máximo`
  <chr>     <dbl>   <dbl>   <dbl>   <dbl>          <dbl>
1 Carla        12       5      39     100            100
2 Daniela       8      21     200      43            200
3 Sergio      300      18      26      86            300
4 Yanel        17     400      64      12            400

La función rowwise() lo que nos permite hacer es cálculos sobre las filas. Dependiendo el caso de uso se puede usar esta alternativa, o “pivotear” o transponer la tabla para que las columnas queden dentro de las filas y luego combinar group_by() y summarise() para calcular el valor máximo para cada persona.

Este enfoque sería así:

library(tidyr)

ejemplo_largo <- ejemplo %>% 
  # Pivoteamos los datos a un formato 'largo'
  pivot_longer(cols = c("Valor.A", "Valor.B", "Valor.C", "Valor.D"), 
               names_to = "Variable",
               values_to = "Valor")

# Veamos el dataset transformado
ejemplo_largo
# A tibble: 16 × 3
   Nombre  Variable Valor
   <chr>   <chr>    <dbl>
 1 Carla   Valor.A     12
 2 Carla   Valor.B      5
 3 Carla   Valor.C     39
 4 Carla   Valor.D    100
 5 Daniela Valor.A      8
 6 Daniela Valor.B     21
 7 Daniela Valor.C    200
 8 Daniela Valor.D     43
 9 Sergio  Valor.A    300
10 Sergio  Valor.B     18
11 Sergio  Valor.C     26
12 Sergio  Valor.D     86
13 Yanel   Valor.A     17
14 Yanel   Valor.B    400
15 Yanel   Valor.C     64
16 Yanel   Valor.D     12
# Ahora hacemos el cálculo combinando 'group_by' y 'summarise'
ejemplo_largo %>% 
  group_by(Nombre) %>% 
  summarise("Valor Máximo" = max(Valor))
# A tibble: 4 × 2
  Nombre  `Valor Máximo`
  <chr>            <dbl>
1 Carla              100
2 Daniela            200
3 Sergio             300
4 Yanel              400

Claramente este es otro enfoque, y depende de la necesidad es una opción válida. Pero en este caso particular necesitaba mantener una fila para cada persona porque después iba a exportar esta tabla a un archivo que luego es cargado en un tablero en Power BI.

Así que, una vez logrado mi objetivo, me dispuse a celebrar como corresponde.