4  Transformacja danych

Transformacja to przekształcanie surowych danych w formę użyteczną do analizy. Obejmuje tworzenie nowych kolumn, sortowanie, grupowanie, agregację i łączenie tabel.

4.1 Dodawanie i modyfikowanie kolumn

library(dplyr)

pracownicy <- data.frame(
  imie       = c("Anna", "Piotr", "Kasia", "Marek", "Zofia"),
  pensja     = c(4500, 5200, 4800, 6100, 5500),
  dzial      = c("IT", "Finanse", "IT", "HR", "Finanse"),
  staz_lat   = c(3, 7, 2, 10, 5),
  czy_menedz = c(FALSE, TRUE, FALSE, TRUE, FALSE)
)

# Dodanie nowych kolumn — mutate()
pracownicy <- pracownicy |>
  mutate(
    pensja_netto  = pensja * 0.77,           # obliczenie
    doswiadczony  = staz_lat >= 5,           # warunek logiczny
    kategoria     = case_when(               # wiele warunków
      pensja < 5000 ~ "junior",
      pensja < 6000 ~ "mid",
      TRUE          ~ "senior"
    )
  )

pracownicy
#>    imie pensja   dzial staz_lat czy_menedz pensja_netto doswiadczony kategoria
#> 1  Anna   4500      IT        3      FALSE         3465        FALSE    junior
#> 2 Piotr   5200 Finanse        7       TRUE         4004         TRUE       mid
#> 3 Kasia   4800      IT        2      FALSE         3696        FALSE    junior
#> 4 Marek   6100      HR       10       TRUE         4697         TRUE    senior
#> 5 Zofia   5500 Finanse        5      FALSE         4235         TRUE       mid
# Usunięcie kolumny
pracownicy <- pracownicy |>
  select(-pensja_netto)

# Zmiana nazwy kolumny
pracownicy <- pracownicy |>
  rename(lata_pracy = staz_lat)

names(pracownicy)
#> [1] "imie"         "pensja"       "dzial"        "lata_pracy"   "czy_menedz"  
#> [6] "doswiadczony" "kategoria"
import pandas as pd
import numpy as np

pracownicy = pd.DataFrame({
    "imie":       ["Anna", "Piotr", "Kasia", "Marek", "Zofia"],
    "pensja":     [4500, 5200, 4800, 6100, 5500],
    "dzial":      ["IT", "Finanse", "IT", "HR", "Finanse"],
    "staz_lat":   [3, 7, 2, 10, 5],
    "czy_menedz": [False, True, False, True, False]
})

# Dodanie nowych kolumn
pracownicy["pensja_netto"] = pracownicy["pensja"] * 0.77     # obliczenie
pracownicy["doswiadczony"] = pracownicy["staz_lat"] >= 5     # warunek logiczny

# Wiele warunków — np.select()
warunki  = [pracownicy["pensja"] < 5000, pracownicy["pensja"] < 6000]
wartosci = ["junior", "mid"]
pracownicy["kategoria"] = np.select(warunki, wartosci, default="senior")

pracownicy
# Usunięcie kolumny
pracownicy = pracownicy.drop(columns=["pensja_netto"])

# Zmiana nazwy kolumny
pracownicy = pracownicy.rename(columns={"staz_lat": "lata_pracy"})

list(pracownicy.columns)

4.2 Sortowanie

# Sortowanie rosnąco
arrange(pracownicy, pensja)
#>    imie pensja   dzial lata_pracy czy_menedz doswiadczony kategoria
#> 1  Anna   4500      IT          3      FALSE        FALSE    junior
#> 2 Kasia   4800      IT          2      FALSE        FALSE    junior
#> 3 Piotr   5200 Finanse          7       TRUE         TRUE       mid
#> 4 Zofia   5500 Finanse          5      FALSE         TRUE       mid
#> 5 Marek   6100      HR         10       TRUE         TRUE    senior
# Sortowanie malejąco
arrange(pracownicy, desc(pensja))
#>    imie pensja   dzial lata_pracy czy_menedz doswiadczony kategoria
#> 1 Marek   6100      HR         10       TRUE         TRUE    senior
#> 2 Zofia   5500 Finanse          5      FALSE         TRUE       mid
#> 3 Piotr   5200 Finanse          7       TRUE         TRUE       mid
#> 4 Kasia   4800      IT          2      FALSE        FALSE    junior
#> 5  Anna   4500      IT          3      FALSE        FALSE    junior
# Sortowanie po wielu kolumnach
arrange(pracownicy, dzial, desc(pensja))
#>    imie pensja   dzial lata_pracy czy_menedz doswiadczony kategoria
#> 1 Zofia   5500 Finanse          5      FALSE         TRUE       mid
#> 2 Piotr   5200 Finanse          7       TRUE         TRUE       mid
#> 3 Marek   6100      HR         10       TRUE         TRUE    senior
#> 4 Kasia   4800      IT          2      FALSE        FALSE    junior
#> 5  Anna   4500      IT          3      FALSE        FALSE    junior
# Sortowanie rosnąco
pracownicy.sort_values("pensja")

