dplyr?dplyr es un paquete de R muy potente
para la exploración, transformación y resumen de datos en formato de
tabla con filas (observaciones) y columnas (variables). Es un componente
de un conjunto de packages llamado tidyverse desarrollados
por Hadley Wickham
El paquete contiene un conjunto de funciones (o verbos) que realizan operaciones comunes para el manejo de datos tales como: filtrar filas, seleccionar columnas, re-ordenar filas, agregar o transformar columnas, resumir datos. También permite agrupar los datos facilitando la estrategia split-apply-combine, es decir, dividir (split) los datos según una variable de grupo, aplicar (apply) alguna transformación o resumen y combinar (combine) las partes para presentar los resultados.
Si bien R base tiene funciones que realizan las
mismas tareas (split(), subset(),
apply(), sapply(), lapply(),
tapply() and aggregate()), el paquete
dplyr brinda una interface más consistente que permite
trabajar de manera más facil con data.frame (tabla de
datos) más que con vectores.
dplyr?Para instalar por primera vez en la computadora:
# Solo
install.packages("dplyr")
# O junto con la familia tidyverse
install.packages("tidyverse")
Lo anterior se debe realizar por única vez si el paquete no está
previamente instalado en la máquina. Para usar las funciones en una
sesion de trabajo hay que cargarlo con library():
# Solo
library("dplyr")
# O junto con la familia tidyverse
library("tidyverse")
R va a avisarnos en la consola que esta enmascarando
(reemplazando) algunas funciones que ya estaban en el entorno, o bien el
paquete nos devuelve algun mensaje. A menos que diga
Error ..., eso está bien.
dplyr para recordarToda la estrategia de trabajo con datos de dplyr se basa
en 6 verbos:
| verbo | descripción | 
|---|---|
select() | 
selecciona columnas (variables) | 
filter() | 
filtra o selecciona las filas (observaciones) | 
arrange() | 
re-ordena las filas | 
mutate() | 
crea nuevas columnas o modifica las preexistentes | 
summarise() | 
resumen los valores de una variable | 
group_by() | 
permite aplicar los verbos anteriores en subgrupos (split-apply-combine) | 
sample_n() | 
para tomar muestras aleatorias con o sin reposición | 
En la mayoría de los casos la sintaxis es:
function(que_datos, que_hacer_con_los_datos)
En que_datos hay que poner el nombre del set de datos o
data.frame (a menos que se use el operador
%>% para encadenar, mas adelante) y en
que_hacer_con_los_datos indicar que operación, condicion,
transformacion, etc aplicar a las filas y columnas.
dplyr en acciónPara ilustrar el uso del paquete vamos a usar los datos contenidos en
el archivo pesada_terneros.xlsx.
Para más detalles ir a la hoja de descripción de datos.
# Cargar los datos con readxl
library(readxl)
terneros <- read_excel("./data/pesada_terneros.xlsx")
Una tareas básicas cuando se exploran datos es la selección de
columnas de interés (variables). Esto se lleva a cabo con
select().
Para seleccionar las columnas Procedencia,
IDV y Peso:
# Sin asignar
select(terneros, Procedencia, IDV, Peso)
# A tibble: 1,598 × 3
   Procedencia IDV        Peso
   <chr>       <chr>     <dbl>
 1 La Rosita   NR047A202   204
 2 La Rosita   GN685B267   186
 3 La Rosita   AI101A751   182
 4 La Rosita   TM603C877   186
 5 La Rosita   TM420B797   186
 6 La Rosita   LH837F500   208
 7 La Rosita   NR047A217   170
 8 La Rosita   LH837F508   188
 9 La Rosita   GN685B256   172
10 La Rosita   QW110A058   172
# … with 1,588 more rows
# Creando un nuevo set de datos
mis_columnas <- select(terneros, Procedencia, IDV, Peso)
Por defecto, si no se asigna a un nuevo objeto, el resultado de la
operación se imprime en la consola con la función print()
la cual por defecto muestra las 10 primeras observaciones y la cantidad
de columnas que entran en la pantalla. Aquellas columnas que no entran
son indicadas al pie.
Si quiero ver más registros se puede usar el argumento
n = de print()
print(mis_columnas, n = 15)
# A tibble: 1,598 × 3
   Procedencia IDV        Peso
   <chr>       <chr>     <dbl>
 1 La Rosita   NR047A202   204
 2 La Rosita   GN685B267   186
 3 La Rosita   AI101A751   182
 4 La Rosita   TM603C877   186
 5 La Rosita   TM420B797   186
 6 La Rosita   LH837F500   208
 7 La Rosita   NR047A217   170
 8 La Rosita   LH837F508   188
 9 La Rosita   GN685B256   172
