# Python Analyzer

---

## Reference

* 주피터 노트북의 단축기

    - 1. Command Mode ( press Esc to enable)
        - 1. shift-Enter : run cell, select below
        - 2. Ctrl-Enter : run cell
        - 3. Alt-Enter : run cell, insert below
        - 4. Y : to code
        - 5. M : to markdown
        - 6. B : insert cell below
        - 7. X : cut selected cell
        - 8. C : copy selected cell
        - 9. Shift-V : paste cell above
        - 10. V : paste cell below
        - 11. Z : undo last cell deletion
        - 12. D,D : delete selected cell
        - 13. Shift-M : merge cell below

    - 2. Edit Mode ( press Enter to enable)
        - 1. Shift-Tab : 툴팁표시
        - 2. Ctrl-] : indent
        - 3. Ctrl-Shift- : split cell

* 주피터 노트북 마크다운 관련 참고 자료
    - 1. https://datanetworkanalysis.github.io/2019/10/12/markdown
    - 2. https://steemit.com/kr/@nand/markdown

* Pandas 데이터 분석
    - 1. https://stmorse.github.io/journal/tidyverse-style-pandas.html

* Python 통계 분석
    - 1. https://partrita.github.io/posts/pingouin/
    - 2. https://pingouin-stats.org/index.html
    - 3. https://www.marsja.se/repeated-measures-anova-in-r-and-python-afex-pingouin/

## Basic Setting & Package Install

* 경로 설정

```python
import os
currDir = os.getcwd()
# os.chdir(r'/Users/deepmem/Desktop/working/aniFCnet/anime/results/an_01')
# os.chdir(r'/Users/deepmem/Desktop/working/aniFCnet/anime/results')
# os.chdir(r'/Users/deepmem/Desktop/working/aniFCnet/anime/results')
os.chdir(currDir)
```

* 패키지 설치

```python
### Data Handling & Analysis
#!pip install numpy scipy pandas sympy statsmodels pingouin
#!conda install numpy scipy pandas sympy statsmodels pingouin

### Plot
#!pip install matplotlib seaborn plotnine bokeh
#!conda install matplotlib seaborn plotnine bokeh

### ML & DL
#!pip install scikit-learn tensorflow keras
#!conda install scikit-learn tensorflow keras
```

* 패키지 불러오기

```python
# ---------------------------------------------------------------------------

# for Data Processing
import pandas as pd
import numpy as np

# ---------------------------------------------------------------------------

# for Plotting

## - matplotlib
import matplotlib as mpl
import matplotlib.pyplot as plt
import pylab as pl

## - seaborn
import seaborn as sns

## - plotnine
import plotnine as ggplot

# ---------------------------------------------------------------------------

# for Statistic

## scipy
from scipy import stats

## sympy
import sympy as sym

## statmodels
import statsmodels as modeling

## pingouin
import pingouin as pg

# ---------------------------------------------------------------------------

## for Machine Learning
import sklearn
from sklearn import preprocessing, feature_selection, linear_model, svm, metrics, model_selection

## for Deep Learning
# import tensorflow as tp
# import keras

# ---------------------------------------------------------------------------

# Reference

# ## - preprocessing
# from sklearn.preprocessing import StandardScaler
# from sklearn.preprocessing import label_binarize

# ## - feature selection
# ## * https://scikit-learn.org/stable/modules/feature_selection.html
# ## * https://datascienceschool.net/03%20machine%20learning/14.03%20%ED%8A%B9%EC%A7%95%20%EC%84%A0%ED%83%9D.html
# from sklearn.feature_selection import VarianceThreshold
# from sklearn.feature_selection import SelectKBest
# from sklearn.feature_selection import chi2
# from sklearn.feature_selection import f_classif
# from sklearn.feature_selection import f_regression

# # ---------------------------------------------------------------------------

# ## - model
# ## * https://scikit-learn.org/stable/modules/linear_model.html
# from sklearn.linear_model import RidgeClassifier
# from sklearn.linear_model import LogisticRegression

# ## * https://scikit-learn.org/stable/modules/svm.html
# from sklearn import svm

# ## * https://xgboost.readthedocs.io/en/latest/
# from xgboost import XGBClassifier

# # ---------------------------------------------------------------------------

# ## - metric, performance measure
# ## * https://scikit-learn.org/stable/modules/model_evaluation.html
# from sklearn.metrics import classification_report
# from sklearn.metrics import accuracy_score
# from sklearn.metrics import roc_auc_score
# from sklearn.metrics import confusion_matrix

# ## * https://scikit-learn.org/stable/auto_examples/model_selection/plot_permutation_tests_for_classification.html#sphx-glr-auto-examples-model-selection-plot-permutation-tests-for-classification-py
# from sklearn.model_selection import permutation_test_score

# # ---------------------------------------------------------------------------

# ## - model selection & evaluation
# ## * https://scikit-learn.org/stable/modules/cross_validation.html
# from sklearn import model_selection
# from sklearn.model_selection import train_test_split
# from sklearn.model_selection import KFold, LeaveOneOut
# from sklearn.model_selection import StratifiedKFold
# from sklearn.model_selection import cross_val_score #prediction
# from sklearn.model_selection import cross_val_predict #prediction
# from sklearn.model_selection import cross_validate #prediction
```

```python

# Global setting
import warnings
warnings.filterwarnings('ignore')

sns.set_style('white')
%matplotlib inline

import platform

# 웬만하면 해주는 것이 좋다.
from matplotlib import font_manager, rc
plt.rcParams['axes.unicode_minus']= False

if platform.system() == 'Darwin': # 맥os 사용자의 경우에
    plt.style.use('seaborn-darkgrid')
    rc('font', family = 'AppleGothic')

elif platform.system() == 'Windows':# 윈도우 사용자의 경우에
    path = 'c:/Windows/Fonts/malgun.ttf'
    font_name = font_manager.FontProperties(fname=path).get_name()
    plt.style.use('seaborn-darkgrid') # https://python-graph-gallery.com/199-matplotlib-style-sheets/
    rc('font', family=font_name)

font = {'weight' : 'bold',
        'size'   : 15}

mpl.rc('font', **font)
```

## Exploratory Data Analysis

### Data Load & Glimpse

```python
data_dir =  ('%s/%s' % (currDir, 'Sample_Data'))
df = pd.read_csv(('%s/conSrep_lme.csv' % (data_dir)), encoding='euc-kr')
```

```python
df.head(3)
```

      subj
      roi
      cond
      ll
      item
      comp1
      comp2
      z
      rd_ll
      rd_cond
      Zr_1
      Zr_2
      Zr_s
      Zr_m
      b_1
      b_2
      b_s
      b_m
      Zr_wm
      b_wm

      0
      1
      loc
      1
      1
      10105
      1
      5
      0.667815
      0.783697
      0.770063
      0.241053
      0.166046
      0.203550
      0.223921
      0.126249
      0.017386
      0.071818
      0.130898
      NaN
      NaN

      1
      1
      loc
      1
      1
      10107
      1
      7
      0.873861
      0.495769
      0.486178
      0.577057
      0.279873
      0.428465
      0.468502
      0.462390
      -0.039847
      0.211272
      0.387839
      NaN
      NaN

      2
      1
      loc
      1
      1
      10111
      1
      11
      0.893776
      0.547011
      0.549124
      0.513251
      0.342322
      0.427786
      0.518879
      0.331198
      0.136710
      0.233954
      0.481692
      NaN
      NaN

```python
df.shape
```

    (38016, 20)

```python
# 결측치 개수 파악
# 셀 실행 결과를 데이터프레임으로 보고 싶을 때 to_frame()과 pd.DataFrame() 두 가지를 사용 가능
df.isnull().sum().to_frame('nan_count')
```

      nan_count

      subj
      0

      roi
      0

      cond
      0

      ll
      0

      item
      0

      comp1
      0

      comp2
      0

      z
      0

      rd_ll
      0

      rd_cond
      0

      Zr_1
      0

      Zr_2
      0

      Zr_s
      0

      Zr_m
      0

      b_1
      0

      b_2
      0

      b_s
      0

      b_m
      0

      Zr_wm
      38016

      b_wm
      38016

```python
pd.DataFrame(data=df.isnull().sum()/len(df),columns=['nan_ratio'])
```

      nan_ratio

      subj
      0.0

      roi
      0.0

      cond
      0.0

      ll
      0.0

      item
      0.0

      comp1
      0.0

      comp2
      0.0

      z
      0.0

      rd_ll
      0.0

      rd_cond
      0.0

      Zr_1
      0.0

      Zr_2
      0.0

      Zr_s
      0.0

      Zr_m
      0.0

      b_1
      0.0

      b_2
      0.0

      b_s
      0.0

      b_m
      0.0

      Zr_wm
      1.0

      b_wm
      1.0

```python
df['subj'].value_counts()
```

    1     1584
    2     1584
    98    1584
    97    1584
    96    1584
    94    1584
    20    1584
    19    1584
    18    1584
    17    1584
    16    1584
    13    1584
    15    1584
    14    1584
    12    1584
    11    1584
    10    1584
    9     1584
    8     1584
    7     1584
    6     1584
    5     1584
    3     1584
    99    1584
    Name: subj, dtype: int64

```python
df['roi'].unique()
```

    array(['loc', 'lloc', 'rloc', 'pf', 'lpf', 'rpf', 'bhpp', 'lhpp', 'rhpp',
           'a_hpp', 'p_hpp', 'mpfc'], dtype=object)

```python
df.info()
```

    <class 'pandas.core.frame.DataFrame'>
    RangeIndex: 38016 entries, 0 to 38015
    Data columns (total 20 columns):
     #   Column   Non-Null Count  Dtype
    ---  ------   --------------  -----
     0   subj     38016 non-null  int64
     1   roi      38016 non-null  object
     2   cond     38016 non-null  int64
     3   ll       38016 non-null  int64
     4   item     38016 non-null  int64
     5   comp1    38016 non-null  int64
     6   comp2    38016 non-null  int64
     7   z        38016 non-null  float64
     8   rd_ll    38016 non-null  float64
     9   rd_cond  38016 non-null  float64
     10  Zr_1     38016 non-null  float64
     11  Zr_2     38016 non-null  float64
     12  Zr_s     38016 non-null  float64
     13  Zr_m     38016 non-null  float64
     14  b_1      38016 non-null  float64
     15  b_2      38016 non-null  float64
     16  b_s      38016 non-null  float64
     17  b_m      38016 non-null  float64
     18  Zr_wm    0 non-null      float64
     19  b_wm     0 non-null      float64
    dtypes: float64(13), int64(6), object(1)
    memory usage: 5.8+ MB

```python
# 범주형이지만 연속형(int64)으로 설정되어있는 경우 string으로 바꿔주기
df['subj']=df['subj'].astype(str)
df['cond']=df['cond'].astype(str)
df['ll']=df['ll'].astype(str)
df['item']=df['item'].astype(str)
df['comp1']=df['comp1'].astype(str)
df['comp2']=df['comp2'].astype(str)
df.dtypes
```

    subj        object
    roi         object
    cond        object
    ll          object
    item        object
    comp1       object
    comp2       object
    z          float64
    rd_ll      float64
    rd_cond    float64
    Zr_1       float64
    Zr_2       float64
    Zr_s       float64
    Zr_m       float64
    b_1        float64
    b_2        float64
    b_s        float64
    b_m        float64
    Zr_wm      float64
    b_wm       float64
    dtype: object

### Preprocessing

* **tidyverse style pandas**
    * **tidyr - pandas**
        * mutate - assign
        * select - filter
        * rename - rename
        * filter - query
        * arrange - sort_values
        * group_by - groupby
        * summarize - agg
        * gather - melt
        * spread - pivot

