楼主: edwardzxf
4818 8

[原创博文] 变量长度的压缩 [推广有奖]

  • 0关注
  • 2粉丝

已卖:32份资源

讲师

26%

还不是VIP/贵宾

-

威望
0
论坛币
222 个
通用积分
0.4203
学术水平
0 点
热心指数
2 点
信用等级
0 点
经验
3991 点
帖子
242
精华
0
在线时间
375 小时
注册时间
2010-7-30
最后登录
2021-5-25

楼主
edwardzxf 学生认证  发表于 2011-10-8 23:10:55 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币
我的数据集里面的一些变量,特别是字符变量的length很大,我想把他们压缩一下,但不能让原有数据缺失,请问有什么快捷的方法吗,谢谢
二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:length 数据缺失 字符变量 leng 数据集

沙发
ntsean 发表于 2011-10-9 00:47:15
对于字符变量,找到该变量的最大length,然后把变量长度改成这个长度
数字变量没必要改,因为储存长度一般都是8, 你能做的最多只是改一下format,显示的时候不同而已

藤椅
edwardzxf 学生认证  发表于 2011-10-9 11:46:26
ntsean 发表于 2011-10-9 00:47
对于字符变量,找到该变量的最大length,然后把变量长度改成这个长度
数字变量没必要改,因为储存长度一般 ...
有没有那里有这样的宏呀?

板凳
yugao1986 发表于 2011-10-9 16:01:04
有个宏SQUEEZE,考虑下COMPRESS=BINARY
三人行必有我师

报纸
wangyf0218 发表于 2011-10-9 18:07:58
这个问题挺有意思,求大牛回答,如何用最小的长度去储存变量?这个对我也挺有用的,有的时候读数据的时候不知道这个变量定义长度为多少才合适?不知道大家是怎样解决这个问题的?

地板
edwardzxf 学生认证  发表于 2011-10-9 20:39:42
下面这个宏基本可以实现这个功能,但好像这个宏有点问题,会报错,如下:

WARNING: Multiple lengths were specified for the variable firmid by input data set(s). This may
         cause truncation of data.
WARNING: Multiple lengths were specified for the variable year by input data set(s). This may
         cause truncation of data.
WARNING: Multiple lengths were specified for the variable length_firmid by input data set(s).
         This may cause truncation of data.
我把宏的原代码给出,看那位高手把这个Bug修改了,那就万分感激了.


原宏代码:

%macro SQUEEZE( DSNIN       /* name of input SAS dataset                                                      */
              , DSNOUT      /* name of output SAS dataset                                                     */
              , NOCOMPRESS= /* [optional] variables to be omitted from the minimum-length computation process */
              ) ;

   /* PURPOSE: create LENGTH statement for vars that minimizes the variable length to:
    *          numeric vars: the fewest # of bytes needed to exactly represent the values contained in the variable
    *
    *          character vars: the fewest # of bytes needed to contain the longest character string
    *
    *          macro variable SQZLENTH is created which is then invoked in a
    *          subsequent data step
    *
    * NOTE:    if no char vars in dataset, produce no char var processing code
    *
    * NOTE:    length of format for char vars is changed to match computed length of char var
    *          e.g., if length( CHAR_VAR ) = 10 after %SQUEEZE-ing, then FORMAT CHAR_VAR $10. ; is generated
    *
    * NOTE:    variables in &DSNOUT are maintained in same order as in &DSNIN
    *
    * NOTE:    variables named in &NOCOMPRESS are not included in the minimum-length computation process
    *          and keep their original lengths as specified in &DSNIN
    *
    * EXAMPLE OF USE:
    *          %SQUEEZE( DSNIN, DSNOUT )
    *          %SQUEEZE( DSNIN, DSNOUT, NOCOMPRESS=A B C D--H X1-X100 )
    *          %SQUEEZE( DSNIN, DSNOUT, NOCOMPRESS=_numeric_          )
    *          %SQUEEZE( DSNIN, DSNOUT, NOCOMPRESS=_character_        )
    */

   %global SQUEEZE ;
   %local I ;

   %if "&DSNIN" = "&DSNOUT"
   %then %do ;
      %put /------------------------------------------------\ ;
      %put | ERROR from SQUEEZE:                            | ;
      %put | Input Dataset has same name as Output Dataset. | ;
      %put | Execution terminating forthwith.               | ;
      %put \------------------------------------------------/ ;

      %goto L9999 ;
   %end ;

   /*############################################################################*/
   /* begin executable code
   /*############################################################################*/

   /*============================================================================*/
   /* create dataset of variable names whose lengths are to be minimized
   /* exclude from the computation all names in &NOCOMPRESS
   /*============================================================================*/

   proc contents data=&DSNIN( drop=&NOCOMPRESS ) memtype=data noprint out=_cntnts_( keep= name type ) ; run ;

   %let N_CHAR = 0 ;
   %let N_NUM  = 0 ;

   data _null_ ;
      set _cntnts_ end=lastobs nobs=nobs ;

      if nobs = 0 then stop ;

      n_char + ( type = 2 ) ;
      n_num  + ( type = 1 ) ;

      /* create macro vars containing final # of char, numeric variables */

      if lastobs
      then do ;
         call symput( 'N_CHAR', left( put( n_char, 5. ))) ;
         call symput( 'N_NUM' , left( put( n_num , 5. ))) ;
      end ;
   run ;

   /*============================================================================*/
   /* if there are NO numeric or character vars in dataset, stop further processing
   /*============================================================================*/

   %if %eval( &N_NUM + &N_CHAR ) = 0
   %then %do ;
      %put /----------------------------------\ ;
      %put | ERROR from SQUEEZE:              | ;
      %put | No variables in dataset.         | ;
      %put | Execution terminating forthwith. | ;
      %put \----------------------------------/ ;

      %goto L9999 ;
   %end ;

   /*============================================================================*/
   /* put global macro names into global symbol table for later retrieval
   /*============================================================================*/

   %do I = 1 %to &N_NUM ;
      %global NUM&I NUMLEN&I ;
   %end ;

   %do I = 1 %to &N_CHAR ;
      %global CHAR&I CHARLEN&I ;
   %end ;

   /*============================================================================*/
   /* create macro vars containing variable names
   /* efficiency note: could compute n_char, n_num here, but must declare macro names to be global b4 stuffing them
   /*
   /* note: if no char vars in data, do not create macro vars
   /*============================================================================*/

   proc sql noprint ;
      %if &N_CHAR > 0 %then %str( select name into :CHAR1 - :CHAR&N_CHAR from _cntnts_ where type = 2 ; ) ;

      %if &N_NUM  > 0 %then %str( select name into :NUM1  - :NUM&N_NUM   from _cntnts_ where type = 1 ; ) ;
   quit ;

   /*============================================================================*/
   /* compute min # bytes (3 = min length, for portability over platforms) for numeric vars
   /* compute min # bytes to keep rightmost character for char vars
   /*============================================================================*/

   data _null_ ;
      set &DSNIN end=lastobs ;

      %if &N_NUM  > 0 %then %str ( array _num_len_  ( &N_NUM  ) 3 _temporary_ ; ) ;

      %if &N_CHAR > 0 %then %str( array _char_len_ ( &N_CHAR ) _temporary_ ; ) ;

      if _n_ = 1
      then do ;
         %if &N_CHAR > 0 %then %str( do i = 1 to &N_CHAR ; _char_len_( i ) = 0 ; end ; ) ;

         %if &N_NUM  > 0 %then %str( do i = 1 to &N_NUM  ; _num_len_ ( i ) = 3 ; end ; ) ;
      end ;

      %if &N_CHAR > 0
      %then %do ;
         %do I = 1 %to &N_CHAR ;
            _char_len_( &I ) = max( _char_len_( &I ), length( &&CHAR&I )) ;
         %end ;
      %end ;

      %if &N_NUM > 0
      %then %do I = 1 %to &N_NUM ;
         if &&NUM&I ne .
         then do ;
            if &&NUM&I ne trunc( &&NUM&I, 7 ) then _num_len_( &I ) = max( _num_len_( &I ), 8 ) ; else
            if &&NUM&I ne trunc( &&NUM&I, 6 ) then _num_len_( &I ) = max( _num_len_( &I ), 7 ) ; else
            if &&NUM&I ne trunc( &&NUM&I, 5 ) then _num_len_( &I ) = max( _num_len_( &I ), 6 ) ; else
            if &&NUM&I ne trunc( &&NUM&I, 4 ) then _num_len_( &I ) = max( _num_len_( &I ), 5 ) ; else
            if &&NUM&I ne trunc( &&NUM&I, 3 ) then _num_len_( &I ) = max( _num_len_( &I ), 4 ) ;
         end ;
      %end ;

      if lastobs
      then do ;
         %if &N_CHAR > 0
         %then %do ;
            %do I = 1 %to &N_CHAR ;
               call symput( "CHARLEN&I", put( _char_len_( &I ), 5. )) ;
            %end ;
         %end ;

         %if &N_NUM > 0
         %then %do I = 1 %to &N_NUM ;
            call symput( "NUMLEN&I", put( _num_len_( &I ), 1. )) ;
         %end ;
      end ;
   run ;

   proc datasets nolist ; delete _cntnts_ ; run ;

   /*============================================================================*/
   /* initialize SQZ_NUM, SQZ_CHAR global macro vars
   /*============================================================================*/

   %let SQZ_NUM      = LENGTH ;
   %let SQZ_CHAR     = LENGTH ;
   %let SQZ_CHAR_FMT = FORMAT ;

   %if &N_CHAR > 0
   %then %do I = 1 %to &N_CHAR ;
         %let SQZ_CHAR     = &SQZ_CHAR %qtrim( &&CHAR&I ) $%left( &&CHARLEN&I ) ;
         %let SQZ_CHAR_FMT = &SQZ_CHAR_FMT %qtrim( &&CHAR&I ) $%left( &&CHARLEN&I ). ;
   %end ;

   %if &N_NUM > 0
   %then %do I = 1 %to &N_NUM ;
      %let SQZ_NUM = &SQZ_NUM %qtrim( &&NUM&I ) &&NUMLEN&I ;
   %end ;

   /*============================================================================*/
   /* build macro var containing order of all variables
   /*============================================================================*/

   data _null_ ;
      length retain $32767 ;
      retain retain 'retain ' ;

      dsid = open( "&DSNIN", 'I' ) ; /* open dataset for read access only */

      do _i_ = 1 to attrn( dsid, 'nvars' ) ;
         retain = trim( retain ) || ' ' || varname( dsid, _i_ ) ;
      end ;

      call symput( 'RETAIN', retain ) ;
   run ;

   /*============================================================================*/
   /* apply SQZ_* to incoming data, create output dataset
   /*============================================================================*/

   data &DSNOUT ;
      &RETAIN ;
      
      %if &N_CHAR > 0 %then %str( &SQZ_CHAR ;     ) ; /* optimize char var lengths      */

      %if &N_NUM  > 0 %then %str( &SQZ_NUM ;      ) ; /* optimize numeric var lengths   */

      %if &N_CHAR > 0 %then %str( &SQZ_CHAR_FMT ; ) ; /* adjust char var format lengths */

      set &DSNIN ;
   run ;