10 La Rosita   QW110A058   172
11 La Rosita   LH837F497   188
12 La Rosita   TM420B803   180
13 La Rosita   LH837F514   198
14 La Rosita   II641B940   200
15 La Rosita   IY735C      242
# … with 1,583 more rows
Con n = "all" se imprimen todas (no se muestra por
razones obvias)
El orden en que aparecen las variables en el resultado es el orden que se utilizó al seleccionarlas.
# El orden altera el producto
select(terneros, Procedencia, IDV, Peso)
# A tibble: 1,598 × 3
   Procedencia IDV        Peso
   <chr>       <chr>     <dbl>
 1 La Rosita   NR047A202   204
 2 La Rosita   GN685B267   186
 3 La Rosita   AI101A751   182
 4 La Rosita   TM603C877   186
 5 La Rosita   TM420B797   186
 6 La Rosita   LH837F500   208
 7 La Rosita   NR047A217   170
 8 La Rosita   LH837F508   188
 9 La Rosita   GN685B256   172
10 La Rosita   QW110A058   172
# … with 1,588 more rows
También se puede usar los comnados starts_with(),
ends_with(), contains(), etc (ver
?select_helpers) para más opciones). Para elegir varias
columnas que tienen un patron sin tener que tipear todos los
nombres.
# Selecciona columnas que empiezan con P
select(terneros, starts_with("P"))
# A tibble: 1,598 × 2
   Procedencia  Peso
   <chr>       <dbl>
 1 La Rosita     204
 2 La Rosita     186
 3 La Rosita     182
 4 La Rosita     186
 5 La Rosita     186
 6 La Rosita     208
 7 La Rosita     170
 8 La Rosita     188
 9 La Rosita     172
10 La Rosita     172
# … with 1,588 more rows
Para omitir algunas columnas en la seleccion se puede usar el
- antes del nombre.
# Selecciona columnas que empiezan con P
select(terneros, -IDV, -starts_with("P"))
# A tibble: 1,598 × 5
   Sexo  `Fecha Ingreso`     Categoria Fecha              
   <chr> <dttm>              <chr>     <dttm>             
 1 Macho 2017-07-20 00:00:00 Ternero   2017-04-06 00:00:00
 2 Macho 2017-07-20 00:00:00 Ternero   2017-04-06 00:00:00
 3 Macho 2017-07-21 00:00:00 Ternero   2017-04-06 00:00:00
 4 Macho 2017-07-20 00:00:00 Ternero   2017-04-06 00:00:00
 5 Macho 2017-07-24 00:00:00 Ternero   2017-04-06 00:00:00
 6 Macho 2017-07-24 00:00:00 Ternero   2017-04-06 00:00:00
 7 Macho 2017-07-21 00:00:00 Ternero   2017-04-06 00:00:00
 8 Macho 2017-07-24 00:00:00 Ternero   2017-04-06 00:00:00
 9 Macho 2017-07-21 00:00:00 Ternero   2017-04-06 00:00:00
10 Macho 2017-07-24 00:00:00 Ternero   2017-04-06 00:00:00
# … with 1,588 more rows, and 1 more variable: Hora <dttm>
Otra tarea muy frecuente es seleccionar casos o observaciones que
cumplan con alguna condición. Esto se lleva a cabo con
filter(). Se pueden usar los operadores booleanos
==, >, <,
>=, <=, !=,
%in%) para crear pruebas o condiciones lógicas.
Para seleccionar los terneros de Los Corralitos:
# Sin asignar
filter(terneros, Procedencia == 'Los Corralitos')
# A tibble: 575 × 8
   IDV       Procedencia    Sexo  `Fecha Ingreso`     Categoria
   <chr>     <chr>          <chr> <dttm>              <chr>    
 1 PO150A167 Los Corralitos Macho 2017-07-23 00:00:00 Ternero  
 2 PO150A168 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 3 PO150A169 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 4 PO150A460 Los Corralitos Macho 2017-07-23 00:00:00 Ternero  
 5 PO150A673 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 6 PO150A461 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 7 PO150A462 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 8 PO150A463 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 9 PO150A433 Los Corralitos Macho 2017-06-06 00:00:00 Ternero  