```python
df.describe(include='all')
```

      subj
      roi
      cond
      ll
      item
      comp1
      comp2
      z
      rd_ll
      rd_cond
      Zr_1
      Zr_2
      Zr_s
      Zr_m
      b_1
      b_2
      b_s
      b_m
      Zr_wm
      b_wm

      count
      38016
      38016
      38016
      38016
      38016
      38016
      38016
      38016.000000
      38016.000000
      38016.000000
      38016.000000
      38016.000000
      38016.000000
      38016.000000
      38016.000000
      38016.000000
      38016.000000
      38016.000000
      0.0
      0.0

      unique
      24
      12
      2
      4
      264
      19
      19
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN

      top
      1
      loc
      1
      1
      20203
      1
      14
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN

      freq
      1584
      3168
      19008
      9504
      216
      6312
      4056
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN

      mean
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      0.268370
      0.890519
      0.891206
      0.077718
      0.077431
      0.077574
      0.096636
      0.046737
      0.046192
      0.046465
      0.095144
      NaN
      NaN

      std
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      0.633812
      0.144079
      0.142009
      0.210166
      0.210233
      0.178061
      0.223164
      0.159033
      0.162393
      0.112774
      0.229099
      NaN
      NaN

      min
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      -5.375781
      0.275210
      0.295550
      -1.166095
      -1.129066
      -0.895102
      -1.154292
      -1.015663
      -1.378982
      -0.683348
      -1.369066
      NaN
      NaN

      25%
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      -0.093046
      0.843164
      0.846574
      -0.051240
      -0.051194
      -0.030740
      -0.041172
      -0.047971
      -0.047607
      -0.022167
      -0.044297
      NaN
      NaN

      50%
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      0.206467
      0.941848
      0.944719
      0.057624
      0.055460
      0.050731
      0.067215
      0.037029
      0.036473
      0.034590
      0.070366
      NaN
      NaN

      75%
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      0.574504
      0.983062
      0.982577
      0.185612
      0.186247
      0.159385
      0.208060
      0.129384
      0.129994
      0.104299
      0.211549
      NaN
      NaN

      max
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      NaN
      4.492980
      1.302216
      1.291541
      1.210359
      1.481196
      1.009792
      1.182038
      1.132774
      1.357869
      0.687433
      1.689432
      NaN
      NaN

```python
df2 = (df
 .filter(['subj', 'roi', 'cond', 'item', 'z', 'b_m', 'rd_ll'])
 .query('roi == "loc" | roi == "pf"')
 .rename(columns={'subj':'sn', 'cond':'con', 'z':'Z', 'b_m':'beta', 'rd_ll':'Zr_n'}))
df2['con'] = df2['con'].replace(['1','2'], ['cons','imm'])
```

```python
df2.info()
```

    <class 'pandas.core.frame.DataFrame'>
    Int64Index: 6336 entries, 0 to 36959
    Data columns (total 7 columns):
     #   Column  Non-Null Count  Dtype
    ---  ------  --------------  -----
     0   sn      6336 non-null   object
     1   roi     6336 non-null   object
     2   con     6336 non-null   object
     3   item    6336 non-null   object
     4   Z       6336 non-null   float64
     5   beta    6336 non-null   float64
     6   Zr_n    6336 non-null   float64
    dtypes: float64(3), object(4)
    memory usage: 396.0+ KB

#### Long Format

```python
Z_long = (df2
          .filter(['sn', 'roi', 'con', 'Z'])
          .groupby(['sn', 'roi', 'con'])
          .agg({'Z': ['mean', 'std', 'count']}))

beta_long = (df2
             .filter(['sn', 'roi', 'con', 'beta'])
             .groupby(['sn', 'roi', 'con'])
             .agg({'beta': ['mean', 'std', 'count']}))

Zr_long = (df2
           .filter(['sn', 'roi', 'con', 'Zr_n'])
           .assign(Zr=lambda x: 1-x.Zr_n)
           .filter(['sn', 'roi', 'con', 'Zr'])
           .groupby(['sn', 'roi', 'con'])
           .agg({'Zr': ['mean', 'std', 'count']}))
```

```python
print(Z_long.head(4))
print(beta_long.head(4))
print(Zr_long.head(4))
```

                        Z
                     mean       std count
    sn roi con
    1  loc cons  1.085557  0.591436    66
           imm   0.895288  0.540499    66
       pf  cons  0.406269  0.351890    66
           imm   0.324837  0.348329    66
                     beta
                     mean       std count
    sn roi con
    1  loc cons  0.394441  0.261579    66
           imm   0.288257  0.251469    66
       pf  cons  0.079983  0.161896    66
           imm   0.074869  0.179356    66
                       Zr
                     mean       std count
    sn roi con
    1  loc cons  0.441953  0.091241    66
           imm   0.379559  0.099872    66
       pf  cons  0.122856  0.070878    66
           imm   0.105637  0.067683    66

#### Wide Format

```python
Z_wide = (Z_long['Z'].reset_index()
 .filter(['sn','roi', 'con', 'mean'])
 .rename(columns={'mean':'Z'})
 .pivot(index=['sn','roi'], columns='con', values='Z'))

beta_wide = (beta_long['beta'].reset_index()
 .filter(['sn','roi', 'con', 'mean'])
 .rename(columns={'mean':'beta'})
 .pivot(index=['sn','roi'], columns='con', values='beta'))

Zr_wide = (Zr_long['Zr'].reset_index()
 .filter(['sn','roi', 'con', 'mean'])
 .rename(columns={'mean':'Zr'})
 .pivot(index=['sn','roi'], columns='con', values='Zr'))

```

```python
print(Z_wide.head(4))
print(beta_wide.head(4))
print(Zr_wide.head(4))
```

    con         cons       imm
    sn roi
    1  loc  1.085557  0.895288
       pf   0.406269  0.324837
    10 loc  0.455391  0.460376
       pf   0.220089  0.291423
    con         cons       imm
    sn roi
    1  loc  0.394441  0.288257
       pf   0.079983  0.074869
    10 loc  0.124292  0.134956
       pf   0.041539  0.070503
    con         cons       imm
    sn roi
    1  loc  0.441953  0.379559
       pf   0.122856  0.105637
    10 loc  0.162410  0.146782
       pf   0.070441  0.061856

#### Outlier Handling

```python
df2_Z = (df2
         .filter(['sn', 'roi', 'con', 'Z']))
df2_beta = (df2
         .filter(['sn', 'roi', 'con', 'beta']))
df2_Zr = (df2
          .filter(['sn', 'roi', 'con', 'Zr_n'])
          .assign(Zr=lambda x: 1-x.Zr_n)
          .filter(['sn','roi','con','Zr']))
```

```python
print(df2_Z.head(2))
print(df2_beta.head(2))
print(df2_Zr.head(2))
```

      sn  roi   con         Z
    0  1  loc  cons  0.667815
    1  1  loc  cons  0.873861
      sn  roi   con      beta
    0  1  loc  cons  0.130898
    1  1  loc  cons  0.387839
      sn  roi   con        Zr
    0  1  loc  cons  0.216303
    1  1  loc  cons  0.504231

```python
# df2_Z_outliers = (df2_Z
#                   .groupby(['sn','roi'])
#                   .Z.transform(lambda x : (x<x.quantile(0.95))&(x>(x.quantile(0.05)))).eq(0))
df2_Z_outliers = (df2_Z
                  .groupby(['sn','roi'])
                  .Z.transform(lambda x : (x<np.mean(x)+np.std(x)*2)&(x>np.mean(x)-np.std(x)*2)).eq(0))

df2_Z_outliers = df2_Z_outliers.to_frame().rename(columns={'Z':'out'})
df2_Z = pd.concat([df2_Z, df2_Z_outliers], axis=1)

# df2_beta_outliers = (df2_beta
#                      .groupby(['sn','roi'])
#                      .beta.transform(lambda x : (x<x.quantile(0.95))&(x>(x.quantile(0.05)))).eq(0))
df2_beta_outliers = (df2_beta
                  .groupby(['sn','roi'])
                  .beta.transform(lambda x : (x<np.mean(x)+np.std(x)*2)&(x>np.mean(x)-np.std(x)*2)).eq(0))

df2_beta_outliers = df2_beta_outliers.to_frame().rename(columns={'beta':'out'})
df2_beta = pd.concat([df2_beta, df2_beta_outliers], axis=1)

# df2_Zr_outliers = (df2_Zr
#                    .groupby(['sn','roi'])
#                    .Zr.transform(lambda x : (x<x.quantile(0.95))&(x>(x.quantile(0.05)))).eq(0))
df2_Zr_outliers = (df2_Zr
                  .groupby(['sn','roi'])
                  .Zr.transform(lambda x : (x<np.mean(x)+np.std(x)*2)&(x>np.mean(x)-np.std(x)*2)).eq(0))

df2_Zr_outliers = df2_Zr_outliers.to_frame().rename(columns={'Zr':'out'})
df2_Zr = pd.concat([df2_Zr, df2_Zr_outliers], axis=1)
```

```python
print('*Z\n',df2_Z.out.value_counts())
print('*beta\n',df2_beta.out.value_counts())
print('*Zr\n',df2_Zr.out.value_counts())
```

    *Z
     False    6036
    True      300
    Name: out, dtype: int64
    *beta
     False    6012
    True      324
    Name: out, dtype: int64
    *Zr
     False    6058
    True      278
    Name: out, dtype: int64

```python
Z_long = (df2_Z
          .filter(['sn', 'roi', 'con', 'Z'])
          .groupby(['sn', 'roi', 'con'])
          .agg({'Z': ['mean', 'std', 'count']}))

beta_long = (df2_beta
             .filter(['sn', 'roi', 'con', 'beta'])
             .groupby(['sn', 'roi', 'con'])
             .agg({'beta': ['mean', 'std', 'count']}))

Zr_long = (df2_Zr
           .filter(['sn', 'roi', 'con', 'Zr'])
           .groupby(['sn', 'roi', 'con'])
           .agg({'Zr': ['mean', 'std', 'count']}))
```

### Summarize

#### Activation

```python
Z_G = (Z_long['Z'].reset_index()
       .filter(['sn', 'roi', 'con', 'mean'])
       .rename(columns={'mean': 'Z'})
       .groupby(['roi', 'con'])
       .agg({'Z': ['mean', 'std', 'sem']}))

Z_long_cons = (Z_long['Z'].reset_index()
               .filter(['sn', 'roi', 'con', 'mean'])
               .rename(columns={'mean': 'Z'})
               .query('con =="cons"'))
Z_long_imm = (Z_long['Z'].reset_index()
              .filter(['sn', 'roi', 'con', 'mean'])
              .rename(columns={'mean': 'Z'})
              .query('con =="imm"'))

ci = pd.DataFrame([stats.t.interval(alpha=0.95,
                                    df=len(
                                        Z_long_cons[Z_long_cons['roi'] == 'loc'])-1,
                                    loc=np.mean(
                                        Z_long_cons[Z_long_cons['roi'] == 'loc'].Z),
                                    scale=stats.sem(Z_long_cons[Z_long_cons['roi'] == 'loc'].Z)),
                   stats.t.interval(alpha=0.95, df=len(Z_long_imm[Z_long_imm['roi'] == 'loc'])-1,
                                    loc=np.mean(
                                        Z_long_imm[Z_long_imm['roi'] == 'loc'].Z),
                                    scale=stats.sem(Z_long_imm[Z_long_imm['roi'] == 'loc'].Z)),
                   stats.t.interval(alpha=0.95,
                                    df=len(
                                        Z_long_cons[Z_long_cons['roi'] == 'pf'])-1,
                                    loc=np.mean(
                                        Z_long_cons[Z_long_cons['roi'] == 'pf'].Z),
                                    scale=stats.sem(Z_long_cons[Z_long_cons['roi'] == 'pf'].Z)),
                   stats.t.interval(alpha=0.95, df=len(Z_long_imm[Z_long_imm['roi'] == 'pf'])-1,
                                    loc=np.mean(
                                        Z_long_imm[Z_long_imm['roi'] == 'pf'].Z),
                                    scale=stats.sem(Z_long_imm[Z_long_imm['roi'] == 'pf'].Z))
                   ])

Z_G = pd.concat([Z_G.Z.reset_index(), ci], axis=1)  # column bind
Z_G = (Z_G
       .rename(columns={0: '95ci_hi', 1: '95ci_lo'}))
Z_G
```

      roi
      con
      mean
      std
      sem
      95ci_hi
      95ci_lo

      0
      loc
      cons
      0.744149
      0.339561
      0.069313
      0.600765
      0.887533

      1
      loc
      imm
      0.703449
      0.343071
      0.070029
      0.558583
      0.848315

      2
      pf
      cons
      0.247465
      0.182439
      0.037240
      0.170428
      0.324502

      3
      pf
      imm
      0.232592
      0.189177
      0.038616
      0.152710
      0.312475

