Exploratory data analysis (EDA) v jazyku R 150 150 cleandata

Exploratory data analysis (EDA) v jazyku R

Exploratory Data Analysis (EDA) je proces analýzy dát s cieľom porozumieť ich základným charakteristikám, často pomocou základných štatistických a vizuálnych metód. V jazyku R sa EDA realizuje rôznymi nástrojmi a technikami, ktoré umožňujú odhaľovať vzory, anomálie a vzťahy medzi premennými, čo je kľúčové pre efektívnu prípravu dát na ďalšie analýzy a modelovanie.

consolidate_sk

Úvod

V tomto blogu sa budem venovať Exploratory Data Analysis (EDA), čiže úvodnej analýze údajov, ktorej cieľom je zistiť aká je kvalita, obsah a štruktúra údajov. V tomto prípade ide o dáta z inzercií nehnuteľností. Dáta sú scrape-nuté z webu Nehnutelnosti, procesmi webscraping-u a geokódovania som prešiel v predchádzajúcich blokoch “Web scrapingpomocou jazyka R” a “Geocoding pomocou jazyka R”.

Čo je EDA


Exploratory Data Analysis je neoddeliteľnou súčasťou dátovej analytiky/dátovej vedy (Data science).

EDA v data science projekte. Zdroj: https://commons.wikimedia.org/wiki/File:Data_visualization_process_v1.png


Účelom EDA je zhrnúť hlavné charakteristiky súboru údajov (ako kvalita, obsah a štruktúra), objaviť vzorce a vzťahy medzi premennými a identifikovať trendy. Malo by nás viesť k pochopeniu údajov a identifikácii kritických premenných vzhľadom na naše ciele. Ako je znázornené na obrázku, ide o iteratívny proces. Na základe vašich zistení môžete buď pokračovať v modelovaní/testovaní hypotéz a reportovaní, alebo sa vrátiť k čisteniu/spracovaniu údajov.
EDA zvyčajne začína načítaním údajov a kontrolou niekoľkých riadkov, aby ste získali prvotný “pocit” z údajov spolu s kontrolou štruktúry údajov, veľkosti vzorky, typov údajov, chýbajúcich hodnôt atď. Potom pokračuje podrobnejšou analýzou, ktorá nám pomáha pochopiť vzťahy a identifikovať odľahlé hodnoty a dôležité premenné. V EDA používame rôzne techniky a nástroje. Vo všeobecnosti ich možno rozdeliť do niekoľkých skupín:

  • Súhrnné (jednopremenné) štatistiky – min, max, priemer, medián, kvartily, IQR, štandardná odchýlka, počty, frekvencia atď.
  • Vizualizácia dát – histogram, boxplot, Paretov graf, bodové grafy, korelačná matica, čiarové grafy (pre časové rady), heatmapy atď.
  • Bi-/viacpremenné štatistiky – korelácia, t-test, chí-kvadrát test, ANOVA, Kruskal-Wallisov test atď.

Na základe zistení vytvoríme záver a buď pokračujeme v projekte, alebo sa vrátime k dodatočnému upratovaniu dát. Je to teda iteratívny proces.
Aj keď radšej robím EDA manuálne, existuje niekoľko R knižníc pre automatizované EDA. Sú užitočné pri prvotnom skúmaní údajov a identifikácii napr. dátových typov, premenných s veľkou časťou chýbajúcich hodnôt a iných “high-level” charakteristík. Sú to napríklad:

  • DataExplorer
  • ExPanDaR
  • dataMaid
  • dlookr

Úvodné čistenie dát


Začínam klasicky, načítaním knižníc pomocou funkcie p_load z knižnice pacman.

Knižnice
# import libraries
if (!require("pacman")) {
  install.packages("pacman")
}

