2011年2月20日日曜日

SQLプロシジャを利用したSASデータセットの存在チェック


Awaiting servers
Creative Commons License photo credit: bugeaters

売上データなどを利用して分析をする場合、複数のデータテーブルを同時に読み込んで処理を実行することがあるかと思います。

例えば・・・

 ・売上データを過去10年分読み込み売上の推移を求める。
 ・直近1年分の売上データ、予算目標データを読み込んで売上目標に満たない店舗を求める。

などなど様々な要件が考えられます。

その様々な要件に応じて対象となるデータを読み込むように処理を作る必要があるわけです。
プログラムを組むという観点では、処理を実行する前に対象となるデータテーブル(SASの場合だとSASデータセット)が存在している事を確認した上で処理を実行するほうが望ましいです。

SASデータセットの存在確認のプログラム例



%SYSFUNC(EXIST(データセット名)) ;



しかし、上記の例で上げたように過去10年分のデータを読み込むなどの場合は、存在確認を10年分記述するのは大変かと思います。
データ件数が膨大だと予想される売上データなどは、月毎にデータを分けて保存していると考えられます。
10年分なので、120ヶ月分の存在確認を記述することになります。
正直大変なので、そのままの方法ではやりたくありません。

WORKの下に以下のようにデータがあるとします。



以下のようなプログラムを使用して、データの確認を行うことが出来ます。


proc sql;
 select
  min(substr(memname,8,6)) as MIN,
  max(substr(memname,8,6)) as MAX,
  count(*) as COUNT_TBL
 from
  dictionary.tables
 where
  libname = "ライブラリ名" and
  substr(memname,1,7) = "データセット名称(一部)"
 ;
quit;


※上記のデータセットの存在を確認する場合は、ライブラリ名に"WORK"、データセット名称には"URIAGE_"を入れます。

実行結果例






DICTIONARY.TABLES にはlibnameで追加された先のSASデータセット(ビュー)、workで作成されたSASデータセット(ビュー)の情報が格納されています。
データセットの作成、または削除された時に意識しなくても更新がかかります。
このテーブルには どのライブラリに存在するかを示す"libname"、とSASデータセット名称を示す"memname"があります。
それを利用してデータセットの確認をしてしまうという方法です。

上記のプログラムはWORKライブラリにある、データセット名称の頭7文字が"URIAGE_"で始まるデータセットの件数を求めるプログラムです。

 ・件数が処理対象になる年月の数と同じになること。
 ・SQLで返される結果の最大の年月が、処理対象の最大年月と同じになることを確認する。
 ・SQLで返される結果の最小の年月が、処理対象の最小年月と同じになることを確認する。

このことにより論理的にデータのチェックを行うことが出来ます。


ただし、この方法には弱点として、読み込む対象のSASデータセット名称が一定の規則で作成されていることが条件になります。
何らかの方法で対象のデータセット群だけを特定する方法が明確である必要があります。

例) 売上データの場合、URIAGE_[年月]という名称の13文字のデータセットで作成する。

などの場合は頭文字7文字分が『URIAGE_』であれば売上のデータとして特定できるため使用ができますが、仮にURIAGE_YOSANというデータセットが存在した場合は正しい件数が取得できなくなる恐れがありますので、抽出条件を変えて特定できるようにするなど工夫が必要です。

投稿者:NI原


[google]

2011年2月1日火曜日

ハッシュオブジェクトによるテーブル結合



以前の記事「SASのハッシュオブジェクトを試してみた」でハッシュオブジェクトを取り上げましたが、今回はそれを使ったテーブル結合について説明していきます。


売り上げ情報が入ったSALESというSASデータセットと、


製品の情報が入ったPRODUCTというSASデータセットを使います。


結合キーは、PRODUCT_CODE(製品コード)です。


まずは、SALES全件を出力対象にしてそれとマッチするPRODUCT情報を付与したデータを作成してみます。

MERGEステートメントでは、