# Sortowanie malejąco
pracownicy.sort_values("pensja", ascending=False)

# Sortowanie po wielu kolumnach
pracownicy.sort_values(["dzial", "pensja"], ascending=[True, False])

4.3 Grupowanie i agregacja

NoteAnalogia do Excela

Grupowanie w R/Pythonie to odpowiednik tabeli przestawnej w Excelu — tylko że wynik to zwykła ramka danych, którą możesz dowolnie dalej przetwarzać.

# Podstawowa agregacja — średnia pensja według działu
pracownicy |>
  group_by(dzial) |>
  summarise(
    srednia_pensja = mean(pensja),
    mediana_pensja = median(pensja),
    liczba_osob    = n()
  )
#> # A tibble: 3 × 4
#>   dzial   srednia_pensja mediana_pensja liczba_osob
#>   <chr>            <dbl>          <dbl>       <int>
#> 1 Finanse           5350           5350           2
#> 2 HR                6100           6100           1
#> 3 IT                4650           4650           2
# Agregacja po wielu grupach
pracownicy |>
  group_by(dzial, kategoria) |>
  summarise(
    srednia  = mean(pensja),
    min_pens = min(pensja),
    max_pens = max(pensja),
    .groups  = "drop"   # usuwa grupowanie po agregacji
  ) |>
  arrange(dzial, srednia)
#> # A tibble: 3 × 5
#>   dzial   kategoria srednia min_pens max_pens
#>   <chr>   <chr>       <dbl>    <dbl>    <dbl>
#> 1 Finanse mid          5350     5200     5500
#> 2 HR      senior       6100     6100     6100
#> 3 IT      junior       4650     4500     4800
# Podstawowa agregacja
pracownicy.groupby("dzial")["pensja"].agg(
    srednia_pensja="mean",
    mediana_pensja="median",
    liczba_osob="count"
).reset_index()
# Agregacja po wielu grupach
(pracownicy
 .groupby(["dzial", "kategoria"])
 .agg(
     srednia  = ("pensja", "mean"),
     min_pens = ("pensja", "min"),
     max_pens = ("pensja", "max")
 )
 .reset_index()
 .sort_values(["dzial", "srednia"])
)

4.4 Łączenie tabel

NoteAnalogia do Excela

Łączenie tabel to odpowiednik funkcji WYSZUKAJ.PIONOWO (VLOOKUP) lub relacji w Power Pivot — ale bardziej elastyczne i odporne na błędy.

# Tabela z informacjami o działach
dzialy <- data.frame(
  dzial       = c("IT", "Finanse", "HR"),
  budzet      = c(500000, 350000, 200000),
  lokalizacja = c("Warszawa", "Kraków", "Warszawa")
)

# LEFT JOIN — wszyscy pracownicy + pasujące info o dziale
left_join(pracownicy, dzialy, by = "dzial")
#>    imie pensja   dzial lata_pracy czy_menedz doswiadczony kategoria budzet
#> 1  Anna   4500      IT          3      FALSE        FALSE    junior 500000
#> 2 Piotr   5200 Finanse          7       TRUE         TRUE       mid 350000
#> 3 Kasia   4800      IT          2      FALSE        FALSE    junior 500000
#> 4 Marek   6100      HR         10       TRUE         TRUE    senior 200000
#> 5 Zofia   5500 Finanse          5      FALSE         TRUE       mid 350000
#>   lokalizacja
#> 1    Warszawa
#> 2      Kraków
#> 3    Warszawa
#> 4    Warszawa
#> 5      Kraków
# INNER JOIN — tylko wiersze pasujące w obu tabelach
inner_join(pracownicy, dzialy, by = "dzial")
#>    imie pensja   dzial lata_pracy czy_menedz doswiadczony kategoria budzet
#> 1  Anna   4500      IT          3      FALSE        FALSE    junior 500000
#> 2 Piotr   5200 Finanse          7       TRUE         TRUE       mid 350000
#> 3 Kasia   4800      IT          2      FALSE        FALSE    junior 500000
#> 4 Marek   6100      HR         10       TRUE         TRUE    senior 200000
#> 5 Zofia   5500 Finanse          5      FALSE         TRUE       mid 350000
#>   lokalizacja
#> 1    Warszawa
#> 2      Kraków
#> 3    Warszawa
#> 4    Warszawa
#> 5      Kraków
# Dostępne typy: left_join, right_join, inner_join, full_join
# Tabela z informacjami o działach
dzialy = pd.DataFrame({
    "dzial":       ["IT", "Finanse", "HR"],
    "budzet":      [500000, 350000, 200000],
    "lokalizacja": ["Warszawa", "Kraków", "Warszawa"]
})

# LEFT JOIN
pd.merge(pracownicy, dzialy, on="dzial", how="left")
# INNER JOIN
pd.merge(pracownicy, dzialy, on="dzial", how="inner")

# Dostępne typy: how="left", "right", "inner", "outer"