2016年3月2日水曜日

テキストファイル(CSVなど)の読み込みと注意点 【IMPORTプロシジャ編】



  • 可変長テキストファイルの読み込みに限定した説明になります。
他の形式で今回のプログラムを流用すると、重要なオプションが効かず、文字切れや変数属性の設定がうまくいかない等の可能性あり。

  • とにかく、想定外の結果になりやすいので、出来たデータセットは要確認です。
例えば、聞いた話ですが、テキストファイルに「クォーテーション」「改行コード」「見えない改行コード」が含まれている際に、正しく読み込めない事があったようです。




構文

 PROC IMPORT
   OUT                         =   出力データセット
   DATAFILE                =   "読込テキストファイル"   /* パスが長いと実行失敗します */
   DBMS                      =    CSV | TAB | DLM             /* ファイル種類 */
   REPLACE                     /* 既存のデータセットを置き換える */
   ;
   DELIMITER             =   "区切り文字";    /* DBMS=DLMの場合に指定 */
   GETNAMES            =   YES | NO ;        /* 1行目を変数名として読み込むか */
   DATAROW              =   行番号 ;             /* 何行目から読み込むか */
   GUESSINGROWS  =   行番号 | MAX;   /* 何行目までのデータで変数属性を判定するか */
 RUN ;


DBMS=オプション
  指定  設定内容
  CSV    カンマ区切り 
  TAB    タブ区切り
  DLM   自分で 「DELIMITER=オプション」 に区切り文字を指定 



📚 注意点


① GUESSINGROWS=オプション ※重要!

SASはテキストファイルの先頭20行を読み込んで各変数の属性(データ型やlength)を決める。

  • 20行目まで数値で、21行目に文字が入ってきた場合
        ⇒ 数値型になるので、文字は読み込めず欠損値になる。

  • 20行目まで文字の最大長さが100バイトで、21行目に200バイトの文字が入ってきた場合
        ⇒length=$100となるので、101バイト以降の文字は切り捨てられる。


てことで、変数属性を判定する行数をGUESSINGROWSで設定しなおします。
ちなみに、SAS9.3 から 「GUESSINGROWS = MAX」 という指定が可能(MAX = SASが読込める最大行数)



② 制限(環境・バージョンによって異なる可能性あり)

 項目 内容
 SASが読込める最大行数   32767行【SAS9.2】、2147483647行【SAS9.3, 9.4】 
 1行につき読込めるバイト数   区切り文字も含め32767バイト



③ REPLACEオプション

世代管理されたデータセット(GENMAX=オプション)をREPLACEすると、そのデータセットの既存の世代データがすべて削除されるようなので注意。



④ GETNAMES=オプション

「YES」で1行目を変数名として読み込んだ場合、変数名として成立しない空白・特殊文字などが含まれていると、SASが変数名として成立する名前に変換してくれます。ただ、システムオプション「VALIDVARNAME」の設定によって、変換される変数名が変わるので注意。



最後に、気づいた範囲で「うまく読み込めない例」を紹介。



うまく読み込めない例(環境・バージョンで動作が異なるかも)


C:\test\test.txt
X,Y,Z
001, pen,2016/01/01
002,apple,2016/01/02


/* PROC IMPORT */
proc import out=OUT1
    datafile="C:\test\test.txt"
    dbms=dlm  replace
    ;
    delimiter=",";
    getnames=yes;
    datarow=2;
    guessingrows=max;
run;


/* 出力データの変数属性を確認 */
proc contents data=OUT1;
run;


① 変数X: 0落ち問題

数値変数として読み込まれて、しかも結果が読み込み元の値と異なっている(先頭の0が取れた: 001 → 1 )
読み込み元の値をダブルクォーテーションで囲ったら0落ちしませんでしたが( "001" → 001 )、データステップによるINFILE/INPUTでの読み込みに切り替えた方が良いかもしれませんね。



② 変数Y: 先頭の半角スペース落ち問題

読み込み元の値は「 pen」と先頭に半角スペースを含んでいますが、結果は先頭の半角スペースが取れた。
PROC IMPORTに限らず、SASの色々な機能で先頭の半角スペース取れがち。



③ 変数Y: FORMATとINFORMATが付与される問題

変数属性を見ると、FORMAT(出力形式)とINFORMAT(入力形式)が付与されています。
これは以下のような場合に、勘違いを起こしやすいです。

data OUT2;
    length Y $20.;
    set OUT1;
    Y = "aaaaaaab";
run;

SET前のLENGTHステートメントでYの長さを増やしてから、Y = "aaaaaaab" としたのに、結果は「aaaaaaa」となった(末尾の "b" が落ちた?)
実はちゃんと "aaaaaaab" が格納されていますが、FORMAT「$7」が付与されているため、見た目7バイト分しか見えていないだけ(この勘違い、よく聞きます)