pacman::p_load(
  janitor, # clean_names()
  skimr, # skim()
  sf, # geospatial data
  ggpubr,
  ggQC, # pareto chart
  scales, # scales
  GGally, # eval_data_col
  knitr,
  modelsummary, # datasummary_correlation()
  gtsummary, # tables
  ggstatsplot, # ggwithinstats()
  effectsize, # interpret_kendalls_w()
  tidyverse, # data wrangling
  kableExtra, # tables
  extrafont # fonts
)

loadfonts(device = "win")


Nasleduje prvotné čistenie dát. V nasledujúcom kóde spájam 3 rôzne súbory. Keďže sú z rôznych zdrojov, je potrebné niektoré hodnoty upraviť do rovnakého tvaru (prípad názvov obcí).
Následne upravujem premenné do správnych typov, odfiltrujem preč záznamy, ktorých hodnoty sú odľahlé alebo úplne chýbajú a nemá zmysel ich imputovať.
Krok preloženia slovenských výrazov do angličtiny nie je nevzhnutný. Robím ho jednak z dôvodu, že som zvyknutý pracovať s anglickými výrazmi pri kódovaní a chcem aby aj dataset bol v tomto ohľade konzistentný. Druhým dôvodom je, že budem dataset nahrávať na Kaggle.
V poslednom kroku robím dve verzie datasetu. Jedna obsahuje premennú ‘geometry’ typu sfc_MULTIPOLYGON, ktorá robí problém alebo extrémne spomaluje výpočty niektorých sumačných funkciách, ak sú aplikované na celý dataset. Preto na všetku EDA budem používať verziu bez nej.

Feature engineering
# Load advertisements data from RDS file
advertisements <- readRDS("data/advertisements.RDS")

# Clean and restructure advertisements data
advertisements <- advertisements %>%
  separate(type_of_real_estate, c("type", "area"), sep = " • ", remove = TRUE) %>%
  select(link, type)

# Load and process districts mapping data from Excel file
districts_mapping <- openxlsx::read.xlsx("data/obce_okresy.xlsx") %>%
  mutate(
    municipality = str_replace(municipality, "Košice - ", "Košice - mestská časť "),
    municipality = str_replace(municipality, "Bratislava - ", "Bratislava - mestská časť ")
  )

# Load and process scraped data with geocoding
scraped_data <- readRDS("data/advertisements_complete_geocoded.RDS") %>%
  filter(!is.na(link)) %>%
  select(-c(address1, address2, info_text, district, municipality, address)) %>%
  rename(quality_of_living = quanlity_of_living) %>%
  mutate(
    NAME_NSI = str_replace(NAME_NSI, "Hodruša-Hámre", "Hodruša - Hámre"),
    NAME_NSI = str_replace(NAME_NSI, "Perín-Chym", "Perín - Chym"),
    NAME_NSI = str_replace(NAME_NSI, "Šaštín-Stráže", "Šaštín - Stráže"),
    NAME_NSI = str_replace(NAME_NSI, "Kostolná-Záriečie", "Kostolná - Záriečie")
  )

