2020年5月5日火曜日

【SETステートメント】データセットを縦結合する






DATAステップの「SETステートメント」で複数のデータセットを縦に結合することができます。





構文

DATA     出力データセット;
    SET    データセット1   データセット2 ・・・;
RUN;

  • 「SET」に結合したいデータセットを列挙
  • 「DATA」に結合後に作成するデータセット名を指定




Sample data

data DT1;
input X Y:$2. Z;
cards;
1 aa 10
2 bb 20
;
data DT2;
input X Y:$1.;
cards;
3 c
4 d
;
XYZ
1aa10
2bb20
XY
3c
4d




例1. SETに1つだけデータセットを指定した場合

data OUT1;
    set DT1;
run;
XYZ
1aa10
2bb20

  • SETに指定されたデータセットと同じ中身のデータセットが作成されます。



例2. SETに複数のデータセットを指定した場合

data OUT2;
    set DT1 DT2;
run;
XYZ
1aa10
2bb20
3c.
4d.

  • SETに指定されたデータセットの順に縦結合されます。
  • SETに指定されたデータセット間で
    • 共通する変数名同士を縦に結合し
    • 片方にしかない変数も残してくれます。



例3. 失敗例

data OUT3;
    set DT2 DT1;
run;
XYZ
3c.
4d.
1a10
2b20

ログ
WARNING: 入力データセットによって、変数Yに複数の長さが指定されています。
 データの切り捨ての原因になります。


  • なんかログに WARNING が出ちゃったし、
  • 結合元の DT1 の1行目の Y が「aa」だったのが、結合後「a」になっちゃってる
  • 結合元の DT1 の2行目の Y が「bb」だったのが、結合後「b」になっちゃってる 


これは以下の重要な性質によるものです
  • SETに指定された最初のデータセットの LENGTH が適用される


今回の例では、変数Y のLENGTHは
  • DT2・・・「1」
  • DT1・・・「2」


そして、SETに指定された最初のデータセットは「DT2」なので、結合後の 変数Y のLENGTHは「1」に設定されてしまい、文字切れが起きたというわけですね。



解決策
data OUT4;
    length Y $2.;
    set DT2 DT1;
run;
XYZ
3c.
4d.
1aa10
2bb20

  • SETの前に「LENGTHステートメント」でLENGTHを再設定してあげればOK



SETステートメントの落とし穴


SASに慣れてきたかたでも、わりと陥りやすい落とし穴。
以下も参考まで(わりと上級者向けの内容ではあるかもです・・)
SETステートメントの落とし穴



2020年3月11日水曜日

PROC COPY の「OVERRIDEオプション」がいい感じ





とにかくこのプログラムを見てほしい。

proc copy in=sashelp out=work override=(keep=name age);
    select class;
run; 



  • SASHELP.CLASSをWORKにコピーしています。
  • OVERRIDE=オプションで、出力データセットに対して変数NAMEとAGEのみをKEEP



こんな感じで、
OVERRIDEオプションは、出力データセットに対して、データセットオプションを設定出来ちゃうんです。




このオプションの面白いところは、以下のような感じで、、

proc copy in=sashelp out=work override=(rename=(weight=wei));
    select class cars;
run;


出力データセットが複数ある場合、そのすべてのデータセットに、データセットオプションを適用できちゃうところ。



こういう、複数のデータセットに一括でオプションを適用するのって、確か他のプロシジャでは出来ないはず。
アイディア次第では結構便利でもあるし、コピーするついでにって感じで、かなり強力な機能だと思いました。



2020年3月2日月曜日

[SGPLOT] 塗りつぶしパターンの設定




SGPLOTの棒グラフやポリゴンの描画などで「塗りつぶしパターン」を設定する方法を紹介。



まずは、塗りつぶしパターンとか気にしないで、普通に棒グラフを描画してみます。

proc sgplot data=sashelp.class;
    vbar age / stat=percent group=age;
run;





