Showing posts with label round(). Show all posts
Showing posts with label round(). Show all posts

Jun 19, 2017

Random graphs (99): Scatterplots

regress treatments index
local r2 = round(e(r2), .01) // Round coeff.
twoway (scatter treatments index) ///
       (lfit treatments index) ///
       (scatter treatments index if inlist(cntry, "IT", "AT", "UA", "BE", "DK", "RU"), msymbol(o)) ///
      , xtitle(ART comprehensive availability) ytitle("ART treatments" "per million women 15-44 y.") ///
        title("{bf:B}", justification(left) bexpand span) ///
        xlabel(0 (1) 9) ///
        text(1500 6.6 "Denmark") ///   
        text(1500 8.6 "Belgium") ///   
        text(470  0.4 "Italy") ///
        text(400  1.6 "Austria") ///
        text(120  7.5 "Ukraine") ///
        text(210  8.0  "Kazakhstan", placement(west)) ///
        text(150  8.0 "Russia", placement(east)) ///
     legend(order(2 "Linear fit R{char 178} = `r2'") ring(0) pos(7)) name(policy, replace)

Apr 4, 2016

Random graphs (70): Bar graphs for interaction plots

unzipfile "output7717034280080927434.zip", replace
use ESS1-6e01_0_F1, clear
*use cntry essround stfjbot wkhct using ESS1-6e01_0_F1, clear

// Select Round 5
keep if inlist(essround, 5)

// Fix country variable
encode cntry, gen(country)

// Satisfaction with WLB
rename stfjbot swlb

// Contracted working hours
recode wkhct (  0/20    = 1 "Marginal part-time") ///
             ( 21/34.75 = 2 "Substantial part-time") ///
             (35/220    = 0 "Full-time") ///
            , gen(parttime)
label var parttime "Working hours"

// Professional status
generate professional = .
replace  professional = (iscoco >= 1000 & iscoco <= 2999)
label var professional "Professional status"
label define professional 0 "Non-prof." 1 "Professional"
label val professional professional 

// Sex
recode gndr ( 2 =  1 "Female") ///
            ( 1 =  0 "Male") ///
            (.a = .a "No answer") ///
            , gen(female) label(female)
label var female "Gender"

// Select cases: Those in paid work and with a partner
keep if partner == 1 & mnactic == 1

// Are part-time workers more satisfied with their WLB than full-time employees?
qui regress swlb i.parttime i.country, cluster(country)
estimates store m1 

// Hypothesis 1b: The effect is stronger for marginal PT than substantial PT.
qui test 1.parttime == 2.parttime
local F = round(r(F), .001)
local p = round(r(p), .001)
local r = r(df)

qui margins, at(parttime=(0 1 2))
marginsplot, recast(bar) ///
             title("SWLB difference between full-time and part-time workers", size(large)) ///
             plotopts(fcolor(gs14) lcolor(black)) ytitle("Predicted SWLB") ylabel(6 (.5) 7.5, format(%6.1f) grid) ///
             name(noint, replace) xtitle("") ysize(3) ///
             note(" " ///
                  "Marginal and substantial part-time" ///
                  "differs significantly:" ///
                  "{it:F}(`r', `r(df_r)') = `F', {it:p} = `p'", ///
                  pos(11) ring(0) bmargin(small)) ///
             nodraw
    
// Are professional part-time workers less satisfied than non-professional part-time workers?
qui regress swlb i.parttime##i.professional i.country, cluster(country)
estimates store m2
qui margins, at(parttime=(0 1 2) professional=(0 1))
marginsplot, recast(bar) xdimension(professional) ///
             bydimension(parttime) byopts(row(1) noiyaxes imargin(zero) ///
    title("Interaction part-time status and professional status")) ///
    subtitle(, pos(6)) /// // Place label of by dimensions below plot 
             plotopts(fcolor(gs14) lcolor(black)) ytitle("Predicted SWLB") ylabel(6 (.5) 7.5, format(%6.1f)) ///
    name(int1, replace) xtitle("") ysize(3) nodraw
    
// Are women working part-time more satisfied with their SWLB than part-time working men?
qui regress swlb i.parttime##i.female i.country, cluster(country)
estimates store m3
qui margins, at(parttime=(0 1 2) female=(0 1))
marginsplot, recast(bar) xdimension(female) ///
             bydimension(parttime) byopts(row(1) noiyaxes imargin(zero) ///
    title("Interaction part-time status and gender")) ///
    subtitle(, pos(6)) /// // Place label of by dimensions below plot 
             plotopts(fcolor(gs14) lcolor(black)) ytitle("Predicted SWLB") ylabel(6 (.5) 7.5, format(%6.1f)) ///
    name(int2, replace) xtitle("") ysize(3) nodraw
