```python
import torch
model = torch.hub.load('pytorch/vision:v0.4.2', 'squeezenet1_0', pretrained=True)
from sklearn.linear_model import Lasso
import numpy as np
import matplotlib.pyplot as plt
from skimage.segmentation import slic
from skimage.segmentation import mark_boundaries
import urllib
from PIL import Image
from torchvision import transforms
```

    Using cache found in C:\Users\44754/.cache\torch\hub\pytorch_vision_v0.4.2

### Local Interpretable Model-agnostic Explanations
Given the original represntation of an instance being explained $x$ (the image), we can generate a binary vector $x'$ for its interpretable representation. In this case, $x'$ is an one vector of the length of number of superpixels. \\ \noindent
A perturbed sample $z'$ is a binary vector which is sampled from $x'$ randomly under the uniform distribution. Then a fake image $z$ with less superpixels is generated.

$$\xi(x) = \arg\min_{g\in G} \underbrace{\mathcal{L}(f,g,\pi_x)}_{\text{cost}} +\underbrace{\Omega (g)}_{\text{complexity}}$$

where

\begin{align}
    \mathcal{L}(f,g,\pi_x) &= \sum_{z,z' \in \mathrm{Z}}\underbrace{\pi_x(z)}_{\text{proximity}}(\overbrace{f(z)}^{\text{blackbox}}-\underbrace{g(z')}_{\text{linear}})^2 \\
    g(z') &= w_g\cdot z' \\
    \Omega(g) &= \infty \mathbf{1}[\|w_g\|_0>K] \\
    \pi_x(z) &= \exp(-\frac{\|x-z\|^2_2}{\sigma^2})
\end{align}

where $f(x)$ is the neural network model which predicts the probability of $x$ belonging to a certain class, $g$ an interpretable model, $\pi_x(z)$ the proximity measure between an instance $z$ to $x$, $\mathcal{L}$ the fidelity function and $\Omega$ the complexity measure.

$\mathcal{L}(f,g,\pi_x)$ measures how unfaithful $g$ is approximating $f$ in the locality defined by $\pi_x$. If this term is small, it represents $g$ faithfully approximates $f$.

$\Omega(g)$ is similar to a regularization term, it limits the complexity of the approximate model $g$. If $g$ is a random forest algorithm, it limits the depth by $K$. If $g$ is a linear model, it limits the number of features.
Our aim is to approximate $f(z)$ by $g(z')$ as follows,

$$f(z)\approx g(z') = w_g\cdot z'$$

Least absolute shrinkage and selection operator (lasso) is applied to compute the representation of $w_g$. The lasso estimate ($\hat{w}_g$) is defined by,

$$\hat{w}_g = \arg\min\{(f(z)-w_g \cdot z')^2\} \qquad \text{subject to } |w_g| \leq K$$

Compared with ridge regression which only shrinks coefficients, lasso can set some coefficients to 0, retain the relatively important varaible and give an easily interpretable model.

\begin{align}
&\xi(x) = \arg\min_{g\in G}\mathcal{L}(f,g,\pi_x)+\Omega(g) \\
&= \arg\min_{w_g}\sum_{z,z'\in Z}\pi_x(z)(f(z)-w_g\cdot z')^2+\infty \mathbf{1}[\|w_g\|_0>K]
\end{align}

\begin{align}
    \hat{w}_g = \arg\min_{w_g}\sum_{z,z'\in Z}(f(z)-w_g\cdot z')+\lambda\|w_g\|_1
\end{align}

```python
class LIME:
    def __init__(self,Image,Network,NumSuperpixels,NumSamples,LabelID):
        self.img = Image
        self.net = Network
        self.nsp = NumSuperpixels
        self.nss = NumSamples
        self.lid = LabelID
    def preprocess(self):
        preprocess = transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        #transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
        ])
        input_tensor = preprocess(input_image)
        input_batch = input_tensor.unsqueeze(0)
        return input_batch
    def classify(self,xtensor):
        if torch.cuda.is_available():
            xtensor = xtensor.to('cuda')
            self.net.to('cuda')
        self.net.eval()
        with torch.no_grad():
            output = self.net(xtensor)
            out = torch.nn.functional.softmax(output, dim=1)
        return out
    def segmentdata(self):
        input_batch = self.preprocess()
        segments=slic(np.moveaxis(input_batch.squeeze().cpu().numpy(),0,-1),n_segments=self.nsp,sigma = 5)
        NumFeatures = len(np.unique(segments))
        NumRemainList=np.random.randint(1,NumFeatures,size=self.nss)
        segmentBinary = []
        for i in range(self.nss):
            tmpBinaryVec = np.zeros((1,NumFeatures))
            tmpNumRemain = NumRemainList[i]
            tmpRemainIdx = np.random.randint(NumFeatures,size=tmpNumRemain)
            tmpBinaryVec[:,tmpRemainIdx] = 1
            segmentBinary.append(tmpBinaryVec)
        return segments,np.array(segmentBinary).squeeze()
    def score(self,xtensor,segments,segmentBinary):
        for i in range(self.nss):
            RemainIdx = np.where(segmentBinary[i]==1)
            tmpMask = np.isin(segments,RemainIdx).reshape(1,1,224,224)
            tmpMask3= torch.from_numpy(np.tile(tmpMask,(1,3,1,1)))
            if i == 0:
                fudgedScores = self.classify(xtensor*tmpMask3)[:,self.lid]
            else:
                tmpfudgedScores = self.classify(xtensor*tmpMask3)[:,self.lid]
                fudgedScores = torch.cat([fudgedScores,tmpfudgedScores],dim=0)
        return fudgedScores.cpu().numpy()
```

## Import SqueezeNet, image and ImageNet labels

```python
url, filename = ("https://github.com/pytorch/hub/raw/master/dog.jpg", "dog.jpg")
try: urllib.URLopener().retrieve(url, filename)
except: urllib.request.urlretrieve(url, filename)
input_image = Image.open(filename)
```

```python
lime = LIME(input_image,model,30,1024,258)
input_batch = lime.preprocess()
output = lime.classify(input_batch)
```

```python
plt.imshow(np.moveaxis(input_batch.squeeze().cpu().numpy(),0,-1))
```

```python
with open("imagenet1000_clsidx_to_labels.txt") as f:
    idx2label = eval(f.read())
for idx in output[0].sort()[1][-5:]:
    print(str(idx.item())+': '+idx2label[idx.item()])
```

    332: Angora, Angora rabbit
    257: Great Pyrenees
    157: papillon
    279: Arctic fox, white fox, Alopex lagopus
    258: Samoyed, Samoyede

## Superpixels

```python
segments,segmentBinary = lime.segmentdata()
dispimg = np.moveaxis(input_batch.squeeze().cpu().numpy(),0,-1)
plt.subplot(1,3,1); plt.imshow(dispimg)
plt.subplot(1,3,2); plt.imshow(mark_boundaries(dispimg, segments))
plt.subplot(1,3,3); plt.imshow(segments)
```

## Local Interpretable Model-Agnostic Explanations (LIME)

```python
scores = lime.score(input_batch,segments,segmentBinary)
# Lasso
clf = Lasso(alpha=.00001)
clf.fit(segmentBinary,scores)
segmentImportance = clf.coef_
# Colormap of importance
mapWeight = np.zeros(segments.shape)
for i in range(len(np.unique(segments))):
    mapWeight[np.where(segments==i)] = segmentImportance[i]
# Plot
plt.matshow(mapWeight)
plt.set_cmap('jet')
plt.colorbar()
```

## Result

```python
fig=plt.figure(figsize=(18, 16), dpi= 80, facecolor='w', edgecolor='k')
plt.subplot(2,2,1); plt.imshow(dispimg); plt.axis('off'); plt.title('Samoyed, Samoyede')
plt.subplot(2,2,2); plt.imshow(mark_boundaries(dispimg, segments)); plt.axis('off'); plt.title('Superpixels margins')
plt.subplot(2,2,3); plt.imshow(segments); plt.set_cmap('gray'); plt.axis('off'); plt.title('Superpixels regions')
plt.subplot(2,2,4)
plt.imshow(mapWeight)
plt.set_cmap('jet')
plt.colorbar()
plt.axis('off')
plt.title('Activation map to Label')
```

```python

```


## First Last - SymPy

```python
import sympy as sp
```

```python
sp.init_printing()
x = sp.symbols('x')
```

## What is the denominator of the third term of the Taylor expansion for the equation

$$ \Large \frac{\sin{\left (x \right )}}{x^{4} + 27}$$

```python

```

```python

```

```python

```

## Answer the Canvas quiz `SymPyQuiz`


# The Explicit Forward Time Centered Space (FTCS) Difference Equation for the Heat Equation
## The Heat Equation
The Heat Equation is the first order in time ($t$) and second order in space ($x$) Partial Differential Equation:
$$  \frac{\partial u}{\partial t} = \frac{\partial^2 u}{\partial x^2},$$
The equation describes heat transfer on a domain
$$ \Omega = \{ t \geq, 0\leq x \leq 1\}. $$
with an initial condition at time $t=0$ for all $x$ and boundary condition on the left ($x=0$) and right side ($x=1$).

## Forward Time Centered Space (FTCS) Difference method
This note book will illustrate the Forward Time Centered Space (FTCS) Difference method for the Heat Equation with the __initial conditions__
$$ u(x,0)=2x, \ \ 0 \leq x \leq \frac{1}{2}, $$
$$ u(x,0)=2(1-x), \ \ \frac{1}{2}  \leq x \leq 1, $$
and __boundary condition__
$$ u(0,t)=0,  u(1,t)=0. $$

```python
# LIBRARY
# vector manipulation
import numpy as np
# math functions
import math

# THIS IS FOR PLOTTING
%matplotlib inline
import matplotlib.pyplot as plt # side-stepping mpl backend
import warnings
warnings.filterwarnings("ignore")
from IPython.display import HTML

HTML('''
<form action="javascript:code_toggle()"><input type="submit" value="Click here to toggle on/off the raw code."></form>''')

```

<form action="javascript:code_toggle()"><input type="submit" value="Click here to toggle on/off the raw code."></form>

## Discete Grid
The region $\Omega$ is discretised into a uniform mesh $\Omega_h$. In the space $x$ direction into $N$ steps giving a stepsize of
$$ h=\frac{1-0}{N},$$
resulting in
$$x[i]=0+ih,$$
and into $N_t$ steps in the time $t$ direction giving a stepsize of
$$ k=\frac{1-0}{N}$$
resulting in
$$t[i]=0+ik.$$
The Figure below shows the discrete grid points for $N=10$ and $N_t=15$, the red dots are the unknown values, the green dots are the known boundary conditions and the blue dots are the known initial conditions of the Heat Equation.

```python
N=10
Nt=200
h=1/N
k=1/Nt
r=k/(h*h)
time_steps=15
time=np.arange(0,(time_steps+.5)*k,k)
x=np.arange(0,1.0001,h)
X, Y = np.meshgrid(x, time)
fig = plt.figure()
plt.plot(X,Y,'ro');
plt.plot(x,0*x,'bo',label='Initial Condition');
plt.plot(np.ones(time_steps+1),time,'go',label='Boundary Condition');
plt.plot(x,0*x,'bo');
plt.plot(0*time,time,'go');
plt.xlim((-0.02,1.02))
plt.xlabel('x')
plt.ylabel('time (ms)')
plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.title(r'Discrete Grid $\Omega_h,$ h= %s, k=%s'%(h,k),fontsize=24,y=1.08)
plt.show();
```

## Discrete Initial and Boundary Conditions

The discrete initial conditions are
$$ w[i,0]=2x[i], \ \ 0 \leq x[i] \leq \frac{1}{2} $$
$$ w[i,0]=2(1-x[i]), \ \ \frac{1}{2}  \leq x[i] \leq 1 $$
and the discete boundary conditions are
$$ w[0,j]=0,  w[10,j]=0, $$
where $w[i,j]$ is the numerical approximation of $U(x[i],t[j])$.

The Figure below plots the inital and boundary conditions for $t[0]=0.$

```python
w=np.zeros((N+1,time_steps+1))
b=np.zeros(N-1)
# Initial Condition
for i in range (1,N):
    w[i,0]=2*x[i]
    if x[i]>0.5:
        w[i,0]=2*(1-x[i])

# Boundary Condition
for k in range (0,time_steps):
    w[0,k]=0
    w[N,k]=0

fig = plt.figure(figsize=(8,4))
plt.plot(x,w[:,0],'o:',label='Initial Condition')
plt.plot(x[[0,N]],w[[0,N],0],'go',label='Boundary Condition t[0]=0')
#plt.plot(x[N],w[N,0],'go')
plt.xlim([-0.1,1.1])
plt.ylim([-0.1,1.1])
plt.title('Intitial and Boundary Condition',fontsize=24)
plt.xlabel('x')
plt.ylabel('w')
plt.legend(loc='best')
plt.show()
```

## The Explicit Forward Time Centered Space (FTCS) Difference Equation
The explicit Forwards Time Centered Space (FTCS) equation difference equation of the Heat Equation is
\begin{equation*}
\frac{w_{ij+1}-w_{ij}}{k}=\frac{w_{i+1j}-2w_{ij}+w_{i-1j}}{h^2}
\end{equation*}
Rearranging the equation we get
\begin{equation}
w_{ij+1}=rw_{i-1j}+(1-2r)w_{ij}+rw_{i+1j}
\end{equation}
for $i=1,...9$ where $r=\frac{k}{h^2}$.

This gives the formula for the unknown term $w_{ij+1}$ at the $(ij+1)$ mesh points
in terms of $x[i]$ along the jth time row.

Hence we can calculate the unknown pivotal values of $w$ along the first row of $j=1$ in terms of the known boundary conditions.

This can be written in matrix form
$$ \mathbf{w}_{j+1}=A\mathbf{w}_{j} +\mathbf{b}_{j} $$
for which $A$ is a $9\times9$ matrix:
$$
\left(\begin{array}{cccc cccc}
1-2r&r& 0&0&0 &0&0&0\\
r&1-2r&r&0&0&0 &0&0&0\\
0&r&1-2r &r&0&0& 0&0&0\\
0&0&r&1-2r &r&0&0& 0&0\\
0&0&0&r&1-2r &r&0&0& 0\\
0&0&0&0&r&1-2r &r&0&0\\
0&0&0&0&0&r&1-2r &r&0\\
0&0&0&0&0&0&r&1-2r&r\\
0&0&0&0&0&0&0&r&1-2r\\
\end{array}\right)
\left(\begin{array}{c}
w_{1j}\\
w_{2j}\\
w_{3j}\\
w_{4j}\\
w_{5j}\\
w_{6j}\\
w_{7j}\\
w_{8j}\\
w_{9j}\\
\end{array}\right)=
\left(\begin{array}{c}
rw_{0j}\\
0\\
0\\
0\\
0\\
0\\
0\\
0\\
rw_{10j}\\
\end{array}\right).
$$
It is assumed that the boundary values $w_{0j}$ and $w_{10j}$ are known for $j=1,2,...$, and $w_{i0}$ for $i=0,...,10$ is the initial condition.

The Figure below shows the matrix in color plot form for $r$.

```python
A=np.zeros((N-1,N-1))
for i in range (0,N-1):
    A[i,i]=1-2*r

for i in range (0,N-2):
    A[i+1,i]=r
    A[i,i+1]=r

fig = plt.figure(figsize=(6,4));
#plt.matshow(A);
plt.imshow(A,interpolation='none');
plt.xticks(np.arange(N-1), np.arange(1,N-0.9,1));
plt.yticks(np.arange(N-1), np.arange(1,N-0.9,1));
clb=plt.colorbar();
clb.set_label('Matrix elements values');
clb.set_clim((-1,1));
plt.title('Matrix r=%s'%(np.round(r,3)),fontsize=24)
fig.tight_layout()
plt.show();
```

## Results
To numerically approximate the solution at $t[1]$ the matrix equation becomes
$$ \mathbf{w}_{1}=A\mathbf{w}_{0} +\mathbf{b}_{0} $$
where all the right hand side is known.
To approximate solution at time $t[2]$ we use the matrix equation
$$ \mathbf{w}_{2}=A\mathbf{w}_{1} +\mathbf{b}_{1}. $$
Each set of numerical solutions $w[i,j]$ for all $i$ at the previous time step is used to approximate the solution $w[i,j+1]$.
The left and right plot below show the numerical approximation $w[i,j]$ of the Heat Equation using the FTCS method for $x[i]$ for $i=0,...,10$ and time steps $t[j]$ for $j=1,...,15$.

```python
fig = plt.figure(figsize=(12,6))

plt.subplot(121)
for j in range (1,time_steps+1):
    w[1:(N),j]=np.dot(A,w[1:(N),j-1])
    plt.plot(x,w[:,j],'o:',label='t[%s]=%s'%(j,time[j]))
plt.xlabel('x')
plt.ylabel('w')
#plt.legend(loc='bottom', bbox_to_anchor=(0.5, -0.1))
plt.legend(bbox_to_anchor=(-.4, 1), loc=2, borderaxespad=0.)

plt.subplot(122)
plt.imshow(w.transpose())
plt.xticks(np.arange(len(x)), x)
plt.yticks(np.arange(len(time)), time)
plt.xlabel('x')
plt.ylabel('time')
clb=plt.colorbar()
clb.set_label('Temperature (w)')
plt.suptitle('Numerical Solution of the  Heat Equation r=%s'%(np.round(r,3)),fontsize=24,y=1.08)
fig.tight_layout()
plt.show()
```

```python

```

Chapter 08 - Heat Equations/.ipynb_checkpoints/02_Heat Equation- BTCS-checkpoint.ipynb


# Frequentist Inference Case Study - Part B

## Learning objectives

Welcome to Part B of the Frequentist inference case study! The purpose of this case study is to help you apply the concepts associated with Frequentist inference in Python. In particular, you'll practice writing Python code to apply the following statistical concepts:
* the _z_-statistic
* the _t_-statistic
* the difference and relationship between the two
* the Central Limit Theorem, including its assumptions and consequences
* how to estimate the population mean and standard deviation from a sample
* the concept of a sampling distribution of a test statistic, particularly for the mean
* how to combine these concepts to calculate a confidence interval

In the previous notebook, we used only data from a known normal distribution. **You'll now tackle real data, rather than simulated data, and answer some relevant real-world business problems using the data.**

## Hospital medical charges

Imagine that a hospital has hired you as their data scientist. An administrator is working on the hospital's business operations plan and needs you to help them answer some business questions.

In this assignment notebook, you're going to use frequentist statistical inference on a data sample to answer the questions:
* has the hospital's revenue stream fallen below a key threshold?
* are patients with insurance really charged different amounts than those without?

Answering that last question with a frequentist approach makes some assumptions, and requires some knowledge, about the two groups.

We are going to use some data on medical charges obtained from [Kaggle](https://www.kaggle.com/easonlai/sample-insurance-claim-prediction-dataset).

For the purposes of this exercise, assume the observations are the result of random sampling from our single hospital. Recall that in the previous assignment, we introduced the Central Limit Theorem (CLT), and its consequence that the distributions of sample statistics approach a normal distribution as $n$ increases. The amazing thing about this is that it applies to the sampling distributions of statistics that have been calculated from even highly non-normal distributions of data! Recall, also, that hypothesis testing is very much based on making inferences about such sample statistics. You're going to rely heavily on the CLT to apply frequentist (parametric) tests to answer the questions in this notebook.

```python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import scipy
import scipy.stats as stats
from scipy.stats import t
from numpy.random import seed
medical = pd.read_csv('insurance2.csv')
```

```python
medical.shape
```

    (1338, 8)

```python
medical.head()
```

      age
      sex
      bmi
      children
      smoker
      region
      charges
      insuranceclaim

      0
      19
      0
      27.900
      0
      1
      3
      16884.92400
      1

      1
      18
      1
      33.770
      1
      0
      2
      1725.55230
      1

      2
      28
      1
      33.000
      3
      0
      2
      4449.46200
      0

      3
      33
      1
      22.705
      0
      0
      1
      21984.47061
      0

      4
      32
      1
      28.880
      0
      0
      1
      3866.85520
      1

__Q1:__ Plot the histogram of charges and calculate the mean and standard deviation. Comment on the appropriateness of these statistics for the data.

__A:__ The population mean is approximatly $13270 with standard deviation of $12105. However, from the visualization we can see that the histogram is heavily skewed to the left which suggests that it would be more appropriate to use median value as a measure of central tendency instead of the mean.

```python
charges = medical.charges
plt.figure(figsize=([10,5]))
plt.hist(charges, bins=20);
plt.xlabel('Charges, USD')
plt.ylabel('Counts');
```

```python
charges_mean = np.mean(charges)
charges_std = np.std(charges)
print('Charges mean: ', charges_mean)
print('Charges STD: ', charges_std)
```

    Charges mean:  13270.422265141257
    Charges STD:  12105.484975561605

__Q2:__ The administrator is concerned that the actual average charge has fallen below 12,000, threatening the hospital's operational model. On the assumption that these data represent a random sample of charges, how would you justify that these data allow you to answer that question? And what would be the most appropriate frequentist test, of the ones discussed so far, to apply?

__A:__ If we assume that the data given to us by the administrator represent a random sample, then, according to the Central Limit Theorem, distribution of those samples will aproach normal shape as their number increases. t-test would be most appropriate in this situation.

__Q3:__ Given the nature of the administrator's concern, what is the appropriate confidence interval in this case? A ***one-sided*** or ***two-sided*** interval? (Refresh your understanding of this concept on p. 399 of the *AoS*). Calculate the critical value and the relevant 95% confidence interval for the mean, and comment on whether the administrator should be concerned.

__A:__ As we can see, 95% interval usin z-value is $12620-$13919, which is significantly above $12000 given to us by the administrator. The margin of error is aproximatly 649$.

```python
def mean_confidence_interval(data, confidence=0.95):
    a = 1.0 * np.array(data)
    n = len(a)
    m, se = np.mean(a), scipy.stats.sem(a)
    h = se * scipy.stats.t.ppf((1 + confidence) / 2., n-1)
    return m-h, m, m+h
```

```python
z_interval = mean_confidence_interval(charges)
print(z_interval)
```

    (12620.954034192644, 13270.422265141257, 13919.890496089869)

```python
moe = np.std((charges/np.sqrt(len(charges)))*1.96)
print(moe)
```

    648.6496792559246

The administrator then wants to know whether people with insurance really are charged a different amount to those without.

__Q4:__ State the null and alternative hypothesis here. Use the _t_-test for the difference between means, where the pooled standard deviation of the two groups is given by:
\begin{equation}
s_p = \sqrt{\frac{(n_0 - 1)s^2_0 + (n_1 - 1)s^2_1}{n_0 + n_1 - 2}}
\end{equation}

and the *t*-test statistic is then given by:

\begin{equation}
t = \frac{\bar{x}_0 - \bar{x}_1}{s_p \sqrt{1/n_0 + 1/n_1}}.
\end{equation}

(If you need some reminding of the general definition of ***t-statistic***, check out the definition on p. 404 of *AoS*).

What assumption about the variances of the two groups are we making here?

__A:__ Null hypothethis: People with insurance are charged the same amount to those withouth insurance.
       Alternative hypothesis: People with insurance are charged different amount to those withouth insurance.

__Q5:__ Perform this hypothesis test both manually, using the above formulae, and then using the appropriate function from [scipy.stats](https://docs.scipy.org/doc/scipy/reference/stats.html#statistical-tests) (hint, you're looking for a function to perform a _t_-test on two independent samples). For the manual approach, calculate the value of the test statistic and then its probability (the p-value). Verify you get the same results from both.

__A:__ We got the same results uing manual calculations and scipy.stats_ttest_ind() function.

```python
ins = medical[medical['insuranceclaim'] == 1]
noins = medical[medical['insuranceclaim'] != 1]
```

```python
ins_charges = ins['charges']
noins_charges = noins.charges
```

```python
manual_std = np.sqrt(((len(ins_charges)-1)*ins_charges.std()**2+(len(noins_charges)-1)*noins_charges.std()**2)
                     /(len(ins_charges) + len(noins_charges) - 2))
```

```python
manual_t_test = (np.mean(ins_charges) - np.mean(noins_charges))/(manual_std*np.sqrt((1/len(ins_charges)) + (1/len(noins_charges))))
```

```python
print(manual_std)
print(manual_t_test)
```

    11520.034268775256
    11.89329903087671

```python
t_test_ind = stats.ttest_ind(ins_charges, noins_charges)
```

```python
t_test_ind
```

    Ttest_indResult(statistic=11.893299030876712, pvalue=4.461230231620717e-31)

Congratulations! Hopefully you got the exact same numerical results. This shows that you correctly calculated the numbers by hand. Secondly, you used the correct function and saw that it's much easier to use. All you need to do is pass your data to it.

__Q6:__ Conceptual question: look through the documentation for statistical test functions in scipy.stats. You'll see the above _t_-test for a sample, but can you see an equivalent one for performing a *z*-test from a sample? Comment on your answer.

__A:__ No, scipy.stats does not provide us with a z-test from a sample.

## Learning outcomes

Having completed this project notebook, you now have good hands-on experience:
* using the central limit theorem to help you apply frequentist techniques to answer questions that pertain to very non-normally distributed data from the real world
* performing inference using such data to answer business questions
* forming a hypothesis and framing the null and alternative hypotheses
* testing this using a _t_-test


Contenido bajo licencia Creative Commons BY 4.0 y código bajo licencia MIT. © Juan Gómez y Nicolás Guarín-Zapata 2020. Este material es parte del curso Modelación Computacional en el programa de Ingeniería Civil de la Universidad EAFIT.

# Transformación de coordenadas en cerchas

```python
from sympy import *
```

```python
init_session()
```

    IPython console for SymPy 1.4 (Python 3.7.3-64-bit) (ground types: python)

    These commands were executed:
    >>> from __future__ import division
    >>> from sympy import *
    >>> x, y, z, t = symbols('x y z t')
    >>> k, m, n = symbols('k m n', integer=True)
    >>> f, g, h = symbols('f g h', cls=Function)
    >>> init_printing()

    Documentation can be found at https://docs.sympy.org/1.4/

## Transformación de coordenadas de vectores

```python

```

## Transformación de las ecuaciones de un elemento cercha

Tomemos un elemento tipo cercha que se encuentra horizontal.

Para este, podemos escribir
el balance de fuerzas en componentes como

\begin{align}
\sum F_x = 0\, ,\\
\sum F_y = 0\, .
\end{align}

Y esto podemos escribirlo de forma matricial de la siguiente manera

$$\begin{bmatrix}
+k &0 &-k &0\\
 0 &0 &0  &0\\
-k &0 &+k &0\\
 0 &0 &0  &0\end{bmatrix}
\begin{Bmatrix} u_1\\ v_1\\ u_2\\ v_2\end{Bmatrix} =
\begin{Bmatrix} -f_1\\ 0\\ -f_2\\ 0\end{Bmatrix}\, .$$

Ahora, queremos expresar nuestro sistema de ecuaciones en un sistema rotado
un ángulo $\theta$.

Las componentes estarían dadas por

\begin{align}
u_1' = u_1 \cos\theta + v_1 \sin\theta\, ,\\
v_1' = -u_1 \sin\theta + v_1 \cos\theta\, ,\\
u_2' = u_2 \cos\theta + v_2 \sin\theta\, ,\\
v_2' = -u_2 \sin\theta + v_2 \cos\theta\, ,\\
\end{align}

o, matricialmente,

$$\begin{Bmatrix}u_1'\\ v_1'\\ u_2'\\ v_2'\end{Bmatrix} =
\underbrace{
\begin{bmatrix}
\cos\theta &\sin\theta &0 &0\\
-\sin\theta &\cos\theta &0 &0\\
0 &0 &\cos\theta &\sin\theta\\
0 &0 &-\sin\theta &\cos\theta
\end{bmatrix}}_{T}
\begin{Bmatrix}u_1\\ v_1\\ u_2\\ v_2\end{Bmatrix}\, .$$

Que podemos escribir de manera abreviada como

$$\{U'\} = [T]\{U\}\, .$$

Tenemos que

\begin{align}
&[K] \{U'\} = \{F\}\, ,\\
&[K] [T] \{U\} = [T] \{F\}\, .
\end{align}

Y, multiplicando ambos lados por $[T]^T$, obtenemos
\begin{align}
&[T]^T [K] [T] \{U\} = \underbrace{[T]^T [T]}_{[I]} \{F\}\, ,\\
&[T]^T [K] [T] \{U\} = \{F\}\, .
\end{align}

## Ejemplo

Calculemos las matrices de rigidez para los elementos de la siguiente
cercha.

En donde la sección transversal ($A$) y módulo de Young ($E$) son los
mismos para todas las barras.

Para facilitar los cálculos, haremos uso de SymPy.

```python
A, E, L, theta = symbols("A E L theta")
L0, L1, L2 = symbols("L0 L1 L2")
T = Matrix([
    [cos(theta), -sin(theta), 0 ,0],
    [sin(theta), cos(theta), 0 ,0],
    [0, 0, cos(theta), -sin(theta)],
    [0, 0, sin(theta), cos(theta)]])
rigidez_local = A*E/L * Matrix([
    [1, 0, -1, 0],
    [0, 0,  0, 0],
    [-1, 0, 1, 0],
    [0, 0, 0, 0]])
```

Veamos las matrices que definimos.

```python
T
```

```python
rigidez_local
```

### Elemento 0

Para este elemento, tenemos que el ángulo es 0, luego

```python
rigidez_0 = T.T.subs(theta, 0) * rigidez_local.subs(L, L0) * T.subs(theta, 0)
rigidez_0
```

### Elemento 1

Para este elemento $\theta = 5\pi/6$, luego

```python
rigidez_1 = T.T.subs(theta, 5*pi/6) * rigidez_local.subs(L, L1) * T.subs(theta, 5*pi/6)
rigidez_1
```

### Elemento 2

Para este elemento $\theta = \pi/2$, luego

```python
rigidez_2 = T.T.subs(theta, pi/2) * rigidez_local.subs(L, L2) * T.subs(theta, pi/2)
rigidez_2
```

## Referencias

1. Abani Patra. (Marzo 15, 1999). Trusses and Transformations. Fecha de acceso: Octubre 28, 2019, disponible en: http://www.eng.buffalo.edu/~abani/fem/node6.html.