Tilastollisen tietojenkäsittelyn seminaari 1998-1999

Aggregointisukro /AGGRE

Kimmo Vehkalahti 22.9.1998

Taustaa

Juha Puranen kertoi taannoin aineistojen aggregoinnin tuottavan joillekin data-analyysin opiskelijoille kohtuuttomia hankaluuksia ja toivoi, että tehtävää voitaisiin jollain tavoin helpottaa. Survossahan on aggregointiin kaksikin operaatiota, FILE AGGRE ja FILE AGGR.

FILE AGGRE

FILE AGGRE on vanhempaa perua. Sen avulla havaintoja yhdistellään laskemalla keskiarvoja tai summia. Aggregoidun tiedoston rakenne on täsmälleen sama kuin alkuperäisen. Tämä tietää aloittelijalle vaikeuksia, mikäli esimerkiksi lasketaan keskiarvoja yhden tai kahden tavun muuttujista. Myös se, että kaikki muuttujat kopioidaan vaikka vain aktiiviset aggregoidaan, aiheuttaa päänvaivaa: varsinaiset aggregoidut tulokset voivat jäädä jonnekin epäoleellisen muuttujajoukon keskelle. Puuttuvat havainnot tuovat FILE AGGRE:n käyttöön vielä omat ongelmansa. Kokenut Survo-käyttäjä väistää tietysti nämä karikot, mutta aloittelija voi aikaansaannoksistaan hämmentyä.

FILE AGGR

Uudempi, vasta vuodesta 1993 mukana kulkenut FILE AGGR tarjoaa aggregointiin aivan erilaisen lähestymistavan. Siinä jokainen valittu muuttuja voidaan aggregoida eri tavalla, vaikka useammallakin tavalla samaan aikaan, ja jopa valita lähtöaineiston havaintoja ehdollisesti jokaista aggregointia kohden. Missään muussa Survon toiminnossa ei ole käytettävissä niin paljon erilaisia aineiston valintaa koskevia vaihtoehtoja kuin FILE AGGR:issa.

Runsaan tarjonnan vuoksi FILE AGGR -operaation syntaksikin on vaativampi. Aggregointisäännöt kuvataan erityisellä VARIABLES-kaaviolla, johon on rivejä kirjattava vähintään niin monta kuin halutaan muuttujia uuteen tiedostoon. Myös tulosmuuttujien tyypit voidaan säätää muuttujakohtaisesti, joskin operaatio osaa myös itse tehdä järkeviä päätelmiä, esimerkiksi valita keskiarvoille soveltuvan neljän tavun muuttujatyypin.

FILE AGGR:in varsinainen runsaudensarvi on kuitenkin sen tuntemien aggregointifunktioiden valikoima. Summien ja keskiarvojen lisäksi voidaan laskea ja käsitellä havaintojen lukumääriä, keskihajontoja, minimejä, maksimeja, aggregaatin ensimmäisiä ja viimeisiä arvoja, puuttuvia havaintoja, mediaaneja tai yleisemmin fraktiileja, järjestysnumeroita, muiden muuttujien arvoja, trimmattuja keskiarvoja, korrelaatioita ja jopa yhden muuttujan regressiomalleja! Järjestystunnuslukujen käsittelystä johtuen aineisto täytyy järjestää ennen aggregointia. Tämä täytyy käyttäjän tehdä FILE SORT -operaatiolla. Pienenä miinuksena FILE AGGR:in kohdalla voidaan pitää sitä, että se ei millään tavoin ota kantaa muuttujakuvauksiin; uudet muuttujat esiintyvät paljaina, vaikka niitä voisi kommentoida esimerkiksi tiedolla mistä muuttujasta ne on saatu ja millä funktiolla.

Periaate

Molemmissa aggregointiohjelmissa on puolensa, mutta aloittelijan kannalta olisi mukavaa, jos aggregointi sujuisi yhtä helposti kuin FILE AGGRE:lla ja toimisi yhtä hyvin kuin FILE AGGR:illa. Missään tapauksessa ei kannata alkaa pohtia kummankaan operaation kohentamista, sillä Survon perusperiaatteiden mukaisesti olemassaolevia moduleita ei muuteta; ainoastaan voidaan lisätä täsmennyksiä, joilla voidaan vanha moduli ohjata tarvittaessa uusille urille. Se ei tässä kuitenkaan auta mitään, sillä aloittelijaa ei paljoa helpota, jos täytyy lisäksi muistaa kirjoittaa jotain lisätäsmennyksiä.

Kuinka kannattaa toimia

Onneksi ratkaisu on yhtä lähellä kuin lähin toimituskenttä: tämänkaltaiset ongelmat ovat omiaan sukrokielellä ratkaistaviksi. Idea on yksinkertaisesti "liimata" olemassaolevia operaatioita toisiinsa ja tarjota tällä tavoin käyttäjälle helpotusta. Tällaisten sukrojen tekeminen ei ole edes kovin vaikeaa. Kokeneena Survo-käyttäjänä ja sukrojen tekijänä oheisen /AGGRE-sukron laatiminen ei kestänyt paria tuntia kauempaa. Enemmän aikaa kului sen jälkeen käyttöohjeen viimeistelemiseen, sukron testaamiseen ja pieneen viilaamiseen.

