ラベル [▼ EXCEL の投稿を表示しています。 すべての投稿を表示
ラベル [▼ EXCEL の投稿を表示しています。 すべての投稿を表示

2019年1月25日金曜日

ODS EXCELで「セル内改行」されてしまうところを「セル内折り返し」に変える




前回の記事「ODS EXCELで文字変数を「文字列」として出力したい。」に続き、もう1個「ODS EXCEL」で知っといた方がいいオプションを紹介します(※ SAS9.4M5からの機能です)


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

ods excel file="出力するパスを指定\test1.xlsx";
proc print data=sashelp.cars (obs=5);
  var model / style={cellwidth=90};
run;
ods excel close;


変数MODELについて、幅を縮めてEXCELに出力したところ、幅に合わせてセル内改行されちゃいました。


そこで以下のオプションを追加すると、、

ods excel file="出力するパスを指定\test2.xlsx" options(flow="table");
proc print data=sashelp.cars (obs=5);
  var model / style={cellwidth=90};
run;
ods excel close;


ちゃんと1行で入って、セル内折り返しに切り替わりました!


幅の関係でセル内改行が入ってしまうところを、セル内折り返しにしてくれるオプションです。
(変数値自体に改行が入ってる場合、改行は残ります)



2019年1月24日木曜日

ODS EXCELで文字変数を「文字列」として出力したい。




※今回の内容は、SASやEXCELのバージョン・環境によってうまくいくか未検証なので各時の環境でご確認下さい



「ODS EXCEL」でプロシジャの結果をEXCELに出力できますが、文字変数の書式が崩れるのが問題なんですよね。
例を見ていきます。

* Sample data ;
data test;
input x$;
cards;
001
002
003
.
;



以下は失敗例。

* NG  ;
ods excel file="出力するパスを指定\test1.xlsx" options( flow="tables" );
proc report data=test;
   define x / display;
run;
ods excel close;


*--- test1.xlsx ------------------------------------;


EXCELに出力すると、文字変数の書式が「標準」になってしまう上に、
変数Aは「001」→「1」みたいに数値に変換されちゃいます。



色々調べたところ以下SAS社の出してるProblem Noteにどんぴしゃな説明がありました。
以下は、データの中身(列ヘッダー以外)のセルの書式を「文字列」に設定しています。

* OK ;
ods excel file="出力するパスを指定\test2.xlsx" options( flow="tables" );
proc report data=test;
   define x / display style(column)={tagattr='type:String format:@'};
run;
ods excel close;


*--- test2.xlsx ------------------------------------;
※ A5セルは書式設定が適用できない点だけ注意

  • 「type:String」を入れると「001」のまま出力してくれました、、けどセルの書式が「文字列」になってくれません。実際の見た目とセルの書式が異なってしまい変な感じです。
  • そこで「format:@」を追記すると、「文字列」になってくれました。
  • ちなみに以下の通り、値がないセルは「文字列」になってくれませんでした。



あと、この「type:String」「format:@」の指定の順番を逆にすると環境によっては書式が「文字列」になってくれませんでした。各環境でちゃんと動くか確認が必要です。。



2015年2月9日月曜日

EXCELを読み込む方法と落とし穴 【LIBNAME EXCEL編】



Windows環境での機能で、SAS/ACCESSのライセンスも必要ですが、LIBNAMEでEXCELを読み込めます。
えぐい落とし穴もあるので最後までご覧ください。


以下のEXCELファイルを読み込みたいとします。

C:\test.xlsx
















データ部分のセル書式は「A列: 標準」「B列: 標準」「C列: 日付」「D列: 時刻」としています。







*** 正しく読み込めないので注意 ******;
* libnameで、EXCELファイルを参照する ;
libname TEST excel "C:\test.xlsx"
                header = yes
                dbmax_text = 8000
                scan_timetype = yes
                mixed = yes ;

* SET ;
data OUT1;
   set TEST."Sheet1$"n;
run;

* EXCELファイルを解放 ;
libname TEST clear;

OUT1
 
  B  
 C 
 D 
   1
  1
                 .
           .
   2
  1
 10JAN2012 
 10:20:00
   3
  1
                 .
           .
   4
  1
                 .
           .
   5
  
                 .
           .
   6
  
 20OCT2012
           .
   7
  1 
                 .
           .
   8
  1 
                 .
           .
   9
      
                 .
           .

黄色で示した部分は「aaa」という文字が読み込まれるはずだけど、欠損値になってしまう事に注目。



解説

libname TEST excel "C:\test.xlsx"
                header = yes
                dbmax_text = 8000
                scan_timetype = yes
                mixed = yes ;

LIBNAMEでEXCELファイルを参照出来るようにしています。
以下が構文(オプションは一部抜粋)

libname  参照名  excel  "EXCELファイルのフルパス"
   header             = yes | no  … 読み込み範囲の最初の行を変数名として読み込むか 
   dbmax_text      = 数値      … 文字変数の最大LENGTHを指定
   scan_timetype  = yes | no  … 項目のデータ型が時刻かどうかスキャンして取り込むか
   mixed              = yes | no  … 1つの項目に数値と文字が混在してたら、文字変数として取り込むか
;

data OUT1;
   set TEST."Sheet1$"n;
run;

EXCELの中身を、「参照名."EXCELシート名$"n」という感じで参照できます。


libname TEST clear;

最後にEXCELファイルを解放。




落とし穴(環境により動作は異なる)


実は、LIBNAME EXCELでの読み込みでは、EXCELのデータ部分の先頭8行だけを調べて、データ型などの属性が決定されているようです。
(レジストリの設定とか、環境によっては異なる可能性あり)

この「先頭8行ルール」により以下①~③の読み込み失敗例が考えられます。



① mixed=yesのオプションをつけると、数値と文字が混在してる項目を、文字変数にしてくれるのですが、、、

「先頭8行ルール」により、先頭8行に数値しかない場合、数値変数と判断されます。
そこで、もし9行目以降に文字があると、そこが取り込めず、欠損値になってしまいます。



黄色セルが、欠損値になってしまうやつ。











② 「先頭8行ルール」により、以下のように先頭8行が全て255バイト以内の文字で、9行目以降に256バイト以上の文字がある場合、256バイト以降の文字が切れたり、「?」とか変な文字になる。




黄色セルが、変になるやつ。















③ 「先頭8行ルール」により、以下のように先頭8行の書式が「標準」や「数値」で数値のみが入力されていると数値変数と判断されて、9行目以降に書式が「時刻」や「日付」で時刻(または日付)が入力されていると、9行目以降の当該データはEXCELのシリアル値で読み込まれてしまいます。



「10:20」の場合は「0.430555・・・」というシリアル値で読み込まれる。










ここまでが「先頭8行ルール」による落とし穴で、他にも以下のような落とし穴がある。

④ これもかなり特定の条件ですが、以下のように数値と256バイト以上の文字のみで構成されてるデータで、書式が「標準」や「数値」になってる場合、数値変数と判断されて、文字はすべて欠損値になってしまう。



黄色セルが、欠損値になってしまうやつ。










⑤ オプションの設定によって挙動が変わりますが、今回紹介したプログラムの設定のまま以下のEXCEL (データ部分の書式は「A列: 日付」「B列: 時刻」「C列: ユーザー定義[yyyy/m/d h:mm]」) を読み込むと、、









変数Cが日付値として読み込まれてしまいました。
  B  
 C 
 01JAN2012 
 12:28:00 
 01FEB2012
 02JAN2012
 13:00:00
 02FEB2012 



一応、以下の青文字で示した「SASDATEFMT=」というオプションに、対応させたいフォーマットを指定すると日時として読み込めるようです。
libname TEST excel "C:\test.xlsx"
                header = yes
                dbmax_text = 8000
                scan_timetype = yes
                mixed = yes ;

data OUT1;

   set TEST."Sheet1$"n (sasdatefmt=(C=datetime19.));
run;



⑥ データステップ等でLIBNAME EXCELのファイルにアクセスする際、WHEREステートメントは使わない方が良さそう。
というのも、変数に欠損値があるとWHEREステートメントでうまくオブザベーションが抽出できない場合がありました。SAS社のリファレンスからはこの辺の説明が見つけられなかったので、環境によるものかも不明です。

data OUT1;
   set TEST."Sheet1$"n;
   where B^=1;   /* ← NG */
run;




以上、わたしが気づいた範囲のえぐい落とし穴でした。

実は根本的な解決法が無いのが現状です。
一応自分なりに、こうするしかないかなぁー。。という方法はあるので、記事としてまとめられたら紹介したいと思います。

追記
いろいろ調べた結果、次回紹介予定の方法で正しく動く確証がもてなかったので、他にいい方法がないか、引き続き調査したいと思います。

この方法を使う際は、動作環境によってうまく動かないことがあったり、EXCELの仕様など不確定要素があったりなので、想定通りに読み込めているかを確認した方が良さそうです。



2014年12月19日金曜日

EXCELを読み込む方法と落とし穴 【DDE編】




Windows環境でDDEを使ってEXCELファイルを読み込む方法。
Unicode版だと日本語が文字化けして、うまく読み込めないようなのでご注意下さい。

また、色々な落とし穴があるので最後までご覧ください。




以下のEXCELファイルを読み込みたいとします。



C:\test.xlsx





EXCELファイルをDDEで読み込む例

* EXCELを起動 ;
options  noxwait noxsync;
%sysexec  "C:\TEST.xlsx";


* 10秒待機 ;
data _NULL_;
  rc = sleep(10,1);
run;


* EXCELデータを読み込む ;
filename EXC dde "Excel|[test.xlsx]Sheet1!C1:C4";

data OUT1;
  attrib
      A length=8.
      B length=$20.
      C length=8.   informat=yymmdd10.  format=yymmdds10.
      D length=8.   informat=time5.         format=time5.
  ;
  /* filenameで読み込む行のみを設定した場合、firstobsオプションは不要 */
  infile EXC notab dlm="09"x dsd missover lrecl=50000 firstobs=2;
  input A B C D;
run;

* ファイル参照を解放 ;
filename EXC clear;


OUT1
 
B 
 C 
 D 
   1
 1
                 .
           .
   2
 1
 2012/01/10 
 10:20 
   3
 1
                 .
           .
   4
 1
                 .
           .
   5
  
                 .
           .
   6
  
 2012/10/20
           .
   7
 1 
                 .
           .
   8
 1 
                 .
           .
   9
 aaa 
                 .
           .




解説

options noxwait noxsync;
%sysexec "C:\TEST.xlsx";

・OSのコマンドの力を借りて、EXCELファイルを開きます。
%sysexec "開きたいファイルのフルパス";

・コマンド発行時にSASが一時停止しないよう、noxwait noxsyncオプションを設定しておく。


data _NULL_;
  rc = sleep(10,1);
run;

・EXCELが開くまで後続のSASプログラムが実行されないよう「 sleep(秒数, 1) 」でSASを10秒スリープさせる。重いファイルで起動が遅い場合は、秒数を長く設定しましょう。


filename EXC dde "Excel|[test.xlsx]Sheet1!C1:C4";

・読み込むEXCELの範囲をファイル参照名で定義しておく。
filename ファイル参照名  dde "Excel|[ファイル名]シート名!読み込むセル範囲"; 

今回は1~4列目を読み込む範囲に設定してます。


data OUT1;
  attrib
      A length=8.
      B length=$20.
      C length=8.   informat=yymmdd10.  format=yymmdds10.
      D length=8.   informat=time5.         format=time5.
  ;
  /* filenameで読み込む行のみを設定した場合、firstobsオプションは不要 */
  infile EXC notab dlm="09"x dsd missover lrecl=50000 firstobs=2;
  input A B C D;
run;

・attribで、読み込む変数の属性を定義しておき、infileとinputで、EXCELデータを読み込んでいきます。

「 lrecl=50000 」で、1行に読み込む長さを50000バイトに増やしてます。
この長さが足りないと、途中で文字が切れてしまう事があるので。。
ここは読み込むデータの長さに応じて、設定して下さい。

・また、今回はEXCELの1~4列目を読み込む範囲に設定しましたが、読み込みを開始したい行は、2行目からなので、「 firstobs=2 」としてます。

「 filename EXC dde "Excel|[test.xlsx]Sheet1!R2C1:R10C4"; 」というように、filenameで読み込む行も設定してる場合、firstobs は不要です。




落とし穴


① EXCELのセル内改行には注意。

たとえば1セルに
「aaa
  aaa」
と入力して、SASデータセット化して開いてみると、「aaaaaa」と入ってるように見えます。
改行きえた?と思うかもしれないけど、実は目に見えない改行が入ってます。
この見た目と中身が異なるデータを持つのは、後々面倒を起こしそうです。

対策として、EXCEL上でセル内改行を全て削除しておくか、
SASYAMAさんが紹介している 「目に見えない改行コードが邪魔をしてくる話」を参考に、SAS上で改行コードを削除する方法があります。
(データに日本語等のマルチバイト文字を含む場合、マルチバイト非対応のCOMPRESS関数ではなく、KCOMPRESS関数を利用して下さい)



② EXCELに表示されてる”見た目”をそのまま読み込んでしまうという罠。

どういう事かというと、

EXCELで長い文字を入力して、セルの書式を「文字列」に設定すると、、


以下の通り、「########…」となってしまいます。(EXCELのバージョンとかによっては、この問題は起こらない)












EXCELの仕様っぽいですが、この状態で今回のプログラムを実行すると、SASデータセットにも「#######…」という文字が入ってしまいます。
対策としては、該当セルの書式を「標準」にしてあげれば、直ります。

また、EXCEL上でフィルタや非表示の設定をしている状態でプログラムを実行すると、非表示になってる部分は読み込んでくれません。


③ 想定した通りの行を読み込めているか確認した方が良さそう。

例えば、今回の例では読み込む範囲を「Sheet1!C1:C4」として、EXCELの1~4列の範囲にあるデータを読み込むよう指定していますが、このような指定方法では、どこの行まで読み込むかを明示的に指定していないため、データが入っている範囲外の空白行を読み込んでしまうことがあった。

その他、EXCELの仕様など、不確定要素があるので、最低限、最初と最後の行が想定通りに読み込めているか確認した方が良さそうです。