In Stata it is possible to add custom markers to graphs. This feature, besides labeling points, can also be used to show direction of change via arrows or other custom markers.

In order to demonstrate this, let’s generate a simple graph in Stata using a straight line:

clear

gen x = .

gen y = .

replace x = 0 in 1

replace x = 10 in 2

replace y = 0 in 1

replace y = 10 in 2

twoway (line y x)

This gives us the following graph:

We can now define a marker to be only shown at the end of the line as follows:

gen dot = 1 if x==10

twoway (line y x) (scatter y x if dot==1, mcolor(black) msize(large) msymbol(arrow)), legend(off)

where arrow is a Stata defined symbol for makers and is show on the top-right corner:

In the next step, we need to define the angle of this arrow to match the direction of the line. Angles are determined via the *atan2 *function which takes a pair of coordinates in the Cartesian plain and returns the angle in radians.

For example, in the circle above, let’s assume a point A with coordinates lies in the center of the circle, while another point B with coordinates lies somewhere on the edge of the circle. Then the angle is defined as:

(1)

Once the angle is recovered in radians, it needs to be converted into degrees, which is the value Stata needs to determine the angle of markers. This can be achieved as:

(2)

Since the figure we drew above is basically a 45 degree line, we can see that the angle calculated by Stata is also 45 degrees:

gen angle = atan2(10, 10) if dot==1

gen angledeg = angle * (180 / _pi)

We can now define a local for the angle and use it in the graph dynamically:

summ angeldeg if dot==1

local ang = `r(mean)'

twoway (line y x) (scatter y x if dot==1, mcolor(black) msize(large) msangle(`ang') msymbol(arrow)), legend(off)

which gives us the following figure:

In the Figure above we notice one thing, even though the arrow’s angle is correctly defined as 45 degrees, it is not perfectly aligned with the straight line. This is because angles in Stata are sensitive to the dimensions of the graph. If we force this graph into a square dimension:

summ angeldeg if dot==1

local ang = `r(mean)'

twoway (line y x) (scatter y x if dot==1, mcolor(black) msize(vlarge) msangle(`ang') msymbol(arrow)), ///

legend(off) xsize(3) ysize(3)

we now see that the arrow fits perfectly on the line:

Since not all graphs can be made into squares, this can be easily correctly by modifying the calculation of the angle based on a custom set of dimensions (we still need to take control of the dimensions in order to estimate the correct angle). This is done as follows:

gen angle2 = atan2(10*4, 10*3) if dot==1

gen angledeg2 = angle2 * (180 / _pi) if dot==1

summ angledeg2 if dot==1

local ang2 = `r(mean)'

twoway (line y x) (scatter y x if dot==1, mcolor(black) msize(vlarge) msangle(`ang2') msymbol(arrow)), ///

legend(off) xsize(4) ysize(3)

Which will give us the correct angle in a 4:3 ratio graph:

This generic example can be extended to include a bunch of lines, custom markers and angles, and can also be made to dynamically change as the lines evolve. More on this later!