Sukrolle kannattaa tekovaiheessa varata oma hakemisto ja toimituskenttä, kuten alla D:\S\AGGRE\AGGRE.EDT. Se selkiyttää työskentelyä, testausta ja varmuuskopioiden tekoa. Kun vielä laittaa kentän viitelistaan tai työvalikkoon, niin pääsee nopeasti työn ääreen. Sukronhan voi tallettaa muuallekin, kuten tässäkin esimerkissä tehdään. Testitilanteita kannattaa kerätä talteen, joko kentän loppuun tai erilliseen testauskenttään. Kun tekee muutoksia sukrokoodiin, kannattaa aina ajaa testit läpi, vaikka olisikin "varma", että koodi nyt toimii. Testien ajaksi on usein hyödyllistä säätää sukro askeltavaan moodiin, jolloin ehtii varmasti nähdä missä mennään kiville. Toiminnan näkymisen piilottavaa {disp off} -koodia ei tietenkään kannata käyttää niin kauan kuin sukro on työn alla. Lopuksi sitä voi {disp reset} -parinsa kanssa lisäillä harkinnan mukaan, jos haluaa rauhoittaa ajonaikaista näkymää. Koodia {disp on} tulee käyttää vain grafiikkatilasta poistumiseen, joten sitä ei tässä tarvita lainkaan.

Toimituskenttä, jossa sukrokoodia kehitetään, on muistettava tallettaa. Sukrokoodin talletus (TUTSAVE-komennolla) on sekin tärkeää, mutta vasta kun alkaa testata sukron toimintaa. Toimituskentän ominaisuuksia kannattaa hyödyntää, esimerkiksi värjätä tärkeitä kohtia koodista varjomerkkien avulla. Tässä FORM-nappi (F5) ja etenkin sukro /S ovat hyödyksi kuten yleensäkin. Varjomerkit ovat vain omaksi hyödyksi, TUTSAVE ei niistä piittaa.

Tämän lyhyen johdannon jälkeen voinemme yrittää siirtyä sukrokoodin tarkasteluun. Usein kannattaa ottaa jokin olemassaoleva sukro pohjaksi ennemmin kuin alkaa kirjoittaa koodia tyhjästä. Tyypillisesti otan lähtökohdakseni jonkin viimeaikaisen sukroni, tällä kertaa päädyin keväällä tekemääni /LOGREG-sukroon. Ei siitä paljoa tainnut jäädä loppujen lopuksi jäljelle, mutta tietyt perusrakenteet kyllä. Niiden kirjoittaminen joka kerta uudelleen on turhauttavaa ja virhealtista.

/AGGRE-sukro ja kootut selitykset

Sukrokoodi alkaa parametrien tarkistuksilla. Koska päädyin valitsemaan komennon syntaksin FILE AGGR:in perusteella, sain varautua tutkimaan hieman BY- ja TO-sanoja. Kuten huomataan, sukro on riveillä 16 ja 20 sallivainen kelpuuttaessaan myös pienellä kirjoitetut välisanat. Sekamuotoja ei kuitenkaan suvaita (ei pidä mennä liiallisuuksiin).

   1  1 SURVO 98  Mon Sep 21 11:18:35 1998          D:\S\AGGRE\   1000   80 0
   1 *SAVE AGGRE / aggregointi helpommaksi
   2 *LOAD PRINT / koodin tulostus
   3 *DD C:\E\S
   4 *
   5 *TUTSAVE C:\E\S\AGGRE / talletus suoraan Survon yleiseen S-hakemistoon
   6 /AGGRE <data> BY <aggr_variable> TO <new_data_file> 
   7 / K.Vehkalahti 13.9.98 (20.9.98)
   8 / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   9 *{tempo 2}{tempo -1}{init}
  10 - if W1 '=' Return then goto Back
  11 *{save stack}{W1=AGGRE}{call SUR-SAVE}{del stack}{load stack}{break on}
  12 - if W1 '=' ? then goto Help
  13 - if W1 '=' (empty) then goto Help
  14 - if W5 '=' {} then goto Help
  15 - if W2 '=' BY then goto To
  16 - if W2 '=' by then goto To
  17 *{goto ERR1}
  18 + To:
  19 - if W4 '=' TO then goto Start
  20 - if W4 '=' to then goto Start
  21 *{goto ERR2}

