---
title: "DiBello Models"
author: "Russell Almond"
date: "March 29, 2019"
output: pdf_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
library(arm)
```
## Parameterized Bayesian Networks
* In a discrete Bayesian Network, the _parameters_ are the _conditional probability tables_ (CPTs).
* Size of CPT grows exponentially with number of parents.
* In educational models, CPTs should be monotonic: higher skill states should imply higher probability of success.
* When learning CPTs from data, if skill variables are correlated certain combinations will be rare in data:
+ Skill 1 is high and Skill 2 is low
+ Skill 2 is low and Skill 1 is high
+ This makes for low effective sample size (high standard errors) when estimating CPTs from data.
# Conditional Probability Tables
## Lets Make a some Simple CPTs
Load PNetica (which loads Peanut and CPTtools), and start session.
Build a blank Network.
```{r launch PNetica}
library(PNetica)
sess <- NeticaSession()
startSession(sess)
tNet <- CreateNetwork("tNet",sess)
```
Let following packages are loaded.
* `CPTtools` -- Tools for building Conditional Probability Tables.
* `RNetica` -- Interface to Netica
* `Peanut` -- Object-oriented parameterized network protocol.
* `PNetica` -- Peanut implementation for Netica
## Create a Simple Network.
Create two parent nodes: Skill1 and Skill2, and a child node, CRItem.
```{r create nodes}
Skills <- NewDiscreteNode(tNet,paste("Skill",1:2,sep=""),c("H","M","L"))
CRItem <- NewDiscreteNode(tNet,"CRItem",c("FullCredit","PartialCredit","NoCredit"))
NodeParents(CRItem) <- Skills
NodeParents(Skills[[2]]) <- Skills[1]
```
## The Shape of CPTs
Skill1 has no parents, so unconditional CPT.
```{r Skill1}
Skills[[1]][]
```
Node that in RNetica the `[]` operator can be used to access the CPT of a node.
Skill2 has one parent, so conditional CPT.
```{r Skill2}
Skills[[2]][]
```
CRItem has two parents, for total of nine (3x3) rows.
```{r CRItem}
CRItem[]
```
## The [<- operator for NeticaNodes
RNetica maps the assignment operator for `[]` (`[<-`) to provide a wide variety of behaviors.
Both `[` and `[<-` allow the user to specify a specific row or cell, or group of rows and cells. This is similar, but not quite the same as the `[` operator behavior for matrixes and data frames.
Single Row:
```{r Single Row}
CRItem[Skill1="H",Skill2="H"] <- c(.7,.2,.1)
CRItem["H","H"]
```
Multiple Rows:
```{r Multiple Row}
CRItem[Skill2="M"] <- c(.25,.5,.25)
CRItem["M",c("H","M")] <-c(.25,.5,.25)
CRItem[,"M"]
CRItem["M",]
```
Fill out a conjunctive model.
```{r df selection}
CRItem["L",] <- c(.1,.2,.7)
CRItem[,"L"] <- c(.1,.2,.7)
CRItem[]
```
## Conditional Probability Frames and Conditional Proability Arrays
When there are two or more parent variables, there are two possible views of the CPT:
* _Conditional Probability Frame_ (CPF) which is a data frame where rows represent configurations of parent variables.
+ First $p$ rows represent parent configuration
+ Last $|States|$ rows represent child states.
+ Numeric part is the conditional probability table
- Note: `calcXXXFrame()` and `calcXXXTable()` methods in CPTtools
+ CPFs can be used on the RHS of `[<-` operator for NeticaNodes, to set the CPT.
* _Conditional Probability Array_ (CPA) which is $p+1$ dimenional array.
The functions `as.CPF` and `as.CPA` convert back and forth between the two views:
```{r CPA}
as.CPA(CRItem[])
```
## Graphing a conditional probability table.
The function `barchart.CPF` (which extends the lattice function `barchart`) will build a visualization of the CPF.
The `baseCol` argument can be any R color specification, it is then used as the base color for the graph.
```{r barchart}
barchart.CPF(CRItem[],baseCol="chocolate")
```
## How far have we come?
* The `[]` function for NeticaNodes is a very convenient way for accessing and manipulating CPTs
* It uses a data-frame representation, the _CPF_
* If we can make a CPF, we can set the table for a node.
* The package `CPTtools` is all about making CPFs!
# The DiBello Models
## A Short History
* When building CPT for Biomass, Lou DiBello had an idea.
* Map each row of the CPT onto an _effective Theta_
* Then use IRT model (Samejima's Graded Response) to calculate CPTs for each row.
* For multivariate parents, use a _structure function_ or _combination rule_ to combine indivitual effective thetas for each parent into a single effective theta.
+ _Compensatory_: (weighted) average of parents
+ _Conjunctive_: minimum of parents
+ _Disjunctive_: maximum of parents
## The DiBello procedure
1 Map the states of the parent variables onto the standard normal (theta) scale.
2 Combine the parent variables using a _combination rule_ to create a single effective theta for each row.
+ The combination rule generally has slope (discrimination) parameters ($\alpha$'s or $a$'s)
+ The combination rule generally has difficulty (intercept) parameters ($\beta$'s or $b$'s)
+ Some rules (e.g., `Compensatory`) have multiple-a's, some (e.g., `OffsetConjuctive`) have multiple-b's
+ Some link functions allow (or even require) different b's for each state of the child variable (step difficulties).
+ The partial credit link function allows different a's as well.
3 Convert the effective thetas to conditional probablities using a _link function_ (IRT-like models)
+ `gradedResponse` -- Lou's original suggestion
+ `partialCredit` -- More flexible alternative
+ `normalLink` -- Regression-like model for proficiency variables.
- Requires a _link scale parameters_
The function `calcDPCFrame` in the CPTtools package does the work. (DPC = Discrete Partial Credit)
## Mapping Parent States onto the Theta Scale
* Effective theta scale is a logit scale corresponds to mean 0 SD 1 in a “standard” population.
* Want the effective theta values to be equally spaced on this scale
* Want the marginal distribution implied by the effective thetas to be uniform (unit of the combination operator)
*What the effective theta transformation to be effectively invertible (this is reason to add the 1.7 to the IRT equation).
## Equally spaced points in Normal Measure
* Assume variable has $M$ states: $0, \ldots, M-1$
* Region $m$ will have lower bound $m/M$ quantile and upper bound at $(m+1)/M$ quantile.
* Midpoint will be at $(m+\frac{1}{2})/M$ quantile
```{r normal quantiles, echo=TRUE}
states <- c("Low","Medium","High")
M <- length(states)
c <- qnorm((1L:(M-1L))/M,0,1)
theta <- qnorm((1L:M -.5)/M,0,1)
curve(dnorm(x),xlim=c(-3,3),xaxt="n",yaxt="n",ylim=c(0,.5),col="red",
ylab="",xlab="Effective Theta")
segments(c,-.1,c,.6)
segments(theta,-.1,theta,dnorm(theta))
text(theta,.45,states)
axis(1,theta, do.call(expression,
lapply(1:M, function (m)
substitute(tilde(theta)[m],list(m=m)))))
```
The function `effectiveThetas` calculates this.
```{r effective Theta}
effectiveThetas
effectiveThetas(3)
## We will need this for building CPTs later.
NodeLevels(Skills[[1]]) <- effectiveThetas(PnodeNumStates(Skills[[1]]))
NodeLevels(Skills[[2]]) <- effectiveThetas(PnodeNumStates(Skills[[2]]))
```
## Combination Rules
* `Compensatory` – more of one skill compensates for lack of another
* `Conjunctive` – weakest skill dominates relationship
* `Disjunctive` – strongest skill dominates relationship
* Inhibitor – minimum threshold of Skill 1 needed, then Skill 2 takes over (special case of conjuctive)
Multi-b rules:
* `OffsetConjunctive` – like conjunctive model, but with separate $b$’s for each parent instead of separate $a$’s
* `Offset Disjunctive` – like disjunctive rule, but with separate $b$’s for each parent instead of separate $a$’s.
## Compensatory Rule
* Weighted average of inputs
* One $\alpha$ (slope) for each parent variable ($k$) and state ($s$): $\alpha_{k,s}$
* One $\beta$ (difficulty) for each state of the child variable (except the last): $\beta_s$
$$ \tilde\theta = \frac{1}{\sqrt{K}} \sum_k \alpha_{k,s},\tilde\theta_{k,m_k} - \beta_s$$
* Factor $1/\sqrt{K}$ is a variance stabilization term. (Make variance independent of number of parents.)
## Conjunctive and Disjunctive Rules (Multi-a)
* Replace sum (and square root of K) with `min` or `max`
* Conjunctive: All skills needed; weakest skill dominates
$$ \tilde\theta = \min_k \alpha_{k,s}\tilde\theta_{k,m_k} - \beta_s$$
* Disjunctive: Any skills needed; strongest skill dominates
$$ \tilde\theta = \max_k \alpha_{k,s}\tilde\theta_{k,m_k} - \beta_s$$
* Not sure what the different slopes mean in this context
## Conjunctive and Disjunctive Rules (Multi-b)
* Different skills may have different demands in a task
+ Skill 1 must be high, but Skill 2 only medium
* Model this with different difficulties ($b$'s) for each parent skill.
* OffsetConjunctive: All skills needed; weakest skill dominates
$$ \tilde\theta = \alpha_{s}\min_k\left(\tilde\theta_{k,m_k} - \beta_{k,s} \right)$$
* Disjunctive: Any skills needed; strongest skill dominates
$$ \tilde\theta = \alpha_{s}\max_k\left(\tilde\theta_{k,m_k} - \beta_{k,s} \right)$$
## Implementation in CPTtools
* `Compensatory`, `Conjunctive`, `Disjunctive`, `OffsetConjunctive` and `OffsetDisjunctive` are implemented as functions in CPTtools
+ This set is expandable by adding new functions with the same signature
* The function `eThetaFrame` demonstrates how this works.
* Note: Uses $\log(\alpha)$ rather than $\alpha$ as slope parameter.
```{r compensatory eTheta, echo=TRUE}
eThetaFrame(ParentStates(CRItem),log(c(Skill1=1.2,Skill2=.8)),0,
Compensatory)
```
Try changing the slopes and intercepts
## Offset Style Rules:
Almost the same, except now we expect beta to be a vector instead of alpha.
```{r conjunctive eTheta, echo=TRUE}
eThetaFrame(ParentStates(CRItem),log(1),c(Skill1=.5,Skill2=-.5),
OffsetConjunctive)
```
Try changing the slopes and intercepts, and changing the rule for `OffSetDisjunctive`
## Link Functions
* Graded Response model
+ Models $\Pr(X \ge s)$
+ Probabilities are differences between curves
+ To keep the curves from crossing, discrimination parameters must be the same for all $s$
* Normal (Regression) model
+ Effective theta is mean predictor
+ Add a residual variance (link scale parameter)
+ Calculate probabilities that value falls into certain regions
* Generalized partial credit model
+ Models state transitions: $\Pr(X \ge s | X \ge s-1)$
+ Does not need the discrimination parameters to be the same
+ Does not even need the combination rules to be the sam
## Graded Response (DiBello--Samejima models)
Samejima's (1969) psychometric model for graded responses
$$\Pr(X_{i,j} \ge k|\theta_i) = {\rm logit}^{-1}(1.7(a_j\theta_i - b_{j,k}))$$
$$\Pr(X_{i,j}=k|\theta_i) = \Pr(X_{i,j} \ge k|\theta_i) - \Pr(X_{i,j} \ge k+1|\theta_i)$$
```{r Samejima curves}
a <- 1
b <- c(-1,+1)
thetas <- seq(-4,4,.025)
P1 <- invlogit(a*thetas-b[1])
P2 <- invlogit(a*thetas-b[2])
layout(matrix(1:2),2,1)
plot(thetas,P1,ylab="Probability",col="firebrick",type="l")
lines(thetas,P2,col="steelblue")
p0 = 1 - P1
p1 = P1 - P2
p2 = P2
plot(thetas,p0,ylab="Probability",col="firebrick",type="l")
lines(thetas,p1,col="steelblue")
lines(thetas,p2,col="seagreen")
```
## Continuous -> Discrete
Evaluate Samejima's graded response model at the effective theta values.
```{r Samejima link function}
a <- 1
b <- c(-1,+1)
thetas <- seq(-4,4,.025)
P1 <- invlogit(a*thetas-b[1])
P2 <- invlogit(a*thetas-b[2])
p0 = 1 - P1
p1 = P1 - P2
p2 = P2
plot(thetas,p0,ylab="Probability",col="firebrick",type="l")
lines(thetas,p1,col="steelblue")
lines(thetas,p2,col="seagreen")
ethetas <- c(Low=-1.8, Med=-.4, High=1)
P1e <- invlogit(a*ethetas-b[1])
P2e <- invlogit(a*ethetas-b[2])
p0e = 1 - P1e
p1e = P1e - P2e
p2e = P2e
abline(v=ethetas)
points(ethetas,p0e,col="firebrick")
points(ethetas,p1e,col="steelblue")
points(ethetas,p2e,col="seagreen")
data.frame(Theta=ethetas,
State2=round(p2e,3),State1=round(p1e,3),
State0=round(p0e,3))
```
## Representing Graded Response Models in Peanut
* Peanut is a framework that allows us to attach the parameters to nodes in the graph.
+ PNetica implements this for NeticaNode objects
* `PnodeLink(node)` accesses the link function
+ Should have value "gradedResponse" for graded response models.
* `PnodeRules(node)` accesses the rules.
+ For now, stick to multiple-a types: `Compensatory`, `Conjunctive` and `Disjunctive`.
* `PnodeLnAlphas(node)` or `PnodeAlphas(node)` gives the slope parameters.
+ This should be a vector which components corresponding to the parents.
+ In general, vectors are used to represent multiple parents.
* `PnodeBetas(node)` gives the difficulty parameters.
+ This should be a _list_ with one fewer elements than there are states (the last state is used for normalization).
+ In general, lists are used to represent multiple states.
* `BuildTable(node)` builds the table.
+ `NodeLevels` of parents need to be set.
+ `NodePriorWeight` (used in learning algorithm) needs to be set.
```{r graded response Peanut, echo=TRUE}
CRItem <- Pnode(CRItem) ## Force into Pnode protocol.
PnodeLink(CRItem) <- "gradedResponse"
PnodeRules(CRItem) <- "Compensatory"
PnodeAlphas(CRItem) <- c(1.2,.8)
PnodeBetas(CRItem) <- list(.25, -.25)
PnodePriorWeight(CRItem) <- 10 ## Used for learning
calcDPCFrame(ParentStates(CRItem),PnodeStates(CRItem),
PnodeLnAlphas(CRItem),PnodeBetas(CRItem),
PnodeRules(CRItem),PnodeLink(CRItem))
BuildTable(CRItem)
CRItem <- CompensatoryGadget(CRItem)
CRItem[]
```
## Don't Cross the curves!
> **Egon Spengler**: There's something very important I forgot to tell you.
> **Peter Venkman**: What?
> **Egon**: Don't cross the streams.
> **Venkman**: Why?
> **Egon**: It would be bad.
> **Venkman**: I'm fuzzy on the whole good/bad thing. What do you mean, "bad"?
> **Egon**: Try to imagine all life as you know it stopping instantaneously, and every molecule in your body exploding at the speed of light.
> **Ray Stantz**: [shocked gasp] Total protonic reversal.
> **Venkman**: Right. That's bad. Okay. All right. Important safety tip. Thanks, Egon.
> _Ghostbusters_
Actually, not as bad as crossing the proton beams, can produce negative probabilities.
CPTtools corrects, but still puts restrictions on parameters.
In particular, must have a common discrimination for all states of the child variable to ensure curves don't cross.
## Downslide of Graded Response Model
* Need to keep curves from crossing restricts discrimination parameter
+ In _Physics Playground (v. 1)_ for some levels difference between **Silver** trophy and **Gold** trophy had more evidence (higher discrimination) than difference between **Silver** and **none**
+ All steps must have the same parent variables and the same combination rule.
* Models probility of achiving certain level of performance, not step between levels.
* Generalized Partial Credit (GPC) model does not have these downsides.
* Note Graded Response and GPC are the same when child variable has only two states.
## Normal (Regression) Model
* As with effective theta transformation, start by dividing theta region up into equally spaced intervals
* Calculate offset curve:
+ mean is effective theta
+ SD, $s$, is _link scale parameter_
*Conditional probabilities:
+ area under curve between cut points
```{r normal offset, echo=TRUE}
states <- c("Low","Medium","High")
M <- length(states)
c <- qnorm((1L:(M-1L))/M,0,1)
thetas <- qnorm((1L:M -.5)/M,0,1)
curve(dnorm(x),xlim=c(-3,3),xaxt="n",yaxt="n",ylim=c(0,.5),col="red",
ylab="",xlab="Effective Theta")
segments(c,-.1,c,.6)
text(thetas,.45,states)
axis(1,c, do.call(expression,
lapply(1:(M-1), function (m)
substitute(c[m],list(m=m)))))
theta <- .5
sig <- .8
curve(dnorm(x,theta,sig),col="sienna",add=TRUE)
cc <- seq(c[1],c[2],.025)
polygon(c(c[1],cc,c[2]),c(0,dnorm(cc,theta,sig),0),angle=45,col="sienna2")
cplus <- c(-Inf,c,Inf)
pvals <- diff(pnorm(cplus,theta,sig))
names(pvals) <- states
pvals
```
## Normal Link (Regression Model) features
* Link function is inverse of the mapping from states to effective thetas
+ Rounding error, but no scale distortion
+ Good for proficiency variables
* Can be used for no parent case.
* Often better to use intercept (negative difficulty) rather than difficulty.
* Can use $R^2$ instead of the link scale parameter, $\sigma$
$$ R^2 = \frac{1/K \sum_k \alpha_{k,s}^2}
{1/K \sum_k \alpha_{k,s}^2 +\sigma^2} $$
* Note: Latent (tetrachoric) correlations, not observed score correlations
* Can use factor analysis output to get model structure and parameters (Almond, 2010)
## Normal Link: No Parent case
* `PnodeLink(node)` is now "normalLink"
* Now need `PnodeLinkScale(node)`, residual standard deviation ($\sigma$)
* Rule doesn't matter, use `PnodeRules(node)="Compensatory"`
* Should be only one `PnodeBeta(node)`
```{r normal link, no parents}
Skill1 <- Pnode(Skills[[1]]) ## Force into Pnode protocol.
PnodeLink(Skill1) <- "normalLink"
PnodeLinkScale(Skill1) <- .8
PnodeRules(Skill1) <- "Compensatory"
PnodeAlphas(Skill1) <- numeric()
PnodeBetas(Skill1) <- list(.25)
PnodePriorWeight(Skill1) <- 10 ## Used for learning
calcDPCFrame(ParentStates(Skill1),PnodeStates(Skill1),
PnodeLnAlphas(Skill1),PnodeBetas(Skill1),
PnodeRules(Skill1),PnodeLink(Skill1),
PnodeLinkScale(Skill1))
BuildTable(Skill1)
Skill1 <- RegressionGadget(Skill1)
Skill1[]
```
## Normal Link: One Parent case
* `PnodeLink(node)` is now "normalLink"
* Now need `PnodeLinkScale(node)`, residual standard deviation ($\sigma$)
* Works best with `PnodeRules(node)="Compensatory"`
* Should be only one `PnodeBeta(node)`
```{r normal link, one parent}
Skill2 <- Pnode(Skills[[2]]) ## Force into Pnode protocol.
PnodeLink(Skill2) <- "normalLink"
PnodeLinkScale(Skill2) <- .6
PnodeRules(Skill2) <- "Compensatory"
PnodeAlphas(Skill2) <- c(.8)
PnodeBetas(Skill2) <- list(-.25)
PnodePriorWeight(Skill2) <- 10 ## Used for learning
calcDPCFrame(ParentStates(Skill2),PnodeStates(Skill2),
PnodeLnAlphas(Skill2),PnodeBetas(Skill2),
PnodeRules(Skill2),PnodeLink(Skill2),
PnodeLinkScale(Skill2))
BuildTable(Skill2)
Skill2 <- RegressionGadget(Skill2)
Skill2[]
```
## Partial Credit Models
* Observable variable takes on states $0, \ldots, S$
* Model transition probabilities:
$$P_{s|s-1}(\tilde{\bf \theta}) = \Pr(X \ge s | X \ge s-1, \tilde{\bf \theta}) =
{\rm logit}^{-1} 1.7 Z_s(\tilde{\bf \theta}) $$
* Define $Z_0()=0$.
* $Z_s()$ can vary will $s$:
+ Different parameters
+ Different functional forms
+ Can easily switch between multi-a and multi-b combination rules
+ Can use only a subset of the parents!
* Need to define combination rule and parameters for each state (except state 0).
* `PnodeLnAlphas`,`PnodeBetas` and `PnodeRules` are now lists (one element per state)
## Partial Credit Link:
* Probability of $X$ being in state $s$ is:
$$\Pr(X = s | \tilde{\bf\theta}) = \frac{\prod_{r=0}^s P_{r|r-1}(\tilde{\bf\theta})}{C}, $$
where $C$ is a normalization constant.
* Can convert the products to sums
$$\Pr(X = s | \tilde{\bf\theta}) = \frac{\exp\left(1.7\sum_{r=0}^s Z_r(\tilde{\bf\theta})\right)}{\sum_{R=0}^S \exp\left(1.7\sum_{r=0}^R Z_r(\tilde{\bf\theta})\right)}$$
## Simple Case 1: Multiple-A rules
* These look a lot like graded response
* `PnodeLink(pnode) = "partialCredit"`
* `PnodeRules(pnode)` is a single value
+ "Compensatory", "Conjunctive", "Disjunctive"
* `PnodeLnAlphas(pnode)` is a _vector_ corresponding to parents
* `PnodeBetas(pnode)` is a _list_ corresponding to states.
+ $Z_s()$ has the same functional form and the same parameters except for $b$'s
* Use `CompensatoryGadget` to edit this style table.
```{r partial credit Compensatory, echo=TRUE}
CRItem <- Pnode(CRItem) ## Force into Pnode protocol.
PnodeLink(CRItem) <- "partialCredit"
PnodeRules(CRItem) <- "Compensatory"
PnodeAlphas(CRItem) <- c(1.2,.8)
PnodeBetas(CRItem) <- list(.25, -.25)
PnodePriorWeight(CRItem) <- 10 ## Used for learning
calcDPCFrame(ParentStates(CRItem),PnodeStates(CRItem),
PnodeLnAlphas(CRItem),PnodeBetas(CRItem),
PnodeRules(CRItem),PnodeLink(CRItem))
BuildTable(CRItem)
CRItem <- CompensatoryGadget(CRItem)
CRItem[]
```
## Simple Case 2: Multiple-B rules
* These look a lot like multiple-a rules.
* `PnodeLink(pnode) = "partialCredit"`
* `PnodeRules(pnode)` is a single value
+ "OffsetConjunctive", "OffsetDisjunctive"
* `PnodeLnAlphas(pnode)` is a _list_ corresponding to states
* `PnodeBetas(pnode)` is a _vector_ corresponding to parent.
+ $Z_s()$ has the same functional form and the same parameters except for $b$'s
* Use `OffsetGadget` to edit this style table.
```{r partial credit Conjunctive, echo=TRUE}
CRItem <- Pnode(CRItem) ## Force into Pnode protocol.
PnodeLink(CRItem) <- "partialCredit"
PnodeRules(CRItem) <- "OffsetDisjunctive"
PnodeAlphas(CRItem) <- list(1.2,.8)
PnodeBetas(CRItem) <- c(.25, -.25)
PnodePriorWeight(CRItem) <- 10 ## Used for learning
calcDPCFrame(ParentStates(CRItem),PnodeStates(CRItem),
PnodeLnAlphas(CRItem),PnodeBetas(CRItem),
PnodeRules(CRItem),PnodeLink(CRItem))
BuildTable(CRItem)
CRItem <- OffsetGadget(CRItem)
CRItem[]
```
## Discrete Partial Credit model unleased
* `PnodeRules(pnode)` is now a _list_
+ different rule for each state
* `PnodeLnAlphas(node)` is also a _list_
+ elements correspond to states
+ will be a vector or scalar according to corresponding rule.
* `PnodeBetas(node)` is also a _list_
+ elements correspond to states
+ will be a vector or scalar according to corresponding rule.
* A _list_ corresponds to states of child variable (except last)
+ If a vector or scalar shows up where a list is expected, the same value is used for all states.
* A _vector_ (within a list) corresponds to parents
* Could eliminate some parents (0 alpha, or infinite beta)
+ Or use _inner_ (node specific) $Q$-matrix
## Example: Math Word Problem
* Based on unpublished analysis by Cocke and Guo (personal communication 2011-07-11)
* Next Generation Sunshine State Standards Benchmark, MA.6.A.5.1, “Use equivalent forms of fractions, decimals, and percents to solve problems" (NGSSS, 2013)
* Sample problem:
> John scored 75% on a test and Mary has 8 out of 12 correct on the same test. Each test item is worth the same amount of points. Who has the better score?
## Scoring Rubric
State | Description | Skills
-----------|------------------------|------------------
No Credit | Null response | N/A
| or off track |
-----------|------------------------|------------------
Partial | Recognizes 75% and | Mathematical
Credit 1 | 8/12 as key elements | Language
-----------|------------------------|------------------
Partial | Converts two fractions | Convert
Credit 2 | to a common form | Fractions
-----------|------------------------|------------------
Full | Makes the correct | Compare Fractions
Credit | comparison | & Math Lang.
-----------|------------------------|------------------
## Model Refinement
* Collapse "Partial Credit 2" and "Full Credit"
+ Few "Partial Credit 2"'s in practice
* Skill1 = Mathematical Language
* Skill2 = Convert Fractions and Compare Fractions
+ Fraction Mainpulation
* Need two combination rules
+ No Credit -> Partial Credit. Only one skill relevant.
- Can use any rule ("Compensatory" is default choice)
+ Partial Credit -> Full Credit.
- Conjuctive model: both skills needed.
- Less of Skill1 than of Skill2
## Inner Q-matrix
* Q-matrix inside node:
+ Rows are state transitions
+ Columns are skills (parent variables)
| Skill1 | Skill2 | Rule
---------|--------|--------|--------------
Partial | 1 | 0 | Compensatory
---------|--------|--------|--------------
Full | 1 | 1 | Conjunctive
---------|--------|--------|--------------
* The function `PnodeQ(node)` allows setting the node level Q-matrix.
* `PnodeQ(node) = TRUE` implies all 1's in Q-matrix.
## Complex Example
* Now use function `DPCGadget()` to edit with full model.
```{r DPC example, echo=TRUE}
CRItem <- Pnode(CRItem) ## Force into Pnode protocol.
PnodeLink(CRItem) <- "partialCredit"
PnodeRules(CRItem) <- list("Compensatory","OffsetDisjunctive")
PnodeAlphas(CRItem) <- list(c(Skill1=1),1)
PnodeBetas(CRItem) <- list(-1,c(Skill1=-1,Skill2=1))
PnodeQ(CRItem) <- matrix(as.logical(c(1,0,1,1)),2,2,byrow = TRUE)
PnodePriorWeight(CRItem) <- 10 ## Used for learning
calcDPCFrame(ParentStates(CRItem),PnodeStates(CRItem),
PnodeLnAlphas(CRItem),PnodeBetas(CRItem),
PnodeRules(CRItem),PnodeLink(CRItem),Q=PnodeQ(CRItem))
BuildTable(CRItem)
CRItem <- DPCGadget(CRItem)
CRItem[]
PnodeQ(CRItem)
```
## Peanut Functions Summary
* `Pnode()` -- converts a Netica node into a Peanut node
* `NodeLevels(node) <- effectiveThetas(PnodeNumStates(node))` -- Sets up parent states.
* `PnodeLink(node)` -- Link Function
* `PnodeRules(node)` -- (list of) Combination Rules
* `PnodeLnAlphas(node)`, `PnodeAlphas(node)` -- (list of) (vectors of) discriminations
* `PnodeBetas(node)` -- (list of) (vectors of) difficulties
* `PnodeLinkScale(node)` -- link scale parameter (for "normalLink")
* `PnodeQ(node)` -- Inner Q-matrix (TRUE = all 1's)
* `PnodePriorWeight(node)` -- prior strength for GEM algorithm learning
* `BuildTable(node)` -- builds the table.
## Peanut Node Gadgets
* CompensatoryGadget -- For simple Multiple-A models
* OffsetGadget -- For simple Multiple-B models
* RegressionGadget -- For normal link function models (no parent case)
* DPCGadget -- For complex models
+ inner Q-matrix
+ different rules per row