Sunday, April 14, 2013

Methodological update

The publication of a Galaxy poll in today's Murdoch tabloids allows me to explain how I have updated my Bayesian models to handle primary vote predictions from the polling agencies.

We will begin with the primary vote estimate. I have rewritten my TPP JAGS model for this purpose. The TPP model was a fairly simple affair; it was only populated with one dimensional arrays, In contrast, the primary vote model is populated with a number of two-dimensional matrices (I was seeking a more integrated solution than the one I have got working).

In the process of testing the new model, I discovered that the element of the model that allowed for rounding errors accounted for about half the reason why the model was not adding to 100 percent. So I have deleted this element from both the Primary and TPP models. This deletion means the line produced by the model is a little more "noisy" than the line from the earlier model.

While there are still issues with the model adding to 100 per cent, it typically hovers between 99.5 and 100.5. I am looking for strategies to constrain the model to add to 100 per cent. If you have any suggestions, I would gladly welcome them.

The primary voting model is as follows.

    model {
        ## -- observational model - note: rounding commented out ...
        for(poll in 1:NUMPOLLS) { # for each poll result - rows
            for(party in 1:PARTIES) { # for each party - columns
                #roundingEffect[poll, party] ~ dunif(-houseRounding[poll], houseRounding[poll])
                yhat[poll, party] <- houseEffect[house[poll], party] + walk[pollDay[poll], party] #+ 
                    #roundingEffect[poll, party]
                primaryVotes[poll, party] ~ dnorm(yhat[poll, party], precision[poll, party])
            }
        }
            
        ## -- temporal model
        for (party in 1:PARTIES) { # columns
            for(day in 2:PERIOD) { # rows
                walk[day, party] ~ dnorm(walk[day-1, party], walkPrecision[party])
            }
        }

        ## -- sum-to-zero constraint on house effects (ignoring Morgan F2F)
        for (party in 1:PARTIES) { # for each party
            houseEffect[1, party] <- -sum( houseEffect[2:HOUSECOUNT, party] ) + 
                houseEffect[MORGANF2F, party]
        }
        
        ## -- constrained priors for the daily walk
        for(party in 1:PARTIES) { # for each party
            sigmaWalk[party] ~ dunif(0, 0.01)                ## uniform prior on std. dev.  
            walkPrecision[party] <- pow(sigmaWalk[party], -2)## for the day-to-day random walk
        }

        ## -- uninformative priors for daily walk
        for (party in 1:PARTIES) { # for each party
            walk[1, party] ~ dunif(0.0, 1.0) # completely uninformative 
        }

        ## -- vague normal priors for house effects
        for (party in 1:PARTIES) { # for each party (cols)
            for(house in 2:HOUSECOUNT) { #  (rows)
                houseEffect[house, party] ~ dnorm(0, pow(0.1, -2))
            }
        }
    }

The results from the model are as follows:





The underlying house effects were as follows.





A couple of diagnostic charts follow.




The TPP model is only changed a little. I made the prior for the daily walk the uniform distribution between 0 and 100%. And I have removed the the element of the model that dealt with rounding adjustments from the pollster. The results are as follows:






No comments:

Post a Comment