フォーマットと日付#

これまでにFORMATステートメントを使用して、変数の値を特定の方法で表示するようにSASに指示する方法に触れました。例えば、日付変数「saledate」をmmddyy10.フォーマットを使用して表示するように指定すると、2008年8月19日は08/19/2008として表示されます。SASが提供する多数のインフォーマットやフォーマットは、SASのヘルプとドキュメントで見つけることができます。ここでは、SASが提供しないインフォーマットやフォーマットを作成する方法に焦点を当てます。

つまり、FORMATプロシージャを使用してユーザー定義のインフォーマットとフォーマットを作成する方法に触れ、フォーマットの機能を拡張します。特に、次の技術を見ていきます:

  • SASデータセットに読み込まれる文字変数の値をINVALUEステートメントを使用して、より意味のある値に変換する方法

  • VALUEステートメントを使用して、文字変数および数値変数にカスタマイズされたフォーマットを作成し、変数を意味のあるフォーマットで表示する方法

  • PICTUREステートメントを使用して、0埋め、小数点およびコンマの区切り記号、充填文字、接頭辞などの特別なフォーマットで数字を表示するためのテンプレートを作成する方法

また、SASシステム内での日時処理のさまざまな側面にも触れます。具体的には、次のことを扱います:

  • SASが数値の日付および時刻の値をどのように定義するか

  • インフォーマットを使用して、日付や時刻をSASデータセットに読み込む方法

  • フォーマットを使用して、SASの日付や時刻を表示する方法

  • 計算で日付や時刻を使用する方法

  • SASの日付を特定の日付定数と比較する方法、およびSASの時刻を特定の時刻定数と比較する方法

  • 利用可能なSASの日付および時刻関数をいくつか使用する方法

  • 日付と時刻の処理に関連するシステムオプションを変更する方法

いつものように、提供されているプログラムを自分で実行してみてください。

FORMATプロシージャ#

このセクションでは、いくつかの異なる変数に対してさまざまなインフォーマットとフォーマットを作成する方法を示す多くの例を見てみます。そのためには、National Institute of HealthのInterstitial Cystitis Data Base (ICDB) Studyに登録された638人の被験者から収集された人口統計(または「背景」)データのサブセットを使用します。ICDB研究では、間質性膀胱炎と診断された人々のデータを収集しました。この病気は、膀胱および骨盤の激しい痛み、頻尿、および識別可能な原因がない痛みを伴う排尿を引き起こします。この病気は女性に多いですが、あらゆる年齢の男女に影響を与えます。ICDB研究では、各被験者は7つの臨床センターのいずれかに登録され、最大4年間で年に4回評価されました。

データが収集された背景の調査票を見ておくと役に立つでしょう。プログラムを実行するには、背景データセットback.sas7bdatをコンピュータのディレクトリに保存する必要があります。データセットはREADMEを参照しダウンロードしてください。

被験者背景の永久データセットicdb.backには638のオブザベーションと16の変数が含まれているため、背景データセットbackを作成する際に、10人の被験者と9つの変数のみを選択します。次のプログラムは、そのサブセットを作成します:

libname phc6089 '/folders/myfolders/SAS_Notes/data/';
 
data back;
  set phc6089.back;
  age = (v_date - b_date)/365.25;
  if subj in (110051, 110088, 210012, 220004, 230006,
              310083, 410012, 420037, 510027, 520017);
  keep subj v_date b_date age sex state country race relig;
  format age 4.1;
run;

title 'Output Dataset: BACK'; 
proc print data=back;  
run;
SAS 出力

Output Dataset: BACK

OBS subj v_date b_date sex state country race relig age
1 110051 01/25/94 12/02/42 2 42 1 4 3 51.1
2 110088 02/28/95 10/03/27 2 23 1 4 2 67.4
3 210012 07/16/93 06/27/24 2 . 6 4 1 69.1
4 220004 07/27/93 08/07/72 2 38 1 4 1 21.0
5 230006 01/06/94 04/24/49 2 21 1 4 3 44.7
6 310083 01/20/95 05/13/54 1 . 17 2 1 40.7
7 410012 09/16/93 11/01/47 2 22 1 4 3 45.9
8 420037 02/02/94 07/25/41 2 22 1 4 1 52.5
9 510027 02/15/94 08/14/63 2 49 1 4 1 30.5
10 520017 11/17/93 09/24/54 2 14 1 4 1 39.1

また、サブセットしたデータセットの生データファイルを作成しておきます。次のコードは、一時データセットbackから固定長のascii生データファイルを作成します:

data _null_;
  set back;
  file '/folders/myfolders/SAS_Notes/data/back.dat';
  put subj 1-6 @8 b_date mmddyy8. sex 17 race 19 
      relig 21 state 23-24 country 26-27 
      @29 age 4.1 @34 v_date mmddyy8.;
run;

SASデータセット名「_NULL_」は、SASに新しいSASデータセットを作成するかのようにDATAステップを実行するよう指示しますが、出力データセットにはオブザベーションや変数は書き込まれません。PUTステートメントは、指定されたフォーマットで変数をFILEステートメントで指定されたファイル名(back.dat)に書き込むようにSASに指示します。PUTステートメントの仕様は、INPUTステートメントの仕様と似ています。

プログラムを開き、FILEステートメントを編集して、生データファイルを保存する場所を反映するようにします。次に、プログラムを実行します。作成された新しいファイルback.datをメモ帳などのテキストエディタで開き、その構造と内容がデータセットbackに準じていることを確認してください。

INVALUEステートメント#

FORMATプロシージャのINVALUEステートメントを使用すると、文字変数のカスタマイズされたインフォーマットを作成できます。つまり、SASに特殊な文字値を読み取る方法を指示できます。これにより、SASは文字変数の値をより意味のある文字または数値に効果的に変換します。例えば、次のINVALUEステートメントは:

invalue $french 'OUI'= 'YES' 
                'NON'= 'NO'; 

フランス語の文字変数を英語の文字変数に変換するようにSASを準備します。

INVALUEステートメントの制限事項は次のとおりです:

  • 文字変数を別の変数に変換することしかできません。INVALUEステートメントを使用して数値変数を変換することはできません。

  • インフォーマットの名前は、文字変数を指すために$記号で始める必要があります。

  • インフォーマットの名前(例えばfrench)は、有効なSAS名であり、$記号の後に最大30文字まで使用できます。名前は数字で終わることはできず、標準のSASインフォーマット名も使用できません。

  • 後でインフォーマットを参照する場合は、名前の後にピリオドを付ける必要があります。

FORMATプロシージャのINVALUEステートメントは、インフォーマットを使用可能にするために定義するだけです。インフォーマットを有効にするには、文字変数を明示的にINPUTステートメントで関連付ける必要があります:

INPUT resp $french.;

または、FORMATステートメントで:

FORMAT resp $french.;

例を見てみましょう!

#

次のコードは、2つの文字変数「sex」と「race」をどのように変換するか定義するFORMATプロシージャの使用方法を示しています:

proc format;
  invalue $insex '1' = 'M'
                 '2' = 'F';
 
  invalue $inrace '1' = 'Indian'
                  '2' = 'Asian'
                  '3' = 'Black'
                  '4' = 'White';
run;
7                                                         SAS システム               2024年 6月 6日 木曜日 07時33分00秒

47         ods listing close;ods html5 (id=saspy_internal) file=_tomods1 options(bitmap_mode='inline') device=svg style=HTMLBlue;
47       ! ods graphics on / outputfmt=png;
48         
49         options notes ;
50         proc format;
51           invalue $insex '1' = 'M'
52                          '2' = 'F';
NOTE: 入力形式$INSEXを作成しました。
53         
54           invalue $inrace '1' = 'Indian'
55                           '2' = 'Asian'
56                           '3' = 'Black'
57                           '4' = 'White';
NOTE: 入力形式$INRACEを作成しました。
58         run;

NOTE: PROCEDURE FORMAT処理(合計処理時間):
      処理時間           0.00 秒
      ユーザーCPU時間    0.00 秒
      システムCPU時間    0.00 秒
      メモリ             157.87k
      OSメモリ           18076.00k
      タイムスタンプ     2024/06/06 午前07:34:03
      ステップ数                        4  スイッチ数  2
      ページフォルト回数                0
      ページリクレーム回数              34
      ページスワップ回数                0
      自発的コンテキストスイッチ回数    13
      非自発的コンテキストスイッチ回数  0
      ブロック入力操作回数              0
      ブロック出力操作回数              56
      

59         
60         
61         ods html5 (id=saspy_internal) close;ods listing;
62         

8                                                         SAS システム               2024年 6月 6日 木曜日 07時33分00秒

63         

INVALUEステートメントが使用されているため、変換は入力時にのみ行われます。このコードにより、後で文字変数「sex」がインフォーマット$insexに関連付けられると、SASが変数「sex」に’1’という文字値を検出するたびに、代わりに文字値’M’が格納されます。同様に、変数「sex」に’2’という文字値を検出するたびに、代わりに文字値’F’が格納されます。

プログラムを開いて実行します。動作を確認する唯一の方法は、ログウィンドウを確認することです。上記のようなメッセージが表示されるはずです。

このセクションの後半で触れるように、「sex」と「race」の読み込み定義を現在の作業セッションを超えて永続的に保存するためには、PROC FORMATステートメントにLIBRARY =オプションを付ける必要があります。ここでは使用していないため、このFORMATプロシージャで定義された定義は一時的なものであり、現在のSASセッションを超えて保存されません。

これまでに行ったのは、インフォーマットを使用できるように定義することだけです。次に、定義したインフォーマットを使用しましょう!

#

次のデータステップは、前の例で定義したインフォーマットを使用して、生データファイルback.datからサブセットされたデータを読み取ります:

data temp1;
  infile '/folders/myfolders/SAS_Notes/data/back.dat';
  length sex $ 1 race $ 6;
  input subj 1-6 @17 sex $insex1. @19 race $inrace1.;
run;
 
title 'Output Dataset: TEMP1';
proc contents data=temp1;
run;
 
proc print data=temp1;
  var subj sex race;
run;
SAS 出力

Output Dataset: TEMP1

CONTENTS プロシジャ

データセット名 WORK.TEMP1 オブザベーション数 10
メンバータイプ DATA 変数の数 3
エンジン V9 インデックス数 0
作成日時 2024/06/06 07:34:25 オブザベーションのバッファ長 16
更新日時 2024/06/06 07:34:25 削除済みオブザベーション数 0
保護   圧縮済み NO
データセットタイプ   ソート済み NO
ラベル      
データ表現 SOLARIS_X86_64, LINUX_X86_64, ALPHA_TRU64, LINUX_IA64    
エンコード utf-8 Unicode (UTF-8)    
エンジン/ホスト関連情報
データセットのページサイズ 131072
データセットのページ数 1
データページの先頭 1
ページごとの最大OBS数 8126
先頭ページのOBS数 10
データセットの修復数 0
ファイル名 /saswork/SAS_work4B440001B7D0_odaws01-apse1.oda.sas.com/SAS_workA11D0001B7D0_odaws01-apse1.oda.sas.com/temp1.sas7bdat
作成したリリース 9.0401M7
作成したホスト Linux
I ノード番号 1610626540
アクセス権限 rw-r--r--
所有者名 user-name
ファイルサイズ 256KB
ファイルサイズ (バイト) 262144
変数と属性リスト (アルファベット順)
# 変数 タイプ 長さ
2 race 文字 6
1 sex 文字 1
3 subj 数値 8

Output Dataset: TEMP1

OBS subj sex race
1 110051 F White
2 110088 F White
3 210012 F White
4 220004 F White
5 230006 F White
6 310083 M Asian
7 410012 F White
8 420037 F White
9 510027 F White
10 520017 F White

データファイルback.datの変数のサブセットのみが読み取られます。変数「subj」を読み取るために列番号(”1-6”)が使用され、変数sex(”@17”)およびrace(”@19”)をファイルから読み取るために絶対ポインタ制御が使用されます。注:

  • 変数を変換したいので、数字であっても性別と人種を文字変数として読み取る必要があります。

  • 入力時に、読み込まれる変数の長さを指定するオプションがあります。読み込まれる変数raceの長さは、インフォーマット名とピリオドの間に指定されます。例えば、読み込まれる変数raceの長さはインフォーマット$inrace1で1と定義されています。

  • LENGTHステートメントは、変換後の性別と人種の長さを定義します。