10 PO150A434 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
# … with 565 more rows, and 3 more variables: Fecha <dttm>,
#   Hora <dttm>, Peso <dbl>
# Creando un nuevo set de datos
corralitos <- filter(terneros, Procedencia == 'Los Corralitos')
La seleccion se puede hacer por más de una condicion. Por ejemplo,
seleccionar los de Los Corralitos que pesen más de 200
kg:
filter(terneros, Procedencia == 'Los Corralitos', Peso > 200)
# A tibble: 260 × 8
   IDV       Procedencia    Sexo  `Fecha Ingreso`     Categoria
   <chr>     <chr>          <chr> <dttm>              <chr>    
 1 PO150A167 Los Corralitos Macho 2017-07-23 00:00:00 Ternero  
 2 PO150A168 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 3 PO150A169 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 4 PO150A673 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 5 PO150A462 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 6 PO150A463 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 7 PO150A434 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 8 PO150A435 Los Corralitos Macho 2017-07-23 00:00:00 Ternero  
 9 PO150A675 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
10 PO150A681 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
# … with 250 more rows, and 3 more variables: Fecha <dttm>,
#   Hora <dttm>, Peso <dbl>
filter() asume que cada condicion se debe cumplir en
simultaneo para que la observación sea seleccionada. Esto equivale a
utilizar el operador & (Y). En caso de querer
seleccionar aquellos registros que cumple una u otra condicion se usa el
operador | (O). Poniendo ! delante de
la condicion se invierte la selección.
# Operador &
filter(terneros, Procedencia == 'Los Corralitos' & Peso > 200)
# A tibble: 260 × 8
   IDV       Procedencia    Sexo  `Fecha Ingreso`     Categoria
   <chr>     <chr>          <chr> <dttm>              <chr>    
 1 PO150A167 Los Corralitos Macho 2017-07-23 00:00:00 Ternero  
 2 PO150A168 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 3 PO150A169 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 4 PO150A673 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 5 PO150A462 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 6 PO150A463 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 7 PO150A434 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 8 PO150A435 Los Corralitos Macho 2017-07-23 00:00:00 Ternero  
 9 PO150A675 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
10 PO150A681 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
# … with 250 more rows, and 3 more variables: Fecha <dttm>,
#   Hora <dttm>, Peso <dbl>
# Operador |
filter(terneros, Procedencia == 'Los Corralitos' | Peso > 200)
# A tibble: 779 × 8
   IDV       Procedencia  Sexo  `Fecha Ingreso`     Categoria
   <chr>     <chr>        <chr> <dttm>              <chr>    
 1 NR047A202 La Rosita    Macho 2017-07-20 00:00:00 Ternero  
 2 LH837F500 La Rosita    Macho 2017-07-24 00:00:00 Ternero  
 3 IY735C    La Rosita    Macho 2017-07-22 00:00:00 Ternero  
 4 QW110A072 La Rosita    Macho 2017-07-24 00:00:00 Ternero  
 5 LH837F526 La Rosita    Macho 2017-07-20 00:00:00 Ternero  
 6 DS289A491 La Rosita    Macho 2017-07-24 00:00:00 Ternero  
 7 TL698HK39 La Rosita    Macho 2017-07-20 00:00:00 Ternero  
 8 LH837F538 La Rosita    Macho 2017-07-21 00:00:00 Ternero  
 9 IW751A017 La Rosita    Macho 2017-07-24 00:00:00 Ternero  
