カイ二乗検定アプリをReactでつくる

# 概要
* スマートフォン片手にカイ二乗検定できるアプリをreactで書きました
* create-react-app自体にPWAの準備が整っていますので何もしなくてもPWAとしてうごきます
* Androidならホーム画面から起動できるアプリとして動作させられます

# 動作画像
[![https://gyazo.com/6a6ddac95469d1ee243221f2b253f62d](https://i.gyazo.com/6a6ddac95469d1ee243221f2b253f62d.gif)](https://gyazo.com/6a6ddac95469d1ee243221f2b253f62d)

* 解析結果をテキストデータとしてコピー

赤矢印の箇所を押すと解析結果をテキスト形式でコピーできます。

[![https://gyazo.com/e47f897b41aad096795801ac5069badf](https://i.gyazo.com/e47f897b41aad096795801ac5069badf.png)](https://gyazo.com/e47f897b41aad096795801ac5069badf)

```text

* 入力データ
        treat non-treat
case        5         2
control    12         8

* 検定結果
χ2 0.290
p値 0.590 （有意差なし）

* 解析日時
Wed Feb 28 2018 18:52:18 GMT+0900 (JST)

* Powered by
https://case-control-test.firebaseapp.com

```

# すぐ使いたい人
## サイトで使いたい
Firebaseのhostingで公開しています。

https://case-control-test.firebaseapp.com/

### PWAとしてホーム画面に追加したい
現在は、iOSでは不可。Androidのみです。

でも、[このような情報があるので](https://www.suzukikenichi.com/blog/pwa-finally-begins-working-in-ios-safari/)iOSでPWAでアプリっぽく使える日も近いです。

とりあえず、現状可能なAndroid（chrome）での追加方法の説明をします。

* Andoridのchromeブラウザで[サイト](https://case-control-test.firebaseapp.com/)を開き、右上からブラウザのメニューを呼び出し、**ホーム画面に追加**を押す

[![https://gyazo.com/e6ca90147b803801816364a6b995cdb6](https://i.gyazo.com/e6ca90147b803801816364a6b995cdb6.jpg)](https://gyazo.com/e6ca90147b803801816364a6b995cdb6)

* ホーム画面に追加されます。ホーム画面上のアイコンをクリックすればアプリの様にスプラッシュ画面が出て起動できます！

[![https://gyazo.com/3799b37198a69746f647ed860cc699fb](https://i.gyazo.com/3799b37198a69746f647ed860cc699fb.png)](https://gyazo.com/3799b37198a69746f647ed860cc699fb)


## localで動かしたい
* https://github.com/junara/two-by-two-table-stat からコードをクローン後、クローンしたフォルダ内へ移動して下記コマンドを打てば localhost:3000で起動します。

```
> yarn install
> npm run build
```

# 環境
* [前回のQiitaの記事](https://qiita.com/junara/items/74801923ca108b328b26)の続きなので、それに準じてください

# 手順
## コード概要
* componentsとcontainersフォルダ
reactのcomponentをこちらに保存する

* modulesフォルダ
    * 関数とか（今回であればカイ二乗検定を計算する所とか）
        * Graph: グラフを描画するためのデータを用意する
        * Stat: カイ二乗検定の計算する
    * サービス
        * LocationHref: mailtoなどのリンクを出力
    * 定数
        * MyProfile: 自己紹介の用のURL
        * Site: サイトのURL

[![https://gyazo.com/fdb4858141db28fab92b93ac02cfef43](https://i.gyazo.com/fdb4858141db28fab92b93ac02cfef43.png)](https://gyazo.com/fdb4858141db28fab92b93ac02cfef43)

## コンポーネントの紹介
* `<TwoByTwoTable/>`： stateはここだけで管理。あとはバケツリレー。
    * `<StatHeader/>`： ロゴ
    * `<StatBody/>`： 結果の表示（統計量、表、グラフ）
        * `<CopyToClipboardButton/>`: 結果をコピーするためのコンポーネント
        * `<SendMailButton/>`: 結果をメールで送信するためのコンポーネント
    * `<StatForm/>`: データを入力するコンポーネント。stateの変更はthis.props.handleChangeでおこなう。
    * `<StatFooter/>`
        * `<AboutMe/>`: 自己紹介
        * `<AboutThis/>`: アプリ紹介

### TwoByTwoTable

https://github.com/junara/two-by-two-table-stat/blob/master/src/containers/TwoByTwoTable.js

いつもは、reduxを使うのだけど、今回のアプリには過剰なので素直にバケツリレーで作りました。久しぶりにバケツリレーで書いた。階層が2階層で、非同期のタスクを書く必要がなければ、バケツリレーのほうが書いていて楽しいですね。

```src/containers/TwoByTwoTable.js
  handleChange = name => event => {
    const value = event.target.value ? parseInt(event.target.value, 10) : ''
    this.setState({[name]: value})
  }
```

formで使うメソッド。子コンポーネントで`onChange={this.props.handleChange('caseTreatment')}`として使うと、state.caseTreatmentを変更することができます。すごい便利。


### StatHeader
そんなに説明するところないな。

### StatBody

https://github.com/junara/two-by-two-table-stat/blob/master/src/containers/StatBody.js

```src/containers/StatBody.js
    const chiSq = Stat.chiSq({a: caseTreatment, b: caseNonTreatment, c: controlTreatment, d: controlNonTreatment})
    const chiP = Stat.chiP(chiSq)
```

今回χ2検定を行うための関数を呼び出しています。

* χ2値を計算する（Stat.chiSq）
* chiSq値を累積分布関数にわたしてあげる（Stat.chiSq）とp値を出力してくれる

### StatForm

https://github.com/junara/two-by-two-table-stat/blob/master/src/containers/StatForm.js

```src/containers/StatForm.js
                    <TextField
                      id={'number'}
                      autoFocus
                      type={'number'}
                      margin={'normal'}
                      onChange={this.props.handleChange('caseTreatment')}
                      value={this.props['caseTreatment']}
                    />
```

onChangeのhandleChangeでstateを変化させ、propsとして受け取った値をvalueに入れる。

## PWA用のカスタマイズ
create-react-appで作ると、最初からPWAの準備はできています。

```public/index.html
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
```

なので、デフォルトで作られているサイト名やアイコン（logo.svgとfavicon.icoとlogo.png）を若干カスタマイズすればOKです。

```public/manifest.json
  "short_name": "2x2 STAT",
  "name": "Case control data analysis by chi squared test",
```

## 統計解析
### Stat.js

χ2値の計算。適当に教科書から取ってきました。ごく一般的な数式。[このサイト](https://www.fujiitoshiki.com/improvesociety/?tag=%E5%9B%9B%E5%88%86%E8%A1%A8)とおなじです。

```
export const chiSq = ({a, b, c, d}) => {
  if (!isValid({a, b, c, d})) return 'error'
  const n = a + b + c + d
  const calc = ((a * d - b * c) ** 2) * (n / ((a + b) * (c + d) * (a + c) * (b + d)))
  return (calc.toFixed(3))
}
```

p値の計算。自由度1のχ2の累積分布関数から計算する。累積分布関数は[chi-squared](https://github.com/substack/chi-squared.js)をつかいました。

```
export const chiP = (chiSq) => {
  if (!(chiSq > 0)) return 'error'
  const df = 1 // dfは自由度
  const calc = 1 - chi.cdf(chiSq, df) // cdfは累積分布関数
  return calc.toFixed(3)
}
```

## その他ライブラリの紹介
### fontawesome
* twitterとgithubとfacebookのアイコン画像が欲しいので導入。fontawesomeってここ半年でバージョンが5になったのですね。svgになったので、表現力が向上したっぽい。

```src/components/AboutThis.js
import FontAwesomeIcon from '@fortawesome/react-fontawesome'
import { faGithub } from '@fortawesome/fontawesome-free-brands/index'

省略
 <FontAwesomeIcon icon={faGithub} size="lg" fixedWidth/>
```

### react-chartjs-2

グラフを作成するためのライブラリ。chart.jsをreact用のコンポーネントとして使えるされたラッパー。

dataをobjectで渡せば描画できます。

```src/containers/StatBody.js
import { Doughnut } from 'react-chartjs-2'

省略
<Doughnut data={Graph.data(controlTreatment, controlNonTreatment)} options={Graph.options}/>
```

```src/modules/Graph.js
export const data = (t = 10, nt = 10) => {
  return {
    labels: [
      'Non Treat',
      'Treat',
    ],
    datasets: [{
      data: [nt, t],
      backgroundColor: [
        color.nonTreat,
        color.treat,
      ],
      hoverBackgroundColor: [
        color.nonTreat,
        color.treat,
      ],
    }],
  }
}
```

### list-it
* 表をテキストで出力する時に、ずれないようにうまい具合に空白を入れてくれる。すごい便利。

http://takamints.hatenablog.jp/entry/npm-list-it-preformatted-text-table-generator


```src/modules/Stat.js
export const tableString = ({a, b, c, d}) => {
  if (!isValid({a, b, c, d})) return 'no result'
  const arr = [
    ['', 'treat', 'non-treat'],
    ['case', a, b],
    ['control', c, d],
  ]
  let buf = listIt.buffer({'autoAlign': true})
  buf.d(arr)
  return (buf.toString())
}
```


### react-scroll-to-component

任意の場所に移動するためのメソッド。

移動したい所にrefを定義して**scrollToComponent(ref, option)**を実行すると、refの場所にスクロールします。

```src/containers/TwoByTwoTable.js
  componentDidMount () {
    this.scrollToForm()
  }

  scrollToForm = () => {
    scrollToComponent(this['form'], {
      align: 'bottom',
      duration: 1000,
    })
  }
省略
            <StatForm {...this.state}
                      handleChange={this.handleChange}
                      ref={(section) => {this['form'] = section}}
            />
```

### react-share

有名どころのシェアボタンを作成出来るreact component。ボタンと画像を別々にimpportして、画像（TwitterIcon）をボタン（TwitterShareButton）囲むところがミソ。

```
import {
  TwitterShareButton,
  TwitterIcon,
} from 'react-share'

省略
<TwitterShareButton
  title={Site.title}
  url={Site.rootUrl}
  hashtags={[Site.title]}
  style={styles.buttonMarginRight}
>
  <TwitterIcon
    size={32}
    round/>
</TwitterShareButton>
```


# 発展
* 統計量を増やすかも（オッズ比とか陽性的中率とか検出力とか）
* 分散データの解析（t検定は作るつもり）。ただし、ノンパラはライブラリがあれば実装します。自分で計算書くのはつらいので。

# 備考

もっと色々やりたい場合は、PCの目の前に腰を据えて、RとかSASとSPSSとかSとか使ってください。

以上。

Happy Statistics.