Jos kyseessä on paluu väliaikaisesta kentästä (parametrina Return), kenttää ei aleta uudelleen tallettaa (tällöin menetettäisiin mahdollisuus palata aiempaan työhön) vaan suoritetaan nöyrästi paluu Back-kohdan kautta. Muissa tapauksissa talletetaan nykyinen kenttä väliaikaisten tiedostojen levylle nimellä AGGRE. Rivi 11 toistuu useimmissa sukroissa kentän nimeä lukuunottamatta täsmälleen samanlaisena. Aivan alussa, rivillä 9 ehdittiin tätä ennen jo nopeuttaa vauhti äärimmilleen ja varmistaa sukrolle jatkon kannalta järkevät olosuhteet.

Mikäli parametreja ei ole annettu riittävästi tai laisinkaan, tai on annettu kysymysmerkki, johdatetaan käyttäjä avustustekstin äärelle. Jos sen sijaan em. BY- tai TO-sanoissa on jotain vialla, mennään virheilmoitusten puolelle. Jos kaikki näyttää olevan kunnossa, päästään aloittamaan varsinainen työ kohdasta Start.

Avustustekstiä varten pohjustetaan riittävän kokoinen toimituskenttä, johon teksti sitten kirjoitetaan. Riveistä ei kannata tehdä liian pitkiä, jotta ne TUTLOAD-komennolla tai DD:lläkin näyttäisivät hyvältä. Sivua vaihtaessaan sukro ottaa talteen tekstiblokkina komennon mallin kentän toiselta riviltä. Sitä se tulee hyödyntämään lopuksi End1-kohdassa.

   1  1 SURVO 98  Mon Sep 21 11:18:35 1998          D:\S\AGGRE\   1000   80 0
  22 + Help: {line start}{erase}{erase}INIT 100 100 30{act}{R}
  23 / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  24 */AGGRE <data> BY <aggr_variable> TO <new_data_file>             {R}
  25 *combines observations in a Survo data file according to values  {R}
  26 *of <aggr_variable>. Only active variables will be processed an  {R}
  27 *saved to <new_data_file>. Also <aggr_variable> must be active.  {R}
  28 *The <data> is automatically sorted according to <aggr_variable>.{R}
  29 *If <new_data_file> already exists, it is overwritten.           {R}
  30 *                                                                {R}
  31 *Sucro /AGGRE itself is a combination of existing Survo modules, {R}
  32 *namely FILE operations SORT, AGGR, COPY, STATUS and UPDATE,     {R}
  33 *offering an easy way for the most typical applications. For more{R}
  34 *demanding tasks, FILE AGGR includes several additional options. {R}
  35 *                                                                {R}
  36 *The observations having the same value in <aggr_variable> are by{R}
  37 *default combined (aggregated) by computing the means. However,  {R}
  38 *aggregation may be controlled by a specification AGGRE having   {R}
  39 *the value of SUM, MEAN, MEDIAN, MIN, MAX or NMISS.              {R}
  40 *The observations are then combined either by summing the values,{R}
  41 *computing the means, medians, minimum or maximum values or by   {R}
  42 *summing the number of missing observations.                     {R}
  43 / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  44 *{W1=        }{W1=W1&Continued on the next page... press ENTER!}
  45 *{message W1}
  46 - on key
  47 -    key _: continue
  48 -   wait 600
  49 *{jump 1,1,1,1}{R}{block}{block}{line end}{block}{erase}
  50 *{line start}SCRATCH{erase}{act}{home}
  51 / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  52 *The frequencies of the groups defined by <aggr_variable> are    {R}
  53 *saved in variable 'FREQ'. An alternative name may be given by   {R}
  54 *specification FREQ=<name_of_variable>.                          {R}
  55 *                                                                {R}
  56 *The IND, CASES and SELECT can be used to select observations.   {R}
  57 *The type (1,2,4 or 8) of new variables can be given by          {R}
  58 *TYPE=<type>. Default is TYPE=4.                                 {R}
  59 / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  60 *{W1=        }{W1=W1&Press ENTER!}{message W1}
  61 - on key
  62 -    key _: continue
  63 -   wait 600
  64 *{goto End1}

Sellaisilla kommenttiriveillä, jotka alkavat def-sanalla, on erityinen merkitys: ne määrittelevät sukromuistipaikoille nimet. Vain erittäin harvoin on syytä käyttää raakoja nimiä W1, W2, jne. Kunnon nimet kertovat selvästi mistä on kysymys missäkin tilanteessa. /AGGRE:n parametrit varaavat ensimmäiset viisi muistipaikkaa, joista on tässä nimetty joka toinen. Sanoja BY ja TO vastaavat paikat on jätetty käyttämättä, vaikka ne olisi voinut ottaakin mihin tahansa käyttöön. Seuraavat kolme (Wx, Wy ja Wz) ovat muistipaikkoja, joita /AGGRE käyttää moniin erilaisiin tehtäviin.

