-
Notifications
You must be signed in to change notification settings - Fork 0
/
02-EDA.Rmd
279 lines (213 loc) · 12.6 KB
/
02-EDA.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# Análisis Exploratorio de los datos
En este capítulo abordaremos el análisis exploratorio de los datos *"EDA"* para el listado de Airbnb de la ciudad de Nueva York en el año 2019.
## Resumen de los datos {-}
Empezamos el análisis exploratorio de nuestros datos con las estadísticas de resumen, haciendo uso de la función `summary` para los datos contenidos en la tabla `airbnb` de nuestra base de datos en `Heroku`
```{r}
con <- dbConnect(RPostgres::Postgres(),
dbname = "d41lsl8qgestjf",
host = "ec2-3-229-43-149.compute-1.amazonaws.com",
port = 5432,
user = "uqtxfaqjjcxggw",
password = "916d311356954de6a99118d13578bb9d1b47bdc86cb8360a60b9606293bd882d")
df = dbGetQuery(con, "SELECT * FROM airbnb")
summary(df)
```
De las columnas de nuestro dataframe, podemos decir por ejemplo que el precio de una noche de los airbnb oscila entre 0 y 10.000 dólares con un promedio de 152.7 dólares, más adelante revisaremos si un precio diario de 10.000 dólares es o no un dato atípico. Adicionalmente, los alojamientos se pueden reservar desde una (1) noche, sin embargo hay algunos cuyas noches mínimas son de 1.250, alrededor de 3.5 años, esto también es un candidato a dato atípico que será revisado en la siguente sección. Por otro lado, existen anfitriones que tienen hasta 327 alojamientos en la región.
A pesar, que las columnas `id`, `host_id`, `latitude` y `longitude` son numéricas, no son relevantes las métricas de mínimo, máximo, media y cuartiles.
Evaluando los valores que pueden tomar las variables categóricas usando la función `unique`, encontramos que los tipos de alojamiento disponibles son: Habitaciones privadas ("Private room"), Apartamentos Completos ("Entire home/apt") o Habitaciones compartidas ("Shared room") y tenemos grupos de vencindarios como Brooklyn, Manhattan, Qeens, Staten Island y Bronx y en total 221 vecindarios disponibles para alojamiento.
```{r}
unique(df$room_type)
unique(df$neighbourhood_group)
length(unique(df$neighbourhood))
```
## Datos Atípicos {-}
Tenemos sospechas que existen datos atípicos en las columnas `price` y `minimum_nights`. Al analizar los histogramas y boxplots encontramos que estos abarcan la mayor parte del rango de las variables.
```{r warning=FALSE}
par(mfrow = c(1, 2))
g1 = ggplot(df) +
aes(x = price) +
geom_histogram(fill = "blue") +
theme_minimal()
g2 = ggplot(df) +
aes(x = minimum_nights) +
geom_histogram(fill = "red") +
theme_minimal()
g3 = ggplot(df) +
aes(x = "", y = price) +
geom_boxplot(fill = "blue") +
theme_minimal()
g4 = ggplot(df) +
aes(x = "", y = minimum_nights) +
geom_boxplot(fill = "red") +
theme_minimal()
fig = subplot(g1, g3,g2,g4, nrows = 2, shareX = FALSE)
annotations = list(
list(
x = 0.2,
y = 1.0,
text = "Price",
xref = "paper",
yref = "paper",
xanchor = "center",
yanchor = "bottom",
showarrow = FALSE
),
list(
x = 0.8,
y = 1,
text = "",
xref = "paper",
yref = "paper",
xanchor = "center",
yanchor = "bottom",
showarrow = FALSE
),
list(
x = 0.2,
y = 0.43,
text = "Minimum_nights",
xref = "paper",
yref = "paper",
xanchor = "center",
yanchor = "bottom",
showarrow = FALSE
),
list(
x = 0.8,
y = 0.45,
text = "",
xref = "paper",
yref = "paper",
xanchor = "center",
yanchor = "bottom",
showarrow = FALSE
))
fig <- fig %>%layout(annotations = annotations)
#options(warn = -1)
fig
```
Según la función `boxplot.stats()$out` que se basa en el criterio `IQR`, los valores atípicos para el precio son aquellos mayores que 335 mientras que para el mínimo de noches el umbral está en 12.
```{r}
min(boxplot.stats(df$price)$out)
min(boxplot.stats(df$minimum_nights)$out)
```
Si revisamos por el criterio de los percentiles, teniendo en cuenta que las observaciones por fuera del intervalo formado por los percentiles 2.5 y 97.5 se consideran posibles valores atípicos, encontramos que para el precio son potenciales datos atípicos aquellos por debajo de 30 y por encima de 799 y en el caso de las noches mínimas, aquellas obervaciones por debajo de 1 y por encima de 30.
```{r}
c(quantile(df$price, 0.025), quantile(df$price, 0.975))
c(quantile(df$minimum_nights, 0.025), quantile(df$minimum_nights, 0.975))
```
Otro método, es el filtro de Hampel que determina un dato atípico estableciendo un intervalo formado por la mediana, teniendo entonces para este caso que si el precio está por encima de 244 y las noches minimas superan las 9.
```{r}
c(median(df$price) - 3 * mad(df$price, constant = 1), median(df$price) + 3 * mad(df$price, constant = 1))
c(median(df$minimum_nights) - 3 * mad(df$minimum_nights, constant = 1), median(df$minimum_nights) + 3 * mad(df$minimum_nights, constant = 1))
```
Ahora bien, revisemos la prueba de Grubbs haciendo uso de la función `grubbs.test` que detecta si el valor más alto o más bajo del conjunto de datos es un valor atípico.
```{r}
grubbs.test(df$price)
grubbs.test(df$price, opposite = TRUE)
grubbs.test(df$minimum_nights)
grubbs.test(df$minimum_nights, opposite = TRUE)
```
Teniendo un umbral $\alpha = 0.05$, tenemos que para el caso del precio, el p-valor es menor cuando se evalua la hipótesis nula $H_0: \text{El valor más alto no es un valor atípico}$ pero es mayor que este umbral escogido cuando se evalua la hopótesis nula $H_0: \text{El valor más bajo no es un valor atípico}$, por lo tanto la primera hipótesis se rechaza y la segunda se acepta por lo que se concluye que el valor más alto es valor atípico y el valor más bajo no lo es. Obtenemos el mismo resultado en el caso de las noches mínimas.
En este caso no realizamos la prueba de Dixon, que al igual que la Grubbs detecta si el valor más alto o más bajo es atípico porque esta prueba funciona mejor en conjunts de datos que tienen menos de 25 elementos y este no es nuestro caso. Sin embargo, realizaremos una última prueba, la de Rosner que a diferencia de las otras nos ayuda a detectar varios valores atípicos a la vez, eso usando la función `rosnerTest`.
Esta prueba debe recibir además del dataset, el número de presuntos valores atípicos. Para determinar dicho número, usaremos los resultados de las pruebas anteriores (no basadas en el valor p), dónde habíamos encontrados en un caso que los valores por encima de 500 para el precio y por encima de 30 para las noches mínimas, representaban valores atípicos. El número de registros por encima de estos valores será usado como el *k* de la función `rosnerTest`.
```{r}
nrow(df[df$price>500, ])
test <- rosnerTest(df$price, k = 1044)
test$all.stats[920:930, ]
```
```{r}
nrow(df[df$minimum_nights>30, ])
test <- rosnerTest(df$minimum_nights, k = 747)
test$all.stats[460:470, ]
```
El test de Rosner nos indica que son valores atípicos, precios a partir de 575 dólares y noches mínimas a partir de 50. Usando este criterio, eliminamos el 2.8% de nuestros datos, resultando un dataframe con 1370 registros menos.
```{r}
nrow(df)
nrow(df[df$price>=575 | df$minimum_nights>=50 , ])
nrow(df[df$price>=575 | df$minimum_nights>=50 , ])/nrow(df)*100.00
```
Una vez eliminados estos datos atípicos, reemplazamos los registros de la tabla `airbnb` creada nuevamente con estos sin datos atípicos, usando la función `dbWriteTable()`
```{r}
df_out = df[df$price<575 & df$minimum_nights<50, ]
dbWriteTable(con, 'airbnb', df_out, row.names=FALSE, overwrite=TRUE)
```
Volvemos a extraer las estadísticas de resumen:
```{r}
summary(df_out)
```
Con estos cambios en nuestro dataframe, podemos ver que los precios ahora oscilan entre 0 y 573 dólares y las noches mínimas no superan las 47 (mes y medio), lo cual tiene mayor sentido para nuestro análisis.
## Análisis del anfitrion {-}
A través de la función de agrupación `group_by`, podemos obtener los anfitriones con más alojamientos disponibles. Hemos encontrando que el anfitrión con mayor número de alojamientos tiene 317 lo que no es consistente con la columna `calculated_host_listings_count` (327) y esto se debe a que hemos eliminado datos del dataframe original una vez identificados los valores atípicos, es por esto, que esta columna, se eliminará.
```{r warning=FALSE}
df_out %>%
group_by(host_id) %>%
summarise(n = n()) %>%
arrange(desc(n)) %>%
head(5) -> top_hostid
g = ggplot(data= top_hostid, aes(x = reorder(factor(host_id), n), y=n)) +
geom_bar(stat="identity", fill = "blue") +
xlab("Host_Id") + ylab("Cant. alojamientos") +
ggtitle("Top 5 de los anfitriones con más alojamientos")
ggplotly(g)
borrar = c("calculated_host_listings_count")
df_out = df_out[, !(names(df_out) %in% borrar)]
```
Ahora, revisemos la distribución de alojamientos por grupos de vecindarios.
```{r fig.width=10, warning=FALSE}
df_host = filter(df_out, host_id %in% top_hostid$host_id)
df_host%>%
group_by(host_id, neighbourhood_group) %>%
summarise(n = n()) %>%
ggplot(aes(x=factor(host_id), y=n, fill=factor(neighbourhood_group))) +
geom_bar(stat="identity", position="dodge")+
xlab("Host_id") + ylab("Cant. Alojamientos") +
ggtitle("Top 5 de anfitriones con más alojamientos distribuidos por grupo vecindario")+
theme(legend.title = element_blank()) -> g
ggplotly(g)
```
A pesar, que los grupos de vecindarios disponibles corresponden a Brooklyn, Manhattan, Qeens, Staten Island y Bronx, vemos que los anfitriones con más alojamientos no tienen disponibilidad en Staten Island y Bronx, la mayoría de estos alojamientos se encuentran en Manhattan y solo dos de estos cinco tienen alojamientos en Brooklyn y el anfitrion 137358866 es el único con alojamiento en Queens
Si revisamos la distribución de los precios de estos cinco anfitriones, atraves, de un `boxplot`, tenemos que el anfitrion 107434423 tiene el mayor precio promedio y el anfitrion 137358866 tiene todos sus alojamnientos en precios similares y más bajos en comparación con los otros.
```{r}
p <- plot_ly(df_host, y = ~price,
alpha = 0.1, boxpoints = "suspectedoutliers")
p1 <- p %>% add_boxplot(x = "Overall")
p2 <- p %>% add_boxplot(x = ~factor(host_id))
subplot(
p1, p2, shareY = TRUE,
widths = c(0.2, 0.8), margin = 0
) %>% hide_legend()
```
El host 107434423 tiene precios por encima de la media del top 5 de anfitriones mientras que el host 137358866, los tiene por debajo. El host 12243051 tiene los precios más simétricos sin valores atípicos, mientras que el host 219517861 tiene alojamientos variedad de valores encontrando bastantes alojamientos con valores superiores considerados valores atípicos.
## Análisis de los precios {-}
Si ahora revisamos los precios, vemos que el mayor precio promedio por noche está en la zona de Manhattan para los tres tipos de alojamiento, adicionalmente y como era de esperarse, es más costoso un alojamiento completo, seguido de una habitación privada y por útimo una habitación compartida, aunque no hay una gran diferencia entre los valores promedios de las habitaciones. Este patrón se mantiene igual, independiente del grupo de vecindario al que pertenezca.
```{r warning=FALSE}
df_out %>%
group_by(neighbourhood_group, room_type)%>%
summarise(m = mean(price)) -> group_type
group_type%>%
ggplot(aes(x=room_type, y=m, fill=room_type)) +
geom_bar(stat="identity")+
facet_wrap(~neighbourhood_group)+
xlab("Grupo Vecindario") + ylab("Precio Medio") +
ggtitle("Precio promedio por grupo de vecindario y tipo de alojamiento")+
theme(axis.text.y = element_text(angle = 90))+
theme(axis.text.x=element_blank(), legend.title = element_blank())-> g
ggplotly(g)
```
```{r}
ggplot(data=df_out, mapping = aes(x=neighbourhood_group, y=price)) +
geom_boxplot(aes(color = neighbourhood_group))+
theme(legend.title = element_blank()) -> g
ggplotly(g)
```
El grupo de vecindario con el precio promedio más alto es Manhattan y en este grupo se manejan el mayor rango de precios. Los precios para Queen y Staten Island tienen distribuciones similares.
Si agrupamos por tipo de alojamiento, vemos que los valores por habitación compartida en Staten Island presentan un sesgo positivo. En cuanto a las habitaciones privadas tenemos una alta concentración de valores atípicos. En los alojamientos completos por grupo el precio promedio es menor a 200 y en el caso de Bronx y Staten Island es sesgo es positivo y Manhattan es el que tiene mayor variación de precios en sus alojamientos.
```{r}
ggplot(data=df_out, mapping = aes(x=neighbourhood_group, y=price)) +
geom_boxplot(width=0.8, aes(color = neighbourhood_group))+
facet_wrap(~room_type)+
theme(legend.title = element_blank(), axis.text.y=element_blank())+
coord_flip()-> g
ggplotly(g)
```