Announcement

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

  • Evaluating a macro expression, gives a different result than using the same expression outside of a macro

    I am evaluating a macro expression, and get the following

    Code:
    sysuse auto
    
    display "`=price if _n==5' "
    
    . 4099
    However when I use the same expression outside of a macro, I get a different result

    Code:
    list price if _n==5
    . 7827

    How can I get the macro expression to correctly use if qualifiers?

    Thanks in advance.

  • #2

    Use this:

    Code:
    display `"`=price[5]'"'
    Stata is not evaluating the "if _n==5" part of your first set of code. The 'if' condition is evaluated in the -list- command because it's allowed as part of the syntax. Take a look at the help file for explicit subscripting.



    Eric A. Booth | Senior Director of Research | Far Harbor | Austin TX

    Comment


    • #3
      Does this help?
      Code:
      sysuse auto, clear
      
      local thePrice=price[5]
      
      display `thePrice'
      
      list price if _n==5

      Comment


      • #4
        Thank you Eric and Ben

        Yes both of your examples work, but I was in fact trying to use an if condition as that is important. In general I do not know that it will be the 5th observation, but would have some other sort of if condition to narrow down to a single observation.


        I have come across this as another possible method which seems to work

        Code:
        levelsof price if _n==5, local(MyLocal)
        7827
        
        . disp `MyLocal'
        7827
        Although I wish I could store in a global, but I can of course add more code to do that.

        Would be interested to hear if you know of any other ways.


        Thanks again.


        Comment


        • #5
          It seems that the -levelsof- is just an extra (superfluous) step. It's not producing a result that is different from running the aforementioned :

          Code:
          sysuse auto, clear
          loc MyLocal = price[5]
              di `"`MyLocal'"'
          **or, again**
              di `"`=price[5]'"'
          I think your statement that " I was in fact trying to use an if condition as that is important. In general I do not know that it will be the 5th observation, but would have some other sort of if condition to narrow down to a single observation" is unclear -- if you need an if condition, tell us why.
          If you are not sure that the value you need to store in the local is always going to be the 5th observation then you shouldn't be using the _n subscript to find the value of interest. I'm not sure that we can help you without more details about your data and the condition you are trying to evaluate.

          Eric A. Booth | Senior Director of Research | Far Harbor | Austin TX

          Comment


          • #6
            Although I wish I could store in a global, but I can of course add more code to do that.
            There is a reason that Stata commands that return macros return them as local macros: global macros are unsafe.

            When you use a global macro, if there is any other Stata program using a global macro of the same name, you have a clash, and either your global is going to overwrite that one, or that one will overwrite yours. And there is this problem: you never know what other programs might be in Stata's memory, called by some sub-program of a sub-program of a command that you've never given a second thought to, and what they might use.

            Local macros don't have that problem: local macro my_macro in one program or do-file or interactive session has nothing to do with any macro of the same name anywhere else. If the concern is that you wish to use the value of that macro across several levels of program calls, the best solution is usually to pass the value as an argument or option in the program calls. Sometimes that ends up making your program calls to complicated, especially if there are several levels of program calls where the value of the macro is not used and is present only for purposes of passing through to a lower level. When that happens, using a global macro might be a better alternataive--but that doesn't happen often. So when you find yourself thinking of using a global macro, stop and ask yourself if it's really, really necessary and worth the risk. If you do use one, you should pick some really bizarre random name for it so you are not likely to clobber something somewhere else with the same name.

            Another situation where people are often tempted to use global macros is that they want to run a sequence of do-files and they want to specify some list of variables or filenames or the like for all of them to use. So it's tempting to create a global macro containing that information and then run the do-files in sequence. In addition to the general problem with global macros pointed out in the preceding paragraphs, this has an additional drawback: the second and subsequent do-files are no dependent on the first one and can only be run after the first. So if, for example, you have to modify the second do-file for some reason, you have to also re-run the first before you can run the second one. The solution again is a local macro, with the use of the -include- command to assure the propagation of the information across the do files. That is, you first create a do-file that contains only the definition(s) of the macro(s) you want to use in common across files:

            Code:
            local outcomes speed emissions price
            local predictors weight stroke_volume body_style
            local magic_number 7.16
            // SAVE THIS FILE AS common_information.do
            Then in each of the do-files that needs to use this common information:

            Code:
            include common_information
            
            foreach o of local outcomes {
               regress `o' `predictors' // OR WHATEVER
               display `magic_number'*_b[stroke_volume]
            }
            The -include- command causes Stata to copy the included do-file into the current do-file, so it works just as if those local macros were actually defined in the current do-file. Note that you cannot replace -include- with -run- or -do-The point is that -include- actually virtually copies the commands into the current do-file, whereas -run- and -do- execute the commands in the separate common_information do-file,causing the macros to go out of scope when control returns to the current do-file.


            Comment


            • #7
              Interesting discussion, but coming back to the starting point, can anyone explain why

              Code:
              sysuse auto
              display "`=price if _n==5' "
              returns 4099?

              More explicitly, why does it not return void (i.e. empty string) or an error?

              Best
              Daniel
              Last edited by daniel klein; 25 Mar 2016, 11:04.

              Comment


              • #8
                I suspect it's ignoring the if condition because it's not legal syntax.
                So, it's returning the first value in the first observation, just like if you ran:

                Code:
                display "`=price' "
                **or
                display price



                Eric A. Booth | Senior Director of Research | Far Harbor | Austin TX

                Comment


                • #9
                  That is what I suspect, too. But I think Stata should not do so. In fact I dare call this behavior a bug. If the expression is invalid, in any sense, then I would expect an error. Even this "works"

                  Code:
                  sysuse auto
                  display `=price this should not just be ignored, should it?'
                  which should clearly not.

                  Best
                  Daniel

                  Comment


                  • #10
                    Thank you Clyde for the insightful comments. I think you are correct that my desire for global macros comes from what is essentially "several levels of program calls", but I think it comes about in a different way than what you touched on. Here is an example where I run into problems due to using a local macro

                    This code is attempting to get the last value shown in variable "price" and then create a new variable in another dataset that is filled with this value.

                    Code:
                    sysuse auto, clear
                    levelsof price in l, local(MyMacro1)
                    
                    
                    clear
                    set obs 20
                    gen newvar = `MyMacro1'

                    Here, running the 1st block and then the 2nd block separately will produce an "invalid syntax" message.

                    If I run it all at once however, it works as expected. Is it correct, this is due to the local macro only existing during the (instantaneous) life of the first block's execution?

                    I usually write my lengthy code in the do file editor, and highlight my code block by block and press the "Execute (do)" icon on the toolbar to run each block and thus go through the whole program. It seems this style of usage is ill fated with the use of local macros. Trying to re-write my code as a series of program calls with arguments is a fairly different approach, and requires a material change to my Stata usage habits. In my humble opinion, being able to run code segments block by block from the do file editor has advantages in flexibility. Namely by allowing editing and re-editing of code as you look at it in the do file editor over time.

                    Comment


                    • #11
                      Is it correct, this is due to the local macro only existing during the (instantaneous) life of the first block's execution?
                      Yes, that is correct. A highlighted section of a do-file run as a block is, for the purposes of determining scope of local macros, like a separate do-file.

                      Trying to re-write my code as a series of program calls with arguments is a fairly different approach, and requires a material change to my Stata usage habits. In my humble opinion, being able to run code segments block by block from the do file editor has advantages in flexibility. Namely by allowing editing and re-editing of code as you look at it in the do file editor over time.
                      Well, as they say, different strokes for different folks. Breaking code down into smaller blocks that are pretty much independent of each other, with the dependencies being limited and explicit as, for example, through program calls with parameters passed, is a generally recommended programming style in any language. It may feel cumbersome at first, but I know that once I switched over to it, it ended up saving me an enormous amount of time. That's because despite the extra thinking that goes into doing it this way, you make far fewer errors, and when you make them they are far easier to find and fix. It also makes it easier to make changes to programs if later developments call for that: you make the changes right where they are needed and you don't have to worry about what implications that has for other programs. I've been programming, in a number of languages, since the early 1960's. The old days were definitely the bad old days in that regard. I changed my work style in the 80's when I started programming in C--it was the first language I used that actually facilitated using good style. It made a world of difference.

                      That said, it certainly is convenient to be able to edit and re-edit sections of code in a do-file as you look at it in the do-file. My response to that is that I have little difficulty doing that despite my style. For one thing, I work sequentially. I write a "paragraph" or two of code that carries out some specific part of the problem, and I run and test that before going on to write more. I don't go beyond that part of the code until I'm pretty sure it's correct. Another habit I have is that I don't define local macros until "just in time." Most local macros are really needed for only a short segment of code (at least that's true if you write short programs that call each other.) So I, too, run a lot of highlighted sections of do-files, but seldom run into the problem you mention because my local macros are almost always defined in the section of code where they are used, and are not needed elsewhere.

                      This style of programming becomes self-reinforcing once you get used to it, and, once you learn it, it's really much easier.

                      Well, enough said. Have a good weekend.

                      Comment


                      • #12
                        Thanks again Clyde, I'll see if I can teach an old dog new tricks. Have a great weekend!

                        Comment

                        Working...
                        X