Sesión 3 - Obtención y preparación de datos

La visualización y uso de análisis exploratorios en R es una herramienta importante para la generación de información, pero es raro que obtenga los datos exactamente en la forma correcta que necesita. A menudo deberá crear algunas variables o resúmenes nuevos, o tal vez solo desea cambiar el nombre de las variables o reordenar las observaciones en para que sea un poco más fácil trabajar con los datos. Aprenderás cómo hacer todo eso (¡y más!) en este capítulo, que le enseñará cómo para transformar sus datos usando el paquete dplyr. Pero antes deberemos aprender a unir las bases de datos con la función merge().

library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union

Cargar los datos

En la sesión anterior estuvimos trabajando con una base de datos de fútbol. El día de hoy trabajaremos con dos bases de datos, todas ellas con información de la empresa AirBnB. Airbnb es una compañía que ofrece una plataforma digital dedicada a la oferta de alojamientos a particulares y turísticos (alquiler vacacional) mediante la cual los anfitriones pueden publicitar y contratar el arriendo de sus propiedades con sus huéspedes; anfitriones y huéspedes pueden valorarse mutuamente, como referencia para futuros usuarios.En esta ocasión tenemos los datos de los alojamientos de la ciudad de Barcelona (España) durante el año 2019. Estos datos se encuentran fragmentados en dos bases diferentes, la primera sobre la ubicación del sitio (Clase 3-1) y la siguiente con la información del alojamiento y sus valoraciones (Clase 3-2)

setwd("C:/Users/Andres Acero/Dropbox/Politécnico/Curso R")
data1 <- read.csv("Clase 3-1.csv")
data2 <- read.csv("Clase 3-2.csv")

Clase 3-1: https://www.dropbox.com/s/k3ilhjyia0lvd55/Clase%203-1.csv?dl=0

Clase 3-2: https://www.dropbox.com/s/fi39f6jpv62z5jm/Clase%203-2.csv?dl=0

Una vez hemos cargado las bases de datos nos damos cuenta que tienen una cantidad desigual de datos:

dim(data1)
## [1] 10367     6
dim(data2)
## [1] 11279    10

Por tanto, no podemos hacer operaciones directamente sobre los datos. Por tanto, lo primero es crear una base de datos conjunta usando la función merge()

Función Merge

Para muchos de ustedes debe ser muy común hacer cruces de bases de datos que se hace con lenguajes como SQL. El comando JOIN es una herramienta muy poderosa que presenta diferentes opciones con las cuales cruzar dos o más tablas. R ha definido una serie de comandos que tratan de “emular” el comportamiento. La función merge() permite fusionar o unir dos data frames por columnas comunes o por nombres de fila. Esta función permite realizar diferentes combinaciones de bases de datos (SQL), como unión izquierda (left join), unión interna (inner join), unión derecha (right join) o unión completa (full join), entre otras. Iremos probando estas operaciones con nuestras bases de datos de AirBnB.

Inner Join

Un inner join (en realidad un natural join), es la unión de conjuntos de datos más habitual que se puede realizar. Consiste en fusionar dos data frames en uno que contenga los elementos comunes de ambos, como se describe en la siguiente ilustración:

Figura 1: Inner Join

Para fusionar o unir los dos conjuntos de datos de muestra, solo tienes que pasarlos a la función merge, sin la necesidad de cambiar otros argumentos, debido a que, de manera predeterminada, la función combina los conjuntos de datos por los nombres de las columnas comunes. En consecuencia, en este caso, la función fusiona los datos por las primera columnas, id y host_id:

data3 <-merge(x=data1, y=data2)

Como podrán notar, en la intersección de las dos bases de datos, que tenemos 8459 lugares. Si queremos la información completa de los lugares, esta es la mejor opción. Por otro lado, si queremos asegurarnos que sea por estas variables podemos modificar la función merge():

data3 <- merge(x=data1, y=data2, by=c("id","host_id"))

¿En qué casos de ustedes se les ocurre que les podría servir esta función?

Full Join

Pero ¿qué pasa si queremos tener las bases de datos completos? En muchas ocasiones tener una gran cantidad de datos ayudan aunque estén incompleto, por lo que un full (outer) join es muy útil. El outer join, o unión completa, combina todas las columnas de ambos conjuntos de datos en uno para todos los elementos

Figura 2: Full Join

Para realizar esta actividad usaremos el parámetro all hacia TRUE, como lo ven a continuación:

data3 <- merge(x=data1, y=data2, all = TRUE)

Como podrán notar, en la unión de las dos bases de datos tenemos 13187 lugares.Ahora, si revisamos los datos veremos que tenemos ahora datos en NA, por lo que esto podría ser problemático para analizar los datos:

unique(data3$room_type)
## [1] "Entire home/apt" "Private room"    "Shared room"     NA               
## [5] ""

¿En qué casos de ustedes se les ocurre que les podría servir esta función?

Left Join

Ahora, pensemos que necesitamos preservar la información de las ubicaciones de los alojamientos pero la otra información no es tan importante. Para eso podremos conservar toda la información de la base de datos en el lado izquierdo y solamente la información que coincida en la base de datos en el lado derecho. Por tanto, El left join en R consiste en unir todas las filas del primer data frame con los valores correspodientes del segundo.

Figura 3: Left Join

Para realizar esta actividad usaremos el parámetro all.x hacia TRUE, como lo ven a continuación:

data3 <- merge(x=data1, y=data2, all.x = TRUE)

Como podrán notar, esta función genera la misma cantidad de posiciones que data1 (izquierdo). Al igual que el método anterior, este método llena los espacios vacíos con N/A, los cuales son problemáticos:

unique(data3$room_type)
## [1] "Entire home/apt" "Private room"    "Shared room"     NA               
## [5] ""

¿En qué casos de ustedes se les ocurre que les podría servir esta función?

Right Join

¿Y si necesitamos lo contrario?, es decir, que queremos conservar la información completa del lado derecho. El right join en R es lo opuesto al left join. En este caso, la combinación consiste en unir todas las filas del segundo data frame con las correspondientes en el primero.

Figura 4: Right Join

Para realizar esta actividad usaremos el parámetro all.y hacia TRUE, como lo ven a continuación:

data3 <- merge(x=data1, y=data2, all.y = TRUE)

Como podrán notar, esta función genera la misma cantidad de posiciones que data2 (derecho). Al igual que el método anterior, este método llena los espacios vacíos con N/A, los cuales son problemáticos:

unique(data3$neighbourhood_group)
##  [1] "Sant Martí"          "Eixample"            "Gràcia"             
##  [4] "Horta-Guinardó"      "Les Corts"           "Ciutat Vella"       
##  [7] "Sants-Montjuïc"      "Sarrià-Sant Gervasi" NA                   
## [10] "Sant Andreu"         "Nou Barris"

¿En qué casos de ustedes se les ocurre que les podría servir esta función?

Cross Join

La última opción para hacer el cruce de la base de datos es poner cada posible combinación de datos. El cross join o unión cruzada, realiza el producto cartesiano de los conjuntos de datos. En matemáticas, el producto cartesiano de dos conjuntos es una operación, que resulta en otro conjunto, cuyos elementos son todos los pares ordenados que pueden formarse de forma que el primer elemento del par ordenado pertenezca al primer conjunto y el segundo elemento pertenezca al segundo conjunto.

Figura 5: Cross Join

Para realizar esta actividad usaremos el parámetro By hacia NULL, como lo ven a continuación:

data3 <- merge(x=data1[1:10,], y=data2, by= NULL[1:10,]) 

Este proceso, dada la cantidad de operaciones, se demora una cantidad considerable de tiempo y crea un archivo con demasiadas filas (por lo que se limita el tamaño del ejemplo).

¿En qué casos de ustedes se les ocurre que les podría servir esta función?

Unir varios dataframes

En el caso de requerir unir más de una base de datos, podemos usar un proceso iterativo o anidado como podrán ver acá:

data3 <- merge(x=data1[1:100,], merge(x=data1[100:500,],y=data2, all=TRUE), all.x = TRUE)

Ejercicio conjunto

Una vez hemos aprendido cómo unir las bases de datos, los reto a que me den la respuesta a las siguientes preguntas:

Realice las siguientes operaciones con el archivo “Clase 3-2.csv”

Realice las mismas operaciones con una nueva base de datos que incluya ambos archivos usando Inner Join

Manipulación de datos

En muchas ocasiones la información que obtenemos puede tener vacíos en algunas posiciones, estar ordenada de forma poco práctica o contener más información de la que necesitamos. Con el fin de depurar la información disponible, ordenarla de forma adecuada para su procesamiento o agregar información adicional, podemos realizar una serie de operaciones.