# Join advertisements and scraped data
joined_data <- scraped_data %>%
  left_join(advertisements, by = "link", multiple = "first", keep = FALSE) %>%
  clean_names() %>%
  filter(!is.na(price)) %>%
  mutate(
    # Convert relevant columns to numeric format
    pocet_izieb_miestnosti = as.numeric(pocet_izieb_miestnosti),
    uzit_plocha = str_replace(str_replace(uzit_plocha, ",", "."), " m2", ""),
    energie = str_replace(str_replace(energie, ",", "."), " €/mesiac", ""),
    provizia_zahrnuta_v_cene = str_replace_na(provizia_zahrnuta_v_cene, "Nie"),
    # Create a 'rooms' column based on 'type' and handle missing values
    rooms = case_when(type == "1 izbový byt" ~ 1,
      type == "2 izbový byt" ~ 2,
      type == "3 izbový byt" ~ 3,
      type == "4 izbový byt" ~ 4,
      type == "5 a viac izbový byt" ~ 5,
      type == "Garsónka" ~ 1,
      type == "Dvojgarsónka" ~ 2,
      .default = NA
    ),
    pocet_izieb_miestnosti = coalesce(pocet_izieb_miestnosti, rooms, pocet_izieb_miestnosti)
  ) %>%
  mutate_at(c(
    "index_of_living",
    "uzit_plocha",
    "energie",
    "pocet_nadzemnych_podlazi",
    "podlazie",
    "pocet_izieb_miestnosti",
    "rok_vystavby",
    "rok_poslednej_rekonstrukcie",
    "pocet_balkonov",
    "pocet_lodzii"
  ), as.numeric) %>%
  select(-link) %>%
  filter(pocet_izieb_miestnosti < 10 & !is.na(pocet_izieb_miestnosti)) %>%
  mutate(
    type = coalesce(type, case_when(
      pocet_izieb_miestnosti == 1 ~ "1 izbový byt",
      pocet_izieb_miestnosti == 2 ~ "2 izbový byt",
      pocet_izieb_miestnosti == 3 ~ "3 izbový byt",
      pocet_izieb_miestnosti == 4 ~ "4 izbový byt",
      pocet_izieb_miestnosti >= 5 ~ "5 a viac izbový byt"
    ))
  ) %>%
  select(-rooms) %>%
  filter(!(type %in% c("Apartmán", "Mezonet", "Iný byt", "Loft"))) %>%
  rename(
    index = index_of_living,
    condition = stav,
    area = uzit_plocha,
    provision = provizia_zahrnuta_v_cene,
    certificate = energeticky_certifikat,
    energy_costs = energie,
    construction_type = typ_konstrukcie,
    year_built = rok_vystavby,
    last_reconstruction = rok_poslednej_rekonstrukcie,
    total_floors = pocet_nadzemnych_podlazi,
    floor = podlazie,
    lift = vytah,
    balkonies = pocet_balkonov,
    loggia = pocet_lodzii,
    cellar = pivnica,
    orientation = orientacia
  ) %>%
  mutate(
    # Recreate 'rooms' column after filtering and handle missing values
    rooms = as.numeric(case_when(
      type == "1 izbový byt" ~ 1,
      type == "2 izbový byt" ~ 2,
      type == "3 izbový byt" ~ 3,
      type == "4 izbový byt" ~ 4,
      type == "5 a viac izbový byt" ~ 5,
      type == "Garsónka" ~ 1,
      type == "Dvojgarsónka" ~ 2,
      .default = NA
    )),
    # Transform 'provision' to binary
    provision = as.numeric(case_when(
      provision == "Áno" ~ 1,
      provision == "Nie" ~ 0,
      .default = NA
    )),
    # Transform 'lift' to binary
    lift = as.numeric(case_when(
      lift == "Áno" ~ 1,
      .default = 0
    )),
    # Transform 'cellar' to binary
    cellar = as.numeric(case_when(
      cellar == "Áno" ~ 1,
      .default = 0
    )),
    certificate = if_else(certificate == "nemá", "none", certificate)
  ) %>%
  select(-pocet_izieb_miestnosti) %>%
  mutate(
    # Convert relevant columns to numeric format
    across(c(
      "environment", "safety", "transport", "relax", "quality_of_living", "services"
    ), na_if, "0"),
    across(c(
      "environment", "safety", "transport", "relax", "quality_of_living", "services"
    ), as.numeric)
  )

