Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Defining a program that uses both a macro name and contents

    Is there a way to write a program definition such that the program uses both the name of a local macro and the content? I'd like to send a program any number of variables stored as a local macro, and then have some commands evaluated using the name of the local, and other commands evaluated for each variable that make up the value of the local.

    Something like this:

    Code:
    sysuse auto, clear
    
    cap program drop simple
    program define simple
    args listofvars
    
        cap frame drop `listofvars'
        frame create `listofvars'
        frame `listofvars': set obs 5
        frame `listofvars': gen mean=. 
        local i=1
        foreach var of local ``listofvars'' {
            summ `var'
            frame `listofvars': replace mean=r(mean) in `i'
            local i=`i'+1
        }
    end
        
    local mylist price mpg headroom trunk
    simple mylist

    I hope this is just one level of quote or backtick away from working, but feel free to tell me if there's a better way to do this where I can use the name of the list and each member of the list regardless of how many variables there are in said list.
    Thanks!

  • #2
    I am not clear exactly on what you are asking, in part because I think you have introduced an X-Y problem. Why is it important that the name of the new frame be the same name as the local macro containing a varlist? Or it is rather important that you simple create a new frame with means of each variable?

    I do notice your example uses -args-, though the use of -syntax- is much more versatile and supercedes -args- in many ways. Here is one idea wherein I rewrite your program to generate means of a list of numeric variables and move them into a new frame. The new frame adds the variable name as a way to match summary statistic to variable, rather than solely rely on position of the argument. The use of syntax adds some basic error checking that at least one numeric variable is passed, and only numeric variables are listed. A new frame name is also supplied which conforms to Stata's naming restrictions.

    Code:
    capture program drop myprog
    program myprog
      syntax varlist(numeric min=1) , frame(name)
      
      local curframe = c(frame)
      cap frame drop `frame'
      
      tempname M
      
      qui tabstat `varlist' , save
      frame create `frame'
      frame change `frame'
      mat `M' = r(StatTotal)
      qui svmat double `M' , names(col)
      rename (*) (mean*)
      qui gen `c(obs_t)' row = _n
      qui reshape long mean, i(row) j(var) string
      drop row
      
      cwf `curframe'
    end
    
    sysuse auto, clear
    myprog price mpg turn , frame(stats)
    frame stats: list

    Comment


    • #3
      As another pointer in a helpful direction, if it really is important for some reason to have access the macro name and it's contents (mylist, in your example), this task might be handled by a utility program you define as s-class so you can control what the name is in downstream use. I can also think of a cumbersome way to do this with string functions, but I strongly discourage it as poor coding practice.

      Comment


      • #4
        Garret Christensen I don't understand the problem here either. I second Leonardo Guizzetti's questions.

        On the most minute level possible, I note that the program in #1 wires in 5 as the number of variables. The example happens to use 4. A more general program would just count what is fed to it by way of variable names.

        Apart from using frames, how is the underlying problem different from using collapse to get a variable set of means?

        I doubt that either of the following is what you want but they may be of interest or use (to anyone intrigued by what is being done here):

        moments from SSC announced in https://www.stata.com/statalist/arch.../msg00682.html

        momentsets from SSC announced in #2 at https://www.statalist.org/forums/for...ercentile-sets
        i

        FWIW, a frame option to momentsets is in mind, but a long way down my to-do list. Here are minimal examples of both in action. The example for momentsets misses the point that the main idea is to save results to a new dataset.

        Code:
        . sysuse auto, clear
        (1978 automobile data)
        
        . moments
        
        -----------------------------------------------------------------------
                        n = 69 |       mean          SD    skewness    kurtosis
        -----------------------+-----------------------------------------------
                         Price |   6146.043    2912.440       1.688       5.032
                 Mileage (mpg) |     21.290       5.866       0.995       3.997
            Repair record 1978 |      3.406       0.990      -0.057       2.678
                Headroom (in.) |      3.000       0.853       0.197       2.144
         Trunk space (cu. ft.) |     13.928       4.343      -0.044       2.159
                 Weight (lbs.) |   3032.029     792.851       0.118       2.073
                  Length (in.) |    188.290      22.747      -0.076       2.000
             Turn circle (ft.) |     39.797       4.441       0.071       2.228
        Displacement (cu. in.) |    198.000      93.148       0.581       2.354
                    Gear ratio |      2.999       0.463       0.279       2.109
                    Car origin |      0.304       0.464       0.850       1.723
        -----------------------------------------------------------------------
        
        . momentsets price-foreign, mean
        
          +-------------------------------------------------------+
          |      varname                 varlabel    n       mean |
          |-------------------------------------------------------|
          |        price                    Price   69   6146.043 |
          |          mpg            Mileage (mpg)   69   21.28986 |
          |        rep78       Repair record 1978   69   3.405797 |
          |     headroom           Headroom (in.)   69          3 |
          |        trunk    Trunk space (cu. ft.)   69   13.92754 |
          |-------------------------------------------------------|
          |       weight            Weight (lbs.)   69   3032.029 |
          |       length             Length (in.)   69   188.2899 |
          |         turn        Turn circle (ft.)   69    39.7971 |
          | displacement   Displacement (cu. in.)   69        198 |
          |   gear_ratio               Gear ratio   69   2.999275 |
          |-------------------------------------------------------|
          |      foreign               Car origin   69   .3043478 |
          +-------------------------------------------------------+
        Code:
        
        


        '

        Comment


        • #5
          Thanks Leonardo Guizzetti and Nick Cox. I suppose without the X-Y problem, and simplifying my MWE even further, my question was really "how can you use both the name of a macro and the contents of a macro in a program?" And one simple answer is "just send the name and the contents separately using -syntax-"

          So something like this is working for me:
          Code:
          sysuse auto, clear
          
          cap program drop foo
          program define foo
          syntax varlist(numeric min=1), bar(name)
          disp "`bar'"
          foreach var in `varlist' {
              disp "`var'"
          }
          end
          
          foo weight price, bar(listname)

          Comment


          • #6
            You're welcome. It's still worth thinking about why you even need to specify and use the name of a local macro. In your example, you can do all of that without the -bar()- option. Notice in your revised example, you don't even use -bar- for anything meaningful.

            (Of course, you can always define a local macro within -foo- as needed which is local to the scope of -foo- and will be discarded when -foo- completes execution.)

            Code:
            program define foo
            syntax varlist(numeric min=1)
            disp "`varlist'"
            foreach var of varlist `varlist' {  // note: there is a form of -foreach- intended for varlists
                disp "`var'"
            }
            end
            
            foo weight price

            Comment


            • #7
              Thanks for the reply in #4 but I have to confess that I am still struggling with what you're asking and why.

              Passing the contents of a local macro is what we often want to do. Passing the name as well would seem unhelpful as well as redundant as the local macro name can't be used in any way outside the space in which it's defined.

              That aside, you say that the program in #4 works. It does run and the results are

              Code:
              listname
              weight
              price
              In other words, you have passed the text listname to your program and got your program to print out exactly that text.


              I am at a loss to know what this has do with your question about local macros. Is listname the name of a local macro?

              Comment

              Working...
              X