Kurzusok
/
R
R
/
Feladatok
/
Fák és tibble-ációk
Fák és tibble-ációk

Fák és tibble-ációk

Tanulófeladat

Bevezetés

A tanterv más részein többféle, különböző tulajdonságokkal rendelkező adattípussal találkoztunk.

  • Az atomi vektorok egydimenziósak, és típusukra nézve homogének.
  • A listák egydimenziósak, elemeik pedig heterogén típusúak lehetnek.
  • A mátrixok és a tömbök többdimenziósak és homogének.

Ez a fogalom azzal foglalkozik, hogyan tárolhatunk többdimenziós, heterogén adatokat. A gyakorlatban a valós adatok többsége ilyen, így most eljutunk ahhoz a lényeghez, hogyan használják (többnyire) az R-t a gyakorlatban.

A dataframe változatai

Az évtizedek során az R több adattípust is kapott a táblázatos adatok kezelésére.

Ez a tanterv elsősorban a tibble típussal foglalkozik, de hasznos ismerni az alternatívákat.

A data.frame

A Base R-ben a data.frame egyenlő hosszúságú vectors-ökből álló list.

Ez egy téglalap alakú adattáblának tekinthető, amelyben minden oszlop homogén, de minden sor különböző típusú adatokat tartalmazhat (és általában tartalmaz is).

Nézzünk egy példát, amely ezt szemlélteti:

# create the column vectors
languages <- c("Fortran", "R", "Python", "Julia")
created <- c(1957, 1993, 1991, 2012)
has.syllabus <- c(FALSE, TRUE, TRUE, TRUE)

# join columns to create the dataframe
df <- data.frame(languages, created, has.syllabus)
df
#>   languages created has.syllabus
#> 1   Fortran    1957        FALSE
#> 2         R    1993         TRUE
#> 3    Python    1991         TRUE
#> 4     Julia    2012         TRUE

Van egy karakterláncokból álló oszlopunk, egy számokból álló oszlopunk és egy logikai értékekből álló oszlopunk. Nagyobb léptékben ez intuitív módja annak, hogy valós adatok sokféle gyűjteményét ábrázoljuk.

A tibble

A data.frame kialakítása régi.

A több évtizedes tapasztalat és az R használatának változó mintázatai egy újratervezéshez vezettek, amely modernizált alternatívát hozott létre a Tidyverse-ben: a tibble-t.

A Base R-hez képest a tibble a következőket nyújtja:

  • Más alapértelmezett beállításokat, hogy csökkentse a gyakori problémákat.
  • Kisebb hajlandóságot az adattípusok automatikus konverziójára a bemenet során.
  • Több és világosabb hibaüzenetet.
  • Más, általában jobb megjelenítési formátumokat.

Röviden, a tibble célja, hogy kevesebbet tegyen és többet panaszkodjon, amit úgy is szoktak jellemezni, hogy lusta és morcos.

A típusok általában felcserélhetők: bármely függvény, amely elfogad egy data.frame-et, elfogad egy tibble-t is, és fordítva.

A típusok közötti átalakítás egyszerű: as_tibble(df) és as.data.frame(tbl).

Új munka esetén a tibble használata valószínűleg segít robusztusabb kódot írni. Az R világában azonban rengeteg örökölt kód és örökölt adat van, így a data.frame valószínűleg még sokáig gyakori marad.

# column vectors are same as for data.frame
library(tibble)
tbl <- tibble(languages, created, has.syllabus)
tbl
  # A tibble: 4 × 3
#>   languages created has.syllabus
#>   <chr>       <dbl> <lgl>       
#> 1 Fortran      1957 FALSE       
#> 2 R            1993 TRUE        
#> 3 Python       1991 TRUE        
#> 4 Julia        2012 TRUE      

Figyeld meg az alapértelmezett nyomtatási formátumot: a méreteket tartalmazó megjegyzéssor automatikusan kiíródik, és az oszloptípusok is megjelennek.

Tibble-ek használata

A tibble-ek a Tidyverse alapelemei, ezért a library(tibble) vagy a library(tidyverse) hívással töltheted be őket.

Tibble létrehozása

A legegyszerűbben a tibble() függvénnyel fűzhetjük össze az oszlopvektorokat, ahogyan a data.frame() esetében is. Erre egy korábbi szakaszban láttunk példát.

Ha kényelmesebb soronként megadni az értékeket, a megfelelő függvény a tribble().

A gyakorlatban tucatnyi módja van a tibble-ek létrehozásának, mivel számos Tidyverse-függvény alapértelmezett kimeneti formátuma.

Egy tibble kezelése

