library(tidyverse)
example_data <- read_csv("https://raw.githubusercontent.com/SurgicalInformatics/healthyr_book/master/example_data.csv")
View(example_data)R para Ciencia de Datos de Salud
Capitulo 2 R básico
A lo largo de este libro somos conscientes del equilibrio entre la teoría y la práctica. Algunos alumnos pueden preferir ver todas las definiciones presentadas antes de que se les muestre un ejemplo de un nuevo concepto. Otros preferirían ver ejemplos prácticos y explicaciones que se desarrollen hasta lograr una comprensión completa con el tiempo. Logramos un equilibrio entre estos dos enfoques que funciona bien para la mayoría de las personas en la audiencia.
A veces, le mostraremos un ejemplo que puede usar palabras que aún no se han presentado formalmente. Por ejemplo, comenzamos este capítulo con la importación de datos: R no es nada sin datos.
Al hacerlo, tenemos que usar la palabra “argumento”, que solo se define dos secciones más adelante (en”Objetos y funciones”). Algunas instancias similares surgen en torno a conceptos estadísticos en la parte de Análisis de datos del libro. Encontrará oraciones del tipo “este concepto se aclarará en la siguiente sección”. Confía en nosotros y déjate llevar.
El objetivo de este capítulo es familiarizarlo con el funcionamiento de R. Leeremos los datos y comenzaremos las manipulaciones básicas. Es posible que desee omitir partes de este capítulo si ya:
- haber encontrado la interfaz Importar conjunto de datos;
- saber cómo se ven los números, caracteres, factores y fechas en R;
- están familiarizados con la terminología sobre objetos, funciones, argumentos;
- han usado la tubería:
%>%; - saber cómo filtrar datos con operadores como
==, >, <, &, |; - saber cómo manejar los datos faltantes (NA) y por qué pueden comportarse de manera extraña en un filtro;
- han usado
mutate(),c(),paste(),if_else()y las uniones.
2.1. Leer datos en R
Los datos generalmente vienen en forma de tabla, como una hoja de cálculo o una base de datos. En el mundo del tidyverse , una tabla leída en R se llama tibble .
Un formato común en el que recibir datos es CSV (valores separados por comas). CSV es una hoja de cálculo sin complicaciones y sin formato. Es solo una tabla con filas y columnas (sin hojas de cálculo ni fórmulas). Además, no necesita un software especial para ver rápidamente un archivo CSV; bastará con un editor de texto, y eso incluye RStudio.
Por ejemplo, mire “example_data.csv” en la carpeta del proyecto SaludR en la Figura (este es el panel Archivos en la esquina inferior derecha de su RStudio).
Al hacer clic en un archivo de datos nos da dos opciones:“View File” o “Import Dataset”.
Le mostraremos cómo usar la interfaz Importar conjunto de datos en un momento, pero para los archivos CSV estándar, generalmente no nos molestamos con la interfaz Importar y simplemente escribimos (o copiamos de un script anterior):
Hay un par de cosas que decir sobre la primera parte del código R de este libro. Lo primero y más importante: no se asuste. Sí, si está acostumbrado a interactuar con los datos haciendo doble clic en una hoja de cálculo que se abre, entonces el código R anterior parece un poco complicado.
Sin embargo, ejecutar el ejemplo anterior también tiene un efecto visual inmediato. Tan pronto como haga clic en Ejecutar (o presione Ctrl+Enter/Comando+Enter), el conjunto de datos aparece inmediatamente en su Entorno y se abre en un Visor. Puede echar un vistazo y desplazarse de la misma manera que lo haría en Excel o similar.
Entonces, ¿qué está pasando realmente en el código R anterior?
- Cargamos los paquetes tidyverse (como se explica en el primer capítulo de este libro).
- Tenemos un archivo CSV llamado “example_data.csv” y estamos usando
read_csv()para leerlo en R. - Estamos usando la flecha de asignación
<-para guardarlo en nuestro entorno usando el mismo nombre:example_data. - La línea
View(example_data)hace que aparezca para que la veamos. Alternativamente, haga clic enexample_dataen el entorno para lograr exactamente lo mismo.
Más adelante en este capítulo se cubre más sobre la flecha de asignación ( <- ) y nombrar cosas en R. No se preocupe si todo no está muy claro en este momento.
2.1.1 Interfaz de importación de conjunto de datos
En el ejemplo read_csv() anterior, leemos un archivo que estaba en un formato específico (pero común).
Sin embargo, si su archivo usa puntos y comas en lugar de comas, o comas en lugar de puntos, o un número especial para valores faltantes (por ejemplo, 99), o cualquier otra cosa extraña o complicada, entonces necesitamos un enfoque diferente.
La interfaz Importar conjunto de datos de RStudio puede manejar todo esto y más.
Después de seleccionar las opciones específicas para importar un archivo en particular, una ventana de vista previa amigable mostrará si R comprende correctamente el formato de sus datos.
NO TENGAS la tentación de presionar el botón Importar .
Sí, esto se leerá en su conjunto de datos una vez, pero significa que debe volver a seleccionar las opciones cada vez que regrese a RStudio. En su lugar, copie y pegue el código en su secuencia de comandos R. De esta manera puedes usarlo una y otra vez.
Asegurarse de que todos los pasos de un análisis se registren en scripts hace que su flujo de trabajo sea reproducible por su futuro yo, colegas, supervisores y extraterrestres.
El botón
Importar conjunto de datostambién puede ayudarlo a leer archivos de Excel, SPSS, Stata o SAS (en lugar deread_csv(), le daráread_excel(),read_sav(),read_stata ()oread_sas()). Si usó R antes o está usando scripts más antiguos aprobados por colegas, es posible que vearead.csv()en lugar deread_csv(). Tenga en cuenta el punto en lugar del guión bajo.
En resumen, read_csv() es más rápido y más predecible y se recomienda en todos los scripts nuevos.
En los scripts existentes que funcionan y están probados, no recomendamos que comience a reemplazar read.csv() con read_csv() . Por ejemplo, read_csv() maneja las variables categóricas de manera diferente 1. Es posible que un script R escrito con read.csv() ya no funcione como se espera si solo se reemplaza con read_csv() .
No comience a actualizar y posiblemente rompa los scripts R existentes reemplazando las funciones básicas de R con los equivalentes de tidyverse que mostramos aquí. Utilice las funciones modernas en cualquier código nuevo que escriba.
2.1.2 Lectura en el conjunto de datos de ejemplo de Global Burden of Disease
En los próximos capítulos de este libro, utilizaremos los conjuntos de datos de la carga global de morbilidad. El Global Burden of Disease Study (GBD) es el estudio epidemiológico observacional mundial más completo hasta la fecha. Describe la mortalidad y la morbilidad de las principales enfermedades, lesiones y factores de riesgo para la salud a nivel mundial, nacional y regional. 2
Los datos de GBD están disponibles públicamente en el sitio web. La tabla y la figura muestran una versión de alto nivel de los datos del proyecto con solo 3 variables: causa , año , death_millions (número de personas que mueren por cada causa cada año). Más adelante, usaremos un conjunto de datos más largo con diferentes subgrupos y le mostraremos cómo resumir conjuntos de datos completos usted mismo.
library(tidyverse)
gbd_short <- read_csv("https://raw.githubusercontent.com/SurgicalInformatics/healthyr_book/master/data/global_burden_disease_cause-year.csv")| year | cause | deaths_millions |
|---|---|---|
| 1990 | Communicable diseases | 15.36 |
| 1990 | Injuries | 4.25 |
| 1990 | Non-communicable diseases | 26.71 |
| 1995 | Communicable diseases | 15.11 |
| 1995 | Injuries | 4.53 |
| 1995 | Non-communicable diseases | 29.27 |
| 2000 | Communicable diseases | 14.81 |
| 2000 | Injuries | 4.56 |
| 2000 | Non-communicable diseases | 31.01 |
| 2005 | Communicable diseases | 13.89 |
| 2005 | Injuries | 4.49 |
| 2005 | Non-communicable diseases | 32.87 |
| 2010 | Communicable diseases | 12.51 |
| 2010 | Injuries | 4.69 |
| 2010 | Non-communicable diseases | 35.43 |
| 2015 | Communicable diseases | 10.88 |
| 2015 | Injuries | 4.46 |
| 2015 | Non-communicable diseases | 39.28 |
| 2017 | Communicable diseases | 10.38 |
| 2017 | Injuries | 4.47 |
| 2017 | Non-communicable diseases | 40.89 |
2.2 Tipos de variables y ¿por qué nos importan?
Hay tres grandes tipos de datos:
- continuo (números), en R: numérico, doble o entero;
- categórica, en R: carácter, factor o lógica (VERDADERO/FALSO);
- fecha/hora, en R: POSIXct fecha-hora3.
Los valores dentro de una columna tienen que ser todos del mismo tipo, pero un tibble, por supuesto, puede contener columnas de diferentes tipos. En general, R es bueno para averiguar qué tipo de datos tiene (en programación, este “descubrir” se llama “analizar”).
Por ejemplo, al leer datos, le dirá lo que se asumió para cada columna:
library(tidyverse)
typesdata <- read_csv("https://raw.githubusercontent.com/SurgicalInformatics/healthyr_book/master/data/typesdata.csv")Rows: 3 Columns: 4
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (2): id, group
dbl (1): measurement
dttm (1): date
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
typesdata# A tibble: 3 × 4
id group measurement date
<chr> <chr> <dbl> <dttm>
1 ID1 Control 1.8 2017-01-02 12:00:00
2 ID2 Treatment 4.5 2018-02-03 13:00:00
3 ID3 Treatment 3.7 2019-03-04 14:00:00
Esto significa que la mayor parte del tiempo no tiene que preocuparse por esas pequeñas etiquetas <chr> vs <dbl> vs <S3: POSIXct> . Pero en casos de datos de entrada irregulares o defectuosos, o al hacer muchos cálculos y modificaciones a sus datos, debemos ser conscientes de estos diferentes tipos para poder encontrar y corregir errores.
Por ejemplo, considere un archivo similar al anterior pero con algunos problemas de entrada de datos introducidos:
typesdata_faulty <- read_csv("https://raw.githubusercontent.com/SurgicalInformatics/healthyr_book/master/data/typesdata_faulty.csv")Rows: 3 Columns: 4
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (4): id, group, measurement, date
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
typesdata_faulty# A tibble: 3 × 4
id group measurement date
<chr> <chr> <chr> <chr>
1 ID1 Control 1.8 02-Jan-17 12:00
2 ID2 Treatment 4.5 03-Feb-18 13:00
3 ID3 Treatment 3.7 or 3.8 04-Mar-19 14:00
Observe que R analizó las variables de medición y fecha como caracteres. La medición se analizó como un carácter debido a un problema de ingreso de datos: la persona que tomó la medición no pudo decidir qué valor anotar (tal vez la escala estaba cambiando entre los dos valores), por lo que incluyeron ambos valores y el texto “o” en la célula.
Una variable numérica también se analizará como una variable categórica si contiene ciertos errores tipográficos, por ejemplo, si se ingresa como “3..7” en lugar de “3.7”.
La razón por la que R no entendía automáticamente la columna de fecha es que no podía decir cuál era la fecha y cuál el año: 02-ene-17 podría representar 02-ene-2017 así como 2002-Ene-17 .
Por lo tanto, si bien la mayor parte del tiempo no tiene que preocuparse por los tipos de variables y puede continuar con su análisis, es importante comprender cuáles son los diferentes tipos para estar listo para tratar con ellos cuando surjan problemas.
Dado que los conjuntos de datos de salud generalmente están llenos de datos categóricos, es crucial comprender la diferencia entre caracteres y factores (ambos son tipos de variables categóricas en R con pros y contras).
Así que, aquí vamos.
2.2.1 Variables numéricas (continuas)
Los números son sencillos de manejar y no suelen causar problemas. R generalmente se refiere a los números como numeric (o num ), pero a veces realmente se pone nervioso y llama a los números integer o double . Los enteros son números sin lugares decimales (por ejemplo, 1, 2, 3 ), mientras que doble significa formato de “coma flotante de doble precisión” (por ejemplo, 1.234, 5.67890 ).
Por lo general, no importa si R está clasificando sus datos continuos numeric/num/double/int , pero es bueno tener en cuenta estos diferentes términos, ya que los verá en los mensajes de R.
Algo a tener en cuenta sobre los números es que R no suele imprimir más de 6 decimales, pero eso no significa que no existan. Por ejemplo, del tibble typedata , tomamos la columna measurement y la enviamos a la función mean() . R luego calcula la media y nos dice cuál es con 6 decimales:
typesdata$measurement %>% mean()[1] 3.333333
Guardemos eso en un nuevo objeto:
measurement_mean <- typesdata$measurement %>% mean()Pero cuando se usa el operador de doble igual para verificar si esto es equivalente a un valor fijo (puede hacer esto cuando se compara con un umbral, o incluso con otro valor medio), R devuelve FALSE :
measurement_mean == 3.333333[1] FALSE
Ahora bien, esto no parece correcto, ¿verdad? R claramente nos dijo justo arriba que la media de esta variable es 3.333333 (recordatorio: los valores reales en la columna de medición son 1.8, 4.5, 3.7) La razón por la que la declaración anterior es “FALSE” es porque “measurement_mean” contiene silenciosamente más de 6 decimales.
Y se pone peor. En este ejemplo, puede reconocer que los decimales repetidos (0.333333…) generalmente significan que hay más de ellos en alguna parte. Y puedes pensar que redondearlos hacia abajo con la función round() haría que tu == se comportara como se esperaba. Excepto que no se trata de redondear, se trata de cómo las computadoras almacenan números con decimales. Las computadoras tienen problemas con los números decimales, y este ejemplo simple ilustra uno:
(0.10 + 0.05) == 0.15[1] FALSE
Esto devuelve FALSE, lo que significa que R no parece pensar que 0.10 + 0.05 es igual a 0.15 . Este problema no es específico de R, sino de los lenguajes de programación en general. Por ejemplo, Python también piensa que la suma de 0.10 y 0.05 no es igual a 0.15 .
Aquí es donde la función near() es útil:
library(tidyverse)
near(0.10+0.05, 0.15)[1] TRUE
near(measurement_mean, 3.333333, 0.000001)[1] TRUE
Los primeros dos argumentos para near() son los números que está comparando; el tercer argumento es la precisión que le interesa. Entonces, si los números son iguales dentro de esa precisión, devuelve TRUE. Puede omitir el tercer argumento: la precisión (en este caso, también conocida como tolerancia). Si lo hace, near() usará un valor de tolerancia predeterminado razonable.
2.2.2 Variables de carácter
Los caracteres (a veces denominados cadenas o cadenas de caracteres ) en R son letras, palabras o incluso oraciones completas (un ejemplo de esto pueden ser los comentarios de texto libre). Los caracteres se muestran entre "" (o '' ).
Una función útil para investigar rápidamente variables categóricas es la función count() :
library(tidyverse)
typesdata %>%
count(group)# A tibble: 2 × 2
group n
<chr> <int>
1 Control 1
2 Treatment 2
count() puede aceptar múltiples variables y contará el número de observaciones en cada subgrupo, por ejemplo, mydata %>% count(var1, var2).
Otra opción útil para contar es sort = TRUE , que ordenará el resultado colocando el recuento más alto ( n ) al principio.
typesdata %>%
count(group, sort = TRUE)# A tibble: 2 × 2
group n
<chr> <int>
1 Treatment 2
2 Control 1
count() con la opción sort = TRUE también es útil para identificar identificaciones duplicadas o errores ortográficos en sus datos. Con este ejemplo tibble ( typesdata ) que solo tiene tres filas, es fácil ver que la columna id es un identificador único, mientras que la columna group es una variable categórica.
Puede verificar todo con solo mirar el tibble usando la pestaña Visor integrada (haga clic en el conjunto de datos en la pestaña Entorno).
Pero para conjuntos de datos más grandes, debe saber cómo verificar y luego limpiar los datos mediante programación; no puede revisar miles de valores verificando que todos estén según lo previsto sin duplicados o errores tipográficos inesperados.
Para la mayoría de las variables (categóricas o numéricas), recomendamos siempre graficar sus datos antes de comenzar el análisis. Pero para buscar duplicados en un identificador único, use count() con sort = TRUE :
# todos los id son únicos:
typesdata %>%
count(id, sort = TRUE)# A tibble: 3 × 2
id n
<chr> <int>
1 ID1 1
2 ID2 1
3 ID3 1
# agregamos en una fila duplicada donde id = ID3,
# luego cuenta de nuevo:
typesdata %>%
add_row(id = "ID3") %>%
count(id, sort = TRUE)# A tibble: 3 × 2
id n
<chr> <int>
1 ID3 2
2 ID1 1
3 ID2 1
2.2.3 Variables factoriales (categóricas)
Factores son personajes quisquillosos. Los factores son quisquillosos porque incluyen algo llamado niveles . Los niveles son todos los valores únicos que podría tomar una variable de factor, por ejemplo, cuando observamos typesdata %>% count(group) . El uso de factores en lugar de solo caracteres puede ser útil porque:
- Los valores que pueden tomar los niveles de los factores son fijos. Por ejemplo, una vez que le diga a R que
typesdata$groupes un factor con dos niveles: Control y Tratamiento, combinarlo con otros conjuntos de datos con diferentes ortografías o abreviaturas para la misma variable generará una advertencia. Esto puede ser útil, pero también puede ser una molestia cuando realmente desea agregar otro nivel a una variablefactor. - Los niveles tienen un orden. Cuando se ejecutan pruebas estadísticas en datos agrupados (p. ej., control frente a tratamiento, adulto frente a niño) y la variable es solo un carácter, no un factor, R utilizará alfabéticamente primero como nivel de referencia (comparación). Convertir una columna de caracteres en una columna de factores nos permite definir y cambiar el orden de sus niveles. El orden de nivel afecta muchas cosas, incluidos los resultados y gráficos de regresión: de forma predeterminada, las variables categóricas se ordenan alfabéticamente. Si queremos un orden diferente en, por ejemplo, un diagrama de barras, debemos convertirlo en un factor y reordenarlo antes de graficarlo. La trama entonces ordenará los grupos correctamente.
Entonces, en general, dado que los datos de salud a menudo son categóricos y tienen un nivel de referencia (comparación), los factores son una forma esencial de trabajar con estos datos en R. Sin embargo, la irritabilidad de los factores a veces puede ser inútil o incluso frustrante. Más adelante se cubrirá mucho más sobre el manejo de factores.
2.2.4 Variables de fecha/hora
R es bueno para trabajar con fechas. Por ejemplo, puede calcular la cantidad de días/semanas/meses entre dos fechas, o puede usarse para encontrar una fecha futura (p. ej., “¿cuál es la fecha exactamente dentro de 60 días?”). También conoce las zonas horarias y se complace en analizar las fechas en prácticamente cualquier formato, siempre que le diga a R cómo se formatea su fecha (por ejemplo, día antes del mes, nombre del mes abreviado, año en 2 o 4 dígitos, etc.) . Dado que R muestra fechas y horas entre comillas (“), se parecen a los caracteres. Sin embargo, es importante saber si R ha entendido cuáles de sus columnas contienen información de fecha/hora y cuáles son solo caracteres normales.
library(lubridate) # lubridate facilita el trabajo con fechas
current_datetime <- Sys.time()
current_datetime[1] "2022-12-14 03:24:43 -04"
my_datetime <- "2022-12-13 23:20"
my_datetime[1] "2022-12-13 23:20"
Cuando se imprimen, los dos objetos - current_datetime y my_datetime parecen tener un formato similar. Pero si intentamos calcular la diferencia entre estas dos fechas, obtenemos un error:
my_datetime - current_datetime[1] "Error in `-.POSIXt`(my_datetime, current_datetime)"
Eso es porque cuando le asignamos un valor a my_datetime , R asumió el tipo más simple para él, es decir, un carácter. Podemos verificar cuál es el tipo de un objeto o variable usando la función class() :
current_datetime %>% class()[1] "POSIXct" "POSIXt"
my_datetime %>% class()[1] "character"
Entonces, debemos decirle a R que my_datetime sí incluye información de fecha/hora para que podamos usarla en los cálculos:
my_datetime_converted <- ymd_hm(my_datetime)
my_datetime_converted[1] "2022-12-13 23:20:00 UTC"
Calcular la diferencia ahora funcionará:
my_datetime_converted - current_datetimeTime difference of -8.078795 hours
Dado que R sabe que esta es una diferencia entre dos objetos de fecha/hora, los imprime de una forma agradablemente legible. Además, el resultado tiene su propio tipo; es un “tiempo de diferencia”.
my_datesdiff <- my_datetime_converted - current_datetime
my_datesdiff %>% class()[1] "difftime"
Esto es útil si queremos aplicar esta diferencia horaria en otra fecha, por ejemplo:
ymd_hm("2021-01-02 12:00") + my_datesdiff[1] "2021-01-02 03:55:16 UTC"
Pero si queremos usar el número de días en un cálculo normal, por ejemplo, ¿qué pasaría si una medida aumentara en 560 unidades arbitrarias durante este período de tiempo? Podríamos querer calcular el aumento por día de esta manera:
560/my_datesdiff[1] "Error in `/.difftime`(560, my_datesdiff)"
No funciona, no lo hace.
Necesitamos convertir my_datesdiff (que es un valor difftime) en un valor numérico usando la función as.numeric() :
560 / as.numeric( my_datesdiff )[1] -69.31727
El paquete lubridate viene con varias funciones convenientes para analizar fechas, por ejemplo, ymd() , mdy() , ymd_hm() , etc. - para obtener una lista completa, consulte lubridate.tidyverse.org.
Sin embargo, si su variable de fecha/hora viene en un formato extra especial, entonces use la función parse_date_time() donde el segundo argumento especifica el formato usando los especificadores dados en la Tabla
| Notation | Meaning | Example |
|---|---|---|
| %d | day as number | 01-31 |
| %m | month as number | 01-12 |
| %B | month name | January-December |
| %b | abbreviated month | Jan-Dec |
| %Y | 4-digit year | 2019 |
| %y | 2-digit year | 19 |
| %H | hours | 12 |
| %M | minutes | 01 |
| %S | seconds | 59 |
| %A | weekday | Monday-Sunday |
| %a | abbreviated weekday | Mon-Sun |
Por ejemplo:
parse_date_time("12:34 07/Jan'20", "%H:%M %d/%b'%y")[1] "2020-01-07 12:34:00 UTC"
Además, se pueden usar los mismos especificador de fecha/hora para reorganizar la fecha y la hora de impresión:
Sys.time()[1] "2022-12-14 03:24:44 -04"
Sys.time() %>% format("%H:%M on %B-%d (%Y)")[1] "03:24 on diciembre-14 (2022)"
Incluso puede agregar texto sin formato en la función format () , R sabrá poner los valores correctos de fecha/hora donde están los % :
Sys.time() %>% format("Feliz dia, el tiempo actual es %H:%M %B-%d (%Y)!")[1] "Feliz dia, el tiempo actual es 03:24 diciembre-14 (2022)!"
2.3 Objetos y funciones
Hay dos conceptos fundamentales en la programación estadística que es importante aclarar: objetos y funciones. El objeto más común con el que trabajará es un conjunto de datos. Suele ser algo con filas y columnas muy parecido al ejemplo de Tabla .
| id | sex | var1 | var2 | var3 |
|---|---|---|---|---|
| 1 | Male | 4 | NA | 2 |
| 2 | Female | 1 | 4 | 1 |
| 3 | Female | 2 | 5 | NA |
| 4 | Male | 3 | NA | NA |
Para obtener el “conjunto de datos” pequeño e inventado en su entorno, copie y ejecute este código4:
library(tidyverse)
mydata <- tibble(
id = 1:4,
sex = c("Male", "Female", "Female", "Male"),
var1 = c(4, 1, 2, 3),
var2 = c(NA, 4, 5, NA),
var3 = c(2, 1, NA, NA)
)Los datos pueden vivir en cualquier lugar: en papel, en una hoja de cálculo, en una base de datos SQL o en su entorno R. Por lo general, iniciamos e interactuamos con R usando RStudio, pero todo lo que hablamos aquí (objetos, funciones, entorno) también funciona cuando RStudio no está disponible, pero R sí. Este puede ser el caso si está trabajando en una supercomputadora que solo puede servir a R Console y no a RStudio.
2.3.1 data frame / tibble
Por lo tanto, los datos de forma regular en filas y columnas se denominan tabla cuando viven fuera de R, pero una vez que los lee/importa a R, se denomina tibble. Si has usado R antes, o te dan un fragmento de código que usa read.csv() en lugar de read_csv() , te habrás topado con el término data frame5
Un tibble es la versión moderna/ tidyverse de un marco de datos en R. En la mayoría de los casos, las “data frame” y los “tibbles” funcionan indistintamente, pero los “tibbles” a menudo funcionan mejor. Otra gran alternativa a las “data frame” básicos de R son las “tablas de datos” . En este libro, y para la mayor parte de nuestro trabajo diario en estos días, usaremos tibbles .
2.3.2 Nombrar objetos
Cuando lees datos en R, desea que aparezcan en la pestaña Entorno. Todo en su entorno necesita tener un nombre. Es probable que tenga muchos objetos, como tibbles, al mismo tiempo. Tenga en cuenta que tibble es lo que es la cosa, en lugar de su nombre. Esta es la ‘clase’ de un objeto.
Para que nuestros ejemplos de código sean fáciles de seguir, llamamos a nuestro ejemplo tibble mydata . En un análisis real, debe dar a sus tibbles nombres significativos, por ejemplo, datos_paciente , resultados_laboratorio , totales_anuales , etc. Los nombres de los objetos no pueden tener espacios, por lo que usamos el guión bajo ( _ ) para separar las palabras. Los nombres de los objetos pueden incluir números, pero no pueden comenzar con un número: por lo tanto , labdata2019 funciona, 2019labdata no.
Entonces, el tibble llamado mydata es un ejemplo de un objeto que puede estar en el entorno de su sesión R:
mydata# A tibble: 4 × 5
id sex var1 var2 var3
<int> <chr> <dbl> <dbl> <dbl>
1 1 Male 4 NA 2
2 2 Female 1 4 1
3 3 Female 2 5 NA
4 4 Male 3 NA NA
2.3.3 Función y sus argumentos
Una función es un procedimiento que toma alguna información (entrada), le hace algo y devuelve la información modificada (salida).
Una función simple que se puede aplicar a datos numéricos es mean() .
Las funciones R siempre tienen corchetes después de su nombre. Esto es por dos razones. Primero, las diferencias fácilmente como funciones: te acostumbrarás a leerlas así.
En segundo lugar, y más importante, podemos poner argumentos entre corchetes.
Los argumentos también se pueden considerar como entrada. En el análisis de datos, la entrada más común para una función son los datos. Por ejemplo, necesitamos darle a mean() algunos datos para promediar. No tiene sentido (ni funcionará) alimentar mean() todo el tibble con múltiples columnas, incluidas las identificaciones de los pacientes y una variable categórica ( sex).
Para extraer rápidamente una sola columna, usamos el símbolo $ así:
mydata$var1[1] 4 1 2 3
Puede ignorar el ## [1] al comienzo de los valores extraídos; esto es algo que se vuelve más útil cuando se imprimen varias líneas de datos, ya que el número entre corchetes cuenta cuántos valores estamos viendo.
Entonces podemos usar mydata$var1 como el primer argumento de mean() colocándolo entre corchetes:
mean(mydata$var1)[1] 2.5
lo que nos dice que la media de var1 ( 4, 1, 2, 3 ) es 2.5 . En este ejemplo, mydata$var1 es el primer y único argumento de mean() .
Pero, ¿qué sucede si tratamos de calcular el valor promedio de var2 ( NA, 4, 5, NA ) (recuerde, NA significa No aplicable/Disponible y se usa para indicar datos faltantes):
mean(mydata$var2)[1] NA
Entonces, ¿por qué mean(mydata$var2) devuelve NA (“no disponible”) en lugar de la media de los valores incluidos en esta columna? Esto se debe a que la columna incluye valores faltantes ( NAs ), y R no quiere promediar sobre NAs implícitamente. Es ser cauteloso: ¿qué pasaría si no supiera que faltan valores para algunos pacientes? Si quisiera comparar las medias de var1 y var2 sin más filtros, estaría comparando muestras de diferentes tamaños.
Podríamos esperar ver un ‘NA’ si intentáramos, por ejemplo, calcular el promedio de ‘sexo’ . Y este es de hecho el caso:
mean(mydata$sex)Warning in mean.default(mydata$sex): argument is not numeric or logical:
returning NA
[1] NA
Además, R también nos da una advertencia bastante clara que sugiere que no puede calcular la media de un argumento que no es numérico o lógico. La oración en realidad se lee bastante divertida, como si R estuviera diciendo que no era lógico calcular la media de algo que no es numérico.
Pero, R en realidad dice que está feliz de calcular la media de dos tipos de variables: numéricas o lógicas, pero lo que ha pasado no es ninguno.
Si decide ignorar las NA y desea calcular la media de todos modos, puede hacerlo agregando este argumento a mean() :
mean(mydata$var2, na.rm = TRUE)[1] 4.5
Agregar na.rm = TRUE le dice a R que está feliz de que calcule la media de cualquier valor existente (pero elimine - rm - los valores NA ). Esta ‘eliminación’ excluye los NA del cálculo, no afecta el tibble real ( mydata ) que contiene el conjunto de datos.
R distingue entre mayúsculas y minúsculas, por lo que na.rm , no NA.rm , etc. Sin embargo, no hay necesidad de memorizar cómo se escriben exactamente los argumentos de las funciones; para eso está la pestaña Ayuda (presione F1 cuando el cursor esté sobre el nombre de la función). Las páginas de ayuda están integradas en R, por lo que no se requiere una conexión a Internet para esto.
Asegúrese de separar múltiples argumentos con comas o R le dará un error de
Error: símbolo inesperado. Finalmente, algunas funciones no necesitan ningún argumento para funcionar. Un buen ejemplo esSys.time()que devuelve la hora y la fecha actuales. Esto es útil cuando se usa R para generar y actualizar informes automáticamente. Incluir esto significa que siempre puede tener claro cuándo se actualizaron los resultados por última vez.
Sys.time()[1] "2022-12-14 03:24:45 -04"
2.3.4 Trabajar con objetos
Para guardar un objeto en nuestro Entorno usamos la flecha de asignación:
a <- 103Esto dice: al objeto a se le asigna el valor ra . <- se llama “el operador de asignación de flecha”, o “flecha de asignación” para abreviar.
Atajos de teclado para insertar
<-:
Windows: Alt-
macOS: Opción-
Sabe que la tarea funcionó cuando aparece en la pestaña Entorno. Si ahora ejecutamosasolo, se nos devuelve:
a[1] 103
De manera similar, si ejecutamos una función sin asignación a un objeto, se imprime pero no se guarda en su entorno:
seq(15, 30) [1] 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
seq() es una función que crea una secuencia de números (+1 por defecto) entre los dos argumentos que le pasas entre paréntesis. Podemos asignar el resultado de seq(15, 30) a un objeto, llamémoslo example_sequence :
example_sequence <- seq(15, 30)Hacer esto crea example_sequence en nuestro entorno, pero no lo imprime. Para imprimirlo, ejecútelo en una línea separada como esta:
example_sequence [1] 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
Si guarda los resultados de una función R en un objeto, no se imprime. Si ejecuta una función sin la asignación (
<-), sus resultados se imprimen, pero no se guardan como un objeto.
Finalmente, a R no le importa sobrescribir un objeto existente, por ejemplo:
example_sequence <- example_sequence/2
example_sequence [1] 7.5 8.0 8.5 9.0 9.5 10.0 10.5 11.0 11.5 12.0 12.5 13.0 13.5 14.0 14.5
[16] 15.0
Observe cómo luego incluimos la variable en una nueva línea para imprimirla y sobrescribirla.
2.3.5 <- y =
Tenga en cuenta que muchas personas usan = en lugar de <- . Tanto <- como = pueden guardar lo que está a la derecha en un objeto con nombre a la izquierda. Aunque <- y = son intercambiables al guardar un objeto en su entorno, no son intercambiables cuando se usan como argumento de función. Por ejemplo, ¿recuerda cómo usamos el argumento na.rm en la función mean() y el resultado se imprimió de inmediato? Si queremos guardar el resultado en un objeto, haremos esto, donde mean_result podría ser cualquier nombre que elijas:
mean_result <- mean(mydata$var2, na.rm = TRUE) Observe cómo el ejemplo anterior usa ambos operadores: la flecha de asignación para guardar el resultado en el entorno, el operador = es igual a para establecer un argumento en la función mean() ( na.rm = TRUE ).
2.3.6 Resumen: objeto, función, entrada y argumento
En resumen, los objetos y las funciones van de la mano. Los objetos son tanto una entrada como la salida de una función (lo que devuelve la función).
Al pasar datos a una función, generalmente es el primer argumento, con argumentos adicionales que se usan para especificar el comportamiento.
Cuando decimos “la función regresa”, nos referimos a su salida (o un Error si es uno de esos días).
El objeto devuelto puede ser diferente a su objeto de entrada. En nuestros ejemplos
mean()anteriores, el objeto de entrada era una columna (mydata$var1: 4, 1, 2, 3 ), mientras que la salida era un solo valor: 2.5 .Si ha escrito una línea de código que no incluye la flecha de asignación (
<-), sus resultados se imprimirán. Si usa la flecha de asignación, un objeto que contiene los resultados se guardará en el entorno.
2.4. Tubería - %>%
\index{símbolos@ !pipe \texttt{ % > % }}
La tubería, denotada %>% , es probablemente la cosa más extraña que verá en este libro. Pero por favor tenga paciencia con nosotros; ¡No es tan aterrador como parece! Además, es súper útil. Usamos la tubería para enviar objetos a funciones.
En los ejemplos anteriores, calculamos la media de la columna var1 de mydata por mean(mydata$var1) . Con la tubería, podemos reescribir esto como:
library(tidyverse)
mydata$var1 %>% mean()[1] 2.5
Que dice: “Trabajando con mydata , seleccionamos una sola columna llamada var1 (con $ ) y luego calculamos mean()”. La tubería se vuelve especialmente útil una vez que el análisis incluye múltiples pasos aplicados uno tras otro. Una buena manera de leer y pensar en la tubería es “y luego”.
Este negocio de tuberías no es una funcionalidad estándar de R y antes de usarlo en un script, debe decirle a R que esto es lo que hará. La tubería proviene del paquete magrittr , pero cargar en tidyverse también cargará la tubería. Entonces library(tidyverse) inicializa todo lo que necesitas.
Para insertar una tubería
%>%, utilice el método abreviado de tecladoCtrl+Shift+M. Con o sin la tubería, la regla general “si el resultado se imprime, no se guarda” aún se aplica. Para guardar el resultado de la función en un nuevo objeto (para que aparezca en el entorno), debe agregar el nombre del nuevo objeto con la flecha de asignación (<-):
mean_result <- mydata$var1 %>% mean()2.4.1 Usando . para dirigir la tubería
De forma predeterminada, la tubería envía datos al comienzo de los corchetes de la función (ya que la mayoría de las funciones que usamos esperan datos como primer argumento).
Entonces `mydata %>% lm(dependent~explanatory)` es equivalente a `lm(mydata, dependent~explanatory)`.
lm() - modelo lineal - se presentará en detalle en el Capítulo @ ref(chap07-h1).
Sin embargo, la función lm() no espera datos como su primer argumento. lm() quiere que especifiquemos las variables primero (`dependent~explanatory`), y luego quiere el tibble en el que se encuentran estas columnas. Así que tenemos que usar . para decirle a la canalización que envíe los datos al segundo argumento de lm() , no al primero, por ejemplo,
mydata %>%
lm(var1~var2, data = .)2.5 Operadores para filtrar datos
Los operadores son símbolos que le dicen a R cómo manejar diferentes datos u objetos. Ya hemos introducido tres: $ (selecciona una columna), <- (asigna valores o resultados a una variable) y la tubería - %>% (envía datos a una función).
Otros operadores comunes son los que usamos para filtrar datos: estos son operadores lógicos y de comparación aritmética. Esto puede ser para crear subgrupos o para excluir casos atípicos o incompletos.
Los operadores de comparación que funcionan con datos numéricos son relativamente sencillos: >, <, >=, <= . Los dos primeros verifican si sus valores son mayores o menores que otro valor, los dos últimos verifican “mayor o igual que” y “menor o igual que”. Estos operadores se ven más comúnmente dentro de la función filter() :
gbd_short %>%
filter(year < 1995)# A tibble: 3 × 3
year cause deaths_millions
<dbl> <chr> <dbl>
1 1990 Communicable diseases 15.4
2 1990 Injuries 4.25
3 1990 Non-communicable diseases 26.7
Aquí enviamos los datos ( gbd_short ) al filter() y le pedimos que conserve todos los años que sean inferiores a 1995. El tibble resultante solo incluye el año 1990. Ahora, si usamos el operador <= (menor o igual que), tanto 1990 como 1995 pasan el filtro:
gbd_short %>%
filter(year <= 1995)# A tibble: 6 × 3
year cause deaths_millions
<dbl> <chr> <dbl>
1 1990 Communicable diseases 15.4
2 1990 Injuries 4.25
3 1990 Non-communicable diseases 26.7
4 1995 Communicable diseases 15.1
5 1995 Injuries 4.53
6 1995 Non-communicable diseases 29.3
Además, los valores a ambos lados del operador podrían ser variables, por ejemplo, mydata %>% filter(var2 > var1) .
Para filtrar valores que son iguales a algo, usamos el operador == .
gbd_short %>%
filter(year == 1995)# A tibble: 3 × 3
year cause deaths_millions
<dbl> <chr> <dbl>
1 1995 Communicable diseases 15.1
2 1995 Injuries 4.53
3 1995 Non-communicable diseases 29.3
Esto lee, toma el conjunto de datos GBD, lo envía al filtro y mantiene las filas donde el año es igual a 1995.
Usar accidentalmente el igual simple = cuando es necesario el doble igual == es un error común y aún nos sucede a los mejores. Ocurre tan a menudo que el error que da la función filter() al usar la incorrecta también nos recuerda cuál era la correcta:
gbd_short %>%
filter(year = 1995)Error in `filter()`:
! We detected a named input.
ℹ This usually means that you've used `=` instead of `==`.
ℹ Did you mean `year == 1995`?
La respuesta a “¿necesitas ==?” es casi siempre, “Sí R, lo hago, gracias”. Pero eso es solo porque
filter()es una cookie inteligente y está acostumbrada a este error común. Hay otras funciones útiles en las que usamos estos operadores, pero no siempre saben que nos dicen que acabamos de confundir=con==. Entonces, si obtiene un error al verificar la igualdad entre las variables, siempre verifique primero sus operadores==.
R también tiene dos operadores para combinar comparaciones múltiples: & y |, que representan AND y OR, respectivamente. Por ejemplo, podemos filtrar para mantener solo los primeros y los últimos años en el conjunto de datos:
gbd_short %>%
filter(year == 1995 | year == 2017)# A tibble: 6 × 3
year cause deaths_millions
<dbl> <chr> <dbl>
1 1995 Communicable diseases 15.1
2 1995 Injuries 4.53
3 1995 Non-communicable diseases 29.3
4 2017 Communicable diseases 10.4
5 2017 Injuries 4.47
6 2017 Non-communicable diseases 40.9
Esto dice: tome el conjunto de datos GBD, envíelo al filtro y mantenga las filas donde el año es igual a 1995 O el año es igual a 2017.
El uso de valores específicos como lo hemos hecho aquí (1995/2017) se denomina “codificación rígida”, lo cual está bien si sabemos con certeza que no querremos usar el mismo script en un conjunto de datos actualizado. Pero una forma más inteligente de lograr lo mismo es usar las funciones min() y max() :
gbd_short %>%
filter(year == max(year) | year == min(year))# A tibble: 6 × 3
year cause deaths_millions
<dbl> <chr> <dbl>
1 1990 Communicable diseases 15.4
2 1990 Injuries 4.25
3 1990 Non-communicable diseases 26.7
4 2017 Communicable diseases 10.4
5 2017 Injuries 4.47
6 2017 Non-communicable diseases 40.9
| Operators | Meaning |
|---|---|
| == | Equal to |
| != | Not equal to |
| < | Less than |
| > | Greater than |
| <= | Less than or equal to |
| >= | Greater then or equal to |
| & | AND |
| | | OR |
2.5.1 Ejemplos resueltos
Filtre el conjunto de datos para incluir solo el año 2000. Guarde esto en una nueva variable usando el operador de asignación.
mydata_year2000 <- gbd_short %>%
filter(year == 2000)Practiquemos combinar selecciones múltiples juntas.
Recordatorio: ‘|’ significa O y ‘&’ significa Y.
Desde gbd_short , seleccione las líneas donde el año sea 1990 o 2017 y la causa sea “Enfermedades transmisibles”:
new_data_selection <- gbd_short %>%
filter((year == 1990 | year == 2013) & cause == "Communicable diseases")
# O podemos deshacernos de los corchetes adicionales a lo largo de los años
# moviendo la causa a un nuevo filtro en una nueva línea:
new_data_selection <- gbd_short %>%
filter(year == 1990 | year == 2013) %>%
filter(cause == "Communicable diseases")
# O incluso mejor, podemos incluir ambos en una llamada filter(), ya que todos
# las condiciones separadas se unen por defecto con "&":
new_data_selection <- gbd_short %>%
filter(year == 1990 | year == 2013,
cause == "Communicable diseases")El símbolo hash ( # ) se usa para agregar comentarios de texto libre al código R. R no intentará ejecutar estas líneas, serán ignoradas. Los comentarios son una parte esencial de cualquier código de programación y son notas de “Querido diario” para tu yo del futuro .
2.6 La función de combinación: c()
La función de combinación como su nombre lo indica se utiliza para combinar varios valores. Es especialmente útil cuando se usa con el operador %in% para filtrar valores múltiples. Recuerde cómo la columna de causa gbd_short tenía tres causas diferentes:
gbd_short$cause %>% unique()[1] "Communicable diseases" "Injuries"
[3] "Non-communicable diseases"
Digamos que queríamos filtrar por enfermedades transmisibles y no transmisibles. 6 Podríamos usar el operador OR | así:
gbd_short %>%
# también filtrado por un solo año para mantener el resultado conciso
filter(year == 1990) %>%
filter(cause == "Communicable diseases" | cause == "Non-communicable diseases")# A tibble: 2 × 3
year cause deaths_millions
<dbl> <chr> <dbl>
1 1990 Communicable diseases 15.4
2 1990 Non-communicable diseases 26.7
Pero eso significa que tenemos que escribir causa dos veces (y más si tuviéramos otros valores que queríamos incluir). Aquí es donde el operador %in% junto con la función c() son útiles:
gbd_short %>%
filter(year == 1990) %>%
filter(cause %in% c("Communicable diseases", "Non-communicable diseases"))# A tibble: 2 × 3
year cause deaths_millions
<dbl> <chr> <dbl>
1 1990 Communicable diseases 15.4
2 1990 Non-communicable diseases 26.7
2.7 Valores perdidos (NA) y filtros
El filtrado de valores perdidos (NA) requiere atención y cuidado especiales. Recuerde el pequeño tibble de ejemplo de Tabla - tiene algunas NA en las columnas var2 y var3 :
mydata# A tibble: 4 × 5
id sex var1 var2 var3
<int> <chr> <dbl> <dbl> <dbl>
1 1 Male 4 NA 2
2 2 Female 1 4 1
3 3 Female 2 5 NA
4 4 Male 3 NA NA
Si ahora queremos filtrar las filas donde falta var2 , filter(var2 == NA) no es la forma de hacerlo, no funcionará.
Dado que R es un lenguaje de programación, puede ser un poco obstinado con cosas como estas. Cuando le pide a R que haga una comparación usando == (o < , > , etc.) espera un valor en cada lado, pero NA no es un valor, es la falta del mismo. La forma de filtrar los valores perdidos es usando la función is.na() :
mydata %>%
filter(is.na(var2))# A tibble: 2 × 5
id sex var1 var2 var3
<int> <chr> <dbl> <dbl> <dbl>
1 1 Male 4 NA 2
2 4 Male 3 NA NA
Enviamos mydata al filtro y mantenemos las filas donde var2 es NA . Tenga en cuenta los corchetes dobles al final: eso se debe a que el interior pertenece a is.na() y el exterior a filter() . Perderse un paréntesis de cierre también es una fuente común de errores, y aún nos sucede a los mejores.
Si filtramos filas donde no falta var2 , hacemos esto7
mydata %>%
filter(!is.na(var2))# A tibble: 2 × 5
id sex var1 var2 var3
<int> <chr> <dbl> <dbl> <dbl>
1 2 Female 1 4 1
2 3 Female 2 5 NA
En R, el signo de exclamación (!) significa “no”.
A veces, desea eliminar un valor específico (por ejemplo, un valor atípico) del conjunto de datos como este. El pequeño tibble de ejemplo mydata tiene 4 filas, con los valores para var2 de la siguiente manera: NA, 4, 5, NA . Podemos excluir la fila donde var2 es igual a 5 usando “no es igual a” ( != )8:
mydata %>%
filter(var2 != 5)# A tibble: 1 × 5
id sex var1 var2 var3
<int> <chr> <dbl> <dbl> <dbl>
1 2 Female 1 4 1
Sin embargo, verá que al hacer esto, R elimina las filas donde var2 también es NA, ya que no puede estar seguro de que estos valores faltantes no sean iguales a 5.
Si desea conservar los valores que faltan, debe utilizar el operador OR ( | ) y la función is.na() :
mydata %>%
filter(var2 != 5 | is.na(var2))# A tibble: 3 × 5
id sex var1 var2 var3
<int> <chr> <dbl> <dbl> <dbl>
1 1 Male 4 NA 2
2 2 Female 1 4 1
3 4 Male 3 NA NA
Quedarse atrapado por valores faltantes, ya sea en filtros u otras funciones, es común (recuerde que mydata$var2 %>% mean() devuelve NA a menos que agregue na.rm = TRUE ). Esta es también la razón por la que insistimos en que siempre trace sus datos primero: los valores atípicos se revelarán y los valores de NA generalmente también se vuelven obvios.
Otra cosa que hacemos para mantenernos seguros con los filtros y los valores faltantes es guardar los resultados y asegurarnos de que la cantidad de filas siga sumando:
subset1 <- mydata %>%
filter(var2 == 5)
subset2 <- mydata %>%
filter(! var2 == 5)
subset1# A tibble: 1 × 5
id sex var1 var2 var3
<int> <chr> <dbl> <dbl> <dbl>
1 3 Female 2 5 NA
subset2# A tibble: 1 × 5
id sex var1 var2 var3
<int> <chr> <dbl> <dbl> <dbl>
1 2 Female 1 4 1
Si los números son pequeños, ahora puede mirar rápidamente la pestaña Entorno de RStudio y averiguar si el número de observaciones (filas) en subset1 y subset2 se suman al conjunto de datos completo ( mydata ). O use la función nrow() para verificar el número de filas en cada conjunto de datos:
Filas en mydata :
nrow(mydata)[1] 4
Filas en `subset1`:
nrow(subset1)[1] 1
Filas en subset2:
nrow (subset2)[1] 1
Preguntándole a R si sumar estos dos es igual al tamaño original:
nrow(subset1) + nrow( subset2) == nrow(mydata)[1] FALSE
Como era de esperar, esto devuelve FALSO, porque no agregamos un manejo especial para los valores faltantes. Vamos a crear un tercer subconjunto que solo incluya filas donde var3 es NA:
Filas en subset2:
subset3 <- mydata %>%
filter(is.na(var2))
nrow( subset1 ) + nrow(subset2) + nrow(subset3) == nrow( mydata)[1] TRUE
2.8 Creando nuevas columnas - mutate()
La función para agregar nuevas columnas (o hacer cambios en las existentes) a un tibble se llama mutate() . Como recordatorio, así es como se veía typesdata :
typesdata# A tibble: 3 × 4
id group measurement date
<chr> <chr> <dbl> <dttm>
1 ID1 Control 1.8 2017-01-02 12:00:00
2 ID2 Treatment 4.5 2018-02-03 13:00:00
3 ID3 Treatment 3.7 2019-03-04 14:00:00
Digamos que decidimos dividir la columna medida por 2. Una forma rápida de ver estos valores sería extraerlos usando el operador $ y luego dividirlos por 2:
typesdata$measurement[1] 1.8 4.5 3.7
typesdata$measurement/2[1] 0.90 2.25 1.85
Pero esto se vuelve engorroso una vez que queremos combinar múltiples variables del mismo tibble en un cálculo. Así que mutate() es el camino a seguir aquí:
typesdata %>%
mutate(measurement/2)# A tibble: 3 × 5
id group measurement date `measurement/2`
<chr> <chr> <dbl> <dttm> <dbl>
1 ID1 Control 1.8 2017-01-02 12:00:00 0.9
2 ID2 Treatment 4.5 2018-02-03 13:00:00 2.25
3 ID3 Treatment 3.7 2019-03-04 14:00:00 1.85
Observe cómo mutate() de arriba devuelve todo el tibble con una nueva columna llamada measurement/2 . Esto es bastante agradable de mutate() , pero sería mejor dar nombres de columnas que no incluyan caracteres que no sean letras, números, guiones bajos ( _ ) o puntos ( . ). Así que asignemos un nombre más estándar para esta nueva columna:
typesdata %>%
mutate(measurement_half = measurement/2)# A tibble: 3 × 5
id group measurement date measurement_half
<chr> <chr> <dbl> <dttm> <dbl>
1 ID1 Control 1.8 2017-01-02 12:00:00 0.9
2 ID2 Treatment 4.5 2018-02-03 13:00:00 2.25
3 ID3 Treatment 3.7 2019-03-04 14:00:00 1.85
Mejor. Puede ver que a R le gusta un poco más el nombre que le dimos, ya que ahora se eliminaron las marcas de verificación a su alrededor. En general, las tildes invertidas se pueden usar para llamar nombres de columnas no estándar, por lo que si se ve obligado a leer datos con, por ejemplo, espacios en los nombres de las columnas, entonces las tildes invertidas permiten llamar nombres de columnas que de otro modo generarían un error9:
mydata$`Nasty column name`
# o
mydata %>%
select(`Nasty column name`)Pero como siempre, si se imprime, no se guarda. Tenemos dos opciones: podemos sobrescribir el tibble typesdata (cambiando la primera línea a typesdata = typedata %>% ), o podemos crear uno nuevo (que aparece en su entorno):
typesdata_modified <- typesdata %>%
mutate(measurement_half = measurement/2)
typesdata_modified# A tibble: 3 × 5
id group measurement date measurement_half
<chr> <chr> <dbl> <dttm> <dbl>
1 ID1 Control 1.8 2017-01-02 12:00:00 0.9
2 ID2 Treatment 4.5 2018-02-03 13:00:00 2.25
3 ID3 Treatment 3.7 2019-03-04 14:00:00 1.85
La función mutate() también se puede usar para crear una nueva columna con un único valor constante; que a cambio se puede utilizar para calcular una diferencia para cada una de las fechas existentes:
library(lubridate)
typesdata %>%
mutate(reference_date = ymd_hm("2020-01-01 12:00"),
dates_difference = reference_date - date) %>%
select(date, reference_date, dates_difference)# A tibble: 3 × 3
date reference_date dates_difference
<dttm> <dttm> <drtn>
1 2017-01-02 12:00:00 2020-01-01 12:00:00 1094.0000 days
2 2018-02-03 13:00:00 2020-01-01 12:00:00 696.9583 days
3 2019-03-04 14:00:00 2020-01-01 12:00:00 302.9167 days
(Entonces estamos usando la función select() para elegir solo las tres columnas relevantes).
Finalmente, la función mutate se puede usar para crear una nueva columna con un valor resumido, por ejemplo, la media de otra columna:
typesdata %>%
mutate(mean_measurement = mean(measurement))# A tibble: 3 × 5
id group measurement date mean_measurement
<chr> <chr> <dbl> <dttm> <dbl>
1 ID1 Control 1.8 2017-01-02 12:00:00 3.33
2 ID2 Treatment 4.5 2018-02-03 13:00:00 3.33
3 ID3 Treatment 3.7 2019-03-04 14:00:00 3.33
Lo que a cambio puede ser útil para calcular una medida estandarizada (es decir, relativa a la media):
typesdata %>%
mutate(mean_measurement = mean(measurement)) %>%
mutate(measurement_relative = measurement/mean_measurement) %>%
select(matches("measurement"))# A tibble: 3 × 3
measurement mean_measurement measurement_relative
<dbl> <dbl> <dbl>
1 1.8 3.33 0.54
2 4.5 3.33 1.35
3 3.7 3.33 1.11
2.8.1 Ejemplo/ejercicio resuelto
Redondea la diferencia a 0 decimales usando la función round() dentro de mutate() . A continuación, agregue un ingenioso coincidencias ("fecha") dentro de la función select() para elegir todas las columnas coincidentes.
Solución:
typesdata %>%
mutate(reference_date = ymd_hm("2020-01-01 12:00"),
dates_difference = reference_date - date) %>%
mutate(dates_difference = round(dates_difference)) %>%
select(matches("date"))# A tibble: 3 × 3
date reference_date dates_difference
<dttm> <dttm> <drtn>
1 2017-01-02 12:00:00 2020-01-01 12:00:00 1094 days
2 2018-02-03 13:00:00 2020-01-01 12:00:00 697 days
3 2019-03-04 14:00:00 2020-01-01 12:00:00 303 days
Puede acortar esto agregando la función round() directamente alrededor de la resta, por lo que la tercera línea se convierte en dates_difference = round(reference_date - date)) %>% . Pero a veces, escribir cálculos más largos que el mínimo absoluto puede hacerlos más fáciles de entender cuando vuelves a un antiguo guión meses después.
Además, no tuvimos que guardar reference_date como una nueva columna, el cálculo podría haber usado el valor directamente: mutate(dates_difference = ymd_hm("2020-01-01 12:00") - date) %> % . Pero nuevamente, definirlo hace que sea más claro para tu yo futuro ver lo que se hizo. Y hace que reference_date esté disponible para su reutilización en cálculos más complicados dentro del tibble.
2.9 Cálculos condicionales - if_else()
Y finalmente, combinamos los operadores de filtrado ( == , > , < , etc.) con la función if_else() para crear nuevas columnas basadas en una condición.
typesdata %>%
mutate(above_threshold = if_else(measurement > 3,
"Above three",
"Below three"))# A tibble: 3 × 5
id group measurement date above_threshold
<chr> <chr> <dbl> <dttm> <chr>
1 ID1 Control 1.8 2017-01-02 12:00:00 Below three
2 ID2 Treatment 4.5 2018-02-03 13:00:00 Above three
3 ID3 Treatment 3.7 2019-03-04 14:00:00 Above three
Estamos enviando typesdata a una función mutate () , estamos creando una nueva columna llamada above_threshold en función de si measurement es mayor o menor que 3. El primer argumento de if_else() es una condición (en este caso, la medida es mayor que 3), el segundo argumento es el valor si la condición es TRUE y el tercer argumento es el valor si la condición es FALSE.
Dice: “si se cumple esta condición, devolver esto, de lo contrario devolver aquello”.
Mire cada línea en el tibble anterior y convénzase de que la variable ‘umbral’ funcionó como se esperaba. Luego mire los dos corchetes de cierre - )) - al final y convénzase de que ambos son necesarios.
Consejo
if_else()y valores faltantes: para filas con valores faltantes (NA), la condición no devuelve TRUE ni FALSE, devuelve NA. Y eso podría estar bien, pero si desea asignar un grupo/etiqueta específico para valores faltantes en la nueva variable, puede agregar un cuarto argumento aif_else(), por ejemplo,if_else(medida > 3, "Por encima de tres ", "Por debajo de tres", "Valor faltante").
2.10 Crear etiquetas - paste()
\index{functions@\textbf{functions}!paste}
\index{paste}
\index{labels}
La función ppaste() se usa para agregar caracteres juntos. También funciona con números y fechas que se convertirán automáticamente en caracteres antes de pegarlos en una sola etiqueta. Vea este ejemplo donde usamos todas las variables de typesdata para crear una nueva columna llamada plot_label ( seleccionamos()` para el espacio de impresión):
typesdata %>%
mutate(plot_label = paste(id,
"fue medido por última vez en", date,
", y el valor era", measurement)) %>%
select(plot_label)# A tibble: 3 × 1
plot_label
<chr>
1 ID1 fue medido por última vez en 2017-01-02 12:00:00 , y el valor era 1.8
2 ID2 fue medido por última vez en 2018-02-03 13:00:00 , y el valor era 4.5
3 ID3 fue medido por última vez en 2019-03-04 14:00:00 , y el valor era 3.7
El pegado también es útil cuando la información se almacena en diferentes columnas. Por ejemplo, considere este tibble inventado:
pastedata <- tibble(year = c(2007, 2008, 2009),
month = c("Jan", "Feb", "March"),
day = c(1, 2, 3))
pastedata# A tibble: 3 × 3
year month day
<dbl> <chr> <dbl>
1 2007 Jan 1
2 2008 Feb 2
3 2009 March 3
Podemos usar paste() para combinarlos en una sola columna:
pastedata %>%
mutate(date = paste(day, month, year, sep = "-"))# A tibble: 3 × 4
year month day date
<dbl> <chr> <dbl> <chr>
1 2007 Jan 1 1-Jan-2007
2 2008 Feb 2 2-Feb-2008
3 2009 March 3 3-March-2009
Por defecto, paste() agrega un espacio entre cada valor, pero podemos usar el argumento sep = para especificar un separador diferente. A veces es útil usar paste0() que no agrega nada entre los valores (sin espacios, sin guiones, etc.).
Ahora podemos decirle a R que la columna de fecha debe analizarse como tal:
library(lubridate)
pastedata %>%
mutate(date = paste(day, month, year, sep = "-")) %>%
mutate(date = dmy(date))# A tibble: 3 × 4
year month day date
<dbl> <chr> <dbl> <date>
1 2007 Jan 1 2007-01-01
2 2008 Feb 2 2008-02-02
3 2009 March 3 2009-03-03
2.11 Unir múltiples conjuntos de datos
Es común que diferentes piezas de información se mantengan en diferentes archivos o tablas y, a menudo, desea combinarlas. Por ejemplo, considere que tiene información demográfica ( id , sex , age ) en un archivo:
library(tidyverse)
patientdata <- read_csv("https://raw.githubusercontent.com/SurgicalInformatics/healthyr_book/master/data/patient_data.csv")
patientdata# A tibble: 6 × 3
id sex age
<dbl> <chr> <dbl>
1 1 Female 24
2 2 Male 59
3 3 Female 32
4 4 Female 84
5 5 Male 48
6 6 Female 65
Y otro con algunos resultados de laboratorio ( id , measurement ):
labsdata <- read_csv("https://raw.githubusercontent.com/SurgicalInformatics/healthyr_book/master/data/labs_data.csv")
labsdata# A tibble: 4 × 2
id measurement
<dbl> <dbl>
1 5 3.47
2 6 7.31
3 8 9.91
4 7 6.11
Observe cómo estos conjuntos de datos no solo tienen diferentes tamaños ( 6 filas en patientdata , 4 filas en labsdata ), sino que también incluyen información sobre diferentes pacientes: patiendata tiene ids rpatientdata$id , labsdata tiene ids 5, 6, 8, 7 .
Una forma integral de unirlos es usar full_join() conservando toda la información de ambos tibbles (y haciendo coincidir filas por columnas compartidas, en este caso id ):
full_join(patientdata, labsdata)Joining, by = "id"
# A tibble: 8 × 4
id sex age measurement
<dbl> <chr> <dbl> <dbl>
1 1 Female 24 NA
2 2 Male 59 NA
3 3 Female 32 NA
4 4 Female 84 NA
5 5 Male 48 3.47
6 6 Female 65 7.31
7 8 <NA> NA 9.91
8 7 <NA> NA 6.11
Sin embargo, si solo estamos interesados en la información coincidente, usamos la unión interna:
inner_join(patientdata, labsdata)Joining, by = "id"
# A tibble: 2 × 4
id sex age measurement
<dbl> <chr> <dbl> <dbl>
1 5 Male 48 3.47
2 6 Female 65 7.31
Y finalmente, si queremos retener toda la información de un tibble, usamos left_join() o right_join() :
left_join ( patientdata, labsdata )Joining, by = "id"
# A tibble: 6 × 4
id sex age measurement
<dbl> <chr> <dbl> <dbl>
1 1 Female 24 NA
2 2 Male 59 NA
3 3 Female 32 NA
4 4 Female 84 NA
5 5 Male 48 3.47
6 6 Female 65 7.31
right_join ( patientdata, labsdata )Joining, by = "id"
# A tibble: 4 × 4
id sex age measurement
<dbl> <chr> <dbl> <dbl>
1 5 Male 48 3.47
2 6 Female 65 7.31
3 8 <NA> NA 9.91
4 7 <NA> NA 6.11
2.11.1 Más notas sobre las uniones
Las funciones de combinación (
full_join(),inner_join(),left_join(),right_join()) buscarán automáticamente los nombres de columna coincidentes. Puede usar el argumentoby =para especificar a mano. Esto es especialmente útil si las columnas tienen nombres diferentes en los conjuntos de datos, por ejemplo,left_join(data1, data2, by = c("id" = "patient_id")).Las filas no tienen que estar ordenadas, las uniones coinciden con los valores dentro de las filas, no con el orden de las filas dentro del tibble.
Las uniones se utilizan para combinar diferentes variables (columnas) en un solo tibble. Si está obteniendo más datos de las mismas variables, use
bind_rows()en su lugar :
labsdata_updated <- read_csv( "https://raw.githubusercontent.com/SurgicalInformatics/healthyr_book/master/data/patient_data_updated.csv " )Rows: 2 Columns: 3
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (1): sex
dbl (2): id, age
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
labsdata_updated# A tibble: 2 × 3
id sex age
<dbl> <chr> <dbl>
1 7 Female 38
2 8 Male 29
bind_rows ( patientdata , labsdata_updated )# A tibble: 8 × 3
id sex age
<dbl> <chr> <dbl>
1 1 Female 24
2 2 Male 59
3 3 Female 32
4 4 Female 84
5 5 Male 48
6 6 Female 65
7 7 Female 38
8 8 Male 29
Finalmente, es importante comprender cómo se comportan las uniones si hay múltiples coincidencias dentro de los tibbles. Por ejemplo, si el ID de paciente 4 también tuviera una segunda medición:
labsdata_updated <- labsdata %>%
add_row(id = 5, measurement = 2.49)
labsdata_updated# A tibble: 5 × 2
id measurement
<dbl> <dbl>
1 5 3.47
2 6 7.31
3 8 9.91
4 7 6.11
5 5 2.49
Cuando ahora hacemos un left_join() con nuestro tibble principal - patientdata :
left_join(patientdata, labsdata_updated)Joining, by = "id"
# A tibble: 7 × 4
id sex age measurement
<dbl> <chr> <dbl> <dbl>
1 1 Female 24 NA
2 2 Male 59 NA
3 3 Female 32 NA
4 4 Female 84 NA
5 5 Male 48 3.47
6 5 Male 48 2.49
7 6 Female 65 7.31
Obtenemos 7 filas, en lugar de 6, ya que el ID de paciente 5 ahora aparece dos veces con las dos medidas diferentes. Por lo tanto, es importante conocer bien sus conjuntos de datos o vigilar la cantidad de filas para asegurarse de que los aumentos o disminuciones en los tamaños de tibble sean los esperados.
Footnotes
No convierte silenciosamente las cadenas en factores, es decir, por defecto es
stringsAsFactors = FALSE. Para aquellos que no estén familiarizados con la terminología aquí, no se preocupen, lo cubriremos en unas pocas secciones↩︎Red Colaborativa de la Carga Global de Enfermedades. Resultados del Estudio de Carga Global de Enfermedad 2017 (GBD 2017). Seattle, Estados Unidos: Instituto de Métricas y Evaluación de la Salud (IHME), 2018. Disponible en http://ghdx.healthdata.org/gbd-results-tool.↩︎
La interfaz de sistema operativo portátil (POSIX) es un conjunto de estándares informáticos. No hay nada más que entender sobre esto aparte de cuando R comienza a gritarle “POSIXct esto o POSIXlt eso”, verifique sus variables de fecha y hora↩︎
c()significa combinar y se presentará con más detalle más adelante en este capítulo↩︎read. csv()viene con base R, mientras queread_csv()viene del paquetereadrdentro deltidyverse. Recomendamos usarread_csv().↩︎En este ejemplo, sería más fácil usar el operador “no igual”, flter(causa
!=“Lesiones”), pero imagina que tu columna tiene más de tres valores diferentes.↩︎En este ejemplo simple,
mydata %>% filter(! is.na(var2))podría reemplazarse por una abreviatura:mydata %>% drop_na(var2), pero es importante entender cómo el ! yis.na()funcionan ya que habrá situaciones más complejas en las que será necesario usarlos.↩︎filter(var2 != 5) es equivalente a filter(! var2 == 5)↩︎Si esto le sucede a menudo, consulte
library(janitor)y su funciónclean_names()para ordenar automáticamente los nombres de columna no estándar.↩︎