Tyylinäni on nimetä sukron tuntemien täsmennysten tarvitsemat muistipaikat ao. täsmennyssanoilla, isoilla kirjoitettuina, tosin tyylini näyttää horjuvan MASK- ja VARS-täsmennysten osalta. Joka tapauksessa täsmennysten mahdolliset arvot talletetaan heti aluksi {save spec} -koodeilla ennen kuin ne häviävät kentän tyhjennyksen yhteydessä. Väliaikaisten tiedostojen hakemisto otetaan talteen omalla koodillaan. Sitä tullaan tarvitsemaan, kun tiedostoja aletaan käsitellä. Koskaan ei saisi väliaikaisia tiedostoja tehdä käyttäjän hakemistoihin, poikkeuksena ehkä matriisitiedostot, jotka kuitenkin pitäisi nimetä niin että ne voi lopuksi tuhota.

Koska /AGGRE-sukro toimii sekä SURVO 84C:ssä että SURVO 98:ssa (muttei kuitenkaan SURVOS-versiossa), tutkitaan missä Survossa ollaan ja toimitaan sen mukaisesti. Pyritään tekemään riittävän suuri kenttä; leveyssuunnassa vaatimuksen asettavat mahdolliset VARSja MASK-täsmennykset - pituussuunnassa aktiivisten muuttujien lukumäärä. Niitä ei kuitenkaan tässä mittailla vaan kentän haluttu koko on pelkkä arvio. Yleensä tällaisissa sukroissa ei pidä luottaa käyttäjän nykyiseen kenttään vaan kannattaa tehdä INIT-komennolla uusi.

Kun uusi kenttä on luotu, sen alkuun kirjoitetaan paluukomento siltä varalta, että jotain menee pieleen ajon aikana ja käyttäjä jää työkenttään. Paluumahdollisuus aiempaan pitää varmistaa, sillä käyttäjä ei ole välttämättä edes tallettanut kenttäänsä vaan lähtenyt uhkarohkeasti kokeilemaan hänelle ennestään tuntematonta sukroa. Näin ei tietysti kannattaisi tehdä vaan aina pitäisi ensin laittaa työ talteen ja sitten vasta antautua seikkailemaan. Tämä on se tapa, jolla Survossa ns. undo- eli eiku-toiminto on aina käytettävissä.

   1  1 SURVO 98  Mon Sep 21 11:18:35 1998          D:\S\AGGRE\   1000   80 0
  65 / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  66 / def Wdata=W1 Waggrvar=W3 Wnewdata=W5 Wx=W6 Wy=W7 Wz=W8 Wtmpd=W9
  67 / def Wtmp1=W11 Wtmp2=W12 Wline=W13 Wback=W14
  68 / def Wvars=W15 Wmask=W16 Winit=W17 WAGGRE=W18 WFREQ=W19 WTYPE=W20
  69 / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  70 + Start: {save spec VARS Wvars}{save spec MASK Wmask}
  71 *{save tempdisk Wtmpd}
  72 *{save spec AGGRE WAGGRE}{save spec FREQ WFREQ}{save spec TYPE WTYPE}
  73 *{line start}{erase}{erase}INIT {save survotype Wx}
  74 - if Wx = 1 then goto 84
  75 - if Wx = 2 then goto ERRS
  76 - if Wx = 3 then goto 98
  77 + 84: {Winit=600,100,30}{goto init}
  78 + 98: {Winit=1000,200,100}
  79 + init: {print Winit}{act}
  80 *{Wback=/&AGGRE Return / Back to the previous work     }
  81 *{form}{write Wback}{form7}PRIND=0{R}
  82 - if Wvars '<>' {} then goto v1
  83 - if Wmask '<>' {} then goto v2
  84 *{goto v3}
  85 + v1: VARS={print Wvars}{goto v3}
  86 + v2: MASK={print Wmask}{goto v3}
  87 + v3: {R}{R}{Wtmp1=Wtmpd&_AG1.SVO}{Wtmp2=Wtmpd&_AG2.SVO}
  88 *{line start}{ref set 1}

Jos muuttujat oli aktivoitu täsmennyksillä VARS tai MASK, niin niistä jompikumpi (ensisijaisesti VARS) kirjoitetaan työkenttään. Muussa tapauksessa käsitellään jatkossa kaikkia aktiivisia muuttujia (valintahan voidaan tehdä myös FILE ACTIVATE -toiminnolla).

Työtiedostojen nimet talletetaan sukromuistipaikkoihin Wtmp1 ja Wtmp2. Nykyinen kursorin sijainti laitetaan muistiin. Tällaisia numeroin merkittyjä muistipaikkoja sukroilla on käytössään kahdeksan. Harvoin tarvitaan niin monta. Jos luulee tarvitsevansa enemmän, pitää ehkä suunnitella sukronsa paremmin.