10 NO133A004 Las Glicinas Macho 2017-07-23 00:00:00 Ternero  
# … with 769 more rows, and 3 more variables: Fecha <dttm>,
#   Hora <dttm>, Peso <dbl>
Con el operador %in% se puede especificar un rango de
valores que deben cumplir. Por ejemplo terneros de
Los Corralitos, Las Glicinas y
Don Alberto
# Indicando cada nombre
filter(terneros, Procedencia == 'Los Corralitos', Procedencia == 'Las Glicinas', Procedencia == 'Don Alberto')
# A tibble: 0 × 8
# … with 8 variables: IDV <chr>, Procedencia <chr>, Sexo <chr>,
#   Fecha Ingreso <dttm>, Categoria <chr>, Fecha <dttm>, Hora <dttm>,
#   Peso <dbl>
# Más resumido con %in%
filter(terneros, Procedencia %in% c('Los Corralitos', 'Las Glicinas', 'Don Alberto'))
# A tibble: 1,138 × 8
   IDV       Procedencia  Sexo  `Fecha Ingreso`     Categoria
   <chr>     <chr>        <chr> <dttm>              <chr>    
 1 SZ208I507 Las Glicinas Macho 2017-07-20 00:00:00 Ternero  
 2 SZ208H993 Las Glicinas Macho 2017-06-05 00:00:00 Ternero  
 3 SZ208H849 Las Glicinas Macho 2017-06-05 00:00:00 Ternero  
 4 SZ208H777 Las Glicinas Macho 2017-06-05 00:00:00 Ternero  
 5 GT542A562 Las Glicinas Macho 2017-07-21 00:00:00 Ternero  
 6 OQ152A550 Las Glicinas Macho 2017-07-21 00:00:00 Ternero  
 7 NO133A057 Las Glicinas Macho 2017-07-24 00:00:00 Ternero  
 8 SZ208H888 Las Glicinas Macho 2017-07-22 00:00:00 Ternero  
 9 OQ152A566 Las Glicinas Macho 2017-07-20 00:00:00 Ternero  
10 NO133A047 Las Glicinas Macho 2017-07-20 00:00:00 Ternero  
# … with 1,128 more rows, and 3 more variables: Fecha <dttm>,
#   Hora <dttm>, Peso <dbl>
%>%)dplyr importa el operador %>% de otro
paquete llamado magrittr. Este operador permite encadenar
operaciones realizadas con los verbos. De este modo no hay que ir
creando tablas intermedias o anidar funciones. El operador traduce como
luego y se le de izquierda a derecha y se puede.
Ejemplo: Reportar los IDV y peso de los terneros con más de 250 kg. Esto implicaría seleccionar las columnas de interés y luego filtrar la tabla o vice versa.
# Creando tablas intermedias
terneros2 <- select(terneros, IDV, Peso)
terneros2
# A tibble: 1,598 × 2
   IDV        Peso
   <chr>     <dbl>
 1 NR047A202   204
 2 GN685B267   186
 3 AI101A751   182
 4 TM603C877   186
 5 TM420B797   186
 6 LH837F500   208
 7 NR047A217   170
 8 LH837F508   188
 9 GN685B256   172
10 QW110A058   172
# … with 1,588 more rows
terneros2 <- filter(terneros2, Peso > 250)
terneros2
# A tibble: 75 × 2
   IDV        Peso
   <chr>     <dbl>
 1 NO133A004   258
 2 OQ152A456   258
 3 NO133A006   256
 4 OQ152A553   256
 5 PO150A166   290
 6 PO150A674   256
 7 PO150A656   272
 8 NO133A045   264
 9 PO150A571   264
10 PO150A686   262
# … with 65 more rows
# A tibble: 75 × 2
   IDV        Peso
   <chr>     <dbl>
 1 NO133A004   258
 2 OQ152A456   258
 3 NO133A006   256
 4 OQ152A553   256
 5 PO150A166   290
 6 PO150A674   256
 7 PO150A656   272
 8 NO133A045   264
 9 PO150A571   264
10 PO150A686   262
# … with 65 more rows
# A tibble: 75 × 2
   IDV        Peso
   <chr>     <dbl>
 1 NO133A004   258
 2 OQ152A456   258
 3 NO133A006   256
 4 OQ152A553   256
 5 PO150A166   290
 6 PO150A674   256
 7 PO150A656   272
 8 NO133A045   264
 9 PO150A571   264