FORMAT/INFORMATは以下で外せます(外す処理はSET後に記述しないと動作しないので注意)

data OUT3;
    length Y $20.;
    set OUT1;
    format Y;
    informat Y;
    Y = "aaaaaaab";
run;

全変数いっぺんにFORMATを外したいところですが、変数ZはFORMAT「YYMMDD10」が付与されているので、外したくないものを避ける必要あり。


5 件のコメント:

  1. こんにちは。以前もご質問させていただきました。

    importプロシジャでCSVファイルの読み込みをしたいのですが,
    変数の数を指定する方法はありますでしょうか。
    (66カラム目までを1行としたいのですが,実際は17行目から次の行へ移っています。)

    宜しくお願い致します。

    返信削除
    返信
    1. こんにちは、コメント有難うございます。

      importプロシジャでは変数の数を指定する等細かい設定は出来ないと思います。
      細かい設定をして読み込みたい場合は、データステップのinfile・inputを使うことになるかと思います。


      >(66カラム目までを1行としたいのですが,実際は17行目から次の行へ移っています。)

      実際のデータを見ないと原因の特定は難しいですが、大体以下が原因な事が多いです。
      なのでまずはこれらを確認してみてください。

      ・データの中に見えない改行など、おかしなものがないか。
      ・記事中で紹介しているオプションで必要なものを全て指定しているか。

      削除
  2. いつも大変勉強になる記事をありがとうございます。

    2016年の記事ではありますが
    proc importについてよく分からない動作があったため、質問させてください。


    SAS9.3上でproc importを使ってcsvファイルを読み込む際に、
    読み込み先の対象Folder内にある全てのcsvファイルを読み込むプログラムを組みました。

    data _null_;
    set WORK._CSV_Filename;
    num=put(_n_,z2.);
    call execute("PROC IMPORT");
    call execute(" OUT=WORK._CSV"||strip(num));
    call execute(" DATAFILE='&folder.\"||strip(filename));
    call execute(" DBMS=CSV REPLACE;");
    call execute(" GETNAMES=YES;");
    call execute(" DATAROW=2;");
    call execute(" GUESSINGROWS = MAX;");
    call execute("RUN;");
    run;

    「WORK._CSV_Filename」はfilename変数に対象Folder内のファイルネーム(○○.csv)がレコード毎に格納されており、
    &folder.には対象FolderのPassが登録されています。

    問題としては、上記プログラムが問題なく流れてしまったことです。

    状況としては、
    call execute内でproc importを流すために全体を""もしくは''で囲いますが、
    DATAFILE=のPass指定で''を使用したかったので、全体を""で囲っていました。
    最後にDATAFILE=で''を使おうと思ったのですが、
    マクロを使用するため、''を使うとマクロがマスクされてしまい流れないと考え、悩んでいたところ、間違えて上記プログラムの状態('が先頭にしか付いていない状態)でプログラムを実行してしまいました。

    すると、上記プログラムは問題なく流れてしまい、
    WORK上には想定通りのDatasetが出来あがり(_csvの後ろに数字が付いたDataset)、
    Logには「DATAFILE='フルパス'」と後ろの'が補われた状態の結果が出てきてびっくりしました。(Error/Warningなし)

    上記動作が通常動作なのかSASの自動補助機能なのか、不具合で起きた動作なのかわからず、ここに質問させていただきました。

    ちなみに、(当たり前ではありますが)call execute外で上記を行うと、先頭'以降から次に発生する'まで全て文字値と判断され、proc importは動きません。call execute内で""で囲っているために起きた現象となります。

    少しでも分かることがあればご教授いただければ幸いです。
    よろしくお願い致します。

    返信削除
    返信
    1. こんにちは、コメント有難うございます!

      これ、知らなかったのですが、proc importではなく、call executeが引き起こしている事のようです。
      例えば以下のように「a="abcd」として締めのダブルクォーテーションを書かないと、、

      data _null_;
      call execute( 'data test; a="abcd' );
      call execute( ';run;' );
      run;


      以下のように勝手に締めのダブルクォーテーションが補完されて実行されました。

      data test; a="abcd"
      ;run;

      どうやら、勝手にクォーテーションを補完するのは、call executeの性質のようです。
      リファレンスを読んでもこの事に言及されていなかったので、バグなのかなんなのか不明ですね。。

      言及されてない以上は、バグだと思っていた方がいいかもしれません。

      削除
    2. >matsu a さん

      早急なご回答ありがとうございます!

      Call executeの動作でしたか。。。
      通常動作ではない以上、実装はしないようが良さそうですね。

      ご丁寧な対応ありがとうございました。

      削除