forked from rmcelreath/rethinking
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathz_link-map-quap.r
188 lines (154 loc) · 5.55 KB
/
z_link-map-quap.r
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# function to compute value of linear models from quap (formerly map) fit, over samples
setMethod("link", "map",
function( fit , data , n=1000 , post , refresh=0 , replace=list() , flatten=TRUE , debug=FALSE , ... ) {
if ( class(fit)!="map" ) stop("Requires map fit")
if ( missing(data) ) {
data <- fit@data
} else {
if ( debug==TRUE ) print(str(data))
}
if ( missing(post) )
post <- extract.samples(fit,n=n)
else {
n <- dim(post[[1]])[1]
if ( is.null(n) ) n <- length(post[[1]])
}
# replace with any elements of replace list
if ( length( replace ) > 0 ) {
for ( i in 1:length(replace) ) {
post[[ names(replace)[i] ]] <- replace[[i]]
}
}
nlm <- length(fit@links)
f_do_lm <- TRUE
if ( nlm==0 ) {
# no linear models!
# so need to flag to skip lm insertions into eval environment for ll
nlm <- 1
f_do_lm <- FALSE
}
link_out <- vector(mode="list",length=nlm)
# for each linear model, compute value for each sample
for ( i in 1:nlm ) {
ref_inc <- floor(n*refresh)
ref_next <- ref_inc
if ( f_do_lm==TRUE ) {
parout <- fit@links[[i]][[1]]
lm <- fit@links[[i]][[2]]
} else {
parout <- "ll"
lm <- "0"
# hacky solution -- find density function and insert whatever expression in typical link spot
flik <- as.character(fit@formula[[1]][[3]][[1]])
# mu for Gaussian
if ( flik=="dnorm" ) lm <- deparse( fit@formula[[1]][[3]][[2]] )
# p for binomial -- assume in third spot, after size
if ( flik=="dbinom" ) lm <- deparse( fit@formula[[1]][[3]][[3]] )
# lambda for poisson
if ( flik=="dpois" ) lm <- deparse( fit@formula[[1]][[3]][[2]] )
}
# empty matrix to hold samples-by-cases values of linear model
n_cases <- 0
dims <- dim( data[[1]] )
if ( length(dims)==1 ) n_cases <- dims[1]
if ( is.null(dims) ) n_cases <- length(data[[1]])
if ( length(dims)==2 ) {
# a matrix of samples?
if ( length(dims)==2 & dims[1]==n )
n_cases <- dims[2]
else
n_cases <- dims[1] # prob just a scaled var
}
value <- matrix(NA,nrow=n,ncol=n_cases)
# for each sample
idat <- data
for ( s in 1:n ) {
# refresh progress display
if ( refresh > 0 ) {
if ( s == ref_next ) {
msg <- paste( "[" , s , "/" , n , "]\r" , collapse=" " )
cat(msg)
ref_next <- s + ref_inc
if ( ref_next > n ) ref_next <- n
}
}
# make environment
init <- list() # holds one row of samples across all params
for ( j in 1:length(post) ) {
par_name <- names(post)[ j ]
dims <- dim( post[[par_name]] )
# scalar
if ( is.null(dims) ) init[[par_name]] <- post[[par_name]][s]
# 1d vector
if ( length(dims)==1 ) init[[par_name]] <- post[[par_name]][s]
# vector
if ( length(dims)==2 ) init[[par_name]] <- post[[par_name]][s,]
# matrix
if ( length(dims)==3 ) init[[par_name]] <- post[[par_name]][s,,]
}#j
# for data, check if any of it are samples (passed from sim maybe)
for ( j in 1:length(data) ) {
var_name <- names(data)[ j ]
dims <- dim( data[[var_name]] )
if ( length(dims)==2 ) {
# could be [cases,1] as a scaled var
if ( dims[1]==n & dims[2] > 1 )
idat[[var_name]] <- data[[var_name]][s,]
}
}#j
e <- list( as.list(idat) , as.list(init) )
e <- unlist( e , recursive=FALSE )
value[s,] <- eval(parse(text=lm),envir=e)
}#s
link_out[[i]] <- value
names(link_out)[i] <- parout
}
if ( refresh>0 ) cat("\n")
if ( flatten==TRUE )
if ( length(link_out)==1 ) link_out <- link_out[[1]]
return(link_out)
}
)
# method to take just a formula list - good for simulating from priors
setMethod("link", "list",
function( fit , data , n=1000 , post , refresh=0 , replace=list() , flatten=TRUE , ... ) {
# first do quap fit, but with dofit=FALSE
# just gives us the translated formula
fit <- quap( fit , data=data , dofit=FALSE )
# now extract prior
prior <- extract.prior(fit,n=n)
# run link
l <- link( fit , data=data , post=prior , n=n , refresh=refresh , replace=replace , flatten=flatten , ... )
return(l)
})
# TESTS
if (FALSE) {
library(rethinking)
data(chimpanzees)
fit <- map(
alist(
pulled.left ~ dbinom( 1 , p ),
logit(p) <- a + b*prosoc.left,
c(a,b) ~ dnorm(0,1)
),
data=chimpanzees,
start=list(a=0,b=0)
)
pred <- link(fit)
pred2 <- link(fit,data=list(prosoc.left=0:1),flatten=FALSE)
sim.pulls <- sim(fit,data=list(prosoc.left=0:1))
fit2 <- map2stan(
alist(
pulled.left ~ dbinom( 1 , p ),
logit(p) <- a + b*prosoc.left,
c(a,b) ~ dnorm(0,1)
),
data=list(
pulled.left=chimpanzees$pulled.left,
prosoc.left=chimpanzees$prosoc.left
),
start=list(a=0,b=0)
)
preds <- link(fit2)
preds2 <- link(fit2,data=list(prosoc_left=0:1))
}