diff --git a/NEWS.md b/NEWS.md
index 283ca342a..a5c22fe59 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -6,6 +6,8 @@
- The default `try_again` message for checkbox questions now prompts the student to "select every correct answer" regardless of whether the question was created by `question()` or `question_checkbox()` (#783).
+- Added translation to Spanish of the first 5 tutorials (which refer to the book r4ds v1 translated elsewhere into Spanish also - https://github.com/cienciadedatos/r4ds )
+
# learnr 0.11.3
- Fixed an issue that prevented authors from using symbols, such as `T` or a variable, as the value of the `exercise` chunk option, which caused tutorials with chunks with `exercise = T` to fail to render (thanks @cknotz #757, #758).
diff --git a/data-raw/i18n_translations.yml b/data-raw/i18n_translations.yml
index 194614266..30e34b11c 100644
--- a/data-raw/i18n_translations.yml
+++ b/data-raw/i18n_translations.yml
@@ -395,7 +395,7 @@ text:
blank:
en: "blank"
fr: ~
- es: ~
+ es: "espacio en blanco"
pt: ~
tr: ~
emo: ~
@@ -407,7 +407,7 @@ text:
blank_plural:
en: "blanks"
fr: ~
- es: ~
+ es: "espacios en blanco"
pt: ~
tr: ~
emo: ~
@@ -420,7 +420,7 @@ text:
# {{count}} - the number of blanks detected in the exercise
en: "This exercise contains {{count}} $t(text.blank)."
fr: ~
- es: ~
+ es: "Este ejercicio contiene {{count}} $t(text.blank)."
pt: ~
tr: ~
emo: ~
@@ -433,7 +433,7 @@ text:
# {{blank}} - the string representing a blank in the exercise (e.g. "___")
en: "Please replace {{blank}} with valid code."
fr: ~
- es: ~
+ es: "Reemplaza {{blank}} con código válido, por favor."
pt: ~
tr: ~
emo: ~
@@ -452,7 +452,14 @@ text:
or {
with a matching "
, '
,
)
or }
.
fr: ~
- es: ~
+ es: >
+ Parece que esto podría no ser un código de R válido.
+ R no puede determinar cómo convertir tu texto en un comando completo.
+ Es posible que hayas olvidado completar un espacio en blanco,
+ eliminar un guión bajo, incluir una coma entre argumentos,
+ o cerrar un "
, '
, (
+ o {
inicial con un "
, '
,
+ )
o }
final.
pt: ~
tr: ~
emo: ~
@@ -500,7 +507,18 @@ text:
There may be other places that need to be fixed, too.
{{suggestion}}
fr: ~
- es: ~
+ es: >
+ Parece que tu código R contiene comillas con un formato especial
+ o comillas "rizadas" ({{character}}
)
+ alrededor de cadenas de caracteres, haciendo que tu código no sea válido.
+ R requiere que los valores de los caracteres estén entre marcas de
+ comillas simples o apóstrofes ("
o '
).
+ {{code}}
+ No te preocupes, esta es una fuente común de errores cuando copias
+ código de otra aplicación que aplica su propio formato al texto.
+ Puedes intentar reemplazar el código en esa línea con lo siguiente.
+ Es posible que también haya otros lugares que deban corregirse.
+ {{suggestion}}
pt: ~
tr: ~
emo: ~
@@ -537,7 +555,15 @@ text:
Try deleting the special character from your code and retyping
it manually.
fr: ~
- es: ~
+ es: >
+ Parece que tu código R contiene un carácter especial inesperado
+ ({{character}}
) que hace que tu código no sea válido.
+ {{code}}
+ A veces, tu código puede contener un carácter especial que parece un
+ carácter regular, especialmente si copias y pegas el código desde
+ otra aplicación.
+ Prueba de nuevo eliminando el carácter especial de tu código y vuelviendo
+ a escribirlo manualmente.
pt: ~
tr: ~
emo: ~
@@ -568,7 +594,16 @@ text:
There may be other places that need to be fixed, too.
{{suggestion}}
fr: ~
- es: ~
+ es: >
+ Parece que tu código R contiene un carácter especial inesperado
+ ({{character}}
) que hace que tu código no sea válido.
+ {{code}}
+ A veces, tu código puede contener un carácter especial que parece un
+ carácter regular, especialmente si copias y pegas el código desde
+ otra aplicación.
+ Prueba de nuevo reemplazando el código de esa linea con lo siguiente.
+ Es posible que también haya otros lugares que deban corregirse.
+ {{suggestion}}
pt: ~
tr: ~
emo: ~
@@ -618,7 +653,7 @@ text:
# separating list items
en: ", "
fr: ~
- es: ~
+ es: ", "
pt: ~
tr: ~
emo: ~
@@ -630,7 +665,7 @@ text:
oxfordcomma:
en: ","
fr: ""
- es: ""
+ es: ","
pt: ""
tr: ""
emo: ""
diff --git a/inst/tutorials/ex-data-basics_es/ex-data-basics.Rmd b/inst/tutorials/ex-data-basics_es/ex-data-basics.Rmd
new file mode 100644
index 000000000..884139e26
--- /dev/null
+++ b/inst/tutorials/ex-data-basics_es/ex-data-basics.Rmd
@@ -0,0 +1,205 @@
+---
+title: "Conceptos básicos de datos"
+output:
+ learnr::tutorial:
+ language: es
+ progressive: true
+ allow_skip: true
+ df_print: default
+runtime: shiny_prerendered
+description: >
+ Obtén información sobre los tipos básicos de datos en R. Explora los _data_
+ _frames_ de R y aprende cómo interactuar con _data frames_ y sus columnas.
+---
+
+```{r setup, include=FALSE}
+library(learnr)
+library(tidyverse)
+library(nycflights13)
+library(datos)
+millas <- as.data.frame(datos::millas) # Hack temporal del traductor del Tutorial. Forzamos aquí a mantener "millas" como data.frame() en memoria de R porque justamente el tutorial quiere mostrar la diferencia entre un tibble y un data.frame usando "millas" como data.frame, y parece que en el paquete "datos", el objeto millas se carga como data frame por omisión en la memoria de R.
+tutorial_options(exercise.timelimit = 60)
+```
+
+## Bienvenidos
+
+En este tutorial, aprenderás a usar R para inspeccionar el contenido de un _data frame_ o _tibble_. Los _data frames_ y los _tibbles_ son estructuras de R para almacenar datos tabulares; si heredas un conjunto de datos tabulares en R, es casi seguro que vendrá como una de estas estructuras.
+
+Aquí aprenderás cómo hacer tres cosas con _data frames_ y _tibbles_:
+
+1. Mirar el contenido de un _data frame_ o _tibble_
+2. Abrir una página de ayuda que describa un _data frame_ o _tibble_
+3. Identificar las variables y sus tipos en un _tibble_
+
+También conocerás los conjuntos de datos `millas` (equivalente a `mpg` de la versión inglesa del libro) y `vuelos` (equivalente a `flights` de la versión inglesa del libro). Estos conjuntos de datos aparecen con frecuencia en los ejemplos de R.
+
+Las lecturas de este tutorial siguen el libro [_R for Data Science_ (v1)](https://es.r4ds.hadley.nz/), secciones 3.2 y 5.1.
+
+## `Data frames`
+
+### ¿Qué es un `data frame`?
+
+Un ___data frame___ es una colección rectangular de valores, generalmente organizados de modo que las variables aparezcan en las columnas y las observaciones en las filas.
+
+Por ejemplo: el _data frame_ `millas` contiene observaciones recopiladas por la Agencia de Protección Ambiental de EE. UU. en 38 modelos de automóviles. Para ver el _data frame_ `millas`, escribe `millas` en el fragmento de código a continuación y luego haz clic en "Ejecutar código".
+
+```{r mpg-setup}
+millas <- as.data.frame(millas)
+```
+
+```{r mpg, exercise = TRUE}
+
+```
+
+
+**Pista:** Escribe `millas` y haz en el botón de "Ejecutar código".
+
+
+### Una nota sobre `millas`
+
+El código de encima funcionó porque te he cargado antes el paquete ggplot2 en este tutorial: `mpg` viene en paquete ggplot2 (y su traducción `millas` viene en el paquete `datos`). Si quieres mirar `millas` en tu ordenador, necesitas cargar los paquetes ggplot2 y datos primero. Lo puedes hacer en dos pasos:
+
+1. Ejecuta `install.packages(c('ggplot2', 'datos'))` para instalar ggplot2 y datos si no los tienes todavía.
+2. Carga ggplot2 y datos con las instrucciones `library(ggplot2)` y `library(datos)`
+
+Después de esto, podrás acceder a cualquier objeto en ggplot2 o datos —incluyendo `mpg` o `millas`— hasta que cierres la sesión de R.
+
+###
+
+¿Notaste cuánta información había dentro de `millas`? Yo también. A veces, el contenido de un _data frame_ no cabe en una sola pantalla, lo que dificulta su inspección. Pronto veremos una alternativa para usar y examinar _data frames_. Pero primero consigamos algo de ayuda...
+
+## Páginas de ayuda
+
+### Como abrir una página de ayuda
+
+Puedes aprender más sobre `millas` abriendo su página de ayuda. La página de ayuda explicará de donde viene el conjunto de datos `millas` y qué describe cada variable en `millas`. Para abrir la página de ayuda, escribe `?millas` en el fragmento de código de debajo y haz clic en "Ejecutar código".
+
+```{r help, exercise = TRUE}
+
+```
+
+
+**Pista:** Escribe `?millas` y haz clic en el botón de Ejecutar código.
+
+
+### ? sintaxis
+
+Puedes abrir una página de ayuda para cualquier objeto que venga con R o con un paquete R. Para abrir la página de ayuda, escribe un `?` antes del nombre del objeto y luego ejecuta el comando, como lo hiziste con `?millas`. Esta técnica funciona para funciones, paquetes y más.
+
+Ten en cuenta que los objetos creados por ti y tus colegas no tendrán una página de ayuda (a menos que crees una).
+
+### Ejercicios
+
+Usa el código de debajo para responder las siguientes preguntas.
+
+```{r exercises1, exercise = TRUE}
+
+```
+
+```{r quiz1, echo = FALSE}
+quiz(caption = "Test",
+ question("¿Qué significa la variable `traccion` de `millas`? Lee la ayuda para `?millas` para averiguarlo.",
+ answer("Si el vehículo tiene airbags laterales para la persona conductora o no"),
+ answer("Si el vehículo tiene transmisión automática o manual"),
+ answer("El número de cilindros del motor del vehículo"),
+ answer("Algo diferente", correct = TRUE, message = "`traccion` describe el tipo de tracción en el vehículo: d = delantera, t = trasera, 4 = 4 ruedas."),
+ allow_retry = TRUE
+ ),
+ question("Cuantas filas tiene el _data frame_ llamado `cars`?",
+ answer("2"),
+ answer("25"),
+ answer("50", correct = TRUE),
+ answer("100"),
+ incorrect = "Incorrecto.\nPista: R numera las filas de un _data frame_ cuando muestra el contenido del _data frame_. Como resultado, puedes detectar el número de filas en `cars` por medio de examinar `cars` en el bloque de código de arriba.",
+ allow_retry = TRUE
+ ),
+ question("Cuantas columnas hay en el _data frame_ llamado `cars`?",
+ answer("1"),
+ answer("2", correct = TRUE),
+ answer("4"),
+ answer("más de cuatro"),
+ incorrect = "Incorrecto.\nPista: Si inspeccionas el contenido de `cars` en el bloque de código de arriba, debería ser bastante fácil contar su número de columnas.",
+ allow_retry = TRUE
+ )
+)
+```
+
+## _tibbles_
+
+### ¿Qué es un _tibble_?
+
+El _data frame_ `vuelos` en el paquete `datos` (traducción del _data frame_ `flights` en el paquete `nycflights13`) es un ejemplo de un _tibble_. Los _tibbles_ son _data frames_ con algunas propiedades extra.
+
+Para ver a lo que me refiero, usa el fragmento de código de debajo para mostrar el contenido de `vuelos`.
+
+```{r flights, exercise = TRUE}
+
+```
+
+
+**Pista:** Escribe el nombre del _data frame_ que quieres imprimir y haz clic en el botón de Ejecutar código. He cargado los paquetes `nycflight13` y `datos` para ti ya.
+
+
+###
+
+Buen trabajo. `vuelos` describe cada vuelo que ha salido de la ciudad de Nueva York en 2013. Los datos provienen de la [Oficina de Estadísticas de Transporte de EE. UU.](http://www.transtats.bts.gov/DatabaseInfo.asp?DB_ID=120&Link=0), y el conjunto de datos está documentado en `?vuelos`.
+
+
+### La visualización de un _tibble_
+
+Quizás te darás cuenta que `vuelos` se ve un poco diferente que `millas`. `vuelos` muestra solo las primeras filas del _data frame_ y solo las columnas que caben en pantalla.
+
+`vuelos` se imprime de forma diferente porque es un ___tibble___. Los _tibbles_ son _data frames_ que se modifican ligeramente para que sean más fáciles de usar. Por ejemplo, R no intenta mostrarte todo un _tibble_ a la vez (pero intentará mostrarte todo un _data frame_ que no sea un _tibble_).
+
+Puedes usar `as_tibble()` para devolver una versión _tibble_ de cualquier _data frame_. Por ejemplo, esto devolvería una versión _tibble_ de `millas`: `as_tibble(millas)`.
+
+
+## Tipos de datos
+
+### Códigos de tipo
+
+```{r flights3, echo = FALSE}
+vuelos
+```
+
+¿Te diste cuenta de que debajo de los nombres de las columnas de `vuelos` aparece una fila de tres (o cuatro) abreviaturas de letras? Estas abreviaturas describen el _tipo_ de datos que se almacenan en cada columna de `vuelos`:
+
+* `int` significa números enteros.
+
+* `dbl` significa dobles o números reales.
+
+* `chr` significa vectores de caracteres o cadenas de texto.
+
+* `dttm` significa fecha-hora (una fecha + una hora; de las siglas en inglès data-time).
+
+Hay otros tres tipos comunes de variables que no se usan en este conjunto de datos pero se usan en otros conjuntos de datos:
+
+* `lgl` significa vectores lógicos que contienen solo `VERDADERO` o `FALSO`.
+
+* `fctr` significa factores, que R usa para representar variables categóricas
+ con posibles valores fijos.
+
+* `fecha` significa fechas.
+
+Esta fila de tipos de datos es exclusiva de _tibbles_ y es una de las formas en que los _tibbles_ intentan ser más fáciles de usar que los _data frames_.
+
+
+### Comprueba tu conocimiento
+
+```{r quiz2, echo=FALSE}
+question("¿Qué tipos de variables contiene `vuelos`? Marca todo lo que corresponda.",
+ type = "multiple",
+ allow_retry = TRUE,
+ incorrect = "No exactamente. Mira con más detalle en `vuelos`.",
+ answer("enteros (integers)", correct = TRUE),
+ answer("dobles (doubles)", correct = TRUE),
+ answer("factores (factors)"),
+ answer("caracteres (characters)", correct = TRUE),
+ correct = "¡Buen trabajo!"
+)
+```
+
+### Felicidades
+
+Has conocido las estructuras básicas de tablas en R: _data frames_ y _tibbles_; y has aprendido a inspeccionar su contenido. Cuando estés listo/a, continúa con el siguiente tutorial para comenzar a visualizar tus datos.
+
diff --git a/inst/tutorials/ex-data-basics_es/images/flights.png b/inst/tutorials/ex-data-basics_es/images/flights.png
new file mode 100644
index 000000000..751a2c7e9
Binary files /dev/null and b/inst/tutorials/ex-data-basics_es/images/flights.png differ
diff --git a/inst/tutorials/ex-data-filter_es/ex-data-filter.Rmd b/inst/tutorials/ex-data-filter_es/ex-data-filter.Rmd
new file mode 100644
index 000000000..49de0c205
--- /dev/null
+++ b/inst/tutorials/ex-data-filter_es/ex-data-filter.Rmd
@@ -0,0 +1,385 @@
+---
+title: "Filtrar observaciones"
+output:
+ learnr::tutorial:
+ language: es
+ progressive: true
+ allow_skip: true
+runtime: shiny_prerendered
+description: >
+ Aprende a filtrar observaciones en un _data frame_. Usa `filter()` para
+ extraer observaciones de un _data frame_, y usa `&`, `|` y `!` para escribir
+ comprobaciones de lógica.
+---
+
+```{r setup, include=FALSE}
+library(learnr)
+library(tidyverse)
+library(nycflights13)
+library(Lahman)
+library(datos)
+
+tutorial_options(
+ exercise.timelimit = 60,
+ # A simple checker function that just returns the message in the check chunk
+ exercise.checker = function(check_code, ...) {
+ list(
+ message = eval(parse(text = check_code)),
+ correct = logical(0),
+ type = "info",
+ location = "append"
+ )
+ }
+)
+knitr::opts_chunk$set(error = TRUE)
+```
+
+## Bienvenidos/as
+
+Este es un tutorial de demostración. compáralo con el [código fuente](https://github.com/rstudio/learnr/tree/main/inst/tutorials/ex-data-filter/ex-data-filter.Rmd) que lo hizo.
+
+###
+
+En este tutorial, aprenderás cómo:
+
+* use `filter()` para extraer observaciones de un _data frame_ o _tibble_
+* escribir pruebas lógicas en R
+* combinar pruebas lógicas con operadores booleanos
+* manejar los valores faltantes dentro de las pruebas lógicas
+
+Las lecturas de este tutorial siguen el libro [_R for Data Science_ (v1)](https://es.r4ds.hadley.nz/), sección 5.2.
+
+### Prerequisitos
+
+Para practicar estas habilidades, utilizaremos el conjunto de datos `vuelos` del paquete datos (que originalmente en inglés proviene del paquete nycflights13). Este _data frame_ proviene de la [Oficina de Estadísticas de Transporte de EE. UU.](http://www.trastats.bts.gov/DatabaseInfo.asp?DB_ID=120&Link=0) y contiene todos los `r format(nrow(datos::vuelos), big.mark = ",")` vuelos que partieron de la ciudad de Nueva York en 2013. Está documentado en `?vuelos`.
+
+Si estás listo/a para comenzar, haz clic en el botón de __Continuar__!
+
+## Filtra filas con `filter()`
+
+### filter()
+
+`filter()` te permite usar una prueba lógica para extraer filas específicas de un _data frame_. Para usar `filter()`, pásale el _data frame_ seguido de una o más pruebas lógicas. `filter()` devolverá cada fila que pase cada prueba lógica.
+
+Entonces, por ejemplo, podemos usar `filter()` para seleccionar cada vuelo en vuelos que partieron el 1 de enero. Haz clic en Ejecutar código para probarlo:
+
+```{r filter1, exercise = TRUE, exercise.eval = FALSE}
+filter(vuelos, mes == 1, dia == 1)
+```
+
+
+### salida
+
+Como en todas las funciones de dplyr, `filter()` devuelve un nuevo _data frame_ para que lo guardes o uses. No sobrescribe el _data frame_ anterior.
+
+Si deseas guardar la salida de `filter()`, deberás usar el operador de asignación, `<-`.
+
+Vuelve a ejecutar el comando en el fragmento de código a continuación, pero primero organiza el código para guardar la salida en un objeto llamado `jan1`.
+
+```{r filter2, exercise = TRUE, exercise.eval = FALSE}
+filter(vuelos, mes == 1, dia == 1)
+```
+
+```{r filter2-solution}
+jan1 <- filter(vuelos, mes == 1, dia == 1)
+```
+
+###
+
+¡Buen trabajo! Ahora puedes ver los resultados ejecutando el nombre `jan1` por sí solo. O puedes pasar `jan1` a una función que tome _data frames_ como entrada.
+
+¿Notaste que este código usaba el operador igual doble, `==`? `==` es uno de los operadores de comparación lógica de R. Los operadores de comparación son clave para usar `filter()`, así que echémosles un vistazo.
+
+## Comparaciones lógicas
+
+### Operadores de comparación
+
+R proporciona un conjunto de operadores de comparación que puedes usar para comparar valores: `>`, `>=`, `<`, `<=`, `!=` (distinto) y `==` (igual) . Cada uno crea una prueba lógica. Por ejemplo, ¿es `pi` mayor que tres?
+
+```{r}
+pi > 3
+```
+
+###
+
+Cuando colocas una prueba lógica dentro de `filter()`, el filtro aplica la prueba a cada fila en el _data frame_ y luego devuelve las filas que pasan, como un nuevo _data frame_
+
+Nuestro código anterior devolvió cada fila cuyo valor de mes era igual a uno _y_ cuyo valor de día era igual a uno.
+
+### ¡Cuidado!
+
+Cuando comienzas con R, el error más fácil de cometer es probar la igualdad con `=` en lugar de `==`. Cuando esto suceda obtendrás un error informativo:
+
+```{r, error = TRUE}
+filter(vuelos, mes = 1)
+```
+
+### Múltiples pruebas
+
+Si le das a `filter()` más de una prueba lógica, `filter()` combinará las pruebas con un "y" implícito. En otras palabras, `filter()` devolverá solo las filas que devuelven `VERDADERO` para cada prueba. Puede combinar pruebas de otras formas con operadores booleanos...
+
+## Operadores booleanos
+
+### &, |, y !
+
+R usa operadores booleanos para combinar múltiples comparaciones lógicas en una sola prueba lógica. Estos incluyen `&` (_y_), `|` (_o_), `!` (_no_ o _negación_) y `xor()` (_exactamente o_).
+
+Tanto `|` como `xor()` devolverán VERDADERO si una u otra comparación lógica devuelve VERDADERO. `xor()` se diferencia de `|` en que devolverá FALSO si ambas comparaciones lógicas devuelven VERDADERO. El nombre _xor_ significa _exactamente o_.
+
+Estudie el siguiente diagrama para tener una idea de cómo funcionan estos operadores.
+
+```{r fig1, echo = FALSE, out.width = "100%", fig.cap = "En la figura anterior, `x` es el círculo de la izquierda, `y` es el círculo de la derecha, y la región sombreada muestra qué partes selecciona cada comando."}
+knitr::include_graphics("images/transform-logical.png")
+```
+
+### Prueba tus conocimientos
+
+```{r logicals, echo = FALSE}
+question(" ¿Qué devolverá el siguiente código?: `filter(vuelos, mes == 11 | mes == 12)`",
+ answer("Todos los vuelos que partieron en noviembre _o_ diciembre", correct = TRUE),
+ answer("Todos los vuelos que partieron en noviembre _y_ diciembre", message = "Técnicamente un vuelo no podría haver partido en noviembre _y_ diciembre a menos que partiera dos veces."),
+ answer("Todos los vuelos _excepto_ los que partieron en noviembre o diciembre"),
+ answer("Un error. Esta es una forma incorrecta de combinar pruebas.", message = "La siguiente sección dirá un poco más sobre la combinación de pruebas."),
+ allow_retry = TRUE
+)
+```
+
+### Errores habituales
+
+En R, el orden de las operaciones no funciona como en inglés. No puedes escribir `filter(vuelos, mes == 11 | 12)`, aunque podrías decir "busca todos los vuelos que salieron en noviembre o diciembre". Asegúrate de escribir una prueba _completa_ en cada lado de un operador booleano.
+
+Aquí hay cuatro consejos más para ayudarte a usar pruebas lógicas y operadores booleanos en R:
+
+###
+
+1. Una abreviatura útil para este problema es `x %in% y`. Esto seleccionará cada fila donde `x` es uno de los valores en `y`. Podríamos usarlo para reescribir el código en la pregunta anterior:
+
+ ```{r, eval = FALSE}
+ nov_dec <- filter(vuelos, mes %in% c(11, 12))
+ ```
+
+###
+
+2. A veces puedes simplificar subconjuntos complicados recordando la ley de _De Morgan_: `!(x & y)` es lo mismo que `!x | !y`, y `!(x | y)` es lo mismo que `!x & !y`. Por ejemplo, si deseas buscar vuelos que no se hayan retrasado (a la llegada o a la salida) más de dos horas, puede utilizar cualquiera de los dos filtros siguientes:
+
+ ```{r, eval = FALSE}
+ filter(vuelos, !(atraso_llegada > 120 | atraso_salida > 120))
+ filter(vuelos, atraso_llegada <= 120, atraso_salida <= 120)
+ ```
+
+###
+
+3. Además de `&` y `|`, R también tiene `&&` y `||`. ¡No los uses con `filter()`! Aprenderás más adelante cuándo debes usarlos.
+
+###
+
+4. Cada vez que comiences a usar expresiones complicadas con varias partes en `filter()`, considera convertirlas en variables explícitas en su lugar. Eso hace que sea mucho más fácil verificar tu trabajo. Aprenderás a crear nuevas variables en breve.
+
+## Valores faltantes
+
+### NA
+
+Los valores faltantes pueden dificultar las comparaciones en R. R usa `NA` (del inglés, _Non Available_) para representar valores faltantes o desconocidos. Los `NA` son "contagiosos" porque casi cualquier operación que involucre un valor desconocido (`NA`) también será desconocida (`NA`). Por ejemplo, ¿puedes determinar qué valor deben retornar estas expresiones que usan valores faltantes? Haz una predicción y luego haz clic en "Enviar respuesta".
+
+```{r nas, exercise = TRUE}
+NA > 5
+10 == NA
+NA + 10
+NA / 2
+NA == NA
+```
+
+```{r nas-check}
+"En todos los casos, R no tiene suficiente información para calcular un resultado. Por lo tanto, cada resultado es un valor desconocido, `NA`."
+```
+
+### is.na()
+
+El resultado más confuso de arriba es éste:
+
+```{r}
+NA == NA
+```
+
+Es más fácil entender por qué esto es cierto con un poco más de contexto:
+
+```{r}
+# Sea x la edad de María. No sabemos cuántos años tiene.
+x <- NA
+
+# Sea y la edad de Juan. No sabemos cuántos años tiene.
+y <- NA
+
+# ¿Juan y María tienen la misma edad?
+x == y
+# ¡No lo sabemos!
+```
+
+Si deseas saber si falta un valor, usa `is.na()`:
+
+```{r}
+is.na(x)
+```
+
+### filter() y NAs
+
+`filter()` solo incluye filas donde la condición es `VERDADERA`; excluye los valores `FALSO` y `NA`. Si deseas conservar los valores faltantes, solicítalos explícitamente:
+
+```{r}
+df <- tibble(x = c(1, NA, 3))
+filter(df, x > 1)
+filter(df, is.na(x) | x > 1)
+```
+
+## Ejercicios
+
+### Ejercicio 1
+
+Usa los fragmentos de código a continuación para encontrar todos los vuelos que
+
+1. Tuvieron un retraso de llegada de dos o más horas.
+
+ ```{r filterex1, exercise = TRUE}
+
+ ```
+
+ ```{r filterex1-solution}
+ filter(vuelos, atraso_llegada >= 120) # atraso_llegada está en minutos
+ ```
+
+1. Volaron a Houston (`IAH` o `HOU`)
+
+ ```{r filterex2, exercise = TRUE}
+
+ ```
+ ```{r filterex2-solution}
+ filter(vuelos, destino %in% c("IAH", "HOU"))
+ ```
+
+
+ **Pista:** Este es un buen caso para el operador `%in%`.
+
+
+1. Fueron operados por United (`UA`), American (`AA`) o Delta (`DL`)
+
+ ```{r filterex3, exercise = TRUE}
+
+ ```
+ ```{r filterex3-solution}
+ filter(vuelos, aerolinea %in% c("UA", "AA", "DL"))
+ ```
+
+
+ **Pista:** La variable `aerolinea` muestra la aerolínea que operó cada vuelo. Este es otro buen caso para el operador `%in%`.
+
+
+1. Partieron en verano (julio, agosto, y septiembre)
+
+ ```{r filterex4, exercise = TRUE}
+
+ ```
+ ```{r filterex4-solution}
+ filter(vuelos, 6 < mes, mes < 10)
+ ```
+
+
+ **Pista:** Cuando se convierten a números, julio, agosto y septiembre se convierten en 7, 8 y 9.
+
+
+1. Llegaron más de dos horas tarde, pero no se fueron tarde.
+
+ ```{r filterex5, exercise = TRUE}
+
+ ```
+ ```{r filterex5-solution}
+ filter(vuelos, atraso_llegada > 120, atraso_salida <= 0)
+ ```
+
+
+ **Pista:** Recuerda que los retrasos de salida y llegada se registran en _minutos_.
+
+
+1. Se retrasaron más de una hora, pero recuperaron más de 30 minutos en vuelo
+
+ ```{r filterex6, exercise = TRUE}
+
+ ```
+ ```{r filterex6-solution}
+ filter(vuelos, atraso_salida > 60, (atraso_salida - atraso_llegada) > 30)
+ ```
+
+
+ **Pista:** El tiempo que recupera un avión es `atraso_salida - atraso_llegada`.
+
+
+1. Salieron entre la medianoche y las 6 a.m. (incluidas)
+
+ ```{r filterex7, exercise = TRUE}
+
+ ```
+ ```{r filterex7-solution}
+ filter(vuelos, horario_salida <= 600 | horario_salida == 2400)
+ ```
+
+
+ **Pista:** No olvides los vuelos que salían exactamente a la medianoche (`2400`). Este es un buen caso para un operador "o".
+
+
+### Ejercicio 2
+
+Otro ayudante útil para el filtrado de dplyr es `between()`. ¿Qué hace? ¿Puedes usar `entre()` para simplificar el código necesario para responder a los desafíos anteriores?
+
+```{r filterex8, exercise = TRUE}
+?between
+```
+
+### Exercise 3
+
+¿A cuántos vuelos les falta el `horario_salida`? ¿Qué otras variables faltan? ¿Qué podrían representar estas filas?
+
+```{r filterex9, exercise = TRUE}
+
+```
+```{r filterex9-solution}
+filter(vuelos, is.na(horario_salida))
+```
+
+
+**Pista:** Este es un buen caso para `is.na()`.
+
+
+```{r filterex9-check}
+"los vuelos con una hora de salida faltante son probablemente vuelos cancelados."
+```
+
+### Ejercicio 4
+
+¿Por qué no falta `NA ^ 0`? ¿Por qué `NA | VERDADERO` no falta?
+¿Por qué no falta `FALSE & NA`? ¿Puedes averiguar la regla general?
+(`NA * 0` es un contraejemplo engañoso!)
+
+```{r filterex10, exercise = TRUE}
+
+```
+```{r filterex10-hint-1}
+# cualquier numero con exponente cero es igual a uno
+NA ^ 0
+```
+
+```{r filterex10-hint-2}
+# valor desconocido o verdadero se evalúa como verdadero
+# (porque si un operando de "o" es verdadero, podemos estar seguros de que el resultado es verdadero)
+NA | TRUE
+```
+
+```{r filterex10-hint-3}
+# valor falso y desconocido se evalúa como falso
+# (porque si un operando de "y" es verdadero, podemos estar seguros de que el resultado es falso)
+FALSE & NA
+```
+
+```{r filterex10-hint-4}
+# esta no es una comparación lógica, es un cálculo numérico que implica un
+# valor desconocido, lo que resulta en un valor desconocido
+NA * 0
+```
diff --git a/inst/tutorials/ex-data-filter_es/images/transform-logical.png b/inst/tutorials/ex-data-filter_es/images/transform-logical.png
new file mode 100644
index 000000000..43462e639
Binary files /dev/null and b/inst/tutorials/ex-data-filter_es/images/transform-logical.png differ
diff --git a/inst/tutorials/ex-data-mutate_es/ex-data-mutate.Rmd b/inst/tutorials/ex-data-mutate_es/ex-data-mutate.Rmd
new file mode 100644
index 000000000..5bf65f61b
--- /dev/null
+++ b/inst/tutorials/ex-data-mutate_es/ex-data-mutate.Rmd
@@ -0,0 +1,305 @@
+---
+title: "Crea nuevas variables"
+output:
+ learnr::tutorial:
+ language: es
+ progressive: true
+ allow_skip: true
+runtime: shiny_prerendered
+description: >
+ Aprendre como seleccionar, crear, y modificar variables en un _data frame_.
+---
+
+```{r setup, include=FALSE}
+library(learnr)
+library(tidyverse)
+library(nycflights13)
+library(Lahman)
+library(datos)
+
+tutorial_options(
+ exercise.timelimit = 60,
+ # A simple checker function that just returns the message in the check chunk
+ exercise.checker = function(check_code, ...) {
+ list(
+ message = eval(parse(text = check_code)),
+ correct = logical(0),
+ type = "info",
+ location = "append"
+ )
+ }
+)
+knitr::opts_chunk$set(error = TRUE)
+```
+
+## Bienvenida
+
+En este tutorial, aprenderás cómo derivar variables a partir de un _data frame_, lo que incluye:
+
+* Cómo crear nuevas variables con `mutate()`
+* Cómo reconocer las familias de funciones más útiles para usar con `mutate()`
+
+Las lecturas de este tutorial siguen el libro [_R for Data Science_ (v1)](https://es.r4ds.hadley.nz/), sección 5.5.
+
+### Configuración
+
+Para practicar estas habilidades, usaremos el conjunto de datos `vuelos` del paquete datos, que conociste en [Conceptos básicos de datos](https://es.r4ds.hadley.nz/transform.html#introducci%C3%B3n-2). Este _data frame_ proviene de la [Oficina de Estadísticas de Transporte de EE.UU.](http://www.trastats.bts.gov/DatabaseInfo.asp?DB_ID=120&Link=0) y contiene todos los `r format(nrow(datos::vuelos), big.mark = ",")` vuelos que partieron de la ciudad de Nueva York en 2013. Está documentado en `?vuelos`.
+
+He precargado los paquetes para este tutorial con
+
+```{r eval = FALSE}
+library(tidyverse) # carga dplyr, ggplot2, y otros
+library(nycflights13) # carga los datos en inglés
+library(datos) # carga las traducciones de datos al castellano
+```
+
+## Agrega nuevas variables con mutate()
+
+Un conjunto de datos a menudo contiene información que puedes usar para calcular nuevas variables. `mutate()` te ayuda a calcular esas variables. Dado que `mutate()` siempre agrega nuevas columnas al final de un conjunto de datos, comenzaremos creando un conjunto de datos estrecho que nos permitirá ver las nuevas variables (si agregásemos nuevas variables a `vuelos`, las nuevas columnas se agregarían fuera del lateral de la pantalla, lo que las haría difíciles de ver).
+
+### select()
+
+Puedes seleccionar un subconjunto de variables por nombre con la función `select()` en dplyr. Ejecuta el código a continuación para ver el conjunto de datos estrecho que crea `select()`.
+
+```{r select, exercise = TRUE, exercise.eval = FALSE}
+vuelos_peq <- select(vuelos,
+ atraso_llegada,
+ atraso_salida,
+ distancia,
+ tiempo_vuelo
+)
+```
+
+### mutate()
+
+El siguiente código crea dos nuevas variables con la función `mutate()` de dplyr. `mutate()` devuelve un _data frame_ nuevo que contiene las nuevas variables añadidas a una copia del conjunto de datos original. Tómate un momento para imaginarte cómo se verá esto y luego haz clic en "Ejecutar código" para averiguarlo.
+
+```{r mutate1-setup}
+vuelos_peq <- select(vuelos,
+ atraso_llegada,
+ atraso_salida,
+ distancia,
+ tiempo_vuelo
+)
+```
+
+```{r mutate1, exercise = TRUE, exercise.eval = FALSE}
+mutate(vuelos_peq,
+ ganancia = atraso_llegada - atraso_salida,
+ velocidad = distancia / tiempo_vuelo * 60
+)
+```
+
+Ten en cuenta que cuando usas `mutate()` puedes crear múltiples variables a la vez, e incluso puedes hacer referencia a las variables que se crearon antes en la llamada para crear otras variables más adelante en la llamada:
+
+```{r echo = FALSE}
+vuelos_peq <- select(vuelos,
+ atraso_llegada,
+ atraso_salida,
+ distancia,
+ tiempo_vuelo
+)
+```
+
+```{r}
+mutate(vuelos_peq,
+ ganancia = atraso_llegada - atraso_salida,
+ horas = tiempo_vuelo / 60,
+ ganancia_por_hora = ganancia / horas
+)
+```
+
+### transmute()
+
+`mutate()` siempre devolverá las nuevas variables añadidas a una copia de los datos originales. Si deseas devolver solo las nuevas variables, usa `transmute()`. En el siguiente código, reemplaza `mutate()` con `transmute()` y luego detecta la diferencia en los resultados.
+
+```{r transmute, exercise = TRUE, exercise.eval = FALSE}
+mutate(vuelos,
+ ganancia = atraso_llegada - atraso_salida,
+ horas = tiempo_vuelo / 60,
+ ganancia_por_hora = ganancia / horas
+)
+```
+
+```{r transmute-solution}
+transmute(vuelos,
+ ganancia = atraso_llegada - atraso_salida,
+ horas = tiempo_vuelo / 60,
+ ganancia_por_hora = ganancia / horas
+)
+```
+
+```{r transmute-check}
+"¡Excelente trabajo! `transmute()` y `mutate()` hacen lo mismo, pero `transmute()` solo devuelve las nuevas variables. `mutate()` devuelve una copia del conjunto de datos original con las nuevas variables añadidas."
+```
+
+## Funciones útiles de mutate
+
+Puedes usar cualquier función dentro de `mutate()` siempre que la función esté **vectorizada**. Una función vectorizada toma un vector de valores como entrada y devuelve un vector con el mismo número de valores como salida.
+
+Con el tiempo, descubrí que varias familias de funciones vectorizadas son particularmente útiles con `mutate()`:
+
+* **Operadores aritméticos**: `+`, `-`, `*`, `/`, `^`. Todos ellos están vectorizados, utilizando las llamadas "reglas de reciclaje". Si un parámetro es más corto que el otro, se repetirá automáticamente varias veces para crear un vector de la misma longitud. Esto es más útil cuando uno de los argumentos es un solo número: `tiempo_vuelo / 60`, `hora * 60 + minuto`, etc.
+
+* **Aritmética modular**: `%/%` (división de enteros) y `%%` (resto), donde `x == y * (x %/% y) + (x %% y)`. La aritmética modular es una herramienta útil porque te permite dividir números enteros en partes. Por ejemplo, en el conjunto de datos de vuelos, puedes calcular `hora` y `minuto` a partir de `horario_salida` con:
+
+ ```{r}
+ transmute(vuelos,
+ horario_salida,
+ hora_ = horario_salida %/% 100,
+ minuto_ = horario_salida %% 100
+ )
+ ```
+
+* **Logs**: `log()`, `log2()`, `log10()`. Los logaritmos son una transformación increíblemente útil para manejar datos que varían en múltiples órdenes de magnitud. También convierten las relaciones multiplicativas en aditivas, una característica a la que volveremos en el modelado.
+
+ En igualdad de condiciones, recomiendo usar `log2()` porque es fácil de interpretar: una diferencia de 1 en la escala logarítmica corresponde a duplicar la escala original y una diferencia de -1 corresponde a reducir a la mitad.
+
+* **Desplazamientos**: `lead()` y `lag()` te permiten referirte a valores adelantados o atrasados. Esto te permite calcular las diferencias de ejecución (por ejemplo, `x - lag(x)`) o encontrar cuándo cambian los valores (`x != lag(x))`. Son más útiles si se usan junto con `group_by()`, sobre el cual aprenderás en breve.
+
+ ```{r}
+ (x <- 1:10)
+ lag(x)
+ lead(x)
+ ```
+
+* **Agregados acumulativos y móviles**: R proporciona funciones para ejecutar sumas, productos, mínimos y máximos: `cumsum()`, `cumprod()`, `cummin()`, `cummax()`; y dplyr proporciona `cummean()` para medias acumulativas. Si necesitas agregados móviles (es decir, una suma calculada en una ventana móvil), prueba el paquete RcppRoll.
+
+ ```{r}
+ x
+ cumsum(x)
+ cummean(x)
+ ```
+
+* **Comparaciones lógicas**, `<`, `<=`, `>`, `>=`, `!=`, que aprendiste anteriormente. Si estás realizando una secuencia compleja de operaciones lógicas, a menudo es una buena idea almacenar los valores intermedios en nuevas variables para que puedas comprobar que cada paso funciona como se esperaba.
+
+* **Clasificación**: hay varias funciones de clasificación, pero debes comenzar con `min_rank()`. Realiza el tipo de clasificación más habitual (por ejemplo, 1º, 2º, 2º, 4º). El valor predeterminado da los valores más pequeños a los rangos pequeños; usa `desc(x)` para dar a los valores más grandes los rangos más pequeños.
+
+ ```{r}
+ y <- c(1, 2, 2, NA, 3, 4)
+ min_rank(y)
+ min_rank(desc(y))
+ ```
+
+ Si `min_rank()` no hace lo que necesitas, mira las variantes
+ `row_number()`, `dense_rank()`, `percent_rank()`, `cume_dist()`,
+ `ntile()`. Mira sus páginas ed ayuda para más detalles.
+
+ ```{r}
+ row_number(y)
+ dense_rank(y)
+ percent_rank(y)
+ cume_dist(y)
+ ```
+
+## Ejercicios
+
+```{r, eval = FALSE, echo = FALSE}
+vuelos <- vuelos %>% mutate(
+ horario_salida = hora * 60 + minuto,
+ horario_llegada = (horario_llegada %/% 100) * 60 + (horario_llegada %% 100),
+ tiempovuelo2 = horario_llegada - horario_salida,
+ hora_prog_salida = horario_salida + atraso_salida
+)
+
+ggplot(vuelos, aes(hora_prog_salida)) + geom_histogram(binwidth = 60)
+ggplot(vuelos, aes(hora_prog_salida %% 60)) + geom_histogram(binwidth = 1)
+ggplot(vuelos, aes(tiempo_vuelo - tiempovuelo2)) + geom_histogram()
+```
+
+### Ejercicio 1
+
+Actualmente `horario_salida` y `salida_programada` son convenientes para verlos, pero difíciles de calcular porque en realidad no son números continuos. Conviértelos en una representación más conveniente de la cantidad de minutos que han pasado desde la medianoche.
+
+```{r mutateex1, exercise = TRUE}
+
+```
+```{r mutateex1-solution}
+mutate(vuelos, horario_salida = horario_salida %/% 100 * 60 + horario_salida %% 100,
+ salida_programada = salida_programada %/% 100 * 60 + salida_programada %% 100)
+```
+
+
+**Pista:** `423 %% 100` retorna `23`, `423 %/% 100` retorna `4`.
+
+
+```{r mutateex1-check}
+"¡Buen trabajo!"
+```
+
+### Ejercicio 2
+
+Compara `tiempo_vuelo` con `horario_llegada - horario_salida`. ¿Qué esperas ver? ¿Qué ves? ¿Cómo explicas esto?
+
+```{r mutateex2, exercise = TRUE}
+# vuelos <- mutate(vuelos, tiempo_total = _____________)
+# horas_de_vuelo <- select(vuelos, tiempo_vuelo, tiempo_total)
+# filter(horas_de_vuelo, tiempo_vuelo != tiempo_total)
+```
+```{r mutateex2-solution}
+vuelos <- mutate(vuelos, tiempo_total = horario_llegada - horario_salida)
+horas_de_vuelo <- select(vuelos, tiempo_vuelo, tiempo_total)
+filter(horas_de_vuelo, tiempo_vuelo != tiempo_total)
+```
+
+```{r mutateex2-check}
+"¡Buen trabajo! No tiene sentido hacer operaciones matemáticas con `horario_llegada` y `horario_salida` hasta que conviertas los valores a minutos pasada la medianoche (como hiciste con `horario_salida` y `salida_programada` en el ejercicio anterior)."
+```
+
+### Ejercicio 3
+
+Compara `horario_salida`, `salida_programada` y `atraso_salida`. ¿Cómo esperarías que se relacionaran esos tres números?
+
+```{r mutateex3, exercise = TRUE}
+
+```
+
+### Ejercicio 4
+
+Encuentra los 10 vuelos más retrasados (`atraso_salida`) usando una función de clasificación. ¿Cómo quieres manejar los empates? Lea atentamente la documentación de `min_rank()`.
+
+Find the 10 most delayed vuelos (`atraso_salida`) using a ranking function. How do you want to handle ties? Carefully read the documentation for `min_rank()`.
+
+```{r mutateex4, exercise = TRUE}
+
+```
+```{r mutateex4-solution}
+?min_rank
+vuelos <- mutate(vuelos, clasif_retraso = min_rank(atraso_salida))
+filter(vuelos, clasif_retraso <= 10)
+```
+
+
+**Pista:** Una vez que calculas un rango, puedes filtrar el conjunto de datos en función de los rangos.
+
+
+```{r mutateex4-check}
+"¡Excelente! No es posible elegir exactamente 10 vuelos a menos que elijas un método arbitrario para elegir entre empates."
+```
+
+### Ejercicio 5
+
+¿Qué devuelve `1:3 + 1:10`? ¿Porqué?
+
+```{r mutateex5, exercise = TRUE}
+
+```
+```{r mutateex5-solution}
+1:3 + 1:10
+```
+
+
+**Pista:** Recuerda las reglas de reciclaje de R.
+
+
+```{r mutateex5-check}
+"¡Bien! R repite 1:3 tres veces para crear un vector lo suficientemente largo como para sumar 1:10. Dado que la longitud del nuevo vector no es exactamente la longitud de 1:10, R también devuelve un mensaje de advertencia."
+```
+
+### Ejercicio 6
+
+¿Qué funciones trigonométricas proporciona R? Sugerencia: busca en la página de ayuda `Trig`.
+
+```{r mutateex6, exercise = TRUE}
+
+```
diff --git a/inst/tutorials/ex-data-summarise_es/ex-data-manip-summarise.Rmd b/inst/tutorials/ex-data-summarise_es/ex-data-manip-summarise.Rmd
new file mode 100644
index 000000000..02b834b9a
--- /dev/null
+++ b/inst/tutorials/ex-data-summarise_es/ex-data-manip-summarise.Rmd
@@ -0,0 +1,592 @@
+---
+title: "Resumir Tablas"
+output:
+ learnr::tutorial:
+ language: es
+ progressive: true
+ allow_skip: true
+runtime: shiny_prerendered
+description: >
+ Aprende a resumir las columnas disponibles en un _data frame_ de R con
+ `summarise()`. También aprenderás a encadenar operaciones junto con el
+ operador `%>%` (del paquete magrittr), y cómo calcular resúmenes agrupados
+ usando `group_by()` junto con `summarise()`.
+---
+
+```{r setup, include=FALSE}
+library(learnr)
+library(tidyverse)
+library(nycflights13)
+library(Lahman)
+library(datos)
+
+tutorial_options(
+ exercise.timelimit = 60,
+ # A simple checker function that just returns the message in the check chunk
+ exercise.checker = function(check_code, ...) {
+ list(
+ message = eval(parse(text = check_code)),
+ correct = logical(0),
+ type = "info",
+ location = "append"
+ )
+ }
+)
+knitr::opts_chunk$set(error = TRUE)
+```
+
+## Bienvenida
+
+En este tutorial, aprenderás cómo resumir una tabla de datos, incluyendo:
+
+* Cómo resumir tablas con `summarise()`
+* Cómo reconocer las familias de funciones más útiles para combinar con `summarise()`
+* Cómo combinar varias operaciones de dplyr con la tubería, `%>%`
+* Cómo calcular recuentos de observaciones con `n()`
+* Cómo resumir grupos de observaciones con `group_by()` y `summarise()`
+
+Las lecturas de este tutorial siguen el libro [_R for Data Science_ (v1)](https://es.r4ds.hadley.nz/), sección 5.6.
+
+### Configuración
+
+Para practicar estas habilidades, utilizaremos el conjunto de datos `vuelos` del paquete datos (traducidos del inglés a partir de nycflights13), que conociste en el tutorial [Conceptos básicos de datos](https://learnr-examples.shinyapps.io/ex-data-basics). Este _data frame_ proviene de la [Oficina de Estadísticas de Transporte de EE.UU.](http://www.trastats.bts.gov/DatabaseInfo.asp?DB_ID=120&Link=0) y contiene todos los `r format(nrow(datos::vuelos), big.mark = ",")` vuelos que partieron de la ciudad de Nueva York en 2013. Está documentado en `?vuelos`.
+
+Para visualizar los datos, usaremos el paquete ggplot2.
+
+He precargado los paquetes para este tutorial con
+
+
+```{r eval = FALSE}
+library(tidyverse) # loads dplyr, ggplot2, and others
+library(nycflights13)
+library(datos)
+```
+
+## Resumir grupos con summarise()
+
+### summarise()
+
+`summarise()` contrae un _data frame_ en una sola fila de resúmenes. Puedes elegir cuántos resúmenes aparecen en la fila y cómo se calculan:
+
+```{r summarize}
+summarise(vuelos, atraso = mean(atraso_salida, na.rm = TRUE),
+ total = sum(atraso_salida, na.rm = TRUE))
+```
+
+(Regresaremos a lo que significa `na.rm = TRUE` muy pronto).
+
+Fíjate que la sintaxis de `summarise()` es similar a `mutate()`. Al igual que con `mutate()`, le das a `summarise()`:
+
+1. El nombre de un _data frame_ para transformar
+2. Uno o más nombres de columna que aparecerán en la salida transformada. Cada nombre de columna se establece igual a la expresión de R que generará el contenido de la columna.
+
+La principal diferencia entre `summarise()` y `mutate()` es el tipo de función que usas para generar las nuevas columnas. `mutate()` toma funciones que devuelven un vector completo de salida (para agregar al _data frame_ original). `summarise()` toma funciones que devuelven un solo valor (o resumen). Estos valores aparecerán en un nuevo _data frame_ que tiene solo una fila.
+
+### group_by()
+
+`summarise()` no es muy útil a menos que lo combines con `group_by()`. `group_by()` cambia la unidad de análisis del _data frame_: asigna observaciones en el _data frame_ a grupos separados e indica a dplyr que aplique funciones por separado a cada grupo. `group_by()` asigna grupos poniendo juntas observaciones que tienen las mismas combinaciones de valores para las variables que pasas a `group_by()`.
+
+Por ejemplo, el código `summarise()` anterior calcula el atraso promedio para todo el conjunto de datos. Si aplicamos exactamente el mismo código a un conjunto de datos que ha sido agrupado por fecha (es decir, las combinaciones únicas de `anio`, `mes` y `dia`), obtenemos el atraso promedio por fecha. Haz clic en "Ejecutar código" para ver lo que quiero decir:
+
+```{r summarise, exercise = TRUE, exercise.eval = FALSE}
+por_fecha <- group_by(vuelos, anio, mes, dia)
+summarise(por_fecha, atraso = mean(atraso_salida, na.rm = TRUE),
+ total = sum(atraso_salida, na.rm = TRUE))
+```
+
+```{r summarise-check}
+"¡Buen trabajo!"
+```
+
+
+### Ejercicio 1
+
+¿Qué aerolínea tiene los peores retrasos? Desafío: ¿puedes desentrañar los efectos de los malos aeropuertos frente a los malos transportistas? ¿Por qué/por qué no? (Pista: piensa en `vuelos %>% group_by(aerolinea, destino) %>% summarise(n())`)
+
+```{r summariseex4, exercise = TRUE}
+
+```
+
+```{r summariseex4-solution}
+vuelos %>%
+ group_by(aerolinea) %>%
+ summarise(atraso_medio = mean(atraso_salida, na.rm = TRUE)) %>%
+ mutate(rank = min_rank(desc(atraso_medio))) %>%
+ filter(rank == 1)
+```
+
+
+**Pista:** Usa`min_rank(desc(atraso_medio))` para clasificar `atraso_medio` (por ejemplo) de modo que el mayor retraso reciba el rango uno.
+
+
+```{r summariseex4-check}
+"¡Buen trabajo! Frontier airlines (`F9`) fue el retraso de salida promedio más alto."
+```
+
+### Ejercicio 2
+
+Para cada avión, cuenta el número de vuelos antes del primer retraso mayor a 1 hora.
+
+```{r summariseex5, exercise = TRUE}
+
+```
+
+```{r summariseex5-solution}
+vuelos %>%
+ filter(!is.na(atraso_salida)) %>%
+ group_by(codigo_cola) %>%
+ mutate(gran_atraso = atraso_salida > 60,
+ antes = !cumany(gran_atraso)) %>%
+ summarise(sum(antes))
+```
+
+
+**Pista:** Para cada avión, cuenta el número de vuelos antes del primer retraso mayor a 1 hora.
+Una estrategia sería:
+* filtrar todas las filas donde `atraso_salida` es `NA`.
+* Luego agrupar por avión,
+* crear una variable que pruebe si cada vuelo se retrasó más de una hora
+* crear una variable que identifique los vuelos que ocurren antes del primer gran retraso con `!cumany()`
+* sumar el número de verdaderos
+
+
+```{r summariseex5-check}
+"¡Excelente trabajo! Eso fue difícil. Asegúrate de comprender cada uno de los pasos y funciones involucrados."
+```
+
+### Agrupación por múltiples variables
+
+Cuando agrupa por múltiples variables, cada resumen elimina un nivel de la agrupación. Eso facilita la acumulación progresiva de un conjunto de datos. Ejecuta el código a continuación e inspecciona cada resultado para ver cómo ha cambiado su criterio de agrupación (el criterio de agrupación se muestra en la parte superior del tibble).
+
+```{r unwrap, exercise = TRUE}
+diariamente <- group_by(vuelos, anio, mes, dia)
+(por_dia <- summarise(diariamente, total = sum(atraso_salida, na.rm = TRUE)))
+(por_mes <- summarise(por_dia, total = sum(total, na.rm = TRUE)))
+(por_anio <- summarise(por_mes, total = sum(total, na.rm = TRUE)))
+```
+
+Ten cuidado cuando resumas progresivamente los resúmenes: está bien para sumas y conteos, pero debes pensar en ponderar medias y varianzas, y no es posible hacerlo exactamente para estadísticas basadas en rangos como la mediana. En otras palabras, la suma de las sumas de los grupos es la suma total, pero la mediana de las medianas de los grupos no es la mediana general.
+
+### Desagrupar
+
+Si necesitas eliminar la agrupación y volver a las operaciones en datos desagrupados, usa `ungroup()`.
+
+```{r echo = FALSE}
+diariamente <- group_by(vuelos, anio, mes, dia)
+```
+
+```{r}
+diariamente <- ungroup(diariamente) # ya no están agrupados por fecha
+summarise(diariamente, total = sum(atraso_salida, na.rm = TRUE)) # todos los vuelos
+```
+
+### Grupos y dplyr
+
+`group_by()` también funciona con las otras funciones de dplyr; dplyr aplicará `filter()`, `select()`, `arrange()` y `mutate()` de forma grupal a los datos agrupados. Sin embargo, `group_by()` es el más útil cuando se combina con `summarise()`. Juntos, `group_by()` y `summarise()` proporcionan una de las herramientas que usarás más comúnmente cuando trabajes con dplyr: resúmenes agrupados. Pero antes de ir más allá con esto, necesitamos introducir una nueva y poderosa idea: la __tubería__ para combinar operaciones secuencialmente una detrás de la otra.
+
+## Combinando múltiples operaciones
+
+### Múltiples pasos
+
+Imagina que queremos explorar la relación entre la distancia y el retraso medio para cada destino en `vuelos`. Usando lo que sabes sobre dplyr, puedes escribir un código como este:
+
+```{r, fig.width = 6, message = FALSE}
+by_destino <- group_by(vuelos, destino)
+atraso <- summarise(by_destino,
+ conteo = n(),
+ dist = mean(distancia, na.rm = TRUE),
+ atraso = mean(atraso_llegada, na.rm = TRUE)
+)
+atraso <- filter(atraso, conteo > 20, destino != "HNL")
+
+ggplot(data = atraso, mapping = aes(x = dist, y = atraso)) +
+ geom_point(aes(size = conteo), alpha = 1/3) +
+ geom_smooth(se = FALSE)
+```
+
+El código funciona y encontramos un efecto interesante: parece que los retrasos aumentan con la distancia hasta ~750 millas y luego disminuyen. ¿Tal vez a medida que los vuelos se hacen más largos, hay más capacidad para compensar los retrasos en el aire?
+
+Ahora veamos cómo preparamos los datos. Hay tres pasos:
+
+1. Agrupar vuelos por destino.
+
+1. Resumir para calcular distancia, atraso promedio y número de vuelos.
+
+1. Filtrar para eliminar los puntos ruidosos y el aeropuerto de Honolulu, que está casi el doble de lejos que el siguiente aeropuerto más cercano.
+
+Este código es un poco frustrante de escribir porque tenemos que dar un nombre a cada _data frame_ intermedio, aunque no nos importe. Nombrar cosas es difícil, por lo que ralentiza nuestro análisis.
+
+### Pipes
+
+Hay otra forma de abordar el mismo problema. Podemos convertir el código en una canalización con el operador de canalizaciones (_pipe_, en inglés, también traducido como _tubería_ a veces), `%>%`:
+
+```{r}
+atrasos <- vuelos %>%
+ group_by(destino) %>%
+ summarise(
+ conteo = n(),
+ dist = mean(distancia, na.rm = TRUE),
+ atraso = mean(atraso_llegada, na.rm = TRUE)
+ ) %>%
+ filter(conteo > 20, destino != "HNL")
+```
+
+Detrás de escena, `x %>% f(y)` se convierte en `f(x, y)`, y `x %>% f(y) %>% g(z)` se convierte en `g(f( x, y), z)` y así sucesivamente. Puedes usar la canalización (_pipe_) para reescribir varias operaciones de manera que puedas leer de izquierda a derecha y de arriba a abajo.
+
+Esto pone el foco en las transformaciones, no en lo que se está transformando, lo que hace que el código sea más fácil de leer. Puedes leerlo como una serie de declaraciones imperativas: agrupar, luego resumir, luego filtrar. Como sugiere esta lectura, una buena manera de pronunciar `%>%` cuando se lee código es "entonces".
+
+A partir de ahora, usaremos _pipes_ con frecuencia porque mejora considerablemente la legibilidad del código, y volveremos a ello con más detalle en [Pipes](https://es.r4ds.hadley.nz/pipes.html).
+
+El _pipe_ es una característica definitoria del tidyverse: todos los paquetes en el tidyverse contienen funciones que están diseñadas para trabajar con el _pipe_. La única excepción es ggplot2: fue escrito antes de que se descubriera el _pipe_. Desafortunadamente, la próxima iteración de ggplot2, ggvis, que usa el _pipe_, aún no está lista para un uso general.
+
+## Funciones de resumen útiles {#summarise-funs}
+
+### Funciones de agregación
+
+Puedes recorrer un largo camino con las medias y la suma; pero R proporciona muchas otras funciones útiles para usar con el resumen. Cada una de estas funciones actúa como una **función de agregación**: toma un vector de valores y devuelve un solo valor.
+
+Demostremos algunas de las funciones de agregación más útiles con este conjunto de datos, que elimina los vuelos que no tienen información de retraso (porque fueron cancelados).
+
+```{r}
+no_cancelados <- vuelos %>%
+ filter(!is.na(atraso_salida), !is.na(atraso_llegada))
+```
+
+* **Medidas de ubicación**: hemos usado `media(x)`, pero `mediana(x)` también es útil. La media es la suma dividida por la longitud; la mediana es un valor donde el 50% de `x` está por encima y el 50% está por debajo.
+
+ A veces es útil combinar la agregación con subconjuntos lógicos. Todavía no hemos hablado de este tipo de subconjuntos, pero obtendrás más información al respecto en [Subconjuntos (_Subsetting_)](https://es.r4ds.hadley.nz/vectores.html#subconjuntos-subsetting).
+
+ ```{r}
+ no_cancelados %>%
+ group_by(anio, mes, dia) %>%
+ summarise(
+ atraso_medio1 = mean(atraso_llegada),
+ atraso_medio2 = mean(atraso_llegada[atraso_llegada > 0]) # el atraso medio positivo
+ )
+ ```
+
+* **Medidas de dispersión**: `sd(x)`, `IQR(x)`, `mad(x)`. La desviación media al cuadrado, o desviación estándar o sd para abreviar, es la medida estándar de dispersión. El rango intercuartílico 'IQR()' y la desviación mediana absoluta 'mad(x)' son equivalentes robustos que pueden ser más útiles si tiene valores atípicos (_outliers_).
+
+ ```{r}
+ # ¿Por qué la distancia a algunos destinos es más variable que a otros?
+ no_cancelados %>%
+ group_by(destino) %>%
+ summarise(distancia_desvest = sd(distancia)) %>%
+ arrange(desc(distancia_desvest))
+ ```
+
+* **Medidas de rango**: `min(x)`, `quantil(x, 0.25)`, `max(x)`. Los cuantiles son una generalización de la mediana. Por ejemplo, `quantile(x, 0.25)` encontrará un valor de `x` que es mayor que el 25% de los valores y menor que el 75 % restante.
+
+ ```{r}
+ # Cuando salen el primer y último vuelo de cada día?
+ no_cancelados %>%
+ group_by(anio, mes, dia) %>%
+ summarise(
+ primero = min(horario_salida),
+ ultimo = max(horario_salida)
+ )
+ ```
+
+* **Medidas de posición**: `first(x)`, `nth(x, 2)`, `last(x)`. Estas funcionan de manera similar a `x[1]`, `x[2]` y `x[length(x)]` pero te permiten establecer un valor predeterminado si esa posición no existe (es decir, estás tratando de obtener el 3er elemento de un grupo que solo tiene dos elementos). Por ejemplo, podemos encontrar la primera y la última salida para cada dia:
+
+ ```{r}
+ no_cancelados %>%
+ group_by(anio, mes, dia) %>%
+ summarise(
+ primera_sal = first(horario_salida),
+ ultima_sal = last(horario_salida)
+ )
+ ```
+
+ Estas funciones son complementarias al filtrado por rangos. El filtrado te brinda todas las variables, con cada observación en una fila separada:
+
+ ```{r}
+ no_cancelados %>%
+ group_by(anio, mes, dia) %>%
+ mutate(r = min_rank(desc(horario_salida))) %>%
+ filter(r %in% range(r))
+ ```
+
+* **Conteos**: en la siguiente sección, encontrarás `n()`, que no acepta argumentos y devuelve el tamaño del grupo actual. También puedes contar otras cantidades útiles. Para contar el número de valores que no faltan, usa `sum(!is.na(x))`. Para contar el número de valores distintos (únicos), usa `n_distinct(x)`.
+
+ ```{r}
+ # Which destinations have the most carriers?
+ no_cancelados %>%
+ group_by(destino) %>%
+ summarise(aerolineas = n_distinct(aerolinea)) %>%
+ arrange(desc(aerolineas))
+ ```
+
+* **Conteos y proporciones de valores lógicos**: `sum(x > 10)`, `mean(y == 0)`. Cuando se usa con funciones numéricas, `TRUE` se convierte en 1 y `FALSE` en 0. Esto hace que 'sum()' y 'mean()' sean muy útiles: 'sum(x)' da el número de `TRUE`s en `x`, y `mean(x)` da la proporción.
+
+ ```{r}
+ # ¿Cuántos vuelos salieron antes de las 5 am? (Estos generalmente indican
+ # vuelos retrasados desde el dia anterior)
+ no_cancelados %>%
+ group_by(anio, mes, dia) %>%
+ summarise(n_early = sum(horario_salida < 500))
+
+ # ¿Qué proporción de vuelos se retrasan más de una hora?
+ no_cancelados %>%
+ group_by(anio, mes, dia) %>%
+ summarise(hour_perc = mean(atraso_llegada > 60))
+ ```
+
+### Ejercicio 3
+
+Haz una lluvia de ideas sobre al menos 5 formas diferentes de evaluar las características típicas de retraso de un grupo de vuelos. Considera los siguientes escenarios:
+
+* Un vuelo llega 15 minutos antes el 50% del tiempo y 15 minutos tarde el 50% del tiempo.
+
+* Un vuelo siempre llega 10 minutos tarde.
+
+* Un vuelo llega 30 minutos antes el 50% del tiempo y 30 minutos tarde el 50% del tiempo.
+
+* 99% del tiempo un vuelo es puntual. El 1% de las veces llega 2 horas tarde.
+
+¿Qué es más importante: el retraso en la llegada o el retraso en la salida?
+
+```{r summariseex1, exercise = TRUE}
+
+```
+
+
+**Pista:** Considera las medidas de ubicación y las medidas de dispersión de R.
+
+
+
+### Valores faltantes
+
+Es posible que te hayas preguntado sobre el argumento `na.rm` que usamos en una sección anterior. ¿Qué pasa si no lo configuramos?
+
+```{r}
+vuelos %>%
+ group_by(anio, mes, dia) %>%
+ summarise(media = mean(atraso_salida))
+```
+
+¡Obtenemos muchos valores faltantes! Esto se debe a que las funciones de agregación obedecen la regla habitual de los valores faltantes: si falta algún valor en la entrada, la salida será un valor faltante. Afortunadamente, todas las funciones de agregación tienen un argumento `na.rm` que elimina antes del cálculo los valores que faltan:
+
+```{r}
+vuelos %>%
+ group_by(anio, mes, dia) %>%
+ summarise(media = mean(atraso_salida, na.rm = TRUE))
+```
+
+En este caso, donde los valores faltantes representan vuelos cancelados, también podríamos abordar el problema eliminando primero los vuelos cancelados, como hicimos para crear `no_cancelados`.
+
+```{r}
+no_cancelados <- vuelos %>%
+ filter(!is.na(atraso_salida), !is.na(atraso_llegada))
+
+no_cancelados %>%
+ group_by(anio, mes, dia) %>%
+ summarise(media = mean(atraso_salida))
+```
+
+### Ejercicio 4
+
+Nuestra definición de vuelos cancelados (`is.na(atraso_salida) | is.na(atraso_llegada)`) es ligeramente subóptima. ¿Por qué? ¿Cuál es la columna más importante?
+
+## Conteos
+
+### n()
+
+Siempre que realices una agregación, es una buena idea incluir un conteo (`n()`) o un conteo de valores que no faltan (`sum(!is.na(x))`). De esta manera, puedes verificar que no estás sacando conclusiones basadas en cantidades muy pequeñas de datos. Por ejemplo, veamos los aviones (identificados por su número de cola) que tienen los retrasos medios más altos:
+
+```{r}
+atrasos <- no_cancelados %>%
+ group_by(codigo_cola) %>%
+ summarise(
+ atraso = mean(atraso_llegada)
+ )
+
+ggplot(data = atrasos, mapping = aes(x = atraso)) +
+ geom_freqpoly(binwidth = 10)
+```
+
+Vaya, ¡hay algunos aviones que tienen un atraso _promedio_ de 5 horas (300 minutos)!
+
+La historia es en realidad un poco más matizada. Podemos obtener más información si dibujamos un diagrama de dispersión del número de vuelos frente al atraso promedio. Completa el código en blanco a continuación para calcularlo y luego trazar el número de vuelos por el atraso medio de llegada (`atraso_llegada`).
+
+```{r atrasos, exercise = TRUE}
+# atrasos <- no_cancelados %>%
+# group_by(codigo_cola) %>%
+# summarise(
+# atraso = _________,
+# n = n()
+# )
+#
+# ggplot(data = atrasos, mapping = aes(x = n, y = atraso)) +
+# geom_point(alpha = 1/10)
+```
+
+```{r atrasos-solution}
+atrasos <- no_cancelados %>%
+ group_by(codigo_cola) %>%
+ summarise(
+ atraso = mean(atraso_llegada),
+ n = n()
+ )
+
+ggplot(data = atrasos, mapping = aes(x = n, y = atraso)) +
+ geom_point(alpha = 1/10)
+```
+
+No es sorprendente que haya una variación mucho mayor en el retraso promedio cuando hay pocos vuelos. La forma de esta gráfica es muy característica: cada vez que graficas una media (u otro resumen) frente al tamaño del grupo, verás que la variación disminuye a medida que aumenta el tamaño de la muestra.
+
+### Contabilización de la variación basada en el tamaño de la muestra
+
+Al mirar este tipo de gráfico, a menudo es útil filtrar los grupos con el menor número de observaciones, para que puedas ver más del patrón y menos de la variación extrema en los grupos más pequeños. Esto es lo que hace el siguiente código, además de mostrarte un patrón útil para integrar ggplot2 en los flujos de dplyr. Es un poco doloroso tener que cambiar de `%>%` a `+`, pero una vez que le coges el tranquillo, es muy conveniente.
+
+```{r echo = FALSE}
+atrasos <- no_cancelados %>%
+ group_by(codigo_cola) %>%
+ summarise(
+ atraso = mean(atraso_llegada),
+ n = n()
+ )
+```
+
+
+```{r}
+atrasos %>%
+ filter(n > 25) %>%
+ ggplot(mapping = aes(x = n, y = atraso)) +
+ geom_point(alpha = 1/10)
+```
+
+--------------------------------------------------------------------------------
+
+Consejo de RStudio: un atajo de teclado útil es Cmd/Ctrl + Shift + P. Esto reenvía el fragmento enviado previamente desde el editor a la consola. Esto es muy conveniente cuando estás (por ejemplo) explorando el valor de `n` en el ejemplo anterior. Envías el bloque completo una vez con Cmd/Ctrl + Enter, luego modificas el valor de `n` y presionas Cmd/Ctrl + Shift + P para volver a enviar el bloque completo.
+
+--------------------------------------------------------------------------------
+
+### Tamaño de la muestra, rendimiento promedio y rango
+
+Hay otra variación común de este tipo de patrón. Veamos cómo se relaciona el rendimiento promedio de los bateadores en el béisbol con la cantidad de veces que están al bate. Aquí utilizo datos del paquete __Lahman__ (traducidos en el paquete `datos` habitual) para calcular el promedio de bateo (cantidad de veces que le dan/cantidad de intentos) de cada jugador de béisbol de las grandes ligas.
+
+Cuando dibujo la habilidad del bateador (medida por el promedio de bateo, `ba`) contra el número de oportunidades para batear la pelota (medida por el número de veces al bate, `ab`), se ven dos patrones:
+
+1. Como arriba, la variación en nuestro agregado disminuye a medida que obtenemos más puntos de datos.
+
+2. Existe una correlación positiva entre la habilidad (`ba`) y las oportunidades de golpear la pelota (`ab`). Esto se debe a que los equipos controlan quién juega y, obviamente, elegirán a sus mejores jugadores.
+
+```{r}
+# Conviértelo en un tibble para que se imprima bien
+bateo <- as_tibble(datos::bateadores) # Originalmente en Lahman::Batting
+
+bateadoras <- bateo %>%
+ group_by(id_jugador) %>%
+ summarise(
+ ba = sum(golpes, na.rm = TRUE) / sum(al_bate, na.rm = TRUE),
+ ab = sum(al_bate, na.rm = TRUE)
+ )
+
+bateadoras %>%
+ filter(ab > 100) %>%
+ ggplot(mapping = aes(x = ab, y = ba)) +
+ geom_point() +
+ geom_smooth(se = FALSE)
+```
+
+Esto también tiene implicaciones importantes para la clasificación. Si miras de cerca, las personas con los mejores promedios de bateo son claramente afortunadas, no hábiles.
+
+Puede encontrar una buena explicación de este problema en y .
+
+### count()
+
+Los conteos son tan útiles que dplyr proporciona una ayuda simple si todo lo que desea es un conteo:
+
+```{r}
+no_cancelados %>%
+ count(destino)
+```
+
+ Opcionalmente, puedes proporcionar una variable de peso. Por ejemplo, podrías usar esto para "contar" (sumar) el número total de millas que voló un avión:
+
+```{r}
+no_cancelados %>%
+ count(codigo_cola, wt = distancia)
+```
+
+### Ejercicio 5
+
+Inventa otro enfoque que te dará el mismo resultado que `no_cancelados %>% count(destino)` y `no_cancelados %>% count(codigo_cola, wt = distancia)` (sin usar `count()`).
+
+```{r summariseex2, exercise = TRUE}
+
+```
+```{r summariseex2-solution}
+no_cancelados %>%
+ group_by(destino) %>%
+ summarise(n = n())
+
+no_cancelados %>%
+ group_by(codigo_cola) %>%
+ summarise(n = sum(distancia))
+```
+
+
+**Pista:** Considera las herramientas a tu disposición "`group_by()`, `summarise()`, `n()`, `sum()`", y `?count`
+
+
+```{r summariseex2-check}
+"¡Excelente trabajo! Esta respuesta fue complicada, pero ahora puedes ver que `count()` es un atajo útil para `group_by()` + `summarise()` + `n()` (o `sum()`)."
+```
+
+### Ejercicio 6
+
+¿Qué hace el argumento `sort` a `count()`? ¿Cuándo podrías usarlo?
+
+```{r summariseex6, exercise = TRUE}
+?count
+```
+
+### Ejercicio 7
+
+Mira la cantidad de vuelos cancelados por día. ¿Hay un patrón? ¿La proporción de vuelos cancelados está relacionada con el atraso promedio?
+
+```{r summariseex3, exercise = TRUE}
+# Tarea 1
+# comienza con una variable que muestra el dia del año
+# vuelos %>%
+# mutate(fecha = as.Date(paste(anio, mes, dia, sep = "-"))) %>%
+# crea una variable que muestre si un vuelo está cancelado
+# agrúpalo por dia
+# crea un resumen contando el número de vuelos donde cancelados sea VERDADERO
+# Dibuja el resultado contra el día
+
+# Tarea 2
+# recrea los datos agrupados arriba
+# crea un resumen tomando la media de la variable cancelados
+# ...así como el atraso promedio
+# traza uno contra el otro
+```
+
+```{r summariseex3-solution}
+vuelos %>%
+ mutate(fecha = as.Date(paste(anio, mes, dia, sep = "-"))) %>%
+ mutate(cancelados = is.na(atraso_salida) | is.na(atraso_llegada)) %>%
+ group_by(fecha) %>%
+ summarise(n = sum(cancelados)) %>%
+ ggplot(aes(x = fecha, y = n)) +
+ geom_point() +
+ geom_smooth()
+
+vuelos %>%
+ mutate(fecha = as.Date(paste(anio, mes, dia, sep = "-"))) %>%
+ mutate(cancelados = is.na(atraso_salida) | is.na(atraso_llegada)) %>%
+ group_by(fecha) %>%
+ summarise(prop = mean(cancelados), atraso_medio = mean(atraso_salida, na.rm = TRUE)) %>%
+ ggplot(aes(x = prop, y = atraso_medio)) +
+ geom_point()
+```
+
+
+**Pista:** No te olvides de usar `na.rm = TRUE` cuando corresponda.
+
+
+```{r summariseex3-check}
+"¡Guau! Lo hiciste increíble."
+```
diff --git a/inst/tutorials/ex-setup-r_es/ex-setup-r.Rmd b/inst/tutorials/ex-setup-r_es/ex-setup-r.Rmd
new file mode 100644
index 000000000..74fc1fb46
--- /dev/null
+++ b/inst/tutorials/ex-setup-r_es/ex-setup-r.Rmd
@@ -0,0 +1,147 @@
+---
+title: "Puesta a punto"
+output:
+ learnr::tutorial:
+ language: es
+ progressive: true
+ allow_skip: true
+runtime: shiny_prerendered
+description: >
+ Aprende a configurar R y RStudio en tu máquina. También demostraremos
+ cómo instalar paquetes de R desde CRAN e instalaremos el paquete tidyverse.
+---
+
+```{r setup, include=FALSE}
+library(learnr)
+tutorial_options(exercise.timelimit = 60)
+```
+
+## Bienvenida
+
+Este es un tutorial de demostración. Compárarlo con el [código fuente](https://github.com/rstudio/learnr/tree/main/inst/tutorials/ex-setup-r/ex-setup-r.Rmd) que lo hizo.
+
+### Idea general
+
+Este tutorial te ayudará a configurar tu ordenador para usar R. Es para ti si necesitas:
+
+* Instalar R en tu ordenador
+* Instalar RStudio como Entorno de Desarrollo Integrado (IDE, de sus siglas en inglés)
+* Instalar el paquete `tidyverse` de R
+
+Puedes omitir este tutorial si ya has hecho estas cosas.
+
+### ¿Este tutorial es para ti?
+
+¿Necesitas trabajar con el tutorial? Responde el cuestionario a continuación para averiguarlo.
+
+```{r quiz1, echo = FALSE}
+question("Marca todo lo que NO has hecho:",
+ answer("He instalado R en mi ordenador", message = "* Instala R"),
+ answer("He instalado RStudio (como _IDE_, o entorno de desarrollo integrado, de sus siglas en inglés, para trabajar con R)", message = "* Instala RStudio"),
+ answer("He instalado el paquete tidyverse de R", message = "* Instala Paquetes"),
+ answer("Ninguna de las anteriores. Los he hecho todos.", correct = TRUE, message = "¡Puedes saltarte este tutorial!"),
+ type = "multiple",
+ incorrect = "¡Este tutorial está aquí para ayudarte! Para ponerlo a punto lee:"
+)
+```
+
+## Instala R
+
+### Como instalar R
+
+
+
+### Prueba tus conocimientos
+
+```{r quiz2, echo=FALSE}
+quiz(caption = "Cuestionario - Instalar R",
+ question("¿Es R gratuito y libre para descargar y usar?",
+ answer("¡Sí!", correct = TRUE, message = "R es gratuito __y__ de código abierto, lo que significa que cualquiera puede leer/usar, copiar, modificar y redistribuir el código fuente del lenguaje R."),
+ answer("No.")
+ ),
+ question("¿Dónde se descarga R?",
+ answer("www.rstudio.com/download"),
+ answer("[cloud.r-project.org](https://cloud.r-project.org)", correct = TRUE, message = "También lo puedes descargar desde [cran.r-project.org](https://cran.r-project.org)"),
+ answer("www.r-project.org", message = "Buen intento, pero no exactamente. www.r-project.org no proporciona un enlace de descarga, pero sí proporciona un enlace a uno de los sitios web anteriores."),
+ answer("www.r.com"),
+ allow_retry = TRUE
+ ),
+ question("¿Con qué frecuencia debes actualizar R?",
+ answer("Cada vez que lo uso", message = "¡Esto será demasiado a menudo a menos que uses R muy raramente!"),
+ answer("Aproximadamente una vez al año", correct = TRUE, "Se lanza una nueva versión de R aproximadamente una vez al año. Actualiza antes si encuentras un error que no puedes explicar."),
+ answer("Nunca", message = "Se lanza una nueva versión de R aproximadamente una vez al año. Asumiré que estás utilizando la versión más reciente de R, que será la versión más rápida con la menor cantidad de comportamientos inesperados." ),
+ allow_retry = TRUE
+ )
+)
+```
+
+## Instala RStudio
+
+### Cómo instalar RStudio
+
+RStudio es un entorno de desarrollo integrado para R. ¿Qué significa eso? Bueno, si piensas en R como un lenguaje, que lo es, puedes pensar en RStudio como un programa que te ayuda a escribir y trabajar en el lenguaje. ¡RStudio hace que la programación en R sea mucho más fácil y te sugiero que lo uses!
+
+
+
+### Prueba tus conocimientos
+
+```{r quiz3, echo=FALSE}
+quiz(caption = "Cuestionario - Instalar RStudio",
+ question("¿Qué es el IDE de RStudio?",
+ answer("Una aplicación que facilita el uso de R.", correct = TRUE, message = "RStudio organiza tu espacio de trabajo y facilita escribir, usar, depurar y guardar código R. Lo recomiendo encarecidamente y me basaré en RStudio en varios tutoriales"),
+ answer("Una aplicación que te permite usar R sin escribir ningún código", message = "¡Y gracias a Dios! El código proporciona un registro reproducible de tu trabajo, que es esencial para la ciencia de datos."),
+ answer("Un programa de hoja de cálculo como Microsoft Excel."),
+ answer("Otro nombre para R", message = "R y RStudio son dos cosas separadas. R es un idioma, como el inglés. Piensa en RStudio como un programa que te ayuda a usar el idioma, algo así como un programa de procesamiento de textos donde tú escribes en inglés."),
+ allow_retry = TRUE
+ ),
+ question("¿Es gratis descargar y usar el IDE de RStudio?",
+ answer("¡Sí!", correct = TRUE, message = "Al igual que R, RStudio es gratuito y de código abierto. Existen versiones profesionales de RStudio con funciones mejoradas, pero no nos basaremos en esas funciones en estos tutoriales.") ,
+ answer("No.", message = "Al igual que R, RStudio es gratuito y de código abierto. Existen versiones profesionales de RStudio con funciones mejoradas, pero no nos basaremos en esas funciones en estos tutoriales.")
+ ),
+ question("¿De dónde descargamos RStudio?",
+ answer("www.rstudio.com/download", correct = TRUE, message = "Para estos tutoriales, descarga e instala el RStudio Desktop de licencia de código abierto"),
+ answer("[cloud.r-project.org](http://cloud.r-project.org)", message = "Aquí es donde descargas R, no RStudio"),
+ answer("www.r-project.org"),
+ answer("[cran.rstudio.org](http://cran.rstudio.org)"),
+ allow_retry = TRUE
+ ),
+ question("¿Necesitas instalar R si ya tienes RStudio?",
+ answer("Sí.", correct = TRUE),
+ answer("No.", message = "R no viene con RStudio; necesitas instalar R por separado.")
+ )
+)
+```
+
+## Instala Paquetes
+
+### Como instalar paquetes de R
+
+
+
+### Prueba tus conocimientos
+
+```{r names, echo = FALSE}
+quiz(caption = "Cuestionario - Trabajar con Paquetes",
+ question("¿Qué comando usas para instalar paquetes?",
+ answer("`library()`", message = "Veremos qué hace `library()` más adelante."),
+ answer("`install.packages()`", correct = TRUE),
+ answer("`install_packages()`"),
+ answer("No hay ningún comando específico. Debes visitar [cran.r-project.org](http://cran.r-project.org) y descargar paquetes manualmente.", message = "R facilita la descarga de paquetes. Te conectas a Internet y luego ejecutas uno de los comandos anteriores."),
+ allow_retry = TRUE
+ ),
+ question("¿Con qué frecuencia necesitas instalar un paquete en tu ordenador?",
+ answer("Cada vez que reinicias R"),
+ answer("Cada vez que reinicias tu ordenador"),
+ answer("Solo una vez. Luego, R puede encontrarlo en su disco duro según sea necesario.", correct = TRUE),
+ answer("Nunca, mientras esté conectado a Internet.", message = "Esto podría ser cierto si estás usando R en un servicio en la nube. Sin embargo, si estás usando R localmente en tu propio ordenador, necesitarás instalar cada paquete que uses en tu ordenador"),
+ allow_retry = TRUE
+ ),
+ question("¿Qué es el tidyverse?",
+ answer("Una colección de paquetes que funcionan bien juntos y proporcionan herramientas para tareas comunes de ciencia de datos.", correct = TRUE, message = 'Puedes instalar todos los paquetes de tidyverse a la vez con `install.packages("tidyverse")`.'),
+ answer("Un programa de procesamiento de textos para R"),
+ answer("Un estilo de código que se lee como poesía"),
+ answer("Un universo alternativo donde todos son programadores de R"),
+ allow_retry = TRUE
+ )
+)
+```