R-ohjelmointi.org

Tilastotieteellistä ohjelmointia R-kielellä

Numero- ja merkkijonojen järjestäminen

Suomessa merkkijonojen aakkostaminen noudattaa kansallista standardia, jossa kaikki numerot aakkostetaan ennen kirjaimia, kirjaimet aakkostetaan koulusta tutulla tavalla a-ö, isot ja pienet kirjaimet ovat samanarvoisia ja välimerkit sekä tarkemerkit (kuten aksentti) jätetään huomiotta. Jälkimmäiseen sääntöön on tosin erikseen mainittuja poikkeuksia. Useammista sanoista koostuvat merkkijonot voidaan aakkostaa sanoittaisesti, jolloin sananväli tulee ennen kaikki muita merkkejä, tai kirjaimittaisesti, jolloin sananvälit jätetään huomiotta.

Tämä on Suomessa käytetty aakkostustapa, ja ihmiset olettavat aakkostuksen toimivan sen mukaisesti. Muissa maissa se voi olla, ja onkin erilainen, erityisesti kansallisten erikoismerkkien osalta. Esimerkiksi Virossa z aakkostetaan s:n ja t:n väliin. Muun muassa aakkostusjärjestyksen toteuttamista paikallisilla piirteillä höystettynä kutsutaan kotoistamiseksi.

R:ssä perusjärjestelyfunktiot (sort ja order) noudattavat pääasiassa järjestelmän kielisasetusta (locale). Tämä asettaa omat haasteensa ohjelmille, joiden tulisi toimia kaikkialla samalla tavalla. Erään näkökulman asiaan liittyvistä seikoista antaa Joel Spolsky.

R:ssä on mahdollista muuttaa kieliasetusta järjestelmän asetuksista riipumatta funktiolla Sys.setlocale(). Voimassaoleva asetus voidaan selvittää funktiolla Sys.getlocale():

Sys.getlocale()
[1] "LC_COLLATE=Finnish_Finland.1252;LC_CTYPE=Finnish_Finland.1252;LC_MONETARY=Finnish_Finland.1252;LC_NUMERIC=C;LC_TIME=Finnish_Finland.1252"
 
Sys.setlocale("LC_ALL", "Estonian")
[1] "LC_COLLATE=Estonian_Estonia.1257;LC_CTYPE=Estonian_Estonia.1257;LC_MONETARY=Estonian_Estonia.1257;LC_NUMERIC=C;LC_TIME=Estonian_Estonia.1257"

Molempien komentojen antamassa merkkijonossa esiintyvät LC_COLLATE, LC_TYPE, jne ovat POSIX:n kuvaamia kategorioita.

Miten locale sitten vaikuttaa merkkijonojen ja numeroiden järjestämiseen? Otetaanpa muutama esimerkki:

a<-c(letters, "å", "ä", "ö", "1", "2", "3", "1a", "a1", "a.1", "1.a")
 
#Suomalainen järjestys:
Sys.setlocale("LC_ALL", "Finnish")
sort(a)
# [1] "1"   "1.a" "1a"  "2"   "3"   "a"   "a.1" "a1"  "b"   "c"   "d"   "e"  
#[13] "f"   "g"   "h"   "i"   "j"   "k"   "l"   "m"   "n"   "o"   "p"   "q"  
#[25] "r"   "s"   "t"   "u"   "v"   "w"   "x"   "y"   "z"   "å"   "ä"   "ö" 
 
#Virolainen järjestys:
Sys.setlocale("LC_ALL", "Estonian")
sort(a)
# [1] "1"   "1.a" "1a"  "2"   "3"   "a"   "å"   "a.1" "a1"  "b"   "c"   "d"  
#[13] "e"   "f"   "g"   "h"   "i"   "j"   "k"   "l"   "m"   "n"   "o"   "p"  
#[25] "q"   "r"   "s"   "z"   "t"   "u"   "v"   "w"   "ä"   "ö"   "x"   "y"  
 
#Tavanomainen palvelinten järjestys:
Sys.setlocale("LC_ALL", "C")
sort(a)
# [1] "1"   "1.a" "1a"  "2"   "3"   "a"   "a.1" "a1"  "b"   "c"   "d"   "e"  
#[13] "f"   "g"   "h"   "i"   "j"   "k"   "l"   "m"   "n"   "o"   "p"   "q"  
#[25] "r"   "s"   "t"   "u"   "v"   "w"   "x"   "y"   "z"   "ä"   "å"   "ö"

Esimerkiksi suomalainen ja virolainen järjestys eroavat toisistaan, ja useilla palvelinkoneilla käytetty järjestys eroaa näistä molemmista. Locale C on kuitenkin se, joka tyypillisesti löytyy jokaisesta tietokoneesta, joten se lienee siinä mielessä turvallisin valinta. Mikään edellä mainituista ei kuitenkaan vastaa ihmisten odottamaa järjestystä. Aika lähelle odotettua tulosta kuitenkin pääsee gtools-paketin funktioilla:

#Tyypillinen ihmisten odottama järjestys:
library(gtools)
mixedsort(a)
# [1] "1"   "1.a" "2"   "3"   "1a"  "a1"  "a"   "a.1" "b"   "c"   "d"   "e"  
#[13] "f"   "g"   "h"   "i"   "j"   "k"   "l"   "m"   "n"   "o"   "p"   "q"  
#[25] "r"   "s"   "t"   "u"   "v"   "w"   "x"   "y"   "z"   "ä"   "å"   "ö"

Merkkijonojen erilainen järjestystapa kannattaa huomioida, jos on tarpeen tuottaa koodia, joka toimii kaikkialla samalla tavalla. Yhtenä vaihtoehtona on aina ennen järjestämistä vaihtaa locale:ksi ”C”, jonka pitäisi toimi aina samalla tavalla.