proc sort data=SALES out=SALES_SORT ;
 by PRODUCT_CODE ;
run ;
proc sort data=PRODUCT out=PRODUCT_SORT ;
 by PRODUCT_CODE ;
run ;
data DATA1 ;
 merge SALES_SORT(in=in1) PRODUCT_SORT ;
 by PRODUCT_CODE ;
 if in1 ;
run ;


のように書きます。



ハッシュオブジェクトを使うと以下のように書くことができます。

data DATA1(drop=rc) ;
 if _N_=1 then do ;
  declare hash hash_in2() ;
  rc = hash_in2.DefineKey("PRODUCT_CODE") ;
  rc = hash_in2.DefineData("PRODUCT_NAME","PRODUCT_PRICE") ;
  rc = hash_in2.DefineDone() ;
 end ;
 do until(last_in2) ;
  set PRODUCT end=last_in2 ;
  rc = hash_in2.add() ;
 end ;
 do until(last_in1) ;
  set SALES end=last_in1 ;
  call missing(PRODUCT_NAME,PRODUCT_PRICE) ;
  rc = hash_in2.find() ;
  output ;
 end ;
run ;


順番に説明していきます。

3~6行目がハッシュオブジェクトを定義している箇所です。

declare hash hash_in2() ;

DECLAREステートメントでハッシュオブジェクト名を指定します。
任意のSAS名でなければなりません。(ここではhash_in2)

rc = hash_in2.DefineKey("PRODUCT_CODE") ;

DefineKeyメソッドでルックアップキーを指定します。
MERGEステートメントでの結合キーと同意です。
""で括る必要があります。

rc = hash_in2.DefineData("PRODUCT_NAME","PRODUCT_PRICE") ;

DefineDataメソッドでハッシュオブジェクトに格納するデータ(変数名)を指定します。
""で括る必要があります。
ルックアップキーだけを使用する場合は指定しなくても構いません。

rc = hash_in2.DefineDone() ;

DefineDoneメソッドでハッシュオブジェクトの定義が済んだことを告げます。


ちなみに、このハッシュオブジェクトを定義している箇所が、
if _N_=1 then do ;
end ;
で括られているのは、ハッシュオブジェクトはDATAステップ内で一度だけ定義されれば良いからです。
(DATAステップ内のループ処理で毎回定義しても結果は変わりません)


ここまでの記述で、DATAステップ内でhash_in2というオブジェクトにアクセスすることができるようになります。




次にオブジェクトに値を格納します。

SETステートメントでSASデータセット名を指定し、ADDメソッドで値を格納します。
DO UNTILステートメントとSETステートメントのENDオプションを使い、1オブザベーションずつ処理します。

 do until(last_in2) ;
  set PRODUCT end=last_in2 ;
  rc = hash_in2.add() ;
 end ;

ADDメソッドで値が追加されるデータは、DefineDataメソッドで指定したPRODUCT_NAMEとPRODUCT_PRICEです。

ここまでの記述で、hash_in2というオブジェクトに値が格納されます。



最後にSASデータセットSALESを読み込みながらハッシュオブジェクトにアクセスして値を取得しアウトプットします。
ADDメソッドのときと同様に、DO UNTILステートメントとSETステートメントのENDオプションを使います。

 do until(last_in1) ;
  set SALES end=last_in1 ;
  call missing(PRODUCT_NAME,PRODUCT_PRICE) ;
  rc = hash_in2.find() ;
  output ;
 end ;


FINDメソッドでハッシュオブジェクトから値を取得します。
最後にアウトプットです。

call missing(PRODUCT_NAME,PRODUCT_PRICE) ;

については後ほど説明します。



結果は以下のとおりです。


PRODUCT_CODEに対応するPRODUCT_NAMEとPRODUCT_PRICEがSASデータセットSALESに付与されました。
SASデータセットPRODUCTにないPRODUCT_CODEについてはPRODUCT_NAMEとPRODUCT_PRICEが共に欠損値です。
(変数の並び順を変えたい場合は、別途FORMATステートメント等で調整します)


