# Belief propagation

> In this class, we study an alternative class of algorithms to the Variable Elimination algorithm, which is the message passing class of algorithms.
>
> Although the Message Passing algorithms are closely related to Variable Elimination in some ways, they also offer us additional flexibility in how we do the summation and factor product steps. This means that we may (potentially) come up with a lower complexity in some cases.

> **Objetives:**
> - To study the cluster graphs and how to pass messages between them.
> - To describe the Belief Propagation algorithm algorithm.
> - To analyze the properties of the Belief Propagation algorithm.
> - To study the Belief Propagation algorihm in clique trees.

> **References:**
> - Probabilistic Graphical Models: Principles and Techniques, By Daphne Koller and Nir Friedman. Ch. 10.
> - Mastering Probabilistic Graphical Models Using Python, By Ankur Ankan and Abinash Panda. Ch. 3.
> - Probabilistic Graphical Models Specialization, offered through Coursera. Prof. Daphne Koller.

 Imagen recuperada de: https://static.thenounproject.com/png/542457-200.png.

___

> In the last class we studied how we can exploit the graphical structure to come up with an (Variable Elimination) algorithm that performs exact inference in an effective way.
>
> The main insight behind the VE algorithm is that the factorization of the distribution allows us to perform local operations on the factors.
>
> In this class, we exploit the same insight with a different class of algorithms.

As in the previous class, we assume that we are dealing with a set of factors we have a set of factors $\bar{\Phi}=\{\phi_1(\bar{X}_1), \dots, \phi_m(\bar{X}_m)\}$ over $\bar{X}$, that define an (unnormalized) measure:

$$\tilde{P}_{\bar{\Phi}}(\bar{X}) = \prod_{\phi_i\in\bar{\Phi}} \phi_i(\bar{X}_i).$$

# 1. Cluster Graph

## 1. Cluster graphs

We begin by defining the *cluster graphs*:

> *Definition.* A **cluster graph** $\mathcal{U}$ for a set of factors $\bar{\Phi}$ over $\bar{X}$ is an undirected graph, whose nodes, say $i$, are associated with a subset of variables, say $\bar{C}_i\subset\bar{X}$, which we call clusters.
>
> **Family preservation property:** Moreover, each factor $\phi_k\in\bar{\Phi}$ must be associated one and only one cluster $\bar{C}_{\alpha(k)}$ such that $\mathrm{scope}[\phi_k]\subseteq \bar{C}_{\alpha(k)}$.
>
> Each edge between a pair of clusters $\bar{C}_i$ and $\bar{C}_j$ is associated with a **sepset** $\bar{S}_{ij}\subseteq \bar{C}_i\cap\bar{C}_j$.

**Example.** Let's consider the pairwise MN:

```python
from IPython.display import Image
```

```python
Image("figures/clusterGraph.png")
```

**Example.** Consider following set of factors:

$$\bar{\Phi}=\{\phi_1(A,B,C), \phi_2(B,C), \phi_3(B,D), \phi_4(D,E), \phi_5(B,E), \phi_6(B,D), \phi_7(B,D,F)\}.$$

Then, possible cluster graphs are ($\alpha(1)=1$, $\alpha(2)=2$, $\alpha(3)=2$, $\alpha(4)=5$, $\alpha(5)=4$, $\alpha(6)=3$, $\alpha(7)=3$):

```python
Image("figures/clusterGraph2.png")
```

```python
Image("figures/clusterGraph3.png")
```

- The idea behind the belief propagation algorithm is that clusters will "talk" to each other;
- They are going to try to convince each other that what they think about a variable is correct.

## 1.2. Belief propagation algorithm

Now that we know what a cluster graph is, we will describe the Belief Propagation algorithm:

1. Assign each factor $\phi_k \in \bar{\Phi}$ to a cluster $C_{\alpha(k)}$.

2. Construct initial potentials $\psi_i(\bar{C_i}) = \prod_{k: \alpha(k)=i} \phi_k(\bar{X}_k)$.

3. Initialize all messages $\delta_{ij}(\bar{S}_{ij})=1$.

4. Repeat:
   - Select edge (i,j) and pass message
   $$\delta_{ij}(\bar{S}_{ij}) = \sum_{\bar{C}_i\setminus\bar{S}_{ij}} \psi_i(\bar{C}_i) \prod_{k\in\mathcal{N}_i \setminus \{j\}} \delta_{ki}(\bar{S}_{ki}),$$
   where $\mathcal{N}_i$ are the set of neighboring nodes of node $i$.

   Explain

5. Compute beliefs:

   $$\beta_i(\bar{C}_i) = \psi_i(\bar{C}_i) \prod_{k\in\mathcal{N}_i} \delta_{ki}(\bar{S}_{ki})$$

### Important

- This algorithm doesn't converge always.
- When it does, it's only approximate, in the general case.

## 1.3. Properties of cluster graphs

Until now, we know what the cluster graphs are, and how to pass messages between clusters to run the belief propagation algorithm.

However, we don't know how to construct cluster graphs given a PGM yet. To this end, let's examine some properties that cluster graphs need to satisfy in order to support message passing:

**1. Family preservation**

Given a set of factors $\bar{\Phi}$, we assign each factor $\phi_k\in\bar{\Phi}$ to one and only one cluster $\bar{C}_{\alpha(k)}$ such that $\mathrm{scope}[\phi_k]\subseteq \bar{C}_{\alpha(k)}$.

- *All the information is used only once.*

**2. Running intersection property**

For each pair of clusters $\bar{C}_i, \bar{C}_j$, and a variable $X \in \bar{C}_i \cap \bar{C}_j$, there exists a unique path between $\bar{C}_i$ and $\bar{C}_j$ for which all clusters and sepsets contain $X$.

- *The existence of such a path porevents the isolation of information about a variable*.
- *The uniqueness prevents self-reinforcement feedback loops*.

Equivalently: For any $X$, the set of clusters and sepsets that contain $X$ form a **tree**.

**Example.** Consider again the set of factors:

$$\bar{\Phi}=\{\phi_1(A,B,C), \phi_2(B,C), \phi_3(B,D), \phi_4(D,E), \phi_5(B,E), \phi_6(B,D), \phi_7(B,D,F)\}.$$

$\alpha(1)=1$, $\alpha(2)=2$, $\alpha(3)=2$, $\alpha(4)=5$, $\alpha(5)=4$, $\alpha(6)=3$, $\alpha(7)=3$.

Then, the following cluster graph satisfies both, the family preservation and the running intersection properties.

```python
Image("figures/clusterGraph2.png")
```

What about this one?

```python
Image("figures/clusterGraph4.png")
```

Is it a valid cluster graph?

-

And this one?

```python
Image("figures/clusterGraph5.png")
```

Is it a valid cluster graph?

-

**Bethe cluster graph**

This type of cluster graphs always satisfy both properties.

1. For each factor $\phi_k\in\bar{\Phi}$, assign a cluster $\bar{C}_k$ such that $\mathrm{scope}[\phi_k]\subseteq \bar{C}_k$.
2. For each variable $X_i$ assign a singleton cluster $\{X_i\}$.
3. Add an edge $\bar{C}_k - X_i$ if $X_i\in\bar{C}_k$.

