luchonacho
luchonacho

Reputation: 7167

Iterate over list produced with levelsof

The example below reproduces my problem. There is a string variable which takes several values. I want to create a global list and iterate over it in a loop. But it does not work. I've tried several versions without success. Here is the example code:

webuse auto, clear
levelsof make // list of car makes
global MAKE r(levels) // save levels in global list

foreach i in $MAKE { // loop some command over saved list 
    sum if make == "`$MAKE'"  // ERROR 198, invalid 'Concord' 
}

Using "`$MAKE'" or $MAKE does not yield desired output.

Any ideas of what am I doing wrong?

Normally, for lists to work, they should be saved as in A B C D [...]. In my case, levelsof produces a list of the following kind:

di $MAKE
`"AMC Concord"' `"AMC Pacer"' `"AMC Spirit"' `"Audi 5000"' `"Audi Fox"' `"BMW 320i"' [...]

So clearly not what is needed. But not sure how to get what I need.

Upvotes: 1

Views: 1695

Answers (2)

TheIceBear
TheIceBear

Reputation: 3255

Here is a solution. Note that I am using a local instead of a global. The difference is only scope. Only use global if you need to reference the value across do-files. You can remove the display lines below.

*Sysuse reads this data from disk, it comes with all Stata installations
sysuse auto, clear

*Use levelsof, and assign the returned r(levels) using a = to the local
levelsof make
local all_makes = r(levels)

*Loop over the local like this. Note that foreach creates a local, in this
*case called this_make that stores the elements in the local one per iteration
foreach this_make of local all_makes { 
    display "`this_make'"
    sum if make == "`this_make'"
}

If global is what you need, then you simply change it to this:

*Sysuse reads this data from disk, it comes with all Stata installations
sysuse auto, clear

*Use levelsof, and assign the returned r(levels) using a = to the global
levelsof make
global all_makes = r(levels)

*Loop over the global like this. Note that foreach creates a local, in this
*case called this_make that stores the elements in the global one per iteration
foreach this_make of global all_makes { 
    display "`this_make'"
    sum if make == "`this_make'"
}

Upvotes: 2

Nick Cox
Nick Cox

Reputation: 37208

There is a fine accepted answer but plenty more can be said. See for example this FAQ.

I am positive about levelsof as its original author, but for the purpose specified, to loop over the levels of a variable, it can be a lot cleaner to use egen, group() and loop over the integer levels of that variable. See the FAQ just linked for more. The example in the original question is a case in point, as looping over distinct string values can be tricky with a need to use double quotes " " and to watch out for spaces and so forth.

The underlying problem is not revealed but an extra comment is to underline that by: and its sibling commands such as statsby or commands similar in spirit such as rangestat from SSC offer, in effect, looping without looping.

Upvotes: 1

Related Questions