最後に、
call missing(PRODUCT_NAME,PRODUCT_PRICE) ;

について説明します。

上記ステートメントを入れないで実行した結果を見れば明らかです。



PRODUCT_CODE="0009"のオブザベーションに、PRODUCT_CODE="0005"のPRODUCT_NAMEとPRODUCT_PRICEが入ってしまいます。
これは、FINDメソッドで新しい値をオブジェクトから取得するまでは内部的に前の値を保持するためです。

試しに、下記のようなプログラムを実行してみると、

data DATA1_NG2 ;
 if _N_=1 then do ;
  declare hash hash_in2() ;
  rc = hash_in2.DefineKey("PRODUCT_CODE") ;
  rc = hash_in2.DefineData("PRODUCT_NAME","PRODUCT_PRICE") ;
  rc = hash_in2.DefineDone() ;
 end ;
 do until(last_in2) ;
  set PRODUCT end=last_in2 ;
  rc = hash_in2.add() ;
 end ;
 do until(last_in1) ;
  set SALES end=last_in1 ;
  * PRODUCT_CODEが"0009"のときだけログに内部的な値を出力 ;
  if PRODUCT_CODE="0009" then put "*** FINDメソッド直前の " PRODUCT_NAME= PRODUCT_PRICE= ;
  rc = hash_in2.find() ;
  output ;
 end ;
run ;


ログには、
*** FINDメソッド直前の product_name=製品0005 product_price=5700
のように出力されます。

この状態で次のFINDメソッドで新しい値が取得できずにアウトプットされるので、上記のようなデータになってしまいます。


よって、このようなやり方でテーブル結合させる場合には、FINDメソッドの直前でDefineDataメソッドで定義した変数を初期化させる必要があります。


以上、今回はもっとも基本的なテーブル結合について説明しました。

次回以降、もう少し複雑なテーブル結合や、ハッシュオブジェクトでのテーブル結合のメリット等を取り上げていく予定です。


投稿者:@Massu_NI


[google]

2011年1月26日水曜日

最近の気になる英語圏News



◇世界におけるtwitterなう



http://blog.twitter.com/2010/10/world-of-tweets.html

すさまじい勢いで世界に輪を広げているツイッター。
様々な国におけるその使用状況がツイッターブログにて報告されている。

【ブラジル】今年は、3人の大統領候補者たちがそれぞれのキャンペーンをツイッター上で行い、多くの反響を得た。

【チリ】地震後に、Sign-up数が500%も急上昇。地下からの作業員救出劇は世界を一つにした!

そして、
【日本】アメリカ以外の国ではどの国よりもtweetが多く、twitterが日本人の個人意識に火を付けた!
とか…。

日本でこれだけツイッターがウケたことについて、携帯の通信能力が果たした役割が大きいというのは確かだろう。

社会的な観点から見たとき、言いたいことは出来るだけ言わずに、うまく周囲と歩調を合わせて談合していくのではなく、むしろ言いたいことを自由につぶやいて、自他の違いを顕にしていく流れが来ているのではないだろうか。

「あなたと私はここが違う」ということが平気で許容されるというのは、個人が主体的に自立(or 自律)して生きていくとき、とても居心地のいい環境だと思うのだが如何だろうか。


続いては、こちら。


◇イノベーション(革新)を起こす4つのルール



http://www.greenbiz.com/blog/2010/10/19/rule-no-1-innovation-have-fun?ms=39689



"Web2.0"の提唱者として知られる、ティム・オライリー氏。
イノベーションを4つのシリンダーから成るエンジンに例えて、以下のように表現した。


第1のシリンダー:「楽しむこと。」
第2のシリンダー:「世界を変えられるんだ!と、大きく考えること。」
第3のシリンダー:「製品・ビジネスモデル・会社を生み出し、自分達だけでなく、その周囲に集まる人達にも得を与えられること。」
第4のシリンダー:「自分達と、自分達を取り巻く人々(ユーザー・提携企業等)が共に発展していくこと。そして自分達の予想をも超えた価値を創造すること」


