Introduktionskurs i SAS Del 1 Datastegsprogrammering
Detta material skyddas av upphovsrättslagen och får inte kopieras eller på annat sätt spridas utan upphovsmannens (Knowit Information Management) skriftliga godkännande. Upphovsmannen har gett sitt godkännande att detta material fritt får användas vid undervisning på Linköpingsuniversitet, IDA. Sidan 2
DATASTEGSPROGRAMMERING SAMT ETT URVAL AV SAS - FUNKTIONER... 4 GRUNDLÄGGANDE HANTERING AV SAS... 4 AUTOEXEC OCH CONFIG... 4 KOMMENTERING I SAS... 5 AVBRYTA HÄNGANDE SAS-SESSIONER ELLER PROGRAM... 5 LOGG OCH RESULTATFÖNSTER... 5 LIBNAME OCH FILNAME... 6 SAS-TABELLER - DATASET...6 DATASTEGET... 7 LENGTH... 7 DATA _NULL_-STEG... 7 DATUM I SAS... 9 LABEL... 9 SKRIVA TILL DATASET... 10 LÄSA FRÅN DATASET... 11 SKRIVA TILL EXTERN FIL... 12 LÄSA FRÅN EXTERN FIL... 13 BEHANDLA DATA... 14 SELEKTERA DATA... 14 VÄLJA UT POSTER/RADER... 14 VÄLJA UT VARIABLER/KOLUMNER... 17 ÄNDRA BEFINTLIGA VARIABLER... 17 SKAPA NYA VARIABLER/KOLUMNER... 18 Nya variabler av gamla... 19 KONVERTERA VARIABLER MED HJÄLP AV PUT/INPUT... 19 Några användbara konverteringar... 20 LOOPA I DATASTEGET...21 BEHÅLLA VÄRDEN PÅ VARIABLER ÖVER FLER OBSERVATIONER I DATASTEGET... 22 ANVÄNDBARA OPTIONS TILL SET... 23 EFFEKTIVISERING... 24 SAS-FUNKTIONER, ETT URVAL... 25 ARITMETISKA FUNKTIONER... 25 TRUNKERINGSFUNKTIONER...25 CHARACTER-FUNKTIONER... 26 DATUM OCH TIDS FUNKTIONER... 27 FUNKTIONER FÖR EXTERNA FILER... 27 SPECIALFUNKTIONER... 27 VARIABELFUNKTIONER... 28 SYSTEM OPTIONS... 28 DATA TILL ÖVNINGAR... 30 COMPANY... 30 Tabellrelationer... 31 PROGRAMHUVUD... 31 Sidan 3
Datastegsprogrammering samt ett urval av SAS - funktioner I den första delen av introduktionskursen i SAS kommer vi att gå igenom grunden i SAS Base programmering - datastegsprogrammering samt några användbara funktioner. Kapitlet innefattar ett antal kodexempel och meningen är att man läser dokumentet på datorn och har SAS igång för att löpande kopiera över kodexemplen till SAS och provköra och studera resultaten. Grundläggande hantering av SAS Den bästa onlinehjälpen för datastegsprogrammering hittar man under Help / SAS Help and Documentation. Har man tillgång till Internet finns även en bra online dokumentation på SAS Institiutes hemsida, antingen via http://support.sas.com eller direkt via http://support.sas.com/onlinedoc/913/docmainpage.jsp Autoexec och config SAS-miljön kan anpassas efter egna behov genom att göra tillägg och ändringar i programfilerna AUTOEXEC.SAS och SASV9.CFG. Dessa filer används av SAS när en session skapas, I SASV9.CFG sätts inställningar för hur SAS-sessionen skall konfigureras, t.ex. med avseende på var work-biblioteket skall ligga eller hur stort utrymme en sortering får använda. De inställningar man gör inne i SAS, t ex egna snabbtangenter, fönsterstorlekar, inställningar under preferences, etc. sparas i en sas-katalog med filnamnet profile.sas7bcat. Katalogen ligger i SASUSER-biblioteket som är ett av de libnames (referens till bibliotek) som SAS själv definierar upp via inställningarna i SASV9.CFG. I AUTOEXEC.SAS kan man skriva vanlig SAS-kod som man vill skall utföras när SAS öppnas. Det vanligaste är att man i AUTOEXEC.SAS lägger in de LIBNAMEs, d.v.s. referenser till de bibliotek som man använder ofta. I Tools - > Options -> Preferences kan man också göra diverse inställningar, t.ex. för hur resultat ska visas. Sidan 4
Kommentering i SAS För att skriva in kommentarer i datasteget används två notationer: För att kommentera bort en sats: * Kommentar bla bla... ; Här kommenteras allt bort fram till nästa semikolon. Det kan alltså vara fler rader eller del av en rad. För att kommentera bort ett helt block med satser: /* sats 1 ; sats 2 ; sats 3 ; */ Ha som grundregel att kommentera ofta och snyggt i koden så att du själv eller andra som skall läsa ditt program förstår vad som händer. Vi rekommenderar att man använder följande standard för att kommentera i löpande SAS kod: *-- Kommentar1 --*; *-- Kommentar2 --*; Använd /* Kommentar */ endast inne i en sats eller om flera satser skall kommenteras bort. Avbryta hängande SAS-sessioner eller program I bland kan det bli så att SAS sessionen hänger sig p.g.a. av att vi kodar fel. Vanligt är att man missar t ex. ett quit; en, eller ett ;, vilket kan göra att SAS hänger sig. I stället för att starta om sessionen kan man testa att köra magic string nedan. Normalt löser det hängningsproblem. *)*/;/*'*/ /*"*/; %mend;quit;;;;; Det går också ofta att avbryta ett hängande program med Break (utropstecknet) i menyraden. Logg och resultatfönster Det är också bra (mer ett måste) att efter varje körning titta i loggen och reflektera över ev. WARNING, NOTEs och ERRORs för att se om körningen gått bra. Gå även in och titta på datat, rapporten etc. som skapats för att se om resultatet blivit som förväntat. Sidan 5
Libname och filname Data sparas i SAS i s.k. sastabeller (dataset). Ett dataset refereras vanligen inte med en vanlig sökväg (c:\nisse\datasetet.sas7bdat), utan refereras normalt via en saskatalog (=referens/pekare till fysisk katalog) som man definierar upp med ett LIBNAME-uttryck. Namnet på katalogen är sedan den logiska adress som SAS använder för att hitta dataseten. Finessen med detta är att man kan skriva programmen generella och oberoende av den fysiska sökvägen till datasetet och skriver då endast en gång, i början av programmet, eller i en styrfil, den fysiska adressen. Det går även via denna teknik att komma åt andra typer av databaser eller filer, t.ex. Excel. Exempel LIBNAME Detta talar om för SAS att allt som refereras med nisse i programmen ligger i c:\saskurs katalogen. Namnet på libnamet kan vara max 8 tecken långt. libname testlib 'c:\saskurs'; Exempel FILENAME Om man använder eller skapar textfiler kan dessa refereras med sin fysiska sökväg, men av samma anledning som ovan är det mer flexibelt att mappa sökvägen till en intern referens som i detta fall kallas FILENAME. filename infilen 'c:\sas\min_lilla_infil.xls'; Exempel Ta bort LIBNAME eller FILENAME För att ta bort ett definierat libname eller filename skriver man bara en tom FILE/LIBNAME-sats. libname testlib; filename infilen; SAS-tabeller - DATASET I SAS lagras all data i ett s.k. dataset, vilket i princip är samma sak som en tabell i databas. I ett dataset finns två typer av variabler/kolumner; alfanumeriska (character) och numeriska. Det finns alltså ingen specifikt variabeltyp för datum eller tid, utan dessa två typer lagras som numeriska variabler, och sedan använder man ett format för att visa datumet. Tidräkningen i SAS startar 1960-01-01 som är dag 0 i SAS. Sidan 6
Datasteget Datasteget används i SAS för att läsa och skriva data från/till dataset och filer. Datasteget är en implicit loop över samtliga observationer i indatasetet (om det finns något). Exempel Skapa tomt dataset med datasteget I sin enklaste form kan ett datasteg se ut som nedan. Datasteget ska i detta exempel skriva till datasetet UTDATA som ligger i biblioteket/saskatalogen TESTLIB. Ett tomt dataset (utdata) utan variabler skapas. data testlib.utdata; Exempel Skapa dataset utifrån ett annat dataset I nedanstående exempel kommer alla observationer från TESTLIB.INDATA skrivas till TESTLIB.UTDATA. data testlib.utdata; set testlib.indata; Length För att definiera variabler kan man använda ett LENGTH statement. Exempel Definiera variabler med LENGTH I nedanstående exempel kommer variabeln X definieras som alfanumerisk (character), 40 tecken lång, och variabeln Y som numerisk och 8 byte. data testlib.utdata; length x $40 y 8; Data _NULL_-steg I ovanstående exempel läses/skapas data i datasteget som skrivs till ett utdataset, men ett datasteg behöver varken ha något indataset eller skapa utdataset. För att köra ett datasteg utan att skriva till ett dataset används nyckelordet _NULL_ i stället för namnet på utdatasetet: data _NULL_; Detta datasteg utför ingenting. Syftet med detta är att man t.ex. kan vilja använda funktioner utan att läsa eller skriva till dataset, alltså endast visa resultatet i loggen. Sidan 7
Exempel - Lägga ut dagens datum och tid i loggen: Nedanstående exempel lägger ut dagens datum och tid i loggen, utan att skriva till ett dataset: data _null_; var = put(date(),yymmdd10.); put 'Dagens datum: ' var; Detta är resultatet i loggen: Dagens datum: 2008-09-19 NOTE: DATA statement used (Total process time): real time 0.17 seconds cpu time 0.01 seconds Exempel Villkorsstyrt meddelande i loggen I detta exempel ska ett meddelande skrivas ut i loggen om det är fredag, annars skrivs ett annat: data _null_; var=put(date(),yymmdd10.); dag=weekday(date()); put 'Dagens datum: ' var; if dag=6 then put 'Trevlig helg!'; else put 'Jobba på!'; Resultat (om det är fredag): Dagens datum: 2008-09-19 Trevlig helg! NOTE: DATA statement used (Total process time): real time 0.01 seconds cpu time 0.00 seconds Sidan 8
Datum i SAS Datum i SAS lagras som en numerisk variabel, och för att den skall presenteras som ett datum måste man lägga ett format på variabeln d.v.s. ange hur den ska visas. Format kan läggas på då dataset skapas, vilket då kan användas varje gång datasetet används. Det andra alternativet är att lägga på ett format då en rapport skapas. Då används formatet bara i just den rapporten. SAS räknar tid från 1/1 1960 detta datum representeras som 0. Format kommer att behandlas ytterligare i Del 5 i denna kurs. Exempel Skriva ut ett formaterat datum i loggen: length y 8; format y yymmdd8.; y=365; put y; PUT-satsen skriver ut följande formaterade text till loggen: 60-12-31 INFORMAT kan användas om man ska läsa från textfiler och direkt vill omvandla t.ex. en datumtextsträng enligt ovan till ett numeriskt SAS-datum. Label Variablerna kan tilldelas labels som sedan används i rapporter o. dyl. där variabeln ska beskrivas mer i klartext: length Y 8; format Y datetime17.; label Y='En variabel innehållande datum och tid.'; y=1000; put y; Här har formatet DATETIME17. använts, som då tolkar det numeriska värdet som antal sekunder det gått sedan 1 januari 1960. För att titta på vilka variabler ett dataset innehåller, vad de är för typer, vilka format de har och vilka labels dom har kan man skriva kommandot VARS libname.dataset, ex VARS TESTLIB.UTDATA, på kommandoraden i sas. Då visas ett fönster som listar variablerna i UTDATA och deras attribut (typ, längd, format, label). Det går också bra att högerklicka på en tabell eller använda en procedur, Proc CONTENTS som kommer att nämnas senare i kursen. Sidan 9
Attrib Istället för att använda LENGTH, LABEL och FORMAT kan samtliga karaktäristika för en variabel definieras via ATTRIB. at trib Y length=8 format=datetime17. label='innehåller datum och tid.'; y=1000; put y; Skriva till dataset Man kan antingen skriva implicit eller explicit till det dataset som angivits i DATAstatementet. Om man bara skriver t.ex.: data testlib.utdata; y=1000; så kommer en post att skrivas till utdata, även om vi inte explicit har angivit något. Man kan lika gärna skriva: data testlib.utdata; y=1000; output; OUTPUT-satsen säger explicit till SAS att skriva till utdata. Om ingen OUTPUT-sats finns i datasteget antar SAS att ett output ska göras när en datastegsiteration är klar. Ifall man skriver: data testlib.utdata; y=1000; output; output; så kommer två identiska poster att skrivas till utdata. Man kan även skriva till olika dataset i ett och samma datasteg genom att ange fler utdataset i data-satsen. data testlib.utdata1 testlib.utdata2; length x $10 y 8; x= 'Hej'; y=1000; Sidan 10
Detta datasteg kommer att skapa två identiska dataset i TESTLIB. Man kan explicit ange till vilket dataset man vill skriva information: data testlib.utdata1 testlib.utdata2; length x $10 y 8; x='hej'; y=1000; output; x='svejs'; output testlib.utdata2; I detta exempel skrivs Hej samt 1000 i en post till båda dataseten, men i utdata2 skrivs dessutom en post med x= Svejs och y=1000 (oförändrad). Läsa från dataset Genom att ange ett dataset i en set-sats läser SAS in en post från detta dataset per iteration i datasteget. Varje post läses in i en temporär programdatavektor som innehåller aktuell posts alla variabelvärden. data testlib.utdata; set testlib.indata; eftersom inget annat har angivits kommer SAS här att skriva till utdata för varje iteration, vilket innebär att utdata blir en exakt kopia av indata (Alla attribut kommer också att kopieras om inget annat anges, dock inte index). Antag nu att indata innehåller variablerna in_x (char 10) och in_y (num 8) och följande körs: data testlib.utdata; set testlib.indata; length ut_y 8.; ut_y=in_y*0.25; så kommer variablerna in_x, in_y samt ut_y att skrivas till utdata, ut_y blir en härledd variabel från in_y. Sidan 11
Kommentar: För att kontrollera vad som händer vid t.ex. debugging är det ett bra hjälpmedel att skriva meddelanden till loggen med put-satsen: data testlib.utdata; set testlib.indata; length ut_y 8.; ut_y=in_y*0.25; put in_y= ut_y=; Resultat: IN_Y=1 UT_Y=0.25 IN_Y=2 UT_Y=0.5 IN_Y=3 UT_Y=0.75 IN_Y=4 UT_Y=1 IN_Y=5 UT_Y=1.25 Detta fall är trivialt, men om man vill kontrollera värden på variabler t.ex. innan villkorssatser är det användbart. Skriva till extern fil PUT-satsen används även för att skriva till en extern fil. Man anger önskat filnamn i en FILE-sats. Om det finns en FILE-sats i ett datasteg kommer alltså inte PUT-satser att resultera i att något skrivs i LOG-fönstret (som i exemplen ovan), utan i filen. Om man inte vill skriva till något dataset anger man _NULL_ som utdataset i DATAsatsen. data _null_; file 'c:\exempelfil.txt'; set testlib.indata; put in_x; Nu kommer samtliga observationers värden på IN_X skrivas till varsin rad i textfilen. För att göra koden mer generell bör man använda ett FILENAME på samma sätt som man använder LIBNAME: filename utfil 'c:\exempelfil.txt'; data _null_; file utfil; set testlib.indata; put in_x; I sista delen kommer vi gå igenom hur man kan använda detta för att skapa rapporter, s.k. data _null_ rapporter. Sidan 12
Läsa från extern fil För att läsa in information från en extern flatfil använder man input-satsen. För att ange vilken fil man ska läsa från används INFILE-satsen: filename infil 'c:\exempelfil.txt'; data testlib.utdata; length ut_x $40; infile infil; input ut_x; För att läsa in fler variabler från bestämda positioner (exempel, finns andra metoder): filename infil 'c:\exempelfil.txt'; data testlib.utdata; length ut_x $40 ut_y 8.; infile infil; input ut_x $1-10 ut_y; Här förväntas det att strängen som ska in i variabeln ut_x finns i position 1-10 i filen. Den numeriska variabeln ut_y specificeras inte med några positioner, det första numeriska värde som hittas efter pos 10 läggs i ut_y. Exempel: +---5----+----5----+ Test ett 7 Test två 8 xxx 99 987 Position 1-10 läses in som text, positionen på nästa variabel spelar ingen roll. Filer som har variabler med fasta positioner kan läsas in på följande sätt: data testlib.utdata; length ut_x $40 ut_y 8.; infile infil; input @1 ut_x $10. @12 ut_y 3.; Som i exemplet ovan läses text i i position 1-10, men den numeriska variabel läses in från 12-15. +---5----+----5----+ Test ett 7 Test två 8 xxx 987 Sidan 13
Många filer som läses in har någon form av avgränsare mellan kolumnerna, då kan man använda följande: data testlib.utdata; length ut_x $40 ut_y 8.; infile infil dlm=';'; input ut_x ut_y; Mellan fnuttarna anger man vilken avgränsare som skall användas. Anger man inget utan bara skriver DLM, så används blanksteg som avgränsare. I detta fallet bör alla variabler deklareras i LENGTH-statementet. Givetvis finns inget som hindrar att man har textfiler både som input och output. INFILE-options Det finns ett antal options som kan läggas till INFILE-uttrycket. Nedan ett exempel på ett av dem: TRUNCOVER - Överrider INPUT-uttryckets beteende när en post är kortare än vad INPUT-uttrycket förväntar sig, och gör det möjligt att läsa poster med varierande längd. Variabler utan värde sätts till MISSING. Behandla data Selektera data Man kan selektera data i två ledder, dels höjden, dvs. välja ut vissa poster från ett dataset, dels på bredden, dvs. välja ut vissa variabler från datasetet. VÄLJA UT POSTER/RADER Vi skapar först ett dataset med testdata: data testlib.indata; length anstnr 8 namn $20; anstnr=1; namn='olle A'; output; anstnr=100; namn='olle B'; output; anstnr=1000; namn='olle C'; output; anstnr=1000; namn='olle D'; output; Att välja ut vissa poster kan göras i koden, eller i form av ett WHERE-option på indatasetet: Exempel - implicit if-sats: set testlib.indata; if anstnr>100 then do; Sidan 14
*-processa data...; output; end; else; *- processa data...; Sidan 15
Exempel Subsetting if: set testlib.indata; if anstnr>100; *-processa data...; Subsetting IF innebär att ALLT efter IF-satsen bara utförs om villkoret är uppfyllt. Eftersom OUTPUT görs sist i datasteget kommer bara de poster som uppfyller villkoret att skrivas till utdata. Exempel WHERE-option: set testlib.indata(where=(anstnr>100)); Den senare metoden är att föredra av effektivitetsskäl, se effektivitetsavsnittet. Exempel Läsa ut en viss post med POINT: För att bara läsa ut en viss post från ett dataset används ett POINT-option: min_pek=2; set testlib.indata point=min_pek; output; stop; Notera att man i detta fall dels måste lägga in ett stop, annars avslutar inte datasteget, att POINT-värdet måste sättas av en variabel (ej t.ex. POINT=2), samt att OUTPUT måste göras explicit. Exempel Läsa ut ett visst antal poster med FIRSTOBS och OBS: FIRSTOBS och OBS kan användas för att välja ut den första posten som ska läsas, respektive den sista. Detta används kanske främst för att välja ut ett fåtal poster i testsyfte, ifall man har stora datamängder, men kan också användas för att t.ex. skapa 10-i-topplistor eller liknande. set testlib.indata (firstobs=3 obs=10); Sidan 16
VÄLJA UT VARIABLER/KOLUMNER Att välja ut variabler kan göras genom att använda DROP respektive KEEP. Det finns flera olika sätt att använda DROP och KEEP, dels som ett dataset-option på in- resp. utdatasetet, dels som ett uttryck ( statement ) inuti datasteget. Exempel DROP på indatasetet: Variabler i indatasetet som man inte är intresserad av alls, ens för att använda för att skapa nya variabler bör kastas bort redan vid inläsning av indatasetet, av effektivitetsskäl: set indata(drop=var10 var11); *- processing data...; Exempel DROP på utdatasetet med dataset-option: Variabler som man vill använda för att t.ex. beräkna en ny variabel, men som man inte behöver ha med i utdatasetet tas bort i DATA.-uttrycket, d.v.s. de används i datasteget, men skrivs inte till det nya datasetet. data utdata(drop=var1); set indata; *- processing data...; Exempel DROP på utdatasetet med DROP-uttryck inuti datasteget: DROP eller KEEP-uttryck inuti datasteget tar alltid bort valda variabler på utdatasetet, ej på indatasetet. Detta sätt att ta bort variabler används alltså på samma sätt som föregående exempel. set indata; datum=date(); *-processa data...; drop datum; Ändra befintliga variabler För att ändra längd, eller ordning på variabeln i programdatavektorn kan LENGTHuttrycket sättas innan SET-uttrycket: length stad $30; set indata; datum=date(); *-processa data...; drop datum; I detta exempel är stad är en befintlig variabel från indatasetet. Sidan 17
Om man vill definiera om variabler när man skriver dem från ett dataset till ett annat måste man lägga LENGTH-satsen som den första satsen i datasteget, detsamma gäller om man vill byta ordningen på befintliga variabler: Skapa nya variabler/kolumner När nya variabler ska skapas kan det göras både implicit och explicit, exemplen nedan i detta avsnitt beskriver skillnaderna, och vad man bör tänka på då nya variabler ska skapas. Exempel Skapa ny variabel implicit kan leda till problem Enklaste sättet att skapa en variabel är att bara referera till den i koden: my_text='xxx'; Detta går i och för sig bra, men man bör alltid explicit definiera variabeln för att vara säker på hur den är definierad. I exemplet ovan kommer SAS automatiskt definiera my_text som alfanumeriskt (char), tre positioner lång. Därmed kommer följande exempel: my_text='xxx'; output; my_text='xxx yyy'; output; leda till att my_text trunkeras i post nr 2. Exempel Skapa ny variabel explicit lösningen på problemet Lösningen är att explicit definiera längden på variabeln med en LENGTH-sats: length my_text $10; my_text='xxx'; output; my_text='xxx yyy'; output; Man bör vänja sig vid att alltid definiera alla variabler explicit i LENGTH-satsen, dels av skälet som visas ovan, att inte få oönskade trunkeringar, men också för att det ger bättre överblick. Sidan 18
Nya variabler av gamla Det vanligaste är kanske att man skapar nya variabler av gamla eller av kombinationer av gamla. Vi skapar ett dataset med indata: data testlib.indata; length anstnr lon 8 namn $20; anstnr=1; namn='olle A'; lon=15000; output; anstnr=100; namn='olle B'; lon=17000; output; anstnr=1000; namn='olle C'; lon=19999;output; anstnr=1000; namn='olle D'; lon=26000;output; Exempel skapa nya variabler av gamla Vi vill ändra lönen för samtliga personer, genom att öka den med 15%. Den gamla lönen skall vara kvar och löneökningen skall också lagras i en ny variabel; data testlib.indata; set testlib.indata; lon_okn = lon * 0.15; nylon = lon + lon_okn; Ett alternativ är att alla personer som tjänar över 18000 får en ökning med 20% och övriga får nöja sig med 10%. data testlib.indata; set testlib.indata; if lon gt 18000 then do; lon_okn = lon * 0.20; nylon = lon + lon_okn; end; else do; lon_okn = lon * 0.10; end; nylon = lon + lon_okn; Konvertera variabler med hjälp av put/input Många gånger vill man konvertera en variabel, eller lägga ett annat format på den. Ett vanligt fall i SAS är datum. Eftersom datum lagras numeriskt så måste man formatera om det innan man skriver ut det, såvida det inte är sparat i datasetet med format. Sidan 19
Exempel Konvertering av datum: Vi läser in filen nedan. +---5----+----5----+ 99-01-21 2 Pelle 99-02-15 3 Nisse 00-01-01 4 Kalle data testlib.dat; length datum idnum 8. namn $8.; infile test; inpu t @1 datum yymmdd8. @10 idnum 1. @12 namn $5.; put datum; Datumen skrivs ut i loggen 14265 14290 14245 Vill man istället få datumet utskrivet i klartext måste man konvertera datum variabeln när den skrivs ut. Det kan man göra genom att i put-satsen lägga till det datum-format som variabeln skall skrivas ut i. put datum yymmdd8.; Nu skrivs datumet ut som ett datum 99-01-21 99-02-15 99-01-01 Man kan också skapa en ny variabel med datumet som char och sedan skriva ut den. nyvar = put(datum, yymmdd8.); Några användbara konverteringar Förutom datum så vill man många gånger konvertera en variabel. Nedan har vi listat några vanliga konverteringar. /* CHAR till NUM NUM till CHAR DATUM till CHAR NUM till DATUM DATUM till NUM CHAR till DATUM */ input(left(var), best.) put(var, $w.) input(left(put(var, yymmddw.)), $w.) input(left(put(var, w.)), yymmddw.) input(put(var, yymmddw.), w.) input(var, yymmddw.) Sidan 20
Exempel Konverteringar av variabler text datum - nummer: data konvert; text_n = '4'; num_n = 4; datum = 365; datum_t = '20080808'; datum_n = 20080808; charrnumm= input(left(text_n), best.); nummcharr=put(num_n, $1.); datcharr=input(left(put(datum, yymmdd8.)), $8.); nummdat = input(left(put(datum_n, 8.)), yymmdd8.); datnumm = input(put(datum, yymmdd6.),8.); chardat= input(datum_t, yymmdd8.); LOOPA I DATASTEGET Ibland kan man behöva loopa i ett dataset (iterera). Nedan visas ett antal exempel på detta. Syntax do i=1 to... (until / while), do while / until, do i=1 by 2 to 11 Exempel - Generera ett dataset med 1000 anställningsnummer: do anstnr=1 to 1000; output; end; Exempel - Generera ett dataset med anställningsnr med endast jämna nummer: do anstnr=2 by 2 to 1000; output; end; Exempel - Loopa tills ett villkor är uppfyllt Detta exempel visar så många gånger det går på två sekunder: length anstnr tmp secs 8; drop tmp secs; tmp=time(); *starttid; anstnr=0; secs=0; do until (secs >= 2); anstnr=anstnr+1; output; secs=time()-tmp; end; Sidan 21
Alternativt: length anstnr tmp secs 8; drop tmp secs; tmp=time(); *starttid; anstnr=0; secs=0; do while (secs < 2); anstnr=anstnr+1; output; secs=time()-tmp; end; Behålla värden på variabler över fler observationer i datasteget Variabler som skrivs till utdatasetet initieras från början som blank eller missing i programdatavektorn inför varje iteration. Exempel SOM INTE FUNGERAR där variabeln up_down ska uppdateras beroende på hur x förhåller sig till föregående observations x: (testin innehåller fyra poster med x=1,2,1,1. Exemplet går ut på att variabeln up_down blir U, U, D,- på respektive post. Först skapas indatasetet: data testlib.testin; x=1; output; x=2; output; x=1; output; output; Exempel Behålla värdet på variabel utan RETAIN fungerar inte: Sedan ett program för att skapa variabeln up_down: data testlib.utdata; set testlib.indata(keep=x); length last 8. up_down $1; if x>last then up_down='u'; else if x<last then up_down='d'; else if x=last then up_down='-'; last=x; Detta exempel fungerar inte. up_down kommer alltid bli U eftersom last kommer att initieras som missing inför varje iteration. Sidan 22
Exempel Behålla värdet på variabel med RETAIN - fungerar: Datasteget kompletteras med ett RETAIN statement som innebär att värdet i programdatavektorn ej initieras av SAS, utan bara ändras då det uttryckligen ändras i programmet: data testlib.utdata; set testlib.indata(keep=x); length last 8. up_down $1; retain last; if x>last then up_down='u'; else if x<last then up_down='d'; else if x=last then up_down='-'; last=x; I detta fall behåller LAST värdet på X i föregående observation och utfallet blir som önskat. Möjligen skulle man vilja att LAST initieras. Annars kommer alltid up_down vara U i första observationen eftersom LAST då är missing. Antag att man vill att ingångsnivån på LAST ska vara 2. Då modifieras retain-satsen enligt nedan: retain last 2; I detta fall kommer up_down i stället bli D i observation ett. Användbara options till SET Till alla uttryck (statements) finns ett antal options som gör att man kan kontrollera dess beteende. Till SET statmentet finns ett antal olika options som man ofta använder för att kontollera hur data läses från indatasetet. I ett dataset kan man läsa från flera indataset på en gång. I detta exempel läses två olika dataset in till datasetet UT. data ut; set test1 test2; Sidan 23
Exempel Option (IN=) Med hjälp av option IN= kan man skapa en variabel som innehåller värdet 1 eller 0, 1 när en post läses från dataset eller 0 när det inte sker någon läsning från datasetet. Observera att denna variabel endast finns under exekveringen av datasteget och inte sparas till resultattabellen. Detta är en mycket användbar option som ofta används, framförallt vid matchning av dataset, vilket vi kommer till i senare i kursen. data ut; set test(in=ett) test(in=tva); if ett then text = 'Läser från dataset 1'; if tva then text = 'Läser från dataset 2'; Exempel option (END=) I bland kan det vara bra att veta när datasetet är slut, d.v.s. när sista posten är inläst. Det kan man göra med END=. Den fungerar på samma sätt som IN=, i den mån att en 0/1 variabel skapas under exekvering som kommer att ha värde 1 när sista posten är läst. data _null_; set test end=slut; if slut then put 'Läsning av dataset är slut, sista posten är läst!'; Effektivisering För att effektivisera datasteget bör man inte lyfta in fler variabler eller observationer än man behöver i datasteget. Exempel: Antag att testdata.indata innehåller ett mycket stort antal variabler och väldigt många observationer. TESTDATA.UTDATA ska innehålla två av variablerna och bara de observationer som uppfyller ett villkor. Man kan då köra datasteget: data testdata.utdata(keep=kategori belopp); set testdata.indata; if kategori='a' and belopp>100 then output; Kommentar: Samtliga variabler i INDATA läses i detta exempel in i minnet (i programdatavektorn), även om man anger att utdata endast ska behålla två av dessa. Detta kräver både minne och tid. Datasteget itererar över samtliga poster i indata även om bara de som uppfyller villkoret skrivs till utdata. Detta tar tid. Sidan 24
Det är betydligt effektivare att avgränsa det som läses in av SAS genom att ange detta som options redan på INDATA-datasetet: data testdata.utdata; set testdata.indata ( keep=kategori belopp where=(kategori='a' and belopp>100) ); Nu kommer bara de använda variablerna och posterna att läsas in i minnet, vilket är avsevärt mycket mer effektivt om det är många variabler och många poster som inte ska tas med. Ovanstående exempel kördes med ett indataset innehållande drygt 100 variabler och ca 12000 observationer och tog för exempel 1 knappt 15 sekunder och för exempel 2 ca 11 sekunder, dvs. en prestandahöjning med 25-30%. SAS-funktioner, ett urval SAS har skapat ett antal funktioner för att göra vissa beräkningar enklare. Nedan följer en mycket kortfattad listning av vissa användbara SAS-funktioner, samt enkla exempel som visar vad vissa av dem utför. Funktionerna indelas i nitton grupper. Här kommer dock endast några av grupperna att nämnas. Aritmetiska funktioner abs(värde) min(värde1,värde2, ) max(värde1,värde2, ) mod(värde1,värde2) Tar fram det absoluta beloppet. Minsta värdet Största värdet Restvärdet vid division värde1/värde2 Exempel: data _null_; a=abs(-40); b=min(1,3,6); c=max(1,3,6); d=mod(9,5); put a= b= c= d=; Trunkeringsfunktioner int(värde) round(värde<,0.1/10/ >) trunc(värde, längd) Tar heltalet (utan att avrunda) Avrundning. Om andra argumentet ej finns avrundas talet till jämnt heltal, annars till valt antal decimaler; tiotal etc. Trunkerar till vald längd Sidan 25
data xxx; x=86.53657; y=round(x); yy=round(x,10); z=trunc(x,3); put y= yy= z=; Character-funktioner compress(text1 <,text2>) index(text1<,text2>) Rensar bort alla text2 från text1 Letar efter första tillfället som texten i text2 förekommer i text2. Resultatet anges i vilken position den sökta text2 hittades. Om inte text2 hittas blir resultatet 0. indexc(text1<,text2>) left(text) right(text) length(text) repeat(text,n) scan(textsträng,n,delimeter) substr(textsträng,pos1,n) translate(text, t2, t1) trim(text) Letar efter första tillfället som någon av tecknen i text2 förekommer i text2. Resultatet blir positionsnumret för det först hittade tecknet. Vänsterställer textsträngen Högerställer textsträngen Räknar fram antalet tecken för angiven textsträng Text repeteras n +1 gånger Letar upp det n:e ordet från angiven textsträng där varje ord avskiljs genom via en delimeter. Hämtar tecknen från position pos1 och n positioner framåt från textsträng. Ändrar tecken i text1 från t1 till t2 Tar bort inledande blanksteg från text data _null_; t='detta är en text som jag vill omforma'; a=compress(t,' '); b=index(t,'jag'); c=indexc(t,'jag'); d=length(t); e=scan(t,3,' '); f=repeat(scan(t,3,' '),4); g=substr(t,4,8); h=translate(t, 'a', 'ä'); p ut a= b= c= d= e= f= g= h=; data _null_; length t $30 z $90; t='lkjdfälkj'; z=t t; put z; z=trim(t) trim(t); put z; Kommentar: Detta tecken slår ihop två textsträngar, s.k. konkatenering. Sidan 26
Datum och tids funktioner date() datetime() day(sasdatum) month(sasdatum) year(sasdatum) Ger dagens datum Ger dagens datum inklusive klockslag Returnerar dagen från ett SAS-datum Returnerar månaden från ett SAS-datum Returnerar året från ett SAS-datum data _null_; dag=day(date()); man=month(date()); ar=year(date()); put 'År:' ar 'Månad:' man 'Dag:' dag; Funktioner för externa filer PATHNAME( LIBNAME / FILEREF ) returnerar den fysiska sökvägen till SAS-katalogen eller filreferensen. libname clib 'C:\'; data _null_; lib=pathname('clib'); put lib=; Specialfunktioner sysget(env-variabel) symget/symput: system(systemkommando) system("exit"); Ex: kör SET kommandot i DOS: listar systemvariabler. Alla dessa kan hämtas in med sysget. Ex: sysget(username) hämta in / skicka ut makrovariabler (se även Del 4, Makroprogrammering, en introduktion) Ex: call system("md &_lxtmp1_"); call data _null_; length x y $50; x=sysget('processor_identifier'); y=sysget('username'); put x= / y=; %let my_mvar=hej; *Makrovariabel som hämtas in i datasteget nedan; data _null_; a='hejdå'; call symput('ny_mvar',a); y=symget('my_mvar'); put y; %put &ny_mvar; Sidan 27
Datasetfunktioner open( datasetnamn ) close(dataset-id) Öppnar ett dataset, returnerar ett positivt id-nr om det gick bra Stänger ett dataset angivet med id-nr som returneras från tidigare open() data _null_; dsid=open('xxx'); put dsid=; a=varname(dsid,2); b=varnum(dsid,'z'); c=vartype(dsid,varnum(dsid,'y')); put a=; put b=; put c=; dsid=close(dsid); put dsid=; Variabelfunktioner varname(dsid,var-num), varnum(dsid, varname ), vartype(dsid,var-num); Returnerar namn på variabel nr. var-num i datasetet dsid. Returnerar vilket ordningsnummer variabeln varname har i datasetet dsid. Returnerar variabeltypen Slumptalsfunktioner Ett exempel på en slumptalsfunktion: RANUNI(seed) Returnerar ett slumptal från en uniform distribution. Seed är ett heltal. Om seed <= 0, så används aktuell tid för att initiera beräkningen. System options När man startar SAS eller under tiden som SAS är igång kan man göra ett antal olika inställningar (options) som påverkar hela SAS-sessionen. Under SAS system help: Main menu / SAS language / SAS System options finns samtliga options. Nedan listar vi några användbara options. För att ange ett eller flera option skriver du: options ; Linesize= (ls=) Pagesize= (ps=) Längden på linjen i en printfil Antal rader som skrivs i en output Sidan 28
Number nonumber Date nodate Source nosource Stänger på/av sidnumreringen i OUTPUTfönstret Stänger på/av datumet i output-fönstret Visar SAS-koden i loggen Sidan 29
Data till övningar Övningarna är baserade på data från finansinspektionen över insideraffärer. Nedan beskrivs de tabeller som kommer att användas i övningarna. Själva övningarna finns i ett separat dokument. COMPANY Alla företag med organisationsnummer och namn ORGNR Organisationsnummer num VCOMPANY AB-kort char COMPANY Aktiemarknadsbolag char TESTHAND Förändring i innehav IDNR Identifikationsnummer num ORGNR Organisationsnummer num FIN Finansiellt instrument char FINTYP Typ av finans instr. char INSDAT Insiderdatum num ANTAL Antal num CHANGE Ändring char ANDART Ändringens art char TESTIHAV Innehav IDNR Identifikationsnummer num FIN Finansiellt instrument char FINTYP Typ av finans instr. char SENDAT Datum för senaste förändringen num ANTAL Innehav num ORGNR Organisationsnummer num TESTINS Personuppgifter BEFATT IDNR FNAMN ENAMN STORINV char num char char num TESTKURS kursvärde FIN FINTYP KURS KURSDAT ORGNR char char num num num Sidan 30
Tabellrelationer Nedan beskrivs relationerna mellan dataseten som används i övningarna. Programhuvud Nedan visas ett exempel på hur ett programhuvud kan se ut, där man skriver vem som skapat programmet, ändringshistorik etc. /*+-------------------------------------------------------------------+ System...: Program...: Function...: Date...: Language...: SAS 9.1 (Base) Programmer...: Dataset/Tables...: Parameters...: See syntax help History...: +--------------------------------------------------------------------+*/ Sidan 31