// Output table and figure esttab m1 m2 m3 using test.tex, compress replace se label nomtitles /// indicate(Country dummies = *country) /// varwidth(30) interaction(" X ") /// title(Regression table\label{tab1}) /// booktabs graph combine noint int1 int2, col(1) ysize(9) ///
                               note("95% CI's based on cluster-robust standard errors")

Sep 3, 2015

Calculate inflection point of curvilinear relationships

// Generate data
clear
set seed 1
set obs 500
generate  e = rnormal(0,10)     
generate  x = rnormal(0,2)     
generate  y  = (-2*x + 2*(x*x) + e)
regress y c.x##c.x

matrix list e(b) // Find out names of coefficients

// Plot regression coefficients
coefplot, xline(0) ///
xtitle(" " "Regression coefficients and 95% CI's") ///
drop(_cons) scheme(s1mono) ///
coeflabels(x = "x" c.x#c.x = "x{char 178}") ///
ciopts(recast(rcap)) ///
headings(x = "U-shaped effect of x") ///
name(coeffs, replace)

// Calculate bend
local bend = round(-_b[x]/(2*_b[c.x#c.x]), .01)
display "b1 = " round(_b[x], .01) _newline ///
        "b2 = " round(_b[c.x#c.x], .01) _newline ///
        "-b1/(2*b2) = " `bend'

predict yhat  // Generate predicted values

// Plot results
twoway (line yhat x, sort(x)) ///
       (scatter y x) ///
   , legend(off) ///
    xline(`bend') ///
    ytitle("y") ///
    xtitle("x") ///
    name(scatter, replace) ///
    title("Inflection point at `bend'")
    



Dec 17, 2014

Random graphs (39): Plotting on a log axis


Rather than looking at a variable in absolute terms, namely GDP per capita in the upper panel of the Figure, it is also sometimes helpful to look at it in terms of percentage increases, as in the lower panel. Equal distances on the x-axis refer to equal percentage increases in GDP per capita. Four equidistant points on the x-axis are labeled, each indicating a fourfold increase in GDP.

use wvs2005_v20090901a.dta, clear

// Country variable
// 1) Turn into string
decode v2, gen(ctry)

// 2) Abbreviate
kountry ctry, from(other) stuck marker
ren _ISO3N_ cntry
kountry cntry, from(iso3n) to(iso2c)
ren _ISO2C_ country

// Generate outcome: % in good health
generate goodhealth = 100 if v11 <= 2
replace  goodhealth =   0 if v11  > 2
replace  goodhealth =   . if v11 == .

// Collapse data set
collapse (mean) goodhealth [pw = v259], by(country)

// Generate year variable
gen year = 2006

// Preserve collapsed data set
preserve

// Get GDP from World Bank data base
wbopendata, language(en - English) country() topics() indicator(NY.GDP.PCAP.PP.CD) clear long

// Fix obtained data set
ren ny_gdp_pcap_pp_cd gdp
ren iso2code country
keep if year == 2006
keep gdp country
drop if country == ""

// Save obtained GDP data
tempfile gdp
save `gdp'

// Restore 
restore

// Merge GDP with collapsed data set
merge m:1 country using `gdp'
keep if _merge == 3
drop _merge

// Create labels with thousand separator
label define gdp 20000 "20,000" ///
                 40000 "40,000" ///
                 60000 "60,000"
label val gdp gdp

// Plot on unlogged axis
twoway (scatter goodhealth gdp, mlabel(country) mlabpos(0) msymbol(none)) ///
       (lfit goodhealth gdp) ///
      , legend(off) xtitle("GDP per capita, 2006, PPP in current international $") ///    
        ytitle("% in good health") ///
        xlabel(0(20000)60000, valuelabels) ///
        name(unlogged, replace)

// Generate logged variable
gen loggdp = log(gdp) * 1000  // Multiply by 1,000 because only integers can be labeled

// Generate numbers for labeling
//  Round them to three decimal digits, then multiply by 1,000 to get integers
local log1 = round(log(1000), .001) * 1000
local log2 = round(log(1000 * 4), .001) * 1000
local log3 = round(log(1000 * 4 * 4), .001) * 1000
local log4 = round(log(1000 * 4 * 4 * 4), .001) *1000

*di `log1' _skip(2) `log2' _skip(2) `log3' _skip(2) `log4' 

// Create labels for numbers to be labeled
label define loggdp `log1' "1,000" ///
                    `log2' "4,000" ///
                    `log3' "16,000" ///                    
                    `log4' "64,000"
label value loggdp loggdp

// Plot on log axis
twoway (scatter goodhealth loggdp, mlabel(country) mlabpos(0) msymbol(none)) ///
       (lfit goodhealth loggdp) ///
      , legend(off) xtitle("GDP per capita, 2006, PPP in current international $") ///   
        ytitle("% in good health") ///
        xlabel(`log1' `log2' `log3' `log4', valuelabels) ///
        name(logged, replace)

// Combine plots
graph combine unlogged logged, col(1) ysize(8)

Oct 24, 2014

Random graphs (34): Country scatterplots

use ART.dta, replace

// Fix missing values
recode avgemtrans (-1 -3 = .)
recode emhum      (-1 = .)

// Calculate r-squared
qui regress avgemtrans emhum
local r2 = round(e(r2), .01) // Round coeff.

// Create first panel
twoway (scatter avgemtrans emhum, mlab(countrycode) mlabpos(0) msymbol(i)) ///
       (lfit avgemtrans emhum) ///
    , xlabel(, format(%6.1f)) ylabel(, format(%6.1f)) ///
    ytitle("Avg. no. of fresh non-donor embryos" "per fresh embryo transfer cycle") ///
    xtitle("Avg. level disagreement that" "embryo is a human being") ///
       legend(order(2 "Linear fit R{char 178} = `r2'") ring(0) pos(8)) ///
    name(figure2a, replace)

// Fix missing values
recode afford clonehelpinf (-1 = .)

// Listwise deletion to control y-axis range
preserve
keep if !missing(afford, clonehelpinf)

// Calculate r-squared    
regress afford clonehelpinf
local r2 = round(e(r2), .01) // Round coeff.

// Create second panel
twoway (scatter afford clonehelpinf, mlab(countrycode) mlabpos(0) msymbol(i)) ///
       (lfit afford clonehelpinf) ///
    , xlabel(, format(%6.1f)) ylabel(0 (5) 25, format(%6.1f)) ///
    ytitle("Affordability (net cost of a fresh ART cycle" "as % of annual disposable income)") ///
    xtitle("Avg. level disagreement with" "cloning to help infertile couples") ///
       legend(order(2 "Linear fit R{char 178} = `r2'") ring(0) pos(8)) ///
    name(figure2b, replace)
restore

// Create final Figure
graph combine figure2a figure2b, row(1)

Jul 9, 2013

Morgan and Winship's (2007) example for bias due to conditioning on a collider in Stata

Morgan and Winship (2007: p. 66) illustrate Pearl's (2009) concern about conditioning on a collider variable using a simple example.

The general problem of conditioning on a collider is as follows. Consider three variables A, B, and C, with both A and B being causes of C: A → C ← B. (Formally, any variable C that has two arrows pointing to it along a given path is a collider.) Unlike a confounder (an uncontrolled common cause of A and B), a collider does not induce a zero-order correlation between A and B. However, when handled inappropriately, a collider can induce a conditional correlation between A and B.

Morgan and Winship's example shows just that: A college admits applicants based on their SAT scores and ratings of their motivation based on an interview. Those in the top 15 per cent of the sum of SAT and motivation ratings are being admitted. SAT scores and motivation ratings are largely uncorrelated.

// Generate and label two variables
drawnorm sat motivation, ///
         n(250) ///
         means(.007, -.053) ///
         sds(1.01, 1.02) ///
         corr(1, .035, 1) cstorage(lower) ///
         clear seed(1)
label var sat        "SAT"
label var motivation "Motivation"

// Only the 15 per cent at the top are admitted
gen admission_sc = sat + motivation
_pctile admission_sc, percentiles(85)

gen admission = (admission_sc > r(r1))
label var admission "Admission status"
label define admission 1 "Admitted applicant" ///
                       0 "Rejected applicant"
label val admission admission
drop admission_sc

// Plot as in Morgan and Winship, p. 67:
twoway (scatter motivation sat if admission == 1) ///
       (scatter motivation sat if admission == 0) ///
       , ///
       legend(label(1 "Admitted applicants") ///
              label(2 "Rejected applicants") ///
              pos(5) ring(0)) ///
       ylabel(none) xlabel(none) ///
       name(collider1, replace)
// Enhanced plot with fitted lines and correlations
quietly cor motivation sat
local r_overall = round(r(rho), .01)
quietly cor motivation sat if admission == 1
local r_admitted = round(r(rho), .01)
quietly cor motivation sat if admission == 0
local r_rejected = round(r(rho), .01)
    
twoway (scatter motivation sat if admission == 1) ///
       (scatter motivation sat if admission == 0) ///
       (lfit motivation sat) ///
       (lfit motivation sat if admission == 1) ///
       (lfit motivation sat if admission == 0) ///
       , ///
       legend(label(1 "Admitted applicants") ///
              label(2 "Rejected applicants") ///
              label(3 "Overall fit, {it:r} = `r_overall'") ///
              label(4 "Fit for admitted, {it:r} = `r_admitted'") ///
              label(5 "Fit for rejected, {it:r} = `r_rejected'") ///
              pos(5) ring(0)) ///
       ylabel(none) xlabel(none) ytitle("Motivation")

What the Figures show is that the very small correlation between motivation and SAT score for the overall group turns out to be much larger when conditioning for admission status.

This also shows in an OLS regression:

regress motivation sat, beta
estimates store m1
regress motivation sat admission, beta
estimates store m2

estimates table m1 m2, b(%7.2f) se(%7.2f) stats(N) label

----------------------------------------------
                Variable |   m1        m2     
-------------------------+--------------------
                     SAT |    0.10     -0.21  
                         |    0.06      0.06  
        Admission status |              1.60  
                         |              0.17  
                Constant |   -0.12     -0.36  
                         |    0.06      0.06  
-------------------------+--------------------
                       N |     250       250  
----------------------------------------------
                                  legend: b/se

Cole et al. (2010) present additional illustrations for this problem.

References


Cole, Stephen R., Robert W. Platt, Enrique F. Schisterman, Haitao Chu, Daniel Westreich, David Richardson, and Charles Poole. 2010. "Illustrating Bias Due to Conditioning on a Collider." International Journal of Epidemiology 39(2):417-420. doi: 10.1093/ije/dyp334

Morgan, Stephen L., and Christopher Winship. 2007. Counterfactuals and Causal Inference. Methods and Principles for Social Research. Cambridge University Press.

Pearl, Judea. 2009. Causality. Models, Reasoning, and Inference, 2nd ed. Cambridge University Press.

Aug 9, 2012

Formatting text in Stata graphs

How can letters in Stata graphs be formatted?

Text in Stata graphs can be formatted according to the SMCL standards, for instance
sysuse auto 
regress mpg weight
local r_sq = round(e(r2_a), .01)
scatter mpg weight, title("Mileage {it:decreases} with weight") ///
                    text(30 4000 "R{sup:2} = `r_sq'")


Other useful SMCL tags for this purpose are bold text {bf}, superscripts {sup}, and subscripts {sub}. The Stata help for -text- has a full list here, including a list of greek, math, and other symbols.  Furthermore, the page explains that fonts in graphs can also be adjusted without having to change the entire -scheme- of the graph.

 

How can odd characters be included in Stata graphs?

Nonstandard characters like the en or em dash can also be included in graph text by using the {char} tag. {char} is followed by the ASCII (or rather ANSI/Windows-1252) code; so in order to obtain the ASCII-superscripted 2, one needs to write {char 178} or just {c 178} (no colon in between).

#delimit ;
scatter mpg weight, title("Mileage{char 151}
                           does it decrease with weight?")
                    text(30 4000 "R{char 178} = `r_sq'") ;
#delimit cr


The readymade superscript 2 actually seems to be a bit clearer than the 2 superscripted by Stata.

More details on displaying characters via ASCII codes can be found here.

Cox (2004) describes a somewhat more awkward way of using odd characters, namely by assigning them to locals first and then including the locals into the text strings.

 

How can a line break be included in a Stata graph?

For titles, creating a line break is rather straightforward. Simply put the text in two sets of double quotes " ", the line break will appear between those two line breaks:

#delimit ;
scatter mpg weight, title("Mileage:" 
                          "Does it decrease with weight?");
#delimit cr

However, when doing this in a label command, it will yield an error message:
option labels() incorrectly specified
        expects, # "label" # label ...  r(198);

Cox (2005) explains how this can be fixed. The intuition to use two sets of double quotes is also correct for labels in a graph. However, in order to have Stata understand this in a -label()- option, the two sets of double quotes need to be included in compound double quotes `" "'. Cox gives the following example:
graph hbar (mean) mpg, over(foreign, relabel(1 `" "Domestic" "cars" "' ///
                                             2 `" "Foreign" "cars" "'))

 

Reference

Cox, Nicholas J. 2004. "Stata Tip 6. Inserting Awkward Characters in the Plot." Stata Journal 4(1):95-96.

Cox, Nicholas J. 2005. "Stata Tip 24. Axis Labels on Two or More Levels." Stata Journal 5(3):469-469.