#### Beta

```python
beta_G = (beta_long['beta'].reset_index()
          .filter(['sn', 'roi', 'con', 'mean'])
          .rename(columns={'mean': 'beta'})
          .groupby(['roi', 'con'])
          .agg({'beta': ['mean', 'std', 'sem']}))

beta_long_cons = (beta_long['beta'].reset_index()
                  .filter(['sn', 'roi', 'con', 'mean'])
                  .rename(columns={'mean': 'beta'})
                  .query('con =="cons"'))
beta_long_imm = (beta_long['beta'].reset_index()
                 .filter(['sn', 'roi', 'con', 'mean'])
                 .rename(columns={'mean': 'beta'})
                 .query('con =="imm"'))

ci = pd.DataFrame([stats.t.interval(alpha=0.95,
                                    df=len(
                                        beta_long_cons[beta_long_cons['roi'] == 'loc'])-1,
                                    loc=np.mean(
                                        beta_long_cons[beta_long_cons['roi'] == 'loc'].beta),
                                    scale=stats.sem(beta_long_cons[beta_long_cons['roi'] == 'loc'].beta)),
                   stats.t.interval(alpha=0.95, df=len(beta_long_imm[beta_long_imm['roi'] == 'loc'])-1,
                                    loc=np.mean(
                                        beta_long_imm[beta_long_imm['roi'] == 'loc'].beta),
                                    scale=stats.sem(beta_long_imm[beta_long_imm['roi'] == 'loc'].beta)),
                   stats.t.interval(alpha=0.95,
                                    df=len(
                                        beta_long_cons[beta_long_cons['roi'] == 'pf'])-1,
                                    loc=np.mean(
                                        beta_long_cons[beta_long_cons['roi'] == 'pf'].beta),
                                    scale=stats.sem(beta_long_cons[beta_long_cons['roi'] == 'pf'].beta)),
                   stats.t.interval(alpha=0.95, df=len(beta_long_imm[beta_long_imm['roi'] == 'pf'])-1,
                                    loc=np.mean(
                                        beta_long_imm[beta_long_imm['roi'] == 'pf'].beta),
                                    scale=stats.sem(beta_long_imm[beta_long_imm['roi'] == 'pf'].beta))
                   ])
beta_G = pd.concat([beta_G.beta.reset_index(), ci], axis=1)  # column bind
beta_G = (beta_G
          .rename(columns={0: '95ci_hi', 1: '95ci_lo'}))
beta_G
```

      roi
      con
      mean
      std
      sem
      95ci_hi
      95ci_lo

      0
      loc
      cons
      0.284502
      0.136095
      0.027780
      0.227034
      0.341970

      1
      loc
      imm
      0.260502
      0.128566
      0.026243
      0.206213
      0.314790

      2
      pf
      cons
      0.093906
      0.075034
      0.015316
      0.062221
      0.125590

      3
      pf
      imm
      0.085180
      0.071422
      0.014579
      0.055021
      0.115339

#### Zr

```python
Zr_G = (Zr_long['Zr'].reset_index()
        .filter(['sn', 'roi', 'con', 'mean'])
        .rename(columns={'mean': 'Zr'})
        .groupby(['roi', 'con'])
        .agg({'Zr': ['mean', 'std', 'sem']}))

Zr_long_cons = (Zr_long['Zr'].reset_index()
                .filter(['sn', 'roi', 'con', 'mean'])
                .rename(columns={'mean': 'Zr'})
                .query('con =="cons"'))
Zr_long_imm = (Zr_long['Zr'].reset_index()
               .filter(['sn', 'roi', 'con', 'mean'])
               .rename(columns={'mean': 'Zr'})
               .query('con =="imm"'))

ci = pd.DataFrame([stats.t.interval(alpha=0.95,
                                    df=len(
                                        Zr_long_cons[Zr_long_cons['roi'] == 'loc'])-1,
                                    loc=np.mean(
                                        Zr_long_cons[Zr_long_cons['roi'] == 'loc'].Zr),
                                    scale=stats.sem(Zr_long_cons[Zr_long_cons['roi'] == 'loc'].Zr)),
                   stats.t.interval(alpha=0.95, df=len(Zr_long_imm[Zr_long_imm['roi'] == 'loc'])-1,
                                    loc=np.mean(
                                        Zr_long_imm[Zr_long_imm['roi'] == 'loc'].Zr),
                                    scale=stats.sem(Zr_long_imm[Zr_long_imm['roi'] == 'loc'].Zr)),
                   stats.t.interval(alpha=0.95,
                                    df=len(
                                        Zr_long_cons[Zr_long_cons['roi'] == 'pf'])-1,
                                    loc=np.mean(
                                        Zr_long_cons[Zr_long_cons['roi'] == 'pf'].Zr),
                                    scale=stats.sem(Zr_long_cons[Zr_long_cons['roi'] == 'pf'].Zr)),
                   stats.t.interval(alpha=0.95, df=len(Zr_long_imm[Zr_long_imm['roi'] == 'pf'])-1,
                                    loc=np.mean(
                                        Zr_long_imm[Zr_long_imm['roi'] == 'pf'].Zr),
                                    scale=stats.sem(Zr_long_imm[Zr_long_imm['roi'] == 'pf'].Zr))
                   ])
Zr_G = pd.concat([Zr_G.Zr.reset_index(), ci], axis=1)  # column bind
Zr_G = (Zr_G
        .rename(columns={0: '95ci_hi', 1: '95ci_lo'}))
Zr_G
```

      roi
      con
      mean
      std
      sem
      95ci_hi
      95ci_lo

      0
      loc
      cons
      0.296430
      0.135909
      0.027742
      0.239041
      0.353819

      1
      loc
      imm
      0.277132
      0.128406
      0.026211
      0.222910
      0.331353

      2
      pf
      cons
      0.103879
      0.067542
      0.013787
      0.075359
      0.132400

      3
      pf
      imm
      0.093853
      0.058852
      0.012013
      0.069002
      0.118704

#### Plotting

```python
df_01 = (Z_long['Z'].reset_index()
         .rename(columns={'mean':'Z'}))
df_02 = (beta_long['beta'].reset_index()
         .rename(columns={'mean':'beta'}))
df_03 = (Zr_long['Zr'].reset_index()
         .rename(columns={'mean':'Zr'}))
df_all = pd.merge(df_01, df_02, how='inner', on=['sn','roi','con'])
df_all = pd.merge(df_all, df_03, how='inner', on=['sn','roi','con'])
df_all = (df_all
          .filter(['sn','roi','con','Z','beta','Zr']))
#df_all
```

```python

fig, axes = plt.subplots(nrows = 2, ncols=3, figsize=(18, 12))
sns.kdeplot(data=Z_long['Z'].reset_index(), x="mean", hue="con", ax=axes[0,0])
sns.kdeplot(data=beta_long['beta'].reset_index(), x="mean", hue="con", ax=axes[0,1])
sns.kdeplot(data=Zr_long['Zr'].reset_index(), x="mean", hue="con", ax=axes[0,2])
# sns.lmplot(data=df_all, x="Z", y="beta", hue="con", ax=axes[1,0])
# sns.lmplot(data=df_all, x="Z", y="Zr", hue="con", ax=axes[1,1])
# sns.lmplot(data=df_all, x="beta", y="Zr", hue="con", ax=axes[1,2])
sns.scatterplot(data=df_all, x="Z", y="beta", hue="con", ax=axes[1,0])
sns.scatterplot(data=df_all, x="Z", y="Zr", hue="con", ax=axes[1,1])
sns.scatterplot(data=df_all, x="beta", y="Zr", hue="con", ax=axes[1,2])
#sns.stripplot(data=df_all, x="con", y="Z", hue="con", ax=axes[2,0])
#sns.boxplot(data=df_all, x="con", y="Z", hue="con", dodge=False, ax=axes[2,0])
#sns.boxplot(data=df_all, x="con", y="beta", hue="con",dodge=False, ax=axes[2,1])
#sns.boxplot(data=df_all, x="con", y="Zr", hue="con", dodge=False,ax=axes[2,2])
plt.tight_layout()
plt.show()
```

```python
g1 = sns.catplot(data=df_all, x="con", y="Z", hue="con", col = "roi", kind='boxen', dodge=False)
g2 = sns.catplot(data=df_all, x="con", y="beta", hue="con", col = "roi", kind='boxen', dodge=False)
g3 = sns.catplot(data=df_all, x="con", y="Zr", hue="con", col = "roi", kind='boxen', dodge=False)
plt.tight_layout()
plt.show()
```

## Statistical Test - rmANOVA & Post-hoc

### Assumption Check

```python
pg.homoscedasticity(data=Z_long.Z.reset_index(), dv='mean', group='con',
                   method='levene')
```

      W
      pval
      equal_var

      levene
      0.005552
      0.940762
      True

```python
spher, W, chisq, dof, pval = pg.sphericity(data=Z_long.Z.reset_index(), dv='mean',
              subject='sn', within=['roi','con'], method='mauchly')
print(spher, round(W, 3), round(chisq, 3), dof, round(pval, 3))
```

    True nan nan 1 1.0

```python
pg.normality(data=Z_long.Z.reset_index(), dv='mean', group='con',
            method='normaltest') # shapiro
```

      W
      pval
      normal

      cons
      5.957387
      0.050859
      True

      imm
      3.612369
      0.164280
      True

### Object-ness - Activation

```python
aov = pg.rm_anova(dv='mean', within=['roi','con'],
                   subject='sn', data=Z_long.Z.reset_index(), detailed=True)
aov.round(3)
```

      Source
      SS
      ddof1
      ddof2
      MS
      F
      p-unc
      p-GG-corr
      np2
      eps

      0
      roi
      5.617
      1
      23
      5.617
      63.311
      0.000
      0.000
      0.734
      1.0

      1
      con
      0.019
      1
      23
      0.019
      2.394
      0.135
      0.135
      0.094
      1.0

      2
      roi * con
      0.004
      1
      23
      0.004
      3.051
      0.094
      0.094
      0.117
      1.0

```python
fig, axes = plt.subplots(ncols=2, figsize=(12, 4))
sns.pointplot(x="roi", y="mean", hue="con",
                    data=Z_long.Z.reset_index(), ax=axes[0])
sns.pointplot(x="con", y="mean", hue="roi",
                    data=Z_long.Z.reset_index(), ax=axes[1])
plt.tight_layout()
plt.show()
```

```python
#targ_df = Z_long.Z.reset_index()
# loc_z = targ_df[targ_df['roi']=='loc'].pairwise_tukey(dv='mean', between='con').round(3)
# loc_z['roi'] = 'loc'
# pf_z =  targ_df[targ_df['roi']=='pf'].pairwise_tukey(dv='mean', between='con').round(3)
# pf_z['roi'] = 'pf'
# pd.concat([loc_z, pf_z], axis=0)

targ_df = Z_long.Z.reset_index()
loc_z = (targ_df[targ_df['roi']=='loc']
          .pairwise_ttests(dv='mean', within='con', subject='sn',
                           parametric=True, padjust='bonf', effsize = 'cohen',
                           return_desc=True)
          .round(3))
loc_z['roi'] = 'loc'
pf_z = (targ_df[targ_df['roi']=='pf']
          .pairwise_ttests(dv='mean', within='con', subject='sn',
                           parametric=True, padjust='bonf', effsize = 'cohen',
                           return_desc=True)
          .round(3))
pf_z['roi'] = 'pf'
print_table(pd.concat([loc_z, pf_z], axis=0)
```

### Constituent Explainability - Beta

```python
aov = pg.rm_anova(dv='mean', within=['roi','con'],
                   subject='sn', data=beta_long.beta.reset_index(), detailed=True)
aov.round(3)
```

      Source
      SS
      ddof1
      ddof2
      MS
      F
      p-unc
      p-GG-corr
      np2
      eps

      0
      roi
      0.803
      1
      23
      0.803
      58.053
      0.000
      0.000
      0.716
      1.0

      1
      con
      0.006
      1
      23
      0.006
      8.372
      0.008
      0.008
      0.267
      1.0

      2
      roi * con
      0.001
      1
      23
      0.001
      7.416
      0.012
      0.012
      0.244
      1.0