Tiedoston rakennekuvaus aktiivisten muuttujien osalta otetaan esiin FILE STATUS -komennolla. Parametri 1 tuo kenttään vain ensimmäisen sarakkeen aktivoinnit. Se ei olisi välttämätöntä tässä. Ensin etsitään varsinaisen muuttujalistan alku rivien 91-92 silmukalla. Tekstiblokin määrittely aloitetaan heti kun avainsana FIELDS: on löydetty ja siirrytty ensimmäisen muuttujan kuvauksen alkuun. Sen jälkeen vain kuljetaan listaa alaspäin, kunnes saavutetaan sen loppu (rivin alussa sana END). Matkalla tarkkaillaan, onko aggregointimuuttuja (se, jonka suhteen aineisto halutaan aggregoida) aktiivisena eli onko se mukana listassa. Jos on, Wz saa arvon yksi (se nollattiin alussa rivillä 90). Blokki saadaan valmiiksi ja se tuhotaan, jolloin se on käytettävissä neljällä block-napin painalluksella myöhemmin. Sukro palaa talletettuun paikkaan ja tyhjentää kentän siitä alaspäin. Jos Wz edelleen on nolla, eli aggregointimuuttujaa ei löytynyt aktiivisten muuttujien joukosta, on virheilmoituksen aika.

   1  1 SURVO 98  Mon Sep 21 11:18:35 1998          D:\S\AGGRE\   1000   80 0
  89 /
  90 *FILE STATUS {print Wdata},1{act}{Wz=0}
  91 + w: {R}{save word Wx}
  92 - if Wx '<>' FIELDS: then goto w
  93 *{R}{next word}{next word}{next word}{next word}{r9}{block}{block}{u}
  94 + x: {R}{save word Wx}
  95 - if Wx '=' END then goto y
  96 *{next word}{next word}{next word}{next word}{save word Wx}
  97 - if Wx '<>' Waggrvar then goto x
  98 *{Wz=1}
  99 + y: {jump END,END,1}{line end}{u2}{block}{erase}
 100 *{ref jump 1}{ref del 1}SCRATCH{erase}{act}{home}
 101 - if Wz = 0 then goto ERR3

Seuraavaksi tarkistetaan täsmennykset AGGRE, TYPE ja FREQ. Yhtäkään niistä ei tarvitse antaa, siispä kaikille tarvitaan oletukset. Sukrokoodi {save spec} tallettaa tyhjää ({}), jos ao. täsmennystä eli spesifikaatiota ei ole lainkaan annettu. Näillä ehdoilla asetetaan oletusarvot täsmennyksille riveillä 104, 117, 118, 125 ja 128. TYPE-täsmennyksen oletusarvo riippuu täsmennyksestä AGGRE: jos lasketaan puuttuvien arvojen lukumääriä arvolla NMISS, varataan vain kahden tavun muuttujat - muissa tapauksissa oletuksena on muuttujatyyppi N4.

AGGRE-täsmennyksen muista kuin alla näkyvistä arvoista annetaan virheilmoitus. TYPE saa aina jonkin arvoista 1, 2, 4 tai 8. FREQ-täsmennyksellä kerrotaan lukumäärämuuttujan nimi. Nimittäminen jää käyttäjän vastuulle, mikäli oletusarvo FREQ ei kelpaa. /AGGRE ei vaivaudu tarkistamaan, onko annettu muuttujanimi millään tavoin kelvollinen.

   1  1 SURVO 98  Mon Sep 21 11:18:35 1998          D:\S\AGGRE\   1000   80 0
 102 /
 103 - if WAGGRE '<>' {} then goto Ag0
 104 *{WAGGRE=MEAN}{goto Ag9}
 105 + Ag0:
 106 - switch WAGGRE
 107 -   case MEAN: goto Ag9
 108 -   case SUM: goto Ag9
 109 -   case MEDIAN: goto Ag9
 110 -   case MIN: goto Ag9
 111 -   case MAX: goto Ag9
 112 -   case NMISS: goto Ag9
 113 - default: goto ERR4
 114 + Ag9:
 115 - if WTYPE '<>' {} then goto Ty0
 116 - if WAGGRE '<>' NMISS then goto Ty04
 117 *{WTYPE=2}{goto Ty9}
 118 + Ty04: {WTYPE=4}{goto Ty9}
 119 + Ty0:
 120 - switch WTYPE
 121 -   case 1: goto Ty9
 122 -   case 2: goto Ty9
 123 -   case 4: goto Ty9
 124 -   case 8: goto Ty9
 125 *{WTYPE=4} 
 126 + Ty9:
 127 - if WFREQ '<>' {} then goto Fr0
 128 *{WFREQ=FREQ} 
 129 + Fr0:

Useimmiten sukrot pysyvät työkentässään, kun ovat sellaisen saaneet tehtyä. Toisinaan on kuitenkin perusteltua palata hetkeksi alkuperäiseen kenttään ja tehdä joitakin toimintoja siellä. Paluu tapahtuu kutsumalla apusukroa SUR-RESTORE, kun ensimmäiseen sukromuistipaikkaan on asetettu nimi, jolla on aiemmin kutsuttu SUR-SAVE-sukroa.