For the example we were analyzing before (in the whiteboard, then show):

```python
# clusterGraph6
Image("figures/clusterGraph6.png")
```

Although the correlations between factors is lost, the Bethe cluster graph provides an easy way to construct cluster graphs satisfying the properties of family preservation and running intersection.

## 1.4. Properties of belief propagation

Now, we turn our attention to some properties that will allow to study convergence of the belief propagation algorithm.

**1. Calibration**

Recall the pairwise MN:

```python
Image("figures/clusterGraph.png")
```

The cluster beliefs are defined as:

$$\beta_i(\bar{C}_i) = \psi_i(\bar{C}_i) \prod_{k\in\mathcal{N}_i}\delta_{ki}(\bar{S}_{ki})$$

For instance,

$$\beta_1(A,B) = \psi_1(A,B) \delta_{41}(A) \delta_{21}(B)$$

> *Definition.* A cluster graph is **calibrated** if every pair of adjacent clusters $\bar{C}_i, \bar{C}_j$ agree on their sepset $\bar{S}_{ij}$:
>
> $$\sum_{\bar{C_i}\setminus\bar{S}_{ij}}\beta_i(\bar{C}_i) = \sum_{\bar{C_j}\setminus\bar{S}_{ij}}\beta_j(\bar{C}_j).$$

This is, the cluster graph is calibrated if the marginal beliefs agree.

**2. Convergence**

The convergence property refers to an *steady state achievement*.

> *Definition.* A cluster graph has attained **convergence** if all the messages at the next time step equal the messages at the previous time step:
>
> $$\delta_{ij}(\bar{S}_{ij}) = \delta_{ij}'(\bar{S}_{ij})$$

One important result is that **convergence implies calibration** (see in the whiteboard, then show)

To see this, first recall that

$$\delta_{ij}'(\bar{S}_{ij}) = \sum_{\bar{C}_i \setminus\bar{S}_{ij}} \psi_i(\bar{C}_i) \prod_{k\in\mathcal{N}_i\setminus\{j\}}\delta_{ki}(\bar{S}_{ki}) = \sum_{\bar{C}_i \setminus\bar{S}_{ij}} \frac{\beta_{ij}(\bar{C}_i)}{\delta_{ji}(\bar{S}_{ji})}.$$

Thus, assuming convergence:
$$\delta_{ij}(\bar{S}_{ij}) \delta_{ji}(\bar{S}_{ji}) = \sum_{\bar{C}_i \setminus\bar{S}_{ij}} \beta_{i}(\bar{C}_i).$$

Using the same sort of argument
$$\delta_{ji}(\bar{S}_{ji}) \delta_{ij}(\bar{S}_{ij}) = \sum_{\bar{C}_j \setminus\bar{S}_{ji}} \beta_{j}(\bar{C}_j).$$

Thus

$$\mu_{ij}(\bar{S}_{ij}) := \delta_{ij}(\bar{S}_{ij}) \delta_{ji}(\bar{S}_{ji}) = \sum_{\bar{C}_i \setminus\bar{S}_{ij}} \beta_{i}(\bar{C}_i) = \sum_{\bar{C}_j \setminus\bar{S}_{ji}} \beta_{j}(\bar{C}_j).$$

**3. Reparametrization**

This property state that there is no loss of information in the message passing process.

To see this, recall that:

$$\beta_i(\bar{C}_i) = \psi_i(\bar{C}_i) \prod_{k\in\mathcal{N}_i}\delta_{ki}(\bar{S}_{ki}),$$

and

$$\mu_{ij}(\bar{S}_{ij}) = \delta_{ij}(\bar{S}_{ij}) \delta_{ji}(\bar{S}_{ji}).$$

Then,

$$\frac{\prod_{i}\beta_i(\bar{C}_i)}{\prod_{i,j} \mu_{ij}(\bar{S}_{ij})} = \frac{\prod_{i}\psi_i(\bar{C}_i) \prod_{j\in\mathcal{N}_i}\delta_{ji}(\bar{S}_{ji})}{\prod_{i,j} \delta_{ij}(\bar{S}_{ij})} = \prod_{i}\psi_i(\bar{C}_i) = \tilde{P}_{\bar{\Phi}}(\bar{X}).$$

___
# 2. Belief Propagation in Clique Trees

We know now that in the general case, the belief propagation algorithm don't work always.

However, when applied over *clique trees*, the belief propagation algorithm has stronger performance guarantees:
- Faster convergence.
- Convergence to exact inference.

## 2.1. Message passing in trees

Consider the following chain:

```python
Image("figures/chain.png")
```

The following is a valid tree cluster graph for the above chain:

```python
Image("figures/clusterGraphChain.png")
```

**How is the form of all the messages that will be passed over this tree cluster graph?**

See in the whiteboard, then show.

```python
# clusterGraphChain2
Image("figures/clusterGraphChain2.png")
```

Thus, it is not hard to realize that after a single run of the belief propagation algorithm in both directions, the beliefs converge to the correct marginals.

For instance, consider cluster 3:

$$\begin{align}
\beta_3(C,D) & = \psi_3(C,D) \delta_{23}(C) \delta_{43}(D) \\
             & = \psi_3(C,D) \sum_{B} \psi_2(B,C) \delta_{12}(B) \sum_{E} \psi_4(D,E)\\
             & = \psi_3(C,D) \sum_{B} \psi_2(B,C) \sum_{A} \psi_1(A,B) \sum_{E} \psi_4(D,E)\\
             & = \tilde{P}_{\bar{\Phi}}(C,D)
\end{align}$$

## 2.2. Clique trees

The behavior in the chain cluster graph is also attained by *clique trees* structures.

> *Definition.* A **clique tree** is an undirected tree such that:
>
> 1. Its nodes are clusters $\bar{C}_i \subset \bar{X}$, which correspond to *cliques* in the original graphical model.
> 2. The edges between $\bar{C}_i$ and $\bar{C}_j$ are associated with the sepset $\bar{S}_{ij}=\bar{C}_i \cap \bar{C}_j$.

What is a *clique*?

> *Definition.* A **clique** is a complete subgraph. This is, a portion of the complete graph where every two nodes are connected by some edge.

When talking about cliques, we always consider the **induced undirected network** (Section 9.4.2.3, page 308).

Of course, since clique trees are a special case of cluster graphs, they should satisfy both, the family preservation and the running intersection properties.

The **running intersection property for clique trees** can be rewritten as:

- For each pair of clusters $\bar{C}_i, \bar{C}_j$ and a variable $X\in\bar{C}_i \cap \bar{C}_j$, in the unique path between $\bar{C}_i$ and $\bar{C}_j$, all clusters and sepsets contain $X$.

**Example.**

Consider the extended student network:

```python
Image("figures/extendedStudent.png")
```

The induced graph for the bove Bayesian network is:

```python
Image("figures/extendedStudentInduced.png")
```

Hence, a valid clique tree for this model is:

```python
Image("figures/extendedStudentCliqueTree.png")
```

What is the form of the messages?

### Running intersection property implies correctness

- If a variable $X$ is marginalized (eliminated) when we pass the message $\delta_{ij}$, then $X$ does not appear in the $\bar{C}_j$ side of the clique tree.