Dplyr es una librería ampliamente usada en la transformación de datos dentro de R. Sus funciones destacadas incluyen:

  • Filtrar información de acuerdo a diferentes parámetros.

  • Reordenar, adicionar o eliminar las columnas de un arreglo.

  • Seleccionar información relevante y crear un arreglo secundario con dicha información.

  • Agrupar bases de datos

Las funciones que se verá a continuación trabajan de forma similar:

  1. El primer argumento siempre será la lista que contiene la información.

  2. Los siguientes argumentos describen que hacer con los datos.

  3. El resultado de su uso es una nueva lista o data frame.

Filter

Imaginemos que tenemos la base de datos de la clase anterior. Muchas de las preguntas que se formularon necesitaban que se obtuvieran una serie de filtros sobre características específicas de la base de datos. Para ello utilizaremos la función Filter, una función que nos permite acortar la información que se muestra de una lista basándose en los valores que contiene. En los input correspondientes se debe nombrar la variable a filtrar y el valor que se desea. Sobre la base de datos conjunta (Inner Join) vamos a estar trabajando. Quisiera filtrar los datos para habitaciones privadas:

data3 <- merge(x=data1, y=data2)
data4 <- filter(data3, room_type == "Private room")

En casos especiales se desea filtrar pensando un grupo de parámetros para una misma variable. En dichos casos podemos usar operadores lógicos para agrupar dichas condiciones. Ahora quiero saber, adicionalmente, los que tengan más de 32 días de uso:

data4 <- filter(data3, room_type == "Private room" & minimum_nights > 32)  

Las condiciones que podremos dar son las siguientes:

Figura 6: Operaciones relacionales

En algunas ocasiones la recolección de datos incluye campos que no fueron llenados. Al importar una base de datos con estos campos vacíos R los considera como “NA”. Cualquier operación que involucre un campo NA producirá un error y si operamos una base de datos sin depurar puede ser problemático.

is.na() nos provee una herramienta simple con la cual podemos localizar dichos campos durante el preprocesamiento de la información, permitiéndonos elegir que operación será la más adecuada.

Además, los operadores lógicos son:

Figura 7: Operaciones lógicos

¿Qué ejemplos se les ocurren a ustedes?

Arrange

Al manejar grandes volúmenes de datos se hace poco práctico ordenar valores uno a uno, arrange() nos permite crear una lista en la que los valores de las filas se ordenarán de acuerdo a los parámetros que suministremos. Si suministramos más de un parámetro a arrange(), el orden se tomara como prioritario para los primeros parámetros que se suministren. Si yo quisiera organizar los datos por noches disponibles al año, el comando es el siguiente:

head(data3)
##         id  host_id neighbourhood_group                         neighbourhood
## 1 10003263 34111537      Sants-Montjuïc                          el Poble Sec
## 2 10005342 51395102        Ciutat Vella Sant Pere, Santa Caterina i la Ribera
## 3 10007287   383697      Sants-Montjuïc                          el Poble Sec
## 4 10008491 40676061            Eixample                         el Fort Pienc
## 5 10009651 51412210            Eixample        la Nova Esquerra de l'Eixample
## 6 10009907  8876390        Ciutat Vella Sant Pere, Santa Caterina i la Ribera
##   latitude longitude       room_type price minimum_nights number_of_reviews
## 1 41.37116   2.16541 Entire home/apt    45              2                 8
## 2 41.38608   2.17976 Entire home/apt    30             31                66
## 3 41.37433   2.16672    Private room    88              1                37
## 4 41.39707   2.18052    Private room   150             32                 3
## 5 41.38325   2.14671 Entire home/apt    90              3                77
## 6 41.38958   2.17895 Entire home/apt    48             60                 2
##   last_review reviews_per_month calculated_host_listings_count availability_365
## 1  01/07/2016              0.22                              1                0
## 2  10/03/2019              1.68                              1              248
## 3  10/03/2019              0.98                             16              227
## 4  31/01/2019              0.19                             18              112
## 5  09/04/2019              1.93                              1               95
## 6  30/06/2017              0.08                              6               43
data4 <-arrange(data3, availability_365)
head(data4)
##         id  host_id neighbourhood_group                         neighbourhood
## 1 10003263 34111537      Sants-Montjuïc                          el Poble Sec
## 2 10018775 51446397              Gràcia                     la Vila de Gràcia
## 3 10085581 19060475            Eixample                    la Sagrada Família
## 4 10111416 19060475            Eixample                    la Sagrada Família
## 5 10120072  8251905        Ciutat Vella Sant Pere, Santa Caterina i la Ribera
## 6 10150711 52096900          Sant Martí            el Camp de l'Arpa del Clot
##   latitude longitude       room_type price minimum_nights number_of_reviews
## 1 41.37116   2.16541 Entire home/apt    45              2                 8
## 2 41.40058   2.15427    Private room    22              1                 4
## 3 41.40954   2.17467    Private room    45              2                33
## 4 41.41079   2.17552 Entire home/apt    25              3                27
## 5 41.38482   2.17982    Private room    40              7                85
## 6 41.41076   2.18219    Private room    11              4                 2
##   last_review reviews_per_month calculated_host_listings_count availability_365
## 1  01/07/2016              0.22                              1                0
## 2  20/05/2018              0.27                              1                0
## 3  23/09/2017              0.83                              2                0
## 4  23/09/2017              0.68                              2                0
## 5  15/08/2018              2.13                              1                0
## 6  27/03/2016              0.05                              1                0