10 PO150A686   262
# … with 65 more rows
Con %>% se puede omitir el nombre de la tabla sobre
la que se está trabajando (bonus: menos tipeo).
La última opción se lee: _tomar la tabla terneros,
luego seleccionar las columnas IDV y
Peso, luego filtrar los terneros con pesos
mayores a 250 kg.
El operador de encadenamiento es muy útil cuando se encadenan muchas operaciones.
Para ordenar según algun criterio aplicado a las columnas se usa
arrange(). Por ejemplo, continuar con lo anterior pero
mostrar ordenadospor peso.
# A tibble: 75 × 2
   IDV        Peso
   <chr>     <dbl>
 1 PO150A546   252
 2 NS509H081   252
 3 PO15A710    252
 4 NO133A019   254
 5 PO150A784   254
 6 IA671B182   254
 7 IA671B176   254
 8 PO150A716   254
 9 NO133A006   256
10 OQ152A553   256
# … with 65 more rows
Con decs(variable) se ordena de mayor a menor
# Ordenar de mayor a menor
terneros %>%
  select(IDV, Peso) %>%
  filter(Peso >  250) %>%
  arrange(desc(Peso))
# A tibble: 75 × 2
   IDV        Peso
   <chr>     <dbl>
 1 PO150A679   304
 2 IA671B041   294
 3 MM429A932   292
 4 PO150A166   290
 5 PO150A763   288
 6 IA671B106   288
 7 NS509G964   286
 8 PO150A821   284
 9 IA671B045   282
10 NO133A000   280
# … with 65 more rows
Para crear nuevas columnas aplicando funciones a otras, o bien para
transformar columnas se usa mutate(). Se pueden modificar
más de una columna a la vez. Por ejemplo, suponiendo que interesa
obtener el logaritmo natura de los pesos o elevar los pesos al
cuadrado.
terneros %>% 
  mutate(log_peso = log(Peso), peso2 = Peso**2) %>%
  select(Peso, log_peso, peso2)                        # para que se vea mejor el resultado 
# A tibble: 1,598 × 3
    Peso log_peso peso2
   <dbl>    <dbl> <dbl>
 1   204     5.32 41616
 2   186     5.23 34596
 3   182     5.20 33124
 4   186     5.23 34596
 5   186     5.23 34596
 6   208     5.34 43264
 7   170     5.14 28900
 8   188     5.24 35344
 9   172     5.15 29584
10   172     5.15 29584
# … with 1,588 more rows
Esto no cambia el set de datos terneros ya que no se lo
asignó a ningun objeto. Para sobreescribir o actualiza el set de datos
terneros hay que asignarlo al mismo objeto.
Aclaración: Si se hubiese usado select() el set de datos
terneros solamente contendría las columnas seleccionadas.
Otro ejemplo más útil: calcular los z-scores de los peso (para ello se requiere calcular el promedio y desvio) y crear una columna que indique si es un outlier y luego reportar los que son outliers.
terneros %>% 
  mutate(z = (Peso - mean(Peso))/sd(Peso), outlier = ifelse(abs(z) > 3, "si", "no")) %>%
  filter(outlier == "si") %>%
  select(IDV)
# A tibble: 2 × 1
  IDV      
  <chr>    
1 PO150A679
2 IA671B041
Mediante summarise() se pueden aplicar funciones para
resumir en un solo valor los valores de las columnas. Las funciones a
aplicar deben devolver un único valor, por ejemplo mean().
Si usamos summary() esto devolverá 6 valores y dará
error.
terneros %>%
  summarise(media = mean(Peso), sd = sd(Peso), n = n(), suma = sum(Peso), procedencias = n_distinct(Procedencia))
# A tibble: 1 × 5
  media    sd     n   suma procedencias
  <dbl> <dbl> <int>  <dbl>        <int>
1  183.  37.0  1598 291802            7
Nuevamente estos resultados pueden asignarse a otro objeto o bien encadenarse con otras operaciones.
Otro ejemplo, obtener la cantidad de terneros de cada procedencia
# A tibble: 7 × 2
  Procedencia        n
  <chr>          <int>
1 Don Alberto       69
2 La Alameda       201
3 La Estrella      118
4 La Rosita         98
5 La Segunda        43
6 Las Glicinas     494
7 Los Corralitos   575
Otro ejemplo más, cantidad de terneros de cada procedencia separados en mayor o menor a 200 kg
# A tibble: 14 × 3
   Procedencia    `Peso > 200`     n
   <chr>          <lgl>        <int>
 1 Don Alberto    FALSE           49
 2 Don Alberto    TRUE            20
 3 La Alameda     FALSE          136
 4 La Alameda     TRUE            65
 5 La Estrella    FALSE          112
 6 La Estrella    TRUE             6
 7 La Rosita      FALSE           89
 8 La Rosita      TRUE             9
 9 La Segunda     FALSE            5
