初めに
今回はDataステップで行えるMergeステートメントについて説明します。
Mergeステートメントは名前の通り、結合処理、つまり複数のデータセットを(横)結合するものです。
役割としては、SQLのjoin句と同じ位置付けのものですが、n 対 m の結合の際、挙動がSQLと異なるので注意が必要です。
n 対 m 含め、Mergeステートメントの概要から順に説明していきます。
※ちなみに、SASでの結合処理は、上記と別に「Proc SQL」という”SASでSQLが使用できる”Procステップがあります。
「Mergeステートメント」の枠組み
上記の通り、「複数データセットを(横)結合」をするためのものです。
下記の枠組みでコードを書くものです。
mergeステートメントに結合対象のデータセットを記述し、byステートメントに結合キーを記述します。
次に「(in=in1)」について説明します。
下記コードでは「table_1(in=in1)」という形で記述されています。
これは、「”出力データにて、table_1に存在するデータならば「1」を設定“というフラグ変数「in1」を作成する」形になります。
前提として、mergeステートメントは所謂”完全外部結合”の形で結合されます。
なので、例えば、”table_1をベースにtable_2を結合“をしたい場合は、下記コードのように、”上記フラグ変数とif文を用いて“、不要なオブザベーションを除外します。
※注意点が一つあります。Mergeステートメントを使用する際、結合対象のデータセット全てが、結合キーでソート済みである必要があります。(そうでないと、エラーが発生します。)なので、ソート済みでない場合は、結合前に「proc sort」等でソートをしましょう。
data merge_data;
merge table_1(in=in1) table_2;
by key_id;
if in1=1 then output;
run;
結合 (1 対 n)
例
それでは、実際に結合処理をしてみましょう。
ここに部署マスタと社員マスタがあります。
社員マスタには部署ID(dept_id)はありますが、部署名(dept_name)がありません。
“社員マスタと部署マスタを結合して、社員マスタに部署名を付与しましょう。“
[データ]
dept_id | dept_name |
---|---|
1 | account |
2 | sales |
3 | legal |
4 | market |
emp_id | emp_name | dept_id |
---|---|---|
100 | sasaki | 2 |
200 | tanaka | 1 |
300 | yoshida | 1 |
400 | okada | 3 |
500 | nishino | 2 |
[求める出力結果]
emp_id | emp_name | dept_id | dept_name |
---|---|---|---|
100 | sasaki | 2 | sales |
200 | tanaka | 1 | account |
300 | yoshida | 1 | account |
400 | okada | 3 | legal |
500 | nishino | 2 | sales |
今回のポイントは下記です。
- 社員マスタに紐づかない部署マスタのデータを削除する。
- 結合前に、社員マスタを部署ID(結合キー)でソートをする必要がある。
上記ポイントを考慮した下記コードで求める結果が得られます。
/* 社員マスタを部署ID(結合キー)でソート */
proc sort data=employee_mst;
by dept_id;
run;
/* 結合 */
data merge_data;
merge employee_mst(in=emp) dept_mst;
by dept_id;
/* 社員マスタに紐づかない部署マスタのデータを削除 */
if emp=1 then output;
run;
如何でしょうか。
SQLを使っている方でしたら、指定する内容(データセット・キー)や結果が同じなので、理解しやすいと思います。
次は、 n 対 m の説明をします。
頭出ししましたが、n 対 m の場合はSQLと結果が異なるので(特にSQLを使っている方は)要注意です。
結合 (n 対 m)
概要
SAS (Mergeステートメント) で、n 対 m の結合を行うと、結論としては下記の通りになります。
[(下記結論の) 前提]
- n >= m とする。
- 1つのキー値について、n 対 m の結合結果を説明する。
- データセット名について、n 側を table_1 、m側を table_2 とする。
- キー以外変数について、n 側を var_1 、m側を var_2 とする。
[結論 (結合結果)]
- オブザベーション数は n になる。 ※SQLの場合は n × m 行。
- var_1 , var_2 の値は、”結合時の入力データセットにおける並び順でそのまま結合した形”になる。(例えば、結合結果1行目の値は、各入力データセットの1行目の値になる。) ※SQLの場合はデカルト積。
- 上記2.の通り、”結合して余った (n-m) 個のオブザベーション(table_1側)”に紐付くvar_2の値は、”table_2の末行におけるvar_2の値”になる。
例
実際に結合してみます。
上記結論の通りになることが確認できます。
入出力データセット
入力
id | str_a |
---|---|
1 | a_1_1 |
1 | a_1_2 |
1 | a_1_3 |
2 | a_2_1 |
2 | a_2_2 |
id | str_b |
---|---|
1 | b_1_1 |
1 | b_1_2 |
2 | b_2_1 |
2 | b_2_2 |
2 | b_2_3 |
出力 (結合結果)
id | str_a | str_b |
---|---|---|
1 | a_1_1 | b_1_1 |
1 | a_1_2 | b_1_2 |
1 | a_1_3 | b_1_2 |
2 | a_2_1 | b_2_1 |
2 | a_2_2 | b_2_2 |
2 | a_2_2 | b_2_3 |
コード
/* 入力データセット(data_a) */
data data_a;
infile datalines delimiter=',';
input
id
str_a $
;
cards;
1 , a_1_1
1 , a_1_2
1 , a_1_3
2 , a_2_1
2 , a_2_2
;
run;
/* 入力データセット(data_b) */
data data_b;
infile datalines delimiter=',';
input
id
str_b $
;
cards;
1 , b_1_1
1 , b_1_2
2 , b_2_1
2 , b_2_2
2 , b_2_3
;
run;
/* 結合 */
data merge_data;
merge data_a data_b;
by id;
run;
最後に
SASを使い始めた方は、既にSQLを使っている(or 知っている)方が多いと思います。
上記で説明した通り、Mergeステートメントを使用する際は、n 対 m になるのか注意が必要です。
n 対 m の結合をSQLと同様に行いたいときは、「Proc SQL」を使いましょう。