Riveillä 139-147 tapahtuu kaksi jatkon kannalta oleellisen tärkeää asiaa: aineiston aktiiviset muuttujat kopioidaan uudeksi aineistoksi, joka puolestaan lajitellaan vielä uudeksi aineistoksi. Keksitkö syyn alkuperäiseen kenttään palaamiseen? Aktiiviset muuttujathan olivat tiedossa työkentässäkin, joten mitä alkuperäisessä on sellaista mitä työkentästä puuttuu?

   1  1 SURVO 98  Mon Sep 21 11:18:35 1998          D:\S\AGGRE\   1000   80 0
 130 / def W1=W1
 131 *{disp off}{save stack}{W1=AGGRE}{call SUR-RESTORE}
 132 *{del stack}{load stack}{break on}
 133 *{line start}{find /}{save char Wx}{Wline=}
 134 - if Wx '<>' / then goto e
 135 *{r2}{save line Wline}
 136 + e: {line start}{erase}{erase}
 137 *{d}{u}{ins line}PRIND=0{u}{home}
 138 / def Wdata=W1
 139 *COPY CUR,CUR TO {print Wtmp1}{act}{home}{erase}
 140 *COPY CUR,CUR TO {print Wtmp2}{act}{home}{erase}
 141 *FILE DEL {print Wtmp1}{act}{home}{erase}
 142 *FILE DEL {print Wtmp2}{act}{home}{erase}{disp reset}
 143 *FILE COPY {print Wdata} TO {print Wtmp1} / {print Wline}{act}
 144 *{line start}{erase}
 145 *FILE SORT {print Wtmp1} BY {print Waggrvar} TO {print Wtmp2}{act}
 146 *{line start}{erase}{disp off}
 147 *FILE DEL {print Wtmp1}{act}{home}{erase}
 148 *INIT {print Winit}{act}{form}{write Wback}{form7}PRIND=0{R}{disp reset}

Kuten varmaan keksitkin, alkuperäinen kenttä saattaa sisältää n kpl täsmennyksiä, joilla halutaan rajata aineistosta aggregoitavaksi vain osa havainnoista. Rajaaminen voidaan tehdä IND-, CASES- ja SELECT-täsmennysten avulla, joista viimeksi mainittu tuottaa sukroille eniten päänvaivaa, se kun voi koostua vaikka kuinka monen IND- tai CASES-tyyppisen alkeisehdon yhdistelmästä. Nämä kaikki erilliset täsmennykset tarvitsisi raahata {save spec} -tekniikalla työkenttään. Yksinkertaisinta on palata hetkeksi alkuperäiseen kenttään, joka alussa talletettiin ja toimia siellä.

Kenttähän on tallessa, joten sitä voi sotkea vapaasti. Kannattaa kuitenkin olla tarkkana eikä suinpäin rynnätä tekemään esimerkiksi lisärivejä tarvittavia komentoja varten. Pitää muistaa, millä logiikalla täsmennykset käsitellään. Ensimmäisellä sijalla on aktivoitavan rivin perässä oleva kommenttialue. Sen sukro tässä tallettaa sukromuistipaikkaan Wline, mikäli kommenttia ilmaiseva kauttaviiva riviltä löytyy.

Rivillä 137 näkyy eräs sukrokoodin idiomi: rivin lisäystä edeltää koodipari {d}{u}. Kannattaa miettiä mitä tapahtuu, jos painaa nappia alt-F9 näkyvän ikkunan viimeisellä rivillä. Seuraavaksi kannattaa miettiä mitä sitten tapahtuisi, jos Survon editorin toimintaa muutettaisiin niin ettei ylimääräistä {d}{u}-koodia tarvittaisi. Eli kirjoittamaton sääntö oli: kirjoita aina {ins line} -koodin eteen {d}{u} (tähän asti se oli kirjoittamaton, nyt kirjoitettu). Sattumoisin samalla lyhyellä rivillä on toinenkin huomionarvoinen kohta. Nimittäin koodia {home} ei pidä käyttää, ellei ole ehdottoman varma mitä tekee. Jos ei ole varma, pitää käyttää sen sijaan koodia {line start}, vaikka se onkin pidempi kirjoittaa.

Lisää idiomeja: mitä ihmettä tehdään riveillä 139-142? Pyrkimys on luonnollisesti varmistaa, että rivin 143 FILE COPY tekee todella uuden tiedoston eikä yritä lisätä tietueita johonkin vanhaan. Tätä varten on tuhottava kohdetiedosto. No, miksei sitten vain tuhota kuten tehdään riveillä 141-142? Vaan entäpä jos tuhottavaa tiedostoa ei ole? Tulee rumia File not found -ilmoituksia, hyi. Siispä voitaisiin tarkistaa, onko tiedosto olemassa. Siihen on käytettävissä mainio CHECK-komento, joka on aikanaan aivan sukroja varten tehty. Mutta tarkistamistakin yksinkertaisempaa on tässä varmistaminen: kopioidaan nykyinen rivi (CUR) ao. tiedoston perään. COPY-komento toimii niin, että se tarvittaessa luo tiedoston, muussa tapauksessa lisää tietoja sen perään. Näin päästään etenemään suoraviivaisesti ilman turhia tarkistuksia: tiedosto on olemassa, joten se voidaan tuhota. Tuntuu tuhlaukselta, mutta on aika näppärää tällaisessa tilanteessa, eikä tunnu missään. COPY on nopea, eikä se välitä, vaikka vastassa olisi millainen tiedosto tahansa.