- So, in this case, the computation of the **belief propagation algorithm is a variant of the variable elimination algorithm**, and the resulting beliefs are guaranteed to be correct marginals of $\tilde{P}_{\bar{\Phi}}$.

## 2.3. Clique tree algorithm computation

Let's consider the chain example:

```python
Image("figures/chain.png")
```

```python
Image("figures/clusterGraphChain2.png")
```

One can note some things:
1. Once you compute $\delta_{12}$, it never changes (instant convergence).

2. For $\delta_{23}$, wait for $\delta_{12}$, then calculate and you get convergence.

3. For $\delta_{34}$, wait for $\delta_{23}$, then calculate and you get convergence.

4. The same happens in the opposite direction.

That is why this is known as **forward-backward algorithm**:

- Once $\bar{C}_i$ receives a final message from all neighbors, except $\bar{C}_j$, then $\delta_{ij}(\bar{S}_{ij})$ is also final.

- Messages from leaves of the tree are immediately final.

- If messages are passed in the right order, you only need to pass $2(k-1)$ messages, where $k$ is the number of cluster.

**Thus, you can compute the marginals over all variables at only twice the cost of one variable elimination over all variables.**

___

How to use belief propagation algorithm in pgmpy?

Restaurant example

```python
Image("figures/restaurant.png")
```

```python
# Import pgmpy.models.BayesianModel
from pgmpy.models import BayesianModel
```

```python
# Import pgmpy.factors.discrete.TabularCPD
from pgmpy.factors.discrete import TabularCPD
```

```python
# Define model skeleton
restaurant_model = BayesianModel([("L", "C"),
                                  ("Q", "C"),
                                  ("L", "N"),
                                  ("C", "N")])
```

```python
# Define CPDs
cpd_L = TabularCPD(variable="L",
                   variable_card=2,
                   values=[[0.4],
                           [0.6]])
cpd_Q = TabularCPD(variable="Q",
                   variable_card=3,
                   values=[[0.2],
                           [0.5],
                           [0.3]])
cpd_C = TabularCPD(variable="C",
                   variable_card=2,
                   evidence=["L", "Q"],
                   evidence_card=[2, 3],
                   values=[[0.95, 0.4, 0.4, 0.9, 0.4, 0.2],
                           [0.05, 0.6, 0.6, 0.1, 0.6, 0.8]])
cpd_N = TabularCPD(variable="N",
                   variable_card=2,
                   evidence=["L", "C"],
                   evidence_card=[2, 2],
                   values=[[0.4, 0.9, 0.2, 0.4],
                           [0.6, 0.1, 0.8, 0.6]])
```

```python
# Attach CPDs to the model
restaurant_model.add_cpds(cpd_L, cpd_Q, cpd_C, cpd_N)
```

```python
# Check if the model is correctly defined
restaurant_model.check_model()
```

    True

```python
# Get a valid clique tree
clique_tree = restaurant_model.to_junction_tree()
```

```python
# Type
type(clique_tree)
```

    pgmpy.models.JunctionTree.JunctionTree

```python
# Nodes
clique_tree.nodes
```

    NodeView((('L', 'C', 'N'), ('L', 'C', 'Q')))

```python
# Edges
clique_tree.edges
```

    EdgeView([(('L', 'C', 'N'), ('L', 'C', 'Q'))])

```python
# Import pgmpy.inference.BeliefPropagation
from pgmpy.inference import BeliefPropagation
```

```python
# Create inference object
inference = BeliefPropagation(restaurant_model)
```

```python
# Calibrate
inference.calibrate()
```

```python
# Get beliefs
print(inference.get_clique_beliefs()[('L', 'C', 'N')])
```

    +------+------+------+--------------+
    | L    | C    | N    |   phi(L,C,N) |
    +======+======+======+==============+
    | L(0) | C(0) | N(0) |       0.0816 |
    +------+------+------+--------------+
    | L(0) | C(0) | N(1) |       0.1224 |
    +------+------+------+--------------+
    | L(0) | C(1) | N(0) |       0.1764 |
    +------+------+------+--------------+
    | L(0) | C(1) | N(1) |       0.0196 |
    +------+------+------+--------------+
    | L(1) | C(0) | N(0) |       0.0528 |
    +------+------+------+--------------+
    | L(1) | C(0) | N(1) |       0.2112 |
    +------+------+------+--------------+
    | L(1) | C(1) | N(0) |       0.1344 |
    +------+------+------+--------------+
    | L(1) | C(1) | N(1) |       0.2016 |
    +------+------+------+--------------+

```python
inference.get_clique_beliefs()[('L', 'C', 'N')].values.sum()
```

    1.0

```python
print(inference.get_clique_beliefs()[('L', 'C', 'Q')])
```

    +------+------+------+--------------+
    | L    | C    | Q    |   phi(L,C,Q) |
    +======+======+======+==============+
    | L(0) | C(0) | Q(0) |       0.0760 |
    +------+------+------+--------------+
    | L(0) | C(0) | Q(1) |       0.0800 |
    +------+------+------+--------------+
    | L(0) | C(0) | Q(2) |       0.0480 |
    +------+------+------+--------------+
    | L(0) | C(1) | Q(0) |       0.0040 |
    +------+------+------+--------------+
    | L(0) | C(1) | Q(1) |       0.1200 |
    +------+------+------+--------------+
    | L(0) | C(1) | Q(2) |       0.0720 |
    +------+------+------+--------------+
    | L(1) | C(0) | Q(0) |       0.1080 |
    +------+------+------+--------------+
    | L(1) | C(0) | Q(1) |       0.1200 |
    +------+------+------+--------------+
    | L(1) | C(0) | Q(2) |       0.0360 |
    +------+------+------+--------------+
    | L(1) | C(1) | Q(0) |       0.0120 |
    +------+------+------+--------------+
    | L(1) | C(1) | Q(1) |       0.1800 |
    +------+------+------+--------------+
    | L(1) | C(1) | Q(2) |       0.1440 |
    +------+------+------+--------------+

```python
# Perform query
print(inference.query(variables=["N"], evidence={"L": 1}))
```

    Eliminating: C: 100%|██████████| 2/2 [00:00<00:00, 185.53it/s]

    +------+----------+
    | N    |   phi(N) |
    +======+==========+
    | N(0) |   0.3120 |
    +------+----------+
    | N(1) |   0.6880 |
    +------+----------+

___

## 2.4. Clique trees and variable elimination

In the variable elimination algorithm we have:

1. Each steps creates a factor $\lambda_k$ through factor product.

2. A variable is eliminated in $\lambda_k$ to generate the new factor $\tau_k$.

3. $\tau_k$ is used in the computation of other factors $\lambda_j$.

From a clique tree perspective:

1. The intermediate factors $\lambda_k$ are the "cliques".

2. $\tau_k$ are "messages" generated by the clique $\lambda_k$ and transmitted to another clique $\lambda_j$.

Thus, the variable elimination algorithm defines the clique tree!

- Define a cluster $\bar{C}_i$ for each factor $\lambda_i$ used in the computation.

- Draw an edge $\bar{C}_i - \bar{C}_j$ if the factor generated from $\lambda_i$ is used in the computation of $\lambda_j$.