プログラムを開いてINFILEステートメントを編集し、保存されたファイルback.datの場所を反映するようにします。次にプログラムを実行し、CONTENTSおよびPRINTプロシージャの出力を確認します。特に、CONTENTSプロシージャの出力で「Type」列の下に「Char」と表示されているように、変数sexとraceの両方が文字変数であることに注意してください。また、CONTENTSプロシージャの出力には、変数「sex」と「race」が出力用に特定の方法でフォーマットされていることを示す情報はありません。出力用の設定にはINVALUEステートメントではなくVALUEステートメントを使用する必要があります!

最後に、少し補足として、TITLEステートメントはグローバルのステートメントであることを思い出してください。つまり、その値は別のTITLEステートメントで変更されるまで有効です。したがって、PRINTプロシージャのタイトルは、CONTENTSプロシージャで使用されるものと同じです。

VALUEステートメント#

FORMATプロシージャのINVALUEステートメントは、変数を意味がわかりやすいように読み取るために独自のカスタマイズされたインフォーマットを作成するのに対し、VALUEステートメントは変数を意味がわかりやすいように表示するために独自のカスタマイズされたフォーマットを作成するのに使用されます。カスタマイズされたフォーマットは変数の型を変更しませんが、SASが変数を表示する方法を指定します。例えば、次のVALUEステートメントで定義されたフォーマット「sexfmt」が数値変数「sex」に関連付けられている場合:

value sexfmt 1 = 'Male'
             2 = 'Female';

SASは変数「sex」の値が1の場合に「Male」を表示し、値が2の場合に「Female」を表示します。変数「sex」の型は数値のままです。VALUEステートメントの制約には以下があります:

  • 数値変数のフォーマット名(例:sexfmt)は、有効なSAS名で32文字まで、末尾に数字を付けてはならない

  • 文字変数のフォーマット名は$記号で始まり、その後31文字以内である必要がある

  • VALUEステートメントでフォーマットを定義するとき、フォーマット名はピリオドで終わってはならない

  • しかし、フォーマットを後で使用する際には、名前の後にピリオドを付ける必要がある

  • フォーマットラベルの最大長は32,767文字です(少し長いですが)

INVALUEステートメントと同様に、FORMATプロシージャのVALUEステートメントはフォーマットを定義するだけです。フォーマットを有効にするためには、DATAステップまたはPROCステップでFORMATステートメントを使用して変数にフォーマットを関連付ける必要があります。

#

次のFORMATプロシージャは、数値変数を出力時にフォーマット「sexfmt」および「racefmt」に関連付ける場合の定義方法を示しています:

proc format;
  value sexfmt 1 = 'Male'
               2 = 'Female';
 
  value racefmt 1 = 'Indian'
                2 = 'Asian'
                3 = 'Black'
                4 = 'White';
run;
11                                                        SAS システム               2024年 6月 6日 木曜日 07時33分00秒

88         ods listing close;ods html5 (id=saspy_internal) file=_tomods1 options(bitmap_mode='inline') device=svg style=HTMLBlue;
88       ! ods graphics on / outputfmt=png;
NOTE: HTML5(SASPY_INTERNAL) Bodyファイルの書き込み先: _TOMODS1
89         
90         proc format;
91           value sexfmt 1 = 'Male'
92                        2 = 'Female';
NOTE: 出力形式SEXFMTを作成しました。
93         
94           value racefmt 1 = 'Indian'
95                         2 = 'Asian'
96                         3 = 'Black'
97                         4 = 'White';
NOTE: 出力形式RACEFMTを作成しました。
98         run;

NOTE: PROCEDURE FORMAT処理(合計処理時間):
      処理時間           0.00 秒
      ユーザーCPU時間    0.00 秒
      システムCPU時間    0.00 秒
      メモリ             103.37k
      OSメモリ           18588.00k
      タイムスタンプ     2024/06/06 午前07:34:34
      ステップ数                        8  スイッチ数  0
      ページフォルト回数                0
      ページリクレーム回数              22
      ページスワップ回数                0
      自発的コンテキストスイッチ回数    0
      非自発的コンテキストスイッチ回数  0
      ブロック入力操作回数              0
      ブロック出力操作回数              40
      

99         
100        
101        ods html5 (id=saspy_internal) close;ods listing;
102        

12                                                        SAS システム               2024年 6月 6日 木曜日 07時33分00秒

103        

このコードにより、数値変数「sex」がフォーマット「sexfmt」に関連付けられると、SASが変数「sex」の数値の値1を表示するときには代わりに文字値「Male」が表示されます。同様に2の場合は「Female」が表示されます。プログラムを開いて実行します。ログウィンドウを確認し、メッセージが表示されることを確認してください。

定義を現在の作業セッションを超えて永続的に保存するには、LIBRARY=オプションをPROC FORMATステートメントに追加する必要があります。ここでは使用しないため、これらの定義は一時的なもので、現在のSASセッションを超えて保存されません。

次に、それらを使用してみましょう!

#

次のコードは、データセットbackに含まれる変数「sex」および「race」を意味がわかりやすいように表示するためにフォーマットを使用します:

data temp2;
  set back;
  f_race=race; 
  f_sex=sex;
  format f_race racefmt. f_sex sexfmt.;
run;

title 'Output Dataset: TEMP2'; 
proc print data=temp2;  
  var subj sex f_sex race f_race;
run;
 
proc contents data=temp2;
run;
SAS 出力

Output Dataset: TEMP2

OBS subj sex f_sex race f_race
1 110051 2 Female 4 White
2 110088 2 Female 4 White
3 210012 2 Female 4 White
4 220004 2 Female 4 White
5 230006 2 Female 4 White
6 310083 1 Male 2 Asian
7 410012 2 Female 4 White
8 420037 2 Female 4 White
9 510027 2 Female 4 White
10 520017 2 Female 4 White

Output Dataset: TEMP2

CONTENTS プロシジャ

データセット名 WORK.TEMP2 オブザベーション数 10
メンバータイプ DATA 変数の数 11
エンジン V9 インデックス数 0
作成日時 2024/06/06 07:34:45 オブザベーションのバッファ長 88
更新日時 2024/06/06 07:34:45 削除済みオブザベーション数 0
保護   圧縮済み NO
データセットタイプ   ソート済み NO
ラベル      
データ表現 SOLARIS_X86_64, LINUX_X86_64, ALPHA_TRU64, LINUX_IA64    
エンコード utf-8 Unicode (UTF-8)    
エンジン/ホスト関連情報
データセットのページサイズ 131072
データセットのページ数 1
データページの先頭 1
ページごとの最大OBS数 1486
先頭ページのOBS数 10
データセットの修復数 0
ファイル名 /saswork/SAS_work4B440001B7D0_odaws01-apse1.oda.sas.com/SAS_workA11D0001B7D0_odaws01-apse1.oda.sas.com/temp2.sas7bdat
作成したリリース 9.0401M7
作成したホスト Linux
I ノード番号 1610636146
アクセス権限 rw-r--r--
所有者名 user-name
ファイルサイズ 256KB
ファイルサイズ (バイト) 262144
変数と属性リスト (アルファベット順)
# 変数 タイプ 長さ 出力形式
9 age 数値 8 4.1
3 b_date 数値 8 MMDDYY8.
6 country 数値 8  
10 f_race 数値 8 RACEFMT.
11 f_sex 数値 8 SEXFMT.
7 race 数値 8  
8 relig 数値 8  
4 sex 数値 8  
5 state 数値 8  
1 subj 数値 8  
2 v_date 数値 8 MMDDYY8.

さて、これは正確には少し違います!まず、データセットbackから新しいデータセットtemp2を作成する際に、「f_sex」と「f_race」という2つの追加の変数(数値)が作成されます。これらはそれぞれ「sex」と「race」の変数に等しいものとします。変数「f_race」はracefmt.フォーマットと、「f_sex」はsexfmt.フォーマットと関連付けられます。SASフォーマットと同様に、FORMATステートメントをDATAステップまたはPROCステップのいずれかで指定できます。FORMATステートメントをPROCステップで指定した場合、フォーマットはそのプロシージャに関連付けられるだけです。一方、FORMATステートメントをDATAステップで指定した場合、そのフォーマットはすべての後続のプロシージャで使用可能になります。標準のSASフォーマットと同様に、ユーザー定義フォーマットを変数に関連付けるにはFORMATステートメントが必要です。FORMATステートメントの構文:

format f_race racefmt. f_sex sexfmt.;

は、変数「f_race」をracefmt.フォーマットと、変数「f_sex」をsexfmt.フォーマットと関連付けます。繰り返しになりますが、標準SASフォーマットと同様に、FORMATステートメントをDATAステップまたはPROCステップのいずれかで指定できます。FORMATステートメントをPROCステップで指定した場合、フォーマットはそのプロシージャに関連付けられるだけです。一方、FORMATステートメントをDATAステップで指定した場合、そのフォーマットはすべての後続のプロシージャで使用可能になります。
ちなみに、この例では教育目的でフォーマットされた変数とフォーマットされていない変数の両方を作成していますが、同じ変数のフォーマットされたバージョンとフォーマットされていないバージョンを作成する必要はありません。同じ変数の2つのバージョンを作成することで、性別と人種変数に対するフォーマットの効果を確認するのに役立ちます。

プログラムを開いて実行し、CONTENTSおよびPRINTプロシージャの出力を確認してください。特に、「f_sex」と「sex」(および「f_race」と「race」)の変数のフォーマットされたバージョンとフォーマットされていないバージョンの出力の違いを観察してください。また、CONTENTSプロシージャは、変数「sex」および「race」がフォーマットされていない数値変数であることを示しているのに対し、「f_sex」および「f_race」はフォーマットされた数値変数であることを示している点にも注意してください(特別なフォーマットが指定されているため)。

#

FORMATプロシージャは、1つまたは複数の連続変数をカテゴリ変数に変換した後に、意味のあるカテゴリを定義するのに役立ちます。次のコードはこの方法を示しています:

proc format;
  value age2fmt 1 = 'LT 20'
                2 = '20-44'
                3 = '45-54'
                4 = 'GE 54'
                OTHER = 'Missing';
run;
 
data temp3;
  set back;
       if age = . then age2 = .;
  else if age < 20 then age2 = 1;
  else if age >= 20 and age < 45 then age2 = 2;
  else if age >= 45 and age < 54 then age2 = 3;
  else if age >= 54 then age2 = 4;
  format age2 age2fmt.;
run;

title 'Age Frequency in TEMP3'; 
proc freq data=temp3;  
  table age2;
run;
SAS 出力

Age Frequency in TEMP3

FREQ プロシジャ

age2 度数 パーセント 累積
度数
累積
パーセント
20-44 5 50.00 5 50.00
45-54 3 30.00 8 80.00
GE 54 2 20.00 10 100.00

FORMATプロシージャは、年齢を5つのカテゴリに分類するフォーマットAGE2FMTを定義します:20歳未満、20-44歳、45-54歳、54歳以上、および欠損値
特別な範囲キーワードOTHERは、他のすべての年齢値を1つのグループにまとめます。ここでは、欠損値が唯一の他の可能な値であるため、OTHERカテゴリに該当する値は「Missing」とラベル付けされます。
データセットtemp3はデータセットbackから派生しています。これら2つのデータセットの唯一の違いは、新しい変数「age2」がデータセットtemp3にif-then-elseステートメントを使用して年齢値をグループ化することで作成されていることです。FORMATステートメントは、FORMATプロシージャで定義されたフォーマットage2fmtと変数「age2」を関連付けます。ifステートメントが年齢の欠損値をコード化している点に注意してください。これがなければ、年齢の欠損値は誤って1としてコード化され、「LT 20」として出力されることになります。

FREQプロシージャは、TABLEステートメントで定義されたカテゴリ変数のさまざまな水準の頻度を決定します。TABLEステートメントに1つの変数(age2)のみが指定されているため、SASは年齢が20未満、20から44の間、45から54の間、54以上、欠損の被験者数を含む1つの表を出力します。0カウントのカテゴリ(LT 20および欠損)は表に表示されない点に注意してください。
プログラムを開いて実行し、元のデータセットおよびFREQプロシージャの出力を確認して、年齢カテゴリが適切にラベル付けされていることを確認してください。

#

次に、1つの変数の値をグループ化することに関心がある場合、前のプログラムで行ったようにするのではなく、FORMATプロシージャ内で直接これをより効率的に行うことができます。次のコードは、変数ageの可能な値に基づいてフォーマットagefmtを定義するためにFORMATプロシージャを使用します:

proc format;
  value agefmt LOW-<20  = 'LT 20'
               20-<45  = '20-44'
               45-<54  = '45-54'
               54-HIGH = 'GE 54'
               OTHER   = 'Missing';
run;

title 'Age Frequency in BACK';  
proc freq data=back;  
  format age agefmt.;
  table age;
run;
SAS 出力

Age Frequency in BACK

FREQ プロシジャ

age 度数 パーセント 累積
度数
累積
パーセント
20-44 5 50.00 5 50.00
45-54 3 30.00 8 80.00
GE 54 2 20.00 10 100.00

FORMATプロシージャの中で値のグループを定義する際には、以下の点:

  • FORMATプロシージャ内で値のグループを定義する際に、このプログラムで示されるように、含める値の範囲はダッシュ(-)を使用して定義されます。

  • 値の範囲をコンマで区切ってリストすることもできます:1,2,3 = ‘Low’。「<」は「含まない」ことを意味します。したがって、ここでは例えば、20-<45は20歳から45歳までを含みますが、45歳は含まれません。

  • LOWおよびHIGHの特別な範囲は、それぞれ最小値および最大値を知らなくても値をグループ化することができます。(LOWキーワードは欠損数値値を含みませんが、文字フォーマットに適用すると、欠損文字値を含みます。)

に注意してください。
FREQプロシージャは、FORMATプロシージャで定義された年齢グループごとに被験者の数を集計します。ここで、変数「age」はFREQプロシージャ内のFORMATステートメントを使用してフォーマットagefmtと関連付けられています。 プログラムを開いて実行し、元のデータセットおよびFREQプロシージャの出力を確認して、年齢カテゴリが再び適切にラベル付けされていることを確認してください。

永久フォーマットカタログ#

これまでに作成したすべてのカスタマイズされたインフォーマットおよびフォーマット定義は、一時的にのみ保存されています。つまり、インフォーマットおよびフォーマットは、それらが定義されたSASセッションの間のみ有効です。別のSASプログラムでインフォーマットまたはフォーマットを再び使用したい場合は、別のFORMATプロシージャを使用してそれらを再作成する必要があります。カスタマイズされたインフォーマットまたはフォーマットを繰り返し使用する予定がある場合は、LIBRARY=オプションをPROC FORMATステートメントで使用してそれをフォーマットカタログに永続的に保存できます。基本的に、LIBRARY=オプションは、フォーマットカタログが保存されている(または保存する)場所をSASに伝えます。LIBNAMEステートメントを使用して、SASにライブラリ(ディレクトリまたは場所)を指定します:

LIBNAME libref 'c:\directory\where\formats\stored';

librefはあなたが設定した名前です。ただし、ユーザー定義のインフォーマットまたはフォーマットがDATAまたはPROCステップによって呼び出された場合、SASは最初にwork.formatsという一時フォーマットカタログを探します。(「work」はSASが常に一時的な作業ライブラリとして扱うものであり、SASセッションの終了時に消去されます。)SASが一時カタログにフォーマットまたはインフォーマットを見つけられない場合、デフォルトでlibrary.formatsという永続的なカタログを探します。したがって、librefは技術的にはあなたが選んだ名前ですが、SASが最初に探すのはlibraryという名前なので、それをlibraryと設定しておくと便利です。永久フォーマットカタログを作成する際にlibrefとしてlibraryという言葉を使用することを推奨しますが、これは必須ではありません。

この話を具体的にするために、次のLIBNAMEステートメントがSASプログラムにあると仮定します:

LIBNAME library 'C:\Simon\Stat480WCDEV\08format\sasndata\';

そして、次のようにFORMATプロシージャが始まります:

PROC FORMAT library=library;

その後、プログラムを実行すると、SASはFORMATプロシージャで定義されたすべてのフォーマットおよびインフォーマットを含む永続的なカタログを作成し、それを上記のフォルダに保存します。

LIBNAMEステートメントで定義されたファイル位置にあるフォーマット定義ファイル

フォーマットカタログは、一時カタログ(work.formats)であろうと、永久カタログ(library.formats)であろうと、FORMATプロシージャで定義された各フォーマットまたはインフォーマットを少なくとも1つ含みます。library.formatsは永久フォーマットカタログの予約名であるため、SASライブラリ(ディレクトリ)ごとに1つのformatsカタログしか作成できません。この制限を回避する方法はありますが、今はそれについては触れません。代わりに、例に進みましょう。

#

次のプログラムは、libraryで参照されるディレクトリ、つまり/folders/myfolders/SAS_Notes/dataにFORMATプロシージャで永久フォーマットカタログを作成する例を示しています:

libname library '/folders/myfolders/SAS_Notes/data/';
 
proc format library=library;
  value sex2fmt 1 = 'Male'
                2 = 'Female';
 
  value race2fmt 3 = 'Black'
                 4 = 'White'
                 OTHER = 'Other';
run;
 
data temp4; 
  infile '/folders/myfolders/SAS_Notes/data/back.dat';
  input subj 1-6 sex 17 race 19;
  format sex sex2fmt. race race2fmt.;
run;

title 'Output Dataset: TEMP4'; 
proc contents data=temp4;   
run;
 
proc print data=temp4;
run;
SAS 出力

Output Dataset: TEMP4

CONTENTS プロシジャ

データセット名 WORK.TEMP4 オブザベーション数 10
メンバータイプ DATA 変数の数 3
エンジン V9 インデックス数 0
作成日時 2024/06/06 07:35:44 オブザベーションのバッファ長 24
更新日時 2024/06/06 07:35:44 削除済みオブザベーション数 0
保護   圧縮済み NO
データセットタイプ   ソート済み NO
ラベル      
データ表現 SOLARIS_X86_64, LINUX_X86_64, ALPHA_TRU64, LINUX_IA64    
エンコード utf-8 Unicode (UTF-8)    
エンジン/ホスト関連情報
データセットのページサイズ 131072
データセットのページ数 1
データページの先頭 1
ページごとの最大OBS数 5431
先頭ページのOBS数 10
データセットの修復数 0
ファイル名 /saswork/SAS_work4B440001B7D0_odaws01-apse1.oda.sas.com/SAS_workA11D0001B7D0_odaws01-apse1.oda.sas.com/temp4.sas7bdat
作成したリリース 9.0401M7
作成したホスト Linux
I ノード番号 1610639877
アクセス権限 rw-r--r--
所有者名 user-name
ファイルサイズ 256KB
ファイルサイズ (バイト) 262144
変数と属性リスト (アルファベット順)
# 変数 タイプ 長さ 出力形式
3 race 数値 8 RACE2FMT.
2 sex 数値 8 SEX2FMT.
1 subj 数値 8  

Output Dataset: TEMP4

OBS subj sex race
1 110051 Female White
2 110088 Female White
3 210012 Female White
4 220004 Female White
5 230006 Female White
6 310083 Male Other
7 410012 Female White
8 420037 Female White
9 510027 Female White
10 520017 Female White

DATAステップは変数「subj」、「sex」、および「race」を生データファイルback.datから読み取り、それぞれの変数「sex」および「race」をFORMATプロシージャで定義されたフォーマットsex2fmtおよびrace2fmtと関連付けることで、一時データセットtemp4を作成します。SASはまず、これら2つのフォーマットが一時フォーマットカタログwork.formatsに存在するかどうかを確認し、見つからない場合、/folders/myfolders/SAS_Notes/dataディレクトリの永久フォーマットカタログでそれらを探します。 プログラムを開き、INFILEステートメントを編集してback.datファイルの場所を反映させます。そして、LIBNAMEステートメントを編集して永久フォーマットカタログの目的の場所を反映させます。次に、プログラムを実行し、CONTENTSおよびPRINTプロシージャの出力を確認して、変数「sex」および「race」が以前に「f_sex」および「f_race」に関連付けられた一時フォーマットではなく、永久フォーマットsex2fmtおよびrace2fmtに関連付けられていることを確認してください。また、LIBNAMEステートメントで参照されているディレクトリを確認し、SASがそこに永久フォーマットカタログを作成して保存したことを確認してください。

この永久フォーマットに関するいくつかのコメントとして、永続的にインフォーマットおよびフォーマットに関連付けられた変数が存在する場合、SASはフォーマットカタログにアクセスできるようにライブラリを参照する必要があります。フォーマットカタログが存在し、ファイルにアクセスできる権限がある限り、適切なLIBNAMEステートメントを指定するだけです:

LIBNAME library '/folders/myfolders/SAS_Notes/data';

カタログにアクセスします。何らかの理由でフォーマットカタログにアクセスできない場合、SASは次のようなエラーを出します: SAS error message when format definitions cannot be found.

OPTIONSステートメントでNOFMTERRを指定すると:

OPTIONS NOFMTERR;

SASデータセットをエラーなしで使用できます。SASはログファイルにメモ(プログラム停止エラーではなく)を表示するだけです:

永久フォーマットを含むデータセットを使用するSASプログラムを実行できます。ただし、フォーマットにはアクセスできません。

#

永久フォーマットカタログを作成する代わりに、VALUEおよびINVALUEステートメントのみを含むFORMATプロシージャを含むSASプログラムファイルを作成し、以下のようにこのプログラムファイルを%INCLUDEステートメントを使用してメインSASプログラムに含めることもできます。

%INCLUDE '/folders/myfolders/SAS_Notes/data/backfmt.sas';

title 'Frequency Count of STATE (statefmt)'; 
proc freq data=back;
  format state statefmt.;
  table state / missing;
run;
SAS 出力

Frequency Count of STATE (statefmt)

FREQ プロシジャ

state 度数 パーセント 累積
度数
累積
パーセント
Missing 2 20.00 2 20.00
Ind 1 10.00 3 30.00
Mass 1 10.00 4 40.00
Mich 2 20.00 6 60.00
Minn 1 10.00 7 70.00
Other 1 10.00 8 80.00
Tenn 1 10.00 9 90.00
Wisc 1 10.00 10 100.00

明確にするために、ファイルbackfmt.sasに含まれるコードを示します:

proc format;
  value statefmt 14 = 'Ind'
                 21 = 'Mass'
                 22 = 'Mich'
                 23 = 'Minn'
                 42 = 'Tenn'
                 49 = 'Wisc'
                 .  = 'Missing'
              Other = 'Other';
run;

ファイルbackfmt.sas内のFORMATプロシージャは永久ライブラリを参照していないため、フォーマットstatefmtは一時的なwork.formatsカタログに保存されます。
このプログラムを実行するには、まずバックfmt.sasファイルをダウンロードしてコンピュータの便利な場所に保存します(READMEを参照)。次に、SASプログラムを開き、%INCLUDEステートメントを編集してバックfmt.sasファイルの場所を反映させます。最後に、プログラムを実行し、FREQプロシージャの出力を確認します。FREQプロシージャのFORMATステートメントが、backfmt.sas内のFORMATプロシージャで作成されたstatefmtフォーマットと適切に関連付けられていることを確認してください。また、FREQプロシージャのMISSINGオプションの効果にも注意してください。これは欠損値を集計可能なカテゴリとして含めるようSASに指示します。
この例の方法は、データセットが共有されるオープンな環境で作業する際には、ユーザーによってフォーマットファイルにアクセスできない場合や、異なるフォーマットを好む場合があることから特に有用です。

フォーマットを定義するためのコードブックの使用#

離散(カテゴリカル)変数には非常に多くの(数百、場合によっては数千の)可能な値があることが一般的です。例としては以下のようなものがあります:

  • 疾患は整数でコード化されることがあります

  • 外科手術は整数でコード化されることがあります

  • 薬剤は整数でコード化されることがあります

電子的な「コードブック」は、これらの整数コードの意味を把握するために一般的に使用されます。つまり、コードブックにはコードとコードのテキスト記述の2つの変数が含まれます。例えば、疾患1124は「Rheumatoid Arthritis」とテキストで定義されることがあります。

これらのコードブックに含まれるコードとテキストを、FORMATプロシージャで再度定義するために手動で入力するのは非常に面倒で時間がかかります。代わりに、コードブックが既に電子フォーマット、例えばデータベース、生データファイル、あるいはSASデータセットの形式で存在していることを利用できます。

コードブックが次の3つの必要な変数を持つSASデータセットである場合:

  • start:範囲の開始値(つまりコード)を含む変数

  • label:テキスト定義を含む変数

  • fmtname:フォーマット名を含む変数

SASはPROC FORMATステートメントのCNTLIN = オプションを使用して適切なフォーマットを作成できます。例を見てみましょう。

#