次に塗りつぶしパターンを設定してみます。

ods graphics / attrpriority=none;
proc sgplot data=sashelp.class;
    vbar age / stat=percent group=age fillpattern;
run;



  • PLOTステートメントで「FILLPATTERN」を指定すると、塗りつぶしパターンが追加されます。
  • ODS GRAPHICS / ATTRPRIORITY=NONE」は以下のリンク記事で詳細を解説しています。
  • https://sas-boubi.blogspot.com/2019/12/sg.html




さらに、もっと見やすくしてみます。

ods graphics / attrpriority=none;
proc sgplot data=sashelp.class;
    styleattrs datafillpatterns=(L3 X3 R3 L5 X5 R5);
    vbar age / stat=percent group=age fillpattern;
run;



  • STYLEATTRステートメント」で塗りつぶしパターンの種類を設定しています。
  • 塗りつぶしパターンには「L1~L5」「R1~R5」「X1~X5」が設定できます。




この塗りつぶしパターンを設定するメリットは、

  • カラーだけに依存していない。
  • 白黒印刷しても、塗りつぶしパターンからグループを区別することができる。
  • 色覚障害のかたによりAccessibilityな出力を提供できる。



2020年2月29日土曜日

[PROC SORT] オブザベーションの並べ替え(ソート)




SORTプロシジャで、オブザベーションの並べかえをすることが出来ます。





構文

PROC  SORT   DATA=対象データセット名   OUT=出力データセット名;
     BY   変数1  変数2 ...;
RUN;


DATA=」のデータセットを「BY」に指定した変数値順にオブザベーションを並べ替え、結果を「OUT=」に指定したデータセット名で出力します。




重要なポイント






/* Sample */
data test;
input a b c$;
cards;
2 1 aa
2 2 bb
1 2 cc
1 1 dd
;










/* PROC SORTの例 */
proc sort data=test out=test2;
   by a b;
run;




2020年2月19日水曜日

【SGPLOT】POLYGONステートメントの描き方いろいろ





SGPLOT の POLYGONステートメント でポリゴンを描画できます。
今回はこのステートメントの色々な書き方を紹介していきたいと思います。





1. POLYGONステートメントの基本




以下の例をご覧ください。

/* 座標を定義 */
data test;
input segment x y;
cards;
1 1 1
1 5 1
1 5 5
1 1 5
;

/* ポリゴンを描画 */
proc sgplot data=test;
   polygon x=x y=y id=segment / fill outline;
   xaxis min=0 max=6;
   yaxis min=0 max=6;
run;




  • 最初にデータステップでx軸とy軸の座標を定義
  • SGPLOT の POLYGONステートメントで、座標間に順番に線を引いていって形をつくる(以下のようなイメージ)









2. 複数のポリゴンを描画する


/* 座標を定義 */
data test;
input segment x y;
cards;
1 1 1
1 2 1
1 2 2
1 1 2
2 3 3
2 4 3
2 4 4
2 3 4
;

/* ポリゴンを描画 */
proc sgplot data=test;
   polygon x=x y=y id=segment / fill outline;
   xaxis min=0 max=6;
   yaxis min=0 max=6;
run;


POLYGONステートメントで「id=segment」と指定しています。
この「ID=」に指定した変数の値ごとにポリゴンを別々に描画することが出来ます。
今回はSEGMENTという変数の値ごとに2つポリゴンを描画しています。




ちなみに、以下のようにポリゴンを重ねることも可能です。

/* 座標を定義 */
data test;
input segment x y;
cards;
1 1 1
1 5 1
1 5 5
1 1 5
2 3 3
2 6 3
2 6 6
2 3 6
;

/* ポリゴンを描画 */
proc sgplot data=test;
   polygon x=x y=y id=segment / fill outline;
   xaxis min=0 max=6;
   yaxis min=0 max=6;
run;





3. 穴を作る


