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)

Можно сделать вывод, что в основном наблюдается положительная линейная попарная зависимость между площадями комнат и квартиры вцелом, а также ценой квартиры.