W projekcie wykorzystano dane z serwisu otodom.pl. Dane zostały zescrapowane samodzielnie za pomocą autorskiego skryptu w języku Python. Otrzymano zbiór 123 050 linków do ofert, z czego wytypowano 69 636 unikalnych ofert.
Cel projektu: Koncepcja pracy oparta jest o badanie przedstawione w empirical_example na zajęciach. W tym przypadku badane są dane dotyczące mieszkań wystawionych na sprzedaż, aby dowiedzieć się, jakie reguły asocjacyjne występują w ramach zebranych danych.
Wykorzystanie narzędzi AI: W pracy wykorzystano częściowo chat Gemini do wygenerowania fragmentów kodu R (np. przy poprawie dyskretyzacji, przy optymalizacji wielowątkowości skryptu Python oraz przy niektórych wykresach). Narzędzie to posłużyło również do konwersji niniejszego raportu do formatu R Markdown.
library(readxl)
library(dplyr)
library(stringr)
library(arules)
library(arulesViz)
Pobranie danych
df_all <- read_excel("otodom_scraped_data_all.xlsx")
Opis danych: Dane pochodzą z serwisu otodom.pl, zbierane były za pomocą skryptu w python, który w pierwszej kolejności zbierał wszystkie dostępne linki ofert, następnie sprawdzał duplikaty i po usunięciu duplikatów ofert pobierał informacje zawarte w każdej z ofert. Uzyskano 69 636 unikalnych ofert. Są to oferty mieszkań sprzedawanych w całej Polsce. Należy zwrócić uwagę, że najprawdopodobniej pochodzą one z miast co może sugerować liczba mieszkań z centralnym ogrzewaniem - zatem zaznaczyć należy że chociaż zbiór nie jest idealnie reprezentacyjny, to stanowi ciekawą i unikalną bazę do dalszej analizy. Zmienne podane w PLN, oraz metrach kwadratowych.
summary(df_all)
## URL Tytuł_Meta Opis_Meta Tytuł_Ogłoszenia
## Length:69636 Length:69636 Length:69636 Length:69636
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## Cena Cena_Za_Metr Adres Szczegóły_HTML
## Length:69636 Length:69636 Length:69636 Length:69636
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## Powierzchnia Liczba_Pokoi Ogrzewanie Piętro
## Length:69636 Length:69636 Length:69636 Length:69636
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## Czynsz Stan_Wykończenia Rynek Forma_Własności
## Length:69636 Length:69636 Length:69636 Length:69636
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## Dostępne_Od Typ_Ogłoszeniodawcy Informacje_Dodatkowe Rok_Budowy
## Length:69636 Length:69636 Length:69636 Min. : 1
## Class :character Class :character Class :character 1st Qu.: 1976
## Mode :character Mode :character Mode :character Median : 2012
## Mean : 2577
## 3rd Qu.: 2024
## Max. :20252026
##
## Winda Rodzaj_Zabudowy Materiał_Budynku Bezpieczeństwo
## Length:69636 Length:69636 Length:69636 Length:69636
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## Wyposażenie Zabezpieczenia Okna Opis_Mieszkania
## Length:69636 Length:69636 Length:69636 Mode:logical
## Class :character Class :character Class :character NA's:69636
## Mode :character Mode :character Mode :character
##
##
##
##
## CZY_ADRES_ZAWIERA_ULICE CENA POWIERZCHNIA
## Min. :0.0000 Min. : 8500 Min. : 1.00
## 1st Qu.:0.0000 1st Qu.: 430000 1st Qu.: 42.59
## Median :1.0000 Median : 599000 Median : 54.00
## Mean :0.6062 Mean : 736767 Mean : 64.00
## 3rd Qu.:1.0000 3rd Qu.: 824000 3rd Qu.: 68.14
## Max. :1.0000 Max. :25000000 Max. :223831.00
##
## LICZBA_POKOI STAN_DO_REMONTU STAN_DO_ZAMIESZKANIA STAN_DO_WYKONCZENIA
## Min. : 1.000 Min. :0.000 Min. :0.0000 Min. :0
## 1st Qu.: 2.000 1st Qu.:0.000 1st Qu.:0.0000 1st Qu.:0
## Median : 3.000 Median :0.000 Median :1.0000 Median :0
## Mean : 2.664 Mean :0.083 Mean :0.5269 Mean :0
## 3rd Qu.: 3.000 3rd Qu.:0.000 3rd Qu.:1.0000 3rd Qu.:0
## Max. :10.000 Max. :1.000 Max. :1.0000 Max. :0
## NA's :36
## STATUS_INNY CZY_RYNEK_PIERWOTNY OGLOSZENIE_PRYWATNE
## Min. :0.0000 Min. :0.0000 Min. :0.00
## 1st Qu.:0.0000 1st Qu.:0.0000 1st Qu.:0.00
## Median :0.0000 Median :0.0000 Median :0.00
## Mean :0.3901 Mean :0.2668 Mean :0.13
## 3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.:0.00
## Max. :1.0000 Max. :1.0000 Max. :1.00
##
## CZY_NOWOCZESNE_BUDOWNICTWO CZY_INFO_O_CZYNSZ WIELKOSC_CZYNSZU
## Min. :1 Min. :0.0000 Min. : 0.0
## 1st Qu.:1 1st Qu.:0.0000 1st Qu.: 310.0
## Median :1 Median :1.0000 Median : 560.0
## Mean :1 Mean :0.7204 Mean : 664.4
## 3rd Qu.:1 3rd Qu.:1.0000 3rd Qu.: 799.0
## Max. :1 Max. :1.0000 Max. :916500.0
## NA's :13 NA's :19478
## CZY_WINDA CZY_TARAS_LUB_BALKON_LUB_OGRODEK CZY_GARAZ
## Min. :0.0000 Min. :0.0000 Min. :0.0000
## 1st Qu.:0.0000 1st Qu.:0.0000 1st Qu.:0.0000
## Median :0.0000 Median :1.0000 Median :1.0000
## Mean :0.4562 Mean :0.7259 Mean :0.5313
## 3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.:1.0000
## Max. :1.0000 Max. :1.0000 Max. :1.0000
##
## ILE_WYPOSAZENIA ILE_ZABEZPIECZENIA_ORAZ_BEZPIECZENSTWO NUMER_PIETRA
## Min. :0.000 Min. :0.000 Min. :-1.00
## 1st Qu.:0.000 1st Qu.:0.000 1st Qu.: 1.00
## Median :0.000 Median :1.000 Median : 2.00
## Mean :1.814 Mean :1.476 Mean : 2.29
## 3rd Qu.:4.000 3rd Qu.:2.000 3rd Qu.: 3.00
## Max. :8.000 Max. :6.000 Max. :10.00
## NA's :1372
## NumerPietraPomocniczy CZY_OGRZEWANIE_MIEJSKIE Powierzchnia_Pomocnicze
## Length:69636 Min. :0.0000 Min. : 1.00
## Class :character 1st Qu.:0.0000 1st Qu.: 42.59
## Mode :character Median :1.0000 Median : 54.00
## Mean :0.5999 Mean : 64.00
## 3rd Qu.:1.0000 3rd Qu.: 68.14
## Max. :1.0000 Max. :223831.00
##
colSums(is.na(df_all))
## URL Tytuł_Meta
## 0 0
## Opis_Meta Tytuł_Ogłoszenia
## 0 6
## Cena Cena_Za_Metr
## 0 0
## Adres Szczegóły_HTML
## 77 0
## Powierzchnia Liczba_Pokoi
## 0 0
## Ogrzewanie Piętro
## 0 0
## Czynsz Stan_Wykończenia
## 0 0
## Rynek Forma_Własności
## 0 0
## Dostępne_Od Typ_Ogłoszeniodawcy
## 0 0
## Informacje_Dodatkowe Rok_Budowy
## 0 0
## Winda Rodzaj_Zabudowy
## 0 8982
## Materiał_Budynku Bezpieczeństwo
## 27752 43963
## Wyposażenie Zabezpieczenia
## 36322 23811
## Okna Opis_Mieszkania
## 24776 69636
## CZY_ADRES_ZAWIERA_ULICE CENA
## 0 0
## POWIERZCHNIA LICZBA_POKOI
## 0 36
## STAN_DO_REMONTU STAN_DO_ZAMIESZKANIA
## 0 0
## STAN_DO_WYKONCZENIA STATUS_INNY
## 0 0
## CZY_RYNEK_PIERWOTNY OGLOSZENIE_PRYWATNE
## 0 0
## CZY_NOWOCZESNE_BUDOWNICTWO CZY_INFO_O_CZYNSZ
## 0 13
## WIELKOSC_CZYNSZU CZY_WINDA
## 19478 0
## CZY_TARAS_LUB_BALKON_LUB_OGRODEK CZY_GARAZ
## 0 0
## ILE_WYPOSAZENIA ILE_ZABEZPIECZENIA_ORAZ_BEZPIECZENSTWO
## 0 0
## NUMER_PIETRA NumerPietraPomocniczy
## 1372 1348
## CZY_OGRZEWANIE_MIEJSKIE Powierzchnia_Pomocnicze
## 0 0
Wybranie interesujących danych do dalszej analizy
selected_cols <- c("CENA","POWIERZCHNIA","LICZBA_POKOI","NUMER_PIETRA","WIELKOSC_CZYNSZU","Rynek","Rok_Budowy","Ogrzewanie","Winda","Stan_Wykończenia","Wyposażenie","Informacje_Dodatkowe")
df_selected <- df_all %>% select(all_of(selected_cols))
colSums(is.na(df_selected))
## CENA POWIERZCHNIA LICZBA_POKOI
## 0 0 36
## NUMER_PIETRA WIELKOSC_CZYNSZU Rynek
## 1372 19478 0
## Rok_Budowy Ogrzewanie Winda
## 0 0 0
## Stan_Wykończenia Wyposażenie Informacje_Dodatkowe
## 0 36322 0
Usunięcie obserwacji z brakującymi danymi kluczowymi (cena, powierzchnia, liczba pokoi, nr. piętra) + WIELKOSC_CZYNSZU około 19 tys. braków, dlatego tworzę dodatkową kategorię brak informacji którą uzupełnię N/A. + dla WYPOSAŻENIE brak danych nie jest problemem, oznacza brak wyposażenia.
#df_clean <- df_selected %>% filter(!is.na(CENA), !is.na(POWIERZCHNIA)) #nie potrzeba, juz oczyszczone.
df_clean <- df_selected[!is.na(df_selected$LICZBA_POKOI), ]
df_clean <- df_clean[!is.na(df_clean$NUMER_PIETRA), ]
colSums(is.na(df_clean))
## CENA POWIERZCHNIA LICZBA_POKOI
## 0 0 0
## NUMER_PIETRA WIELKOSC_CZYNSZU Rynek
## 0 18500 0
## Rok_Budowy Ogrzewanie Winda
## 0 0 0
## Stan_Wykończenia Wyposażenie Informacje_Dodatkowe
## 0 35272 0
Dla zmiennych CENA etc. na kategorie -> podział na Niski/Sredni/Wysoki (tercyle), jeśli N/A (np. w WIELKOSC_CZYNSZU) wstawi “Brak danych”
discretization <- function(vector, variable_name){
#jeśli puste to brak danych
if(all(is.na(vector))){
return(rep(paste0(variable_name,"=Brak_danych"),length(vector)))
}
#jeśli dane to podział na tercyle i przypisanie kateogrii
q <- quantile(vector,probs=c(0,0.33,0.66,1),na.rm=TRUE)
cat <- cut(vector,breaks=q,labels=c("Niski","Sredni","Wysoki"),include.lowest=TRUE)
#jeśli braki
cat_txt <- as.character(cat)
cat_txt[is.na(cat_txt)]<-"Brak_danych"
#nazwa zmiennej dla czytelności
return(paste0(variable_name,"=",cat_txt))
}
#Wykonanie dyskretyzacji
df_clean$Cena_Kat <- discretization(df_clean$CENA,"Cena")
df_clean$Powierzchnia_Kat <- discretization(df_clean$POWIERZCHNIA,"Powierzchnia")
df_clean$Czynsz_Kat <- discretization(df_clean$WIELKOSC_CZYNSZU,"Czynsz")
df_clean$LiczbaPokoi_Kat <- discretization(df_clean$LICZBA_POKOI,"LiczbaPokoi")
if(is.numeric(df_clean$NUMER_PIETRA)){
df_clean$Pietro_Kat <- discretization(df_clean$NUMER_PIETRA,"Pietro")
}
if(is.numeric(df_clean$Rok_Budowy)){
df_clean$Rok_Kat <- discretization(df_clean$Rok_Budowy,"Rok")
}
Należy dokonać rozbicia koszyków zmiennych wyposażenia i informacje dodatkowe
split_text_col <- function(data,col_name){
#zaciągnięcie całego ciągu
v_text <- as.character(data[[col_name]])
v_text[is.na(v_text)] <- ""#aby być pewnym, że NA pusty text (brak)
characteristics_all <- unlist(strsplit(v_text,split=","))
characteristics_all <- str_trim(characteristics_all)
unique_characteristics <- unique(characteristics_all)
unique_characteristics <- unique_characteristics[unique_characteristics != ""]#ponownie usunięcie pustych
for(char in unique_characteristics){
#tutaj tworze kolumny z uwzględnieneim nazwy
clean_name <- gsub(" ","_",char)
new_name <- paste0(col_name,"=",clean_name)
data[[new_name]] <- grepl(char,v_text,fixed=TRUE)
}
return(data)
}
# Wykonanie rozbicia cech
df_clean <- split_text_col(df_clean, "Wyposażenie")
df_clean <- split_text_col(df_clean, "Informacje_Dodatkowe")
colSums(is.na(df_clean))
## CENA
## 0
## POWIERZCHNIA
## 0
## LICZBA_POKOI
## 0
## NUMER_PIETRA
## 0
## WIELKOSC_CZYNSZU
## 18500
## Rynek
## 0
## Rok_Budowy
## 0
## Ogrzewanie
## 0
## Winda
## 0
## Stan_Wykończenia
## 0
## Wyposażenie
## 35272
## Informacje_Dodatkowe
## 0
## Cena_Kat
## 0
## Powierzchnia_Kat
## 0
## Czynsz_Kat
## 0
## LiczbaPokoi_Kat
## 0
## Pietro_Kat
## 0
## Rok_Kat
## 0
## Wyposażenie=meble
## 0
## Wyposażenie=pralka
## 0
## Wyposażenie=zmywarka
## 0
## Wyposażenie=lodówka
## 0
## Wyposażenie=kuchenka
## 0
## Wyposażenie=piekarnik
## 0
## Wyposażenie=telewizor
## 0
## Wyposażenie=klimatyzacja
## 0
## Informacje_Dodatkowe=brak_informacji
## 0
## Informacje_Dodatkowe=balkon
## 0
## Informacje_Dodatkowe=piwnica
## 0
## Informacje_Dodatkowe=garaż/miejsce_parkingowe
## 0
## Informacje_Dodatkowe=pom._użytkowe
## 0
## Informacje_Dodatkowe=oddzielna_kuchnia
## 0
## Informacje_Dodatkowe=ogródek
## 0
## Informacje_Dodatkowe=taras
## 0
## Informacje_Dodatkowe=dwupoziomowe
## 0
W efekcie mamy 68 236 obserwacji z 35 kolumnami cech w df_clean, ale nalezy z niego usunąć te, których teraz nie potrzebuję w dalszej analizie. To będzie ostatni krok oczyszczania danych przed przejsciem do analizy
col_to_delete <- c("CENA","POWIERZCHNIA","LICZBA_POKOI","NUMER_PIETRA","WIELKOSC_CZYNSZU","Rok_Budowy","Wyposażenie","Informacje_Dodatkowe")
df_final <- df_clean %>% select(-any_of(col_to_delete)) %>% mutate(across(where(is.character),as.factor))
str(df_final)
## tibble [68,236 × 27] (S3: tbl_df/tbl/data.frame)
## $ Rynek : Factor w/ 2 levels "pierwotny","wtórny": 2 2 2 2 1 2 2 2 2 2 ...
## $ Ogrzewanie : Factor w/ 7 levels "brak informacji",..: 1 6 6 2 6 6 3 1 6 6 ...
## $ Winda : Factor w/ 2 levels "nie","tak": 2 1 2 1 2 1 1 1 2 2 ...
## $ Stan_Wykończenia : Factor w/ 4 levels "brak informacji",..: 4 4 4 4 3 4 4 4 4 4 ...
## $ Cena_Kat : Factor w/ 3 levels "Cena=Niski","Cena=Sredni",..: 2 3 1 3 1 1 1 3 3 3 ...
## $ Powierzchnia_Kat : Factor w/ 3 levels "Powierzchnia=Niski",..: 1 2 2 2 1 3 3 3 1 3 ...
## $ Czynsz_Kat : Factor w/ 4 levels "Czynsz=Brak_danych",..: 2 4 2 2 2 4 2 4 1 3 ...
## $ LiczbaPokoi_Kat : Factor w/ 3 levels "LiczbaPokoi=Niski",..: 1 1 1 2 1 2 1 3 1 2 ...
## $ Pietro_Kat : Factor w/ 3 levels "Pietro=Niski",..: 3 1 2 2 1 3 1 2 2 1 ...
## $ Rok_Kat : Factor w/ 3 levels "Rok=Niski","Rok=Sredni",..: 2 1 2 1 3 2 1 1 3 2 ...
## $ Wyposażenie=meble : logi [1:68236] TRUE FALSE TRUE TRUE FALSE TRUE ...
## $ Wyposażenie=pralka : logi [1:68236] FALSE FALSE TRUE FALSE FALSE FALSE ...
## $ Wyposażenie=zmywarka : logi [1:68236] FALSE FALSE TRUE FALSE FALSE FALSE ...
## $ Wyposażenie=lodówka : logi [1:68236] FALSE FALSE TRUE FALSE FALSE TRUE ...
## $ Wyposażenie=kuchenka : logi [1:68236] FALSE FALSE TRUE FALSE FALSE TRUE ...
## $ Wyposażenie=piekarnik : logi [1:68236] FALSE FALSE TRUE FALSE FALSE TRUE ...
## $ Wyposażenie=telewizor : logi [1:68236] FALSE FALSE TRUE FALSE FALSE FALSE ...
## $ Wyposażenie=klimatyzacja : logi [1:68236] FALSE FALSE FALSE FALSE FALSE FALSE ...
## $ Informacje_Dodatkowe=brak_informacji : logi [1:68236] TRUE FALSE FALSE FALSE FALSE FALSE ...
## $ Informacje_Dodatkowe=balkon : logi [1:68236] FALSE TRUE TRUE TRUE TRUE TRUE ...
## $ Informacje_Dodatkowe=piwnica : logi [1:68236] FALSE TRUE TRUE TRUE FALSE TRUE ...
## $ Informacje_Dodatkowe=garaż/miejsce_parkingowe: logi [1:68236] FALSE FALSE TRUE FALSE TRUE FALSE ...
## $ Informacje_Dodatkowe=pom._użytkowe : logi [1:68236] FALSE FALSE TRUE FALSE FALSE FALSE ...
## $ Informacje_Dodatkowe=oddzielna_kuchnia : logi [1:68236] FALSE FALSE TRUE FALSE FALSE TRUE ...
## $ Informacje_Dodatkowe=ogródek : logi [1:68236] FALSE FALSE FALSE FALSE FALSE FALSE ...
## $ Informacje_Dodatkowe=taras : logi [1:68236] FALSE FALSE FALSE FALSE FALSE FALSE ...
## $ Informacje_Dodatkowe=dwupoziomowe : logi [1:68236] FALSE FALSE FALSE FALSE FALSE FALSE ...
names(df_final)
## [1] "Rynek"
## [2] "Ogrzewanie"
## [3] "Winda"
## [4] "Stan_Wykończenia"
## [5] "Cena_Kat"
## [6] "Powierzchnia_Kat"
## [7] "Czynsz_Kat"
## [8] "LiczbaPokoi_Kat"
## [9] "Pietro_Kat"
## [10] "Rok_Kat"
## [11] "Wyposażenie=meble"
## [12] "Wyposażenie=pralka"
## [13] "Wyposażenie=zmywarka"
## [14] "Wyposażenie=lodówka"
## [15] "Wyposażenie=kuchenka"
## [16] "Wyposażenie=piekarnik"
## [17] "Wyposażenie=telewizor"
## [18] "Wyposażenie=klimatyzacja"
## [19] "Informacje_Dodatkowe=brak_informacji"
## [20] "Informacje_Dodatkowe=balkon"
## [21] "Informacje_Dodatkowe=piwnica"
## [22] "Informacje_Dodatkowe=garaż/miejsce_parkingowe"
## [23] "Informacje_Dodatkowe=pom._użytkowe"
## [24] "Informacje_Dodatkowe=oddzielna_kuchnia"
## [25] "Informacje_Dodatkowe=ogródek"
## [26] "Informacje_Dodatkowe=taras"
## [27] "Informacje_Dodatkowe=dwupoziomowe"
glimpse(df_final)
## Rows: 68,236
## Columns: 27
## $ Rynek <fct> wtórny, wtórny, wtórny…
## $ Ogrzewanie <fct> brak informacji, miejs…
## $ Winda <fct> tak, nie, tak, nie, ta…
## $ Stan_Wykończenia <fct> do zamieszkania, do za…
## $ Cena_Kat <fct> Cena=Sredni, Cena=Wyso…
## $ Powierzchnia_Kat <fct> Powierzchnia=Niski, Po…
## $ Czynsz_Kat <fct> Czynsz=Niski, Czynsz=W…
## $ LiczbaPokoi_Kat <fct> LiczbaPokoi=Niski, Lic…
## $ Pietro_Kat <fct> Pietro=Wysoki, Pietro=…
## $ Rok_Kat <fct> Rok=Sredni, Rok=Niski,…
## $ `Wyposażenie=meble` <lgl> TRUE, FALSE, TRUE, TRU…
## $ `Wyposażenie=pralka` <lgl> FALSE, FALSE, TRUE, FA…
## $ `Wyposażenie=zmywarka` <lgl> FALSE, FALSE, TRUE, FA…
## $ `Wyposażenie=lodówka` <lgl> FALSE, FALSE, TRUE, FA…
## $ `Wyposażenie=kuchenka` <lgl> FALSE, FALSE, TRUE, FA…
## $ `Wyposażenie=piekarnik` <lgl> FALSE, FALSE, TRUE, FA…
## $ `Wyposażenie=telewizor` <lgl> FALSE, FALSE, TRUE, FA…
## $ `Wyposażenie=klimatyzacja` <lgl> FALSE, FALSE, FALSE, F…
## $ `Informacje_Dodatkowe=brak_informacji` <lgl> TRUE, FALSE, FALSE, FA…
## $ `Informacje_Dodatkowe=balkon` <lgl> FALSE, TRUE, TRUE, TRU…
## $ `Informacje_Dodatkowe=piwnica` <lgl> FALSE, TRUE, TRUE, TRU…
## $ `Informacje_Dodatkowe=garaż/miejsce_parkingowe` <lgl> FALSE, FALSE, TRUE, FA…
## $ `Informacje_Dodatkowe=pom._użytkowe` <lgl> FALSE, FALSE, TRUE, FA…
## $ `Informacje_Dodatkowe=oddzielna_kuchnia` <lgl> FALSE, FALSE, TRUE, FA…
## $ `Informacje_Dodatkowe=ogródek` <lgl> FALSE, FALSE, FALSE, F…
## $ `Informacje_Dodatkowe=taras` <lgl> FALSE, FALSE, FALSE, F…
## $ `Informacje_Dodatkowe=dwupoziomowe` <lgl> FALSE, FALSE, FALSE, F…
trans <- as(df_final, "transactions")#niezbędne dla apriori
inspect(head(trans, 3))
## items transactionID
## [1] {Rynek=wtórny,
## Ogrzewanie=brak informacji,
## Winda=tak,
## Stan_Wykończenia=do zamieszkania,
## Cena_Kat=Cena=Sredni,
## Powierzchnia_Kat=Powierzchnia=Niski,
## Czynsz_Kat=Czynsz=Niski,
## LiczbaPokoi_Kat=LiczbaPokoi=Niski,
## Pietro_Kat=Pietro=Wysoki,
## Rok_Kat=Rok=Sredni,
## Wyposażenie=meble,
## Informacje_Dodatkowe=brak_informacji} 1
## [2] {Rynek=wtórny,
## Ogrzewanie=miejskie,
## Winda=nie,
## Stan_Wykończenia=do zamieszkania,
## Cena_Kat=Cena=Wysoki,
## Powierzchnia_Kat=Powierzchnia=Sredni,
## Czynsz_Kat=Czynsz=Wysoki,
## LiczbaPokoi_Kat=LiczbaPokoi=Niski,
## Pietro_Kat=Pietro=Niski,
## Rok_Kat=Rok=Niski,
## Informacje_Dodatkowe=balkon,
## Informacje_Dodatkowe=piwnica} 2
## [3] {Rynek=wtórny,
## Ogrzewanie=miejskie,
## Winda=tak,
## Stan_Wykończenia=do zamieszkania,
## Cena_Kat=Cena=Niski,
## Powierzchnia_Kat=Powierzchnia=Sredni,
## Czynsz_Kat=Czynsz=Niski,
## LiczbaPokoi_Kat=LiczbaPokoi=Niski,
## Pietro_Kat=Pietro=Sredni,
## Rok_Kat=Rok=Sredni,
## Wyposażenie=meble,
## Wyposażenie=pralka,
## Wyposażenie=zmywarka,
## Wyposażenie=lodówka,
## Wyposażenie=kuchenka,
## Wyposażenie=piekarnik,
## Wyposażenie=telewizor,
## Informacje_Dodatkowe=balkon,
## Informacje_Dodatkowe=piwnica,
## Informacje_Dodatkowe=garaż/miejsce_parkingowe,
## Informacje_Dodatkowe=pom._użytkowe,
## Informacje_Dodatkowe=oddzielna_kuchnia} 3
# LHS (antecedent) = items on the left-hand side of the rule (IF)
# RHS (consequent) = items on the right-hand side of the rule (THEN)
Wygenerowanie reguł dla ceny = wysoka Czyli odpowiadamy na pytanie: Jakie cechy są typowe dla mieszkań które są drogie?
rules_high_price <- apriori(trans,
parameter = list(supp=0.1,conf=0.5),
appearance = list(default = "lhs",rhs="Cena_Kat=Cena=Wysoki"),#tutaj okazało się, że wczesniej dokleiłem nazwę klumny do kategorii, ale na szczęście jedyne co pozostaje zrobic to podać właściwą nazwę kateogrii
control = list(verbose = FALSE))
summary(rules_high_price)
## set of 46 rules
##
## rule length distribution (lhs + rhs):sizes
## 2 3 4 5
## 4 20 21 1
##
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 2.000 3.000 3.000 3.413 4.000 5.000
##
## summary of quality measures:
## support confidence coverage lift
## Min. :0.1005 Min. :0.5006 Min. :0.1360 Min. :1.472
## 1st Qu.:0.1041 1st Qu.:0.5168 1st Qu.:0.1798 1st Qu.:1.520
## Median :0.1106 Median :0.5459 Median :0.2075 Median :1.606
## Mean :0.1176 Mean :0.5750 Mean :0.2074 Mean :1.691
## 3rd Qu.:0.1220 3rd Qu.:0.6281 3rd Qu.:0.2194 3rd Qu.:1.847
## Max. :0.2047 Max. :0.7982 Max. :0.3400 Max. :2.348
## count
## Min. : 6857
## 1st Qu.: 7103
## Median : 7544
## Mean : 8025
## 3rd Qu.: 8328
## Max. :13970
##
## mining info:
## data ntransactions support confidence
## trans 68236 0.1 0.5
## call
## apriori(data = trans, parameter = list(supp = 0.1, conf = 0.5), appearance = list(default = "lhs", rhs = "Cena_Kat=Cena=Wysoki"), control = list(verbose = FALSE))
# Wyfiltrowanie interesujących (najlepszych) reguł wg. lift.
rules_high_best <- head(sort(rules_high_price,by ="lift"),20)
inspect(sort(rules_high_best,by="lift")[1:10])
## lhs rhs support confidence coverage lift count
## [1] {Winda=tak,
## Powierzchnia_Kat=Powierzchnia=Wysoki} => {Cena_Kat=Cena=Wysoki} 0.1085497 0.7981681 0.1359986 2.347578 7407
## [2] {Rynek=wtórny,
## Powierzchnia_Kat=Powierzchnia=Wysoki,
## Informacje_Dodatkowe=garaż/miejsce_parkingowe} => {Cena_Kat=Cena=Wysoki} 0.1055455 0.7308707 0.1444106 2.149642 7202
## [3] {Powierzchnia_Kat=Powierzchnia=Wysoki,
## Rok_Kat=Rok=Sredni} => {Cena_Kat=Cena=Wysoki} 0.1004895 0.6948723 0.1446157 2.043763 6857
## [4] {Powierzchnia_Kat=Powierzchnia=Wysoki,
## LiczbaPokoi_Kat=LiczbaPokoi=Wysoki} => {Cena_Kat=Cena=Wysoki} 0.1036110 0.6844806 0.1513717 2.013199 7070
## [5] {Powierzchnia_Kat=Powierzchnia=Wysoki,
## Informacje_Dodatkowe=garaż/miejsce_parkingowe} => {Cena_Kat=Cena=Wysoki} 0.1437951 0.6786554 0.2118823 1.996066 9812
## [6] {Ogrzewanie=miejskie,
## Powierzchnia_Kat=Powierzchnia=Wysoki} => {Cena_Kat=Cena=Wysoki} 0.1156721 0.6650097 0.1739404 1.955931 7893
## [7] {LiczbaPokoi_Kat=LiczbaPokoi=Wysoki} => {Cena_Kat=Cena=Wysoki} 0.1065860 0.6527553 0.1632862 1.919889 7273
## [8] {Rynek=wtórny,
## Powierzchnia_Kat=Powierzchnia=Wysoki,
## Informacje_Dodatkowe=balkon} => {Cena_Kat=Cena=Wysoki} 0.1025265 0.6492807 0.1579078 1.909669 6996
## [9] {Rynek=wtórny,
## Stan_Wykończenia=do zamieszkania,
## Powierzchnia_Kat=Powierzchnia=Wysoki} => {Cena_Kat=Cena=Wysoki} 0.1152617 0.6480719 0.1778533 1.906113 7865
## [10] {Stan_Wykończenia=do zamieszkania,
## Powierzchnia_Kat=Powierzchnia=Wysoki} => {Cena_Kat=Cena=Wysoki} 0.1199807 0.6466825 0.1855326 1.902027 8187
# Reprezentacja graficzna
plot(rules_high_price, method = "graph", engine = "htmlwidget")
plot(rules_high_price, method = "graph", control = list(type = "items"))
## Available control parameters (with default values):
## layout = stress
## circular = FALSE
## ggraphdots = NULL
## edges = <environment>
## nodes = <environment>
## nodetext = <environment>
## colors = c("#EE0000FF", "#EEEEEEFF")
## engine = ggplot2
## max = 100
## verbose = FALSE
WNIOSKI Analiza 1: Metraż jest kluczowym determinantem wystąpienia wysokiej ceny mieszkania. Jednak ciekawsze informacje możemy określić jako determinantę “wygody” mieszkania. Reguła 1. określa, że posiadanie windy oraz dużej powierzchni to na 80% trafi do przedziału wysokich cen, a obecność obu czynników zwiększa szansę blisko 2,3 krotnie na wysoką cenę. Analogicznie z garażem, który przy współwystąpieniu z mieszkaniem dużym i z rynku wtórnego będzie zwiększać szansę ponad 2-krotnie na wysoką cenę. Przy confidence wynoszącym 73%. Podobnie wyceniane są mieszkania gotowe do zamieszkania (reguła 9-10), w których pojawia się stan wykończenia jako stan do zamieszkania (lift 1.9 i conf 64%)
Podsumowując są to niektóre z opisanych czynników, które są typowe dla drogich mieszkań.
Porównanie rynku wtórnego i pierowtnego Czyli odpowiadamy na pytania: 1) Jakie cechy są typowe dla mieszkań z rynku wtórnego? 2) Jakie cechy są typowe dla mieszkań z rynku pierwotnego?
rules_secondary <- apriori(trans,
parameter = list(supp = 0.01, conf = 0.5),
appearance = list(default = "lhs", rhs = "Rynek=wtórny"),
control = list(verbose = F))
rules_primary <- apriori(trans,
parameter = list(supp = 0.01, conf = 0.5),
appearance = list(default = "lhs", rhs = "Rynek=pierwotny"),
control = list(verbose = F))
summary(rules_secondary)
## set of 313119 rules
##
## rule length distribution (lhs + rhs):sizes
## 1 2 3 4 5 6 7 8 9 10
## 1 45 812 6529 26189 60554 83870 74137 43623 17359
##
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.000 6.000 7.000 7.247 8.000 10.000
##
## summary of quality measures:
## support confidence coverage lift
## Min. :0.01001 Min. :0.5000 Min. :0.01001 Min. :0.6747
## 1st Qu.:0.01158 1st Qu.:0.9677 1st Qu.:0.01186 1st Qu.:1.3059
## Median :0.01413 Median :0.9860 Median :0.01451 Median :1.3305
## Mean :0.01789 Mean :0.9745 Mean :0.01846 Mean :1.3150
## 3rd Qu.:0.01939 3rd Qu.:0.9933 3rd Qu.:0.01997 3rd Qu.:1.3404
## Max. :0.74106 Max. :1.0000 Max. :1.00000 Max. :1.3494
## count
## Min. : 683
## 1st Qu.: 790
## Median : 964
## Mean : 1221
## 3rd Qu.: 1323
## Max. :50567
##
## mining info:
## data ntransactions support confidence
## trans 68236 0.01 0.5
## call
## apriori(data = trans, parameter = list(supp = 0.01, conf = 0.5), appearance = list(default = "lhs", rhs = "Rynek=wtórny"), control = list(verbose = F))
summary(rules_primary)
## set of 6838 rules
##
## rule length distribution (lhs + rhs):sizes
## 2 3 4 5 6 7 8 9
## 3 97 795 2114 2345 1183 277 24
##
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 2.000 5.000 6.000 5.679 6.000 9.000
##
## summary of quality measures:
## support confidence coverage lift
## Min. :0.01001 Min. :0.5000 Min. :0.01039 Min. :1.931
## 1st Qu.:0.01172 1st Qu.:0.7432 1st Qu.:0.01447 1st Qu.:2.870
## Median :0.01457 Median :0.8384 Median :0.01878 Median :3.238
## Mean :0.01889 Mean :0.8041 Mean :0.02410 Mean :3.106
## 3rd Qu.:0.02063 3rd Qu.:0.8913 3rd Qu.:0.02687 3rd Qu.:3.442
## Max. :0.24893 Max. :0.9760 Max. :0.33318 Max. :3.769
## count
## Min. : 683
## 1st Qu.: 800
## Median : 994
## Mean : 1289
## 3rd Qu.: 1408
## Max. :16986
##
## mining info:
## data ntransactions support confidence
## trans 68236 0.01 0.5
## call
## apriori(data = trans, parameter = list(supp = 0.01, conf = 0.5), appearance = list(default = "lhs", rhs = "Rynek=pierwotny"), control = list(verbose = F))
# Wyfiltrowanie interesujących (najlepszych) reguł wg. lift.
rules_secondary_best <- head(sort(rules_secondary,by ="lift"),20)
inspect(sort(rules_secondary_best,by="lift")[1:10])
## lhs rhs support confidence coverage lift count
## [1] {Ogrzewanie=kotłownia,
## Stan_Wykończenia=do zamieszkania,
## Rok_Kat=Rok=Sredni} => {Rynek=wtórny} 0.01036110 1 0.01036110 1.349418 707
## [2] {Stan_Wykończenia=do remontu,
## Wyposażenie=meble,
## Informacje_Dodatkowe=oddzielna_kuchnia} => {Rynek=wtórny} 0.01238349 1 0.01238349 1.349418 845
## [3] {Stan_Wykończenia=do remontu,
## Rok_Kat=Rok=Niski,
## Wyposażenie=kuchenka} => {Rynek=wtórny} 0.01090334 1 0.01090334 1.349418 744
## [4] {Stan_Wykończenia=do remontu,
## Wyposażenie=kuchenka,
## Wyposażenie=piekarnik} => {Rynek=wtórny} 0.01000938 1 0.01000938 1.349418 683
## [5] {Stan_Wykończenia=do remontu,
## Wyposażenie=piekarnik,
## Informacje_Dodatkowe=piwnica} => {Rynek=wtórny} 0.01163609 1 0.01163609 1.349418 794
## [6] {Stan_Wykończenia=do remontu,
## Wyposażenie=lodówka,
## Informacje_Dodatkowe=piwnica} => {Rynek=wtórny} 0.01188522 1 0.01188522 1.349418 811
## [7] {Ogrzewanie=miejskie,
## Stan_Wykończenia=do remontu,
## Wyposażenie=meble} => {Rynek=wtórny} 0.01481623 1 0.01481623 1.349418 1011
## [8] {Stan_Wykończenia=do remontu,
## Wyposażenie=meble,
## Informacje_Dodatkowe=balkon} => {Rynek=wtórny} 0.01163609 1 0.01163609 1.349418 794
## [9] {Powierzchnia_Kat=Powierzchnia=Niski,
## Czynsz_Kat=Czynsz=Wysoki,
## Rok_Kat=Rok=Niski} => {Rynek=wtórny} 0.01286711 1 0.01286711 1.349418 878
## [10] {Rok_Kat=Rok=Niski,
## Wyposażenie=pralka,
## Informacje_Dodatkowe=garaż/miejsce_parkingowe} => {Rynek=wtórny} 0.01716103 1 0.01716103 1.349418 1171
rules_primary_best <- head(sort(rules_primary,by ="lift"),20)
inspect(sort(rules_primary_best,by="lift")[1:10])
## lhs rhs support confidence coverage lift count
## [1] {Stan_Wykończenia=do wykończenia,
## Cena_Kat=Cena=Niski,
## Powierzchnia_Kat=Powierzchnia=Sredni,
## Czynsz_Kat=Czynsz=Brak_danych,
## LiczbaPokoi_Kat=LiczbaPokoi=Sredni,
## Rok_Kat=Rok=Wysoki} => {Rynek=pierwotny} 0.01014127 0.9760226 0.01039041 3.769306 692
## [2] {Stan_Wykończenia=do wykończenia,
## Cena_Kat=Cena=Niski,
## Powierzchnia_Kat=Powierzchnia=Sredni,
## LiczbaPokoi_Kat=LiczbaPokoi=Sredni,
## Rok_Kat=Rok=Wysoki,
## Informacje_Dodatkowe=garaż/miejsce_parkingowe} => {Rynek=pierwotny} 0.01160678 0.9717791 0.01194384 3.752919 792
## [3] {Stan_Wykończenia=do wykończenia,
## Cena_Kat=Cena=Niski,
## Czynsz_Kat=Czynsz=Brak_danych,
## LiczbaPokoi_Kat=LiczbaPokoi=Sredni,
## Rok_Kat=Rok=Wysoki} => {Rynek=pierwotny} 0.01320417 0.9709052 0.01359986 3.749544 901
## [4] {Stan_Wykończenia=do wykończenia,
## Czynsz_Kat=Czynsz=Brak_danych,
## LiczbaPokoi_Kat=LiczbaPokoi=Sredni,
## Pietro_Kat=Pietro=Niski,
## Rok_Kat=Rok=Wysoki,
## Informacje_Dodatkowe=ogródek} => {Rynek=pierwotny} 0.01085937 0.9673629 0.01122575 3.735864 741
## [5] {Stan_Wykończenia=do wykończenia,
## Czynsz_Kat=Czynsz=Brak_danych,
## LiczbaPokoi_Kat=LiczbaPokoi=Sredni,
## Rok_Kat=Rok=Wysoki,
## Informacje_Dodatkowe=ogródek} => {Rynek=pierwotny} 0.01138695 0.9664179 0.01178264 3.732214 777
## [6] {Stan_Wykończenia=do wykończenia,
## Cena_Kat=Cena=Niski,
## Czynsz_Kat=Czynsz=Brak_danych,
## Pietro_Kat=Pietro=Niski,
## Rok_Kat=Rok=Wysoki,
## Informacje_Dodatkowe=balkon} => {Rynek=pierwotny} 0.01383434 0.9652352 0.01433261 3.727647 944
## [7] {Cena_Kat=Cena=Niski,
## Czynsz_Kat=Czynsz=Brak_danych,
## LiczbaPokoi_Kat=LiczbaPokoi=Sredni,
## Rok_Kat=Rok=Wysoki,
## Informacje_Dodatkowe=garaż/miejsce_parkingowe} => {Rynek=pierwotny} 0.01134299 0.9650873 0.01175333 3.727075 774
## [8] {Winda=tak,
## Stan_Wykończenia=do wykończenia,
## Cena_Kat=Cena=Niski,
## Czynsz_Kat=Czynsz=Brak_danych,
## Pietro_Kat=Pietro=Niski,
## Rok_Kat=Rok=Wysoki} => {Rynek=pierwotny} 0.01311624 0.9644397 0.01359986 3.724574 895
## [9] {Stan_Wykończenia=do wykończenia,
## Cena_Kat=Cena=Niski,
## LiczbaPokoi_Kat=LiczbaPokoi=Sredni,
## Rok_Kat=Rok=Wysoki,
## Informacje_Dodatkowe=garaż/miejsce_parkingowe} => {Rynek=pierwotny} 0.01478692 0.9637058 0.01534381 3.721740 1009
## [10] {Winda=tak,
## Stan_Wykończenia=do wykończenia,
## Cena_Kat=Cena=Niski,
## Czynsz_Kat=Czynsz=Brak_danych,
## Rok_Kat=Rok=Wysoki,
## Informacje_Dodatkowe=balkon,
## Informacje_Dodatkowe=garaż/miejsce_parkingowe} => {Rynek=pierwotny} 0.01184126 0.9630513 0.01229556 3.719212 808
# Reprezentacja graficzna
plot(rules_secondary_best, method = "graph", engine = "htmlwidget")
plot(rules_primary_best, method = "graph", engine = "htmlwidget")
# plot(head(sort(rules_secondary, by="lift"), 10), method="graph", engine="default", main="Rynek Wtórny")
# plot(head(sort(rules_primary, by="lift"), 10), method="graph", engine="default", main="Rynek Pierwotny")
WNIOSKI Analiza 2:
Rynek wtórny: Dla wszystkich 10 reguł confidence wynosi 1. Najwyższy lift wynosi 1,35 dla stanu w którym występuje równocześnie kotłownia, stan do zamieszkania i średni rok budowy. Pojawiają się elementy które były dość przewidywalne takie jak niski rok budowy (dawno), oraz większa liczba wyposażenia (meble, pralka etc.)
Rynek pierwotny: Wartość lift wynosi dla 10 najważniejszych reguł około 3,7-3,8 co oznacza, że wystąpienie wymienionych cech zwiększa szanse na to, że ofera jest deweloperska. (!) Ważne: Kiedy na wcześniejszym etapie nie podjąłem decyzji o usunięciu obserwacji nie posiadających informację dotyczących braków w wysokości czynszu, była to dobra decyzja. Brak informacji efektywnie przekazuje nam całkiem dużo informacji. Mogę domniemywać, że brak czynszu był pochodną np. nieustalenia go jeszcze przez dewelopera. Gdybym usunął te obserwacje straciłbym wartościowe informacje. Ciekawy wniosek nasuwa się także z połączenia informacji o niskim piętrze, wysokim roku (współcześnie budowane), posiadaniu ogródka, braku czynszu oraz stanu do wykończenia. W nowoczesych blokach zdaje się, że częściej buduje się je z ogródkami na parterach (dla przykładu bloki z wielkiej płyty z lat 70-80’ raczej ich nie posiadają) - chociaż ten ostatni aspekt nie jest tutaj przebadany, to jest ciekawym punktem wyjścia do dalszej analizy. Oczywistym predyktorem był stan do wykończenia bo właśnie takim cechują sie mieszkania deweloperskie.
W przedstawionej pracy zastosowałem wykorzystanie reguł asocjacyjnych, w rzeczywistych warunkach rynkowych. Kluczową wartością dodaną w przedstawionym projekcie jest oryginalny zbiór danych, pozyskany samodzielnie za pomoca scrappignu danych z internetowego serwisu otodom.pl W ramach projektu przeprowadziłem kilkuetapowe przygotowanie danych, zanim poddałem je dalszej analizie. W szczególności nalezy wymienić: 1) wstepne oczyszczenie danych w excel, 2) obsługe braków, 3) dyskretyzację zmiennych ciągłych na podstawie wyznaczonych tercyli 4) rozbicie zmienych zawierających listy wyposażenia oraz informacji dodatkowych, które zamieniłem na zmienne binarne. Wnioski konkretnych analiz rynku zostaly przedstawione powyżej.
Możliwości rozwinięcia badania na przyszłość: badanie większej ilości cech w tym tekstu ogłoszeń. Oraz wpływu lokalizacji. Ten ostatni punkt nie został wykonany z powodu ograniczonego dostepu do API Google, którym testowałem jakość położenia mieszkania wg. znajdujących się w jego pobliżu udogodnień (parki, szkoły, sklepy). Niestety nie mogłem dokonać tej analizy dla wszystkich recordów a jedynie kilkuset. W przyszłości ten parametr mógłby być ciekawym rozwinięciem analizy.