Announcement

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

  • number of elements in macro created by levelsof

    Dear statalisters,

    Have you noticed this behavior of levelsof: the number of elements in the macro it creates and in r(levels) is one more than it should be. This can create errors and undesirable consequences when looping over all elements of that macro. Or if one wants to assign the contents of that macro to be rownames or colnames of a matrix, one gets conformability error.

    Is this intended? If yes, how can one drop the last blank element?

    Code:
    * gen 4 obs
    capture set obs 4
    gen var_num = _n
    
    * see how levelsof processes these 4 obs
    levelsof var_num, l(values_num)
    * count number of elements in the local created by levelsof
    local count_values_num: word count of `values_num'
    di "Number of numeric values: `count_values_num'"
    * display each element in turn
    forvalues i = 1/`count_values_num' {
    local l: word `i' of `values_num'
    di "Word `i' of values_num: `l'"
    }
    *
    
    * what happens when the variable is type string
    
    * label the numeric variable and then decode it
    label define var_num_label 1 "A X" 2 "B Y" 3 "C Z" 4 "D W"
    label values var_num var_num_label
    decode var_num, g(var_str)
    
    * levelsof with default compound quotes
    levelsof var_str, l(values_str)
    local count_values_str: word count of `values_str'
    di "Number of string values: `count_values_str'"
    * display each element in turn
    forvalues i = 1/`count_values_str' {
    local l: word `i' of `values_str'
    di "Word `i' of values_str: `l'"
    }
    *
    
    * beware of option clean when spaces in strings
    * levelsof, clean, i.e. no compound quotes
    levelsof var_str, l(values_str_cl) clean
    local count_values_str_cl: word count of `values_str_cl'
    di "Number of string values: `count_values_str_cl'"
    * display each element in turn
    forvalues i = 1/`count_values_str_cl' {
    local l: word `i' of `values_str_cl'
    di "Word `i' of values_str_cl: `l'"
    }
    *
    
    * last, try to use the contents of levelsof to assign names of the rows or columns of a matrix
    
    mat C = (1,2,3,4\5,6,7,8\9,11,22,33\44,55,66,77)
    mat colnames C = "(1)" "(2)" "(3)" "(4)" 
    mat rownames C = "row 1" "row 2" "row 3" "row 4"
    matlist C
    mat rownames C = "`values_str'"

  • #2
    Hi,

    as far as I can see, Stata does exactly what you tell it to do.
    There are three issues here, I think: (1) wrong syntax in counting elements of a list, (2) using forvalues instead of foreach and (3) wrong quoting when naming matrix rows in Mata.

    (1)
    In your example, levelsof behaves just as to be expected, I think. However, in creating your forvalues-loop, you repeatedly introduce an error: It should read
    Code:
    local count_values_num: word count `values_num'
    instead of
    Code:
    local count_values_num: word count of `values_num'
    When you read help extended_fcn carefully, you will see that this is the correct syntax -- and your syntax additionally counts the word "of" you introduced. Consequently, your code iterates over 5 words, but your local macro only contains 4, and in the last iteration Stata (correctly) returns an empty result.


    (2)
    Anyways, I would recommend using foreach instead of forvalues here (this avoids pitfalls in counting elements manually):
    Code:
    * gen 4 obs
    capture set obs 4
    gen var_num = _n
    
    * see how levelsof processes these 4 obs
    levelsof var_num, l(values_num)
    * display each element in turn
    foreach i of local values_num {
        di "Next word of values_num: `i'"
    }

    (3)
    Your final conformability error at the end, comes from using quotes where you must not: You are using already-quoted elements (as saved in your local macro `values_str') to name matrix rows. You put quotes around it, leading to Stata seeing this command:
    Code:
    mat rownames C = "`"A X"' `"B Y"' `"C Z"' `"D W"'"
    What you want to achieve is this:
    Code:
    mat rownames C = `"A X"' `"B Y"' `"C Z"' `"D W"'
    Thus, remove the double quotes around "`values_str'" and you're fine:
    Code:
    mat rownames C = `values_str'
    This, however, only works as soon as the loops in the example blocks are fixed.

    Regards
    Bela
    Last edited by Daniel Bela; 25 Aug 2017, 05:54. Reason: added paragraph about the conformability issue

    Comment


    • #3
      Thank you, Bela,

      this "of" must have stayed in my brain from the syntax in : word # of macroname. Also thank you for catching the double quotes in the rownames assignment.

      There is nothing wrong in the loops, once the pesky "of" is removed. I agree that if I had used foreach i of local values_str instead of forvalues = 1/`count_values_str', I would have caught the problem myself.

      Thanks again,
      Maria

      Comment

      Working...
      X