A funkcionális programozás fogalom a purrr könyvtárral foglalkozott, amellyel vektorokat és listákat (egydimenziós adatszerkezeteket) kezelhetünk.

Dataframe-ek (akár hagyományosak, akár tibble-ek) esetében a megfelelő könyvtár a dplyr.

Részhalmazok kiválasztása

A dataframe-ek, köztük a tibble-ek is, oszlopvektorok listájaként kezelhetők, így a lista indexelésével egy adott oszlopot kapunk vissza.

tbl
# A tibble: 4 × 3
#>   languages created has.syllabus
#>   <chr>       <dbl> <lgl>       
#> 1 Fortran      1957 FALSE       
#> 2 R            1993 TRUE        
#> 3 Python       1991 TRUE        
#> 4 Julia        2012 TRUE    

tbl$created
#> [1] 1957 1993 1991 2012

A dataframe mátrixstílusú indexeléssel is indexelhető.

tbl[c(2, 4), 1:2]
  # A tibble: 2 × 2
#>   languages created
#>   <chr>       <dbl>
#> 1 R            1993
#> 2 Julia        2012

A modern R-ben, a Tidyverse ökoszisztémával a dplyr függvények általában rugalmasabbak és kényelmesebbek, és ezek állnak a fogalom hátralévő részének középpontjában.

Oszloponkénti műveletek

Egyetlen oszlopot a pull() függvénnyel kaphatsz meg, a nevével vagy a sorszámával (negatív számok jobbról balra számolnak).

tbl |> pull(created)
#> [1] 1957 1993 1991 2012

Ez ugyanaz az eredmény, mint a tbl$created esetében, csak egy pipe-barát függvénnyel.

Több oszlop kiválasztásához a megfelelő függvény a select(), amely rendkívül sokoldalú. Oszlopokat kaphatsz meg (vagy ejthetsz el) a nevük vagy típusuk tulajdonságai alapján.

# Range with position and/or name
tbl |> select(1:created)
  # A tibble: 4 × 2
#>   languages created
#>   <chr>       <dbl>
#> 1 Fortran      1957
#> 2 R            1993
#> 3 Python       1991
#> 4 Julia        2012      

# Use type of column
tbl |> select(where(is.numeric))
  # A tibble: 4 × 1
#>   created
#>     <dbl>
#> 1    1957
#> 2    1993
#> 3    1991
#> 4    2012

Több feltétel is megadható a &, | és ! logikai operátorokkal (és, vagy, nem).

Ez a lehetőség meglehetősen nevetségesnek tűnik a nyelveket tartalmazó játékdataframe-ünkkel. Szerencsére a starwars tibble benne van a dplyr-ben, így van mivel nagyobbat gyakorolni.

# pick a subset of columns
starwars |> 
  select(name | ends_with("color")) |> 
  head(5)
  # A tibble: 5 × 4
#>   name           hair_color skin_color  eye_color
#>   <chr>          <chr>      <chr>       <chr>    
#> 1 Luke Skywalker blond      fair        blue     
#> 2 C-3PO          NA         gold        yellow   
#> 3 R2-D2          NA         white, blue red      
#> 4 Darth Vader    none       white       yellow   
#> 5 Leia Organa    brown      light       brown    

Soronkénti műveletek

Sorokat kapsz meg bizonyos feltételeknek megfelelően a filter() függvénnyel, vagy kizárhatod őket a filter_out() függvénnyel.

starwars |> 
  select(name:mass) |> 
  filter(between(height, 150, 165) & !is.na(mass))
  # A tibble: 4 × 3
#>   name               height  mass
#>   <chr>               <int> <dbl>
#> 1 Leia Organa           150    49
#> 2 Beru Whitesun Lars    165    75
#> 3 Nien Nunb             160    68
#> 4 Ben Quadinaros        163    65

A szűrési feltételek tetszőlegesen összetettek lehetnek, de mindig a sorok tartalmán alapulnak.

Ha ismertek a sorok számai, többféle slice() függvényt használhatunk.

starwars |> 
  select(name | homeworld) |> 
  slice(20:25)
  # A tibble: 6 × 2
#>   name             homeworld
#>   <chr>            <chr>    
#> 1 Palpatine        Naboo    
#> 2 Boba Fett        Kamino   
#> 3 IG-88            NA       
#> 4 Bossk            Trandosha
#> 5 Lando Calrissian Socorro  
#> 6 Lobot            Bespin   

Egy tibble módosítása

Első figyelmeztetés: a copy-on-modify alapértelmezés miatt az eredeti tibble általában változatlan marad.

A legtöbb módosítás oszloponként történik.