次のプログラムは、ICDBの背景フォームで収集された変数「state」のコードブックであるstate_cdからstatesというデータセットを作成します。データセットstate_cdの最初の10件のオブザベーションは次のようになります:

title 'Codebook for States';
proc print data=phc6089.state_cd(obs=10);    
run;
SAS 出力

Codebook for States

OBS NAME CODE
1 alabama 1
2 alaska 2
3 arizona 3
4 arkansas 4
5 california 5
6 colorado 6
7 connecticut 7
8 delaware 8
9 florida 9
10 georgia 10

データセットstateは、その後、データセットbackの変数「state」のフォーマットを定義するためにFORMATプロシージャで使用されます。

data states;
  set phc6089.state_cd (rename = (code = start name=label));
  fmtname = 'stat2fmt';
run;
 
proc format cntlin=states;
run;

title 'Freqency Count of STATE (stat2fmt)'; 
proc freq data=back;   
  format state stat2fmt.;
  table state;
run;
SAS 出力

Freqency Count of STATE (stat2fmt)

FREQ プロシジャ

state 度数 パーセント 累積
度数
累積
パーセント
欠損値の度数 = 2
indiana 1 12.50 1 12.50
massachusetts 1 12.50 2 25.00
michigan 2 25.00 4 50.00
minnesota 1 12.50 5 62.50
pennsylvania 1 12.50 6 75.00
tennessee 1 12.50 7 87.50
wisconsin 1 12.50 8 100.00

このプログラムを実行する前に、コードブックstate_cdをダウンロードし、以前にlibref phc6089で参照したコンピュータの場所に保存してください(README参照)。その後、プログラムを開いて実行してください。
出力からわかるように、PRINTプロシージャはphc6089.state_cdコードブックの(最初の10行を)単純に出力します。state_cdの変数名がSASのコードブックの要件を満たしていないことに気付くはずです。したがって、データセットstatesを作成するDATAステップでは、phc6089.state_cdの変数「code」を「start」に、変数「name」を「label」に変更して、要件を満たすようにしています。SETステートメントのRENAME=オプションを使用して、変数「code」を「start」に、変数「name」を「label」に変更します。RENAME=オプションの一般的な構文は以下の通りです:

set dsname (rename = (oldvr1 = newvr1 oldvr2 = newvr2 ...));

次に、データセットphc6089.state_cdに含まれる各オブザベーション(つまりコード)に対して、変数「fmtname」に”stat2fmt”を割り当てる割り当てステートメントを使用します。
その後、CNTLIN = statesオプションを使用したFORMATプロシージャは、データセットstatesの内容に基づいてフォーマットstat2fmtを作成するようSASに指示します。
最後に、FREQプロシージャでこの方法で作成されたstat2fmtを指定します。SASは各州の被験者の数をカウントして結果を出力します。TABLEステートメントにMISSINGオプションを含めていないため、SASは欠損値の数をテーブルの行ではなくテーブルの後に記載します。

FMTLIBオプション#

FORMATプロシージャ自体が出力を生成しないことに気付いたかもしれません。実際、FMTLIBオプションをPROC FORMATステートメントで指定した場合にのみ、FORMATプロシージャは出力を表示します。FORMATプロシージャのFMTLIBオプションは、カタログ内のすべてのフォーマットおよび/またはインフォーマットのリストを、それらの値の説明とともに表示するようにSASに指示します。FMTLIBオプションは、大規模なフォーマットカタログを扱っていて、特定のフォーマット名の正確な綴りやその値の範囲を忘れた場合に特に役立ちます。

#

次のコードは、カタログwork.formatに表示される3つのフォーマットに関する情報をSASに表示するよう要求するためにFORMATプロシージャのFMTLIBオプションを使用しています:

title 'Selected Formats from WORK.FORMAT Catalog';
proc format fmtlib;   
  select racefmt;
run;
SAS 出力

Selected Formats from WORK.FORMAT Catalog

                            ----------------------------------------------------------------------------                            
                            |       FORMAT NAME: RACEFMT  LENGTH:    6   NUMBER OF VALUES:    4        |                            
                            |   MIN LENGTH:   1  MAX LENGTH:  40  DEFAULT LENGTH:   6  FUZZ: STD       |                            
                            |--------------------------------------------------------------------------|                            
                            |START           |END             |LABEL  (VER. V7|V8   06JUN2024:07:34:35)|                            
                            |----------------+----------------+----------------------------------------|                            
                            |               1|               1|Indian                                  |                            
                            |               2|               2|Asian                                   |                            
                            |               3|               3|Black                                   |                            
                            |               4|               4|White                                   |                            
                            ----------------------------------------------------------------------------                            

プログラムを開いて実行し、出力を確認してください。ここでのFORMATプロシージャは永久ライブラリを参照していないため、一時カタログwork.formatsの内容が表示されます。SELECTステートメントはカタログ全体ではなく選択された少数のフォーマットに関する情報のみを表示するように指示します(SELECTステートメントおよびEXCLUDEステートメントの詳細についてはSASヘルプを参照してください)。
この例では使用されていませんが、PAGEオプションを追加で使用して、カタログ内の各フォーマットおよびインフォーマットに関する情報を別々のページに表示するように指定することができます。

proc format fmtlib page;
run;

FORMATプロシージャのPAGEオプションは、FMTLIBオプションが呼び出されていない限り意味がありません。

日付および時間の処理#

SASは日付を一意の数値として保存し、プログラム内で他の数値と同様に使用できるようにします。具体的には、SASは日付を1960年1月1日からの日数として保存します。つまり、1960年1月1日以前の日付は一意の負の整数として保存され、1960年1月1日以降の日付は一意の正の整数として保存されます。例えば、SASは以下のように保存します:

  • 1960年1月1日は0

  • 1960年1月2日は1

  • 1960年1月3日は2

  • 以降も同様…

また、SASは以下のように保存します:

  • 1959年12月31日は-1

  • 1959年12月30日は-2

  • 1959年12月29日は-3

  • 以降も同様…

どの方法を使用してSAS日付を作成しても、SASは常に日付を上記のように整数に変換します。

日付のインフォーマットおよびフォーマット#

日付が含まれる変数を読み取るためには、その日付がどの形式であるかをSASに伝える必要があります。例えば、日付の形式は「Dec 1, 2005」の形式ですか?それとも「12/01/05」ですか?あるいは「01 December 2005」ですか?読み込み際の日付の形式は日付インフォーマットです。SASには想像できる限りの方法で日付を記述するための日付インフォーマットが数多くあります。まぁ、実際にはそれほど多くはないかもしれませんが。このセクションの後半で利用可能なインフォーマットのいくつかを見ていきます。まずは、inputステートメントで日付を読み込むために必要な指定を復習しましょう。

#

次のプログラムは、5つのオブザベーションをデータセットdietに読み込みます。2つの変数 — 体重日(wt_date)および誕生日(b_date) — はmm/dd/yy形式であり、したがってmmddyy8.インフォーマットを使用して日付を読み込むように指示します:

data diet;
  input subj 1-4 l_name $ 18-23 weight 30-32
      +1 wt_date mmddyy8. @43 b_date mmddyy8.;
  datalines;
1024 Alice       Smith  1 65 125 12/1/05  01/01/60
1167 Maryann     White  1 68 140 12/01/05 01/01/59
1168 Thomas      Jones  2    190 12/2/05  06/15/60
1201 Benedictine Arnold 2 68 190 11/30/05 12/31/60
1302 Felicia     Ho     1 63 115 1/1/06   06/15/58
;
run;

title 'The unformatted diet data set'; 
proc print data=diet;
run;
SAS 出力

The unformatted diet data set

OBS subj l_name weight wt_date b_date
1 1024 Smith 125 16771 0
2 1167 White 140 16771 -365
3 1168 Jones 190 16772 166
4 1201 Arnold 190 16770 365
5 1302 Ho 115 16802 -565

まず、mmddyy8.インフォーマットが日付の変数名の直後にあることに注意してください。ここでは、「wt_date」の直後に、その次に「b_date」の直後にあります。ちなみに、mmddyy8.の8は、インフォーマットの幅を一般的に定義しています。それは、SASに読み込まれる日付が最大8文字であることを示します。ここでは、2つの位置がスラッシュ(/)によって占められています。代わりに、ハイフン(-)や空白スペースをmmとddおよびyyの間に使用することもできます。また、ピリオドはインフォーマット名の非常に重要な部分です。これがないと、SASはインフォーマットを変数名として解釈しようとするかもしれません。

次に、SASプログラムを開いて実行し、データセットdietの内容に慣れるために結果の出力を確認してください。特に、変数「wt_date」および「b_date」に保存された数値に注目してください。予想通り、1960年1月1日の誕生日は0として保存され、1959年1月1日の誕生日は-365として保存され、1960年12月31日の誕生日は+365として保存されます。そして、もう一つの点として、インフォーマットを使用して日付の値を読み込むようにSASに指示するだけでは不十分であり、日付の値を表示するためのフォーマットをSASに指示する必要があることを示しています。そうしないと、ここで見たように、表示される日付は特にユーザーフレンドリーではありません!

#

次のプログラムは前のプログラムと同じですが、変数「wt_date」および「b_date」をdate7.形式で表示するようSASに指示するFORMATステートメントが追加されています:

data diet;
  input subj 1-4 l_name $ 18-23 weight 30-32
          +1 wt_date mmddyy8. @43 b_date mmddyy8.;
  format wt_date b_date date7.;
  datalines;
1024 Alice       Smith  1 65 125 12/1/05  01/01/60
1167 Maryann     White  1 68 140 12/01/05 01/01/59
1168 Thomas      Jones  2    190 12/2/05  06/15/60
1201 Benedictine Arnold 2 68 190 11/30/05 12/31/60
1302 Felicia     Ho     1 63 115 1/1/06   06/15/58
    ;
run;

title 'The formatted diet data set'; 
proc print data=diet;    
run;
SAS 出力

The formatted diet data set

OBS subj l_name weight wt_date b_date
1 1024 Smith 125 01DEC05 01JAN60
2 1167 White 140 01DEC05 01JAN59
3 1168 Jones 190 02DEC05 15JUN60
4 1201 Arnold 190 30NOV05 31DEC60
5 1302 Ho 115 01JAN06 15JUN58

前の例が示すように、SASにどの形式で日付を表示したいかを指示する必要があります。出力時の日付の形式は日付フォーマットです。日付を「Dec 1, 2005」の形式で表示しますか?あるいは「12/01/05」ですか?あるいは「01 December 2005」ですか?繰り返しになりますが、SASには日付を記述するための日付フォーマットが数多くあります。どの形式で日付を表示したいかをSASに指示するために、FORMATステートメントを使用します。

まず、FORMATステートメントに注目してください。ここでは、date7.フォーマットが、値をddMonyy形式で表示したい2つの変数名 —「wt_date」および「b_date」 — に続きます。その後、SASプログラムを開いて実行し、FORMATステートメントの動作を結果の出力から確認してください。

SAS日付の最も良い点は、日付値が数値であるため、簡単に並べ替えたり、引き算や足し算ができることです。日付を比較することもできます。また、多くの利用可能な数値関数で使用することもできます。

#

次のプログラムは、日付変数を他の数値変数と同様に扱うことができるため、数値計算で日付を使用できることを示しています。データセットdietの個人が14日ごとに体重を測定する必要があると仮定すると、個人の次回訪問の予定日の新しい変数「nxt_date」を、個人の現在の体重の測定日(wt_date)に14を足すだけで決定します。その後、個人の年齢の概算も、「wt_date」から「b_date」を引き、その結果の日数を365.25で割って、おおよその年齢を年単位で計算します。そして、MEAN関数を使用して、各個人の誕生日と体重日の平均を計算します:

data diet;
  input  subj 1-4 l_name $ 18-23 weight 30-32
         +1 wt_date mmddyy8. @43 b_date mmddyy8.;
  nxt_date = wt_date + 14;
  age_wt = (wt_date - b_date)/365.25;
  avg_date = MEAN(wt_date, b_date);
  format wt_date b_date nxt_date avg_date date7. 
         age_wt 4.1; 
  datalines;
1024 Alice       Smith  1 65 125 12/1/05  01/01/60
1167 Maryann     White  1 68 140 12/01/05 01/01/59
1168 Thomas      Jones  2    190 12/2/05  06/15/60
1201 Benedictine Arnold 2 68 190 11/30/05 12/31/60
1302 Felicia     Ho     1 63 115 1/1/06   06/15/58
    ;
run;

