R-ohjelmointi.org

Tilastotieteellistä ohjelmointia R-kielellä

Lukujen pyöristäminen R:ssä

Desimaaliukujen pyöristämiseen liittyvät seikat ovat varsin yksiselitteisiä, paitsi siinä tapauksessa, että pyöristettävän luvun viimeinen numero katkaisun jälkeen on 5. Koulussa opetetaan lukujen pyöristämistapa, joka käskee pyöristämään numeroon 5 päättyvät luvut aina ylöspäin. Positiivisilla luvuilla asia on selvä: jos haluaisimme pyöristää luvun 1.5 lähimpään kokonaislukuun, olisi tulos säännön mukaan 2. Mutta mitä tarkoitetaan termillä ”ylöspäin”, kun puhutaan negatiivisista luvuista? Luku -1.5 voitaisiin pyöristää lukuun -1 tai -2 riippuen siitä, mitä ”ylöspäin” tarkoittaa.

Kaiken kaikkiaan numeroon 5 päättyvien lukujen pyöristämiseen on koko joukko erilaisia sääntöjä:

1. Pyöristä ylöspäin (ceiling, pyöristetään aina kohti +ääretöntä)
2. Pyöristä alaspäin (floor, pyöristetään aina kohti -ääretöntä)
3. Pyöristys kohti nollaa (jos positiivinen, pyöristetään alaspäin; jos negatiivinen, pyöristetään ylöspäin)
4. Pyöristys nollasta poispäin (jos positiivinen, pyöristetään ylöspäin; jos negatiivinen, pyöristetään alaspäin)
5. Pyöristys lähimpään parilliseen (pyöristetään lähimpään parilliseen lukuun)
6. Pyöristys lähimpään parittomaan (pyöristetään lähimpään parittomaan lukuun)
7. Sattumanvarainen pyöristys (pyöristetään satunnaiseen suuntaan)
8. Vuorotteleva pyöristys (pyöristetään vuorotellen alas- tai ylöspäin)
9. Euromääräisten maksujen pyöristäminen (eli ”älä pyöristä”, https://www.finlex.fi/fi/laki/ajantasa/2000/20000890)

Kohtien 1-8 algoritmeista on hyvä kuvallisilla esimerkeillä varustettu artikkeli ”Rounding Algorithms 101”. Se tarjoaa myös esimerkkejä liukulukulaskennasta.

Kohdan 5 menetelmää kutsutaan myös nimellä Pankkiirin pyöristys, ja se on oletusarvoinen pyöristystapa muun muassa R:n round()-funktiossa. Kyseinen menetelmä on kuvattu standardissa IEEE 754.

Monet yllä mainituista pyöristystavoista on helppo toteuttaa R:ssä, vaikkei kaikille olekaan olemassa valmista funktiota. Esimerkiksi paketin DescTools funktio RoundTo() ja plyr-paketin round_any() tarjoavat hyvän pohjan, mutta ne eivät tarjoa aivan kaikkia yllä olevia tapoja. Katsotaanpa miten kaikki eri tavat saa tarvittaessa toteutettua R:llä (tässä kahden desimaalin tarkkuuteen).

# Esimerkkiluvut
luku1 <- -1.345
luku2 <- 1.345
luvut <- c(luku1, luku2)
 
# R:n asetukset
options(digits=7)
options(scipen=32)
 
#1. Pyöristä ylöspäin (ceiling, pyöristetään aina kohti +ääretöntä)
round_half_up <- function(luvut) {
   round_any(luvut, 0.01, ceiling)
}
round_half_up(luvut)
#[1] -1.34  1.35
 
#2. Pyöristä alaspäin (floor, pyöristetään aina kohti -ääretöntä)
round_half_down <- function(luvut) {
   round_any(luvut, 0.01, floor)
}
round_half_down(luvut)
#[1] -1.35  1.34
 
#3. Pyöristys kohti nollaa (jos positiivinen, pyöristetään alaspäin; jos negatiivinen, pyöristetään ylöspäin)
round_half_towards_zero <- function(luvut, interval=0.01) {
   s <- sign(luvut)
   res <- floor(luvut/(interval*s))*(interval*s)
   res[luvut == 0] <- 0
   return(res)
}
round_half_towards_zero(luvut)
#[1] -1.34  1.34
 
#4. Pyöristys nollasta poispäin (jos positiivinen, pyöristetään ylöspäin; jos negatiivinen, pyöristetään alaspäin)
round_half_away_from_zero <- function(luvut) {
   s <- sign(luvut)
   res <- ceiling(luvut/(0.01*s))*(0.01*s)
   res[luvut == 0] <- 0
   return(res)
}
round_half_away_from_zero(luvut)
 
#5. Pyöristys lähimpään parilliseen (pyöristetään lähimpään parilliseen lukuun)
round_half_to_even <- function(luvut) {
   round(luvut, 2)
}
#[1] -1.34  1.34
 
#6. Pyöristys lähimpään parittomaan (pyöristetään lähimpään parittomaan lukuun)
round_half_to_odd <- function(luvut) {
   s <- sign(luvut)
   interval <- 0.02 * s
   offset <- interval / 2
   res <- (luvut - luvut %% interval) + offset
   res[luvut == 0] <- 0
   return(res)
}
round_half_to_odd(luvut)
#[1] -1.35  1.35
 
#7. Sattumanvarainen pyöristys (pyöristetään satunnaiseen suuntaan)
round_half_randomly <- function(luvut, seed=NULL) {
  if(!is.null(seed)) {
     set.seed(seed)
  }
  rn <- sample(c(TRUE, FALSE), length(luvut), replace=T)
  res <- rep(NA, length(luvut))
  res[rn] <- round_half_up(luvut[rn])
  res[!rn] <- round_half_down(luvut[!rn])
  return(res)
}
round_half_randomly(luvut)
#[1] -1.34  1.34
 
#8. Vuorotteleva pyöristys (pyöristetään vuorotellen alas- tai ylöspäin)
round_half_alternatingly <- function(luvut, seed=NULL) {
  rn <- rep(c(TRUE, FALSE), ceiling(length(luvut)/2)) [1:length(luvut)]
  res <- rep(NA, length(luvut))
  res[rn] <- round_half_up(luvut[rn])
  res[!rn] <- round_half_down(luvut[!rn])
  return(res)
}
round_half_alternatingly(luvut)
#[1] -1.34  1.34

Yllä olevat funktiot on testattu vain pintapuolisesti, eikä niiden täydellisestä toimivuudesta ole takuita. Käyttäkää siis omalla vastuullanne! Kuten huomaatte, funktiot eivät ole täysin yhtenevästi toteutettuja, joten käyttäjän vastuulle jää luonnollisesti myös tarkempien syötteiden tarkistusten toteuttaminen ym.

Sekä kohtien 4 että 5 menetelmiä käytetään muun muassa finanssimaailmassa. Esimerkiksi Euroon siiirryttäessä on annettu asetus, jossa määritellään, että vaihtokurssit on keskivälintapauksissa (siis numeroon 5 päättyvissä tapauksisa) pyöristettävä ylöspäin. Numeerisessa laskennassa pankkiirin pyöristyksellä on eräitä hyviä ominaisuuksia, joiden vuoksi se on siellä tyypillisesti valittu menetelmä.


Category