# Post 6: Refactoring Part II: a generic proposal function

**Markov Chain Monte Carlo in Item Response Models**, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)

Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

In this post we refactor the proposal function from the previous post into a generic normal proposal function. This allows us to implement normal proposals for the (yet to be developed) samplers for the item parameters without duplicating code.

Our approach is to write a function which returns a function. We begin with a toy example to explain why that would work, implement a function that returns a normal proposal function, and then check that the refactored function works.

# The toy example

Writing functions that return functions sounds strange. Here we give a toy example to explain how it works.

Pretend that we want to write two similar functions for comparing numbers:

## A function to check if the parameter is less than three is.three.less.than <- function( b.value ) { return( 3 < b.value ) } ## A very similar function is.two.less.than <- function( b.value ) { return( 2 < b.value ) } ## Testing them out c( is.three.less.than(2), is.three.less.than(4), is.two.less.than(1), is.two.less.than(3) ) ## [1] FALSE TRUE FALSE TRUE ## ## As expected.

R is designed to allow us to make a function which can build other functions. For example, we can make an "is X less than" function that takes X as a variable and returns a function like the `is.three.less.than`

function above:

## We can write a function in R to build those other functions is.X.less.than <- function( X ) { ## Return return( ## a function function( b.value ) { ## that depends on the value of X return( X < b.value ) } ) }

Now we can use `is.X.less.than`

to define our functions from before, which still work the same:

## Define them is.three.less.than <- is.X.less.than( 3 ) is.two.less.than <- is.X.less.than( 2 ) ## Use them c( is.three.less.than(2), is.three.less.than(4), is.two.less.than(1), is.two.less.than(3) ) ## [1] FALSE TRUE FALSE TRUE ## ## As expected.

### For the curious: Why does that even work?

This works because functions in R are implemented as a kind of paired object called a closure. A closure contains both the "text" of the function to be run and an "environment" which can contain additional variables.

In the example above, the `is.X.less.than`

function returned a closure which contained both the "text" of `function( b.value ) { return( X < b.value )}`

and the value of `X`

. To see this for our example, we simply call the generated `is.three.less.than`

function without parenthesis:

is.three.less.than ## function( b.value ) { ## return( X < b.value ) ## } ## < environment: 0x3489ee8 >

And peek inside the environment with the `ls(...)`

function, to see that it does contain `X`

:

ls( envir=environment(is.three.less.than) ) ## [1] "X"

And even see what `X`

is set to in the environment of `is.three.less.than`

:

environment(is.three.less.than)$X ## [1] 3

Similarly, for the other function, we get

environment(is.two.less.than)$X ## [1] 2

as expected.

# A function to return a proposal function

Recall that in post 5 we defined the person ability proposal function to be:

## Proposal function for the person ability parameters prop.th.abl <- function( state ) { ## Extract parameters from the state ## ... Previously: ## th.old <- old$th ## MH.th <- old$MH$th ## ... Now: th.old <- state$th MH.th <- state$MH$th ## Draw the proposal to update the state ## ... Previously: ## P.persons <- length(th.old) ## th.star <- rnorm(P.persons,th.old,MH.th) ## ... Now: state$th <- rnorm( length( th.old ), mean=th.old, sd=MH.th ) ## Build a function to return the density of the proposal ## ... Previously: ## log.prop.star <- log(dnorm(th.star,th.old,MH.th)) ## log.prop.old <- log(dnorm(th.old,th.star,MH.th)) ## ... Now, we define the function log.density <- function( state.to, state.from ) { dnorm( x = state.to$th, mean = state.from$th, sd = MH.th, log = TRUE ) } ## Return the proposal object ## ... Note that we'll need the name of the parameter later, ## so return it too return( list( param.name='th', state=state, log.density=log.density )) }

If we were okay with duplicating the code, we could copy the text of `prop.th.abl`

and change all of the names from depending on `th`

to depending on the name of the other parameter (e.g. `a`

).

Instead, we shall write a function which takes the name of the parameter as a variable and returns the equivalent function. Here we interleave comments for what the person ability version would have been:

## Write a function to return a proposal function for that parameter generic.normal.proposal <- function( param.name ) { return( function( state ) { ## Extract parameters from the state param.old <- state[[param.name]] ## th.old <- state$th MH.param <- state$MH[[param.name]] ## MH.th <- state$MH$th ## Draw the proposal to update the state state[[param.name]] <- rnorm( length( param.old ), mean=param.old, sd=MH.param ) ## state$th <- rnorm( ## length( th.old ), ## mean=th.old, ## sd=MH.th ) ## Build a function to return the density of the proposal log.density <- function( state.to, state.from ) { dnorm( x = state.to[[param.name]], ## state.to$th, mean = state.from[[param.name]], ## state.from$th, sd = MH.param, ## MH.th, log = TRUE ) } ## Return the proposal object return( list( param.name=param.name, state=state, ## list( param.name='th', state=state, ## log.density=log.density )) } ) }

Then implementing the refactored proposal function is simple:

prop.th.abl.refactor <- generic.normal.proposal('th')

# Testing the refactored code

Here we see if the sampler will produce the same output with the refactored code in place.

## Load the necessary code from this and previous posts source('http://mcmcinirt.stat.cmu.edu/setup/post-6.R') ## Check that we have the version from Post 4. head(prop.th.abl) ## 1 function (state) ## 2 { ## 3 th.old <- state$th ## 4 MH.th <- state$MH$th ## 5 state$th <- rnorm(length(th.old), mean = th.old, sd = MH.th) ## 6 log.density <- function(state.to, state.from) { ## Set the seed and run the sampler for ten iterations set.seed(314159) run.post4 <- run.chain.2pl( M.burnin = 0, M.keep = 10, M.thin = 1, U.data = U, hyperpars = hyperpars, th.init = runif( length(theta.abl), min=-5, max=5 ), a.init = a.disc, b.init = b.diff, s2.init = sig2.theta, MH.th=1, MH.a=NULL, MH.b=NULL) ## Swap in the refactored proposal prop.th.abl.post4 <- prop.th.abl prop.th.abl <- prop.th.abl.refactor ## Set the seed and run the sampler for ten iterations set.seed(314159) run.refactor <- run.chain.2pl( M.burnin = 0, M.keep = 10, M.thin = 1, U.data = U, hyperpars = hyperpars, th.init = runif( length(theta.abl), min=-5, max=5 ), a.init = a.disc, b.init = b.diff, s2.init = sig2.theta, MH.th=1, MH.a=NULL, MH.b=NULL) ## Check that they return the same values all.equal( run.post4, run.refactor ) ## [1] TRUE

That the output is identical suggests that the code was refactored correctly.

**leave a comment**for the author, please follow the link and comment on their blog:

**Markov Chain Monte Carlo in Item Response Models**.

R-bloggers.com offers

**daily e-mail updates**about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.

Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.