Tästä kaikesta jää henkiin vain Wtmp2:lla osoitettu datatiedosto, joka on lajiteltu aggregointimuuttujan suhteen ja sisältää vain aktiivisen osan alkuperäisestä aineistosta. Tyhjennetään kenttä jälleen samalla INIT-komennolla kuin alussa. Komennon parametrit otettiin silloin talteen Winit-paikkaan ja paluukomento paikkaan Wback, joten niitä ei tarvitse kirjoittaa useampaan kohtaan sukrokoodissa.

Mystinen PRIND=0, joka kummittelee joka puolella, on täsmennys, joka saa useimmat Survon operaatiot vaikenemaan, ts. jättämään sinipohjaisen väliaikaistulostuksen väliin. Koska tämän sukron aikana tapahtuu useita aineiston läpikäyntejä, niin tulostuksen jättäminen pois nopeuttaa selvästi toimintaa. Näkymä käyttäjän suuntaan on myös hieman rauhallisempi.

Lopulta saavumme asian ytimeen: havaintoaineiston aggregointiin! Sen sukro tekee tietenkin FILE AGGR -operaatiolla. Se kirjoittaa jo toiveikkaasti rivillä 150 komennon, mutta ei aktivoi sitä vielä. Aggregoinnin kuvauskaavio on vielä saatava aikaiseksi. Se syntyy mukavasti tiedoston rakennekuvauksen pohjalta. Siispä FILE STATUS -komento saa jälleen käyttöä.

Listan alkuun sukro laittaa lukumäärämuuttujan, joka kiilaa siis lopullisen tiedoston ensimmäiseksi muuttujaksi. Aggregointifunktiona on N ja miinus tarkoittaa sitä, ettei sen arvoja lasketa minkään muun muuttujan avulla. Kirjaimella g merkityssä silmukassa tarkkaillaan jälleen kuvauslistan loppua. Lukumäärä- ja aggregointimuuttujille on omat erikoiskäsittelynsä: edellisen yli hypätään ja jälkimmäiselle tehdään FIRST-funktiolla uuteen tiedostoon sellainen vastine, jossa ovat sen arvot kertaalleen (esim. KUNNAT-tiedoston läänikohtaisessa aggregoinnissa läänien lyhenteet). Muita muuttujia tulee vastaamaan yksi rivi kutakin. Se muodostetaan rivillä 161. Wx:ään on rivillä 158 talletettu ao. muuttujan nimi. Kun loppu saavutetaan ja kuvauskaavio on valmis, aktivoidaan FILE AGGR -komento, tyhjennetään kenttä ja tuhotaan Wtmp2:lla osoitettu datatiedosto. Nyt aggregoitu aineisto on käyttäjän haluamassa paikassa, mutta se ei vielä riitä pedantille sukrollemme.

   1  1 SURVO 98  Mon Sep 21 11:18:35 1998          D:\S\AGGRE\   1000   80 0
 149 /
 150 *FILE AGGR {print Wtmp2} BY {print Waggrvar} TO {print Wnewdata}{R}
 151 *FILE STATUS {print Wtmp2},1{act}
 152 + f: {R}{save word Wx}
 153 - if Wx '<>' FIELDS: then goto f
 154 *VARIABLES:{erase}{home}{d}{u}{ins line}
 155 *{print WFREQ} N -
 156 + g: {R}{save word Wx}
 157 - if Wx '=' END then goto G
 158 *{next word}{next word}{next word}{next word}{save word Wx}{home}{erase}
 159 - if Wx '=' WFREQ then goto Gz1
 160 - if Wx '=' Waggrvar then goto gA
 161 + gM: {print Wx}:{print WTYPE} {print WAGGRE} {print Wx}{goto g}
 162 + gA: {print Waggrvar} FIRST {print Waggrvar}{goto g}
 163 + Gz1: {d}{u}{del line}{u}{goto g}
 164 + G: {jump 1,1,1,1}{R}{act}{erase}SCRATCH{act}{home}
 165 *FILE DEL {print Wtmp2}{act}{home}{erase}{ref set 1}

Vielä kerran käytetään FILE STATUS -toimintoa, nyt uudella tiedostolla. FILE AGGR:in luomaa kommenttia siistitään hieman, sehän ei voinut tietää, mistä datasta alunperin oli kysymys. Lukumäärämuuttujalle lisätään kuvaus, ja lopulta täräytetään tallessa ollut tekstiblokki näkyville niin että muutkin muuttujat saavat kuvauksensa. Kuvaukset sukro käy nuuskimassa siltä varalta, että ne sisältäisivät formaattimäärityksiä. Se ei poista niitä, mutta amputoi ne sutaisemalla alkusulun pois. Rivin 175 tyhjät aaltosulut ovat todella tarpeen, sillä niitä edeltää välilyönti, jolla sukro alkusulun poistaa.