```python
fig, axes = plt.subplots(ncols=2, figsize=(12, 4))
sns.pointplot(x="roi", y="mean", hue="con",
                    data=beta_long.beta.reset_index(), ax=axes[0])
sns.pointplot(x="con", y="mean", hue="roi",
                    data=beta_long.beta.reset_index(), ax=axes[1])
plt.tight_layout()
plt.show()
```

```python
targ_df = beta_long.beta.reset_index()
loc_beta = (targ_df[targ_df['roi']=='loc']
          .pairwise_ttests(dv='mean', within='con', subject='sn',
                           parametric=True, padjust='bonf', effsize = 'cohen',
                           return_desc=True)
          .round(3))
loc_beta['roi'] = 'loc'
pf_beta = (targ_df[targ_df['roi']=='pf']
          .pairwise_ttests(dv='mean', within='con', subject='sn',
                           parametric=True, padjust='bonf', effsize = 'cohen',
                           return_desc=True)
          .round(3))
pf_beta['roi'] = 'pf'
pd.concat([loc_beta, pf_beta], axis=0)
```

      Contrast
      A
      B
      mean(A)
      std(A)
      mean(B)
      std(B)
      Paired
      Parametric
      T
      dof
      alternative
      p-unc
      BF10
      cohen
      roi

      0
      con
      cons
      imm
      0.285
      0.136
      0.261
      0.129
      True
      True
      3.320
      23.0
      two-sided
      0.003
      13.554
      0.181
      loc

      0
      con
      cons
      imm
      0.094
      0.075
      0.085
      0.071
      True
      True
      1.666
      23.0
      two-sided
      0.109
      0.714
      0.119
      pf

### Representational Similarity - Zr

```python
aov = pg.rm_anova(dv='mean', within=['roi','con'],
                   subject='sn', data=Zr_long.Zr.reset_index(), detailed=True)
aov.round(3)
```

      Source
      SS
      ddof1
      ddof2
      MS
      F
      p-unc
      p-GG-corr
      np2
      eps

      0
      roi
      0.847
      1
      23
      0.847
      67.664
      0.000
      0.000
      0.746
      1.0

      1
      con
      0.005
      1
      23
      0.005
      12.181
      0.002
      0.002
      0.346
      1.0

      2
      roi * con
      0.001
      1
      23
      0.001
      3.134
      0.090
      0.090
      0.120
      1.0

```python
fig, axes = plt.subplots(ncols=2, figsize=(12, 4))
sns.pointplot(x="roi", y="mean", hue="con",
                    data=Zr_long.Zr.reset_index(), ax=axes[0])
sns.pointplot(x="con", y="mean", hue="roi",
                    data=Zr_long.Zr.reset_index(), ax=axes[1])
plt.tight_layout()
plt.show()
```

```python
targ_df = Zr_long.Zr.reset_index()
loc_Zr = (targ_df[targ_df['roi']=='loc']
          .pairwise_ttests(dv='mean', within='con', subject='sn',
                           parametric=True, padjust='bonf', effsize = 'cohen',
                           return_desc=True)
          .round(3))
loc_Zr['roi'] = 'loc'
pf_Zr = (targ_df[targ_df['roi']=='pf']
          .pairwise_ttests(dv='mean', within='con', subject='sn',
                           parametric=True, padjust='bonf', effsize = 'cohen',
                           return_desc=True)
          .round(3))
pf_Zr['roi'] = 'pf'
pd.concat([loc_Zr, pf_Zr], axis=0)
```

      Contrast
      A
      B
      mean(A)
      std(A)
      mean(B)
      std(B)
      Paired
      Parametric
      T
      dof
      alternative
      p-unc
      BF10
      cohen
      roi

      0
      con
      cons
      imm
      0.296
      0.136
      0.277
      0.128
      True
      True
      3.227
      23.0
      two-sided
      0.004
      11.202
      0.146
      loc

      0
      con
      cons
      imm
      0.104
      0.068
      0.094
      0.059
      True
      True
      2.754
      23.0
      two-sided
      0.011
      4.367
      0.158
      pf

## Statistical Test - Correlation & Partial Corr

```python
targ_df1 = Z_long.Z.reset_index()
z_df = (targ_df1
        .filter(['sn','roi','con','mean'])
        .rename(columns={'mean':'z'}))

targ_df2 = beta_long.beta.reset_index()
beta_df = (targ_df2
        .filter(['sn','roi','con','mean'])
        .rename(columns={'mean':'beta'}))

targ_df3 = Zr_long.Zr.reset_index()
Zr_df = (targ_df3
        .filter(['sn','roi','con','mean'])
        .rename(columns={'mean':'Zr'}))

df_fnl = pd.merge(z_df, beta_df, how='inner', on=['sn','roi','con'])
df_fnl = pd.merge(df_fnl, Zr_df, how='inner', on=['sn','roi','con'])
```

```python
df_cons = df_fnl[(df_fnl['con'] == 'cons') & (df_fnl['roi']=='loc')]
df_imm = df_fnl[(df_fnl['con'] == 'imm')  & (df_fnl['roi']=='loc')]
```

```python
df_dff = pd.merge(df_cons, df_imm, how='inner', on=['sn','roi'])
```

```python
df_dff = (df_dff
         .assign(z = lambda x: x.z_x-x.z_y,
                beta = lambda x: x.beta_x-x.beta_y,
                Zr = lambda x: x.Zr_x-x.Zr_y)
         .filter(['sn','roi','z','beta','Zr']))
```

```python
df_dff.head()
```

      sn
      roi
      z
      beta
      Zr

      0
      1
      loc
      0.190268
      0.106183
      0.062394

      1
      10
      loc
      -0.004985
      -0.010664
      0.015628

      2
      11
      loc
      0.136077
      0.005941
      0.029292

      3
      12
      loc
      0.079221
      -0.001217
      0.020856

      4
      13
      loc
      -0.035033
      0.043352
      0.050008

* Assumption Check

```python
pg.multivariate_normality(df_dff[['z', 'beta', 'Zr']])
```

    HZResults(hz=0.7703488985936779, pval=0.10968901247185164, normal=True)

### Activation & Beta

```python
sns.jointplot(data=df_dff, x='z', y='beta', kind='reg')
```

* Pearson Correlation

```python
pg.corr(df_dff.z, df_dff.beta,
        alternative="two-sided",
        method="pearson") # pearson, spearman, kendall, bicor, percbend, shepherd, skipped
```

      n
      r
      CI95%
      p-val
      BF10
      power

      pearson
      24
      0.448832
      [0.06, 0.72]
      0.027807
      2.489
      0.615555

```python
g = sns.lmplot(x="z", y="beta",
           col="roi", data=df_dff);
g = (g.set_axis_labels("z (cons-imm)", "beta (cons-imm)")
      #.set(xlim=(0, 60), ylim=(0, 12),
      #     xticks=[10, 30, 50], yticks=[2, 6, 10])
      .fig.subplots_adjust(wspace=.02))
```

* Patial Correlation

```python
pg.partial_corr(data=df_dff, x='z', y='beta', covar='Zr',
               alternative='two-sided', method='pearson').round(3)
```

      n
      r
      CI95%
      p-val

      pearson
      24
      0.283
      [-0.15, 0.62]
      0.191

```python

```

```python
sns.lmplot(x="z", y="beta", hue="con",
           col="roi", data=df_fnl);
```

### Activation & Zr

```python
sns.jointplot(data=df_dff, x='z', y='Zr', kind='reg')
```

* Robust correlation (skipped method)

```python
pg.corr(df_dff.z, df_dff.Zr,
        alternative="two-sided",
        method="skipped") # pearson, spearman, kendall, bicor, percbend, shepherd, skipped
```

      n
      outliers
      r
      CI95%
      p-val
      power

      skipped
      24
      1
      0.307312
      [-0.12, 0.64]
      0.153748
      0.303899

```python
g = sns.lmplot(x="z", y="Zr",
           col="roi", data=df_dff);
g = (g.set_axis_labels("z (cons-imm)", "Zr (cons-imm)")
      #.set(xlim=(0, 60), ylim=(0, 12),
      #     xticks=[10, 30, 50], yticks=[2, 6, 10])
      .fig.subplots_adjust(wspace=.02))
```

* Patial Correlation

```python
pg.partial_corr(data=df_dff, x='z', y='Zr', covar='beta',
               alternative='two-sided', method='pearson').round(3)
```

      n
      r
      CI95%
      p-val

      pearson
      24
      0.105
      [-0.32, 0.5]
      0.632

```python
sns.lmplot(x="z", y="Zr", hue="con",
           col="roi", data=df_fnl);
```

### Beta & Zr

```python
sns.jointplot(data=df_dff, x='beta', y='Zr', kind='reg')
```

* Spearman Correlation

```python
pg.corr(df_dff.beta, df_dff.Zr,
        alternative="two-sided",
        method="spearman") # pearson, spearman, kendall, bicor, percbend, shepherd, skipped
```

      n
      r
      CI95%
      p-val
      power

      spearman
      24
      0.381739
      [-0.03, 0.68]
      0.065663
      0.466299

```python
g = sns.lmplot(x="beta", y="Zr",
           col="roi", data=df_dff);
g = (g.set_axis_labels("beta (cons-imm)", "Zr (cons-imm)")
      #.set(xlim=(0, 60), ylim=(0, 12),
      #     xticks=[10, 30, 50], yticks=[2, 6, 10])
      .fig.subplots_adjust(wspace=.02))
```

* Patial Correlation

```python
pg.partial_corr(data=df_dff, x='beta', y='Zr', covar='z',
               alternative='two-sided', method='pearson').round(3)
```

      n
      r
      CI95%
      p-val

      pearson
      24
      0.624
      [0.28, 0.82]
      0.001

```python
sns.lmplot(x="beta", y="Zr", hue="con",
           col="roi", data=df_fnl);
```

## Statistical Test - Regression & Mediation Analysis

```python
df_fnl.head()
```

      sn
      roi
      con
      z
      beta
      Zr

      0
      1
      loc
      cons
      1.085557
      0.394441
      0.441953

      1
      1
      loc
      imm
      0.895288
      0.288257
      0.379559

      2
      1
      pf
      cons
      0.406269
      0.079983
      0.122856

      3
      1
      pf
      imm
      0.324837
      0.074869
      0.105637

      4
      10
      loc
      cons
      0.455391
      0.124292
      0.162410

```python
lm = pg.linear_regression(X = df_dff[['z', 'Zr']],  y = df_dff['beta' ],
                         remove_na = True, add_intercept = True,
                         coef_only = False, relimp=True)
lm.round(3)
```

      names
      coef
      se
      T
      pval
      r2
      adj_r2
      CI[2.5%]
      CI[97.5%]
      relimp
      relimp_perc

      0
      Intercept
      0.007
      0.006
      1.111
      0.279
      0.512
      0.466
      -0.006
      0.020
      NaN
      NaN

      1
      z
      0.070
      0.052
      1.352
      0.191
      0.512
      0.466
      -0.038
      0.177
      0.122
      23.81

      2
      Zr
      0.728
      0.199
      3.658
      0.001
      0.512
      0.466
      0.314
      1.141
      0.390
      76.19

```python
lm.residuals_, lm.df_model_, lm.df_resid_
```

    (array([ 0.04037782, -0.02880129, -0.03199547, -0.02904239,  0.00230377,
            -0.00496017, -0.03976243,  0.02242275,  0.02731909, -0.02331989,
             0.01746612,  0.04105124, -0.00992838,  0.00840776, -0.02203583,
            -0.00706285, -0.00563117,  0.0176607 ,  0.0234147 ,  0.00406036,
            -0.01894301,  0.04231559, -0.0242374 , -0.00107959]),
     2,
     21)

```python
pg.qqplot(lm.residuals_, dist='norm')
```

