Práca s databázou

Téma: ceny bývania na Slovensku a v okolitých krajinách

Author

Riano Vajas
(podľa predlohy V. Gazdu)

Published

October 19, 2026

knitr::opts_chunk$set(
    echo = TRUE,
    message = FALSE,
    warning = FALSE
)

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")
Mzdy a ceny bytov v krajoch
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))
Medziročný rast cien bývania v % podľa krajín
Rast cien v %
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"))
Regresia: Index ~ Rok (Slovensko)
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")
Kvalita modelu
r.squared adj.r.squared statistic p.value nobs
0.904 0.897 131.24 0 16

Zdroje