La función auxiliar para los parámetros que recibe arrange es desc() para generar valores descendientes ya que lo ordena para valores ascendentes.

data4 <-arrange(data3, desc(availability_365))
head(data4)
##         id  host_id neighbourhood_group                 neighbourhood latitude
## 1 10034834  8848709          Sant Martí la Vila Olímpica del Poblenou 41.38795
## 2 10237654  8848709          Sant Martí la Vila Olímpica del Poblenou 41.38739
## 3 11975045  7414663          Sant Martí    el Camp de l'Arpa del Clot 41.41122
## 4 12219001 45515449            Eixample        la Dreta de l'Eixample 41.39520
## 5 12337767 14304404         Sant Andreu      el Congrés i els Indians 41.42653
## 6 13345852 75620994           Les Corts                     les Corts 41.38788
##   longitude       room_type price minimum_nights number_of_reviews last_review
## 1   2.19836 Entire home/apt   450              2                 7  27/07/2018
## 2   2.19864 Entire home/apt   295              1                 1  29/05/2016
## 3   2.18108 Entire home/apt   189             15                 5  31/10/2016
## 4   2.17354 Entire home/apt    60             30                 1  31/08/2018
## 5   2.18026    Private room    39              1                 1  27/06/2016
## 6   2.13748 Entire home/apt    85             32                 2  02/11/2016
##   reviews_per_month calculated_host_listings_count availability_365
## 1              0.18                              8              365
## 2              0.03                              8              365
## 3              0.14                             38              365
## 4              0.13                              2              365
## 5              0.03                              2              365
## 6              0.06                             13              365

¿Qué ejemplos se les ocurren a ustedes?

Select

Algunas veces tenemos a nuestra disposición más información de la que es necesaria, en bases de datos donde contamos con muchas columnas puede ser un factor distractor si no contamos con la práctica suficiente. La función select() nos permite extraer de las bases de datos la información necesaria, dicha información podrá ser almacenada en un nuevo arreglo. La primera versión es seleccionar algunas columnas

data4 <- select(data3, id, price, last_review) 
head(data4)
##         id price last_review
## 1 10003263    45  01/07/2016
## 2 10005342    30  10/03/2019
## 3 10007287    88  10/03/2019
## 4 10008491   150  31/01/2019
## 5 10009651    90  09/04/2019
## 6 10009907    48  30/06/2017

Si nuestra intención, por el contrario, es quitar alguna columna, lo que debemos hacer es poner un guión -:

data4 <- select(data3, -host_id)
head(data4)
##         id neighbourhood_group                         neighbourhood latitude
## 1 10003263      Sants-Montjuïc                          el Poble Sec 41.37116
## 2 10005342        Ciutat Vella Sant Pere, Santa Caterina i la Ribera 41.38608
## 3 10007287      Sants-Montjuïc                          el Poble Sec 41.37433
## 4 10008491            Eixample                         el Fort Pienc 41.39707
## 5 10009651            Eixample        la Nova Esquerra de l'Eixample 41.38325
## 6 10009907        Ciutat Vella Sant Pere, Santa Caterina i la Ribera 41.38958
##   longitude       room_type price minimum_nights number_of_reviews last_review
## 1   2.16541 Entire home/apt    45              2                 8  01/07/2016
## 2   2.17976 Entire home/apt    30             31                66  10/03/2019
## 3   2.16672    Private room    88              1                37  10/03/2019
## 4   2.18052    Private room   150             32                 3  31/01/2019
## 5   2.14671 Entire home/apt    90              3                77  09/04/2019
## 6   2.17895 Entire home/apt    48             60                 2  30/06/2017
##   reviews_per_month calculated_host_listings_count availability_365
## 1              0.22                              1                0
## 2              1.68                              1              248
## 3              0.98                             16              227
## 4              0.19                             18              112
## 5              1.93                              1               95
## 6              0.08                              6               43

