Announcement

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

  • Loop over macro containing spaces

    I'm trying to loop through the values of a macro whose values contain spaces. There would many uses for such a loop. My example tries to associate short variable names with longer descriptions.

    [CODE]
    local stats Coverage CI_width_mean CI_width_median CI_width_dif_mean CI_width_dif_median rel_bias
    local longstats `" "Coverage" "Mean interval width" "Median interval width" "Mean change in interval width" "Median change in interval width" "Relative bias" "'

    foreach stat in Coverage CI_width_mean CI_width_median CI_width_dif_mean CI_width_dif_median rel_bias {
    local i=`i'+1
    local longstat=word(`longstats',`i')
    di "`i': `stat': `longstat'" }
    [CODE]

    The desired output is
    1: Coverage: Coverage
    2: CI_width_mean: Mean interval width
    3: CI_width_median: Median interval width
    /* etc. */
    The actual output is
    1: Coverage: Coverage
    2: CI_width_mean:
    3: CI_width_median:
    /* etc. */
    What am I doing wrong?

  • #2
    It seems that the word() function separates words by blanks regardless of quotes. You need more smart way such as -tokenize- command to separate words. Following will work:
    Code:
    local stats Coverage CI_width_mean CI_width_median CI_width_dif_mean CI_width_dif_median rel_bias
    local longstats `" "Coverage" "Mean interval width" "Median interval width" "Mean change in interval width" "Median change in interval width" "Relative bias" "'
    
    tokenize `"`longstats'"'
    foreach stat in Coverage CI_width_mean CI_width_median CI_width_dif_mean CI_width_dif_median rel_bias {
        local i=`i'+1
        di `"`i': `stat': ``i''"'
    }

    Comment


    • #3
      Thank you. And what would I do if I wanted to use `i' to loop over the values of `longstats' and put each value in a macro called `longstat'?

      Comment


      • #4
        word() should work fine here so long as you use (compound) double quotes when you are insisting that longstats contains a literal string. Without those Stata looks inside the macro for a variable or scalar name.

        For example, in the context of the auto data make is the name of a string variable while “make” is just a literal string.

        My phone isn’t up to getting the font exactly right....


        Note: a different issue is that I don’t see much charm in a loop over a simple series of display statements.
        Last edited by Nick Cox; 10 Dec 2019, 14:10.

        Comment


        • #5
          An alternative:
          Code:
          local stats Coverage CI_width_mean CI_width_median CI_width_dif_mean CI_width_dif_median rel_bias
          
          local longstats `" "Coverage" "Mean interval width" "Median interval width" "Mean change in interval width" "Median change in interval width" "Relative bias" "'
          
          ********************************************************************************
          local i = 0
          
          foreach stat of local stats {
          
              local ++i
              
              gettoken longstat longstats : longstats 
              
              di "`i': `stat': `longstat'"
          }
          
          local i 
          ********************************************************************************
          Last edited by Bjarte Aagnes; 10 Dec 2019, 14:25.

          Comment


          • #6
            Nick Cox : What do you mean by "compound (double) quotes".

            Comment


            • #7
              I did write and mean "(compound) double quotes" and

              Code:
              help quotes
              explains. But you used them in #1, perhaps without knowing their name.

              Although I trust that #1 based on word() could be made to work, in practice it got too messy in my limited experiments.

              Without feeling very contradictory, I suggest that

              1. A series of display statements is here easier to write, easier to understand and likely to be easier to modify.

              2. The approach used by Bjarte Aagnes is one I would favour given a preference or instruction to write this as a loop.

              3. This is different technique and does work too.

              Code:
              tokenize `" "Coverage" "Mean interval width" "Median interval width" "Mean change in interval width" "Median change in interval width" "Relative bias" "' 
              
              foreach stat in Coverage CI_width_mean CI_width_median CI_width_dif_mean CI_width_dif_median rel_bias {
                  local i = `i' + 1
                  di `"`i':  `stat' : ``i''"'  
              }

              Comment


              • #8
                Nick Cox and group, As a follow-up question to this topic, I have been trying to run a loop where I have string observations that are plain alphabetical strings and those that are with quotes and am having similar problems trying to get the compound quotes to work correctly, even after reviewing the help guide and reading similar threads.

                The quotes in some need to be actually interpreted as part of the string--Examples: "HI" LO" are actually the full strings, including the quotes. I cannot figure out how to add the surrounding compound quotes to get the loop to work. Stata is not recognizing when I tried `"HI"' or `"`HI'"' or `""HI""'
                I am just going in circles...
                I do have strings that are plainly HI and LO and those have worked fine defining in the classic manner in the loop as "HI" LO"

                Here is the Code with the plain strings. Trouble happens when I try to add the strings that contain the double quotes.

                Code:
                foreach L in "HI" "LO"   {
                     replace lactate = "0.5" if lactate =="`L'"
                }

                Any thoughts here?

                Thank you.
                Last edited by Leonard Scott; 25 Jan 2021, 14:03.

                Comment


                • #9
                  Code:
                  foreach L in HI LO{
                       replace lactate = "0.5" if lactate == "`L'" | lactate ==`""`L'""'
                  }

                  Comment


                  • #10
                    "HI" LO" are actually the full strings
                    I don't quite understand what that means, but here's example code that shows how, when assigning a value to a macro, Stata strips off surrounding quotation marks. So you need the compound quotes in your list of values. Perhaps this example will start you in a useful direction.
                    Code:
                    . foreach L in HI LO "HI" "LO" `""HI""' `"LO""' `""HI" LO""' {
                      2.    macro list _L
                      3.    display `"           L is `L' "'
                      4. }
                    _L:             HI
                               L is HI 
                    _L:             LO
                               L is LO 
                    _L:             HI
                               L is HI 
                    _L:             LO
                               L is LO 
                    _L:             "HI"
                               L is "HI" 
                    _L:             LO"
                               L is LO" 
                    _L:             "HI" LO"
                               L is "HI" LO"

                    Comment


                    • #11
                      Thank you--I am still having trouble here. Stata is still not recognizing the strings that contain quotes.
                      To be more specific on my goal, I am trying to replace each LO or "LO" with "0.5" and will then destring.

                      So the loop (shown briefly, as there are more strings) is:

                      Code:
                      foreach L in  "LO"  `""LO""'  {
                           replace var = "0.5" if var =="`L'"
                      }
                      This isn't working for the LO with quotes.

                      To test this as you suggested, I tried:

                      Code:
                      foreach L in HI LO LOW {
                        2. display "`L'"
                        3. }
                      HI
                      LO
                      LOW
                      AND

                      Code:
                      foreach L in "HI" "LO" "LOW" {
                        2. display "`L'"
                        3. }
                      HI
                      LO
                      LOW
                      The first 2 work as expected. However the following does not and yields the error as invalid:

                      Code:
                      foreach L in `""HI""' HI "LO" LOW {
                        2. display "`L'"
                        3. }
                      HI"" invalid name
                      I'm not sure how to get this right for the strings that themselves contain quotes. I'm not getting the compound quotes right.

                      Thank you.

                      Comment


                      • #12
                        Not sure if you saw #9, but this will do it:

                        Code:
                        clear
                        input str5 lactate
                        "HI"    
                        "LO"    
                        `""HI""'
                        `""LO""'
                        end
                        gen lactate2 = .
                        foreach L in HI LO{
                             replace lactate2 = 0.5 if lactate == "`L'" | lactate ==`""`L'""'
                        }
                        
                        . list in 1/4
                        
                             +--------------------+
                             | lactate   lactate2 |
                             |--------------------|
                          1. |      HI         .5 |
                          2. |      LO         .5 |
                          3. |    "HI"         .5 |
                          4. |    "LO"         .5 |
                             +--------------------+
                        In terms of your final example in #11, you need another set of compound double quotes

                        Code:
                        foreach L in `""HI""' HI "LO" LOW {
                        2. display `"`L'"'
                        3. }
                        
                        "HI"
                        HI
                        LO
                        LOW

                        Comment


                        • #13
                          Ali Atia Yes thanks! I see this better now. I was spending more time trying to quote the strings properly, when I could have just quoted the macro name referenced in final command.
                          This works and thank you.
                          It's really a tongue twister to get it right...

                          Comment

                          Working...
                          X