```python
Image("figures/cliqueTreeVE.png")
```

This clique tree satisfies both, the family preserving property and the running intersection property.

**Example**

For the restaurant example of the last class:

$$P(L,Q,C,N)= \phi_L(L)\phi_Q(Q)\phi_C(C,L,Q)\phi_N(N,L,C).$$

1. Eliminate $Q$: $\tau_1(C,L) = \sum_{Q} \underbrace{\phi_Q(Q) \phi_C(C,L,Q)}_{\lambda_1(C,L,Q)}$.

2. Eliminate $C$: $\tau_2(N,L) = \sum_{C} \underbrace{\phi_N(N,L,C)\tau_1(C,L)}_{\lambda_2(N,L,C)}$.

3. Eliminate $L$: $\tau_3(N) = \sum_{L} \underbrace{\phi_L(L)\tau_2(N,L)}_{\lambda_3(N,L)}$.

4. Eliminate $N$: $\tau_4() = \sum_{N} \tau_3(N)$.

```python
Image("figures/cliqueTreeVE2.png")
```

Remove redundant cliques: variables that are subsets of adjacent cliques.

# Announcements

## Quiz.

Created with Jupyter by Esteban Jiménez Rodríguez.


# Longitudinal Data Analysis

# Panel Data Analysis IV

Table of Contents

1  Quick reminder

2  Defining our statistical model

2.1  A word of caution

3  Random Effects Model

3.1  Conceptualising the Random Effects model

3.2  Estimation

3.3  Interpretation

3.4  Post-estimation

3.5  Benefits of Random Effects

3.6  Limitations of Random Effects

3.7  Summarising the Random Effects model

4  Summary

In this section we estimate a statistical model that leverages some of the main advantages of using panel data: **Random Effects**. We show some examples of how to estimate and interpret this model, and reflect on the conditions under which the model is appropriate.

## Quick reminder

Let's briefly recap some essential concepts regarding panel data:

Two sources of variation ([Gould, n.d.](https://www.stata.com/support/faqs/statistics/between-estimator/)):
1. Cross-section information on differences between units
2. Time series information on differences over time within units

Pooled OLS and Between Effects models only allow us to examine differences between units.

Fixed Effects only allow us to examine differences within units.

What if we wanted a model that leveraged aspects of the Between Effects and Fixed Effects estimators?

Such a model would give us:

* More variation with which to explain the outcome.

* More flexibilty (e.g., include observed time-invariant explanatory variables)

* Other methodological and modelling benefits (e.g., decomposing explanatory variables into within and between effects)

## Defining our statistical model

Recall the general form of Fixed Effects and Random Effects model:

\begin{equation} \text{y}_{it} = \beta_0 + \beta_1x_{1it} + \mu_{i} + \text{e}_{it} \tag{1.3} \end{equation}

In equation 1.3. we have introduced a **unit-specific** term to represent some of the residual variation in the outcome that is unexplained by the explanatory variables.

This term ($\mu_{i}$) captures the effect of *residual heterogeneity* on the outcome i.e., unobserved or immeasurable characteristics of the units that are associated with the outcome (and possibly the explanatory variables), and vary across units.

### A word of caution

Note the lack of a time subscript *t* in the new term $\mu_{i}$.

The implication is that the unobserved unit-specific effect is **constant** over time (i.e., within units).

Therefore Fixed Effects and Random Effects models only control for omitted variables that do not change within units (e.g., race, sex at birth, natural ability).

## Random Effects Model

### Conceptualising the Random Effects model

1. The Random Effects model focuses on how changes in explanatory variables are associated with changes in the outcome **within and between units**.

Gayle and Lambert (2018, p. 68):
> ...the random effects panel model is a matrix weighted average of the within-effects (fixed effects) and the between effects.

In essence the Random Effects model borrows some information from the Between Effects model and some from the Fixed Effects model.

Therefore the coefficients in a Random Effects model allow you to speak in terms of the effect of an explanatory variable on an outcome, whether we are comparing different individuals or different observations for the same individual - we'll what this means when we estimate our first Random Effects model.

2. It assumes the observed explanatory variables and unobserved unit-specific effects are **not** correlated (i.e., omitted variable bias is not an issue).

In essence: the unobserved unit-specific effect explains some of the variation in the outcome, but is not associated with any of the observed explanatory variables.

The Random Effects model is specified as follows:

\begin{equation} \text{y}_{it} = \beta_0 + \beta_1x_{1it} +...+ \beta_kx_{kit} + \upsilon_{i} + \text{e}_{it} \tag{1.6} \end{equation}

Where:

$\upsilon_{i}$ represents the unit-specific effect on the outcome.

As we assume the observed explanatory variables and unobserved unit-specific effects are **not** correlated, there is no need to estimate $\upsilon_{i}$ as if it were an explanatory variable and hence why it is part of the error component of the model.

Remember, it would only need to be estimated in the model if it would alter the coefficients for the observed explanatory variables: we assume it wouldn't it. Therefore instead of estimating the value of $\upsilon_{i}$ using the data (as we would for the observed explanatory variables), we assume the unit-specific effects are drawn from a known probability distribution (Gayle and Lambert, 2018).

As a result, we are only interested in the variance of $\upsilon_{i}$ and the extent to which it accounts for variation in the outcome.

#### Final thoughts on conceptualisation

In a Random Effects model we are unconcerned with the actual value of the unit-specific effect.

We simply want to know to what degree variation in these unit-specific effects is associated with variation in the outcome.

### Estimation

```stata
use "../data/charity-panel-analysis-2020-09-10.dta", clear
```

    (Contains annual accounts of charities in E&W for financial years 2006-2017)

```stata
xtreg linc orgage localc west genchar nsources govern_share, re
```

    Random-effects GLS regression                   Number of obs     =     23,826
    Group variable: regno                           Number of groups  =      2,166

    R-sq:                                           Obs per group:
         within  = 0.0135                                         min =         11
         between = 0.0888                                         avg =       11.0
         overall = 0.0832                                         max =         11

                                                    Wald chi2(6)      =     507.12
    corr(u_i, X)   = 0 (assumed)                    Prob > chi2       =     0.0000

    ------------------------------------------------------------------------------
            linc |      Coef.   Std. Err.      z    P>|z|     [95% Conf. Interval]
    -------------+----------------------------------------------------------------
          orgage |    .005022   .0003686    13.62   0.000     .0042995    .0057446
          localc |  -.3323159   .0412995    -8.05   0.000    -.4132615   -.2513704
            west |   .0797179   .0801801     0.99   0.320    -.0774323    .2368681
         genchar |  -.2729578   .0414011    -6.59   0.000    -.3541025   -.1918131
        nsources |    .030451     .00276    11.03   0.000     .0250415    .0358605
    govern_share |    .001024    .000121     8.47   0.000      .000787    .0012611
           _cons |   15.15988    .048456   312.86   0.000     15.06491    15.25485
    -------------+----------------------------------------------------------------
         sigma_u |  .90700183
         sigma_e |   .2821005
             rho |  .91179586   (fraction of variance due to u_i)
    ------------------------------------------------------------------------------

**QUESTION TIME**

1. How much of the variation in the outcome is accounted for by the model? Is this a lot?
2. Why are `localc`, `west` and `genchar` included in the estimation of the model (when they weren't in the Fixed Effects model)?
3. What does the $\text{rho}$ statistic tell us?
4. Is there evidence of correlation between the unit-specific effects and observed explanatory variables?

### Interpretation

The effect of the observed explanatory variables is **not** net of the unit-specific effects. That is, we haven't controlled away any correlation between *X* and $\mu_{i}$ (because we assume they are not correlated).

$\text{_cons}$ is the intercept.

$\text{orgage}$ is the predicted change in the outcome for a one-unit increase in organisational age, **whether age changes within or between units**.

$\text{rho}$ is the proportion of unexplained variance in the outcome explained by unobserved differences between charities (the unit-specific effects), rather than changes within them.

If $\text{rho}$ > .5 then most of the residual variation in the outcome is due to differences between units, if $\text{rho}$ < .5 then most of the residual variation is accounted for by differences within units (i.e., the effects of the explanatory variables).

$\text{corr(u_i, X) = 0}$ is the formal statement of the (untested) assumption that there is no correlation between the unit-specific effect and the observed explanatory variables in the model.

* $\text{sigma_u}$ (or $\sigma_u$) is the standard deviation of residuals within units.
* $\text{sigma_e}$ (or $\sigma_e$) is the standard deviation of residuals ei.
* $\text{R-sq: overall}$ is the proportion of variance explained by the observed explanatory variables (i.e., excluding the unit-specific effect).

### Post-estimation

Though it's very rarely of substantive interest, we can recover the unit-specific effects (and other parameter estimates) after estimating a Random Effects model:

```stata
capture predict random, u
capture predict y_hat, xb
capture predict ei, e
capture predict residuals, ue
capture egen pickone = tag(regno)
```

```stata
l regno fin_year random if pickone in 1/100, clean
```

            regno   fin_year      random
      1.   200048    2006-07   -1.144261
     12.   200051    2006-07    1.582299
     23.   200069    2006-07     -.21995
     34.   200222    2006-07    -.199622
     45.   200424    2006-07   -.1241415
     56.   200431    2006-07   -.2140733
     67.   200500    2006-07    .2228235
     78.   201081    2006-07    .2918403
     89.   201321    2006-07    -1.13723
    100.   201911    2006-07   -.6447842

```stata
l regno fin_year y_hat residuals random ei in 1/11, clean
```

            regno   fin_year      y_hat   residuals      random          ei
      1.   200048    2006-07   15.34991   -1.348024   -1.144261   -.2037623
      2.   200048    2007-08   15.35493   -1.177057   -1.144261   -.0327961
      3.   200048    2008-09   15.39064   -1.205535   -1.144261   -.0612741
      4.   200048    2009-10   15.36498   -1.132381   -1.144261    .0118806
      5.   200048    2010-11    15.3716   -1.200694   -1.144261   -.0564324
      6.   200048    2011-12   15.37502    -1.22701   -1.144261   -.0827486
      7.   200048    2012-13   15.41329   -1.037294   -1.144261    .1069672
      8.   200048    2013-14   15.38507   -1.085107   -1.144261    .0591543
      9.   200048    2014-15   15.42087   -1.160563   -1.144261   -.0163015
     10.   200048    2015-16   15.39511    -1.09398   -1.144261    .0502813
     11.   200048    2016-17   15.40013   -1.029922   -1.144261    .1143396

```stata
di -1.144261 + -.2037623
```

    -1.3480233

```stata
tabstat random ei, s(mean sd) format(%5.4f)
```

       stats |    random        ei
    ---------+--------------------
        mean |    0.0000    0.0000
          sd |    0.9096    0.2691
    ------------------------------

### Benefits of Random Effects

Analyse both change over time and differences between units [Substantive]

Control for residual heterogeneity [Methodological]

(Mehmetoglu and Jakobsen, 2016)

Estimate observed time-invariant explanatory variables in the model [Substantive and Methodological]

Coefficient estimates are **efficient**, especially when compared to those from a Fixed Effects model. As a result, standard errors tend to be smaller.

Put simply, the estimates of the coefficients are based on more information than those in the Fixed Effects model, which bases its estimates on only one source of variation (within).

### Limitations of Random Effects

Key assumption is often unrealistic [Methdological]

Coefficient estimates are **inconsistent** if the key assumption is violated.

That is, if the coefficients for the observed explanatory variables are biased, then increasing the sample size does not necessarily mean we are getting closer to the true value of the parameter.

Difficult to infer whether the value of a coefficient is mainly determined by within or between variation [Methodological]

Cannot control for unobserved residual heterogeneity that varies over time [Methodological]

Educational ability? Natural resilience?

The last point is worth expanding on: if units differ in an unobserved way that varies over time, this will not be controlled for in the Random Effects model.

### Summarising the Random Effects model

Analyses both change within a unit's outcomes, and differences between units' outcomes.

Can control for the effect of unobserved time-invariant explanatory variables (residual heterogeneity).

Can include observed explanatory variables that do not vary within units (e.g., race, sex at birth).

Does not provide robust estimates of observed explanatory variables when said variables are correlated with unobserved unit-specific effects.

## Summary

Both the Pooled OLS and Between Effects models provide useful information on the association between an outcome *Y* and a set of explanatory variables *X*.

Fixed Effects provide potentially different information on the association between an outcome *Y* and a set of explanatory variables *X.

Random Effects combines the *within* and *between* perspectives - methodological and substantive benefits.


# Lab 7: Moment of Inertia
### Objectives
- To learn to take good measurements
- To learn how to estimate and propagate errors
- To learn how to take measurements to verify a theory
- To learn how torque is related to angular acceleration
- To learn how moment of inertia depends on both mass and distribution.

### Equipment
- One Assembled Angular Acceleration Station
    - One “A” frame base
    - One 1-m steel rod
    - One CI-6538 Rotary Motion Sensor
    - One “Super Pulley”
- One PASCO “Mass and Hanger Set” (ME-8979)
- One 2-m length of thread
- One “Hoop and Disk” assembly
- One “Point Mass” assembly
    - One mounting rod with captured screw
    - Two cylindrical masses with nylon thumbscrews
- One meter stick
- One measurement caliper
- One OHAUS scale

### Safety
You will be rotating fairly heavy masses to somewhat high rotational velocities. Above all else,
you should ensure that all masses are **securely attached** to the rotor and you should be aware of
where your hands and head are with respect to the apparatus at all times. Avoid high speeds---they are not necessary.

**Be extremely careful with the plastic pulley**. It will break if you mistreat it. Not only will points be deducted from your lab, but you will be unable to complete the experiment if you break it.

# Introduction
Newton’s second law, $a=F/m$, describes mass as that which resists acceleration. Rotational
motion is very similar to linear motion in that a kind of force called a torque will cause an object to change its rotational velocity, $\omega$. What resists such a change is not the mass of the object alone, but the way in which that mass is distributed. This combined effect of mass and its distribution is called the moment of inertia, *I*.

# Theory
### Torque
Torque is the “force” which causes an object to change the rate at which it is rotating. Examples of this kind of motion are: starting, stopping, or changing the “speed” with which a wheel is rotating; bending your arm at the elbow or leg at the knee; or even screwing (or unscrewing) a bolt or screw-top lid.

If you try to remove a securely fastened bolt, you quickly find that it is much easier to use a
wrench than it is trying to use your hand alone. The force you can apply to the bolt is, itself, not enough to cause the bolt to turn. But the force you can *apply* times a distance away from the
center of rotation (often called the “lever arm”) is directly related to the magnitude of the torque you can generate (i.e. how effective you will be at getting the bolt to turn).

Although it seems obvious, it must be mentioned that not all forces are equally effective at generating torques. If you push or pull the wrench towards or away from the bolt, this will not cause the bolt to turn and you will generate no torque. Only the component of the force which is perpendicular to the lever arm has any effect.

We roll all three of these concepts into the equation

\begin{equation}
\Gamma = r F \sin \theta \quad \textrm{or} \quad \vec{\Gamma} = \vec{r} \times \vec{F}
\tag{1}
\end{equation}

Where $\Gamma$ is the torque, $r$ is the distance from the point where the force is applied to the center of the axis of rotation, and $\theta$ is the angle between the two vectors $\vec{r}$ and $\vec{F}$.

### The Equation of Motion
For linear motion, a simple re-arrangement of Newton’s Second Law states:

\begin{equation}
\vec{F} = m \vec{a}
\tag{2}
\end{equation}

where $a$ is the acceleration of the object (in $m/s^2$), F is the force applied to the object (in Newtons), and $m$ is the mass of the object (in kg).

We can write a similar relationship for rotation:

\begin{equation}
\vec{\Gamma} = I \vec{\alpha}
\tag{3}
\end{equation}

where $\alpha$ is the *angular* acceleration of the object (in $rad/s^2$), $\Gamma$ is the *torque* applied to the object (in Nm), and $I$ is the *moment of inertia* of the object (in $kg m^2$).

Using equation ([3](#mjx-eqn)), we should be able to determine an unknown moment of inertia if we know both the torque applied and if we can measure the resulting angular acceleration.

### The Moment of Inertia of a Point Mass
It is not just the mass of an object that makes it difficult to rotate, but also the distribution of that mass. If the mass is close to the axis of rotation, it will be easier to rotate than if the mass is mostly far away from the axis. For a single point of mass $m$ which is a distance $r$ away from the axis of rotation, the moment of inertia of this point mass around this axis is given by

\begin{equation}
I = m r^2
\tag{4}
\end{equation}

For more complicated mass distributions, we can add up all the moments of inertia of all the points.

### Data Analysis
The Rotary Motion Sensor will provide you with three data sets: Angle vs. Time, Angular Velocity vs. Time, and Angular Acceleration vs. Time. The initial data set is the Angle vs. Time data set. The others are computed by taking numerical derivatives. Even though you might think it is better to take the angular acceleration data when you want it, this may not be the case. When you take your measurement, you should look carefully at each data set. The “noisier” the data, the worse your uncertainties will be. But if the angular acceleration data is not the proper data set to use, how do you get angular acceleration from the other data sets? You must fit your “good” data and use your knowledge of kinematics to extract the acceleration (and its uncertainty) from the fit.

### Error Analysis
Your Lab Manual and the previous labs can guide you in estimating the errors in your
measurements and propagating those errors into your computed moments of inertia. ***You may
not use the naïve method for computing your uncertainties in this lab***. The uncertainties in
your accelerations can come from the quadratic fit to your data. In some cases, it might be easier
to use the standard deviation of a **large** (i.e. greater than five) number of measurements as your
measurement uncertainty.

In order to minimize random errors, it is extremely important that each of your measurements be
performed a few times.

# Experimental Procedure

### Torque vs. Angular Acceleration
The first part of this experiment is to verify that angular acceleration is directly proportional to torque. The result of this experiment will be a graph of Torque vs. Angular Acceleration. The PASCO Capstone Data Acquisition system will measure the acceleration of the hoop and disk system, but to create a known torque, you will apply a known force at a known distance from the axis of rotation. You have a set of weights and hangers at your disposal.

### Setting up the Experiment--Hardware
On top of your Rotary Motion Sensor is a three-stage pulley. You will need to remove this pulley to measure the diameter of each of the stages. Remove any other equipment atop the three-stage pulley by unscrewing the center screw and gently lifting it off the three-stage pulley. The pulley should now slide off the axle. Measure the mass and diameters of the three-stage pulley as well as the masses and dimensions of the ring and disk.

```python
# Raw Data 1
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from P201_Functions import *

# Create an empty numpy array to hold the raw data
raw_data_1 = np.empty((2,6))

# Create a Pandas dataframe
df1 = pd.DataFrame(raw_data_1, columns=[" ",
                                        "Mass (kg)",
                                        "Uncertainty (kg)",
                                        "Radius (m)",
                                        "Uncertainty (m)",
                                         "Moment of Inertia (kg m^2)"])

df1[' '] = ["Disk", "Hoop"]

#### Enter Raw Data Here!!!!!!!!!!!!!! ####

# Pulley diameters (mm) and uncertainties
p1 = 9.45
p1_u = 0.05

p2 = 28.60
p2_u = 0.10

p3 = 47.75
p3_u = 0.20

df1['Mass (kg)'] = [0.11911,0.47027]
df1['Uncertainty (kg)'] = [0.00005,0.00005]

# Radii (m) and their uncertainties
disk_radius = 0.04699
hoop_r1 = 0.02816
hoop_r2 = 0.03815
df1['Uncertainty (m)'] = [0.0005,0.0005]

df1['Moment of Inertia (kg m^2)'] = [1.315e-4, 5.287e-4]

###########################################

hoop_radii = "R1: %0.5f" % (hoop_r1)
df1['Radius (m)'] = [disk_radius, hoop_radii]

# Converts the values to be in scientific notation
mass_sci = ["%g" %(df1['Uncertainty (kg)'][0]),"%g" %(df1['Uncertainty (kg)'][1])]
df1['Uncertainty (kg)'] = mass_sci

from IPython.display import display
display(df1)
print(' '*32, "R2:%0.5f" % (hoop_r2))
print()
```

      Mass (kg)
      Uncertainty (kg)
      Radius (m)
      Uncertainty (m)
      Moment of Inertia (kg m^2)

      0
      Disk
      0.11911
      5e-05
      0.04699
      0.0005
      0.000131

      1
      Hoop
      0.47027
      5e-05
      R1: 0.02816
      0.0005
      0.000529

                                     R2:0.03815

Once you have completed your measurements, you will need to replace the three-stage pulley on the axle. Make sure that the pulley is oriented so that the largest diameter stage is at the bottom of the “stack.” **N.B.:** Please note that the axle has a groove cut into it and the three-stage pulley has a key-way. Ensure that the keyway of the pulley slides into the groove in the axle when you replace the three-stage pulley.

Once the three-stage pulley is oriented properly, place the disk atop it so that the square cut-out locks onto the square shaft on the top of the three-stage pulley. Secure the disk with the knurled screw (This screw may be stored on the bottom of the axle). You may now place the ring on top of the disk so that the two posts in the ring sit in the two holes in the disk (this “locks” the ring to the disk and keeps it from sliding around).

### Setting up the Experiment--Software

1. Make sure that the yellow plug is connected to digital input 1 and the black plug is connected to digital input 2.
2. Power on the PASCO 850 by pressing and holding the power button until it beeps and the green light comes on.
3. Start the PASCO Capstone software.
4. Click “Hardware Setup” on the left-side “Tools” palette. A picture of the PASCO 850 interface will appear.
5. Click on the Digital Input 1 port and select “Rotary Motion Sensor.”
6. At the lower-right, click “Properties” and change “Resolution” to “High: 1440 counts per revolution.” and click “OK.”
7. Click “Hardware Setup” again to close that menu.
8. Drag “Graph” from the right-side “Displays” palette onto the main window. A graph will magically appear. Click on `<Select Measurement>` on your y-axis and select “Angle (rad).”
9. Take some data while rotating the ring to make sure that everything is working properly.

### Data Taking Procedure
The goal of this experiment is to find the angular acceleration of the disk and ring for different applied torques. The torque you apply will be created by the weight you place on the mass hanger (don’t forget the mass of the hanger!) and the radius of the stage you choose to wrap the thread around.

Thread the string through the hole in the stage you will use. Push at least 10 cm of thread through the hole. Wrap the thread around the stage by rotating the stage in the direction opposite to the way you’d like the stage to spin, and letting the thread slide across your hand as you guide it onto the pulley. After a few turns, the thread will have enough friction to stay in place. Continue rotating the stage with just a little tension on the thread.

The mass-hanger end of the string should rest on the “super pulley.” Make sure that the “super pulley” is at a height so that the thread remains level as it travels from the three-stage pulley to the “super pulley.” You can adjust the height of the “super pulley” by loosening the two screws on either side of it (don’t forget to re-tighten them!) Then follow these instructions:

1. Hold the stage so that it does not rotate.
2. Place your mass hanger on the end of the string dangling over the “super pulley.” **Do not exceed 40 g**.
3. Wait for the hanger to stop spinning.
4. Release the stage **first**
5. Click “Record”
6. Click “Stop” **before the hanger hits the ground**.

You will need to perform the same experiment for each mass you choose. Run each mass a few times and use the average and standard deviation of your results. Then change the mass on the hanger and do it again.

### Torque vs. Angular Acceleration
The goal of this experiment is to verify equation ([4](#mjx-eqn)). You will have *two* “point” masses which you must place at identical distances from the axle (this is to keep the system dynamically balanced). Using a single hanging mass which generates the same torque for each trial, you will measure the angular acceleration of the system when the “point” masses are at 14, 15, 16, 17, and 18 cm from the axle. You will compute the moment of inertia for each trial and plot it as a function of distance.

```python
# Torque vs. Angular Acceleration Graph

# Reads the name of the csv file and gets the data
df = pd.read_csv("./ExampleFiles/Torque vs. Angular Acceleration.csv")

# Prints information about the file
df.info()

print()

# Defines the x and y values
aa = df.filter(['Angular Acceleration (rad/s^2)'], axis=1).dropna()
torque = df.filter(['Torque (Nm)'], axis=1).dropna()

delta_aa = np.std(aa)
delta_t = np.std(torque)

# Create a figure of reasonable size and resolution, white background, black edge color
fig=plt.figure(figsize=(7,5), dpi= 100, facecolor='w', edgecolor='k')

# Gets the data values for x and y
x_data = aa.values.reshape(-1, 1)
y_data = torque.values.reshape(-1, 1)

xi = df['Angular Acceleration (rad/s^2)'].to_numpy()
yi = df['Torque (Nm)'].to_numpy()

# Creates the base plot with titles
plt.plot(x_data,y_data,'b.')
#plt.errorbar(x_data,y_data,torque,aa,'b.',label='Raw Data')
plt.ylabel('Torque (Nm)')
plt.xlabel('Angular Acceleration (rad/s^2)')
plt.title('Torque vs. Angular Acceleration')

# Takes the x and y values to make a trendline
intercept, slope, dintercept, dslope = linear_fit_plot_errors(xi,yi,0.97,10.62)

# Adds the legend to the plot
plt.legend()

# Displays the plot
plt.show()

print("")
```

### Setting up the Experiment
Before doing anything, you should take the “point” masses and rod and measure their masses and sizes. Before measuring the mass of the rod, remove the “captured screw.” **Replace this screw immediately after weighing the rod.**

1. Remove the ring and disk and store the knurled screw in the bottom of the axle.
2. Flip the three-stage pulley so that the largest diameter stage is on top (**remember that it’s keyed!**)
3. Place the center of the “point” masses at your first location. Tighten the nylon screws so the masses are firmly held in place.
4. Notice that the “top” of the three-stage pulley has a guide. Set the rod in the guide and secure it to the axle with the screw.

You are now ready to take your data.

**Safety Tip:** Depending on the hanger mass you choose, the rod may be spinning fairly quickly at the end of a run. **Do not try to stop the rod by grabbing the rod!** You can safely stop the rod by grabbing onto the portion of the axle that sticks out of the *bottom* of the sensor (where you stored the knurled screw).

```python
# Raw Data 2: Torque vs. Angular Acceleration

# Create an empty numpy array to hold the raw data
raw_data_2 = np.empty((10,7))

# Set the trial number column identifiers for each Trial
raw_data_2[0][0]=1
raw_data_2[1][0]=2
raw_data_2[2][0]=3
raw_data_2[3][0]=4
raw_data_2[4][0]=5
raw_data_2[5][0]=6
raw_data_2[6][0]=7
raw_data_2[7][0]=8
raw_data_2[8][0]=9

# Create a Pandas dataframe, and convert the Trial number column to integer format
df2 = pd.DataFrame(raw_data_2, columns=["Trial",
                                        "Hanger Mass (kg)",
                                        "Uncertainty (kg)",
                                       "Pulley Radius (m)",
                                       "Uncertainty (m)",
                                       "Torque (Nm)",
                                       "Angular Acceleration(rad/s^2)"])

df2['Trial'] = df2['Trial'].astype(int)

#### Enter Raw Data Here!!!!!!!!!!!!!! ####

df2['Hanger Mass (kg)'] = [0,0,0,0,0,0,0,0,0,0]
df2['Uncertainty (kg)'] = [0,0,0,0,0,0,0,0,0,0]

df2['Pulley Radius (m)'] = [0,0,0,0,0,0,0,0,0,0]
df2['Uncertainty (m)'] = [0,0,0,0,0,0,0,0,0,0]

###########################################

df2['Torque (Nm)'] = df['Torque (Nm)']

df2['Angular Acceleration(rad/s^2)'] = df['Angular Acceleration (rad/s^2)']

from IPython.display import display
display(df2)
print()
```

      Trial
      Hanger Mass (kg)
      Uncertainty (kg)
      Pulley Radius (m)
      Uncertainty (m)
      Torque (Nm)
      Angular Acceleration(rad/s^2)

      0
      1
      0
      0
      0
      0
      0.000694
      0.97

      1
      2
      0
      0
      0
      0
      0.001396
      2.06

      2
      3
      0
      0
      0
      0
      0.002097
      3.16

      3
      4
      0
      0
      0
      0
      0.002799
      4.24

      4
      5
      0
      0
      0
      0
      0.003500
      4.82

      5
      6
      0
      0
      0
      0
      0.004201
      6.42

      6
      7
      0
      0
      0
      0
      0.004903
      7.52

      7
      8
      0
      0
      0
      0
      0.005604
      8.70

      8
      9
      0
      0
      0
      0
      0.006306
      9.80

      9
      0
      0
      0
      0
      0
      0.007007
      10.62

### The Effective Torque
Now that you’ve read through the whole lab hand out, it is important to notice that friction contributes a torque *opposite* to the torque you are applying. Your effective torque is, of course, the difference between these two torques and this is the torque you should be using to compute your moments of inertia. But how do you determine the torque due to friction?

The theoretical frictional torque is given by equation ([3](#mjx-eqn)) if we know the angular acceleration and the moment of inertia of the system. You can measure the angular acceleration due to friction by measuring it right after the system has been spun up by the hanging mass. Simply use Capstone to record the data right after a run while the system is still spinning (although this might be done after *every* run, time constraints might make it more appropriate to measure it one time for each of the two systems). Ten seconds of spin-down measurement should be sufficient. The moment of inertia should be the theoretical one you can compute by knowing the masses and dimensions of the system.

***

```python
# Moment of Inertia vs. Distance from Axle Graph

# Reads the name of the csv file and gets the data
df = pd.read_csv("./ExampleFiles/Moment of Inertia vs. Distance from Axle.csv")

# Prints information about the file
df.info()

#print(df)
print()

# Defines the x and y values
distance = df.filter(['Distance R^2 (m^2)'], axis=1).dropna()
moi = df.filter(['Moment of I (Kg m^2)'], axis=1).dropna()

delta_distance = np.std(distance)
delta_moi = np.std(moi)

# Create a figure of reasonable size and resolution, white background, black edge color
fig=plt.figure(figsize=(7,5), dpi= 100, facecolor='w', edgecolor='k')

# Gets the data values for x and y
x_data = distance.values.reshape(-1, 1)
y_data = moi.values.reshape(-1, 1)

xi = df['Distance R^2 (m^2)'].to_numpy()
yi = df['Moment of I (Kg m^2)'].to_numpy()

# Creates the base plot with titles
plt.plot(x_data,y_data,'b.')
#plt.errorbar(x_data,y_data,torque,aa,'b.',label='Raw Data')
plt.ylabel('Moment of I (Kg m^2)')
plt.xlabel('Distance R^2 (m^2)')
plt.title('Moment of Inertia vs. Distance from Axle')

# Takes the x and y values to make a trendline
intercept, slope, dintercept, dslope = linear_fit_plot_errors(xi,yi,0.000196,0.000324)

# Adds the legend to the plot
plt.legend()

# Displays the plot
plt.show()

print("")
```

```python
# Raw Data 3: Moment of Inertia vs. Distance from Axle

# Create an empty numpy array to hold the raw data
raw_data_3 = np.empty((9,5))

# Create a Pandas dataframe, and convert the Trial number column to integer format
df3 = pd.DataFrame(raw_data_3, columns=["d1 (m)",
                                        "d2 (m)",
                                        "Angular Accel (rad/s^2)",
                                       "Uncertainty (rad/s^2)",
                                       "Moment of I (kg m^2)"])

#### Enter Raw Data Here!!!!!!!!!!!!!! ####

# Mass of "point masses" #1 and #2 (kg)
pm1 = 0
pm2 = 0

# Mass (kg) and length of rod (m)
m_rod = 0
len_rod = 0

# Mass of hanging mass (kg)
m_hang = 0

# Radius of pulley used (m)
rad = 0

# Torque generated (Nm)
torque = 0

df3['d1 (m)'] = [0,0,0,0,0,0,0,0,0]
df3['d2 (m)'] = [0,0,0,0,0,0,0,0,0]

df3['Angular Accel (rad/s^2)'] = [1.390,1.318,0,0,0,0,0,0,0]

###########################################

df3['Uncertainty (rad/s^2)'] = np.std(df3['Angular Accel (rad/s^2)'])

df3['Moment of I (kg m^2)'] = df['Moment of I (Kg m^2)']

# Print statements
print("Static Data:")
print('-'*80)
print("Mass of 'point mass' #1: %0.5f kg      Mass of 'point mass' #2: %0.5f kg" % (pm1, pm2))
print()
print("Mass of rod: %0.5f kg                  Length of rod: %0.3f m" % (m_rod, len_rod))
print()
print("Mass of hanging mass: %0.5f kg         Radius of pulley used: %0.5f m" % (m_hang, rad))
print()
print("Torque Generated: %0.5f Nm" %(torque))
print('-'*80)

from IPython.display import display
display(df3)
print()
```

    Static Data:
    --------------------------------------------------------------------------------
    Mass of 'point mass' #1: 0.00000 kg      Mass of 'point mass' #2: 0.00000 kg

    Mass of rod: 0.00000 kg                  Length of rod: 0.000 m

    Mass of hanging mass: 0.00000 kg         Radius of pulley used: 0.00000 m

    Torque Generated: 0.00000 Nm
    --------------------------------------------------------------------------------

      d1 (m)
      d2 (m)
      Angular Accel (rad/s^2)
      Uncertainty (rad/s^2)
      Moment of I (kg m^2)

      0
      0
      0
      1.390
      0.563167
      0.000349

      1
      0
      0
      1.318
      0.563167
      0.000352

      2
      0
      0
      0.000
      0.563167
      0.000354

      3
      0
      0
      0.000
      0.563167
      0.000356

      4
      0
      0
      0.000
      0.563167
      0.000358

      5
      0
      0
      0.000
      0.563167
      0.000361

      6
      0
      0
      0.000
      0.563167
      0.000363

      7
      0
      0
      0.000
      0.563167
      0.000366

      8
      0
      0
      0.000
      0.563167
      0.000369

***

# Your Report
### Introduction:
Write a few sentences about what you set out to measure and how you will compare the measured values with theory (Hint: Write the Introduction **last**. This way you’ll have the whole lab in your head when you write it and you can properly foreshadow the results). Do not include details here. That is what the rest of the report is for.

### Theory:
For a good theory section, you will need to write a few paragraphs which answer the following questions:

1) What is your theoretical prediction for the slope of the first graph?
2) What is your theoretical prediction for the shape of the second graph?
3) Detail the method you used for computing any uncertainties (but don’t use any numbers, here).

Explain your reasoning! Use equations where necessary. They should each be on a separate line and numbered (like in this handout).

### Experimental Procedure:
Detail carefully the experiments you performed and how you performed them so that someone could repeat your experiment by reading this section. Talk about any problems you encountered and how you handled them.

### Results and Conclusion:
This will be your longest section. Use good paragraph structure to link the results together:

Compare your theoretical prediction of the slope of the Torque vs. Angular Acceleration graph to the experimental value. Compare the fit of the Moment of Inertia vs. Distance to Axle graph with your theoretical prediction. Do your results support or refute Newton’s Second Law for rotation?

As always, talk about your errors and how you might be able to reduce them. Make sure that you discuss all possible sources of error in your experiments.

### Appendix
- Include your signed raw data sheets.
- Include your sample calculations.