Announcement

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

  • automating graph titles

    Hello
    I am trying to automate labeling mutliple graphs.
    #delimit;
    global dvlist Assembly Public Corruption Freedom v2cltort v2clkill polrights civilliberties FreePress ReligFreedom ;
    foreach i of global dvlist {;
    if `i'==Public {; local j = "Public Goods";};
    else if `i'==Corruption {; local j = "Corruption";};
    else if `i'==Assembly {; local j = "Assembly";};
    else if `i'==Freedom {; local j = "Freedom";};
    else if `i'==v2cltort {; local j = "Freedom from Torture";};
    else if `i'==v2clkill {; local j = "Freedom from Political Killing";};
    else if `i'==polrights {; local j = "Political Rights";};
    else if `i'==civilliberties {; local j = "Civil Rights";};
    else if `i'==FreePress {; local j = "Press Freedom";};
    else if `i'==ReligFreedom {; local j = "Religious Freedom";};

    display "`i' `j'";};
    Using the following code I get the following output
    Assembly Public Goods
    Public Public Goods
    Corruption Corruption
    Freedom Public Goods
    v2cltort Freedom from Torture
    v2clkill Freedom from Political Killing
    polrights Public Goods
    civilliberties Public Goods
    FreePress Public Goods
    ReligFreedom Religious Freedom

    I do not understand why j defaults to "Public goods" in some cases and takes the correct label in others. None of the variable name are abbreviated.
    Any help in getting this to run would be appreciated.




  • #2
    There are two kinds of comparison (1) with variables (or scalars) if you give their names (2) of literal text. The last requires double quotes.

    I won't repeat "(or scalars)" in what follows as being tedious if I did and likely to be irrelevant.

    With variable names I think you will find that

    Code:
    if `i' == Foo
    is equivalent to

    Code:
    if Foo[1] == Foo[1]
    which is easily true for any variable Foo.

    The if command always uses the first observation of a variable. This is discussed in https://journals.sagepub.com/doi/10....6867X231175349

    If Stata can't find a variable with that name, the previous assignment of local macro j will still hold.

    So, my guess is that you need to look carefully at the list of variable names.

    Backing up, I think the best strategy is to assign variable labels before graphing.

    Comment


    • #3

      Sorry I am hopelessly confused by this answer. I have simplifed the code.
      #delimit;
      clear;
      set obs 1; gen Public =.; gen Assembly=.;
      global dvlist Assembly Public ;
      foreach i of global dvlist {;
      if `i'==Public {; local j "Public Goods";};
      else if `i'==Assembly {; local j "Assembly"; };
      display "`i' and `j'";};

      I dont understand what this has to do with observation numbers.
      The output is
      Assembly and Public Goods
      Public and Public Goods

      ​​​​​​​

      Comment


      • #4
        Did you read the linked paper?

        I will try to give a longer answer later.

        Comment


        • #5
          This sounds like an example of the xy problem: see https://xyproblem.info/

          The data example in #3 allows, I think, a very full story, so thanks for that.

          1. The problem is automating graph titles. But you're telling us nothing about what kind of graph you're planning or which titles. The global macro name dvlist suggests to me a list of names of dependent variables. Usually dependent variables (outcomes, responses) are plotted on the y axis. So, Stata will show the variable label for the y variable if one is defined as the y axis title; otherwise it will show the variable name.

          That is probably familiar. If not, here is some example code. The variable labels are by default on the x axis, but the idea is the same.

          Code:
          sysuse auto, clear
          
          histogram price
          
          label var price "Price (USD)"
          
          histogram price
          
          label var price
          
          histogram price
          2. Using variable labels (or variable names) is still the best approach, usually, for automating something else graphical. Here is how to get a series of histograms with the variable information in the subtitle not the x axis title.

          Code:
          foreach v in price mpg weight {
              local this : var label `v'
              if `"this"' == "" local this `v'
              histogram `v', xtitle("") subtitle("`this'")
          }
          3. I don't use semi-colons as delimiters, except rarely. That may be congenial if you've a lot of experience with languages that require it. Sorry if it irritates, but I will wrote code in a style congenial to me, just as you are writing in a style congenial to you.

          Here is your code translated.


          Code:
          clear
          set obs 1
          
          gen Public = .
          gen Assembly = .
          
          global dvlist Assembly Public
          foreach i of global dvlist {
              
          if `i'== Public {
              local j "Public Goods"
          }
          else if `i' == Assembly {
              local j "Assembly"
          }
          
          display "`i' and `j'"
          }
          It gives the output you report, which is not what you want.

          The reason for that was already given in #2, if not clearly enough for you. (At a couple of points, the explanation is badly mangled, but the main idea was right.)

          The if command is not doing what you think it is, or if you get the result you want it is by accident.

          The story is explained at greater length in https://journals.sagepub.com/doi/ful...6867X231175349 Your access to that may be limited.

          The essence is that the code you uses compares variables with variables and -- even if you have no idea or no intent of doing this -- it compares variables according to their values in observation 1. That may seem quirky but if you are comparing variables and variables, the true or false comparison would yield an entire variable's worth of truth values, 0 or 1, which can't possibly fit helpfully into a local macro. The whole point of the if command after all is to yield one truth value, true or false.

          Rather than telling you that the syntax is illegal, Stata compares values in the first observation only.

          Now follow the loop. First time round, the argument is Public so the comparison is

          Code:
          if Public == Public
          interpreted as

          Code:
          if Public[1] == Public[1]
          which you know is always true, so the assignment of Public Goods goes ahead and the else code is never entered.

          So far, so good. But the next one goes wrong.

          Code:
          if Assembly == Public
          is interpreted as

          Code:
          if Assembly[1] == Public[1]
          which has to be checked by looking at the values -- in observation 1. But in your data example both values are missing -- hence they are equal -- and the wrong assignment is made. The else code is never entered.

          What you really wanted to do was compare the names, not the values, which requires a comparison of literal text, hence quotation marks. This is nearer what you want.

          Code:
          clear
          set obs 1
          
          gen Public = .
          gen Assembly = .
          
          global dvlist Assembly Public
          foreach i of global dvlist {
              
          if "`i'" == "Public" {
              local j "Public Goods"
          }
          else if "`i'"== "Assembly" {
              local j "Assembly"
          }
          
          display "`i' and `j'"
          }
          I can't recommend that you follow that route. It's a long-winded way to define desired titles and one easy to get wrong in other ways.

          That is by no means all that could be said about automating titles. If your problem is some distance from what I am imagining, you may want to spell out how.
          Last edited by Nick Cox; 22 Jan 2025, 20:22.

          Comment


          • #6
            My guess is that OP's problem is nothing inherently to do with graphs, but in dealing with macros that involve strings with spaces. Here is one way that illustrates how to do that:

            Code:
            #delimit ;
            local dvlist Assembly Public Corruption Freedom v2cltort
                        v2clkill polrights civilliberties
                        FreePress ReligFreedom;
            local titles `" "Assembly" "Public Goods" "Corruption" "Freedom" "Freedom from Torture"
                        "Freedom from Political Killing" "Political Rights" "Civil Rights"
                        "Press Freedom" "Religious Freedom" "' ;
            #delimit cr
                        
            local numdvs: word count `dvlist'
            
            forval i = 1/`numdvs' {
                local dv: word `i' of `dvlist' 
                local title: word `i' of `titles'
                dis "Variable: `dv'; Title: `title'"
            }
            which produces:

            Code:
            Variable: Assembly; Title: Assembly
            Variable: Public; Title: Public Goods
            Variable: Corruption; Title: Corruption
            Variable: Freedom; Title: Freedom
            Variable: v2cltort; Title: Freedom from Torture
            Variable: v2clkill; Title: Freedom from Political Killing
            Variable: polrights; Title: Political Rights
            Variable: civilliberties; Title: Civil Rights
            Variable: FreePress; Title: Press Freedom
            Variable: ReligFreedom; Title: Religious Freedom

            Comment


            • #7
              Also, as a side note, your code in #1 does not actually work. It produces the error:

              Code:
              Assembly not found
              r(111);
              This is because of pretty much the same issue that Nick discussed in #5. If you
              Code:
              set trace on
              prior to running the code, it shows:
              Code:
              - foreach i of global dvlist {
              - if `i'==Public {
              = if Assembly==Public {
              Assembly not found
                local j = "Public Goods"
                }
              The issue is that it is not actually comparing two strings. It is trying to look for things (variables, scalars, matrices) called Assembly, which of course don't exist in memory. So at the very least, in your code you want to enclose your strings in double quotes, so they are evaluated as strings; in this way:

              Code:
              ... if "`i'" == "Public" ...
              Additionally: I understand this was probably dummy code created to demonstrate your problem on this forum, but please actually test that code before claiming that "
              Using the following code I get the following output", as you did in #1.

              Comment


              • #8
                #7 Hemanshu Kumar How did you test the code in #1 without a dataset?

                I strongly agree with your point that the code will fail if Stata can't find an entity with the variable or scalar name implied, which is also one that I made in #2.

                Comment


                • #9
                  Ah of course Nick, I see your point. If OP has a dataset in memory with those variables (Assembly, Corruption, et al), the code will run without error.

                  My bad. So yes, #7 can be ignored. But I think #6 will help OP solve the problem.

                  Comment


                  • #10
                    #6 certainly shows helpfully some technique in manipulating strings using macros.

                    When an OP titles a thread "automating graph titles" and starts

                    I am trying to automate labeling mutliple graphs.
                    I think we're getting a signal about the real problem and my main advice remains "use variable labels".

                    Comment


                    • #11
                      Thank to all. Putting "" around the local was the trick I needed. I think I am finally getting an understanding of this language structure. Thanks to all.

                      Comment

                      Working...
                      X