/* 座標を定義 */
data test;
input segment x y;
cards;
1 1 1
1 5 1
1 5 5
1 1 5
1 . .
1 2 2
1 4 2
1 4 4
1 2 4
;

/* ポリゴンを描画 */
proc sgplot data=test;
   polygon x=x y=y id=segment / fill outline;
   xaxis min=0 max=6;
   yaxis min=0 max=6;
run;



上で作成したデータセットのように、

  • まず外側のポリゴンを定義した後、
  • x軸とy軸が欠損値の行を用意することで、そこから穴の始まりを示すことが出来る。
  • その後の定義は穴として描画される。





ちなみに、穴をちょっとずらして定義すると、以下のように面白い感じになる。

/* 座標を定義 */
data test;
input segment x y;
cards;
1 1 1
1 5 1
1 5 5
1 1 5
1 . .
1 3 3
1 6 3
1 6 6
1 3 6
;

/* ポリゴンを描画 */
proc sgplot data=test;
   polygon x=x y=y id=segment / fill outline;
   xaxis min=0 max=6;
   yaxis min=0 max=6;
run;




2020年2月6日木曜日

グラフのマーカーや線などの属性をGROUP毎に設定する【Attribute Map Statement 編】



「Attribute Map Statement」を利用して、GTLでマーカーや線などの属性を定義する方法を紹介。



と、その前に、以前「Attribute Map Dataset」を紹介しました。
グラフのマーカーや線などの属性をGROUP毎に設定する【Attribute Map Dataset 編】


今回はもう1つの方法として「Attribute Map Statement」を紹介していきます。
(※Attribute Map StatementはGTLのみの機能となります)



さっそくサンプルプログラム

proc template ;
     define statgraph MYTEMP ;
     begingraph;

     /* Attribute Map を定義 */
     discreteattrmap name="mymap";
        value "11" "12" / markerattrs=(color=blue    symbol=starFilled        size=11);
        value "13" "14" / markerattrs=(color=yellow symbol=TriangleFilled  size=11);
        value other       / markerattrs=(color=red      symbol=CircleFilled     size=11);
     enddiscreteattrmap;

     /* 「Attribute Map」と「グループ変数」を関連付ける */
     discreteattrvar  var=age attrmap="mymap" attrvar=myvar;

     /* 「Attribute 変数」の使用 */
     layout overlay;
          scatterplot x=HEIGHT y=WEIGHT / group=myvar grouporder=ascending name="leg1";
          discretelegend "leg1" / title="Age";
     endlayout;

     endgraph;
     end;
run;

proc sgrender data=SASHELP.CLASS template=MYTEMP;
run;


上の例では以下のような設定をしています。
  • 年齢(AGE)をグループ変数として、マーカーの種類と色を以下に設定
  • AGEが「11」「12」の時、青色の星マーク
  • AGEが「13」「14」の時、黄色の三角マーク
  • AGEが上記以外の時、赤色の丸マーク


ではどのように設定しているか見ていきましょう。




Step1. 「Attribute Map」を定義する


まずは、グループ変数の値にどういった書式(属性)を割り当てるのか?を定義します。

構文
DISCRETEATTRMAP  NAME="Attribute Mapの名前";
     VALUE  "グループ変数の値1"  /  属性;
     VALUE  "グループ変数の値2"  /  属性;
     …
ENDDISCRETEATTRMAP;


今回は以下のように書いています。

discreteattrmap name="mymap";
   value "11" "12" / markerattrs=(color=blue    symbol=starFilled        size=11);
   value "13" "14" / markerattrs=(color=yellow symbol=TriangleFilled  size=11);
   value other       / markerattrs=(color=red      symbol=CircleFilled     size=11);
enddiscreteattrmap;

  • 「mymap」というAttribute Map を定義
  • グループ変数の値が「11」「12」の時、青色の星マーク
  • グループ変数の値が「13」「14」の時、黄色の三角マーク
  • グループ変数の値が上記以外の時、赤色の丸マーク


