3

I have 10 variables (var1-var10), which I need to rename var10-var1 in SAS. So basically I need var10 to be renamed var1, var9 var2, var8 var3, and so on.

This is the code that I used based on this paper, http://analytics.ncsu.edu/sesug/2005/PS06_05.PDF:

%macro new;

    data temp_one;
        set temp;

        %do i=10 %to 1 %by -1;
            %do j=1 %to 10 %by 1;
                var.&i=var.&j
            %end;
        %end;
        ;
%mend new;

%new;

The problem I'm having is that it only renames var1 as var10, so the last iteration in the do-loop.

Thanks in advance for any help!

Emily

llsabang
  • 143
  • 1
  • 10
  • 1
    I wonder why the downvote? This is a quite high quality question; it's fairly basic, but it has all of the elements we look for in a good question - detailed code to reproduce, explicit statement of the incorrect behavior, and specificity. – Joe Nov 27 '17 at 17:19
  • If you just want to rename variables without creating a new copy of the dataset, use `proc datasets` to do this, using the rename statement from reeza's answer. – user667489 Nov 28 '17 at 14:46

2 Answers2

2

You really don't need to do that, you can rename variable with list references, especially if they've been named sequentially.

ie:

rename var1-var10 = var10-var1;

Here's a test that demonstrates this:

data check;
    array var(10) var1-var10 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    output;
run;

data want;
    set check;
    rename var1-var10 = var10-var1;
run;

If you do need to do it manually for some reason, then you need two arrays. Once you've assigned the variable you've lost the old variable so you can't access it anymore. So you need some sort of temporary array to hold the new values.

Reeza
  • 20,510
  • 4
  • 21
  • 38
  • Thanks Reeza!! I had no idea it was that simple. For some reason, I didn't think you could use a hyphen in the rename statement. – llsabang Nov 27 '17 at 17:21
1

While Reeza's answer is correct, it's probably worth going through why your method didn't work - which is another reasonable, if convoluted, way to do it.

First off, you have some minor syntax issues, such as a misplaced semicolon, periods in the wrong places (They end macro variable names, not begin them), and a missing run statement; we'll ignore those and fix them as we change the code.

Second, you have two nested loops, when you don't really want that. You don't want to do the inner code 10 times (once per iteration of j) for each iteration of i (so 100 times total); you want to do the inner code once for each iteration of both i and j.

Let's see what this fix, then, gives us:

data temp;
  array var[10];
  do _n_ = 1 to 15;
    do _i = 1 to 10;
      var[_i] = _i;
    end;
    output;
  end;
  drop _i;

run;

%macro new();

    data temp_one;
        set temp;

        %do i=10 %to 1 %by -1;
            %let j = %eval(11-&i.);
            var&i.=var&j.;
        %end;
    run;

%mend new;

%new();

Okay, so this now does something closer to what you want; but you have an issue, right? You lose the values for the second half (well, really the first half since you use %by -1) since they're not stored in a separate place.

You could do this by having a temporary dumping area where you stage the original variables, allowing you to simultaneously change the values and access the original. A common array-based method (rather than macro based) works this way. Here's how it would look like in a macro.

%macro new();

    data temp_one;
        set temp;

        %do i=10 %to 1 %by -1;
            %let j = %eval(11-&i.);
            _var&i. = var&i.;
            var&i.=coalesce(_var&j., var&j.);
        %end;
        drop _:;
    run;

%mend new;

We use coalesce() which returns the first nonmissing argument; for the first five iterations it uses var&j. but the second five iterations use _var&j. instead. Rather than use this function you could also just prepopulate the variable.

A much better option though is to use rename, as Reeza does in the above answer, but presented here with something more like your original answer:

%macro new();

    data temp_one;
        set temp;
        rename
        %do i=10 %to 1 %by -1;
            %let j = %eval(11-&i.);
            var&i.=var&j.
        %end;
        ;
    run;

%mend new;

This works because rename does not actually move things around - it just sets the value of "please write this value out to _____ variable on output" to something different.

This is actually what the author in the linked paper proposes, and I suspect you just missed the rename bit. That's why you have the single semicolon after the whole thing (since it's just one rename statement, so just one ; ) rather than individual semicolons after each iteration (as you'd need with assignment).

Joe
  • 62,789
  • 6
  • 49
  • 67
  • Thank you Joe! Your response is very thorough. I'm just curious, what does the _n_ =1 to 15 in the top part of the code correspond to? I'm also not quite sure why there's a _ before var&i and var&j...Thanks! – llsabang Nov 28 '17 at 22:51
  • I'm just creating some example data in the first part; `_n_` is an automatic variable that usually corresponds to the data step iteration and in a data step like the above where we're making our own iterations, it's common to use `_n_` as the iterator to make that clear (and to take advantage of the fact that the variable is dropped automatically). – Joe Nov 28 '17 at 22:58
  • As for the `_var&j` and `_var&i`, those are temporary variable arrays that are made to store the original values in `var1-var10` so that you can access them despite having changed the values in the actual variables. Sort of a read-only mirror image of the original array. – Joe Nov 28 '17 at 22:59