10 La Segunda     TRUE            38
11 Las Glicinas   FALSE          428
12 Las Glicinas   TRUE            66
13 Los Corralitos FALSE          315
14 Los Corralitos TRUE           260
El verbo group_by() es muy útil para aplicar operaciones
en subgrupos y presentar todo junto (split-apply-combine). Lo
que hace es indicar que en el data.frame hay una o más
variables que conforman los grupos. Luego cada operación se aplica a
esos subgrupos.
Ejemplo: calcular media, desvio, n y suma para cada procedencia.
terneros %>%
  group_by(Procedencia) %>%
  summarise(media = mean(Peso), sd = sd(Peso), n = n(), suma = sum(Peso))
# A tibble: 7 × 5
  Procedencia    media    sd     n   suma
  <chr>          <dbl> <dbl> <int>  <dbl>
1 Don Alberto     179.  26.9    69  12354
2 La Alameda      190.  39.5   201  38192
3 La Estrella     179.  14.2   118  21138
4 La Rosita       180.  17.4    98  17620
5 La Segunda      229.  24.8    43   9868
6 Las Glicinas    160.  33.7   494  79254
7 Los Corralitos  197.  35.2   575 113376
El verbo sample_n() and sample_frac() son
útiles para tomar muestras aleatorias (con o sin reposición) de un
conjunto de observaciones. También se puede hacer por subgrupo!
# A tibble: 50 × 10
   IDV       Procedencia    Sexo  `Fecha Ingreso`     Categoria
   <chr>     <chr>          <chr> <dttm>              <chr>    
 1 JM173A252 La Alameda     Macho 2017-07-24 00:00:00 Ternero  
 2 CW828Z158 La Rosita      Macho 2017-07-20 00:00:00 Ternero  
 3 JM173A274 La Alameda     Macho 2017-07-21 00:00:00 Ternero  
 4 OH874D871 La Alameda     Macho 2017-07-21 00:00:00 Ternero  
 5 NS509G975 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
 6 SZ208I511 Las Glicinas   Macho 2017-07-21 00:00:00 Ternero  
 7 IA671B212 La Alameda     Macho 2017-07-24 00:00:00 Ternero  
 8 PO150A642 Los Corralitos Macho 2017-07-20 00:00:00 Ternero  
 9 SZ208H840 Las Glicinas   Macho 2017-07-24 00:00:00 Ternero  
10 NS509H025 Los Corralitos Macho 2017-07-22 00:00:00 Ternero  
# … with 40 more rows, and 5 more variables: Fecha <dttm>,
#   Hora <dttm>, Peso <dbl>, log_peso <dbl>, peso2 <dbl>
# Una muestra de 10 novillos de cada procedencia
muestra_procedencia <- terneros %>%
  group_by(Procedencia) %>%
  sample_n(10)
muestra_procedencia
# A tibble: 70 × 10
# Groups:   Procedencia [7]
   IDV       Procedencia Sexo  `Fecha Ingreso`     Categoria
   <chr>     <chr>       <chr> <dttm>              <chr>    
 1 GH738B338 Don Alberto Macho 2017-06-16 00:00:00 Ternero  
 2 GH738B354 Don Alberto Macho 2017-07-21 00:00:00 Ternero  
 3 GH738B326 Don Alberto Macho 2017-07-20 00:00:00 Ternero  
 4 GH738B381 Don Alberto Macho 2017-07-22 00:00:00 Ternero  
 5 GH738B368 Don Alberto Macho 2017-06-16 00:00:00 Ternero  
 6 GH738B380 Don Alberto Macho 2017-07-21 00:00:00 Ternero  
 7 GH738B350 Don Alberto Macho 2017-07-22 00:00:00 Ternero  
 8 GH738B330 Don Alberto Macho 2017-06-16 00:00:00 Ternero  
 9 GH738B386 Don Alberto Macho 2017-07-21 00:00:00 Ternero  
10 GH738B357 Don Alberto Macho 2017-06-16 00:00:00 Ternero  
# … with 60 more rows, and 5 more variables: Fecha <dttm>,
#   Hora <dttm>, Peso <dbl>, log_peso <dbl>, peso2 <dbl>