Y si queremos hacer de un punto al otro del arreglo, las podemos escoger usando el símbolo ::

data4 <- select(data3, host_id:price)
head(data4)
##    host_id neighbourhood_group                         neighbourhood latitude
## 1 34111537      Sants-Montjuïc                          el Poble Sec 41.37116
## 2 51395102        Ciutat Vella Sant Pere, Santa Caterina i la Ribera 41.38608
## 3   383697      Sants-Montjuïc                          el Poble Sec 41.37433
## 4 40676061            Eixample                         el Fort Pienc 41.39707
## 5 51412210            Eixample        la Nova Esquerra de l'Eixample 41.38325
## 6  8876390        Ciutat Vella Sant Pere, Santa Caterina i la Ribera 41.38958
##   longitude       room_type price
## 1   2.16541 Entire home/apt    45
## 2   2.17976 Entire home/apt    30
## 3   2.16672    Private room    88
## 4   2.18052    Private room   150
## 5   2.14671 Entire home/apt    90
## 6   2.17895 Entire home/apt    48

Finalmente, también lo podemos hacer para eliminar un rango de columnas:

data4 <- select(data3, -(host_id:price))
head(data4)
##         id minimum_nights number_of_reviews last_review reviews_per_month
## 1 10003263              2                 8  01/07/2016              0.22
## 2 10005342             31                66  10/03/2019              1.68
## 3 10007287              1                37  10/03/2019              0.98
## 4 10008491             32                 3  31/01/2019              0.19
## 5 10009651              3                77  09/04/2019              1.93
## 6 10009907             60                 2  30/06/2017              0.08
##   calculated_host_listings_count availability_365
## 1                              1                0
## 2                              1              248
## 3                             16              227
## 4                             18              112
## 5                              1               95
## 6                              6               43

¿Qué ejemplos se les ocurren a ustedes?

Mutate

En algunas ocasiones debemos añadir valores nuevos a nuestras bases de datos, bien sea más información o el resultado de alguna operación. Con la función mutate() podemos computar tranformaciones de variables en un data frame. A menudo, tendremos la necesidad de crear nuevas variables que se calculan a partir de variables existentes,mutate() nos proporciona una interface clara para realizar este tipo de operaciones. Vamos a crear una variable del precio mínimo del lugar al multiplicar el precio por noche por la mínima cantidad de noches:

data4 <- mutate(data3, minPrice = price*minimum_nights)
summary(data4$minPrice)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##     8.0    66.0   140.0   482.1   380.0 30000.0

Summarise y Group_By

Y ahora, ¿qué hago si yo quiero crear un nuevo dataframe que tenga el resumen de las variables? Sencillo, para eso existe el método summarise(). Este método funciona de manera análoga que la función mutate(), pero en lugar de crear una nueva columna con el cálculo que se realice lo que obtienes es el resultado de cada una de las operaciones en un nuevo dataframe. Así por ejemplo, ara calcular la media, mediana y la desviación estandar de la variable priceen el conjunto de datos:

data4 <- summarise(data3, media= mean(price), mediana=median(price), desviacion= sd(price))
data4
##      media mediana desviacion
## 1 96.74914      60   187.2671

Todas las operaciones que vimos las clases anteriores y las que aprendieron en el curso anterior de R las podrán utilizar acá. Sin embargo, tener la información de toda la columna no parece tan interesante. Por tanto, existe otro método que nos ayudará con esta labor, group_by(). Esta función agrupa un conjunto de filas seleccionado en un conjunto de filas de resumen de acuerdo con los valores de una o más columnas o expresiones. Si quisieramos agrupar, por ejemplo, a las variables de nuestro dataframe de acuerdo con el área en el que está debemos hacer las siguientes operaciones:

data4 <- group_by(data3, neighbourhood_group)
head(data4)
## # A tibble: 6 x 14
## # Groups:   neighbourhood_group [3]
##         id  host_id neighbourhood_gr~ neighbourhood latitude longitude room_type
##      <int>    <int> <chr>             <chr>            <dbl>     <dbl> <chr>    
## 1 10003263 34111537 Sants-Montjuïc    el Poble Sec      41.4      2.17 Entire h~
## 2 10005342 51395102 Ciutat Vella      Sant Pere, S~     41.4      2.18 Entire h~
## 3 10007287   383697 Sants-Montjuïc    el Poble Sec      41.4      2.17 Private ~
## 4 10008491 40676061 Eixample          el Fort Pienc     41.4      2.18 Private ~
## 5 10009651 51412210 Eixample          la Nova Esqu~     41.4      2.15 Entire h~
## 6 10009907  8876390 Ciutat Vella      Sant Pere, S~     41.4      2.18 Entire h~
## # ... with 7 more variables: price <int>, minimum_nights <int>,
## #   number_of_reviews <int>, last_review <chr>, reviews_per_month <dbl>,
## #   calculated_host_listings_count <int>, availability_365 <int>

Si miramos el resultado no veremos un cambio, ¿cierto? Lo que realizó fue un dataframe que tiene particiones internas que podremos utilizar. La función group_by() es extremadamente útil trabajando en conjunción con la función summarise() ya que permite realizar las operaciones sobre cada una de las divisiones que se crean. Ahora cruzaremos las dos operaciones, buscaremos la media, la moda y la desviación estándar de cada una de las áreas de Barcelona:

data4 <- summarise(group_by(data3, neighbourhood_group), media= mean(price), mediana=median(price), desviacion= sd(price))
data4
## # A tibble: 10 x 4
##    neighbourhood_group media mediana desviacion
##    <chr>               <dbl>   <dbl>      <dbl>
##  1 Ciutat Vella         73.0      50       70.3
##  2 Eixample            125.       80      199. 
##  3 Gràcia               87.8      65      196. 
##  4 Horta-Guinardó       55.2      38       66.3
##  5 Les Corts           102.       50      318. 
##  6 Nou Barris           42.1      29       55.5
##  7 Sant Andreu          50.0      30      134. 
##  8 Sant Martí           94.5      52      158. 
##  9 Sants-Montjuïc       90.7      55      300. 
## 10 Sarrià-Sant Gervasi  90.9      60       80.4

¿Qué ejemplos se les ocurren a ustedes?

Uso de pipes

El operador pipeline %>% es útil para concatenar múltiples dplyr operaciones. Obsérvese en el siguiente ejemplo, que cada vez que queremos aplicar mas de una función, la instrucción es una secuencia de llamadas a funciones de forma anidada y que resulta ilegible:

data4 <- summarise(group_by(data3, neighbourhood_group), media= mean(price), mediana=median(price), desviacion= sd(price))
summary(data4)
##  neighbourhood_group     media           mediana        desviacion    
##  Length:10           Min.   : 42.06   Min.   :29.00   Min.   : 55.47  
##  Class :character    1st Qu.: 59.66   1st Qu.:41.00   1st Qu.: 72.80  
##  Mode  :character    Median : 89.25   Median :51.00   Median :146.29  
##                      Mean   : 81.14   Mean   :50.90   Mean   :157.79  
##                      3rd Qu.: 93.60   3rd Qu.:58.75   3rd Qu.:198.26  
##                      Max.   :125.10   Max.   :80.00   Max.   :317.53

Este anidamiento no es una forma natural de expresar un secuencia de operaciones. El operador %>% nos permite escribir una secuencia de operaciones de izquierda a derecha:

data4 <- data3 %>% group_by(neighbourhood_group) %>% summarise(media= mean(price), mediana=median(price), desviacion= sd(price))
summary(data4)
##  neighbourhood_group     media           mediana        desviacion    
##  Length:10           Min.   : 42.06   Min.   :29.00   Min.   : 55.47  
##  Class :character    1st Qu.: 59.66   1st Qu.:41.00   1st Qu.: 72.80  
##  Mode  :character    Median : 89.25   Median :51.00   Median :146.29  
##                      Mean   : 81.14   Mean   :50.90   Mean   :157.79  
##                      3rd Qu.: 93.60   3rd Qu.:58.75   3rd Qu.:198.26  
##                      Max.   :125.10   Max.   :80.00   Max.   :317.53

El atajo para sacar el pipe es Ctrl + Shift + M (Windows) o Cmd + Shift + M