ちなみに属性には「マーカー」や「塗りつぶし」といった設定を行う以下の属性オプションが用意されています(GTL書いたことある人ならお馴染みの書き方)

  • MARKERATTRS=()
  • FILLATTRS=()
  • LINEATTRS=()
  • TEXTATTRS=()


重要ポイント

グループにする変数にFORMATが割り当てられている場合、FORMATを当てた後の値を指定する事。
例えば年齢をグループ変数とする場合、11 = "11歳" のようにFORMATが当てられている場合、"11歳" の方を指定する。

discreteattrmap name="mymap";
   value "11歳"  / markerattrs=(color=blue symbol=starFilled  size=11);
enddiscreteattrmap;




Step2. 「Attribute Map」と「グループ変数」を関連付ける


次に、先ほど定義した「Attribute Map」と「実際のグループ変数」を関連付けます。


構文
DISCRETEATTRVAR
    VAR              = グループ変数
    ATTRVMAP  = "グループ変数と関連付けるAttribute Map"
    ATTRVAR     = グループ変数とAttribute Mapの関連付けに対して、任意の名前を定義
;


今回は以下のように書いています。

discreteattrvar var=age attrmap="mymap" attrvar=myvar ;

「グループ変数=AGE」と「Attribute Map="mymap"」を「myvar」という名前(Attribute変数)で関連付けています。



Step3. 「Attribute変数」の利用


あとはPLOTするときに、グループ変数として「Attribute変数」を指定すればOK

scatterplot x=HEIGHT y=WEIGHT / group=myvar grouporder=ascending ;

これで、Attribute Mapの定義に基づいてマーカーの種類や色などの属性を展開してPLOTしてくれます。

注意:
SAS9.4M3より前のバージョンでは、この機能 (Attribute Map Statement) と一緒に、PLOTステートメント (SCATTERPLOTやSERIESPLOTなど) でも属性を定義している場合、結果のプロット部分と凡例部分の属性が一致していないおかしな状態になってしまうことがある?らしいので注意。



2020年1月28日火曜日

[CALL SORT] 1オブザベーション内で変数間の値をソートする





SAS9.4M6で「CALL SORT」が追加されてますね。
これは「1オブザベーション内で変数間の値をソート」することができます。


例1
data test;
    x=2; y=3; z=1;
run;






data test2;
    set test;
    call sort(x,y,z);
run;


数値変数 X, Y, Z に対して、1つのオブザベーション内で値を昇順にソートしています。



例2
data test3;
    length x y z $3.;
    x="tea"; y="sas"; z="";
run;






data test4;
    set test3;
    call sort(x,y,z);
run;





文字変数 X, Y, Z に対して、1つのオブザベーション内で値を昇順にソートしています。




ポイント
  • 引数がすべて数値変数の場合は数値としてソートし、引数がすべて文字変数の場合は文字としてのソートが行われます
  • 引数に指定する変数の LENGTH はすべて同じであること


上のポイントの1つ目についてもっと詳しくいうと、たとえば、以下のように文字変数に数値が格納されている場合、

data test5;
    length x y z $3.;
    x = "2";
    y = "1";
    z = "10";
run;


それを CALL SORT(X,Y,Z) とすると、文字変数なんで文字としてのソートが行われて、

x = "1";
y = "10";
z = "2";

となります。


2020年1月20日月曜日

SVGにコントロールボタンを仕込む [SVGCONTROLBUTTONS, SVGMAGNIFYBUTTON]





システムオプション「SVGCONTROLBUTTONS」と「SVGMAGNIFYBUTTON」を指定して、SVGファイルにちょっとした仕掛けを組み込むことが出来ます。


サンプルプログラム
options svgcontrolbuttons svgmagnifybutton;

ods _all_ close;
ods printer printer=svg file="出力するパス/test.svg";

title "棒グラフ";
proc sgplot data=sashelp.class;
    vbar age / response=height group=Sex groupdisplay=cluster stat=mean fillpattern;
run;