%L9999:

%mend SQUEEZE ;

7
YueweiLiu 发表于 2011-10-9 21:16:51
我之前写过一个,找找看。

8
YueweiLiu 发表于 2011-10-9 21:25:32
很久之前写的,供楼主参考:
  1. data have;
  2.         input x :$500. y;
  3. cards;
  4. redajingjiluntan 2
  5. ;


  6. %let lib=work;
  7. %let mem=have;

  8. proc sql noprint;
  9.         select count( * ) into :nCHAR
  10.         from dictionary.columns
  11.         where libname = %upcase("&lib.") and memname = %upcase("&mem.") and upcase ( type ) = 'CHAR';
  12. quit;

  13. data _null_;
  14.         set &lib..&mem. end = eof;
  15.         array ch{ * } _character_;
  16.         array tmp{ &nCHAR } _temporary_ ( 1 * %eval( &nCHAR ) );
  17.         do i = 1 to dim( ch );
  18.                 do j = 1 to dim( tmp );
  19.                         if tmp{ i } < length( ch{ i } ) then tmp{ i } = length( ch{ i } );
  20.                 end;
  21.         end;
  22.         if eof then do;
  23.                 length len $2000.;
  24.                 retain len '';
  25.                 do i = 1 to dim( ch );
  26.                         len=catx( ' ', len ,catx ( ' ', vname( ch{ i } ) , cats( ' , tmp{ i } , ' . ' ) ) );
  27.                 end;
  28.                 call execute ( "       
  29.                                                 data &lib..&mem.;
  30.                                                         length "||strip( len )||" ;
  31.                                                         set &lib..&mem.;
  32.                                                 run;
  33.                                           ");
  34.         end;
  35. run;
复制代码

9
sunkylover 发表于 2014-7-24 21:56:35
YueweiLiu 发表于 2011-10-9 21:25
很久之前写的,供楼主参考:
这个代码是可以实现 但是有个问题首先len=catx( ' ', len ,catx ( ' ', vname( ch{ i } ) , cats( ', tmp{ i } , '. ' ) ) );这一行有两个错误len=catx( ' ', len ,catx ( ' $', vname( ch{ i } ) , cats( '' , tmp{ i } , '. ' ) ) );
其次 这样的话还是会有那个数据截断的WARNING出现

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注cda
拉您进交流群
GMT+8, 2026-1-18 02:43