title 'The diet data set with three new variables'; 
proc print data=diet;    
run;
SAS 出力

The diet data set with three new variables

OBS subj l_name weight wt_date b_date nxt_date age_wt avg_date
1 1024 Smith 125 01DEC05 01JAN60 15DEC05 45.9 16DEC82
2 1167 White 140 01DEC05 01JAN59 15DEC05 46.9 17JUN82
3 1168 Jones 190 02DEC05 15JUN60 16DEC05 45.5 10MAR83
4 1201 Arnold 190 30NOV05 31DEC60 14DEC05 44.9 16JUN83
5 1302 Ho 115 01JAN06 15JUN58 15JAN06 47.5 24MAR82

まず、3つの新しい変数 — 「nxt_date」、「age_wt」および「avg_date」 — が標準的な数値の式を使用してどのように計算されているかを確認してください。また、「avg_date」の計算は、日付を標準の数値関数で使用することを示すためのだけの試みであり、他にはほとんど役に立たないことも覚えておいてください。その後、SASプログラムを開いて実行し、計算結果が問題ないことを結果の出力から確認してください。

#

次のプログラムは、日付変数を他の数値変数と同様に扱うことができることにより、日付を並べ替えることができることを示しています。データセットdietは、「nxt_date」で昇順に並べ替えられ、次回の体重測定日が最も近い個人が最初に表示されます:

proc sort data = diet out = sorteddiet;
  by nxt_date;
run;

title 'The diet data set sorted by nxt_date';             
proc print data = sorteddiet;    
run;
SAS 出力

The diet data set sorted by nxt_date

OBS subj l_name weight wt_date b_date nxt_date age_wt avg_date
1 1201 Arnold 190 30NOV05 31DEC60 14DEC05 44.9 16JUN83
2 1024 Smith 125 01DEC05 01JAN60 15DEC05 45.9 16DEC82
3 1167 White 140 01DEC05 01JAN59 15DEC05 46.9 17JUN82
4 1168 Jones 190 02DEC05 15JUN60 16DEC05 45.5 10MAR83
5 1302 Ho 115 01JAN06 15JUN58 15JAN06 47.5 24MAR82

プログラムを開いてコードを確認し、実行します。次に、結果の出力を確認し、変数「nxt_date」が期待のとおりにソートされていることを確認します。

繰り返しになりますが、SASの日付値は数値値であるため、2つ以上の日付を簡単に比較できます。比較は任意の2つの数値間の比較と同様に行われます。たとえば、日付01/03/60がSASでは2として格納されるため、日付01/10/60(SASでは9として格納される)よりも小さいと見なされます。

#

以下のプログラムは、ある日付変数の値を他の日付変数の値ではなく、日付定数と比較する方法を示しています。具体的には、DATAステートメントに表示されるWHERE=オプションは、「b_date」が1960年1月1日より前の日付の個人のみをデータセットdietに出力するように指示します:

data diet (where = (b_date < '01jan1960'd));
  input subj 1-4 l_name $ 18-23 weight 30-32
      +1 wt_date mmddyy8. @43 b_date mmddyy8.;
  format wt_date b_date date9.;
  datalines;
1024 Alice       Smith  1 65 125 12/1/05  01/01/60
1167 Maryann     White  1 68 140 12/01/05 01/01/59
1168 Thomas      Jones  2    190 12/2/05  06/15/60
1201 Benedictine Arnold 2 68 190 11/30/05 12/31/60
1302 Felicia     Ho     1 63 115 1/1/06   06/15/58
;
run;

title 'Birthdays in the diet data set before 01/01/1960';
proc print data=diet;
run;
SAS 出力

Birthdays in the diet data set before 01/01/1960

OBS subj l_name weight wt_date b_date
1 1167 White 140 01DEC2005 01JAN1959
2 1302 Ho 115 01JAN2006 15JUN1958

まず、SAS日付定数の形式に注意してください:

'01jan1960'd

これはデータセットオプションのWHERE=オプションで使用されます。一般的に、SAS日付定数は’ddMONyyyy’dの形式を取ります。ここで、ddは月の日(0から31)、MONは月の最初の3文字、yyyyは4桁の年を表します。日付の後に一重引用符のdが続くことで、SASは日付文字列を定数として扱います。SASの日付をどのようにインフォーマットまたはフォーマットしたとしても、SAS日付定数は常に上記の形式を取ります。 次に、SASプログラムを開いて実行します。次に、結果の出力を確認し、1960年1月1日より前の日付を持つ個人のみがデータセットdietに含まれていることを確認します。また、フォーマットdate7.とdate9.の違いに注意することをお勧めします。以前、フォーマットdate7.を使用すると、日付がddMonyy形式で表示されることを確認しました。ここでは、date9.フォーマットを使用すると、日付がddMonyyyy形式で表示されることを確認できます。(ちなみに、可能な限り4桁の年を使用するのが良い習慣だと思います。これはあらゆる曖昧さを避けるためです。)このセクションの後半では、他のインフォーマットとフォーマットをいくつか見ていきます。まずは、SAS日付値で特に機能する関数のいくつかを見てみましょう。

これまでに、インフォーマットmmddyy8.を使用してSAS日付を読み込んできました。また、フォーマットdate7.およびdate9.を使用してSAS日付を表示してきました。このセクションでは、SASで利用可能な他のインフォーマットとフォーマットの簡単な例をいくつか見ていきます。

#

次のプログラムでは、インフォーマットmmddyyを使用して3つの日付(date1、date2、date3)を読み込んでいます。その後、フォーマットddmmyyを使用して日付を出力しています。

data inputdates1;
  input @6 date1 mmddyy6. @13 date2 mmddyy8. @22 date3 mmddyy10.;
  format date1 ddmmyy10. date2 ddmmyyb10. date3 ddmmyyc10.;
  datalines;
     041008 04-10-08 04 10 2008
  ;
run;

title 'The mmddyy informat and the ddmmyy format'; 
proc print data = inputdates1;    
run;
SAS 出力

The mmddyy informat and the ddmmyy format

OBS date1 date2 date3
1 10/04/2008 10 04 2008 10:04:2008

まず、INPUTステートメントとDATALINESステートメントの2008年4月10日の日付の表記の形式を確認してください。特に、インフォーマットmmddyyの幅(6、8、または10)がSASに期待される日付の形式を伝えていることに注目してください。インフォーマットの幅を間違えると、SASが警告を出してくれます!また、日付をフォーマットする方法は、日付をインフォーマットする方法とは完全に独立していることにも注意してください。ここでは、インフォーマットmmddyyを使用して日付を読み込み、並び替えられたフォーマットddmmyyで表示しています。フォーマットddmmyyについてもう少し詳しく説明しましょう。変数「date2」のフォーマットに現れる “b” はSASに対して月、日、年の間に空白を表示するように指示しています。date3変数のフォーマットに現れる “c” はSASに対して月、日、年の間にコロンを表示するように指示しています。フォーマットに何も指定されていない場合 (または “s” と指定された場合)、date1変数のように、SASは月、日、年の間にスラッシュを表示します。

インフォーマットmmddyyとフォーマットddmmyyの使用方法を理解したら、SASプログラムを開いて実行してください。出力からプログラムが想定通りに動作することを確認してください。

#

次のプログラムでは、インフォーマットddmmyyを使用して3つの日付(date1、date2、date3)を読み込んでいます。その後、フォーマットmmddyyを使用して日付を出力しています。

data inputdates2;
  input @6 date1 ddmmyy6. @13 date2 ddmmyy8. @22 date3 ddmmyy10.;
  format date1 mmddyyd10. date2 mmddyyn8. date3 mmddyyp10.;
  datalines;
     100408 10-04-08 10 04 2008
;
run;

title 'The ddmmyy informat and the mmddyy format'; 
proc print data = inputdates2;
run;
SAS 出力

The ddmmyy informat and the mmddyy format

OBS date1 date2 date3
1 04-10-2008 04102008 04.10.2008

まず、INPUTステートメントとDATALINESステートメントの2008年4月10日の日付の表記形式を確認してください。インフォーマットddmmyyの幅(6、8、または10)がSASに期待される日付の形式を伝えていることにも注意してください。変数「date1」のフォーマットに現れる “d” はSASに対して月、日、年の間にハイフンを表示するように指示しています。変数「date2」のフォーマットに現れる “n” はSASに対して月、日、年の間に何も表示しないように指示しています。(mmddyyn8.フォーマットの幅は8であり、10ではないことに注意してください。”n”拡張子を指定した場合に幅を10と指定すると、SASはエラーを出します。) 変数「date3」のフォーマットに現れる “p” はSASに対して月、日、年の間にピリオドを表示するように指示しています。

インフォーマットddmmyyとフォーマットmmddyyの使用方法を理解したら、SASプログラムを開いて実行してください。出力からプログラムが想定通りに動作することを確認してください。

#

次のプログラムでは、インフォーマットdateを使用して3つの日付(date1、date2、date3)を読み込んでいます。その後、フォーマットweekdate、worddate、およびworddatxを使用してそれぞれ日付を出力しています。

data inputdates3;
  input @6 date1 date7. @14 date2 date9. @24 date3 date11.;
  format date1 weekdate25. 
         date2 worddate19.
         date3 worddatx19.;
  datalines;
     10Apr08 10Apr2008 10-Apr-2008
;
run;

title 'The date7 informat and the weekdate and worddate formats'; 
proc print data = inputdates3;
run;
SAS 出力

The date7 informat and the weekdate and worddate formats

OBS date1 date2 date3
1 Thursday, Apr 10, 2008 April 10, 2008 10 April 2008

まず、INPUTステートメントとDATALINESステートメントの2008年4月10日の日付の表記形式を確認してください。インフォーマットdateの幅(7、9、または11)がSASに期待される日付の形式を伝えていることにも注意してください。インフォーマットの幅を間違えると、SASが警告を出してくれます!

次に、プログラムを開いて実行し、出力を確認して、weekdate、worddate、およびworddatxフォーマットで日付がどのように表示されるかを理解してください。これらのフォーマットに指定した幅が小さすぎる場合、SASは日付を省略形で表示しようとします。weekdateフォーマットの幅を20に変更して、実際にそうなるかを確認してみてもよいでしょう。

SAS日付関数#

SASで利用可能な日付関数は次のことに使用できます:

  • 日付値の作成

  • 日付値の分解

  • 日付値の調整

  • 間隔の計算

特に理由はありませんが、この順序で見ていきます。

関数を使用して日付値を作成する#

日付値を作成するために使用できる関数には次のものがあります:

  • date() 今日の日付をSAS日付値として返します

  • today() 今日の日付をSAS日付値として返します

  • mdy(m, d, y) 指定された月(m)、日(d)、年(y)値からSAS日付値を返します

  • datejul(juldate) ジュリアン日付(juldate)をSAS日付値に変換します

  • yyq(y, q) 指定された年(y)と四半期(q:1、2、3、または4)からSAS日付値を返します

date()とtoday()関数は同等です。つまり、どちらもSASプログラムが実行される日に定義される現在の日付を返します。これら2つの関数の()には何も入れる必要はありません。

ユリウス日(1年の始まりからの経過日数)は、SASではyydddまたはyyyyddd形式の日付として定義されます。ここで、yyまたはyyyyは年を表す2桁または4桁の整数であり、dddは年の日数を表します。dddの値は001から365(うるう年の場合は366)の範囲でなければなりません。たとえば、2008年1月21日のSASユリウス日付は2008021です。

これらの5つの関数が使用される例を見てみましょう。

#

次のプログラムは、6つの日付変数を含む一時データセットcreatedatesを作成します。変数「current1」と「current2」は、date()およびtoday()関数を使用して現在の日付に割り当てられます。変数「current3」は、datejul()関数を使用して2008年の95日目に割り当てられます。変数「current4」と「current5」は、mdy()関数を使用して2008年4月4日に割り当てられます。そして、変数「current6」は、yyq()関数を使用して2008年4月1日に割り当てられます。

data createdates;
  current1= date();
  current2 = today();
  current3 = datejul(2008095);
  mon = 4; day = 4; year = 2008;
  current4 = mdy(mon, day, year);
  current5 = current4;
  current6 = yyq(2008, 2);
  format current1 current2 current3 current5 current6 date9.;
run;

title 'The createdates data set'; 
proc print data=createdates;    
  var current1 current2 current3 current4 current5 current6;
run;
SAS 出力

The createdates data set

OBS current1 current2 current3 current4 current5 current6
1 06JUN2024 06JUN2024 04APR2008 17626 04APR2008 01APR2008