Az oszlopnevek a rename(newname = oldname) hívással változtathatók meg, vagy a rename_with() függvénnyel, hogy egy függvényt alkalmazzunk. Figyeld meg a rename() szintaxisát. Az oldname oszlop tartalma a newname névhez kötődik, innen ered a sorrend.

Az oszlopok sorrendje a relocate() függvénnyel változtatható meg. A megadott oszlop(ok) alapértelmezés szerint a legbaloldalibb pozícióba kerülnek, de a .before vagy .after argumentummal finomabb pozicionálás is elérhető.

sw <- starwars |> select(name:species) |> slice(1:4)
sw
  # A tibble: 4 × 11
#>   name           height  mass hair_color skin_color  eye_color birth_year sex   gender    homeworld species
#>   <chr>           <int> <dbl> <chr>      <chr>       <chr>          <dbl> <chr> <chr>     <chr>     <chr>  
#> 1 Luke Skywalker    172    77 blond      fair        blue            19   male  masculine Tatooine  Human  
#> 2 C-3PO             167    75 NA         gold        yellow         112   none  masculine Tatooine  Droid  
#> 3 R2-D2              96    32 NA         white, blue red             33   none  masculine Naboo     Droid  
#> 4 Darth Vader       202   136 none       white       yellow          41.9 male  masculine Tatooine  Human  

sw |> relocate(c(species, homeworld), .after = name)
  # A tibble: 4 × 11
#>   name           species homeworld height  mass hair_color skin_color  eye_color birth_year sex   gender   
#>   <chr>          <chr>   <chr>      <int> <dbl> <chr>      <chr>       <chr>          <dbl> <chr> <chr>    
#> 1 Luke Skywalker Human   Tatooine     172    77 blond      fair        blue            19   male  masculine
#> 2 C-3PO          Droid   Tatooine     167    75 NA         gold        yellow         112   none  masculine
#> 3 R2-D2          Droid   Naboo         96    32 NA         white, blue red             33   none  masculine
#> 4 Darth Vader    Human   Tatooine     202   136 none       white       yellow          41.9 male  masculine

Nagyobb változtatásokhoz a mutate() a következőket teszi lehetővé:

  • Új oszlopok létrehozása, amelyek a meglévő oszlopok függvényei.
  • Meglévő oszlop lecserélése úgy, hogy egy ugyanilyen nevű új oszlopot hozunk létre.
  • Oszlop törlése úgy, hogy az értékét NULL-ra állítjuk.

Nyilvánvalóan a mutate() hatékony, esetleg zavaró is lehet, és ok arra, hogy hálásak legyünk a copy-on-modify-nak.

Nincs nyilvánvaló oka annak, hogy a Star Wars-szereplők testtömegindexével törődjünk, de azért csak:

starwars |> 
  select(c(name, species, height, mass)) |> 
  mutate(BMI = mass / (height / 100)^2) |> 
  head(4)
  # A tibble: 4 × 5
#>   name           species height  mass   BMI
#>   <chr>          <chr>    <int> <dbl> <dbl>
#> 1 Luke Skywalker Human      172    77  26.0
#> 2 C-3PO          Droid      167    75  26.9
#> 3 R2-D2          Droid       96    32  34.7
#> 4 Darth Vader    Human      202   136  33.3

A soronkénti műveletek kevésbé gyakoriak egyetlen tibble módosításakor (több tibble egyesítését egy későbbi fogalom tárgyalja).

Egy kivétel: az arrange() sorokat rendez egy vagy több oszlop értékei szerint.

tbl
#>       A tibble: 4 × 3
#>   languages created has.syllabus
#>   <chr>       <dbl> <lgl>       
#> 1 Fortran      1957 FALSE       
#> 2 R            1993 TRUE        
#> 3 Python       1991 TRUE        
#> 4 Julia        2012 TRUE  

tbl |> arrange(languages)
#>       A tibble: 4 × 3
#>   languages created has.syllabus
#>   <chr>       <dbl> <lgl>       
#> 1 Fortran      1957 FALSE       
#> 2 Julia        2012 TRUE        
#> 3 Python       1991 TRUE        
#> 4 R            1993 TRUE     

Utasítások

Egy nagy cseresznyeültetvénynek általában van egy kisebb készlete felesleges fából, amelyet fűrészáruként szeretne felajánlani. Ez segít egészségesen tartani az ültetvényt, miközben plusz bevételt is hoz. Arra kértek, hogy segíts kódot írni néhány funkcióhoz, amelyek segítenek a fák kiválasztásában, amikor egy potenciális vevő telefonál.

Note