```python
pg.mediation_analysis(data=df_dff, x='z', m='Zr', y='beta', alpha=0.05,
                   seed=42)
```

      path
      coef
      se
      pval
      CI[2.5%]
      CI[97.5%]
      sig

      0
      Zr ~ X
      0.097763
      0.051329
      0.069984
      -0.008687
      0.204213
      No

      1
      Y ~ Zr
      0.828695
      0.187705
      0.000219
      0.439419
      1.217971
      Yes

      2
      Total
      0.141011
      0.059856
      0.027807
      0.016877
      0.265145
      Yes

      3
      Direct
      0.069888
      0.051678
      0.190637
      -0.037582
      0.177358
      No

      4
      Indirect
      0.071123
      0.047993
      0.064000
      0.002036
      0.183404
      No

```python
# 모듈 불러오기
import statsmodels.api as sm
import statsmodels.formula.api as smf

```

```python
linear_model = smf.ols(formula='beta ~ z + Zr + C(con)', data=df_fnl).fit()
```

```python
linear_model.summary2()
```

        Model:	               OLS	         Adj. R-squared:	     0.904

  Dependent Variable:	       beta	              AIC:	         -326.3848

         Date:	        2021-11-28 15:50	        BIC:	         -316.1274

   No. Observations:	         96	          Log-Likelihood:	    167.19

       Df Model:	              3	           F-statistic:	       300.5

     Df Residuals:	           92	        Prob (F-statistic):	 2.14e-47

      R-squared:	            0.907	            Scale:	        0.0018762

        	         Coef.	  Std.Err.	    t	     P>|t|	 [0.025	  0.975]

  Intercept	     -0.0026	  0.0090	  -0.2923	 0.7708	 -0.0204	 0.0152

  C(con)[T.imm]	 -0.0023	  0.0089	  -0.2653	 0.7914	 -0.0199	 0.0152

  z	             0.0045	   0.0283	  0.1596	  0.8736	 -0.0517	 0.0608

  Zr	            0.9472	   0.0741	  12.7876	 0.0000	 0.8001	  1.0943

     Omnibus:	     1.899	  Durbin-Watson:	   1.028

  Prob(Omnibus):	  0.387	 Jarque-Bera (JB):	 1.381

       Skew:	     -0.272	     Prob(JB):	     0.501

     Kurtosis:	    3.222	  Condition No.:	    22

```python
NDF = pd.DataFrame()
NDF['fitted'] = linear_model.fittedvalues
NDF['resid'] = linear_model.resid
NDF['stdresid'] = linear_model.resid_pearson
```

```python
NDF.head()
```

      fitted
      resid
      stdresid

      0
      0.420898
      -0.026457
      -0.610800

      1
      0.358590
      -0.070332
      -1.623721

      2
      0.115582
      -0.035600
      -0.821867

      3
      0.096556
      -0.021687
      -0.500668

      4
      0.153270
      -0.028978
      -0.668998

```python
plt.plot(NDF.fitted, NDF.stdresid, 'o')
plt.axhline(y = 0, color ='grey', linestyle='dashed')
plt.xlabel('Fitted Value')
plt.ylabel('Standardized Residual')
plt.show()
```

```python
sm.qqplot(NDF.stdresid, line='s')
plt.show()
```

```python
df_fnl_logReg = df_fnl
df_fnl_logReg['con'] = df_fnl_logReg['con'].replace(['cons','imm'], ['1','0'])
df_fnl_logReg['con'] = df_fnl_logReg['con'].astype(int)
```

```python
logreg = pg.logistic_regression(df_fnl_logReg[['z', 'beta', 'Zr']], df_fnl_logReg['con'],
                             remove_na=True, coef_only=False)
```

```python
logreg
```

      names
      coef
      se
      z
      pval
      CI[2.5%]
      CI[97.5%]

      0
      Intercept
      -0.135120
      0.352751
      -0.383048
      0.701684
      -0.826499
      0.556258

      1
      z
      -0.286458
      1.312846
      -0.218196
      0.827277
      -2.859589
      2.286674

      2
      beta
      1.307065
      4.827110
      0.270776
      0.786563
      -8.153896
      10.768026

      3
      Zr
      0.190483
      5.712118
      0.033347
      0.973398
      -11.005062
      11.386028

```python
logreg['coef'][0]
```

    -0.1351203794008318

```python
# log-odd ratio of intercept = -0.135120 / logreg['coef'][0]
# log-odd ratio of z = -0.286457 / logreg['coef'][1]
# log-odd ratio of beta = 1.307065 / logreg['coef'][2]
# log-odd ratio of Zr = 0.190483 / logreg['coef'][3]

# odd ratio of intercept = np.exp(logreg['coef'][0])
# odd ratio of z = np.exp(logreg['coef'][1])
# odd ratio of beta = np.exp(logreg['coef'][2])
# odd ratio of Zr = np.exp(logreg['coef'][3])
np.exp(logreg['coef'][0])
np.exp(logreg['coef'][1])
np.exp(logreg['coef'][2])
np.exp(logreg['coef'][3])

# probability
1/(1+np.exp(-(-logreg['coef'][0])))
1/(1+np.exp(-(-logreg['coef'][0]+1*logreg['coef'][1])))
1/(1+np.exp(-(-logreg['coef'][0]+1*logreg['coef'][2])))
1/(1+np.exp(-(-logreg['coef'][0]+1*logreg['coef'][3])))
```

    0.5806892448602095


```python
#!/usr/bin/env python3
from traitlets.config.manager import BaseJSONConfigManager
from pathlib import Path
path = Path.home() / ".jupyter" / "nbconfig"
cm = BaseJSONConfigManager(config_dir=str(path))
cm.update(
    "rise",
    {
        "theme": "white",
        "transition": "none",
        "autolaunch": True,
    }
)
```

```python
%matplotlib inline
```

Coding as a Service:

Librería pypsdier, aprendizajes y metodología.

Sebastián Flores

Pycon Argentina 2020

##### Coding as a Service: librería pypsdier, aprendizajes y metodología.
### Observaciones:

* @sebastiandres en twitter y github.
* Opiniones y críticas a título personal.
* No se necesita conocer de química, matemática o python avanzado.
* Presentación disponible en https://www.github.com/sebastiandres/charlas y realizada con jupyter+RISE.

##### Coding as a Service: librería pypsdier, aprendizajes y metodología.
### Autores

Sebastián Flores

M.Sc. Mathematical and Computational Engineering

M.Sc. Mécanique Numérique

Ingeniero Civil Matemático

u-planner - Chile

Pedro Valencia

Ph.D Ingeniería Bioquímica

M.Sc. Ingeniería Bioquímica

Bioquímico

UTFSM - Chile

##### Coding as a Service: librería pypsdier, aprendizajes y metodología
### Historia

* Origen:
   * Proyecto de colaboración en curso de matemáticas aplicadas.

* Primeras implementaciones en MATLAB:
    * Problema: Cada nueva reacción química requiere una nueva implementación numérica.
    * Repetición de código y mantención tediosa.

* Desarrollo en Python:
    * Código genérico: librería pypsdier.
    * Interface simple e instalación por pypi.
    * Reacciones químicas definidas como funciones.
    * Número arbitrario de sustancias (sustratos o productos).
    * Tamaño de partículas definidas por una lista de radios y frecuencias.

* Impacto:
    * 9 publicaciones.
    * Código abierto y en constante evolución.

##### Coding as a Service: librería pypsdier, aprendizajes y metodología
### Problema

* Pedro no conocía Python. Actualmente, nivel de usuario básico.
* Sebastián tiene (aún) conocimientos nulos en bioquímica.

**¿Cómo colaborar de manera eficiente?**

El problema más general:

**¿Cuál es la manera más sencilla de entregar "Coding As A Service"?**

##### Coding as a Service: librería pypsdier, aprendizajes y metodología
### Ejemplo

¿Cuál es la manera más sencilla de entregar "Coding As A Service"?

Tiene que ser tan fácil que puedas explicárselo a tu abuela.

##### Coding as a Service: librería pypsdier, aprendizajes y metodología
### Solución para la abuela
Abue, vaya a este link y aprete play:

https://bit.ly/2IetMRn

##### Coding as a Service: librería pypsdier, aprendizajes y metodología.

### Nuestro problema a resolver

##### Coding as a Service: librería pypsdier, aprendizajes y metodología.

### Nuestro problema a resolver

##### Coding as a Service: librería pypsdier, aprendizajes y metodología.

### Nuestro problema a resolver

El problema es obtener $S_b(t)$ y $S(t,r,R_i)$, para $t>0$, $0<r<R_i$.

$$
   \frac{\partial S}{\partial t}(t,r,R_i) = D_S \left(\frac{\partial^2 S}{\partial r^2}(t,r,R_i)
   + \frac{2}{r}\frac{\partial S}{\partial r}(t,r,R_i)\right) - V_e\left(S, r, t \right)
$$

Condición de borde en el centro de las partículas:

\begin{align}
   \frac{\partial S}{\partial r}(t, 0, R_i) = 0 \textrm{ para } t > 0
\end{align}

Condiciones de borde en la superficie de la partícula:

\begin{align}
S_b(t)  = S(t, R_i,R_i) \textrm{ para } t > 0
\end{align}

\begin{align}
\frac{d S_b}{d t}(t)
   &= - 3 D_S \frac{V_c}{V_R E \left[ R^3 \right] } E \left[ R^2 \left. \frac{\partial S}{\partial r} \right|_{r=R} \right] \textrm{ para } t > 0
\end{align}

Con la condición inicial

\begin{align}
S_b(0) &= S_0 \\
S(0,r, R_i) &= 0 \textrm{ para } 0 \leq r < R_i
\end{align}

##### Coding as a Service: librería pypsdier, aprendizajes y metodología.

### Nuestro problema a resolver

##### Coding as a Service: librería pypsdier, aprendizajes y metodología.
### Los Desafíos

Desafíos técnicos:

* ¿Cómo discretizar las ecuaciones?
* ¿Cómo realizar la implementación numérica?
* ¿Cómo resolver para reacciones químicas distintas y un número arbitrario de sustancias y tamaños de partículas?

Esta es la parte tiene solución directa y conocida:
* Discretizar ecuaciones utilizando diferencias finitas para el tiempo ($\Delta t$) y espacio ($\Delta R_i$).
* Asegurar estabilidad numérica dependiendo de los inputs.
* Implementación flexible con python + numpy + matplotlib + otros.

##### Coding as a Service: librería pypsdier, aprendizajes y metodología.
### Los Desafíos

Desafíos organizacionales:

* ¿Cómo colaborar?
* ¿Cómo distribuir el código?
* ¿Cómo hacer fácil el uso?
* ¿Cómo hacer reproducible las simulaciones?
* ¿Cómo documentar el código?
* ¿Cómo almacenar los resultados de las simulaciones?

Esta parte no tiene una solución directa:
* No había mucha información al respecto.
* Cada equipo lo resuelve a su manera.
* Existen muchas herramientas que nos simplifican la vida.

##### Coding as a Service: librería pypsdier, aprendizajes y metodología
### Framework: ¿Qué necesitábamos?

(1) Propocionar **instalación** y **versionamiento** de python, jupyter y librerías necesarias.

(2) Facilitar **desarrollo incremental** considerando devs y users.

(3) Permitir una buena **documentación** para desarrolladores y usuarios.

(4)  Exponer una **interface simple** al usuario final, permitiendo ocultar una implementación numérica compleja y con una curva de aprendizaje apropiado.

(5) Permitir **reproducir**, **almacenar** y **compartir** resultados de simulación.

(6) Permitir el uso de recursos computacionales en la **nube**.

##### Coding as a Service: librería pypsdier, aprendizajes y metodología
#### Necesidades

(1) Propocionar **instalación** y **versionamiento** de python, jupyter y librerías necesarias.

(2) Facilitar **desarrollo incremental** considerando desarrolladores y usuarios.

### Solución

Estructura de proyecto de python, para instalar con pypi o desde github.

##### Coding as a Service: librería pypsdier, aprendizajes y metodología
#### Necesidades

(3) Permitir una buena **documentación** para desarrolladores y usuarios.

### Solución

##### Coding as a Service: librería pypsdier, aprendizajes y metodología
#### Necesidades

(4) Exponer una **interface simple** al usuario final, permitiendo ocultar una implementación numérica compleja y con una curva de aprendizaje apropiado.