まず、各関数の使用方法を理解するためにプログラムを確認してください。たとえば、2008年の95日目のSAS日付値を決定するために、2008095をdatejul()関数に入力する必要があります。代わりに200895をdatejul()関数に入力すると、SASはdatejul()関数に無効な引数を提供したことを報告し、そのため「current3」を欠損値に設定します。また、「current5」は「current4」がフォーマットされたであることにも注意してください。5つの関数の使用方法を理解したら、SASプログラムを開いて実行します。出力を確認し、データセットcreatedatesが説明どおりの6つの日付変数を含むことを確認します。

関数を使用して日付値を分解する#

日付値を分解するために使用できる関数には次のものがあります:

  • day(date) SAS日付値(date)から月の日を返します

  • month(date) SAS日付値(date)から月を返します

  • year(date) SAS日付値(date)から年を返します

dateは変数名またはSAS日付定数として指定できます。その他はそのままです!例を見てみましょう。

#

次のプログラムは、day()、month()、およびyear()関数を使用して変数「wt_date」から月、日、および年を抽出します:

data takeapart;
  input subj 1-4 l_name $ 18-23 weight 30-32
      +1 wt_date mmddyy8. @43 b_date mmddyy8.;
  wt_mo = month(wt_date);
  wt_day = day(wt_date);
  wt_yr = year(wt_date);
  format wt_date b_date date9.;
  datalines;
1024 Alice       Smith  1 65 125 12/1/05  01/01/60
1167 Maryann     White  1 68 140 12/01/05 01/01/59
1168 Thomas      Jones  2    190 12/2/05  06/15/60
1201 Benedictine Arnold 2 68 190 11/30/05 12/31/60
1302 Felicia     Ho     1 63 115 1/1/06   06/15/58
;
 
run;

title 'The dissected weight dates'; 
proc print data=takeapart;
  var wt_date wt_mo wt_day wt_yr;
run;
SAS 出力

The dissected weight dates

OBS wt_date wt_mo wt_day wt_yr
1 01DEC2005 12 1 2005
2 01DEC2005 12 1 2005
3 02DEC2005 12 2 2005
4 30NOV2005 11 30 2005
5 01JAN2006 1 1 2006

まず、3つの関数の使用方法を理解するためにプログラムを確認してください。次に、SASプログラムを開いて実行し、出力を確認し、プログラムが想定どおりに動作することを確認します。

関数を使用して日付値を調整する#

さて、これは興味深いタイトルのセクションです。日付値を調整するために使用できる関数には次のものがあります:

  • juldate(date) SAS日付値(date)からyyddd形式のユリウス日付を返します

  • juldate7(date) SAS日付値(date)からyyyyddd形式のユリウス日付を返します

  • qtr(date) SAS日付値(date)から四半期を返します(1=最初の3ヶ月、2=次の3ヶ月、3=次の3ヶ月、または4=最後の3ヶ月)

  • weekday(date) 日付値(date)から曜日の番号を返します(1=日曜日、2=月曜日、…、7=土曜日)

dateは変数名またはSAS日付定数として指定できます。また、SASでのユリウス日付は、yydddまたはyyyyddd形式の日付として定義されます。ここで、yyまたはyyyyは年を表す2桁または4桁の整数であり、dddは年の日数を表します(001から365(うるう年の場合は366)の範囲)。

これら4つの関数が使用される例を見てみましょう。

#

次のプログラムは、4つの割り当てステートメントにより変数「wt_date」を加工します。「wt_jul1」はyydddのユリウス日付、「wt_jul2」はyyyydddのユリウス日付、「wt_qtr」は「wt_date」の該当する四半期、「wt_day」は「wt_date」の曜日を表す整数を割り当てます。 T

data massaged;
  input subj 1-4 l_name $ 18-23 weight 30-32
      +1 wt_date mmddyy8. @43 b_date mmddyy8.;
  wt_jul1 = juldate(wt_date);
  wt_jul2 = juldate7(wt_date);
  wt_qtr = qtr(wt_date);
  wt_day = weekday(wt_date);
  format wt_date b_date date9.;
  datalines;
1024 Alice       Smith  1 65 125 12/1/05  01/01/60
1167 Maryann     White  1 68 140 12/01/05 01/01/59
1168 Thomas      Jones  2    190 12/2/05  06/15/60
1201 Benedictine Arnold 2 68 190 11/30/05 12/31/60
1302 Felicia     Ho     1 63 115 1/1/06   06/15/58
;
 
run;

title 'The massaged data set'; 
proc print data = massaged;
  var wt_date wt_jul1 wt_jul2 wt_qtr wt_day;
run;
SAS 出力

The massaged data set

OBS wt_date wt_jul1 wt_jul2 wt_qtr wt_day
1 01DEC2005 5335 2005335 4 5
2 01DEC2005 5335 2005335 4 5
3 02DEC2005 5336 2005336 4 6
4 30NOV2005 5334 2005334 4 4
5 01JAN2006 6001 2006001 1 1

最初にプログラムを確認し、各関数の使用方法を確認してください。次に、SASプログラムを開いて実行します。最後に、出力を確認からプログラムが想定どおりに動作することを確認します。特に注目すべきは、juldate7関数が4桁の年を含むジュリアン日付を返すことと、weekday関数が月曜日を2として返すことです。

関数を使用して間隔を計算する#

間隔を計算するために使用できる関数には次のものがあります:

  • yrdif(startdate, enddate, ‘method’) は2つのSAS日付値 (startdate, enddate)の年数の差を4つの方法のうちの1つ(’method’)により返します。

  • datdif(startdate, enddate, ‘method’) は2つのSAS日付値 (startdate, enddate)の日数の差を4つの方法のうちの1つ(’method’)により返します。

  • intck(’interval’, fromdate, todate) は2つの変数 (fromdate, todate)の間で発生した間隔(’interval’)の回数を返します。

  • intnx(’interval’, date, increment) は特定の日付(date) から指定の回数 (increment) の間隔 (’interval’) が経過した日付を返し、今後の日付だけでなく過去の日付を算出するのに使えます。

これから5つの例を見ていきますが、最初の例では yrdif関数 、datdif関数、次の3つでは intck関数 、最後の例ではintnx関数を使います。

#

次のプログラムは、yrdif関数により被験者の誕生日 (b_date)と最初の体重の測定日 (wt_date1)から被験者の年齢を算出します。またdatdif関数により被験者の初回と2回めの体重測定日 (wt_date1、wt_date2)の差を算出します。

data diet;
  input subj 1-4 l_name $ 18-23 weight 30-32
      +1 wt_date1 mmddyy8. @43 wt_date2 mmddyy8. @52 
      b_date mmddyy8.;
  age  = yrdif(b_date, wt_date1, 'act/act');
  days = datdif(wt_date1, wt_date2, 'act/act');
  format wt_date1 wt_date2 b_date date9.  age 4.1;
  datalines;
1024 Alice       Smith  1 65 125 12/1/05  03/04/06 01/01/60
1167 Maryann     White  1 68 140 12/01/05 03/07/06 01/01/59
1168 Thomas      Jones  2    190 12/2/05  3/30/06  06/15/60
1201 Benedictine Arnold 2 68 190 11/30/05 2/27/06  12/31/60
1302 Felicia     Ho     1 63 115 1/1/06   4/1/06   06/15/58
;
run;

title "The calculation of subject's age"; 
proc print data=diet;    
  var subj b_date wt_date1 age;
RUN;

title 'The calculation of days between weighings'; 
proc print data=diet;
  var subj wt_date1 wt_date2 days;
run;
SAS 出力

The calculation of subject's age

OBS subj b_date wt_date1 age
1 1024 01JAN1960 01DEC2005 45.9
2 1167 01JAN1959 01DEC2005 46.9
3 1168 15JUN1960 02DEC2005 45.5
4 1201 31DEC1960 30NOV2005 44.9
5 1302 15JUN1958 01JAN2006 47.5

The calculation of days between weighings

OBS subj wt_date1 wt_date2 days
1 1024 01DEC2005 04MAR2006 93
2 1167 01DEC2005 07MAR2006 96
3 1168 02DEC2005 30MAR2006 118
4 1201 30NOV2005 27FEB2006 89
5 1302 01JAN2006 01APR2006 90

変数「age」の値を計算するために使用される割り当てステートメントを確認します。yrdif関数の最初と2番目の引数は、それぞれ目的の期間の開始日と終了日をS指定します。ここでは、開始日は「b_date」で、終了日は「wt_date1」です。yrdif関数の3番目の引数は、単一引用符で囲む必要があり、差の計算方法を指定します。ここで、’act/act’はSASに対し、2つの日付間の実際の年数を使用して差を計算するよう指示しています。yrdif関数を使用して2つの日付間の年数を計算する4つの可能な方法は次のとおりです。

  • act/act’ 2つの日付間の実際の日数と年数を使用します

  • 30/360’ 1ヶ月を30日、1年を360日として年数を算出します

  • act/360’ 年数を計算する際に、2つの日付間の実際の日数を使用します(日数を360で割って計算)

  • act/365’ 年数を計算する際に、2つの日付間の実際の日数を使用します(日数を365で割って計算)

act/act’ は最も正確だと考えられている方式です。他の方式は、会計処理の際などに使用される方式です。

次に、変数「days」の値を計算するために使用される割り当てステートメントを確認します。datdif関数の最初と2番目の引数は、それぞれ目的の期間の開始日と終了日を指定します。ここでは、開始日は「wt_date1」で、終了日は「wt_date2」です。datdif関数の3番目の引数は、単一引用符で囲む必要があり、SASに差の計算方法を指示します。ここでは、’act/act’はSASに対し、2つの日付間の実際の日数を使用して差を計算するよう指示しています。datdif関数を使用して2つの日付間の日数を計算する2つの可能な方法は次のとおりです。

  • act/act’ 2つの日付間の実際の日数と年数を使用します

  • 30/360’ 30日の月と360日の年を指定します

同じく、’act/act’方式は最も正確だと考えられている方式です。他の方式は、会計処理の際などに使用される方式です。

2つの関数を理解できたことを確認したら、SASプログラムを開いて実行します。出力から「age」と「days」が実際に説明されたとおりに計算されていることを確認します。

#

intck()関数は、2つの日付間に発生する時間間隔の数、例えば日数や年数を返すことを思い出してください。次のプログラムは、前のプログラムと同じですが、ここでは被験者の最初の体重測定時の年齢をyrdif()およびintck()関数の両方を使って、それぞれ「age_yrdif」および「age_intchk」として算出しています。同様に、被験者の2回の体重測定間の日数は、datdif()およびintck()関数の両方を使って、それぞれ「days_datdif」および「days_intchk」として算出されています。

data diet;
  input subj 1-4 l_name $ 18-23 weight 30-32
      +1 wt_date1 mmddyy8. @43 wt_date2 mmddyy8. @52 
      b_date mmddyy8.;
  age_yrdif  = yrdif(b_date, wt_date1, 'act/act');
  age_intck = intck('year', b_date, wt_date1);
  days_datdif = datdif(wt_date1, wt_date2, 'act/act');
  days_intck = intck('day', wt_date1, wt_date2);
  format wt_date1 wt_date2 b_date date9.  age 4.1;
  datalines;
1024 Alice       Smith  1 65 125 12/1/05  03/04/06 01/01/60
1167 Maryann     White  1 68 140 12/01/05 03/07/06 01/01/59
1168 Thomas      Jones  2    190 12/2/05  3/30/06  06/15/60
1201 Benedictine Arnold 2 68 190 11/30/05 2/27/06  12/31/60
1302 Felicia     Ho     1 63 115 1/1/06   4/1/06   06/15/58
  ;
run;

title "The calculation of subject's age"; 
proc print data=diet;    
  var subj b_date wt_date1 age_yrdif age_intck;
run;

title 'The calculation of days between weighings';
proc print data=diet;    
  var subj wt_date1 wt_date2 days_datdif days_intck;
run;
SAS 出力

The calculation of subject's age

OBS subj b_date wt_date1 age_yrdif age_intck
1 1024 01JAN1960 01DEC2005 45.9151 45
2 1167 01JAN1959 01DEC2005 46.9151 46
3 1168 15JUN1960 02DEC2005 45.4643 45
4 1201 31DEC1960 30NOV2005 44.9151 45
5 1302 15JUN1958 01JAN2006 47.5479 48

The calculation of days between weighings