title "散布図";
proc sgplot data=sashelp.class;
    scatter x=height y=weight;
run;

ods printer close;
ods html;



以下が作成されたSVGファイルです。


上の方にボタンが出てきました。



システムオプション「SVGMAGNIFYBUTTON」で拡大ボタンが追加されます。
ボタンを押すと、マウスカーソルの位置が拡大されて表示されます。




システムオプション「SVGCONTROLBUTTONS」でインデックスボタンが追加されます。ページの一覧が表示され、サムネイルをクリックするとそのページが開きます。



「前へ」とか「次へ」のボタンを押してページ移動させることが出来ます。




グラフを沢山つくって、眺めたいときにこの機能が便利そうね。



2020年1月1日水曜日

SASで年賀状





すべてのSASユーザーへ、SASで年賀状をつくりました。
2020年もどうぞよろしくお願いいたします!




年賀状をつくるプログラム (SAS9.4以上で動作)

*** 描画用のパーツ ;
data mouse;
  x1= 0;     y1= 0;
  x2= 0;     y2=-6.5;
  x3=-0.31;  y3= 1.2;
  x4= 0.31;  y4= 1.2;
  x5= 0;     y5= 0.15;
  x6= 0;     y6= 0.7;
  x7=-1.1;   y7= 4.5;
  x8= 1.1;   y8= 4.5;
run;

data whisker;
input higeno x9 y9;
cards;
1 -1.5  1.3
1 -0.8  1.1
2 -1.5  0.6
2 -0.8  0.6
3 -1.5 -0.1
3 -0.8  0.1
4  1.5  1.3
4  0.8  1.1
5  1.5  0.6
5  0.8  0.6
6  1.5 -0.1
6  0.8  0.1
;

data tail;
input x10 y10;
cards;
1.45 -9.5
1.8  -8
2    -5
1.9  -2
;

data nenga;
 merge mouse whisker tail;
run;

*** 描画 ;
title ;
ods graphics on / height=10cm width=14.8cm;

proc sgplot data=nenga noautolegend;
 styleattrs wallcolor=lavenderblush;
   
 /* Text */
 inset "HAPPY NEW YEAR"  /  position=top textattrs=(color=gold size=31cm);
 inset "20"  /  position=bottomleft textattrs=(color=green size=140cm );
 inset "20"  /  position=bottomright textattrs=(color=orange size=140cm );
   
 /* mouse */
 scatter x=x1 y=y1  /  markerattrs=(symbol=circlefilled  size=3cm color=skyblue);
 scatter x=x2 y=y2  /  markerattrs=(symbol=circlefilled  size=4cm color=skyblue);
 scatter x=x3 y=Y3  /  markerattrs=(symbol=circlefilled  size=0.2cm color=black);
 scatter x=x4 y=Y4  /  markerattrs=(symbol=circlefilled  size=0.2cm color=black);
 scatter x=x5 y=y5  /  markerattrs=(symbol=ArrowDown  size=0.5cm color=black);
 scatter x=x6 y=y6  /  markerattrs=(symbol=TriangleDownFilled  size=0.3cm color=pink);
 scatter x=x7 y=y7  /  markerattrs=(symbol=CircleFilled  size=1.5cm color=skyblue);
 scatter x=x8 y=y8  /  markerattrs=(symbol=CircleFilled  size=1.5cm color=skyblue);
 scatter x=x7 y=y7  /  markerattrs=(symbol=CircleFilled  size=1.2cm color=lightpink);
 scatter x=x8 y=y8  /  markerattrs=(symbol=CircleFilled  size=1.2cm color=lightpink);

 /* wisker */
 series  x=x9 y=y9  /  group=higeno lineattrs=(color=black);

 /* tail */
 series  x=x10 y=y10  /  lineattrs=(color=lightpink thickness=0.12cm) smoothconnect;

 xaxis min=-3 max=3  display=none;
 yaxis min=-5 max=1  display=none;
run;