ライト兄弟が飛行機を発明したとき『よっしゃ、これで航空会社が始められる!』とか、AppleのSteve Jobsがパソコンを作ったとき『よっしゃ、これでパソコン産業が興せる!』と思っただろうか?

彼らはただ、自分達が没頭して取り組んでいることが楽しくて仕方がなかっただけではないだろうか。

『オイオイ、俺たち空飛んじゃったよ!!』
『やべぇ、オレ用のコンピューターできちゃったよ!!』

彼らのその情熱の種は恐らく、ある夢を描きそれの実現に向けてひた走る、その過程の中に見出される「楽しさ」から始まったのではないだろうか。

マラソンランナーが経験する"Runner's high"のような高揚感を栄養分として、イノベーションが始まるのかもしれない。

そして、その発明品を普及させることにより、もっと大きなインパクトを社会に与えることができる。
更に言えば、世界を変える可能性だって出てくる。
そうなったら、色々な人や企業を巻き込んで、会社を起こす。

会社を起こしたら、サービスのユーザーのみならず、ユーザーから還元されたデータを元に自他ともに発展・成長を遂げていくというサイクルを巻き起こす。

そのサイクルがまさに世界を取り巻く渦となって、国境も言語の壁も越えて、世界中に広がっていく。

なんとも壮大な話だが、なるほどMicrosoftもAppleもGoogleもtwitterも、このとんでもない条件を満たしている。

更にオライリー氏は、この先に広がる展望として、iPhoneなどのSmart PhoneやiPadといった最新デバイスが個人個人に未来をより良くする可能性を与えるのだという。

例えば、Smart Phoneなどの最新デバイスを使って、皆が好き勝手にtwitterでつぶやく。
twitterは一人一人の情報を集積し、例えばどの地域・場所でどんなことが起きているのかをマッピングすることが可能になる。
その"Map"が、また皆に還元されて一人一人のより良い行動・思考の糧となる。

まるで、酸素や二酸化炭素、水分の循環のような"情報の循環"による恩恵を、誰しもが享受でき、そしてより良い未来へと向かっていける。
そんな夢のような土壌が、我々の眼前に広がりつつあるのではないだろうか。

オライリー氏は言う、
『未来は一人の人間によってもたらされるのではなく、皆によってもたらされる。システムは、皆が力を合わせることを、もっと容易にしてくれるだろう。』

近くにいなくても、言語の壁があっても、人々の力が確実に集積されるようになれば、確かに未来は、我々一人一人がまるで投票権でも持ったかのように変えられるものなんだと実感できるものになるのかもしれない。


他方、コンピューターが女流棋士に勝利したりGoogleが自動運転する車を開発したりと、コンピューターによる「人間の仕事への侵食」は今後ますますその度合いを深めていくことが予想される。

もの凄い勢いで動いていく世界の潮流の中において、ワケも分からずにただ流されていくしかない時もあるだろう。
また、逆にうまく流れに乗って、未知なる世界に進んでいくこともできるかもしれない。

様々な雑事に追われて毎日を過ごす中で、そんな大それた夢など思い描く暇もない人も多々いるだろう。

誰しもが不安や期待に揺れ動く日々において、言えることはただ、目の前の状況や仕事などから「楽しみ」を見出すかどうかは、人間固有の自由としてまだ残されているということだ。


投稿者:@Nawito_NI


[google]

2011年1月14日金曜日

データの取り出しをスムーズに進める一つのヒント。(その2)




前回の筆者の記事にて「INDEX関数はシート選択も可能です」ということでしたので、その辺を早速。
INDEX関数、MATCH関数などは前回の記事を参考にしてください。


前回は売上数という単一のシートだけで話をしてましたが、売上数のほかに売上金をまとめた表もあるとします。
その場合のチェックする領域の切り替えを使うことができる、INDEX関数の領域指定「第4引数」の話をしましょう。