OBS subj wt_date1 wt_date2 days_datdif days_intck
1 1024 01DEC2005 04MAR2006 93 93
2 1167 01DEC2005 07MAR2006 96 96
3 1168 02DEC2005 30MAR2006 118 118
4 1201 30NOV2005 27FEB2006 89 89
5 1302 01JAN2006 01APR2006 90 90

変数「age_intck」の値を計算するために使用される割り当てステートメントを確認します。intck関数の最初の引数は、単一引用符で囲む必要があり、数えたい時間間隔を指定します。他の間隔も利用可能ですが、最も一般的に使用される間隔には’day’、’weekday’、’week’、’month’、’qtr’、および’year’があります。intck関数の2番目と3番目の引数は、それぞれ目的の期間の開始日と終了日を指定します。ここでは、開始日は「b_date」、終了日は「wt_date1」、時間間隔は’year’です。
次に、変数「days_intc」kの値を計算するために使用される割り当てステートメントを確認します。日数を計算する際、開始日は「wt_date1」、終了日は「wt_date2」、時間間隔は’day’です。
理論的には、「age」についてはyrdif関数とintck関数は同じ結果となり、「days」についてはdatdif関数とintck関数が同じ結果となるはずです。プログラムを開いてして実行し、結果の出力を確認してください。同じ答えでしたか?確かに…days_datdifとdays_intckの値は同じですが、(丸めた)age_yrdifとage_intckの値は異なることがわかるはずです。
なぜそうなるのでしょうか?これは、intck関数が固定された間隔の始まりから間隔をカウントし、startdate値から間隔ユニットの倍数でカウントしないことに関係しています。一部の間隔はカウントされません。例えば、間隔’week’はstartdateの値から7日の倍数ではなく、日曜日からカウントされます。間隔’month’は各月の1日からカウントされ、間隔’year’はstartdateの値から365日の倍数ではなく、1月1日からカウントされます。
次の2つの例は、intck()関数が間隔をどのようにカウントするかを理解するのに役立つはずです。

#

次のプログラムは、intck()関数とSASの日付値を使用して、2006年12月31日から2007年1月1日までの日数、週数、月数、年数を求めます。また、2007年1月1日から2007年12月31日までの年数「years2」と、2007年1月1日から2008年1月1日までの年数「years3」も算出します:

data timeintervals1;
  days = intck('day', '31dec2006'd,'01jan2007'd);
  weeks = intck('week', '31dec2006'd,'01jan2007'd);
  months = intck('month', '31dec2006'd,'01jan2007'd);
  years = intck('year', '31dec2006'd,'01jan2007'd);
  years2 = intck('year', '01jan2007'd, '31dec2007'd);
  years3 = intck('year', '01jan2007'd, '01jan2008'd);
run;

title 'Time intervals as calculated by intck function'; 
proc print data = timeintervals1; 
run;
SAS 出力

Time intervals as calculated by intck function

OBS days weeks months years years2 years3
1 1 0 1 1 0 1

まず、プログラムを確認して何をしているのかを理解するようにしてください。次に、SASプログラムを開いて実行し、出力を確認してください。結果に驚きますか?「days」、「weeks」、および「years3」の結果が納得できるものであり、「months」、「years」、および「years2」の結果が少し奇妙であると感じたのではないでしょうか。3つの奇妙な結果に焦点を当てましょう。2006年12月31日から2007年1月1日までの間にわずか1日しか経過していないにもかかわらず、SASは変数「months」に1を割り当てます。なぜなら、2006年12月31日から2007年1月1日までの間に、ちょうど1つの月の初日が交差するからです(これは1月1日です)。同様に、2006年12月31日から2007年1月1日までの間にわずか1日しか経過していないにもかかわらず、SASは変数「years」に1を割り当てます。なぜなら、2006年12月31日から2007年1月1日までの間に、ちょうど1つの1月1日が交差するからです。そして、2007年1月1日から2007年12月31日までの間に364日が経過しているにもかかわらず、SASは変数「years2」に0を割り当てます。なぜなら、1月1日が交差しないからです。「days」、「weeks」、および「years3」の結果は直感的に理解できるものだったかもしれませんが、intck関数の動作に基づいてSASがここでどのように値を割り当てるか理解しておくといいでしょう。

#

intck()関数をさらに詳しく見るために、次のプログラムは、2007年3月15日から2008年3月15日までの日数、週数、平日数、月数、四半期数、および年数を求めます:

data timeintervals2;
  days = intck('day', '15mar2007'd,'15mar2008'd);
  weeks = intck('week', '15mar2007'd,'15mar2008'd);
  weekdays = intck('weekday', '15mar2007'd,'15mar2008'd);
  months = intck('month', '15mar2007'd,'15mar2008'd);
  qtrs = intck('qtr', '15mar2007'd,'15mar2008'd);
  years = intck('year', '15mar2007'd,'15mar2008'd);
run;

title 'Time intervals as calculated by intck function';        
proc print data = timeintervals2;    
run;
SAS 出力

Time intervals as calculated by intck function

OBS days weeks weekdays months qtrs years
1 366 52 261 12 4 1

このプログラムの主な目的は、intck関数で最も一般的に使用されるいくつかの間隔を示すことです。まず、何をしているのかを理解するためにプログラムを確認してください。次に、SASプログラムを開いて実行し、出力を確認してください。結果に驚きますか?いずれの場合も、結果が直感的に理解できるものであると感じるはずです。

#

ここでは、データセットdietに登場する各被験者が3ヶ月後に再度体重を測定する必要があると仮定します。次のプログラムは、被験者の前回の体重測定日「wt_date」)とintnx関数のさまざまな指定により、各被験者の次の体重測定日を算出します:

data diet;
  input subj 1-4 l_name $ 18-23 weight 30-32
      +1 wt_date mmddyy8. @43 b_date mmddyy8.;
  nxdate_b1 = intnx('month', wt_date, 3);
  nxdate_b2 =  intnx('month', wt_date, 3, 'beginning');
  nxdate_m = intnx('month', wt_date, 3, 'middle');
  nxdate_e = intnx('month', wt_date, 3, 'end');
  nxdate_s = intnx('month', wt_date, 3, 'sameday');
  format wt_date b_date nxdate_b1 nxdate_b2 
       nxdate_m nxdate_e nxdate_s date9.;
  datalines;
1024 Alice       Smith  1 65 125 12/1/05  01/01/60
1167 Maryann     White  1 68 140 12/01/05 01/01/59
1168 Thomas      Jones  2    190 12/2/05  06/15/60
1201 Benedictine Arnold 2 68 190 11/30/05 12/31/60
1302 Felicia     Ho     1 63 115 1/1/06   06/15/58
;
run;

title 'The data set containing next weight dates'; 
proc print data=diet;
  var subj wt_date nxdate_b1 nxdate_b2 
      nxdate_m nxdate_e nxdate_s;
run;
SAS 出力

The data set containing next weight dates

OBS subj wt_date nxdate_b1 nxdate_b2 nxdate_m nxdate_e nxdate_s
1 1024 01DEC2005 01MAR2006 01MAR2006 16MAR2006 31MAR2006 01MAR2006
2 1167 01DEC2005 01MAR2006 01MAR2006 16MAR2006 31MAR2006 01MAR2006
3 1168 02DEC2005 01MAR2006 01MAR2006 16MAR2006 31MAR2006 02MAR2006
4 1201 30NOV2005 01FEB2006 01FEB2006 14FEB2006 28FEB2006 28FEB2006
5 1302 01JAN2006 01APR2006 01APR2006 15APR2006 30APR2006 01APR2006