Kuvaus päivitetään FILE STATUS -operaation työparilla FILE UPDATE. Samalla valmistellaan FILE SHOW -komentoa luovutettavaksi käyttäjälle, kunhan palataan alkuperäiseen kenttään. Blokki on taas oiva siirtomuoto työkentästä varsinaiseen. Loppukohtaus tullaan näyttelemään kohdassa End1.

   1  1 SURVO 98  Mon Sep 21 11:18:35 1998          D:\S\AGGRE\   1000   80 0
 166 *FILE STATUS {print Wnewdata}{act}{R}{erase}
 167 *Aggregated from data {print Wdata} by variable {print Waggrvar}{R}{R}
 168 *{next word}{next word}{next word}{next word}{r9}{ref}
 169 *Frequencies of the groups defined by {print Waggrvar}{ref}{ref}{d}
 170 *{block}{block}{block}{block}
 171 + s1: {ref set 2}{line start}{save word Wx}{ref jump 2}
 172 - if Wx '=' END then goto s9
 173 *{find string (#}{save char Wx}
 174 - if Wx '<>' ( then goto s8
 175 * {}
 176 + s8: {ref jump 2}{d}{goto s1}
 177 + s9: {ref jump 1}
 178 *FILE UPDATE {print Wnewdata}{act}{R}{erase}{block}{block}
 179 *FILE SHOW {print Wnewdata} / {print WAGGRE}-aggregated {print Wdata}
 180 *{block}{erase}{goto End1}

Lopun edellä ovat virheilmoitukset, jotka kaikki päättyvät yhteiseen uomaan ERR0. Siinä pyydetään käyttäjältä ENTERin painallusta ja palataan sitten lähtötilanteeseen Back-kohdan kautta. ERRS-kohdan ilmoitukseen ajaudutaan, jos yritetään käyttää tätä sukroa SURVOS-version alta. Sukro on ankara ja ilmoittaa vaativansa SURVO 98:n, vaikka SURVO 84C:kin kävisi. En kuitenkaan ole edes testannut sukroa SURVO 84C:n puolella, joten antaa olla.

Rivin 193 {goto Back} on turha tavallaan mutta mukana tahallaan. Jos vaikka tulisi lisättyä jotain koodia sen ja 194:n väliin, olisi turhauttavaa haeskella virhettä, joka joissain tilanteissa esiintyisi ja johtuisi vain siitä, että ERR0:n kaverit pääsisivät mukaan uuteen koodiin vaikka ne kuuluisivat jo siinä vaiheessa muualle. Sama koskee esimerkiksi rivin 188 goto-koodia.

Kohdat Back ja End1 ovat muuten samanlaisia - ne palauttavat alkuperäisen kentän ja näkymän mutta End1 lisää yhden rivin ja siirtää kursorin siihen. Tälle riville tulee FILE SHOW -komento silloin kun suoritus loppuu normaalisti tai /AGGRE-komennon malli, jos tullaan avustustekstin kautta.

Viimeisen rivin End on loppujen loppu, jonne vievät kaikki tiet. Jos {disp}-koodeja on käytetty, on parasta laittaa {disp reset} loppuun. Samoin jos {message}-koodeja on käytetty, kannattaa loppuun panna yksi {message}@. Vihonviimeisenä on kuitattu sukron ensimmäisellä koodirivillä asetettu nopeus. Ainoana pakollisena koodina on lopussa lakoninen {end}.

   1  1 SURVO 98  Mon Sep 21 11:18:35 1998          D:\S\AGGRE\   1000   80 0
 181 / - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 182 + ERR1: {Wx=BY <aggr_variable> missing!}{goto ERR0}
 183 + ERR2: {Wx=TO <new_data_file> missing!}{goto ERR0}
 184 + ERR3: {Wx=Variable }{Wx=Wx&Waggrvar}{Wx=Wx& must also be active!}
 185 *{goto ERR0}
 186 + ERR4: {Wx=AGGRE must be one of: MEAN,SUM,MEDIAN,MIN,MAX,NMISS!}
 187 *{goto ERR0}
 188 + ERRS: {Wx=This sucro requires SURVO 98!}{goto ERR0}
 189 + ERR0: {Wx=        &Wx}{Wx=Wx& Press ENTER!}{message Wx}
 190 - on key
 191 -    key _: continue
 192 -   wait 600
 193 *{goto Back}
 194 / def W1=W1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 195 + Back: {W1=AGGRE}{call SUR-RESTORE}{goto End}
 196 + End1: {W1=AGGRE}{call SUR-RESTORE}{line start}{ins}
 197 *{block}{block}{block}{block}{ins}{R}
 198 + End: {disp reset}{message}@{tempo +1}{end}