(5) Permitir **reproducir**, **almacenar** y **compartir** resultados de simulación.

### Solución

##### Coding as a Service: librería pypsdier, aprendizajes y metodología
### Solución

(5) Permitir **reproducir**, **almacenar** y **compartir** resultados de simulación.

(6) Permitir el uso de recursos computacionales en la **nube**.

### Solución

##### Coding as a Service: librería pypsdier, aprendizajes y metodología
### Ejemplo de pypsdier

Primero que nada, instalaremos la librería:

```python
#!pip install pypsdier --upgrade
!pip install git+https://github.com/sebastiandres/pypsdier.git --upgrade
```

    Collecting git+https://github.com/sebastiandres/pypsdier.git
      Cloning https://github.com/sebastiandres/pypsdier.git to /private/var/folders/68/v1ds0ld152q_wk4sbcns3ckw0000gn/T/pip-req-build-mm52_b8o
      Running command git clone -q https://github.com/sebastiandres/pypsdier.git /private/var/folders/68/v1ds0ld152q_wk4sbcns3ckw0000gn/T/pip-req-build-mm52_b8o
    Building wheels for collected packages: pypsdier
      Building wheel for pypsdier (setup.py) ... done
      Created wheel for pypsdier: filename=pypsdier-1.1.1-cp37-none-any.whl size=14659 sha256=34556c2c7b1b3be6222355f403a45817e8b5a0154f44037f940a4923711fe71d
      Stored in directory: /private/var/folders/68/v1ds0ld152q_wk4sbcns3ckw0000gn/T/pip-ephem-wheel-cache-csxbayor/wheels/57/71/0f/2f6f43ab73e6371b622a1e181130163a445c394d19b74a80d1
    Successfully built pypsdier
    Installing collected packages: pypsdier
      Found existing installation: pypsdier 1.1.1
        Uninstalling pypsdier-1.1.1:
          Successfully uninstalled pypsdier-1.1.1
    Successfully installed pypsdier-1.1.1

##### Coding as a Service: librería pypsdier, aprendizajes y metodología
### Ejemplo

Consideremos un ejemplo de una reacción simple. Definamos los inputs de la simulación:

```python
def MichaelisMenten(S, E0, k, K):
  """Definition for Michaelis Menten reaction with inputs E0 [mM], k [1/s] and K [mM]"""
  return (-k*E0*S[0]/(K+S[0]), )

inputs = {}
inputs["SimulationTime"] = 30. # [s]
inputs["SavingTimeStep"] = 1. # [s]
inputs["CatalystVolume"] = 0.5 # [mL]
inputs["BulkVolume"]  = 100.0  # [mL]
inputs["Names"] = ('Substrat',)  # legend for the xls, reports and plots
inputs["InitialConcentrations"] = (1.3,)   # [mM]
inputs["EffectiveDiffusionCoefficients"] = (5.3E-10,)  # [m2/s]
inputs["CatalystParticleRadius"] = [40.0E-6, 60.0E-6, 80.0E-6] # [m]
inputs["CatalystParticleRadiusFrequency"] = [0.3, 0.5, 0.2] # []
inputs["ReactionFunction"] = MichaelisMenten # function
inputs["ReactionParameters"] = (41 , 0.13)   # [1/s], [mM/s], parameters
inputs["CatalystEnzymeConcentration"] = 0.35 # [mM]
```

##### Coding as a Service
### Ejemplo
Definamos ahora opciones de gráficos para la simulación:

```python
plot_options = {}
plot_options["label_x"] = "Tiempo de reacción [s]"
plot_options["label_y"] = "Concentración [mM]"
plot_options["title"] = "Simulación de Michaelis Menten para la PyconAr"
plot_options["ode_kwargs"] = {'label':'Enzima Libre', 'color':'blue', 'marker':'', 'markersize':6, 'linestyle':'dashed', 'linewidth':2}
plot_options["pde_kwargs"] = {'label':'Enzima Inmobilizada', 'color':'blue', 'marker':'', 'markersize':6, 'linestyle':'solid', 'linewidth':2}
plot_options["data_kwargs"] = {'label':'Experimento', 'color':'green', 'marker':'s', 'markersize':8, 'linestyle':'none', 'linewidth':2}
plot_options["data_x"] = [0.0, 30, 60, 90, 120]
plot_options["data_y"] = [1.3, 0.65, 0.25, 0.10, 0.0]
```

##### Coding as a Service: librería pypsdier, aprendizajes y metodología
### Ejemplo pypsdier
Todo está listo para realizar una simulación (muy sencilla):

```python
import pypsdier
SIM1 = pypsdier.SimulationInterface()
SIM1.new(inputs, plot_options)
```

```python
SIM1.simulate("ode")
```

    ODE: Solving without diffusional restriction (simplified problem). No porous particles are present. The substance and the enzyme are assumed to be diluted on the bulk phase
    Simulation completed.

```python
SIM1.simulate("pde")
```

    PDE: Solving the complete reaction-diffusion problem, considering a single substance and 3 particle radii.
    Simulated 030 secs out of  30 secs (Remaining time < 1 mins)

```python
SIM1.save("pycon_saving_example.rde")
```

    Saving simulation into file at /Users/sebastiandres/Desktop/Personales/github_repos/charlas/2020_11_XX_pycon_ar_pypsdier/pycon_saving_example.rde

##### Coding as a Service: librería pypsdier, aprendizajes y metodología
### Ejemplo pypsdier
Podemos definir funciones específicas para exportar en formato excel o graficar

```python
SIM1.export_xls("pycon_excel_example.xls")
```

    Saving simulation as xls file at /Users/sebastiandres/Desktop/Personales/github_repos/charlas/2020_11_XX_pycon_ar_pypsdier/pycon_excel_example.xls

```python
SIM1.plot(figsize=(16,12))
```

##### Coding as a Service: librería pypsdier, aprendizajes y metodología
### Ejemplo pypsdier

¿Y la reproducibilidad?

Una simulación realizada almacena todas las características con las cuales se generó.

```python
import pypsdier
SIM2 = pypsdier.SimulationInterface()
SIM2.load("pycon_colab_example.rde") # load instead of new
```

    Loaded a simulation from /Users/sebastiandres/Desktop/Personales/github_repos/charlas/2020_11_XX_pycon_ar_pypsdier/pycon_colab_example.rde

```python
SIM2.status()
```

    System configuration:
        environment: google_colab
        python version: 3.6.9
        GenericSimulationLibrary version: 1.1.0
        numpy version: 1.18.5
        scipy version: 1.4.1
        xlwt version: 1.3.0
        matplotlib version: 3.2.2
        dill version: 0.3.2
    Inputs:
        SimulationTime: 120.0
        SavingTimeStep: 1.0
        CatalystVolume: 0.5
        BulkVolume: 100.0
        Names: ('Substrat',)
        InitialConcentrations: (1.3,)
        EffectiveDiffusionCoefficients: (5.3e-10,)
        CatalystParticleRadius: [4e-05, 6e-05, 8e-05]
        CatalystParticleRadiusFrequency: [0.3, 0.5, 0.2]
        ReactionFunction: <function MichaelisMenten at 0x134d50dd0>
        ReactionParameters: (41, 0.13)
        CatalystEnzymeConcentration: 0.35
    Plot Options:
        title: Simulación de Michaelis Menten para la PyconAr
        label_x: Tiempo de reacción [s]
        label_y: Concentración [mM]
        ode_kwargs: {'label': 'ode', 'color': 'black', 'marker': '', 'markersize': 6, 'linestyle': 'dashed', 'linewidth': 2}
        pde_kwargs: {'label': 'pde', 'color': 'black', 'marker': '', 'markersize': 6, 'linestyle': 'solid', 'linewidth': 2}
        data_kwargs: {'label': 'exp', 'color': 'red', 'marker': 's', 'markersize': 6, 'linestyle': 'none', 'linewidth': 2}
        data_x: [0.0, 30, 60, 90, 120]
        data_y: [1.3, 0.65, 0.25, 0.1, 0.0]
    Simulations:
        ODE: yes
        PDE: yes

##### Coding as a Service: librería pypsdier, aprendizajes y metodología
### Ejemplo pypsdier

Podemos cargar una simulación realizada previamente y analizarla (sin necesidad de volver a realizar la simulación).

```python
SIM2.plot(figsize=(20,12))
```

##### Coding as a Service: librería pypsdier, aprendizajes y metodología
### Framework Propuesto

La librería pypsdier es específica al problema de reacción-difusión para reactores de enzima inmobilizada, con flexibilidad para diferentes modelos de reacción y condiciones de operación.

Sin embargo, se propone un framework de trabajo con las funcionalidades mínimas: **GenericSimulationLibrary**

Disponible en
https://github.com/sebastiandres/GenericSimulationLibrary:

* En `simulation_interface.py` actualizar los métodos: new, load, simulate, save, plot, export.
* En `docs/source/*.rst` actualizar la documentación.

##### Coding as a Service: librería pypsdier, aprendizajes y metodología
### Conclusión

Todo lo anterior no es muy distinto a lo que se hace en una librería "formal".

**Aprendizaje**:

Puedes usar las mismas herramientas e ideas, aunque tu equipo y proyecto sea más pequeño e informal, porque simplifican y mejoran la colaboración.

* git + github/bitbucket.
* readthedocs
* pypi
* google colab/mybinder
* Interface OO

Coding as a Service:

Librería pypsdier, aprendizajes y metodología.

Sebastián Flores

Pycon Argentina 2020

Encuesta:

https://bit.ly/354jhrQ

```python

```


# PHY321: Classical Mechanics 1
**Homework 5, due Monday  February 22**

Date: **Feb 21, 2022**

### Practicalities about  homeworks and projects

1. You can work in groups (optimal groups are often 2-3 people) or by yourself. If you work as a group you can hand in one answer only if you wish. **Remember to write your name(s)**!

2. Homeworks are available Wednesday/Thursday the week before the deadline. The deadline is at the Friday lecture.

3. How do I(we)  hand in?  You can hand in the paper and pencil exercises as a hand-written document. For this homework this applies to exercises 1-5. Alternatively, you can hand in everyhting (if you are ok with typing mathematical formulae using say Latex) as a jupyter notebook at D2L. The numerical exercise(s) (exercise 6 here) should always be handed in as a jupyter notebook by the deadline at D2L.

### Introduction to homework 5

This week's sets of classical pen and paper and computational
exercises are a continuation of the topics from the previous homework set. We keep dealing with simple motion problems and conservation laws; energy, momentum and angular momentum. These conservation laws are central in Physics and understanding them properly lays the foundation for understanding and analyzing more complicated physics problems.
The relevant reading background is
1. chapters 3 and 4 of Taylor (there are many good examples there) and

2. chapters 10-14 of Malthe-Sørenssen.

In both textbooks there are many nice worked out examples. Malthe-Sørenssen's text contains also several coding examples you may find useful.

The numerical homework focuses on another motion problem where you can
use the code you developed in homework 4, almost entirely. Please take
a look at the posted solution (jupyter-notebook) for homework 4. You
need only to change the forces at play.

### Exercise 1 (15 pt), Work-energy theorem and conservation laws

This exercise was partly discussed during the lectures. You can also find it in the online notes from the week of February 15-19.
We will study a classical electron which moves in the $x$-direction along a surface. The force from the surface is

$$
\boldsymbol{F}(x)=-F_0\sin{(\frac{2\pi x}{b})}\boldsymbol{e}_x.
$$

The constant $b$ represents the distance between atoms at the surface of the material, $F_0$ is a constant and $x$ is the position of the electron.

* 1a (2pt) Is this a conservative force? And if so, what does that imply?

This is indeed a conservative force since it depends only on position and its **curl** is zero. This means that energy is conserved and the integral over the work done by the force is independent of the path taken.
* 1b (4pt) Use the work-energy theorem to find the velocity $v(x)$.

Using the work-energy theorem we can find the work $W$ done when moving an electron from a position $x_\
0$ to a final position $x$ through the integral