5つの割り当てステートメントを確認して、被験者の次の体重測定日を計算する5つの方法を見てみましょう。ご覧のように、SASは各計算で間隔’month’を使用するように指示されています。他にも使用可能な間隔がありますが、最も一般的に使用される間隔には、’day’、’weekday’、’week’、’month’、’qtr’、および’year’があります。また、SASは各計算で「wt_date」を開始日として使用するように指示されています。そして、各場合で「wt_date」を3ヶ月進めるように指示されています。さて、5つの計算の間で異なるのは、最後のオプションの引数(’beginning’、’middle’、’end’、および’sameday’)だけです。これらのいわゆるアラインメント引数は、結果の月の初日、中日、または最終日を返すようにSASに指示します。アラインメント引数が指定されていない場合、デフォルトで’beginning’が使用されます。’sameday’アラインメントが指定されている場合はもちろん、指定された間隔の数だけシフトした同じ日を返します。
プログラムを開いて実行し、出力を確認しましょう。日付は説明通りであるはずです。「nxdate_b1」の内容は、デフォルトでは初日が返されるため、「nxdate_b2」と同じです。変数「nxdate_m」には結果の月の中間日が含まれ、「nxdate_e」には結果の月の最終日が含まれます。そして、4人の被験者に対して、SASは同じ日付を3ヶ月後に返します。被験者(#1201)に対して、11月30日から正確に3ヶ月後の2月30日を返すことを期待するかもしれませんが、この例は、intnx関数が結果の日付が存在しない場合に日付を自動的に調整する方法を示しています。

SASの日付システムオプション#

SASが日付を扱う方法に影響を与える2つのシステムオプションがあります。DATESTYLE=オプションとYEARCUTOFF=オプションです。

DATESTYLE=システムオプションは、日付があいまいな場合の月(M)、日(D)、年(Y)の意図される順序をSASに指示します。可能な設定にはMDY、MYD、YMD、YDM、DMY、DYMおよびLOCALEがあります。デフォルトでは、DATESTYLEシステムオプションはLOCALEに設定されており、これはSASにLOCALEシステムオプションで指定された地理的地域の言語と地域の慣習を反映した日付の形式を使用するよう指示します。これは循環しているようですね!LOCALEがデフォルトで米国のユーザーに対してENGLISHに設定されているため、デフォルトのDATESTYLEオプションはMDYになります。DATESTYLEシステムオプションについてはこれ以上時間を費やしませんが、anydtdte.インフォーマットを使用したいということがあれば、知っておく必要があるでしょう。(anydtdte.インフォーマットは、同じ日付の異なる形式を1つの日付変数に読み込むことができるため使用したくなりますが、私はSASにデータについての判断をさせたくないので、今回は説明しません!)

SASは、2桁の年を扱う方法をユーザーに提供するためにYEARCUTOFF=システムオプションを開発しました。日付定数’13apr08’d’を指定した場合、2008年、1908年、さらには1808年のいずれを意味するのかあいまいです。YEARCUTOFF=システムオプションは、SASが2桁の年に遭遇したときにSASが使用する100年間の最初の年をSASに指示することで、この曖昧さを解消します。YEARCUTOFFのデフォルト値は1920です。デフォルトの場合、プログラムで20から99の2桁の年に遭遇すると、SASはその日付に19の接頭辞があると見なします。そして、プログラムで00から19の2桁の年に遭遇すると、SASはその日付に20の接頭辞があると見なします。2桁の日付の扱い方が気に入らない場合、2つのことができます。4桁を使うか、OPTIONSステートメントを使ってYEARCUTOFF=オプションを変更します。次の2つの例を見て、SASが2桁の年をどのように扱うかを確認しましょう。

#

次のプログラムは、デフォルトのYEARCUTOFF=1920を使用して、20から99、そして00から19の範囲の2桁の年を含む9つの日付を読み込みます。

options yearcutoff=1920;
 
data twodigits1920;
  input date1 mmddyy8.;
  format date1 worddatx20.;
  datalines;
01/03/20
01/03/21
01/03/49
01/03/50
01/03/51
01/03/99
01/03/00
01/03/01
01/03/19
    ;
run;

title 'Years with two-digits when YEARCUTOFF = 1920'; 
proc print data=twodigits1920;
run;
SAS 出力

Years with two-digits when YEARCUTOFF = 1920

OBS date1
1 3 January 1920
2 3 January 1921
3 3 January 1949
4 3 January 1950
5 3 January 1951
6 3 January 1999
7 3 January 2000
8 3 January 2001
9 3 January 2019

まず、DATALINESステートメントの日付を確認し、データセットtwodigits1920に読み込もうとしている2桁の年の範囲を確認します。次に、SASプログラムを開いて実行し、結果の出力を確認します。20から99の間の2桁の年を含む日付は、1920から1999の間の4桁の年として表示されることに注意してください。また、00から19の間の2桁の年を含む日付は、2000から2019の間の4桁の年として表示されます。

#

次のプログラムは、前のプログラムと同じですが、YEARCUTOFF=システムオプションが1950に変更されています。前と同様に、20から99、そして00から19の範囲の2桁の年を含む9つの日付を読み込みます:

options yearcutoff=1950;
 
data twodigits1950;
  input date1 mmddyy8.;
  format date1 worddatx20.;
  datalines;
01/03/20
01/03/21
01/03/49
01/03/50
01/03/51
01/03/99
01/03/00
01/03/01
01/03/19
    ;
run;

title 'Years with two-digits when YEARCUTOFF = 1950';
proc print data=twodigits1950;
run;
SAS 出力

Years with two-digits when YEARCUTOFF = 1950

OBS date1
1 3 January 2020
2 3 January 2021
3 3 January 2049
4 3 January 1950
5 3 January 1951
6 3 January 1999
7 3 January 2000
8 3 January 2001
9 3 January 2019

再度、DATALINESステートメントの日付を確認し、データセットtwodigits1950に読み込もうとしている2桁の年の範囲を理解していることを確認します。次に、SASプログラムを開いて実行し、結果の出力を確認します。今度は、50から99の間の2桁の年を含む日付が、1950から1999の間の4桁の年として表示されることに注意してください。また、00から49の間の2桁の年を含む日付は、2000から2049の間の4桁の年として表示されます。

SAS時間値の基本#

時間値の取り扱いについては、日付値の取り扱いほど詳細には学びませんが、基本は理解しておくといいでしょう。このセクションでは、SASで時間を扱うための基本事項を簡単に幅広く見ていきます。SASが時間と日時の値をどのように定義しているか、時間をSASデータセットに読み込むために使用するインフォーマット、SAS時間値を表示するために使用するフォーマット、最も一般的な時間関数の使用方法、およびSAS時間定数の定義方法を学びます。

SAS時間値および日時の定義#

SASは時間の値を日付の値と同様に保存します。具体的には、SASは時間を真夜中からの秒数として数値で保存します。たとえば、SASは次のように保存します:

  • 午前0時1分0秒は60、これは真夜中から60秒後

  • 午前0時1分35秒は95、これは真夜中から95秒後

  • 午前0時2分0秒は120、これは真夜中から120秒後

  • 以降も同様

1日に86,400秒があるため、SAS時間の値は0から86,400の間の値を取ります。どのように時間を読み込んでも、SASは定義に沿ってその時間を数値に変換します。

SAS 日時値は日付と時間の両方の値を組み合わせた特別な値です。SAS日時の値は、1960年1月1日午前0時から指定された日時までの秒数として保存されます。計算の詳細は避けますが、SASマニュアルによると、例えば1989年4月22日午後4時10分45秒のSAS日時は92,488,384秒に相当するとのことです。秒単位の精度が必要な場合は日時が便利ですが、私自身は使ったことがありません。

インフォーマットとフォーマットを使用してSAS時間を読み込み表示する#

日付の形式をSASに伝える必要があるのと同様に、時間の形式も伝える必要があります。予想通り、時間のインフォーマットをINPUTステートメントで使用して、読み込む時間の形式をSASに伝えます。そして、時間のフォーマットをFORMATステートメントで使用して、SAS時間値を表示する形式を伝えます。

#

次のプログラムは、5つのオブザベーションをデータセットdietに読み込みます。そのうちの1つの変数である体重の測定時間(wt_time)はhh:mm:ss形式であり、time8.インフォーマットを使用して時間を読み込みます:

data diet;
  input subj 1-4 l_name $ 18-23 weight 30-32
      +1 wt_date mmddyy8. @43 b_date mmddyy8.
      @52 wt_time time8.;
  wtm_fmt1 = wt_time;
  wtm_fmt2 = wt_time;
  wtm_fmt3 = wt_time;
  format wtm_fmt1 hhmm.
         wtm_fmt2 hour5.2
         wtm_fmt3 time8.;
  datalines;
1024 Alice       Smith  1 65 125 12/1/05  01/01/60 00:01:00
1167 Maryann     White  1 68 140 12/01/05 01/01/59 00:15:00
1168 Thomas      Jones  2    190 12/2/05  06/15/60 12:00:00
1201 Benedictine Arnold 2 68 190 11/30/05 12/31/60 00:00:00
1302 Felicia     Ho     1 63 115 1/1/06   06/15/58 23:59:59
;
run;

title 'The diet data set with formatted weight times';    
proc print data=diet;    
  var subj wt_time wtm_fmt1 wtm_fmt2 wtm_fmt3;
run;
SAS 出力

The diet data set with formatted weight times

OBS subj wt_time wtm_fmt1 wtm_fmt2 wtm_fmt3
1 1024 60 0:01 0.02 0:01:00
2 1167 900 0:15 0.25 0:15:00
3 1168 43200 12:00 12.00 12:00:00
4 1201 0 0:00 0.00 0:00:00
5 1302 86399 24:00 24.00 23:59:59

このプログラムでは、変数「wt_time」を読み込むために使用されるインフォーマットtime8.に注目してください。また、変数「wt_time」の値を持つ3つの新しい体重時間変数(wtm_fmt1、wtm_fmt2、wtm_fmt3)が割り当てられていますが、これらの新しい変数はそれぞれ異なる形式のフォーマットが指定されています。FORMATステートメントは、「wtm_fmt1」をhhmm.、「wtm_fmt2」をhour5.2、「wtm_fmt3」をtime8.形式でフォーマットするように指示しています。

プログラムを開いて実行し、データセットdietの内容を確認してください。特に、フォーマットされていない変数「wt_time」に保存されている数値に注目してください。予想通り、00:01:00の時間は60として保存され、00:00:00の時間は0として保存され、00:15:00の時間は900として保存されます。その後、体重の測定時間変数のフォーマット済みバージョンに注目してください。hhmm.フォーマットは24時間制で時間を表示します。hour5.2フォーマットは時間を時間と時間の小数部分として表示します。そして、time8.フォーマットは時間をhh:mm:ss形式で表示します。

SAS時間関数の使用#

SAS日付と同様に、SAS時間の最も良い点は、SAS時間の値が数値であるため、簡単にソート、減算、および加算できることです。時間を比較することもできます。また、利用可能な時間関数のいずれかを使用することもできます。最も一般的に使用される時間関数は次のとおりです:

  • time( ) 現在の時間をSAS時間値として返します

  • hms(h, m, s) 指定された時間(h)、分(m)、および秒(s)からSAS時間値を返します

  • hour(time) SAS時間値(time)の時間部分を返します

  • minute(time) SAS時間値(time)の分部分を返します

  • second(time) SAS時間値(time)の秒部分を返します

ここでは例を示しませんが、日付に対して探求したintnx( )およびintck( )の間隔関数は、時間値に対しても使用できます。

#

次のプログラムは、上記の5つの時間関数の使用方法を示しています。具体的には、変数「curtime」にtime関数を使用して現在の時間を割り当てています。その後、hour、minute、およびsecond関数を使用して、変数「wt_time」から時間、分、および秒を抽出しています。最後に、hms関数を使用して、時間、分、および秒を再度組み合わせて、元の変数「wt_time」と同じ値を持つ新しい変数「wt_time2」を作成しています:

data diet;
  input subj 1-4 l_name $ 18-23 weight 30-32
      +1 wt_date mmddyy8. @43 b_date mmddyy8.
      @52 wt_time time8.;
  curtime = time();
  wt_hr = hour(wt_time);
  wt_min = minute(wt_time);
  wt_sec = second(wt_time);
  wt_time2 = hms(wt_hr, wt_min, wt_sec);
  format curtime wt_time wt_time2 time8.;
  datalines;
1024 Alice       Smith  1 65 125 12/1/05  01/01/60 00:01:00
1167 Maryann     White  1 68 140 12/01/05 01/01/59 00:15:00
1168 Thomas      Jones  2    190 12/2/05  06/15/60 12:00:00
1201 Benedictine Arnold 2 68 190 11/30/05 12/31/60 00:00:00
1302 Felicia     Ho     1 63 115 1/1/06   06/15/58 23:59:59
;
run;

title 'The diet data set with five new variables'; 
proc print data=diet;   
  var subj curtime wt_time wt_hr wt_min wt_sec wt_time2;
run;
SAS 出力

The diet data set with five new variables

OBS subj curtime wt_time wt_hr wt_min wt_sec wt_time2
1 1024 7:38:58 0:01:00 0 1 0 0:01:00
2 1167 7:38:58 0:15:00 0 15 0 0:15:00
3 1168 7:38:58 12:00:00 12 0 0 12:00:00
4 1201 7:38:58 0:00:00 0 0 0 0:00:00
5 1302 7:38:58 23:59:59 23 59 59 23:59:59

まず、プログラムを確認して、各関数の使用方法を理解しておいてください。次に、プログラムを開いて実行し、プログラムが想定の通りに動作することを確認してください。

時間値の比較#

繰り返しになりますが、SAS時間値は数値であるため、簡単に2つ以上の時間を比較できます。比較は、任意の2つの数値の比較と同じように行われます。たとえば、時間00:10:00はSASでは600として保存されるため、時間00:15:00(SASでは900として保存)よりも小さいと見なされます。

#

次のプログラムは、時間変数の値を他の時間変数の値ではなく、時間定数と比較する方法を示しています。具体的には、DATAステートメントのWHERE=オプションにより、体重の測定時間が真夜中から正午までの個人をデータセットdietに出力するように指示しています:

data diet (where = ((wt_time >= '00:00:00't) 
  and (wt_time <= '12:00:00't)));;
  input subj 1-4 l_name $ 18-23 weight 30-32
      +1 wt_date mmddyy8. @43 b_date mmddyy8.
      @52 wt_time time8.;
  time_int = abs((wt_time - '05:00:00't)/3600);
  format wt_time time8. time_int 4.1;
datalines;
1024 Alice       Smith  1 65 125 12/1/05  01/01/60 00:01:00
1167 Maryann     White  1 68 140 12/01/05 01/01/59 00:15:00
1168 Thomas      Jones  2    190 12/2/05  06/15/60 12:00:00
1201 Benedictine Arnold 2 68 190 11/30/05 12/31/60 00:00:00
1302 Felicia     Ho     1 63 115 1/1/06   06/15/58 23:59:59
; 
run;

title 'The subsetted diet data set'; 
proc print data=diet;
  var subj l_name wt_time time_int;
run
SAS 出力

The subsetted diet data set

OBS subj l_name wt_time time_int
1 1024 Smith 0:01:00 5.0
2 1167 White 0:15:00 4.8
3 1168 Jones 12:00:00 7.0
4 1201 Arnold 0:00:00 5.0

まず、プログラムを確認して、何をしているかを理解しておいてください。例えば、WHERE=オプションで使用されているSAS時間定数:
‘00:00:00’t
および ‘12:00:00’t

の形式に注目してください。一般にSASの時間定数は’hh:mm:ss’tの形式で、 hh は 24時間制の時間, mm は分, and ss (オプション) は秒です。その後の文字 t は時間定数であることを示します。
またこのプログラムはSAS時間値を容易に計算で用いることができることを示しています。
変数「time_int」は期待される体重の測定時間(例では5.am)の定数と体重の測定時間ら絶対値を計算し、単位を時間にしています。
それでは、プログラムを開いて実行し、深夜から正午の間の体重の測定時間だけがデータセットdietに出力されていることを確認してください。

演習#

ファイルBike_Lanes.csvのデータを使用するので、READMEを参照してファイルを入手しておいてください。

  1. Freqプロシージャを使用して、bike lane 種類別の表を作成してください。

  2. bike lane について”SIDEPATH”, “BIKE BOULEVARD” と “BIKE LANE”はそのまま、その他は “ “とするフォーマットを作成し、Freqプロシージャでフォーマットを使用してbike lane種類別の表を作成してください。表はどのようになりましたか?

  3. bike lane について”CONTRAFLOW”, “SHARED BUS BIKE”, “SHARROW”, “SIGNED ROUTE”はそのまま、その他は “OTHER”とするフォーマットを作成し、Freqプロシージャでフォーマットを使用してbike lane種類別の表を作成してください。

  4. 以下の日付3つと時間を含むデータセットを読み込むようなデータステップのコードを作成してください。その後データセットを読めるような日時フォーマットを使用して結果に出力し、正しく読み込めたことを確認してください(SASドキュメントの日時フォーマット・インフォーマットのページを必要に応じ参照してください)。

data temp;
  input date1 /*informat1*/ +1 date2 /*informat2*/ +1
        date3 /*informat3*/ @32 time /*informat4*/;
  datalines;
2014/02/14 06Jan2018 4/5/2016   03:2:22
;
run;