Wprowadzenie
Cel Raportu
Celem tego raportu jest analiza zbioru danych
apartaments_pl_2024_06.
Aby to osiągnąć, dane zostaną oczyszczone, brakujące wartości
uzupełnione, a następnie przeprowadzona zostanie analiza wraz z
wizualizacją wyników.
Przegląd zbioru danych
Zbiór danych apartaments_pl_2024_06 zawiera
informacje o rynku nieruchomości w największych miastach Polski.
Koncentruje się na cenach nieruchomości, wielkości mieszkań, bliskości
infrastruktury miejskiej oraz roku budowy.
Zmienne w zbiorze danych aparaments_pl_2024_06
Zbiór danych zawiera następujące zmienne:
Zmienne opisujące lokalizację mieszkania:
id – Unikalny identyfikator każdej nieruchomości generowany komputerowo
city – Miasto, w którym znajduje się nieruchomość
latitude – Szerokość geograficzna
longitude – Długość geograficzna
Zmienne opisujące cenę i status własności nieruchomości:
price – Cena nieruchomości
ownership – Status własności nieruchomości
Zmienne opisujące typ i wielkość mieszkania:
type – Typ budynku: blok mieszkalny, apartamentowiec, kamienica
buildingMaterial – Materiał użyty do budowy budynku
condition – Aktualny stan nieruchomości
squareMeters – Całkowita powierzchnia nieruchomości (w metrach kwadratowych)
rooms – Liczba pokoi w nieruchomości
floor – Piętro, na którym znajduje się nieruchomość
floor count – Całkowita liczba pięter w budynku
Zmienne opisujące odległość od kluczowych udogodnień w mieście:
centreDistance – Odległość od centrum miasta
poiCount – Liczba punktów użyteczności publicznej w pobliżu nieruchomości
schoolDistance – Odległość do najbliższej szkoły
clinicDistance – Odległość do najbliższej przychodni medycznej
postOfficeDistance – Odległość do najbliższej poczty
kindergartenDistance – Odległość do najbliższego przedszkola
restaurantDistance – Odległość do najbliższej restauracji
collegeDistance – Odległość do najbliższego college’u/uniwersytetu
pharmacyDistance – Odległość do najbliższej apteki
Zmienne opisujące, czy nieruchomość lub budynek, w którym się znajduje, obejmuje określoną infrastrukturę:
hasParkingSpace – Czy nieruchomość posiada miejsce parkingowe (Tak/Nie)
hasBalcony – Czy nieruchomość posiada balkon (Tak/Nie)
hasElevator – Czy budynek posiada windę (Tak/Nie)
hasSecurity – Czy nieruchomość/budynek posiada zabezpieczenia (Tak/Nie)
hasStorageRoom – Czy nieruchomość posiada pomieszczenie gospodarcze (Tak/Nie)
Czyszczenie i przetwarzanie danych
Installation of needed libraries and data
Visualizing the missing data using missing-data map
# Missing Data Map
vis_miss(apartments)
# UpSet plot for missing data
library(naniar)
gg_miss_upset(apartments, nsets = 3)
Otrzymany wykres pokazuje, że brakujące dane w zbiorze stanowią około 6,3% wszystkich danych. Większość brakujących wartości pochodzi z zmiennej condition, której braki wynoszą 74%, oraz material, gdzie braki sięgają 41%. Znaczące deficyty danych można również zauważyć w zmiennych: type, floor oraz buildYear, które zawierają odpowiednio 20%, 17% i 16% brakujących wartości.
Brakujące dane w zbiorze mogą wynikać z kilku czynników. Wysoki odsetek brakujących wartości w zmiennych condition i buildingMaterial sugeruje, że takie informacje często nie są ujawniane przez właścicieli nieruchomości lub w ogłoszeniach na portalach. Podobnie, luki w zmiennych type, floor i buildYear mogą wynikać z niekompletnych zapisów dotyczących starszych nieruchomości lub niespójności w sposobie raportowania danych w różnych miastach. Braki w floor mogą być również związane z nieruchomościami oznaczonymi jako domy lub mieszkania na parterze, gdzie ta informacja nie jest istotna. W niektórych przypadkach szczegóły dotyczące nieruchomości mogą być celowo pomijane przez sprzedających, aby zwiększyć atrakcyjność ogłoszenia lub z powodu braku dokładnych informacji. Dodatkowo, metody zbierania danych – takie jak web scraping z portali nieruchomości – mogą przyczyniać się do brakujących wartości, jeśli niektóre szczegóły nie są konsekwentnie udostępniane na różnych platformach.
str(apartments)
## spc_tbl_ [21,501 × 28] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ id : chr [1:21501] "811891f98a870dfd6e414374a0a85560" "adaf636d0c44d8d9325bce42403eefee" "9b957bd60885a469c96f17b58a914f4b" "74fef2ff7135bc70797a3fbfd7d44ed6" ...
## $ city : chr [1:21501] "szczecin" "szczecin" "szczecin" "szczecin" ...
## $ type : chr [1:21501] "blockOfFlats" "apartmentBuilding" "apartmentBuilding" "blockOfFlats" ...
## $ squareMeters : num [1:21501] 47 88.2 117 33.3 56 ...
## $ rooms : num [1:21501] 2 3 5 1 3 3 4 1 4 1 ...
## $ floor : num [1:21501] 6 1 4 1 7 4 NA 7 NA 4 ...
## $ floorCount : num [1:21501] 12 2 4 4 7 4 2 11 3 5 ...
## $ buildYear : num [1:21501] 1981 2000 NA 1963 2018 ...
## $ latitude : num [1:21501] 53.4 53.4 53.4 53.4 53.4 ...
## $ longitude : num [1:21501] 14.6 14.5 14.6 14.5 14.6 ...
## $ centreDistance : num [1:21501] 0.79 4.09 2.19 1.93 2.68 3.94 7.95 2.52 1.33 1.69 ...
## $ poiCount : num [1:21501] 67 0 10 39 10 7 14 15 18 25 ...
## $ schoolDistance : num [1:21501] 0.288 0.509 0.216 0.073 0.268 0.139 0.201 0.247 0.311 0.218 ...
## $ clinicDistance : num [1:21501] 0.285 1.039 0.611 0.326 0.771 ...
## $ postOfficeDistance : num [1:21501] 0.268 0.998 0.743 0.284 0.676 0.926 0.315 0.418 0.434 0.121 ...
## $ kindergartenDistance: num [1:21501] 0.245 0.676 0.28 0.089 0.26 0.93 0.25 0.458 0.403 0.102 ...
## $ restaurantDistance : num [1:21501] 0.068 0.661 0.298 0.18 0.322 0.071 0.216 0.141 0.161 0.162 ...
## $ collegeDistance : num [1:21501] 0.593 1.192 1.522 0.041 1.643 ...
## $ pharmacyDistance : num [1:21501] 0.085 0.668 0.229 0.388 0.178 0.304 0.316 0.022 0.26 0.02 ...
## $ ownership : chr [1:21501] "condominium" "condominium" "udział" "cooperative" ...
## $ buildingMaterial : chr [1:21501] "concreteSlab" "brick" "brick" "brick" ...
## $ condition : chr [1:21501] NA "premium" "premium" NA ...
## $ hasParkingSpace : chr [1:21501] "no" "yes" "yes" "yes" ...
## $ hasBalcony : chr [1:21501] "yes" "yes" "yes" "no" ...
## $ hasElevator : chr [1:21501] "yes" "no" "no" "no" ...
## $ hasSecurity : chr [1:21501] "no" "no" "no" "yes" ...
## $ hasStorageRoom : chr [1:21501] "yes" "no" "no" "yes" ...
## $ price : num [1:21501] 449000 950000 1099000 380000 799000 ...
## - attr(*, "spec")=
## .. cols(
## .. id = col_character(),
## .. city = col_character(),
## .. type = col_character(),
## .. squareMeters = col_double(),
## .. rooms = col_double(),
## .. floor = col_double(),
## .. floorCount = col_double(),
## .. buildYear = col_double(),
## .. latitude = col_double(),
## .. longitude = col_double(),
## .. centreDistance = col_double(),
## .. poiCount = col_double(),
## .. schoolDistance = col_double(),
## .. clinicDistance = col_double(),
## .. postOfficeDistance = col_double(),
## .. kindergartenDistance = col_double(),
## .. restaurantDistance = col_double(),
## .. collegeDistance = col_double(),
## .. pharmacyDistance = col_double(),
## .. ownership = col_character(),
## .. buildingMaterial = col_character(),
## .. condition = col_character(),
## .. hasParkingSpace = col_character(),
## .. hasBalcony = col_character(),
## .. hasElevator = col_character(),
## .. hasSecurity = col_character(),
## .. hasStorageRoom = col_character(),
## .. price = col_double()
## .. )
## - attr(*, "problems")=<externalptr>
# Wybór zmiennych numerycznych do analizy
numeric_cols <- apartments %>% select(where(is.numeric))
# Metoda 1: Wykrywanie wartości odstających metodą IQR
find_outliers_iqr <- function(x) {
Q1 <- quantile(x, 0.25, na.rm = TRUE)
Q3 <- quantile(x, 0.75, na.rm = TRUE)
IQR <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
return(which(x < lower_bound | x > upper_bound))
}
outliers_iqr <- lapply(numeric_cols, find_outliers_iqr)
# Wybór tylko zmiennych numerycznych
numeric_cols <- apartments %>% select(where(is.numeric))
# Funkcja do wykrywania wartości odstających metodą IQR
find_outliers_iqr <- function(x) {
Q1 <- quantile(x, 0.25, na.rm = TRUE)
Q3 <- quantile(x, 0.75, na.rm = TRUE)
IQR <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
return(which(x < lower_bound | x > upper_bound)) # Zwraca indeksy wartości odstających
}
# Znalezienie wartości odstających dla wszystkich zmiennych numerycznych
outliers_iqr <- lapply(numeric_cols, find_outliers_iqr)
# Wizualizacja wartości odstających za pomocą boxplotów dla każdej zmiennej numerycznej
par(mfrow = c(3, 3)) # Układ wykresów (3x3)
for (col in colnames(numeric_cols)) {
boxplot(numeric_cols[[col]], main = col, col = "lightblue", border = "red", outline = TRUE)
}
par(mfrow = c(1, 1)) # Powrót do domyślnego układu wykresów
W Wyniku analizy zbioru danych, możemy zobaczyć, że dane
apartments_pl_2024_06 zawierają wiele wartości odstających. Wartości
odstające, możemy zaobserwować dla niemal wszystkich zmiennych.
Szczególnymi przykładami może być zmienne: Price, collegeDistance oraz
Centre Distance.
Zastępowanie wartości odstających medianą
# Lista zmiennych związanych z odległościami (distance)
distance_vars <- c("centreDistance", "schoolDistance", "clinicDistance",
"postOfficeDistance", "kindergartenDistance",
"restaurantDistance", "collegeDistance", "pharmacyDistance")
# Funkcja do zastępowania wartości odstających medianą (dla zmiennych numerycznych poza distance_vars)
replace_outliers_with_median <- function(x) {
Q1 <- quantile(x, 0.25, na.rm = TRUE)
Q3 <- quantile(x, 0.75, na.rm = TRUE)
IQR <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
# Zastąp wartości odstające medianą
x[x < lower_bound | x > upper_bound] <- median(x, na.rm = TRUE)
return(x)
}
# Tworzenie nowego zbioru danych apartments2
apartments2 <- apartments
# Przetwarzanie zmiennych numerycznych z wyłączeniem distance_vars
for (col in names(numeric_cols)) {
if (!(col %in% distance_vars)) {
apartments2[[col]] <- replace_outliers_with_median(apartments[[col]])
}
}
# Sprawdzenie pierwszych kilku wierszy w nowym zbiorze danych
head(apartments2)
## # A tibble: 6 × 28
## id city type squareMeters rooms floor floorCount buildYear latitude
## <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 811891f98a… szcz… bloc… 47 2 6 12 1981 53.4
## 2 adaf636d0c… szcz… apar… 88.2 3 1 2 2000 53.4
## 3 9b957bd608… szcz… apar… 52.8 3 4 4 NA 53.4
## 4 74fef2ff71… szcz… bloc… 33.3 1 1 4 1963 53.4
## 5 77cc78c75b… szcz… bloc… 56 3 7 7 2018 53.4
## 6 7d0c31d540… szcz… bloc… 68.6 3 4 4 1997 53.5
## # ℹ 19 more variables: longitude <dbl>, centreDistance <dbl>, poiCount <dbl>,
## # schoolDistance <dbl>, clinicDistance <dbl>, postOfficeDistance <dbl>,
## # kindergartenDistance <dbl>, restaurantDistance <dbl>,
## # collegeDistance <dbl>, pharmacyDistance <dbl>, ownership <chr>,
## # buildingMaterial <chr>, condition <chr>, hasParkingSpace <chr>,
## # hasBalcony <chr>, hasElevator <chr>, hasSecurity <chr>,
## # hasStorageRoom <chr>, price <dbl>
# Porównanie statystyk przed i po przetwarzaniu
cat("Statystyki dla pierwotnego zbioru danych:\n")
## Statystyki dla pierwotnego zbioru danych:
summary(apartments)
## id city type squareMeters
## Length:21501 Length:21501 Length:21501 Min. : 25.00
## Class :character Class :character Class :character 1st Qu.: 42.69
## Mode :character Mode :character Mode :character Median : 52.81
## Mean : 56.97
## 3rd Qu.: 66.30
## Max. :150.00
##
## rooms floor floorCount buildYear
## Min. :1.000 Min. : 1.000 Min. : 1.000 Min. :1850
## 1st Qu.:2.000 1st Qu.: 2.000 1st Qu.: 3.000 1st Qu.:1969
## Median :3.000 Median : 3.000 Median : 4.000 Median :1993
## Mean :2.623 Mean : 3.405 Mean : 5.475 Mean :1987
## 3rd Qu.:3.000 3rd Qu.: 4.000 3rd Qu.: 7.000 3rd Qu.:2016
## Max. :6.000 Max. :29.000 Max. :29.000 Max. :2024
## NA's :3573 NA's :209 NA's :3380
## latitude longitude centreDistance poiCount
## Min. :49.98 Min. :14.45 Min. : 0.020 Min. : 0.00
## 1st Qu.:51.10 1st Qu.:18.54 1st Qu.: 2.130 1st Qu.: 7.00
## Median :52.19 Median :19.91 Median : 4.130 Median : 14.00
## Mean :51.99 Mean :19.50 Mean : 4.432 Mean : 20.54
## 3rd Qu.:52.38 3rd Qu.:20.99 3rd Qu.: 6.250 3rd Qu.: 24.00
## Max. :54.58 Max. :23.21 Max. :16.480 Max. :212.00
##
## schoolDistance clinicDistance postOfficeDistance kindergartenDistance
## Min. :0.005 Min. :0.0050 Min. :0.002 Min. :0.001
## 1st Qu.:0.178 1st Qu.:0.3610 1st Qu.:0.238 1st Qu.:0.154
## Median :0.289 Median :0.6770 Median :0.392 Median :0.256
## Mean :0.408 Mean :0.9709 Mean :0.510 Mean :0.357
## 3rd Qu.:0.462 3rd Qu.:1.2440 3rd Qu.:0.621 3rd Qu.:0.411
## Max. :4.920 Max. :4.9180 Max. :4.801 Max. :4.767
## NA's :11 NA's :63 NA's :20 NA's :19
## restaurantDistance collegeDistance pharmacyDistance ownership
## Min. :0.0010 Min. :0.006 Min. :0.0010 Length:21501
## 1st Qu.:0.1120 1st Qu.:0.565 1st Qu.:0.1360 Class :character
## Median :0.2240 Median :1.121 Median :0.2350 Mode :character
## Mean :0.3358 Mean :1.441 Mean :0.3471
## 3rd Qu.:0.4020 3rd Qu.:2.050 3rd Qu.:0.4030
## Max. :4.9280 Max. :5.000 Max. :4.8020
## NA's :31 NA's :584 NA's :30
## buildingMaterial condition hasParkingSpace hasBalcony
## Length:21501 Length:21501 Length:21501 Length:21501
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## hasElevator hasSecurity hasStorageRoom price
## Length:21501 Length:21501 Length:21501 Min. : 191000
## Class :character Class :character Class :character 1st Qu.: 549000
## Mode :character Mode :character Mode :character Median : 721824
## Mean : 823868
## 3rd Qu.: 965000
## Max. :3000000
##
cat("\nStatystyki dla przetworzonego zbioru danych (apartments2):\n")
##
## Statystyki dla przetworzonego zbioru danych (apartments2):
summary(apartments2)
## id city type squareMeters
## Length:21501 Length:21501 Length:21501 Min. : 25.00
## Class :character Class :character Class :character 1st Qu.: 42.69
## Mode :character Mode :character Mode :character Median : 52.81
## Mean : 54.25
## 3rd Qu.: 63.70
## Max. :101.71
##
## rooms floor floorCount buildYear
## Min. :1.000 Min. :1.000 Min. : 1.000 Min. :1899
## 1st Qu.:2.000 1st Qu.:2.000 1st Qu.: 3.000 1st Qu.:1970
## Median :3.000 Median :3.000 Median : 4.000 Median :1993
## Mean :2.557 Mean :2.827 Mean : 5.084 Mean :1988
## 3rd Qu.:3.000 3rd Qu.:4.000 3rd Qu.: 6.000 3rd Qu.:2016
## Max. :4.000 Max. :7.000 Max. :13.000 Max. :2024
## NA's :3573 NA's :209 NA's :3380
## latitude longitude centreDistance poiCount
## Min. :49.98 Min. :16.82 Min. : 0.020 Min. : 0.00
## 1st Qu.:51.10 1st Qu.:18.58 1st Qu.: 2.130 1st Qu.: 7.00
## Median :52.19 Median :19.91 Median : 4.130 Median :14.00
## Mean :51.70 Mean :19.66 Mean : 4.432 Mean :14.65
## 3rd Qu.:52.24 3rd Qu.:20.99 3rd Qu.: 6.250 3rd Qu.:20.00
## Max. :53.50 Max. :23.21 Max. :16.480 Max. :49.00
##
## schoolDistance clinicDistance postOfficeDistance kindergartenDistance
## Min. :0.005 Min. :0.0050 Min. :0.002 Min. :0.001
## 1st Qu.:0.178 1st Qu.:0.3610 1st Qu.:0.238 1st Qu.:0.154
## Median :0.289 Median :0.6770 Median :0.392 Median :0.256
## Mean :0.408 Mean :0.9709 Mean :0.510 Mean :0.357
## 3rd Qu.:0.462 3rd Qu.:1.2440 3rd Qu.:0.621 3rd Qu.:0.411
## Max. :4.920 Max. :4.9180 Max. :4.801 Max. :4.767
## NA's :11 NA's :63 NA's :20 NA's :19
## restaurantDistance collegeDistance pharmacyDistance ownership
## Min. :0.0010 Min. :0.006 Min. :0.0010 Length:21501
## 1st Qu.:0.1120 1st Qu.:0.565 1st Qu.:0.1360 Class :character
## Median :0.2240 Median :1.121 Median :0.2350 Mode :character
## Mean :0.3358 Mean :1.441 Mean :0.3471
## 3rd Qu.:0.4020 3rd Qu.:2.050 3rd Qu.:0.4030
## Max. :4.9280 Max. :5.000 Max. :4.8020
## NA's :31 NA's :584 NA's :30
## buildingMaterial condition hasParkingSpace hasBalcony
## Length:21501 Length:21501 Length:21501 Length:21501
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## hasElevator hasSecurity hasStorageRoom price
## Length:21501 Length:21501 Length:21501 Min. : 191000
## Class :character Class :character Class :character 1st Qu.: 549000
## Mode :character Mode :character Mode :character Median : 721824
## Mean : 741078
## 3rd Qu.: 888000
## Max. :1589000
##
Wypełnianie brakujących danych używając funkcji hotdeck
#Używanie funkcji hotdeck do wypełnienia brakujących danych
czyste <- hotdeck(apartments2)
n_miss(czyste) # Count of NA values: 0
## [1] 0
n_complete(czyste) # Count of complete values: 1204056
## [1] 1204056
pct_miss(czyste) # Percentage of NA values: [1] 0
## [1] 0
##Rdzeń analizy
Kluczową zmienną do analizy w zbiorze danych jest cena, ponieważ opisuje ona sytuację na polskim rynku mieszkaniowym, na którym oparty jest ten zbiór danych.
descriptive_stats <- czyste %>%
select(price) %>%
summarise(
Mean = format(mean(price, na.rm = TRUE), big.mark = ".", scientific = FALSE, digits = 0),
Median = format(median(price, na.rm = TRUE), big.mark = ".", scientific = FALSE, digits = 0),
Std_Dev = format(sd(price, na.rm = TRUE), big.mark = ".", scientific = FALSE, digits = 0),
Min = format(min(price, na.rm = TRUE), big.mark = ".", scientific = FALSE, digits = 0),
Max = format(max(price, na.rm = TRUE), big.mark = ".", scientific = FALSE, digits = 0),
)
# Print the results
print(descriptive_stats)
## N Mean SD Min Q1 Median Q3 Max
## 1 price 21501 741077.8 280184.9 191000 549000 721824 888000 1589000
skew_value <- skewness(czyste$price, na.rm = TRUE)
kurt_value <- kurtosis(czyste$price, na.rm = TRUE)
print(paste("Skewness: ", skew_value))
## [1] "Skewness: 0.69144483660002"
print(paste("Kurtosis: ", kurt_value))
## [1] "Kurtosis: 3.26861387871498"
Po odrzuceniu danych odstających otrzymany rozkład dla zmiennej price jest względnie równy. Skośność na poziomie 0,69 wskazuje na rozkład lekko prawostronnie skośny tzw. “Dłuższy ogon po prawej stronie”.
install.packages("psych")
library(psych)
raport <-
list("Cena w PLN" =
list("Min"= ~ min(price),
"Max"= ~ max(price),
"Kwartyl dolny"= ~ quantile(price,0.25),
"Mediana"= ~ round(median(price),0),
"Kwartyl górny"= ~ quantile(price,0.75),
"Średnia"= ~ round(mean(price),0),
"Odch. std."= ~ round(sd(price),0),
"Odstęp Międzykwartyllowy"= ~ round(iqr(price),0),
"Odchylenie ćwiartkowe"=~round(iqr(price)/2,0),
"Odch. std. w %"=~round((sd(price)/mean(price)),4),
"Odch. ćwiartkowe w %"=~round((iqr(price)/median(price)),4),
"Skośność"=~round(skew(price),4),
"Kurtoza"=~round(kurtosi(price),4)
))
tabela<-summary_table(czyste, summaries = raport, by = c("rooms"))
knitr::kable(tabela,
digits = 2,
align = "lcccc",
caption="Tabela cen mieszkań w Polskich miastach - ceny w PLN wg liczby pokoi.",
col.names = c("Statystyka","1 pokój", "2 pokoje", "3 pokoje", "4 pokoje"))
| Statystyka | 1 pokój | 2 pokoje | 3 pokoje | 4 pokoje |
|---|---|---|---|---|
| Cena w PLN | ||||
| Min | 191000 | 195000 | 249000 | 299000 |
| Max | 970000 | 1589000 | 1589000 | 1588000 |
| Kwartyl dolny | 399000 | 499000 | 639000 | 721824 |
| Mediana | 517000 | 649000 | 750000 | 799000 |
| Kwartyl górny | 599250 | 799000 | 965000 | 1099000 |
| Średnia | 504602 | 666988 | 814180 | 898836 |
| Odch. std. | 160881 | 249595 | 277110 | 283880 |
| Odstęp Międzykwartyllowy | 200250 | 3e+05 | 326000 | 377176 |
| Odchylenie ćwiartkowe | 100125 | 150000 | 163000 | 188588 |
| Odch. std. w % | 0.3188 | 0.3742 | 0.3404 | 0.3158 |
| Odch. ćwiartkowe w % | 0.3873 | 0.4622 | 0.4347 | 0.4721 |
| Skośność | 0.0428 | 0.7286 | 0.6305 | 0.6241 |
| Kurtoza | -0.3184 | 0.6812 | -0.0309 | -0.4714 |
Min i Max
Najtańsze mieszkanie w oczyszczonej serii danych kosztuje 191.000 PLN najdroższe zaś 1.588.000. Najtańsze mieszkania dla wszystkich liczb pokoi kosztują poniżej 300.000 PLN. W przypadku najdroższych mieszkań jedynie w przypadku mieszkań 1 pokojowych cenna jest mniejsza od 1.000.000 PLN. Największa różnica pomiędzy najdroższym a najtańszym mieszkaniem występuję w przypadku mieszkań 2 pokojowych i wynosi 1.394.000 PLN jednak różnica dla mieszkań 3 i 4 pokojowych nie dużo mniejsza i wynosi odpowiednio 1.340.000 PLN oraz 1.289.000 PLN. Najmniejsza zaś różnica jest w przypadku mieszkań 1 pokojowych i wynosi 779.000 PLN. Jednak są to wartości nominalne nie uwzględniające tego jak duże są pokoje. Poniższa tabela przedstawia metraż jaki mają badane przez nas skrajne obserwacje wraz z ceną za m^2.
skrajne_mieszkania <- czyste %>%
group_by(rooms) %>%
filter(price == min(price) | price == max(price)) %>%
group_by(rooms, price) %>%
filter(ifelse(price == min(price), squareMeters == min(squareMeters), squareMeters == max(squareMeters))) %>%
ungroup() %>%
mutate(price_per_m2 = round(price / squareMeters, 2)) %>%
select(rooms, price, squareMeters, price_per_m2) %>%
arrange(rooms, price)
knitr::kable(skrajne_mieszkania,
digits = 2,
align = "lccc",
caption = "Tabela skrajnych mieszkań - ceny, metraż i koszt za m²",
col.names = c("Liczba pokoi", "Cena (PLN)", "Powierzchnia (m²)", "Cena za m² (PLN)"))
| Liczba pokoi | Cena (PLN) | Powierzchnia (m²) | Cena za m² (PLN) |
|---|---|---|---|
| 1 | 191000 | 26.10 | 7318.01 |
| 1 | 970000 | 34.00 | 28529.41 |
| 2 | 195000 | 25.00 | 7800.00 |
| 2 | 1589000 | 59.00 | 26932.20 |
| 3 | 249000 | 46.00 | 5413.04 |
| 3 | 1589000 | 52.81 | 30089.00 |
| 4 | 299000 | 56.33 | 5308.01 |
| 4 | 1588000 | 64.00 | 24812.50 |
Mimo że największa dysproporcja w cenie była obserwowalna dla mieszkań z dwoma pokojami to największa różnica w cenie za m^2 jest widoczna w przypadku mieszkań z 3 pokojami i wynosi 24675,96 PLN dla wartości skrajnych, gdzie pozycją o drugiej największej różnicy są miezkania z 1 pokojem 21211,40 PLN
Średnia i Mediana
Średnia cena mieszkań dla mieszkań o 2; 3 i 4 pokojach jest większa od mediany co sugeruje rozkład cen skośny prawostornnie oznaczao to, że mieszkania drogie znacznie wpływają na średnią.
Na Potrzeby dalszej analizy dodajmy zmienną priceSquareMeter oznaczającą cenę za m^2 dla danego mieszkania.
czyste <- czyste %>%
mutate(priceSquareMeter = round(price / squareMeters, 2))
Aby przeanalizować stan polskiego rynku mieszkaniowego, apartamenty zostały pogrupowane w przedziały cenowe:
limits <- c(0, 250000, 500000, 750000, 1000000, 1250000, Inf)
labels <- c("<250,000 PLN", "250,000 - 499,999 PLN", "500,000 - 749,999 PLN",
"750,000 - 999,999 PLN", "1,000,000 - 1,249,999 PLN", "≥1,250,000 PLN")
czyste$price_category <- cut(
czyste$price,
breaks = limits,
labels = labels,
include.lowest = TRUE,
right = FALSE
)
for (i in 1:length(labels)) {
czyste[paste0("in_range_", i)] <- ifelse(czyste$price_category == labels[i], 1, 0)
}
price_counts <- czyste %>%
count(price_category) %>%
mutate(percentage = n / sum(n) * 100)
fixed_label_height <- max(price_counts$n) * 1.05 # Slightly above max count for visibility
ggplot(price_counts, aes(x = price_category, y = n, fill = price_category)) +
geom_bar(stat = "identity", show.legend = FALSE) +
geom_text(aes(label = paste0(round(percentage, 1), "%")),
vjust = 2,
size = 5,
color = "black",
nudge_y = fixed_label_height - max(price_counts$n)) +
scale_y_continuous(labels = scales::comma) +
theme_minimal() +
labs(
title = "Rozkład cen apartamentów",
x = "Przedział cenowy",
y = "Liczba apartamentów"
) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Wizualizacja pokazuje, w jakich przedziałach cenowych znajdują się apartamenty w zbiorze danych. Największy klaster apartamentów znajduje się w przedziale cenowym od 500 000 do 749 999 PLN, który obejmuje 40,2% obserwacji. Kolejne dwie kategorie z największą liczbą obserwacji to 750 000 do 999 999 PLN (24,7%) oraz 250 000 do 499 999 PLN (18,5%). Łącznie stanowią one 83,4% obserwacji.
Orginalne dane zawierały skrajne ceny mieszkań, poniższa tabela przedstawia dystrybucję orginalnych danych dla przyjętych pułapów cenowych.
limits <- c(0, 250000, 500000, 750000, 1000000, 1250000, Inf)
apartments$price_category <- cut(apartments$price, breaks = limits, labels = labels, include.lowest = TRUE, right = FALSE)
price_counts <- apartments %>%
count(price_category) %>%
mutate(percentage = round(n / sum(n) * 100, 1))
knitr::kable(price_counts,
digits = 1,
align = "lc",
caption = "Tabela rozkładu cen mieszkań przed odrzuceniem wartości skrajnych",
col.names = c("Przedział cenowy", "Liczba mieszkań", "Procentowy udział (%)"))
| Przedział cenowy | Liczba mieszkań | Procentowy udział (%) |
|---|---|---|
| <250,000 PLN | 233 | 1.1 |
| 250,000 - 499,999 PLN | 3968 | 18.5 |
| 500,000 - 749,999 PLN | 7295 | 33.9 |
| 750,000 - 999,999 PLN | 5308 | 24.7 |
| 1,000,000 - 1,249,999 PLN | 1864 | 8.7 |
| ≥1,250,000 PLN | 2833 | 13.2 |
Po odrzuceniu wartości odstających mieszkania o cenie wyższej niż 1.250.000 PLN stanowią 6,9% badanych mieszkań jednak uwzględniając wartości odstające mieszkania o cenie wyższej niż 1.250.000 PLN stanowiły 13,2%
ggplot(czyste, aes(x = city, y = priceSquareMeter)) +
geom_boxplot(aes(fill = city), alpha = 0.85) +
theme_minimal() +
labs(title = "Rozkład cen według miasta", x = NULL, y = "Cena za metr kwadratowy") +
theme(axis.text.x = element_blank(), axis.ticks.x = element_blank()) +
scale_y_continuous(labels = scales::label_number(big.mark = ".", decimal.mark = ","))
ggplot(czyste, aes(x = city, y = squareMeters)) +
geom_boxplot(aes(fill = city), alpha = 0.85) +
theme_minimal() +
labs(title = "Rozkład metrażu mieszkań według miasta", x = NULL, y = "Metraż") +
theme(axis.text.x = element_blank(), axis.ticks.x = element_blank()) +
scale_y_continuous(labels = scales::label_number(big.mark = ".", decimal.mark = ","))
# Zlicz wystąpienia i oblicz procentowy udział dla zmiennej 'ownership'
ownership_counts <- czyste %>%
count(ownership) %>%
mutate(percentage = n / sum(n) * 100) # Convert counts to percentages
# Ustal stałą wysokość etykiet (dostosuj w zależności od zbioru danych)
fixed_label_height <- max(ownership_counts$n) * 1.05 # Slightly above max count for visibility
# Wizualizuj rozkład własności ('ownership')
ggplot(ownership_counts, aes(x = ownership, y = n, fill = ownership)) +
geom_bar(stat = "identity", show.legend = FALSE) +
geom_text(aes(label = paste0(round(percentage, 1), "%")),
vjust = 2,
size = 5,
color = "black",
nudge_y = fixed_label_height - max(ownership_counts$n)) + # Fixed height
scale_y_continuous(labels = scales::comma) + # Format Y-axis with thousands separator
theme_minimal() +
labs(
title = "Distribution of Ownership",
x = "Ownership",
y = "Count of Apartments"
) +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) # Rotate labels for readability
89,7% Własność kondominium: Dominacja kondominiów sugeruje, że większość osób w zbiorze danych posiada swoje mieszkania jako indywidualne jednostki z osobistym tytułem własności. Jest to zgodne z bardziej rynkowym modelem mieszkalnictwa, w którym podkreśla się prywatną własność, a właściciele mają pełną kontrolę nad swoimi lokalami.
10,3% Własność spółdzielcza: Mniejszy odsetek osób mieszka w lokalach spółdzielczych, co wskazuje, że choć nadal istotna, ta forma własności jest mniej powszechna. Własność spółdzielcza jest często bardziej regulowana i może ograniczać pewne swobody, takie jak możliwość swobodnej sprzedaży lub wynajmu lokalu.
Blisko 0% Własność udziałowa: Znikoma obecność opcji „udział” sugeruje, że ta forma własności jest rzadka lub może zanikać na polskim rynku mieszkaniowym. Może to wskazywać na odchodzenie od starszych, zbiorowych struktur własnościowych.
# aov and interaction plot
# Using aov to assess interaction effects between variables
aov <- aov(price ~ city * condition, data = czyste)
summary(aov)
## Df Sum Sq Mean Sq F value Pr(>F)
## city 14 5.805e+14 4.146e+13 808.166 <2e-16 ***
## condition 1 4.260e+12 4.260e+12 83.032 <2e-16 ***
## city:condition 14 1.491e+12 1.065e+11 2.076 0.0103 *
## Residuals 21471 1.102e+15 5.131e+10
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Ensure 'city' is treated as a factor (if not already)
czyste$city <- factor(czyste$city)
# Interaction plot
interaction.plot(
czyste$city,
czyste$condition,
czyste$price,
main = "Interaction Plot: City x Condition",
xlab = "City",
ylab = "Average Price",
col = 1:length(unique(czyste$city)), # Dynamic coloring based on number of cities
las = 2, # Rotate labels on x-axis for better readability
cex.axis = 0.7, # Adjust axis label size if needed
cex.lab = 1.2 # Adjust label size if needed
)
Wykres przedstawia średnią cenę mieszkania w każdym mieście w zależności od jego stanu.
Analiza mapy cieplnej
# Compute correlation matrix for selected variables
correlation_matrix <- cor(czyste %>% select(price, poiCount, squareMeters, rooms),
use = "pairwise.complete.obs")
# Plot correlation heatmap for key apartment features
corrplot(correlation_matrix, method = "square",
tl.cex = 0.8, cl.cex = 0.8,
type = "upper", order = "hclust",
addCoef.col = "black", number.cex = 0.7,
title = "Correlation Heatmap for Selected Variables", mar = c(0, 0, 2, 0))
# Define relevant POI variables
poi_vars <- c("schoolDistance", "clinicDistance", "postOfficeDistance",
"kindergartenDistance", "restaurantDistance", "collegeDistance",
"pharmacyDistance", "centreDistance", "price")
# Ensure all selected variables exist in the dataset
existing_poi_vars <- poi_vars[poi_vars %in% colnames(czyste)]
# Compute correlation matrix for POI distances and price
poi_correlation <- cor(czyste %>% select(all_of(existing_poi_vars)),
use = "pairwise.complete.obs")
# Plot POI correlation heatmap
corrplot(poi_correlation, method = "square",
tl.cex = 0.8, cl.cex = 0.8,
type = "upper", order = "hclust",
addCoef.col = "black", number.cex = 0.7,
title = "POI Distance Correlations with Price", mar = c(0, 0, 2, 0))
# Define correlation analysis function
correlation_analysis <- function(data) {
required_columns <- c("price", "poiCount", "squareMeters", "rooms")
# Ensure required columns exist
existing_columns <- required_columns[required_columns %in% names(data)]
if (length(existing_columns) < length(required_columns)) {
stop("One or more required columns are missing from the dataset.")
}
# Compute correlation values
cor_matrix <- cor(data[, existing_columns], use = "pairwise.complete.obs")
# Create correlation heatmap
corrplot(cor_matrix,
method = "color",
col = colorRampPalette(c("orange", "white", "navy"))(200),
type = "upper",
order = "hclust",
addCoef.col = "black",
tl.col = "black",
tl.srt = 45,
title = "Correlation Heatmap")
}
# Execute correlation analysis
correlation_analysis(czyste)
Analiza mapy cieplnej korelacji między różnymi zmiennymi prowadzi do trzech wykresów:
Pierwszy wykres przedstawia korelację między wybranymi zmiennymi, takimi jak szerokość geograficzna, cena, metraż, liczba pokoi, piętro, liczba pięter w budynku, długość geograficzna, rok budowy oraz odległość od centrum. Wykazuje on niewielką lub żadną korelację między większością z tych zmiennych.
Druga mapa cieplna przedstawia korelację między mieszkaniem a odległością do różnych udogodnień. Pokazuje ona, że zazwyczaj, jeśli mieszkanie znajduje się blisko jednej infrastruktury, to jest także blisko innej. Co jednak ciekawe, istnieje negatywna korelacja między ceną a odległością do kliniki oraz restauracji, co wskazuje, że im mniejsza odległość, tym wyższa cena mieszkania. Jest to szczególnie nietypowe w kontekście pozytywnej korelacji między ceną a odległością do centrum, co sugeruje, że im bliżej centrum znajduje się mieszkanie, tym jest tańsze. Wynik ten stoi w sprzeczności z intuicyjną hipotezą, że mieszkania w centrum miasta są najdroższe. Może to oznaczać, że w danych znajduje się znaczna liczba mieszkań na przedmieściach, które są warte więcej niż mieszkania w obszarach miejskich.
Trzeci wykres przedstawia mapę cieplną mierzącą cenę w odniesieniu do liczby pokoi oraz powierzchni mieszkania. Naturalna hipoteza, że większe mieszkania i większa liczba pokoi oznaczają wyższą cenę, znajduje potwierdzenie, wykazując bardzo wysoką dodatnią korelację.
Następnie tworzony jest wykres analizujący wpływ piętra, na którym znajduje się mieszkanie, na jego cenę.
ggplot(czyste, aes(x = floor)) +
geom_histogram(binwidth = 1, fill = "blue", alpha = 0.7) +
theme_minimal() +
scale_x_continuous(breaks = seq(min(czyste$floor, na.rm = TRUE),
max(czyste$floor, na.rm = TRUE),
by = 1))
labs(title = "Distribution of Apartments by Floor", x = "Floor", y = "Count")
## $x
## [1] "Floor"
##
## $y
## [1] "Count"
##
## $title
## [1] "Distribution of Apartments by Floor"
##
## attr(,"class")
## [1] "labels"
Wykres pokazuje, że większość mieszkań znajduje się na niższych piętrach oraz że ogólnie liczba budynków mających mniej niż 5 pięter jest większa, co sugeruje, że większość budynków jest raczej niewielka.
Analizę pięter można lepiej zrozumieć poprzez analizę typów budynków w badanej populacji.
# Calculate the count and percentage of each building type
type_summary <- czyste %>%
count(type) %>%
mutate(percentage = n / sum(n) * 100)
# Create a bar plot to show the count and percentage of each building type with fixed bar height
ggplot(type_summary, aes(x = type, y = n)) +
geom_bar(stat = "identity", fill = "steelblue", color = "black", width = 0.7) +
geom_text(aes(label = paste0(round(percentage, 1), "%")),
vjust = 0.5, hjust = 0.5, size = 5, color = "black") + # Center the percentage
theme_minimal() +
labs(title = "Distribution of Building Types", x = "Building Type", y = "Count") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
# Calculate the average price for each building type
average_price_by_type <- czyste %>%
group_by(type) %>%
summarise(avg_price = mean(price, na.rm = TRUE))
Bloki mieszkalne dominują w 62,5%, budynki mieszkalne stanowią 20,2%, a kamienice 17,3%. Znaczna liczba kamienic, które zazwyczaj są małe i niezbyt wysokie, w dużym stopniu przyczynia się do tego, że apartamenty w populacji znajdują się zazwyczaj na niższych piętrach.
Następnie tworzony jest wykres, który pokazuje wpływ piętra na cenę.
# Create scatter plot with a linear trend line
ggplot(czyste, aes(x = rooms, y = price)) +
geom_point(alpha = 0.5, color = "blue") + # Scatter plot points
geom_smooth(method = "lm", color = "red", se = FALSE) + # Linear trend line
labs(
title = "Cena vs. Liczba pokoji",
x = "Liczba pokoji",
y = "Cena (PLN)"
) +
theme_minimal()
Wykres pokazuje stosunkowo nieistotną i słabą dodatnią korelację między ceną a piętrem. Sugeruje to, że piętro, na którym znajduje się apartament, nie wpływa znacząco na jego cenę.
Następnie analizowane są materiały budowlane i infrastruktura bezpieczeństwa w celu określenia ich wpływu na cenę budynku.
ggplot(czyste, aes(x = buildingMaterial, y = price, fill = buildingMaterial)) +
geom_boxplot(alpha = 0.8) +
theme_minimal() +
coord_flip() +
labs(title = "Impact of Building Material on Price", x = "Building Material", y = "Price")
#Impact of Security on Apartment Prices
ggplot(czyste, aes(x = factor(hasSecurity, labels = c("No Security", "Has Security")), y = price)) +
geom_boxplot(aes(fill = factor(hasSecurity)), alpha = 0.8) +
theme_minimal() +
labs(title = "Impact of Security on Apartment Prices", x = "Security", y = "Price") +
scale_fill_manual(values = c("red", "green"))
Dane pokazują, że ogólnie apartamenty zbudowane z cegły są droższe niż te wykonane z betonu, a apartamenty z ochroną są droższe niż te bez ochrony. Jednak w obu przypadkach istnieje wiele odstających wartości w populacji, co oznacza, że nie ma silnej korelacji między tymi dwoma czynnikami. Ważne dla analizy jest jednak to, że gęstość odstających wartości dla apartamentów bez ochrony sugeruje, że istnieją bardzo drogie apartamenty, które nie mają ochrony.
Wnioskowanie Statystyczne
if (!require(nortest)) install.packages("nortest")
library(nortest)
# Test Andersona-Darlinga
ad_test <- ad.test(czyste$price)
# Histogram z gęstością rozkładu
ggplot(czyste, aes(x = price)) +
geom_histogram(aes(y = ..density..), bins = 50, fill = "skyblue", color = "black", alpha = 0.7) +
geom_density(color = "red", linewidth = 1) +
labs(title = "Histogram cen mieszkań z gęstością", x = "Cena", y = "Gęstość") +
theme_minimal()
# QQ-plot
ggplot(data.frame(price = czyste$price), aes(sample = price)) +
stat_qq(distribution = qnorm) +
stat_qq_line(distribution = qnorm, color = "red", linewidth = 1) +
labs(title = "QQ-plot dla cen mieszkań", x = "Kwantyle teoretyczne", y = "Kwantyle empiryczne") +
theme_minimal()
# Empiryczna funkcja rozkładu vs. normalna
ggplot(data.frame(price = sort(czyste$price)), aes(x = price)) +
stat_ecdf(geom = "step", color = "blue", linewidth = 1) +
stat_function(fun = function(x) pnorm(x, mean = mean(czyste$price), sd = sd(czyste$price)),
color = "red", linetype = "dashed", linewidth = 1) +
labs(title = "Empiryczna CDF vs. Normalna CDF", x = "Cena", y = "Prawdopodobieństwo") +
theme_minimal()
Wyniki
Dane ogólne
Analiza polskiego rynku nieruchomości na podstawie danych z czerwca 2024 roku pokazuje, że większość polskich apartamentów, bo 77,1%, mieści się w przedziale cenowym od 250 000 do 1 000 000 PLN. Rozkład cen jest wyraźnie przesunięty w prawo, co wskazuje na dużą liczbę apartamentów o wysokiej cenie. Ponad 13% nieruchomości przekracza cenę 1 250 000 PLN, co potwierdza obecność nieruchomości luksusowych.
Miasto, w którym znajduje się apartament, jest najważniejszym czynnikiem wpływającym na jego cenę
Wykresy pudełkowe pokazują znaczną zmienność cen między miastami. Stan apartamentu nie wpływa silnie na cenę, co pokazuje wykres skrzypcowy. Analiza ANOVA ceny według miasta i stanu potwierdza, że różnice cenowe zależą bardziej od miasta niż od stanu technicznego apartamentu.
Odległość od miasta
Negatywna korelacja między ceną a odległością od klinik/restauracji sugeruje, że apartamenty o wysokiej wartości znajdują się w rejonach o lepszej infrastrukturze miejskiej. Nieoczekiwany wynik: Apartamenty oddalone od centrum są droższe, co może wynikać z obecności luksusowych nieruchomości podmiejskich w zamkniętych osiedlach. Zbiór danych może być zniekształcony przez luksusowe inwestycje podmiejskie, a nie tradycyjne miejskie apartamenty.
Ostateczne wnioski
Polski rynek nieruchomości zdominowany jest przez apartamenty w średnim przedziale cenowym (500 000 - 999 999 PLN). W rynku luksusowym występują wartości odstające, które znacząco wpływają na miary statystyczne. Przewagę ma własność prywatna (prawie 90%), a spółdzielcze mieszkania stanowią niewielki odsetek. Stan apartamentu ma niewielki wpływ na cenę, co sugeruje, że bardziej liczy się lokalizacja. Bliskość do klinik i restauracji ma pozytywny związek z ceną, podczas gdy odległość od centrum miasta wykazuje nieoczekiwaną negatywną korelację. Nieoczekiwany efekt odległości od centrum sugeruje, że drogie nieruchomości mogą być skoncentrowane w podmiejskich, luksusowych inwestycjach, a nie w tradycyjnych centrach miast.
Źródła
citation("ggstatsplot")
## Aby zacytować pakiet 'ggstatsplot' w publikacjach użyj:
##
## Patil, I. (2021). Visualizations with statistical details: The
## 'ggstatsplot' approach. Journal of Open Source Software, 6(61), 3167,
## doi:10.21105/joss.03167
##
## Wpis BibTex dla użytkowników LaTeX to
##
## @Article{,
## doi = {10.21105/joss.03167},
## url = {https://doi.org/10.21105/joss.03167},
## year = {2021},
## publisher = {{The Open Journal}},
## volume = {6},
## number = {61},
## pages = {3167},
## author = {Indrajeet Patil},
## title = {{Visualizations with statistical details: The {'ggstatsplot'} approach}},
## journal = {{Journal of Open Source Software}},
## }