# Translating Slovak terms into English
# Define mapping vectors
original_conditions <- c(
  "Pôvodný stav", "Čiastočná rekonštrukcia", "Kompletná rekonštrukcia",
  "Novostavba", "Vo výstavbe", "Developerský projekt"
)
english_conditions <- c(
  "Original condition", "Partial reconstruction", "Complete reconstruction",
  "New building", "Under construction", "Development project"
)
original_construction_type <- c("Tehlová", "Panelová", "Iná", "Kvádrová", "Zmiešaná", "Panelová, Tehlová", "Skeletová", "Tehlová, Železobetónová", "Kamenná", "Montovaná", "Drevená")
english_construction_type <- c("Brick", "Panel", "Other", "Cube", "Mixed", "Panel, Brick", "Skeletal", "Brick, Reinforced concrete", "Stone", "Mounted", "Wooden")
original_orientation <- c("Juhozápadná", "Južná", "Západná", "Východná", "Juhovýchodná", "Severovýchodná", "Severozápadná", "Severná")
english_orientation <- c("Southwest", "South", "West", "East", "Southeast", "Northeast", "Northwest", "North")
original_type <- c("3 izbový byt", "1 izbový byt", "2 izbový byt", "4 izbový byt", "Garsónka", "5 a viac izbový byt", "Dvojgarsónka")
english_type <- c("3-room apartment", "1-room apartment", "2-room apartment", "4-room apartment", "Studio", "5 or more room apartment", "Double studio")

# Translate values
joined_data <- joined_data %>%
  mutate(
    condition = recode(condition, !!!setNames(english_conditions, original_conditions)),
    construction_type = recode(construction_type, !!!setNames(english_construction_type, original_construction_type)),
    orientation = recode(orientation, !!!setNames(english_orientation, original_orientation)),
    type = recode(type, !!!setNames(english_type, original_type))
  )

# Join with districts mapping data
joined_data <- joined_data %>%
  left_join(districts_mapping, join_by(name_nsi == municipality), keep = FALSE, multiple = "first")

# Create a copy of joined data without geometry information
joined_data_wo_geom <- joined_data
joined_data_wo_geom$geometry <- NULL

write.csv2(joined_data_wo_geom, "data/apartments_appraisal.csv", row.names = F)

EDA

Záver a nasledujúce kroky


EDA poskytla cenné poznatky, ktoré budú zohľadnené v predikčnom modeli:

  • Rozloženie cien je vychýlené doprava – ponuky drahých bytov sú obmedzené
  • Geopriestorové rozloženie má západ-východný gradient – nižšie ceny sú na východe a juhu, s výnimkou niekoľkých regionálnych centier.
  • Väčšina miest v datasete má pomerne vysokú úroveň indexu bývania. Vo všeobecnosti existuje pozitívny vzťah medzi jeho hodnotou a cenou.
  • Existujú preukázané rozdiely medzi cenami bytov s rôznymi stavmi. Nie je prekvapujúce, že nové byty majú najvyššie ceny.
  • Podobný efekt je pri energetickom certifikáte. Počet chýbajúcich údajov je v tomto prípade vysoký a budem ho riešiť imputáciou.
  • Veľká väčšina bytov v súbore má 2 a 3 izby. Cena rastie s rastúcou veľkostnou triedou. Zvýšenie ceny z 2 izbovej na 3 izbovú skupinu je v však priemere dosť nízke. Dva možné dôvody sú – dopyt po 2 izbových bytoch (keďže sú stále lacnejšie ako 3 izbové) a lokalita. Ak by sa väčšina 2-izbových bytov nachádzala na západe, ich cena by bola v priemere za celú krajinu vyššia v porovnaní s rovnomerným priestorovým rozložením.
  • Post Tags:
  • Ako vytvoriť API v R pomocou knižnice Plumber 150 150 cleandata Ako vytvoriť API v R pomocou knižnice Plumber
  • Ako zrýchliť XGBoost v R: Benchmark trénovania modelu na CPU vs. GPU 150 150 cleandata Ako zrýchliť XGBoost v R: Benchmark trénovania modelu na CPU vs. GPU
  • EDA časových radov: Ako odhaliť vzory skryté v čase 150 150 cleandata EDA časových radov: Ako odhaliť vzory skryté v čase

Leave a Reply

Your email address will not be published.