$$
W=\int_{x_0}^x \boldsymbol{F}(x')dx' =  -\int_{x_0}^x F_0\sin{(\frac{2\pi x'}{b})} dx',
$$

which results in

$$
W=\frac{F_0b}{2\pi}\left[\cos{(\frac{2\pi x}{b})}-\cos{(\frac{2\pi x_0}{b})}\right].
$$

Since this is related to the change in kinetic energy we have, with $v_0$ being the initial velocity at a  time $t_0$,

$$
v  = \pm\sqrt{\frac{2}{m}\frac{F_0b}{2\pi}\left[\cos{(\frac{2\pi x}{b})}-\cos{(\frac{2\pi x_0}{b})}\right]+v_0^2}.
$$

* 1c (4pt) With the above expression for the force, find the potential energy.

The potential energy, due to energy conservation is

$$
V(x)=V(x_0)+\frac{1}{2}mv_0^2-\frac{1}{2}mv^2,
$$

with $v$ given by the previous answer.
We can now, in order to find a more explicit expression for the potential energy at a given value $x$, define a zero level value for the potential. The potential is defined , using the work-energy theorem , as

$$
V(x)=V(x_0)+\int_{x_0}^x (-F(x'))dx',
$$

and if you recall the definition of the indefinite integral, we can rewrite this as

$$
V(x)=\int (-F(x'))dx'+C,
$$

where $C$ is an undefined constant. The force is defined as the gradient of the potential, and in that case the undefined constant vanishes. The constant does not affect the force we derive from the potential.

We have then

$$
V(x)=V(x_0)-\int_{x_0}^x \boldsymbol{F}(x')dx',
$$

which results in

$$
V(x)=\frac{F_0b}{2\pi}\left[\cos{(\frac{2\pi x}{b})}-\cos{(\frac{2\pi x_0}{b})}\right]+V(x_0).
$$

We can now define

$$
\frac{F_0b}{2\pi}\cos{(\frac{2\pi x_0}{b})}=V(x_0),
$$

which gives

$$
V(x)=\frac{F_0b}{2\pi}\left[\cos{(\frac{2\pi x}{b})}\right].
$$

* 1d (5pt) Make a plot of the potential energy and discuss the equilibrium points where the force on the electron is zero. Discuss the physical interpretation of stable and unstable equilibrium points. Use energy conservation.

The following Python code plots the potential

```
%matplotlib inline

import numpy as np
import pandas as pd
from math import *
import matplotlib.pyplot as plt
Deltax = 0.01
#set up arrays
xinitial = -2.0
xfinal = 2.0
n = ceil((xfinal-xinitial)/Deltax)
x = np.zeros(n)
for i in range(n):
    x[i] = xinitial+i*Deltax
V = np.zeros(n)
# Setting values for the constants.
F0 = 1.0; b = 1.0;
# Defining the potential
V = F0*b/(2*pi)*np.cos(2*pi*x/b)
# Plot position as function of time
fig, ax = plt.subplots()
ax.set_ylabel('V')
ax.set_xlabel('x')
ax.plot(x, V)
fig.tight_layout()
plt.show()
```

We have stable equilibrium  points for every minimum of the $\cos$ function and unstable equilibrium points where it has its maximimum values. At the minimum the particle has the lowest potential energy and the largest kinetic energy whereas at the maxima it has the largest potential energy and lowest kinetic energy.

### Exsercise 2 (15pt), Rocket, Momentum and mass

Taylor exercise 3.11.    This exercise was partly discussed during the lectures, see the notes on [Energy and Momentum etc, see the part on Momentum conservation ](https://mhjensen.github.io/Physics321/doc/pub/energyconserv/html/energyconserv.html). Taylor's chapter 3.2 covers also this example.

* 3.11 a and b

Consider the rocket of mass $M$ moving with velocity $v$. After a
brief instant, the velocity of the rocket is $v+\Delta v$ and the mass
is $M-\Delta M$. Momentum conservation gives

$$
\begin{eqnarray*}
Mv&=&(M-\Delta M)(v+\Delta v)+\Delta M(v-v_e)\\
0&=&-\Delta Mv+M\Delta v+\Delta M(v-v_e),\\
0&=&M\Delta v-\Delta Mv_e.
\end{eqnarray*}
$$

In the second step we ignored the term $\Delta M\Delta v$ since we
assume it is small. The last equation gives

$$
\begin{eqnarray}
\Delta v&=&\frac{v_e}{M}\Delta M,\\
\nonumber
\frac{dv}{dt}&=&\frac{v_e}{M}\frac{dM}{dt}.
\end{eqnarray}
$$

Here we let $\Delta v\rightarrow dv$ and $\Delta M\rightarrow dM$.
We have also assumed that $M(t) = M_0-kt$.
Integrating the expression with lower limits $v_0=0$ and $M_0$, one finds

$$
\begin{eqnarray*}
v&=&v_e\int_{M_0}^M \frac{dM'}{M'}\\
v&=&v_e\ln(M/M_0)\\
&=&v_e\ln[(M_0-k t)/M_0].
\end{eqnarray*}
$$

We have ignored gravity here. If we add gravity as the external force, we get when integrating an additional terms $-gt$, that is

$$
v=v_e\ln[(M_0-k t)/M_0]-gt.
$$

* 3.11c

Inserting numbers $v_e=3000$ m/s, $M_0/M=2$ and $g=9.8$ m/s$^{2}$, we find $v=900$ m/s. With $g=0$ the corresponding number is $2100$ m/s, so gravity reduces the speed acquired in the first two minutes to a little less than half its weight-free value.

* 3.11d

If the thrust $\Delta Mv_e$ is less than the weight $mg$, the rocket will just sit on the ground until it has shed enough mass that the thrust can overcome the weight, definitely not a good design.

### Exercise 3 (10pt), More Rockets

Taylor exercises 3.13 (5pt) and 3.14 (5pt). This is a continuation of the previous exercise and most of the relevant background material can be found in Taylor chapter 3.2.

Taking the velocity from the previous exercise and integrating over time we find the height

$$
y(t) = y(t_0=0)+\int_0^tv(t')dt',
$$

which gives

$$
y(t) = v_et\ln{M_0}-v_e\int_0^t \ln{M(t')}dt'-\frac{1}{2}gt^2.
$$

To do the integral over time we recall that $M(t')=M_0-\Delta M t'$. We assumed that $\Delta M=k$ is a constant.
We use that $M_0-M=kt$ and assume that mass decreases by a constant $k$ times time $t$.

We obtain then that the integral gives

$$
\int_0^t \ln{M(t')}dt' = \int_0^t \ln{(M_0-kt')}dt',
$$

and defining the variable $u=M_0-kt'$, with $du=-kdt'$ and the new limits $M_0$ when $t=0$ and $M_0-kt$ when time is equal to $t$, we have

$$
\int_0^t \ln{M(t')}dt' = \int_0^t \ln{(M_0-kt')}dt'=-\frac{1}{k}\int_{M_0}^{M_0-kt} \ln{(u)}du=-\frac{1}{k}\left[u\ln{(u)}-u\right]_{M_0}^{M_0-kt},
$$

and writing out we obtain

$$
-\frac{1}{k}\left[u\ln{(u)}-u\right]_{M_0}^{M_0-kt} = \frac{1}{k}\left(M_0\ln{M_0}-M\ln{M}\right)-t,
$$

Mulitplying with $-v_e$ we have

$$
-\frac{v_e}{k}\left(M_0\ln{M_0}-M\ln{M}\right)+v_et,
$$

which we can rewrite as, using $M_0=M+kt$,

$$
-\frac{v_e}{k}\left((M+kt)\ln{M_0}-M\ln{M}\right)+v_et=v_et-v_et\ln{M_0}-\frac{Mv_e}{k}\ln{(\frac{M_0}{M})}.
$$

Inserting into $y(t)$ we obtain then

$$
y(t) = v_et-\frac{1}{2}gt^2-\frac{Mv_e}{k}\ln{(\frac{M_0}{M})}.
$$

Using the numbers from the previous exercise with $t=2$ min we obtain that $y\approx 40$ km.

For exercise 3.14 (5pt) we have the equation of motion which reads $Ma=kv_e-bv$ or

$$
\frac{Mdv}{kv_e-bv}=dt.
$$

We have that $dM/dt =-k$ (assumed a constant rate for mass change). We can then replace $dt$ by $-dM/k$ and we have

$$
\frac{kdv}{kv_e-bv}=-\frac{dM}{M}.
$$

Integrating gives

$$
v = \frac{kv_e}{b}\left[1-(\frac{M}{M_0})^{b/k}\right].
$$

### Exercise 4 (10pt), Center of mass

Taylor exercise 3.20. Here Taylor's chapter 3.3 can be of use. This relation will turn out to be very useful when we discuss systems of many classical particles.

The definition of the center of mass for $N$ objects can be written as

$$
M\boldsymbol{R}=\sum_{i=1}^Nm_i\boldsymbol{r}_i,
$$

where $m_i$ and $\boldsymbol{r}_i$ are the masses and positions of object $i$, respectively.

Assume now that we have a collection of $N_1$ objects with masses $m_{1i}$ and positions $\boldsymbol{r}_{1i}$
with $i=1,\dots,N_1$ and  a collection of $N_2$ objects with masses $m_{2j}$ and positions $\boldsymbol{r}_{2j}$
with $j=1,\dots,N_2$.

The total mass of the two-body system is $M=M_1+M_2=\sum_{i=1}^{N_1}m_{1i}+\sum_{j=1}^{N_2}m_{2j}$. The center of mass position $\boldsymbol{R}$ of the whole system satisfies then

$$
M\boldsymbol{R}=\sum_{i=1}^{N_1}m_{1i}\boldsymbol{r}_{1i}+\sum_{j=1}^{N_2}m_{2j}\boldsymbol{r}_{2j}=M_1\boldsymbol{R}_1+M_2\boldsymbol{R}_2,
$$

where $\boldsymbol{R}_1$ and $\boldsymbol{R}_2$ are the the center of mass positions of the two separate bodies and the second equality follows from our rewritten definition of the center of mass applied to each body separately. This is the required result.

### Exercise 5 (10pt) and 6 (40pt)

### Answers

We start with a simpler case first, the Earth-Sun system  in two dimensions only.  The gravitational force $F_G$ on the earth from the sun is

$$
\boldsymbol{F}_G=-\frac{GM_{\odot}M_E}{r^3}\boldsymbol{r},
$$

where $G$ is the gravitational constant,

$$
M_E=6\times 10^{24}\mathrm{Kg},
$$

the mass of Earth,

$$
M_{\odot}=2\times 10^{30}\mathrm{Kg},
$$

the mass of the Sun and

$$
r=1.5\times 10^{11}\mathrm{m},
$$

is the distance between Earth and the Sun. The latter defines what we call an astronomical unit **AU**.
From Newton's second law we have then for the $x$ direction

$$
\frac{d^2x}{dt^2}=-\frac{F_{x}}{M_E},
$$

and

$$
\frac{d^2y}{dt^2}=-\frac{F_{y}}{M_E},
$$

for the $y$ direction.

Here we will use  that  $x=r\cos{(\theta)}$, $y=r\sin{(\theta)}$ and

$$
r = \sqrt{x^2+y^2}.
$$

We can rewrite these equations

$$
F_{x}=-\frac{GM_{\odot}M_E}{r^2}\cos{(\theta)}=-\frac{GM_{\odot}M_E}{r^3}x,
$$

and

$$
F_{y}=-\frac{GM_{\odot}M_E}{r^2}\sin{(\theta)}=-\frac{GM_{\odot}M_E}{r^3}y,
$$

as four first-order coupled differential equations

$$
\frac{dv_x}{dt}=-\frac{GM_{\odot}}{r^3}x,
$$

and

$$
\frac{dx}{dt}=v_x,
$$

and

$$
\frac{dv_y}{dt}=-\frac{GM_{\odot}}{r^3}y,
$$

and

$$
\frac{dy}{dt}=v_y.
$$

The four coupled differential equations

$$
\frac{dv_x}{dt}=-\frac{GM_{\odot}}{r^3}x,
$$

and

$$
\frac{dx}{dt}=v_x,
$$

and

$$
\frac{dv_y}{dt}=-\frac{GM_{\odot}}{r^3}y,
$$

and

$$
\frac{dy}{dt}=v_y,
$$

can be turned into dimensionless equations or we can introduce astronomical units with $1$ AU = $1.5\times 10^{11}$.

Using the equations from circular motion (with $r =1\mathrm{AU}$)

$$
\frac{M_E v^2}{r} = F = \frac{GM_{\odot}M_E}{r^2},
$$

we have

$$
GM_{\odot}=v^2r,
$$

and using that the velocity of Earth (assuming circular motion) is
$v = 2\pi r/\mathrm{yr}=2\pi\mathrm{AU}/\mathrm{yr}$, we have

$$
GM_{\odot}= v^2r = 4\pi^2 \frac{(\mathrm{AU})^3}{\mathrm{yr}^2}.
$$

The four coupled differential equations can then be discretized using Euler's method as (with step length $h$)

$$
v_{x,i+1}=v_{x,i}-h\frac{4\pi^2}{r_i^3}x_i,
$$

and

$$
x_{i+1}=x_i+hv_{x,i},
$$

and

$$
v_{y,i+1}=v_{y,i}-h\frac{4\pi^2}{r_i^3}y_i,
$$

and

$$
y_{i+1}=y_i+hv_{y,i},
$$

The code here implements Euler's method for the Earth-Sun system using a more compact way of representing the vectors. Alternatively, you could have spelled out all the variables $v_x$, $v_y$, $x$ and $y$ as one-dimensional arrays.

```
# Common imports
import numpy as np
import pandas as pd
from math import *
import matplotlib.pyplot as plt
import os

# Where to save the figures and data files
PROJECT_ROOT_DIR = "Results"
FIGURE_ID = "Results/FigureFiles"
DATA_ID = "DataFiles/"

if not os.path.exists(PROJECT_ROOT_DIR):
    os.mkdir(PROJECT_ROOT_DIR)

if not os.path.exists(FIGURE_ID):
    os.makedirs(FIGURE_ID)

if not os.path.exists(DATA_ID):
    os.makedirs(DATA_ID)

def image_path(fig_id):
    return os.path.join(FIGURE_ID, fig_id)

def data_path(dat_id):
    return os.path.join(DATA_ID, dat_id)

def save_fig(fig_id):
    plt.savefig(image_path(fig_id) + ".png", format='png')

DeltaT = 0.01
#set up arrays
tfinal = 10 # in years
n = ceil(tfinal/DeltaT)
# set up arrays for t, a, v, and x
t = np.zeros(n)
v = np.zeros((n,2))
r = np.zeros((n,2))
# Initial conditions as compact 2-dimensional arrays
r0 = np.array([1.0,0.0])
v0 = np.array([0.0,2*pi])
r[0] = r0
v[0] = v0
Fourpi2 = 4*pi*pi
# Start integrating using Euler's method
for i in range(n-1):
    # Set up the acceleration
    # Here you could have defined your own function for this
    rabs = sqrt(sum(r[i]*r[i]))
    a =  -Fourpi2*r[i]/(rabs**3)
    # update velocity, time and position using Euler's forward method
    v[i+1] = v[i] + DeltaT*a
    r[i+1] = r[i] + DeltaT*v[i]
    t[i+1] = t[i] + DeltaT
# Plot position as function of time
fig, ax = plt.subplots()
#ax.set_xlim(0, tfinal)
ax.set_xlabel('x[AU]')
ax.set_ylabel('y[AU]')
ax.plot(r[:,0], r[:,1])
fig.tight_layout()
save_fig("EarthSunEuler")
plt.show()
```

We notice here that Euler's method doesn't give a stable orbit with for example $\Delta t =0.01$. It
means that we cannot trust Euler's method. Euler's method does not conserve energy. It is an
example of an integrator which is not
[symplectic](https://en.wikipedia.org/wiki/Symplectic_integrator).

Here we present thus two methods, which with simple changes allow us
to avoid these pitfalls. The simplest possible extension is the
so-called Euler-Cromer method.  The changes we need to make to our
code are indeed marginal here.  We need simply to replace

```
    r[i+1] = r[i] + DeltaT*v[i]
```

in the above code with the velocity at the new time $t_{i+1}$

```
    r[i+1] = r[i] + DeltaT*v[i+1]
```

By this simple caveat we get stable orbits.  Below we derive the
Euler-Cromer method as well as one of the most utlized algorithms for
solving the above type of problems, the so-called Velocity-Verlet
method.

Let us repeat Euler's method.
We have a differential equation

$$
\begin{equation}
  y'(t_i)=f(t_i,y_i)
\label{_auto1} \tag{1}
\end{equation}
$$

and if we truncate at the first derivative, we have from the Taylor expansion

$$
\begin{equation}
   y_{i+1}=y(t_i) + (\Delta t) f(t_i,y_i) + O(\Delta t^2), \label{eq:euler} \tag{2}
\end{equation}
$$

which when complemented with $t_{i+1}=t_i+\Delta t$ forms
the algorithm for the well-known Euler method.
Note that at every step we make an approximation error
of the order of $O(\Delta t^2)$, however the total error is the sum over all
steps $N=(b-a)/(\Delta t)$ for $t\in [a,b]$, yielding thus a global error which goes like
$NO(\Delta t^2)\approx O(\Delta t)$.

To make Euler's method more precise we can obviously
decrease $\Delta t$ (increase $N$), but this can lead to loss of numerical precision.
Euler's method is not recommended for precision calculation,
although it is handy to use in order to get a first
view on how a solution may look like.

Euler's method is asymmetric in time, since it uses information about the derivative at the beginning
of the time interval. This means that we evaluate the position at $y_1$ using the velocity
at $v_0$. A simple variation is to determine $x_{n+1}$ using the velocity at
$v_{n+1}$, that is (in a slightly more generalized form)

$$
\begin{equation}
   y_{n+1}=y_{n}+ v_{n+1}+O(\Delta t^2)
\label{_auto2} \tag{3}
\end{equation}
$$

and

$$
\begin{equation}
   v_{n+1}=v_{n}+(\Delta t) a_{n}+O(\Delta t^2).
\label{_auto3} \tag{4}
\end{equation}
$$

The acceleration $a_n$ is a function of $a_n(y_n, v_n, t_n)$ and needs to be evaluated
as well. This is the Euler-Cromer method. It is easy to change the above code and see that with the same
time step we get stable results.

Let us stay with $x$ (position) and $v$ (velocity) as the quantities we are interested in.

We have the Taylor expansion for the position given by

$$
x_{i+1} = x_i+(\Delta t)v_i+\frac{(\Delta t)^2}{2}a_i+O((\Delta t)^3).
$$

The corresponding expansion for the velocity is

$$
v_{i+1} = v_i+(\Delta t)a_i+\frac{(\Delta t)^2}{2}v^{(2)}_i+O((\Delta t)^3).
$$

Via Newton's second law we have normally an analytical expression for the derivative of the velocity, namely

$$
a_i= \frac{d^2 x}{dt^2}\vert_{i}=\frac{d v}{dt}\vert_{i}= \frac{F(x_i,v_i,t_i)}{m}.
$$

If we add to this the corresponding expansion for the derivative of the velocity

$$
v^{(1)}_{i+1} = a_{i+1}= a_i+(\Delta t)v^{(2)}_i+O((\Delta t)^2)=a_i+(\Delta t)v^{(2)}_i+O((\Delta t)^2),
$$

and retain only terms up to the second derivative of the velocity since our error goes as $O(h^3)$, we have

$$
(\Delta t)v^{(2)}_i\approx a_{i+1}-a_i.
$$

We can then rewrite the Taylor expansion for the velocity as

$$
v_{i+1} = v_i+\frac{(\Delta t)}{2}\left( a_{i+1}+a_{i}\right)+O((\Delta t)^3).
$$

Our final equations for the position and the velocity become then

$$
x_{i+1} = x_i+(\Delta t)v_i+\frac{(\Delta t)^2}{2}a_{i}+O((\Delta t)^3),
$$

and

$$
v_{i+1} = v_i+\frac{(\Delta t)}{2}\left(a_{i+1}+a_{i}\right)+O((\Delta t)^3).
$$

Note well that the term $a_{i+1}$ depends on the position at $x_{i+1}$. This means that you need to calculate
the position at the updated time $t_{i+1}$ before the computing the next velocity.  Note also that the derivative of the velocity at the time
$t_i$ used in the updating of the position can be reused in the calculation of the velocity update as well.

We can now easily add the Verlet method to our original code as

```
DeltaT = 0.01
#set up arrays
tfinal = 10
n = ceil(tfinal/DeltaT)
# set up arrays for t, a, v, and x
t = np.zeros(n)
v = np.zeros((n,2))
r = np.zeros((n,2))
# Initial conditions as compact 2-dimensional arrays
r0 = np.array([1.0,0.0])
v0 = np.array([0.0,2*pi])
r[0] = r0
v[0] = v0
Fourpi2 = 4*pi*pi
# Start integrating using the Velocity-Verlet  method
for i in range(n-1):
    # Set up forces, air resistance FD, note now that we need the norm of the vecto
    # Here you could have defined your own function for this
    rabs = sqrt(sum(r[i]*r[i]))
    a =  -Fourpi2*r[i]/(rabs**3)
    # update velocity, time and position using the Velocity-Verlet method
    r[i+1] = r[i] + DeltaT*v[i]+0.5*(DeltaT**2)*a
    rabs = sqrt(sum(r[i+1]*r[i+1]))
    anew = -4*(pi**2)*r[i+1]/(rabs**3)
    v[i+1] = v[i] + 0.5*DeltaT*(a+anew)
    t[i+1] = t[i] + DeltaT
# Plot position as function of time
fig, ax = plt.subplots()
ax.set_xlabel('x[AU]')
ax.set_ylabel('y[AU]')
ax.plot(r[:,0], r[:,1])
fig.tight_layout()
save_fig("EarthSunVV")
plt.show()
```

You can easily generalize the calculation of the forces by defining a function
which takes in as input the various variables. We leave this as a challenge to you.

Running the above code for various time steps we see that the Velocity-Verlet is fully stable for various time steps.

We can also play around with different initial conditions in order to find the escape velocity from an orbit around the sun with distance one astronomical unit, 1 AU. The theoretical value for the escape velocity, is given by

$$
v = \sqrt{8\pi^2}{r},
$$

and with $r=1$ AU, this means that the escape velocity is $2\pi\sqrt{2}$ AU/yr. To obtain this we required that the kinetic energy of Earth equals the potential energy given by the gravitational force.

Setting

$$
\frac{1}{2}M_{\mathrm{Earth}}v^2=\frac{GM_{\odot}}{r},
$$

and with $GM_{\odot}=4\pi^2$ we obtain the above relation for the velocity. Setting an initial velocity say equal to $9$ in the above code, yields a planet (Earth) which escapes a stable orbit around the sun, as seen by running the code here.

```
DeltaT = 0.01
#set up arrays
tfinal = 100
n = ceil(tfinal/DeltaT)
# set up arrays for t, a, v, and x
t = np.zeros(n)
v = np.zeros((n,2))
r = np.zeros((n,2))
# Initial conditions as compact 2-dimensional arrays
r0 = np.array([1.0,0.0])
# setting initial velocity larger than escape velocity
v0 = np.array([0.0,9.0])
r[0] = r0
v[0] = v0
Fourpi2 = 4*pi*pi
# Start integrating using the Velocity-Verlet  method
for i in range(n-1):
    # Set up forces, air resistance FD, note now that we need the norm of the vecto
    # Here you could have defined your own function for this
    rabs = sqrt(sum(r[i]*r[i]))
    a =  -Fourpi2*r[i]/(rabs**3)
    # update velocity, time and position using the Velocity-Verlet method
    r[i+1] = r[i] + DeltaT*v[i]+0.5*(DeltaT**2)*a
    rabs = sqrt(sum(r[i+1]*r[i+1]))
    anew = -4*(pi**2)*r[i+1]/(rabs**3)
    v[i+1] = v[i] + 0.5*DeltaT*(a+anew)
    t[i+1] = t[i] + DeltaT
# Plot position as function of time
fig, ax = plt.subplots()
ax.set_xlabel('x[AU]')
ax.set_ylabel('y[AU]')
ax.plot(r[:,0], r[:,1])
fig.tight_layout()
save_fig("EscapeEarthSunVV")
plt.show()
```