SAS変数と割り当てステートメント#
データセットに値を読み込んだら、必ずそれらの値に対して何らかの処理をしたくなるはずです。よくあることとしては、元のデータをリサーチクエスチョンに答えられるように変更することです。データを変更する方法は2つあります。
基本的な割り当てステートメントを使用して、すべてオブザベーションにある情報を追加します。一部の割り当てステートメントでは、プログラミングの計算を簡単にするための数多くのSAS関数を利用できます(平均を取得する場合など)。
あるいは、if-then-else ステートメントを使って、一部のオブザベーションにのみ情報を追加することができます。
ここでは、割り当てステートメントとSAS数値関数を使ってデータを変更する方法、およびif-then-else
ステートメントを使ってデータの一部を変更する方法を学びます。
データを変更する際、変数の値だけでなく変数の型も変更する必要がある場合があります。つまり文字型の変数を数値型に変更する必要があるかもしれない、ということで文字変数を数値に変換するためのINPUT関数についても確認しましょう。
割り当てステートメントの基礎#
データセットのデータを変更する基本的な方法は、基本的な割り当てステートメントを使うことです。形式は必ず次のようになります。
variable = expression;
ここで、variable
は任意の有効なSAS名で、expression
は変数にその値を与えるために必要な計算です。変数は常に等号の左側に置かれ、式は右側に置かれます。そしてもちろん、ステートメントの最後にセミコロン(;)を付けます。
割り当てステートメントは変数の値を変更するものなので、割り当てステートメントについて確認する過程で、数値変数と文字変数の両方を扱います。また、計算を簡単にするためのSAS数値関数の使い方にも触れます。
例#
このセクション全体を通して、次のデータステップで作成される一時データセットgradesのいろいろな部分を変更していきます。
data grades;
input name $ 1-15 e1 e2 e3 e4 p1 f1;
datalines;
Alexander Smith 78 82 86 69 97 80
John Simon 88 72 86 . 100 85
Patricia Jones 98 92 92 99 99 93
Jack Benedict 54 63 71 49 82 69
Rene Porter 100 62 88 74 98 92
;
run;
proc print data = grades;
var name e1 e2 e3 e4 p1 f1;
run;
OBS | name | e1 | e2 | e3 | e4 | p1 | f1 |
---|---|---|---|---|---|---|---|
1 | Alexander Smith | 78 | 82 | 86 | 69 | 97 | 80 |
2 | John Simon | 88 | 72 | 86 | . | 100 | 85 |
3 | Patricia Jones | 98 | 92 | 92 | 99 | 99 | 93 |
4 | Jack Benedict | 54 | 63 | 71 | 49 | 82 | 69 |
5 | Rene Porter | 100 | 62 | 88 | 74 | 98 | 92 |
このデータセットには、学生の名前(name)、4回の試験の点数(e1、e2、e3、e4)、プロジェクトの点数(p1)、期末試験の点数(f1)が含まれています。
注意点がいくつかあります。例としての都合上、ここではDATALINESステートメントを使ってデータを読み込んでいますが、INFILEステートメントを使っても動作は同じです。また簡単にするため永久データセットでなく一時データセットを作成しています。最後に各データステップの後で、PRINTプロシージャを使ってSASデータセットの全部または一部を確認できるようにしています。
例#
次のプログラムは、とてもシンプルな割り当てステートメントで、各学生の4回の試験の点数を足し合わせ、その合計を新しい数値変数「examtotal」に格納します。
data grades;
input name $ 1-15 e1 e2 e3 e4 p1 f1;
* add up each students four exam scores and store it in examtotal;
* 各学生の4回の試験の点数を足し合わせ、examtotalに格納する;
examtotal = e1 + e2 + e3 + e4;
datalines;
Alexander Smith 78 82 86 69 97 80
John Simon 88 72 86 . 100 85
Patricia Jones 98 92 92 99 99 93
Jack Benedict 54 63 71 49 82 69
Rene Porter 100 62 88 74 98 92
;
run;
proc print data = grades;
var name e1 e2 e3 e4 examtotal;
run;
OBS | name | e1 | e2 | e3 | e4 | examtotal |
---|---|---|---|---|---|---|
1 | Alexander Smith | 78 | 82 | 86 | 69 | 315 |
2 | John Simon | 88 | 72 | 86 | . | . |
3 | Patricia Jones | 98 | 92 | 92 | 99 | 381 |
4 | Jack Benedict | 54 | 63 | 71 | 49 | 237 |
5 | Rene Porter | 100 | 62 | 88 | 74 | 324 |
新しい変数名「examtotal」が等号の左側にあり、4回の試験の点数を足し合わせる式(e1+e2+e3+e4)が等号の右側にあることに注目してください。
SASプログラムを開いて実行し、PRINTプロシージャの出力を確認して、新しい数値変数「examtotal」が確かに4回の試験の合計点であることを確認してください。また、SASがデータの一部が欠損している場合にどう処理されるかも確認してください。John Simonの3つの試験の点数を足し合わせるのではなく、SASは彼の「examtotal」に欠損値を割り当てています。よく考えれば、これは正しい処理です。そうしないと、彼の 「examtotal」が他の学生と根本的に異なるのかがわからなくなってしまいます。ここで重要なのは、SASが様々な計算を行う際にはどのように欠損値を扱うかを常に意識しておくことです。
例#
前の例では、まだ存在しない変数名を使って新しい変数をデータセットに作成しました。既存の変数名を使って、その変数の値を変更することもできます。次のプログラムは、もし教師が2回目の試験の点数「e2」を各学生で8点加算したい場合、どのように変更するかを示しています。
data grades;
input name $ 1-15 e1 e2 e3 e4 p1 f1;
e2 = e2 + 8; * add 8 to each student's second exam score (e2);
* 各学生の2回目の試験の点数(e2)に8を加算する;
datalines;
Alexander Smith 78 82 86 69 97 80
John Simon 88 72 86 . 100 85
Patricia Jones 98 92 92 99 99 93
Jack Benedict 54 63 71 49 82 69
Rene Porter 100 62 88 74 98 92
;
run;
proc print data = grades;
var name e1 e2 e3 e4 p1 f1;
run;
OBS | name | e1 | e2 | e3 | e4 | p1 | f1 |
---|---|---|---|---|---|---|---|
1 | Alexander Smith | 78 | 90 | 86 | 69 | 97 | 80 |
2 | John Simon | 88 | 80 | 86 | . | 100 | 85 |
3 | Patricia Jones | 98 | 100 | 92 | 99 | 99 | 93 |
4 | Jack Benedict | 54 | 71 | 71 | 49 | 82 | 69 |
5 | Rene Porter | 100 | 70 | 88 | 74 | 98 | 92 |
変更する変数名「e2」が等号の左側に、「e2」に8を加算する算術式(e2+8)が等号の右側にあることに注目してください。一般に、変数名が等号の両側にある場合、右側の元の値が式を評価するために使われます。次に、式の結果が等号の左側の変数に割り当てられます。\ プログラムを開いて実行し、PRINTプロシージャの出力を確認して、数値変数「e2」の値が元の値より8高いことを確かめてください。
算術演算子を使った算術計算#
これまでは変数の足し算しかしていませんでしたが、引き算、掛け算、割り算、べき乗もできます。ただし、SASが認識する記号を使う必要があります。
操作 |
記号 |
代入文 |
アクション |
---|---|---|---|
加算 |
+ |
a = b + c ; |
bとcを加算 |
減算 |
- |
a = b - c ; |
bからcを減算 |
乗算 |
* |
a = b * c ; |
bとcを乗算 |
除算 |
/ |
a = b / c ; |
bをcで除算 |
指数 |
** |
a = b ** c ; |
bをc乗する |
負の接頭辞 |
- |
a = - b ; |
bの負値をとる |
他のプログラミング言語と同様、1つの割り当てステートメントで複数の演算を行うことができます。演算の順序は通常の数学の式と同じです。
べき乗が最初に実行され、次に乗算と除算、最後に加算と減算
加算のみ、減算のみ、または加算と減算が同じ式内にある場合、左から右へ実行されます
乗算のみ、除算のみ、または乗算と除算が同じ式内にある場合、左から右へ実行されます
べき乗が複数ある場合、右から左へ実行されます
かっこ内の演算が最初に実行されます
最後のものが特に役に立つと思います。かっこを使って最初に計算したい部分を明示すれば、他のルールをあまり気にする必要がありません。次の2つの例を見てみましょう。
例#
次の例は、標準的な計算の順序を示す計算を含んでいます。ある統計学の教師が、試験の平均点に0.6、プロジェクト点数に0.2、期末試験に0.2をそれぞれ重み付けして最終成績を計算するとします。次のプログラムでは、教師が(間違って)学生の最終成績を計算しています。
data grades;
input name $ 1-15 e1 e2 e3 e4 p1 f1;
final = 0.6*e1+e2+e3+e4/4 + 0.2*p1 + 0.2*f1;
datalines;
Alexander Smith 78 82 86 69 97 80
John Simon 88 72 86 . 100 85
Patricia Jones 98 92 92 99 99 93
Jack Benedict 54 63 71 49 82 69
Rene Porter 100 62 88 74 98 92
;
run;
proc print data = grades;
var name e1 e2 e3 e4 p1 f1 final;
run;
OBS | name | e1 | e2 | e3 | e4 | p1 | f1 | final |
---|---|---|---|---|---|---|---|---|
1 | Alexander Smith | 78 | 82 | 86 | 69 | 97 | 80 | 267.45 |
2 | John Simon | 88 | 72 | 86 | . | 100 | 85 | . |
3 | Patricia Jones | 98 | 92 | 92 | 99 | 99 | 93 | 305.95 |
4 | Jack Benedict | 54 | 63 | 71 | 49 | 82 | 69 | 208.85 |
5 | Rene Porter | 100 | 62 | 88 | 74 | 98 | 92 | 266.50 |
まあ、教師は統計学に専念し、数学はやめたほうがいいかもしれません。割り当てステートメントを見ると、教師は4回の試験の点数を足して4で割り、その結果に0.6を掛けることで試験の平均点を求めようとしているのがわかります。しかし、SASはそうはしていません。SASプログラムを実行し、出力を確認して、Alexander Smithの場合にSASが何をしているのか確認してみてください。まだわからない場合は、計算の順序のルールをもう一度確認してください。ルールによると、SASは最初に次のことを行います。
Alexander の1回目の試験の点数78に0.6を掛けて46.8を得る
Alexander の4回目の試験の点数69を4で割って17.25を得る
Alexander のプロジェクト点数97に0.2を掛けて19.4を得る
Alexander の期末試験の点数80に0.2を掛けて16.0を得る
次に、SASはすべての加算を行います。46.8 + 82 + 86 + 17.25 + 19.4 + 16.0 = 267.45となり、これがAlexanderの最終成績になります。たぶんAlexanderはこの点数を望むかもしれませんが、根本的に間違っています。かっこを利用して、統計学の教師を正しい方向に導いてみましょう。
例#
次の例は、標準的な順序による計算を含んでいます。ある統計学の教師が、試験の平均点に0.6、プロジェクト点数に0.2、期末試験に0.2をそれぞれ重み付けして最終成績を計算するとします。次のプログラムは、教師が(正しく)学生の最終成績を計算する方法を示しています。
data grades;
input name $ 1-15 e1 e2 e3 e4 p1 f1;
final = 0.6*((e1+e2+e3+e4)/4) + 0.2*p1 + 0.2*f1;
datalines;
Alexander Smith 78 82 86 69 97 80
John Simon 88 72 86 . 100 85
Patricia Jones 98 92 92 99 99 93
Jack Benedict 54 63 71 49 82 69
Rene Porter 100 62 88 74 98 92
;
run;
proc print data = grades;
var name e1 e2 e3 e4 p1 f1 final;
run;
OBS | name | e1 | e2 | e3 | e4 | p1 | f1 | final |
---|---|---|---|---|---|---|---|---|
1 | Alexander Smith | 78 | 82 | 86 | 69 | 97 | 80 | 82.65 |
2 | John Simon | 88 | 72 | 86 | . | 100 | 85 | . |
3 | Patricia Jones | 98 | 92 | 92 | 99 | 99 | 93 | 95.55 |
4 | Jack Benedict | 54 | 63 | 71 | 49 | 82 | 69 | 65.75 |
5 | Rene Porter | 100 | 62 | 88 | 74 | 98 | 92 | 86.60 |
Alexander の最終成績の計算を再度見てみましょう。final
の割り当てステートメントでは、SASに次のことを指示しています。
まずAlexanderの4回の試験の点数(78、82、86、69)を足して315を得る
その合計315を4で割り、試験の平均点78.75を得る
その平均点78.75に0.6を掛けて47.25を得る
Alexanderのプロジェクト点数97に0.2を掛けて19.4を得る
Alexanderの期末試験の点数80に0.2を掛けて16.0を得る
次に、SASは最後の3つの項目を足し合わせます。
47.25 + 19.4 + 16.0 = 82.65
これがAlexanderの最終成績になります。そうですね、申し訳ありませんAlexander。
SASプログラムを開いて実行し、PRINTプロシージャの出力を確認して、最終成績が教師の意図どおりに計算されていることを確認してください。
再度にはなりますが、John Simonの最終成績が欠損となっている点には注意してください。
前の例では、4つの試験の点数を足して4で割ることで平均点を計算しましたが、SASの数多くある関数の1つであるMEAN関数を使うこともできます。
数値関数#
他のプログラミング言語(C++やS-Plusなど)と同様に、SAS関数はプログラムされたルーチンで、1つ以上の引数から計算された値を返します。SAS関数の標準的な形式は次のとおりです。
functionname(argument1, argument2,…);
例えば、変数a、b、cを足し合わせてその結果を変数dに割り当てる場合、SASの関数SUMを使った正しい割り当てステートメントの形式は次のようになります。
d = sum(a, b, c) ;
この場合、sumが関数名で、dが関数SUMの結果を割り当てる対象の変数です。そして、a、b、cが関数の引数になります。一部の関数は特定の数の引数を必要とし、他の関数(SUMなど)では任意の数の引数を取ることができます。また、引数を必要としない関数もあります。次の例で示すように、引数には変数名、定数、または式を使うこともできます。
SASには、算術、金融、統計、確率の関数があります。これらの関数をすべて詳しく見ていくことはできませんが、いくつかの例を見ていきましょう。
例#
前の例では、4回の試験の点数を足して4で割ることで平均点を計算しました。代わりにMEAN関数を使うこともできます。次のプログラムは、自分で定義する方法と MEAN関数の2つの方法で平均点を計算する方法を示しています。
data grades;
input name $ 1-15 e1 e2 e3 e4 p1 f1;
* calculate the average by definition;
* 定義に従って平均を計算する;
avg1 = (e1+e2+e3+e4)/4;
* calculate the average using the mean function;
* MEAN関数を使って平均を計算する;
avg2 = mean(e1,e2,e3,e4);
datalines;
Alexander Smith 78 82 86 69 97 80
John Simon 88 72 86 . 100 85
Patricia Jones 98 92 92 99 99 93
Jack Benedict 54 63 71 49 82 69
Rene Porter 100 62 88 74 98 92
;
run;
proc print data = grades;
var name e1 e2 e3 e4 avg1 avg2;
run;
OBS | name | e1 | e2 | e3 | e4 | avg1 | avg2 |
---|---|---|---|---|---|---|---|
1 | Alexander Smith | 78 | 82 | 86 | 69 | 78.75 | 78.75 |
2 | John Simon | 88 | 72 | 86 | . | . | 82.00 |
3 | Patricia Jones | 98 | 92 | 92 | 99 | 95.25 | 95.25 |
4 | Jack Benedict | 54 | 63 | 71 | 49 | 59.25 | 59.25 |
5 | Rene Porter | 100 | 62 | 88 | 74 | 81.00 | 81.00 |
プログラムを実行し、PRINTプロシージャの出力を確認すると、2つの平均点算出方法で同じ結果になることがわかります。
おっと!何が起こったのでしょうか?MEAN関数を使って平均点を計算した場合、John Simonの平均点が82と表示されていますが、定義を使った場合は欠損値になっています。結果を見ると、MEAN関数を使って平均を計算する場合、SASは欠損値を無視し、利用可能な値から平均を計算することがわかります。
どちらの方法が適切かについて、一般的な言明はできません。状況とプログラマーの意図次第です。ここで重要なのは、SASが様々な計算方法でどのように欠損値を扱うかを知ることです。このコースですべての可能な計算や関数を扱うことはできません。ですので、典型的ないくつかのオブザベーションで計算をチェックし、SASプログラミングが意図した通りに動作することを確認することを、良いプログラミングの習慣に加えるとよいでしょう。
SASヘルプおよびドキュメンテーション(「By Category」の下の「Functions」)を参照すれば、SASに組み込まれているすべての数値関数を確認できますが、以下で統計解析で役立つ一般的な数値関数をいくつか紹介しておきます。
一般的な関数 |
例 |
---|---|
INT: 数値の整数部分 |
a = int(x); |
ABS: 引数の絶対値 |
a = abs(x); |
SQRT: 引数の平方根 |
a = sqrt(x); |
MIN: 引数の最小値 |
a = min(x, y, z); |
MAX: 引数の最大値 |
a = max(x, y, z); |
SUM: 引数の和 |
a = sum(x, y, z); |
MEAN: 引数の平均 |
a = mean(x, y, z); |
ROUND: 指定された単位で引数を丸める |
a = round(x, 1); |
LOG: 引数の自然対数(底eの対数) |
a = log(x); |
LAG: 前のオブザベーションでの引数の値 |
a = lag(x); |
DIF: 現在のオブザベーションと前のオブザベーションでの引数の値の差 |
a = dif(x); |
N: 引数の欠損値でない値の数 |
a = n(x); |
NMISS: 引数の欠損値の数 |
a = nmiss(x); |
私はINT関数を何度か使ったことがあります。例えば、この地域のエリアコードは814です。電話番号が812341230のように数値で格納されている場合、INT関数を使ってエリアコードを抽出することができます。INT関数のこの使い方の例を見てみましょう。
INT関数は数値の最初の何桁かだけが必要なときに使うことがあります。例えばPennsylvaniaの地域コードが814でその地域の電話番号が8142341230なら、INT関数により電話番号から地域コードを得ることができます。このような使い方の例を見てみましょう。
例#
次のプログラムは、INT関数を使って10桁の電話番号から地域コードを抽出する方法を示しています。
data grades;
input name $ 1-15 phone e1 e2 e3 e4 p1 f1;
areacode = int(phone/10000000);
datalines;
Alexander Smith 8145551212 78 82 86 69 97 80
John Simon 8145562314 88 72 86 . 100 85
Patricia Jones 7175559999 98 92 92 99 99 93
Jack Benedict 5705551111 54 63 71 49 82 69
Rene Porter 8145542323 100 62 88 74 98 92
;
run;
proc print data = grades;
var name phone areacode;
run;
OBS | name | phone | areacode |
---|---|---|---|
1 | Alexander Smith | 8145551212 | 814 |
2 | John Simon | 8145562314 | 814 |
3 | Patricia Jones | 7175559999 | 717 |
4 | Jack Benedict | 5705551111 | 570 |
5 | Rene Porter | 8145542323 | 814 |
要するにINT関数はかっこ内の式の整数部分を返します。したがって電話番号が8145562314の場合、int(phone/10000000)はint(814.5562314)となり、意図するとおり地域コード814になります。プログラムを開いて実行し、PRINTプロシージャの出力を確認して、地域コードが正しく計算されていることを確かめてください。
例#
SASでは関数をネストすることもできます(他のほとんどのプログラミング言語と同様)。つまり、関数の中で別の関数を計算することができます。関数をネストする場合、SASは内側の関数から順に実行します。つまり、最も内側の関数のアクションを最初に実行し、その結果を次の関数の引数として使用し、以下同様に実行していきます。引数の要件を満たしていれば、任意の関数をネストできます。次のプログラムは、試験の平均点を最近接の単位に丸める際に、関数をネストしている例を示しています。
data grades;
input name $ 1-15 e1 e2 e3 e4 p1 f1;
*calculate the average using the mean function and then round it to the nearest digit;
*MEAN関数で平均を計算し、その結果を最近接の整数に丸める;
avg = round(mean(e1,e2,e3,e4),1);
datalines;
Alexander Smith 78 82 86 69 97 80
John Simon 88 72 86 . 100 85
Patricia Jones 98 92 92 99 99 93
Jack Benedict 54 63 71 49 82 69
Rene Porter 100 62 88 74 98 92
;
run;
proc print data = grades;
var name e1 e2 e3 e4 avg;
run;
OBS | name | e1 | e2 | e3 | e4 | avg |
---|---|---|---|---|---|---|
1 | Alexander Smith | 78 | 82 | 86 | 69 | 79 |
2 | John Simon | 88 | 72 | 86 | . | 82 |
3 | Patricia Jones | 98 | 92 | 92 | 99 | 95 |
4 | Jack Benedict | 54 | 63 | 71 | 49 | 59 |
5 | Rene Porter | 100 | 62 | 88 | 74 | 81 |
例えば、Alexanderの4回の試験の平均は78.75です(78、82、86、69を足して4で割ったもの)。そのため、avgを計算する際に78.75がROUND関数の引数となり、丸め単位を1で丸められて79になります。プログラムを開いて実行し、PRINTプロシージャの出力を確認して、試験の平均が意図のとおりに丸められていることを確かめてください。
文字変数の割り当て#
これまでの例は数値変数についてのみでした。次にデータセットに新しい文字変数を追加したり、既存の文字変数の値を変更する方法を見ていきましょう。データの入出力では、INPUTステートメントで変数名の後にドル記号($)を付けることで、文字変数の値を読み込む方法を学びました。ここでは割り当てステートメントを使って文字変数を更新したり(または新しい文字変数を作成したり)します。
例#
データセットに新しい文字変数を作成する際には、ほとんどの場合、特定の条件に基づいて値を割り当てたいでしょう。例えば、教師が最終成績に基づいて、学生の「合格」または「不合格」状況を示す文字変数「status」を作成したいとします。65点未満は不合格、65点以上は合格とみなすとします。この場合、if-then-elseステートメントを使う必要があります。if-then-elseステートメントの詳細は後で扱いますが、ここでは基本的な考え方がわかるはずです。次のSASプログラムは、割り当てステートメントとif-then-elseステートメントを組み合わせて、新しい文字変数「status」を作成する例を示しています。
data grades;
input name $ 1-15 e1 e2 e3 e4 p1 f1;
* calculate the average using the mean function;
* MEAN関数で平均を計算する;
avg = mean(e1,e2,e3,e4);
* if the average is less than 65 indicate failed, otherwise indicate passed;
* 平均が65未満なら不合格、それ以外は合格を示す;
if (avg < 65) then status = 'Failed';
else status = 'Passed';
datalines;
Alexander Smith 78 82 86 69 97 80
John Simon 88 72 86 . 100 85
Patricia Jones 98 92 92 99 99 93
Jack Benedict 54 63 71 49 82 69
Rene Porter 100 62 88 74 98 92
;
run;
proc print data = grades;
var name e1 e2 e3 e4 avg status;
run;
OBS | name | e1 | e2 | e3 | e4 | avg | status |
---|---|---|---|---|---|---|---|
1 | Alexander Smith | 78 | 82 | 86 | 69 | 78.75 | Passed |
2 | John Simon | 88 | 72 | 86 | . | 82.00 | Passed |
3 | Patricia Jones | 98 | 92 | 92 | 99 | 95.25 | Passed |
4 | Jack Benedict | 54 | 63 | 71 | 49 | 59.25 | Failed |
5 | Rene Porter | 100 | 62 | 88 | 74 | 81.00 | Passed |
SASプログラムを実行し、PRINTプロシージャの出力を確認すると、文字変数「status」の値が正しく割り当てられていることがわかります。文字変数の値を割り当てステートメントで指定する際は、値を引用符で囲む必要があることに注意してください。
単引用符と二重引用符のどちらを使ってもかまいません。上のプログラムの単引用符を二重引用符に変更して再実行すれば、文字の値が同様に割り当てられることを確認できます。
データの変換#
売上収益を計算するために価格と販売数量が必要だと言われたとしましょう。かなり簡単ですね。価格と販売数量がデータセットで数値変数として格納されていれば、次のような割り当てステートメントを使えばよいでしょう。
sales = price * units;
しかし、もし価格と販売数量が文字変数として格納されている場合はどうでしょうか。そうすると、文字変数の価格と販売数量を掛け合わせるのはおかしなことになってしまいます。その場合、文字変数の価格と販売数量を最初に数値変数に変換する必要があります。SASがそれをどのように助けてくれるかが、このセクションのテーマです。具体的には、文字値を数値に変換するINPUT関数について扱います。
実際のところ、SASはかなりスマートなアプリケーションです。文字変数に対して、数値変数にのみ適用できる操作を試みると、SASは自動的にその文字変数を数値に変換しようとします。しかし、このような怠惰なアプローチを取ると、期待したようには動作しないことがあります。そのため、このレッスンの結論となりますが、常に自分でINPUT関数を使ってデータ型の変換を行うことが最善の方法であることがわかるはずです。
例#
次のプログラムでは、SASが自動的に文字から数値への変換を試み、算術演算を実行できるようにしています。
data grades;
input name $ 1-15 e1 $ e2 $ e3 $ e4 $ standtest $;
avg = round(mean(e1,e2,e3,e4),1);
std = standtest/4;
datalines;
Alexander Smith 78 82 86 69 1,210
John Simon 88 72 86 . 990
Patricia Jones 98 92 92 99 1,010
Jack Benedict 54 63 71 49 875
Rene Porter 100 62 88 74 1,180
;
run;
23 SAS システム 2024年 6月 6日 木曜日 07時40分00秒
255 ods listing close;ods html5 (id=saspy_internal) file=_tomods1 options(bitmap_mode='inline') device=svg style=HTMLBlue;
255 ! ods graphics on / outputfmt=png;
256
257 options notes ;
258 data grades;
259 input name $ 1-15 e1 $ e2 $ e3 $ e4 $ standtest $;
260 avg = round(mean(e1,e2,e3,e4),1);
261 std = standtest/4;
262 datalines;
NOTE: 以下の箇所で文字値を数値に変換しました。(行:カラム)
260:20 260:23 260:26 260:29 261:9
NOTE: 無効な数値データstandtest='1,210'が行 261 カラム 9にあります。
RULE: ----+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0
263 Alexander Smith 78 82 86 69 1,210
name=Alexander Smith e1=78 e2=82 e3=86 e4=69 standtest=1,210 avg=79 std=. _ERROR_=1 _N_=1
NOTE: 無効な数値データstandtest='1,010'が行 261 カラム 9にあります。
265 Patricia Jones 98 92 92 99 1,010
name=Patricia Jones e1=98 e2=92 e3=92 e4=99 standtest=1,010 avg=95 std=. _ERROR_=1 _N_=3
NOTE: 無効な数値データstandtest='1,180'が行 261 カラム 9にあります。
267 Rene Porter 100 62 88 74 1,180
name=Rene Porter e1=100 e2=62 e3=88 e4=74 standtest=1,180 avg=81 std=. _ERROR_=1 _N_=5
NOTE: 欠損値を含んだ計算により、以下の箇所で欠損値が生成されました。
(回数)(行:カラム)
3 261:18
NOTE: データセットWORK.GRADESは5オブザベーション、8変数です。
NOTE: DATA ステートメント処理(合計処理時間):
処理時間 0.00 秒
ユーザーCPU時間 0.00 秒
システムCPU時間 0.00 秒
メモリ 538.50k
OSメモリ 18336.00k
タイムスタンプ 2024/06/06 午前07:41:22
ステップ数 20 スイッチ数 2
ページフォルト回数 0
ページリクレーム回数 132
ページスワップ回数 0
自発的コンテキストスイッチ回数 8
非自発的コンテキストスイッチ回数 0
ブロック入力操作回数 0
ブロック出力操作回数 264
268 ;
269 run;
270
271
272 ods html5 (id=saspy_internal) close;ods listing;
273
24 SAS システム 2024年 6月 6日 木曜日 07時40分00秒
274
最初に、なぜかデータセットのすべてのデータが文字データとして読み込まれていることに注目してください。つまり、試験の点数(e1、e2、e3、e4)と標準化テストの点数(standtest)も文字変数として格納されています。次に、SASが試験点数の平均(avg)を計算する際、e1、e2、e3、e4を数値変数に変換しようと試みます。同様に、新しい標準化テストの点数(std)を計算する際、SASは「standtest」を数値変数に変換しようと試みます。それがうまくいったかどうか見てみましょう。
プログラムを開いて実行し、出力ウィンドウを確認する前に、ログウィンドウを確認してください。上のようなメッセージが表示されているはずです。
最初のNOTEは、SASが自動的に文字から数値への変換を行ったことを警告するための標準的なメッセージです。次に、「standtest」の値1,210、1,010、1,180の無効な数値データに関する3つのNOTEが表示されます。これらの数値にカンマが入っているため、SASが混乱しているのです。一般に、自動変換では、標準的な数値(0、1、…、9の数字、小数点、プラス記号やマイナス記号以外の文字を含む)に準拠しない文字値は、すべて数値の欠損値に変換されてしまいます。そのため、5番目のNOTEで欠損値が生成されたことが通知されています。結果を出力すると以下のようになります。
proc print data = grades;
run;
OBS | name | e1 | e2 | e3 | e4 | standtest | avg | std |
---|---|---|---|---|---|---|---|---|
1 | Alexander Smith | 78 | 82 | 86 | 69 | 1,210 | 79 | . |
2 | John Simon | 88 | 72 | 86 | 990 | 82 | 247.50 | |
3 | Patricia Jones | 98 | 92 | 92 | 99 | 1,010 | 95 | . |
4 | Jack Benedict | 54 | 63 | 71 | 49 | 875 | 59 | 218.75 |
5 | Rene Porter | 100 | 62 | 88 | 74 | 1,180 | 81 | . |
自動変換の最終的な結果がわかります。「avg」の計算は問題なく行われましたが、e1、e2、e3、e4には標準的な数値が含まれていたからです。一方、「std」の計算は「standtest」に標準的でない数値が含まれていたため失敗しました。文字から数値への変換を自分で制御することにしましょう。
例#
次のプログラムは、INPUT関数を使って文字変数「standtest」を明示的に数値変数に変換し、算術演算を行う例を示しています。
data grades;
input name $ 1-15 e1 $ e2 $ e3 $ e4 $ standtest $;
std = input(standtest,comma5.)/4;
datalines;
Alexander Smith 78 82 86 69 1,210
John Simon 88 72 86 . 990
Patricia Jones 98 92 92 99 1,010
Jack Benedict 54 63 71 49 875
Rene Porter 100 62 88 74 1,180
;
run;
proc print data = grades;
var name standtest std;
run;
OBS | name | standtest | std |
---|---|---|---|
1 | Alexander Smith | 1,210 | 302.50 |
2 | John Simon | 990 | 247.50 |
3 | Patricia Jones | 1,010 | 252.50 |
4 | Jack Benedict | 875 | 218.75 |
5 | Rene Porter | 1,180 | 295.00 |
「std」の計算方法と、前の例での計算方法の唯一の違いは、「standtest」がここではINPUT関数に渡されていることです。INPUT関数の一般的な形式は次のとおりです。
INPUT(source, informat)
ここで、
sourceは数値変数に変換される文字変数、定数、または式
informatはsourceに格納された値を読み取るための数値informat
この場合、変換したい文字変数は「standtest」です。「standtest」の値はcomma5.informatに準拠しているため、INPUT関数でそのinformatを指定しています。
うまくいったかどうか確認しましょう。プログラムを開いて実行し、出力ウィンドウを確認する前に、今度はログウィンドウを確認してください。以下のようなメッセージが表示されるはずです。
data grades;
input name $ 1-15 e1 $ e2 $ e3 $ e4 $ standtest $;
std = input(standtest,comma5.)/4;
datalines;
Alexander Smith 78 82 86 69 1,210
John Simon 88 72 86 . 990
Patricia Jones 98 92 92 99 1,010
Jack Benedict 54 63 71 49 875
Rene Porter 100 62 88 74 1,180
;
run;
29 SAS システム 2024年 6月 6日 木曜日 07時40分00秒
312 ods listing close;ods html5 (id=saspy_internal) file=_tomods1 options(bitmap_mode='inline') device=svg style=HTMLBlue;
312 ! ods graphics on / outputfmt=png;
NOTE: HTML5(SASPY_INTERNAL) Bodyファイルの書き込み先: _TOMODS1
313
314 data grades;
315 input name $ 1-15 e1 $ e2 $ e3 $ e4 $ standtest $;
316 std = input(standtest,comma5.)/4;
317 datalines;
NOTE: データセットWORK.GRADESは5オブザベーション、7変数です。
NOTE: DATA ステートメント処理(合計処理時間):
処理時間 0.00 秒
ユーザーCPU時間 0.00 秒
システムCPU時間 0.00 秒
メモリ 536.21k
OSメモリ 18336.00k
タイムスタンプ 2024/06/06 午前07:41:43
ステップ数 24 スイッチ数 2
ページフォルト回数 0
ページリクレーム回数 130
ページスワップ回数 0
自発的コンテキストスイッチ回数 9
非自発的コンテキストスイッチ回数 0
ブロック入力操作回数 0
ブロック出力操作回数 264
323 ;
324 run;
325
326
327 ods html5 (id=saspy_internal) close;ods listing;
328
30 SAS システム 2024年 6月 6日 木曜日 07時40分00秒
329
SASが自動的に変換を行ったことを警告するメッセージはありません。今回は私たちがプログラムをコントロールしているからです。結果を出力してみると(コードは前のものを参照)、今回は「std」を正しく計算できていることがわかります。これでよくなりました!
最後にコメントを2点。まず、今回の議論を踏まえて、良いプログラミングの習慣のリストに新しい項目を追加することをお勧めします。可能な限りプログラムをコントロールし、プログラムが意図したとおりに動作することを常に把握しておくことです。状況によってプログラムが期待通りに動作しない場合は、動作するようにプログラムを書き換えましょう。
2つ目は、文字から数値への変換ばかり話してきましたが、逆に数値から文字への変換が必要な場合はどうしたらよいか不安になるかもしれません。それについては心配無用です。SASなら対応できます。数値変数に対して文字変数にのみ適用できる操作を試みると、SASは自動的にその数値変数を文字に変換しようと試みます。うまくいかない場合は、PUT関数を使って明示的に数値値を文字に変換する必要があります。PUT関数については、文字関数についての際に扱います。
If-Then ステートメント#
ここでは、データセットの一部のオブザベーションを変更する様々な例を扱っていきます。SASで特定の条件を満たすオブザベーションを選択する最も一般的な方法は、if-thenステートメントを利用することです。if-thenステートメントの基本的な形式は次のとおりです。
IF (条件が真である) THEN (このアクションを実行する);
これまでに、次のような条件の例を見ました。
avg < 65
そして、アクションは次のようになります。
status = 'Failed'
各オブザベーションに対して、SASはIFキーワードに続く条件(この場合は学生の平均点が65未満かどうか)を評価し、真または偽を判断します。条件が真の場合、SASはTHEN キーワードに続くアクション(この場合は学生のステータスを’Failed’に変更する)を実行します。条件が偽の場合、SASはTHENを無視し、データステップの次のステートメントに進みます。条件には常に何らかの比較が含まれ、実行されるアクションは通常、何らかの割り当てステートメントになります。
例#
この例では新しいことは特に無く、前のレッスンで既にif-then(-else)ステートメントを見ています。そこでは主に割り当てステートメントに焦点を当てていました。ここでは、条件部分を含むif-thenステートメント全体に注目します。次のプログラムは、学生の1回目の試験の点数が65点未満の場合にステータスを’Failed’に設定する文字変数「status」を作成しています。
data grades;
input name $ 1-15 e1 e2 e3 e4 p1 f1;
* if the first exam is less than 65 indicate failed;
* 1回目の試験が65点未満の場合は不合格を示す;
if (e1 < 65) then status = 'Failed';
datalines;
Alexander Smith 78 82 86 69 97 80
John Simon 88 72 86 . 100 85
Patricia Jones 98 92 92 99 99 93
Jack Benedict 54 63 71 49 82 69
Rene Porter 100 62 88 74 98 92
;
run;
proc print data = grades;
var name e1 status;
run;
OBS | name | e1 | status |
---|---|---|---|
1 | Alexander Smith | 78 | |
2 | John Simon | 88 | |
3 | Patricia Jones | 98 | |
4 | Jack Benedict | 54 | Failed |
5 | Rene Porter | 100 |
以降でもデータセット「grades」を使用します。再度データセットについて説明しておくと、学生の名前(name)、4つの試験の結果(e1,e2,e3,e4)、プロジェクトの結果(p1)、最終試験(f1)の結果が含まれています。SASプログラムを開いて実行し、PRINTプロシージャの出力を確認すると、文字変数「status」の値が正しく割り当てられていることがわかります。
比較演算子#
前の例では、小なり記号(<)を使って比較を行いました。SASの構文に従えば、標準的な比較演算子を使ってどのような比較も行えます。
比較 |
SAS構文 |
代替のSAS構文 |
---|---|---|
小なり |
< |
LT |
大なり |
> |
GT |
以下 |
<= |
LE |
以上 |
>= |
GE |
等しい |
= |
EQ |
等しくない |
^= |
NE |
リストのいずれかに等しい |
in |
IN |
どちらの構文を使ってもかまいません。単なる好みの問題です。代替のSAS構文の動作を確認するには、以前のプログラムの小なり記号(<)を”LT”(または”lt”)に置き換えてみてください。次に、プログラムを再実行し、PRINTプロシージャの出力を確認して、プログラムが期待通りに動作することを確認してみてください。
例#
次のプログラムは、IN演算子を使って、プロジェクト点数p1が98、99、100のいずれかの学生を特定しています。つまり、p1の値が98、99、100のいずれかに等しい学生には、’Excellent’という値が変数「project」に割り当てられます。
data grades;
input name $ 1-15 e1 e2 e3 e4 p1 f1;
if p1 in (98, 99, 100) then project = 'Excellent';
datalines;
Alexander Smith 78 82 86 69 97 80
John Simon 88 72 86 . 100 85
Patricia Jones 98 92 92 99 99 93
Jack Benedict 54 63 71 49 82 69
Rene Porter 100 62 88 74 98 92
;
run;
proc print data = grades;
var name p1 project;
run;
OBS | name | p1 | project |
---|---|---|---|
1 | Alexander Smith | 97 | |
2 | John Simon | 100 | Excellent |
3 | Patricia Jones | 99 | Excellent |
4 | Jack Benedict | 82 | |
5 | Rene Porter | 98 | Excellent |
プログラムを開いて実行し、PRINTプロシージャの出力を確認すると、プログラムが説明どおりに動作していることがわかります。 注: 比較演算子を扱った後では、割り当てステートメントでEQの構文を使いたくなることがよくあります。試してみると、SASがエラーを出すことがわかるはずです。割り当てステートメントでは必ず等号(=)を使わなければなりません。
代替のアクション: ELSEステートメント#
if-thenステートメントだけでなく、if-then-elseステートメントを使いたい場合もあるでしょう。前の例では、IF キーワードに続く条件が真の場合に SAS がどうするかだけ指示しました。elseステートメントを含めることで、条件が偽の場合にどうするかを指示できます。
例#
次のプログラムでは、学生の1回目の試験の点数が65点未満の場合はステータスを “Failed” に設定し、それ以外の場合は “Passed” に設定する文字変数「status」を作成しています。
data grades;
input name $ 1-15 e1 e2 e3 e4 p1 f1;
* if the first exam is less than 65 indicate failed;
* 1回目の試験が65点未満の場合は不合格を示す;
if (e1 < 65) then status = 'Failed';
* otherwise indicate passed;
* それ以外の場合は合格を示す;
else status = 'Passed';
datalines;
Alexander Smith 78 82 86 69 97 80
John Simon 88 72 86 . 100 85
Patricia Jones 98 92 92 99 99 93
Jack Benedict 54 63 71 49 82 69
Rene Porter 100 62 88 74 98 92
;
run;
proc print data = grades;
var name e1 status;
run;
OBS | name | e1 | status |
---|---|---|---|
1 | Alexander Smith | 78 | Passed |
2 | John Simon | 88 | Passed |
3 | Patricia Jones | 98 | Passed |
4 | Jack Benedict | 54 | Failed |
5 | Rene Porter | 100 | Passed |
プログラムを開いて実行し、PRINTプロシージャの出力を確認すると、文字変数「status」の値が正しく割り当てられていることがわかります。
一般にELSEステートメントをIF-THENステートメントと組み合わせると、リソースを節約できます。
ELSEステートメントなしでIF-THENステートメントを使うと、SASはすべてのIF-THENステートメントを評価します。
ELSEステートメント付きのIF-THENステートメントを使うと、SASは最初に真のステートメントを検出するまでIF-THENステートメントを実行し、それ以降のIF-THENステートメントを評価されません。
効率を高めるのであれば、 IF-THEN-ELSEステートメントは生じる確率順に書くようにします。
論理演算子#
以前に学習した比較演算子に加えて、次の論理演算子も使用できます:
演算 |
SAS構文 |
別のSAS構文 |
---|---|---|
両方の条件が真であるか? |
& |
AND |
どちらかの条件が真であるか? |
| |
OR |
比較の論理を逆転させる |
^ or ~ |
NOT |
AND演算子を使うと、ANDでリンクされた両方の式が真の場合にTHENステートメントが実行されます。例えば:
IF (p1 > 90) AND (f1 > 90) THEN performance = 'excellent';
OR演算子を使うと、ORでリンクされた式のいずれかが真の場合にTHENステートメントが実行されます。例えば:
IF (p1 > 90) OR (f1 > 90) THEN performance = 'very good';
そしてNOT演算子は他のオペレーターと組み合わせて、比較の論理を逆転させたい場合に使います:
IF p1 NOT IN (98, 99, 100) THEN performance = 'not excellent';
これらの論理演算子を使った例を見てみると、なぜ2つまでのELSEステートメントしか使わないのでしょうか? たくさんのELSEステートメントをプログラムしてみましょう!ただし、そうするときは条件が相互に排他的であることを確実にしなければなりません。つまり、データセット内の各オブザベーションに対して、1つの条件のみが成立するようにする必要があります。これは、多くの場合、間隔の端点が何らかの形で重複しないようにする必要があることを意味します。
例#
以下のプログラムは、if-then-else ステートメントの中で複数の互いに排他的な条件を使用する例です。このプログラムでは、AND演算子を使用して条件を定義しています。繰り返しになりますが、比較がANDで接続されている場合、条件が真であるためには、すべての比較が真でなければなりません。
data grades;
length overall $ 10;
input name $ 1-15 e1 e2 e3 e4 p1 f1;
avg = round((e1+e2+e3+e4)/4,0.1);
if (avg = .) then overall = 'Incomplete';
else if (avg >= 90) then overall = 'A';
else if (avg >= 80) and (avg < 90) then overall = 'B';
else if (avg >= 70) and (avg < 80) then overall = 'C';
else if (avg >= 65) and (avg < 70) then overall = 'D';
else if (avg < 65) then overall = 'F';
datalines;
Alexander Smith 78 82 86 69 97 80
John Simon 88 72 86 . 100 85
Patricia Jones 98 92 92 99 99 93
Jack Benedict 54 63 71 49 82 69
Rene Porter 100 62 88 74 98 92
;
run;
proc print data = grades;
var name avg overall;
run;
OBS | name | avg | overall |
---|---|---|---|
1 | Alexander Smith | 78.8 | C |
2 | John Simon | . | Incomplete |
3 | Patricia Jones | 95.3 | A |
4 | Jack Benedict | 59.3 | F |
5 | Rene Porter | 81.0 | B |
プログラムを開いて実行してください。PRINTプロシージャからの出力を確認し、成績の文字が正しく割り当てられていることを確認してください。またプログラム全体、特にif-then-elseステートメントがどのように書かれているかに注目してください。条件と割り当てステートメントがきれいに列に並べられ、括弧を使って条件を区切っています。可能な限り…いや、必ずそうするべきですが…プログラムでは書式を整え(コメントも付けましょう)。結局のところ、数年後にそのプログラムを実際に使う必要が出てくるかもしれません。その時は当時のあなたは感謝するはずです!
注意点:
まず、プログラムが学生の全体の平均を計算し、その結果を小数点以下1桁に丸めています。平均の算出方法を定義して計算することで、試験を欠席した学生には欠席値が割り当てられます。
欠席値をチェックするifステートメントがあることに注目してください。学生の平均が欠席値の場合、Incompleteが割り当てられます。SASが欠損値をどのように扱うかについては、後ほど学習します。
学生の総合平均が90以上の場合、Aが割り当てられます。
学生の総合平均が80以上90未満の場合、Bが割り当てられます。
学生の総合平均が70以上80未満の場合、Cが割り当てられます。
学生の総合平均が65以上70未満の場合、Dが割り当てられます。
学生の総合平均が65未満の場合、Fが割り当てられます。
もう1点、欠損値に関する条件の後で条件がA、B、…Fの順に並んでいることに気づきませんでしたか?インストラクターは前向きな考え方をしているのでしょうか?実は、この順序は文の効率性に関係しています。SASが特定のオブザベーションに対して真となる条件があると、SASはif-then-elseステートメントを抜けて、次のデータステップのステートメントに移ります。これによりSASは、残りの条件を不必要に評価する手間が省けます。したがって、if-then-elseステートメントで条件の順序を並べるときには、(おおよそですが)最も一般的な条件を最初に、次に一般的な条件を2番目に、というように並べるのが良いプログラミングの習慣です。また、欠損値に関する条件がIFステートメントの最初に現れるようにする必要があります。そうしないと、SASがそれを飛ばしてしまう可能性があります。
例#
前のプログラムでは、条件がAND演算子を使って書かれていました。代わりに、単純な数値の範囲を使うこともできます。次のプログラムは、範囲と比較演算子の代替構文の使用例を示しています。前のプログラムと同じ出力が得られることに注目してください。
data grades;
length overall $ 10;
input name $ 1-15 e1 e2 e3 e4 p1 f1;
avg = round((e1+e2+e3+e4)/4,0.1);
if (avg EQ .) then overall = 'Incomplete';
else if (90 LE avg LE 100) then overall = 'A';
else if (80 LE avg LT 90) then overall = 'B';
else if (70 LE avg LT 80) then overall = 'C';
else if (65 LE avg LT 70) then overall = 'D';
else if (0 LE avg LT 65) then overall = 'F';
datalines;
Alexander Smith 78 82 86 69 97 80
John Simon 88 72 86 . 100 85
Patricia Jones 98 92 92 99 99 93
Jack Benedict 54 63 71 49 82 69
Rene Porter 100 62 88 74 98 92
;
run;
proc print data = grades;
var name avg overall;
run;
OBS | name | avg | overall |
---|---|---|---|
1 | Alexander Smith | 78.8 | C |
2 | John Simon | . | Incomplete |
3 | Patricia Jones | 95.3 | A |
4 | Jack Benedict | 59.3 | F |
5 | Rene Porter | 81.0 | B |
例#
さて、インストラクターが、コースの最初から終わりまでの間に何らかの改善の兆しを示した学生にボーナスポイントを与えたいとします。第1回目の試験の成績が第3回目と第4回目の成績より低いか、第2回目の試験の成績が第3回目と第4回目の成績より低い場合、その学生の総合平均に2点を加えたいとします。(なぜかは聞かないでください。ここでは動機付けのために使っているだけです。)ここで行いたい操作は「either(どちらか)」と「or(または)」です。インストラクターの意向を満たすためには、ORの比較演算子を使う必要があります。ORで比較がつながれている場合、条件が真になるためには比較のうち1つが真であれば十分です。次のプログラムは、OR演算子、AND演算子、そしてORとANDの両方の演算子の使用例を示しています。
data grades;
input name $ 1-15 e1 e2 e3 e4 p1 f1;
avg = round((e1+e2+e3+e4)/4,0.1);
if ((e1 < e3) and (e1 < e4))
or ((e2 < e3) and (e2 < e4)) then adjavg = avg + 2;
else adjavg = avg;
datalines;
Alexander Smith 78 82 86 69 97 80
John Simon 88 72 86 . 100 85
Patricia Jones 98 92 92 99 99 93
Jack Benedict 54 63 71 49 82 69
Rene Porter 100 62 88 74 98 92
;
run;
proc print data = grades;
var name e1 e2 e3 e4 avg adjavg;
run;
OBS | name | e1 | e2 | e3 | e4 | avg | adjavg |
---|---|---|---|---|---|---|---|
1 | Alexander Smith | 78 | 82 | 86 | 69 | 78.8 | 78.8 |
2 | John Simon | 88 | 72 | 86 | . | . | . |
3 | Patricia Jones | 98 | 92 | 92 | 99 | 95.3 | 95.3 |
4 | Jack Benedict | 54 | 63 | 71 | 49 | 59.3 | 59.3 |
5 | Rene Porter | 100 | 62 | 88 | 74 | 81.0 | 83.0 |
まずプログラムを確認して、コードを理解しておきましょう。特に()で囲まれた論理比較は、他の式と比較される前に真偽が評価されることに注目してください。この例では:
SASは最初に、e1がe3より小さく、かつe1がe4より小さいかどうかを判定します。
次にSASは、e2がe3より小さく、かつe2がe4より小さいかどうかを判定します。
最後にSASは、最初の箇条書きが真であるか、2番目の箇条書きが真であるかを判定します。
このSASプログラムを起動して実行してください。PRINTプロシージャからの出力を確認し、適切な場合には、学生の平均(avg)に2点を加えて調整済み平均(adjavg)が計算されていることを確認してください。また、ここでは欠損値のプログラミングを心配する必要がなかったことに注目してください。学生の調整済み平均(adjavg)は、その平均(avg)が欠席値の場合、自動的に欠席値が割り当てられます。SASではこれを「欠席値のプロパゲーション(伝播)」と呼んでいます。
文字値の比較#
これまで見てきたif-then-elseステートメントの例はすべて数値変数のみを扱っていました。文字変数を含む比較も同様に行えます。文字値を比較する際の重要なポイントは、SASが大文字と小文字を区別することです。つまり、文字値はデータセットに現れる場合と同じ大文字小文字で指定しなければならず、この点ではSASはケース・センシティブです。また文字値は引用符で囲む必要があります。
例#
インストラクターがコースを完了しなかった学生や不合格の学生を特定したいとします。SASは大文字と小文字を区別するため、これらの学生を識別するためのif-then-elseステートメントで、’failed’や’Failed’や’FAILED’など、”failed(不合格)”や”incomp(未完了)”のすべての可能な表記をチェックする必要があります。1つの面倒な解決策は、”failed”と”incomp”のすべての可能な表記をチェックすることですが、代わりにUPCASE関数を使って最初に大文字の値を生成し、その後、大文字値同士の比較のみを行うこともできます。次のプログラムはそのようなアプローチを取っています。
data grades;
length action $ 7
action2 $ 7;
input name $ 1-15 e1 e2 e3 e4 p1 f1 status $;
if (status = 'passed') then action = 'none';
else if (status = 'failed') then action = 'contact';
else if (status = 'incomp') then action = 'contact';
if (upcase(status) = 'PASSED') then action2 = 'none';
else if (upcase(status) = 'FAILED') then action2 = 'contact';
else if (upcase(status) = 'INCOMP') then action2 = 'contact';
datalines;
Alexander Smith 78 82 86 69 97 80 passed
John Simon 88 72 86 . 100 85 incomp
Patricia Jones 98 92 92 99 99 93 PAssed
Jack Benedict 54 63 71 49 82 69 FAILED
Rene Porter 100 62 88 74 98 92 PASSED
;
run;
proc print data = grades;
var name status action action2;
run;
OBS | name | status | action | action2 |
---|---|---|---|---|
1 | Alexander Smith | passed | none | none |
2 | John Simon | incomp | contact | contact |
3 | Patricia Jones | PAssed | none | |
4 | Jack Benedict | FAILED | contact | |
5 | Rene Porter | PASSED | none |
プログラムを開いて実行してください。PRINTプロシージャからの出力を確認し、変数「action」に関連するif-then-elseステートメントが不適切であるのに対し、UPCASE関数を使って作成した変数「action2」は上手く機能していることを確認してください。 ちなみに、文字値を含む比較を行う場合、SASでは欠損文字値(ブランクスペース’ ‘)が任意の文字より小さいと見なされるので、欠損値をプログラムするという良い習慣は文字変数の場合にも当てはまります。
複数のアクションを実行する#
これまでの例では、ある条件に対して1つのアクションのみを実行していました。状況によっては、複数のアクションを実行したい場合があるかもしれません。
例#
インストラクターが、第4回の試験を欠席した学生には0点を与え、その旨を学生に通知したいとします。次のSASプログラムは、DO-ENDを使ってインストラクターの意向を実現する方法を示しています。
data grades;
input name $ 1-15 e1 e2 e3 e4 p1 f1;
if e4 = . then do;
e4 = 0;
notify = 'YES';
end;
datalines;
Alexander Smith 78 82 86 69 97 80
John Simon 88 72 86 . 100 85
Patricia Jones 98 92 92 99 99 93
Jack Benedict 54 63 71 49 82 69
Rene Porter 100 62 88 74 98 92
;
run;
proc print data = grades;
var name e1 e2 e3 e4 p1 f1 notify;
run;
OBS | name | e1 | e2 | e3 | e4 | p1 | f1 | notify |
---|---|---|---|---|---|---|---|---|
1 | Alexander Smith | 78 | 82 | 86 | 69 | 97 | 80 | |
2 | John Simon | 88 | 72 | 86 | 0 | 100 | 85 | YES |
3 | Patricia Jones | 98 | 92 | 92 | 99 | 99 | 93 | |
4 | Jack Benedict | 54 | 63 | 71 | 49 | 82 | 69 | |
5 | Rene Porter | 100 | 62 | 88 | 74 | 98 | 92 |
DOステートメントは、対応するENDが現れるまで、SASにすべてのステートメントを1つの包括的なアクションとして扱うように指示します。対応するENDが現れない場合、SASはエラーを出力します。このプログラムを起動して実行し、PRINTプロシージャの出力を確認して、プログラムが想定通りの動作をしていることを確認してください。
演習#
次のデータステップを使って、新しい変数
Grade
とCourse
を以下の定義に従って作成してください。
data school;
input Age Quiz : $1. Midterm Final;
/* ここにステートメントを追加 */
datalines;
12 A 92 95
12 B 88 88
13 C 78 75
13 A 92 93
12 F 55 62
13 B 88 82
;
run;
If-Then-Elseステートメントを使って新しい2つの変数を作成してください。
Grade(数値)の値は、Ageが12の場合は6、Ageが13の場合は8となります。
クイズの成績には以下の数値が対応しています: A=95、B=85、C=75、D=70、F=65
この情報を使って、コースの成績「Course」(クイズ「Quiz」20%、中間試験「Midterm」30%、期末試験「Final」50%の加重平均)を計算してください。