library(readr)
library(e1071)
library(ggplot2)
library(dplyr)
library(plotly)
library(stringr)
library(scales)
df = read_csv("SPb_dwellings_for_rent_EMLS_sample_1.csv", locale = locale(encoding = "WINDOWS-1251"))
numeric_df = df %>% select_if(is.numeric)
nominal_df = df %>% select_if(is.character)
Создадим функцию для генерации таблицы с описательными статистиками для количественных переменных.
descriptive = function(data){data.frame(sapply(data, function(x) c(
"Min" = round(min(x, na.rm = TRUE),1),
"Max" = round(max(x, na.rm = TRUE),1),
"First quartile" = round(quantile(x,0.15, na.rm = TRUE),1),
"Mean"= round(mean(x,na.rm=TRUE),1),
"Median" = round(median(x, na.rm = TRUE),1),
"Mode" = round(median(x, na.rm = TRUE),1),
"Standard deviation" = round(sd(x, na.rm = TRUE),1),
"Range" = round(max(x, na.rm = TRUE)-min(x, na.rm = TRUE),1),
"Skewness" = round(skewness(x, na.rm = TRUE),1),
"Kurtosis" = round(kurtosis(x, na.rm = TRUE),1) ))) }
Таблица с описательными статистиками количественных данных до очистки выборки от ошибок.
knitr::kable(descriptive(numeric_df))
| Dist_metro_ad | Price | Area_total | Area_living | Area_kitchen | Floor | Latitude | Longitude | Year_construction | |
|---|---|---|---|---|---|---|---|---|---|
| Min | 0.0 | 16.0 | 15.0 | 2.0 | 1.0 | 1.0 | 59.5 | 30.0 | 1832.0 |
| Max | 44100.0 | 900000.0 | 45000.0 | 332.0 | 200.0 | 26.0 | 60.1 | 30.7 | 2017.0 |
| First quartile.15% | 90.0 | 20000.0 | 35.0 | 18.0 | 7.0 | 2.0 | 59.9 | 30.2 | 1981.2 |
| Mean | 1402.8 | 45516.2 | 85.6 | 36.8 | 13.9 | 6.6 | 59.9 | 30.3 | 2001.2 |
| Median | 770.0 | 30000.0 | 50.0 | 30.0 | 10.0 | 5.0 | 59.9 | 30.3 | 2011.0 |
| Mode | 770.0 | 30000.0 | 50.0 | 30.0 | 10.0 | 5.0 | 59.9 | 30.3 | 2011.0 |
| Standard deviation | 2622.3 | 48956.2 | 947.3 | 26.1 | 12.3 | 4.9 | 0.1 | 0.1 | 26.5 |
| Range | 44100.0 | 899984.0 | 44985.0 | 330.0 | 199.0 | 25.0 | 0.6 | 0.7 | 185.0 |
| Skewness | 6.7 | 5.9 | 42.5 | 2.9 | 4.9 | 1.3 | -0.2 | 0.1 | -3.5 |
| Kurtosis | 65.0 | 61.1 | 1896.7 | 15.2 | 39.2 | 1.4 | -0.1 | 0.3 | 15.2 |
Удалим наблюдения, соторые содержат выбросы. Рассмотрим некоторые переменные и найдем ошибки.
df_outliers = df %>% subset(Dist_metro_ad >= 44100 | Price <= 30 | Area_total >= 1000 | Area_total <= 9 |
Area_kitchen >= 150 | Rooms == 47 | Minimum_duration == c('11000 mes.','2000 mes.') |
Refurbished == c('Sovmeshchennaya', 'Dush'))
knitr::kable(df_outliers[,c(6,7,8,9,12,14,20)])
| Dist_metro_ad | Rooms | Price | Minimum_duration | Area_total | Area_kitchen | Refurbished |
|---|---|---|---|---|---|---|
| 200 | 1 | 25000 | 2000 mes. | 50 | NA | NA |
| 1230 | 1 | 16 | 11 mes. | 33 | 6.0 | Proizveden |
| 44100 | 1 | 10000 | 10 mes. | 31 | 5.5 | NA |
| 2250 | 2 | 2500 | NA | NA | 30.4 | Sovmeshchennaya |
| 800 | 1 | 30000 | 6 mes. | NA | 18.0 | Sovmeshchennaya |
| 2520 | 1 (studiya) | 17000 | 11 mes. | NA | NA | Sovmeshchennaya |
| 710 | 47 | 23000 | 11 mes. | 37 | NA | NA |
| 810 | 2 | 23000 | 12 mes. | 23000 | 7.0 | Proizveden |
| 480 | 5 | 500000 | NA | NA | 200.0 | NA |
| 2980 | 1 | 28000 | 6 mes. | NA | 18.0 | Sovmeshchennaya |
| 1450 | 1 | 20 | 11 mes. | 44 | 13.0 | Evrostandart |
| 3950 | 2 | 28000 | 14 mes. | NA | 33.0 | Sovmeshchennaya |
| 3490 | 2 | 35000 | 11 mes. | NA | 31.0 | Sovmeshchennaya |
| 920 | 1 | 20000 | 14 mes. | NA | 18.0 | Sovmeshchennaya |
| 4920 | 1 | 22000 | 11000 mes. | NA | NA | Ne trebuetsya |
| 770 | 1 | 25000 | 12 mes. | NA | 17.0 | Sovmeshchennaya |
| 10 | 1 | 19000 | 12 mes. | NA | 20.0 | Sovmeshchennaya |
| 60 | 2 | 45000 | 11 mes. | 45000 | NA | Proizveden |
| 590 | 2 | 25 | 6 mes. | 50 | 6.0 | Proizveden |
| 2470 | 1 | 35000 | 1 mes. | NA | 18.0 | Sovmeshchennaya |
| 370 | 2 | 30000 | 11 mes. | NA | 33.0 | Dush |
| 3160 | 2 | 30 | 11 mes. | 62 | 10.0 | Evrostandart |
| 1550 | 1 | 19000 | 11 mes. | NA | 18.0 | Sovmeshchennaya |
В таблице выше можно видеть наблюдения, в которых содержатся ошибки. Исключим их из нашей выборки и построим таблицы с описательными статистиками снова.
clear_numeric_df = df %>%
anti_join(df_outliers) %>%
select_if(is.numeric)
clear_nominal_df = df %>%
anti_join(df_outliers) %>%
select_if(is.character)
knitr::kable(descriptive(clear_numeric_df))
| Dist_metro_ad | Price | Area_total | Area_living | Area_kitchen | Floor | Latitude | Longitude | Year_construction | |
|---|---|---|---|---|---|---|---|---|---|
| Min | 0.0 | 1500.0 | 15.0 | 2.0 | 1.0 | 1.0 | 59.5 | 30.0 | 1832.0 |
| Max | 37630.0 | 900000.0 | 600.0 | 251.0 | 126.0 | 26.0 | 60.1 | 30.7 | 2017.0 |
| First quartile.15% | 90.0 | 20000.0 | 35.0 | 18.0 | 7.0 | 2.0 | 59.9 | 30.2 | 1981.1 |
| Mean | 1386.5 | 45553.3 | 61.8 | 36.7 | 13.8 | 6.6 | 59.9 | 30.3 | 2001.1 |
| Median | 770.0 | 30000.0 | 50.0 | 30.0 | 10.0 | 5.0 | 59.9 | 30.3 | 2011.0 |
| Mode | 770.0 | 30000.0 | 50.0 | 30.0 | 10.0 | 5.0 | 59.9 | 30.3 | 2011.0 |
| Standard deviation | 2507.5 | 48371.2 | 39.0 | 25.6 | 11.8 | 4.9 | 0.1 | 0.1 | 26.5 |
| Range | 37630.0 | 898500.0 | 585.0 | 249.0 | 125.0 | 25.0 | 0.6 | 0.7 | 185.0 |
| Skewness | 6.1 | 5.9 | 3.9 | 2.6 | 4.1 | 1.3 | -0.2 | 0.1 | -3.5 |
| Kurtosis | 50.1 | 62.1 | 30.1 | 10.8 | 24.0 | 1.4 | -0.4 | 0.3 | 15.2 |
Создадим функцию для генерации таблиц с частотой значений номинальных переменных.
freq_tab = function(var, name, rows = 5){
freq = data.frame( sort( table( var ), decreasing =T ) ) %>% head(rows)
names(freq) = c(name,'Count')
knitr::kable(freq)}
Построим таблицу показывающую частоту различных значений для ближайших станций метро. Покажем наиболее часто встречающиеся.
freq_tab(var = clear_nominal_df$Metro, name = 'Metro')
| Metro | Count |
|---|---|
| Primorskaya | 152 |
| Komendantskii pr. | 127 |
| Moskovskaya | 113 |
| Chernyshevskaya | 111 |
| Leninskii pr. | 102 |
Построим таблицу показывающую частоту различных значений для районов. Покажем наиболее часто встречающиеся.
freq_tab(var = clear_nominal_df$District_ad, name = 'District')
| District | Count |
|---|---|
| Центральный | 375 |
| Московский | 342 |
| Приморский | 335 |
| Петроградский | 275 |
| Выборгский | 260 |
Построим таблицу показывающую частоту различных значений для количества комнат. Покажем наиболее часто встречающиеся.
freq_tab(var = clear_nominal_df$Rooms, name = 'Rooms')
| Rooms | Count |
|---|---|
| 1 | 1170 |
| 2 | 988 |
| 3 | 521 |
| 1 (studiya) | 114 |
| 4 | 100 |
Построим таблицу показывающую частоту различных значений для типа здания. Покажем наиболее часто встречающиеся.
freq_tab(var = clear_nominal_df$Building, name = 'Building')
| Building | Count |
|---|---|
| Kirpichnyi | 983 |
| Kirp.Monolit | 589 |
| Monolit | 335 |
| Panel’nyi | 183 |
| Blochnyi | 124 |
Построим таблицу показывающую частоту различных значений для типа балкона. Покажем наиболее часто встречающиеся.
freq_tab(var = clear_nominal_df$Balcony, name = 'Balcony')
| Balcony | Count |
|---|---|
| Balkon | 775 |
| Lodjiya | 349 |
| Net | 267 |
| Zasteklennaya lodjiya | 240 |
| est’ | 124 |
Построим таблицу показывающую частоту различных значений для количества этажей в доме. Покажем наиболее часто встречающиеся. Можно видеть что пяти- и девятиэтажки наиболее распространены.
freq_tab(var = clear_nominal_df$NFloor, name = 'NFloor')
| NFloor | Count |
|---|---|
| 5 | 463 |
| 9 | 380 |
| 12 | 222 |
| 16 | 200 |
| 6 | 186 |
Создадим функции плотности для генерации диаграмм распределения значений количественных переменных.
density_plot = function(var, title='title', name='name',
begin=min(var, na.rm = T), end=max(var, na.rm = T),
bars=100){ggplot(data=clear_numeric_df, aes(var)) +
geom_histogram(aes(y =..density..),
breaks=seq(begin, end, by=bars),
col="black",
fill="black") + #geom_density(col='black') +
labs(title=title, x=name, y="Count") + theme_minimal()}
density_plot(clear_numeric_df$Dist_metro_ad, bars=350, title = 'Distance to metro station', name = 'Distance')
length(which( clear_numeric_df$Dist_metro_ad < 6000 ))/nrow(df)
## [1] 0.9353333
Можно видеть, что 94% квартир находятся ближе чем 6 км от метро.
density_plot(clear_numeric_df$Price, bars = 5000, title = 'Mounthly rent price', name = 'Price')
На графике изображен общий вид гистограммы распределения цены аренды квартиры. Давайте посмотрим поближе на кваритры с ценой аренды меньше 100к.
density_plot(clear_numeric_df$Price, bars = 1000, end = 100000, title = 'Mounthly rent price', name = 'Price')
sh_mult5 = length(which( (df$Price/5000)%%1 == 0 ) ) / nrow(clear_numeric_df) # доля квартир с ценой кратной 5000
sh_less30_mult5 = length(which( (df$Price/5000)%%1 != 0 & df$Price < 30000) ) / nrow(clear_numeric_df) # доля квартир с ценой менее 30к при это мне кратной 5000
sh_less30_in_not_mult5 = length(which( (df$Price/5000)%%1 != 0 & df$Price < 30000) ) /
length(which( (df$Price/5000)%%1 != 0) ) # доля квартир с ценой менее 30к среди квартир с ценой не кратной 5000
sh_not_mult5_in_less30 = length(which( (df$Price/5000)%%1 != 0 & df$Price < 30000) )/length(which( df$Price < 30000 ) ) # доля квартир с ценой не кратной 5000 среди квартир цена которых меньше 30к
sh_not_mult5_in_more30 = length(which( (df$Price/5000)%%1 != 0 & df$Price > 30000) )/length(which( df$Price > 30000 ) ) # доля квартир с ценой не кратной 5000 среди квартир цена которых больше 30к
## Доля квартир с ценой кратной 5000: 0.62
## Доля квартир с ценой менее 30к при это мне кратной 5000: 0.31
## Доля квартир с ценой менее 30к среди квартир с ценой не кратной 5000: 0.78
## Доля квартир с ценой не кратной 5000 среди квартир цена которых меньше 30к: 0.64
## Доля квартир с ценой не кратной 5000 среди квартир цена которых больше 30к: 0.17
На диаграмме видно что очень часто значения цены кратны числу 5000 – более 61% всех квартир имеют именно такую “круглую” цену. Доля квартир с ценой менее 30к среди тех, чья цена не кратна 5000 составляет 78%. Таким образом, люди чаще указывают “круглую” цену, однако если цена аренды невелика, то арендодатель с большей вероятностью укажет цену не кратную 5000 – для квартир с ценой менее и более 30к доля квартир с ценой не кратной 5000 составляет 64% и 17% соответственно.
density_plot(clear_numeric_df$Area_total, bars = 10, title = 'Total area', name = 'Total area')
length(which( clear_numeric_df$Area_total < 100 ))/
nrow(clear_numeric_df[complete.cases(clear_numeric_df[ , 3]),])
## [1] 0.8778249
88% всех квартир имеют общую площадь меньше 100 кв.м.
density_plot(clear_numeric_df$Area_living, bars = 3, title = 'Living area', name = 'Living area')
На диаграмме явно видно что однокомнатный квартиры и студии с маленькой жилой площадью наиболее распространены. Еще одна мода - двухкомнатный кваритры, который также сдают в наем, но не так часто как однокомнатные.
density_plot(clear_numeric_df$Area_kitchen, bars=2.5, title = 'Kitchen area', name = 'Kitchen area')
length(which( clear_numeric_df$Area_kitchen <= 20 ))/
nrow(clear_numeric_df[complete.cases(clear_numeric_df[ , 5]),])
## [1] 0.8829787
Как правило площадь кухни в сдаваемых в наем кваритрах небольшая – в 88% кваритир площадь кухни не больше 20 кв.м.
density_plot(clear_numeric_df$Year_construction, bars = 1)
Можно заметить что чаще всего сдают квартиры в новостройках.
Создадим функции генерации столбчатых диаграмм для описания категориальных переменных.
bar_plot = function(var, title, name, width = 1){step_df = data.frame(table(var))
names(step_df) = c('x', 'count')
ggplot(data=step_df, aes(x=x, y=count)) +
geom_bar(stat="identity", fill="black", width = width)+
geom_text(aes(label=count), vjust=-0.05, color="black", size=3)+
labs(title=title, x=name, y="Count") + theme_minimal()}
bar_plot_ordered = function(var, title, name, width = 1){ggplot(var, aes(x = Var1, y = Freq)) +
geom_bar(stat="identity", color='black',fill='black', width = width) +
geom_text(aes(label=Freq), vjust=-0.05, color="black", size=3)+
labs(title=title, x=name, y="Count")}
Регион
bar_plot(clear_nominal_df$Region, title = 'Region', name = 'Region', width = 0.5)
На диаграмме видно, что подавляющее большинство квартир сдаются в Санкт-Петербурге.
Ремонт
ref = data.frame( sort( table( clear_nominal_df$Refurbished ), decreasing =T ) )
bar_plot_ordered(ref, "Refurbished", 'Refurbished', width = 0.95)
Минимальный срок аренды
dur = data.frame( sort( table( clear_nominal_df$Minimum_duration ), decreasing =T ) )
bar_plot_ordered(dur, "Minimum duration", 'Minimum duration', width = 0.95)
Три наиболее распространенных значения - на год (11 или 12 месяцев) или на полгода.
Наличие мебели
fur = data.frame( sort( table( clear_nominal_df$Furnished ), decreasing =T ) )
bar_plot_ordered(fur, "Furnished", 'Furnished', width = 0.8)
Большинство сдаваемых в наем кваритр меблированы.
Наличие лифта
lifty = data.frame( sort( table( clear_nominal_df$Lift ), decreasing =T ) )
bar_plot_ordered(lifty, "Lift", 'Lift', width = 0.4)
Более чем в половине домов, в которых сдают в наем квартиры, есть лифт.
Напишем функцию для рисовки графика рассеяния между ценой квартир и остальными переменными.
plot_scatter_price = function(column, description){
ggplot() +
geom_point(aes(x=column, y=clear_numeric_df$Price)) +
labs(title = paste("The dependence of housing prices and",str_to_lower(description))) +
xlab(description) +
ylab("Price") +
scale_y_continuous(labels = label_number(scale = 1/1000, suffix = "K")) +
theme_minimal()
}
Далее, смотрим, что у нас получилось.
Расстояние до ближайшего метро
plot_scatter_price(clear_numeric_df$Dist_metro_ad , "Distance to the nearest metro station\n(in meters)")
cor(clear_numeric_df$Price, clear_numeric_df$Dist_metro_ad, use = "complete.obs")
## [1] -0.06729301
В данном случае у нас наблюдается нелинейная зависимость. Можно точно сказать, что чем дальше квартира от метро, тем она дешевле. Если же кваритра находится рядом с метро то она может быть как и дешевой, так и дорогой. Коэффициент корреляции равен -0.06, что указывает на то, что взаимосвязи нет. Однако корреляция показывает только линейную зависимость.
Общая площадь квартиры
plot_scatter_price(clear_numeric_df$Area_total, "Total area")
cor(clear_numeric_df$Price, clear_numeric_df$Area_total, use = "complete.obs")
## [1] 0.7968676
Как видно из полученного графика, у нас есть положительная зависимость, то есть чем больше общая площадь квартиры, тем дороже сама квартира. Корреляция равна 0.80, что доказывает вышесказанное.
Площадь гостинной комнаты
plot_scatter_price(clear_numeric_df$Area_living, "Living area (m^2)")
cor(clear_numeric_df$Price, clear_numeric_df$Area_living, use = "complete.obs")
## [1] 0.7219513
Аналогично общей площади квартиры, цена квартиры положительно зависит от площади гостинной. Коэффициент корреляции равено 0.73, то есть чем больше гостинная, тем дороже квартира.
Площадь кухни
plot_scatter_price(clear_numeric_df$Area_kitchen, "Kitchen area (m^2)")
cor(clear_numeric_df$Price, clear_numeric_df$Area_kitchen, use = "complete.obs")
## [1] 0.4652686
Сложно сказать о какой-либо зависимости, но небольшой положительный тренд наблюдается. Коэффициент корреляции, равный 0.50, подтверждает о неявной положительной зависимости.
Этаж
ggplot() +
geom_point(aes(x=df$Floor, y=df$Price, color = df$Lift)) +
labs(title = paste("The dependence of housing prices and",str_to_lower("Floor")),
color = "Lift") +
xlab("Floor") +
ylab("Price") +
scale_y_continuous(labels = label_number(scale = 1/1000, suffix = "K")) +
theme_minimal()
cor(clear_numeric_df$Price, clear_numeric_df$Floor, use = "complete.obs")
## [1] -0.1183932
В нашей выборке получилось так, что чем выше расположена квартира, тем дешевле она стоит, даже несмотря на то, что в большинстве случаев в доме присутствует лифт. Касательно нижних этажов, цена квартиры ранжируется от дешевых до дорогих. Однако коэффициент корреляции равен -0.11, ввиду нелинейной зависимости.
Год постройки
plot_scatter_price(clear_numeric_df$Year_construction, "Year construction")
cor(clear_numeric_df$Price, clear_numeric_df$Year_construction, use = "complete.obs")
## [1] 0.01073571
Очевидно, что квартиры в старых домах будут дешевыми. Чем позже построен дом, тем размах цены выше, то есть он может быть как и дешевым, так и дорогим, в то время как старые дома строго дешевы по цене. Из-за нелинейности, вновь получаем коэффициент корреляция близкий к 0.
Долгота и широта
plot_scatter_price(clear_numeric_df$Latitude, "Latitude")
cor(clear_numeric_df$Price, clear_numeric_df$Latitude, use = "complete.obs")
## [1] 0.04973503
В середине скопления точек размах цены гораздно высок, чем на краях графика. Можно предположить, что в данной широте расположены квартиры Петербурга, следовательно и цены широко варьируются. Мы можем утверждать о нелинейной независимости, которую, однако, коэффициент корреляции не сможет уловить. К слову, корреляция равна 0.05.
plot_scatter_price(clear_numeric_df$Longitude, "Longitude")
cor(clear_numeric_df$Price, clear_numeric_df$Longitude, use = "complete.obs")
## [1] -0.1050305
Аналогично и для долготы. в середине есть промежуток, который указывает нам на город Петербург (за промежутком, следовательно, расположены области). Вывод такой же как и с широтой. Чем ближе к городу, то есть ближе к определенному промежутку долготы, тем дороже квартиры. Коэффициент корреляции -0.1.
round(cor(clear_numeric_df, use = "pairwise.complete.obs"),2)
## Dist_metro_ad Price Area_total Area_living Area_kitchen Floor
## Dist_metro_ad 1.00 -0.07 -0.04 -0.06 -0.06 0.00
## Price -0.07 1.00 0.80 0.72 0.47 -0.12
## Area_total -0.04 0.80 1.00 0.91 0.59 -0.10
## Area_living -0.06 0.72 0.91 1.00 0.59 -0.12
## Area_kitchen -0.06 0.47 0.59 0.59 1.00 -0.02
## Floor 0.00 -0.12 -0.10 -0.12 -0.02 1.00
## Latitude -0.09 0.05 0.06 0.03 0.03 0.09
## Longitude -0.19 -0.11 -0.08 -0.09 -0.08 -0.03
## Year_construction 0.07 0.01 0.01 -0.03 0.09 0.32
## Latitude Longitude Year_construction
## Dist_metro_ad -0.09 -0.19 0.07
## Price 0.05 -0.11 0.01
## Area_total 0.06 -0.08 0.01
## Area_living 0.03 -0.09 -0.03
## Area_kitchen 0.03 -0.08 0.09
## Floor 0.09 -0.03 0.32
## Latitude 1.00 0.03 0.04
## Longitude 0.03 1.00 -0.06
## Year_construction 0.04 -0.06 1.00
Из полученной матрицы коэффициентов корреляции можно выделить несколько сильно-зависимые переменные: - Area_total и Price (0.8) - Area_living и Price (0.73) - Area_total и Area living (0.91) - Area_living и Area kitchen (0.62)
Можно сделать вывод, что в основном наблюдается положительная линейная попарная зависимость между площадями комнат и квартиры вцелом, а также ценой квартиры.