knitr::opts_chunk$set(
echo = TRUE,
message = FALSE,
warning = FALSE
)Práca s databázou
Téma: ceny bývania na Slovensku a v okolitých krajinách
Pred prvým renderom treba (iba raz) nainštalovať knižnice: install.packages(c("dplyr", "tidyr", "ggplot2", "kableExtra", "mice", "broom"))
Práca s údajmi
Tradičná práca s databázou
Údaje v R ukladáme najčastejšie do typu data.frame, teda tabuľky, kde jeden riadok predstavuje jeden záznam. Ako príklad použijem údaje o krajoch z minulého cvičenia (mzda v EUR, cena bytu v EUR/m²).
Kraj <- c("BA", "KE", "PO", "BB")
Mzda <- c(1949, 1399, 1195, 1354)
Cena <- c(3190, 2102, 1860, 1538)
kraje <- data.frame(Kraj, Mzda, Cena)
print(kraje) Kraj Mzda Cena
1 BA 1949 3190
2 KE 1399 2102
3 PO 1195 1860
4 BB 1354 1538
Niektoré operácie s data.frame:
print(kraje$Mzda) # jedna premenna (stlpec)[1] 1949 1399 1195 1354
print(mean(kraje$Cena)) # priemerna cena[1] 2172.5
print(kraje[Kraj == "PO", ]) # cely riadok Presovskeho kraja Kraj Mzda Cena
3 PO 1195 1860
print(kraje[2, ]) # druhy riadok Kraj Mzda Cena
2 KE 1399 2102
print(kraje[, 2:3]) # druhy a treti stlpec Mzda Cena
1 1949 3190
2 1399 2102
3 1195 1860
4 1354 1538
print(kraje[1, 3]) # jedna bunka[1] 3190
summary(kraje) # zakladna statistika Kraj Mzda Cena
Length :4 Min. :1195 Min. :1538
N.unique :4 1st Qu.:1314 1st Qu.:1780
N.blank :0 Median :1376 Median :1981
Min.nchar:2 Mean :1474 Mean :2172
Max.nchar:2 3rd Qu.:1536 3rd Qu.:2374
Max. :1949 Max. :3190
Pridanie stĺpca a riadku:
Krajske_mesto_nad_100tis <- c(TRUE, TRUE, FALSE, FALSE)
kraje <- cbind(kraje, Krajske_mesto_nad_100tis)
novy.riadok <- data.frame(Kraj = "NR", Mzda = 1330, Cena = 1650,
Krajske_mesto_nad_100tis = FALSE)
kraje <- rbind(kraje, novy.riadok)
print(kraje) Kraj Mzda Cena Krajske_mesto_nad_100tis
1 BA 1949 3190 TRUE
2 KE 1399 2102 TRUE
3 PO 1195 1860 FALSE
4 BB 1354 1538 FALSE
5 NR 1330 1650 FALSE
Údaje za Nitriansky kraj sú len orientačné, slúžia na ukážku rbind().
Tabuľky v prostredí kableExtra
library(knitr)
library(kableExtra)
kable(kraje, align = c("l", "r", "r", "c"),
caption = "Mzdy a ceny bytov v krajoch") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE, position = "center")| Kraj | Mzda | Cena | Krajske_mesto_nad_100tis |
|---|---|---|---|
| BA | 1949 | 3190 | TRUE |
| KE | 1399 | 2102 | TRUE |
| PO | 1195 | 1860 | FALSE |
| BB | 1354 | 1538 | FALSE |
| NR | 1330 | 1650 | FALSE |
Tidyverse - moderná práca s údajmi
Používam knižnice dplyr (úprava údajov), tidyr (zmena tvaru tabuľky) a ggplot2 (grafy). Pomocou %>% posielam výsledok jednej funkcie do ďalšej.
library(dplyr)
library(tidyr)
library(ggplot2)Výber a triedenie
kraje %>%
filter(Cena > 1700) %>% # kraje s cenou nad 1700 EUR/m2
arrange(desc(Cena)) %>% # zoradenie od najdrahsieho
kable() %>%
kable_styling(full_width = FALSE, position = "center")| Kraj | Mzda | Cena | Krajske_mesto_nad_100tis |
|---|---|---|---|
| BA | 1949 | 3190 | TRUE |
| KE | 1399 | 2102 | TRUE |
| PO | 1195 | 1860 | FALSE |
Zoskupenie a sumarizácia
kraje %>%
group_by(Krajske_mesto_nad_100tis) %>%
summarise(Priem.Cena = mean(Cena), Pocet = n()) %>%
kable(col.names = c("Mesto nad 100 tis.", "Priemerná cena", "Počet"),
align = "c") %>%
kable_styling(full_width = FALSE, position = "center")| Mesto nad 100 tis. | Priemerná cena | Počet |
|---|---|---|
| FALSE | 1682.667 | 3 |
| TRUE | 2646.000 | 2 |
Vytváranie novej premennej
kraje %>%
mutate(
Roky_70m2 = round(70 * Cena / (Mzda * 12), 1), # kolko rokov mzdy na byt 70 m2
Dostupnost = case_when(
Roky_70m2 >= 9 ~ "nízka",
Roky_70m2 >= 7 ~ "stredná",
TRUE ~ "dobrá"
)
) %>%
kable() %>%
kable_styling(full_width = FALSE, position = "center")| Kraj | Mzda | Cena | Krajske_mesto_nad_100tis | Roky_70m2 | Dostupnost |
|---|---|---|---|---|---|
| BA | 1949 | 3190 | TRUE | 9.5 | nízka |
| KE | 1399 | 2102 | TRUE | 8.8 | stredná |
| PO | 1195 | 1860 | FALSE | 9.1 | nízka |
| BB | 1354 | 1538 | FALSE | 6.6 | dobrá |
| NR | 1330 | 1650 | FALSE | 7.2 | stredná |
Import údajov z otvorenej databázy
Výber databázy
Pre ďalšie hodiny som si vybral databázu Eurostatu: House price index (prc_hpi_a), ktorá obsahuje ročný index cien nehnuteľností na bývanie (2015 = 100). Údaje sú zoradené v čase (roky 2010 – 2025) a pre viacero krajín, ide teda o panelové údaje. Vybral som si:
- krajiny: Slovensko, Česko, Maďarsko, Poľsko, Rakúsko, Nemecko a priemer EÚ,
- typy bývania: všetky byty a domy spolu (
TOTAL), nové (DW_NEW) a existujúce (DW_EXST).
Odkaz na databázu: Eurostat – House price index (prc_hpi_a).
Stiahnutie a uloženie do .csv
Údaje stiahnem priamo cez rozhranie (API) Eurostatu a uložím do súboru udaje/ceny_byvania_eu.csv. Ak súbor už existuje, znova sa nesťahuje. Ako oddeľovač používam bodkočiarku a desatinnú bodku, rovnako ako v predlohe.
subor <- "udaje/ceny_byvania_eu.csv"
if (!file.exists(subor)) {
url <- paste0(
"https://ec.europa.eu/eurostat/api/dissemination/sdmx/2.1/data/prc_hpi_a/",
"A.TOTAL+DW_NEW+DW_EXST.I15_A_AVG.SK+CZ+HU+PL+AT+DE+EU27_2020/",
"?format=SDMX-CSV&startPeriod=2010"
)
dir.create("udaje", showWarnings = FALSE)
stiahnute <- read.csv(url)
write.table(stiahnute, subor, sep = ";", dec = ".", row.names = FALSE)
}Import do data.frame
udaje <- read.csv2(subor, header = TRUE, sep = ";", dec = ".",
na.strings = c("", "NA"), stringsAsFactors = FALSE)
head(udaje) # prvych niekolko riadkov DATAFLOW LAST.UPDATE freq purchase unit geo
1 ESTAT:PRC_HPI_A(1.0) 01/10/26 11:00:00 A DW_EXST I15_A_AVG AT
2 ESTAT:PRC_HPI_A(1.0) 01/10/26 11:00:00 A DW_EXST I15_A_AVG AT
3 ESTAT:PRC_HPI_A(1.0) 01/10/26 11:00:00 A DW_EXST I15_A_AVG AT
4 ESTAT:PRC_HPI_A(1.0) 01/10/26 11:00:00 A DW_EXST I15_A_AVG AT
5 ESTAT:PRC_HPI_A(1.0) 01/10/26 11:00:00 A DW_EXST I15_A_AVG AT
6 ESTAT:PRC_HPI_A(1.0) 01/10/26 11:00:00 A DW_EXST I15_A_AVG AT
TIME_PERIOD OBS_VALUE OBS_FLAG CONF_STATUS
1 2010 77.75 <NA> NA
2 2011 81.97 <NA> NA
3 2012 86.81 <NA> NA
4 2013 91.14 <NA> NA
5 2014 94.53 <NA> NA
6 2015 100.00 <NA> NA
colnames(udaje) # nazvy premennych [1] "DATAFLOW" "LAST.UPDATE" "freq" "purchase" "unit"
[6] "geo" "TIME_PERIOD" "OBS_VALUE" "OBS_FLAG" "CONF_STATUS"
dim(udaje) # pocet riadkov a stlpcov[1] 336 10
Čistenie údajov
Výber stĺpcov a premenovanie
Stiahnutá tabuľka obsahuje aj stĺpce, ktoré nepotrebujem (napr. dátum aktualizácie). Ponechám iba krajinu, typ bývania, rok a hodnotu indexu a premenujem ich na jednoduché názvy bez medzier.
old_names <- names(udaje)
udaje <- udaje %>%
dplyr::select(geo, purchase, TIME_PERIOD, OBS_VALUE) %>%
rename(Krajina = geo, Typ = purchase, Rok = TIME_PERIOD, Index = OBS_VALUE)
old_names [1] "DATAFLOW" "LAST.UPDATE" "freq" "purchase" "unit"
[6] "geo" "TIME_PERIOD" "OBS_VALUE" "OBS_FLAG" "CONF_STATUS"
names(udaje)[1] "Krajina" "Typ" "Rok" "Index"
Zmena tvaru tabuľky
Každý typ bývania dám do samostatného stĺpca, aby jeden riadok = jedna krajina v jednom roku.
udaje <- udaje %>%
pivot_wider(names_from = Typ, values_from = Index) %>%
rename(Spolu = TOTAL, Nove = DW_NEW, Existujuce = DW_EXST) %>%
arrange(Krajina, Rok) %>%
as.data.frame()
head(udaje, 10) Krajina Rok Existujuce Nove Spolu
1 AT 2010 77.75 76.16 77.32
2 AT 2011 81.97 80.13 81.47
3 AT 2012 86.81 86.79 86.86
4 AT 2013 91.14 90.98 91.20
5 AT 2014 94.53 94.72 94.68
6 AT 2015 100.00 100.00 100.00
7 AT 2016 105.95 107.85 106.71
8 AT 2017 112.33 110.47 112.14
9 AT 2018 119.40 115.98 118.83
10 AT 2019 126.58 122.90 125.97
Chýbajúce údaje
Po zmene tvaru tabuľky sa na miestach, kde Eurostat hodnotu nemá, objavia hodnoty NA.
library(mice)
colSums(is.na(udaje)) # pocet NA v kazdom stlpci Krajina Rok Existujuce Nove Spolu
0 0 0 0 0
md.pattern(udaje[, c("Spolu", "Nove", "Existujuce")], rotate.names = TRUE) /\ /\
{ `---' }
{ O O }
==> V <== No need for mice. This data set is completely observed.
\ \|/ /
`-----'
Spolu Nove Existujuce
112 1 1 1 0
0 0 0 0
Chýbajúce hodnoty doplním pomocou imputácie (knižnica mice), rovnako ako v predlohe. Imputujem iba číselné stĺpce.
cisla <- udaje[, c("Rok", "Spolu", "Nove", "Existujuce")]
if (sum(is.na(cisla)) > 0) {
imp <- mice(cisla, m = 1, seed = 123, printFlag = FALSE)
cisla <- complete(imp, 1)
}
udaje[, c("Spolu", "Nove", "Existujuce")] <- cisla[, c("Spolu", "Nove", "Existujuce")]
colSums(is.na(udaje)) Krajina Rok Existujuce Nove Spolu
0 0 0 0 0
Nová premenná: medziročný rast
Keďže sú údaje zoradené v čase, viem vypočítať medziročný rast cien v % pre každú krajinu zvlášť (group_by + lag).
udaje <- udaje %>%
group_by(Krajina) %>%
arrange(Rok, .by_group = TRUE) %>%
mutate(Rast = round((Spolu / lag(Spolu) - 1) * 100, 2)) %>%
ungroup() %>%
as.data.frame()
udaje %>% filter(Krajina == "SK") %>% head(8) Krajina Rok Existujuce Nove Spolu Rast
1 SK 2010 93.94 104.33 97.06 NA
2 SK 2011 93.03 101.64 95.60 -1.50
3 SK 2012 91.57 96.11 92.94 -2.78
4 SK 2013 92.75 95.86 93.69 0.81
5 SK 2014 93.98 97.19 94.96 1.36
6 SK 2015 100.00 100.00 100.00 5.31
7 SK 2016 106.96 105.88 106.67 6.67
8 SK 2017 112.77 112.92 112.91 5.85
Grafy
Vývoj cien v čase
ggplot(udaje, aes(x = Rok, y = Spolu, color = Krajina)) +
geom_line(linewidth = 1) +
geom_point(size = 1) +
theme_minimal() +
labs(title = "Index cien bývania (2015 = 100)",
x = "Rok", y = "Index", color = "Krajina")Scatter plot: nové vs. existujúce byty
ggplot(udaje, aes(x = Existujuce, y = Nove)) +
geom_point(color = "steelblue") +
theme_minimal() +
labs(title = "Ceny nových a existujúcich bytov",
x = "Index - existujúce", y = "Index - nové")Boxplot: medziročný rast podľa krajín
ggplot(udaje %>% filter(!is.na(Rast)),
aes(x = Krajina, y = Rast)) +
geom_boxplot(fill = "lightblue", color = "darkblue") +
theme_minimal() +
labs(title = "Medziročný rast cien bývania 2011 - 2025",
x = "Krajina", y = "Rast v %")Základné štatistiky
statistiky <- udaje %>%
filter(!is.na(Rast)) %>%
group_by(Krajina) %>%
summarise(
n = n(),
mean = mean(Rast),
sd = sd(Rast),
min = min(Rast),
median = median(Rast),
max = max(Rast),
.groups = "drop"
)
statistiky %>%
kable(digits = 2, caption = "Medziročný rast cien bývania v % podľa krajín") %>%
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed")) %>%
column_spec(1, bold = TRUE) %>%
row_spec(0, bold = TRUE, background = "#f2f2f2") %>%
add_header_above(c(" " = 2, "Rast cien v %" = 5))| Krajina | n | mean | sd | min | median | max |
|---|---|---|---|---|---|---|
| AT | 15 | 5.33 | 3.74 | -2.85 | 5.62 | 11.57 |
| CZ | 15 | 6.69 | 6.42 | -1.67 | 7.20 | 19.69 |
| DE | 15 | 4.17 | 4.55 | -8.41 | 4.71 | 11.54 |
| EU27_2020 | 15 | 3.29 | 3.26 | -2.10 | 4.31 | 8.43 |
| HU | 15 | 9.82 | 8.30 | -3.70 | 13.08 | 22.34 |
| PL | 15 | 5.05 | 5.65 | -4.38 | 4.89 | 14.97 |
| SK | 15 | 5.19 | 4.94 | -2.78 | 5.85 | 13.70 |
Testovanie hypotéz
t-test: rástli ceny na Slovensku rýchlejšie ako v Česku?
t.test(
udaje$Rast[udaje$Krajina == "SK"],
udaje$Rast[udaje$Krajina == "CZ"]
)
Welch Two Sample t-test
data: udaje$Rast[udaje$Krajina == "SK"] and udaje$Rast[udaje$Krajina == "CZ"]
t = -0.71793, df = 26.266, p-value = 0.4791
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-5.797719 2.795053
sample estimates:
mean of x mean of y
5.192667 6.694000
ANOVA: líši sa rast cien medzi krajinami?
anova.vysledok <- aov(Rast ~ Krajina, data = udaje)
summary(anova.vysledok) Df Sum Sq Mean Sq F value Pr(>F)
Krajina 6 403.7 67.29 2.224 0.0469 *
Residuals 98 2964.4 30.25
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
7 observations deleted due to missingness
Lineárna regresia: vývoj cien na Slovensku v čase
udaje.SK <- udaje %>% filter(Krajina == "SK")
model <- lm(Spolu ~ Rok, data = udaje.SK)
summary(model)
Call:
lm(formula = Spolu ~ Rok, data = udaje.SK)
Residuals:
Min 1Q Median 3Q Max
-13.548 -9.648 -2.478 5.817 23.012
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -1.497e+04 1.319e+03 -11.36 1.89e-08 ***
Rok 7.487e+00 6.535e-01 11.46 1.69e-08 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 12.05 on 14 degrees of freedom
Multiple R-squared: 0.9036, Adjusted R-squared: 0.8967
F-statistic: 131.2 on 1 and 14 DF, p-value: 1.693e-08
Koeficient pri premennej Rok hovorí, o koľko bodov v priemere rástol index cien bývania na Slovensku za jeden rok.
library(broom)
tidy(model, conf.int = TRUE) %>%
kable(digits = 3, caption = "Regresia: Index ~ Rok (Slovensko)") %>%
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed"))| term | estimate | std.error | statistic | p.value | conf.low | conf.high |
|---|---|---|---|---|---|---|
| (Intercept) | -14974.940 | 1318.540 | -11.357 | 0 | -17802.926 | -12146.954 |
| Rok | 7.487 | 0.654 | 11.456 | 0 | 6.085 | 8.889 |
glance(model) %>%
dplyr::select(r.squared, adj.r.squared, statistic, p.value, nobs) %>%
kable(digits = 3, caption = "Kvalita modelu") %>%
kable_styling(full_width = FALSE, bootstrap_options = "condensed")| r.squared | adj.r.squared | statistic | p.value | nobs |
|---|---|---|---|---|
| 0.904 | 0.897 | 131.24 | 0 | 16 |