まず、前提の条件として、「同じレイアウトのシートであるということ」です。
つまり、レイアウトの違う2つでは何も期待したものが得られない可能性があります。
(やり方は無いわけではないと思うんですが、今回は便宜上こうしておきます)
今回は、「同じレイアウトのシートで別データを取り出そう」という括りでいきます。


同じシートレイアウトであれば、「売上数」と「売上金」が切り替わると式を2本コピーして、セル位置を入れ替えておけばいいのでは?
と考えたくなるのが普通です。
でも、今回はプルダウンリストの切り替えで、自動的に読む位置を変えるというものを作成してみます。


基本的なINDEX(MATCH)関数の書き方は前回と一緒です。
1つ違うのは、第1引数の範囲の指定の仕方です。


選択する領域が2つ存在する今回は、以下のように記述します。






「B2:G8」の箇所が赤枠内の「売上数」
「I2:N8」の箇所が青枠内の「売上金」

となります。

このように、複数の範囲を指定することで、「第4引数」の役割が生きてきます。






赤枠内では、始点となる「4月」の値を求め、青枠内では終点となる「6月」の値を求めています。

求める方法は前回同様INDEX(MATCH)関数です。
前回と大きく違うのは、第4引数によるデータテーブル選択域の違いということです。
前回もとめたものは単一の領域指定であったため、第4引数は省略できましたが、今回は複数領域を指定しています。



第4引数で指定しているものは、「売上数」「売上金」という言葉を指定しています。
この場合、セルC13には「売上金」が入っていますので第4引数の結果は「2」となります(「売上数」であれば、「1」となります)。
よって、第1引数の範囲の指定は「2」、つまりセル番号「I2:N8」の範囲からの検索となるのです。


今回は、同一テーブル上で実施した方法だけですが、別シートの指定も活用できます。
シートごとにテーブルを変え(できれば、レイアウトは同一形態のほうが楽です)、それぞれを呼び出すことも可能です。


このように第4引数をうまく活用することで、より利用の幅が広がるはずです。

今回はデータ数が、縦も横もさほど多くなく、VLOOKUP関数での代用も可能であるとは思われます。
しかし、データが膨大になると、列番号の指定が少々面倒なのでINDEX関数(MATCH関数)を利用するほうがよいのではないかと思われます。
データの数を見極めた上で、関数を効果的に使っていくことがよいでしょう。




余談…
関数の入力画面(下図)のようなものがないとやりにくい(わかりにくい)という方へ。



簡単に出すためには、セル内に「=INDEX」と入力し、Ctrl+A で上記の図がでてきます。
他の関数でも可能ですので、Excelの便利方法として覚えておくとよいでしょう。

ちなみに、INDEX関数の場合には、第4引数の省略の関係でこのような選択肢が先に出てきます。
複数の引数選択肢がある場合にでるようです。



今回の場合であれば、「配列、行番号、列番号」を選択すると、同様の操作が可能です。


投稿者:@DeDe_NI


[google]

2011年1月4日火曜日

謹賀新年 2011




新年あけましておめでとうございます。

昨年は格別の御厚情を賜り、厚く御礼を申し上げます。
本年もNI社員一同、皆様にご満足頂けるサービスを心がける所存でございます。

また、不定期ではありますが、当ブログでの情報発信も続けていきますので昨年同様のご愛顧を賜わりますよう、お願い申し上げます。

皆様のご健勝と益々のご発展を心よりお祈り致します。
本年もどうぞ宜しくお願い申し上げます。


ネイチャーインサイト サイトリニューアル&NIBLOGの引っ越し

ご連絡が遅くなりましたが、 ネイチャーインサイトの際とがリニューアルしました。 https://www.n-insight.co.jp/ それに伴い、NIBLOGも引っ越しすることになりました。 https://www.n-insight.co.jp/niblog/ ...