A beépített datasets könyvtárban sok játékadatkészlet található, és ez a feladat a trees adatkészletet használja. Az adatkészlet eléréséhez egyszerűen használd a trees nevet.

library(datasets) # loading is optional

trees |> head(3)
#>    A data.frame: 3 × 3 	
#>   Girth	Height	Volume
#>	 <dbl>	 <dbl>	 <dbl>
#> 1   8.3	    70	  10.3  
#> 2   8.6	    65	  10.3
#> 3   8.8	    63	  10.2

Figyeld meg a három oszlopot: Girth, Height és Volume

Az adatkészlet kezdetben data.frame formában van, de választhatod azt is, hogy tibble-ként dolgozol vele.

1. Nevezz át egy oszlopot

Sajnos volt egy adminisztrációs hiba, amelyet soha nem javítottak ki: az adatkészlet egyik oszlopa Girth néven szerepel, pedig valójában ez a Diameter.

Definiálj egy tree_data nevű data.frame/tibble-t, amely a trees Girth oszlopának nevét Diameter-re javítja.

head(tree_data, 3)
#>     A tibble: 3 × 3 
#> Diameter	 Height	Volume
#>    <dbl>	  <dbl>	 <dbl>
#>      8.3	     70	  10.3
#>      8.6	     65	  10.3
#>      8.8	     63	  10.2

2. Adj hozzá Girth és Weight oszlopokat

A Girth (vagyis a kerület) továbbra is hasznos mérés, és a fa súlya is az. A Girth könnyen kiszámítható a πD képlettel, ahol D az átmérő. A teljes fa súlya a ρV képlettel számítható ki, ahol ρ = 35 a cseresznyefa sűrűsége, V pedig a fa térfogata.

Definiáld a girth_n_weight(data, rnd_digits) függvényt, amely egy adatkeretet és numerikus argumentumokat vár. A függvény egy új adatkeretet adjon vissza két további oszloppal: Girth és Weight, a rnd_digits által megadott számú tizedesjegyre kerekítve.

girth_n_weight(tree_data, 1) |> head(3)
#>           A tibble: 3 × 5 
#> Diameter  Height	 Volume	 Girth	Weight
#>    <dbl>	  <dbl>	  <dbl>	 <dbl>	 <dbl>
#>      8.3	     70	   10.3	  26.1	 360.5
#>      8.6	     65	   10.3	  27.0	 360.5
#>      8.8	     63	   10.2	  27.6	 357.0

3. Az ültetvény másolata az adatkészletről

Minden potenciális vevő számára az ültetvény egy külön másolatot tart az adatkészletből, hogy segítse az eladást. Ebben az adatkészletben a legfontosabb oszlopok, a Weight és a Height, előre vannak mozgatva, és az adatok Weight szerint vannak rendezve.

Definiáld az orchard_copy(data, important_cols) függvényt, amely bemenetként egy adatkeretet kap. Ez egy új adatkeretet adjon vissza, amelyben a Weight és a Height előre van mozgatva, a sorok pedig Weight szerint vannak rendezve.

tree_data |> girth_n_weight() |> orchard_copy() |> head(3)
#>            A tibble: 3 × 5 
#> Weight	Height	Diameter	Volume	Girth
#>  <dbl>	 <dbl>	   <dbl>	 <dbl>	<dbl>
#>    357	    63	       9	    10	   28
#>    360	    70	       8	    10	   26
#>    360	    65	       9	    10	   27

4. A vevő másolata az adatkészletről

Minden potenciális vevő az igényei alapján egy személyre szabott változatot kap az adatkészletből. Ehhez a vevő megadhatja a minimális magasságot, a maximális magasságot és bármely egyetlen fa maximális súlyát.

Definiáld a customer_copy(data, min_height, max_height, max_weight) függvényt. Add vissza az adatkeret egy vevői másolatát Height, Weight, Diameter és Girth oszlopokkal, csak azokkal a sorokkal, amelyek megfelelnek a feltételeknek.

tree_data |> 
    girth_n_weight(1) |> 
    orchard_copy() |>
    customer_copy(65, 75, 1500) |> 
    head(3)
#>        A tibble: 3 × 4 
#> Height	Weight	Diameter	Girth
#>  <dbl>	 <dbl>	   <dbl>	<dbl>
#>     70	 360.5	     8.3	 26.1
#>     65	 360.5	     8.6	 27.0
#>     66	 546.0	    11.0	 34.6
Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg
R Exercism

Készen állsz elkezdeni a(z) Fák és tibble-ációk feladatot?

Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) R nyelvet 21 fogalom111 feladat segítségével, valódi emberi mentorálással, mindez ingyen.