Stata: replace, if, forvalues

送分小仙女□ 提交于 2019-12-24 07:13:33

问题


use "locationdata.dta", clear
gen ring=.
* Philly City Hall
gen lat_center = 39.9525468
gen lon_center = -75.1638855
destring(INTPTLAT10), replace
destring(INTPTLON10), replace
vincenty INTPTLAT10 INTPTLON10 lat_center lon_center , hav(distance_km) inkm
quietly su distance_km
local min = r(min)
replace ring=0 if (`min' <= distance_km < 1)
local max = ceil(r(max))
* forval loop does not work
forval i=1/`max'{
    local j = `i'+1
    replace ring=`i' if (`i' <= distance_km < `j')
}

I am drawing rings by 1 km from a point. The last part of the code (forval) does not work. Something wrong here?

EDIT:

The result for the forval part is as follows:

. forval i=1/`max'{
2.         local j = `i'+1
3.         replace ring=`i' if `i' <= distance_km < `j'
4. }
(1746 real changes made)
(0 real changes made)
(0 real changes made)
(0 real changes made)
....

So, replacing does not work for i = 2 and beyond.


回答1:


A double inequality such as

(`min' <= distance_km < 1)

which makes sense according to mathematical conventions is clearly legal in Stata. Otherwise, your code would have triggered a syntax error. However, Stata does not hold evaluation until the entire expression is evaluated. The parentheses here are immaterial, as what is key is how their contents are evaluated. As it turns out, the result is unlikely to be what you want.

In more detail: Stata interprets this expression from left to right as follows. The first equality

`min' <= distance_km

is true or false and thus evaluated as 0 or 1. Evidently you want to select values such that

distance_km >= `min' 

and for such values the inequality above is true and returns 1. Stata would then take a result of 1 forward and turn to the second inequality, evaluating

1 < 1 

(i.e. result of first inequality < 1), but that is false for such values. Conversely,

(`min' <= distance_km < 1)

will be evaluated as

 0 < 1 

-- which is true (returns 1) -- if and only if

 `min' > distance_km 

In short, what you intend would need to be expressed differently, namely by

  (`min' <= distance_km) & (distance_km < 1)

I conjecture that this is at the root of your problem.

Note that Stata has an inrange() function but it is not quite what you want here.

But, all that said, from looking at your code the inequalities and your loop both appear quite unnecessary. You want your rings to be 1 km intervals, so that

 gen ring = floor(distance_km) 

can replace the entire block of code after your call to vincenty, as floor() rounds down with integer result. You appear to know of its twin ceil().

Some other small points, incidental but worth noting:

  1. You can destring several variables at once.

  2. Putting constants in variables with generate is inefficient. Use scalars for this purpose. (However, if vincenty requires variables as input, that would override this point, but it points up that vincenty is too strict.)

  3. summarize, meanonly is better for calculating just the minimum and maximum. The option name is admittedly misleading here. See http://www.stata-journal.com/sjpdf.html?articlenum=st0135 for discussion.

As a matter of general Stata practice, your post should explain where the user-written vincenty comes from, although it seems that is quite irrelevant in this instance.

For completeness, here is a rewrite, although you need to test it against your data.

 use "locationdata.dta", clear
 * Philly City Hall
 scalar lat_center = 39.9525468
 scalar lon_center = -75.1638855
 destring INTPTLAT10 INTPTLON10, replace
 vincenty INTPTLAT10 INTPTLON10 lat_center lon_center , hav(distance_km) inkm
 gen ring = floor(distance_km)


来源:https://stackoverflow.com/questions/19716527/stata-replace-if-forvalues

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!