【jsPsych】行動実験でグラフを提示してみよう

# はじめに
- 5年ほど前に以下のような質問が黒木先生宛にきていました。
    - https://groups.google.com/g/ptb_japanese_users/c/Xz9cNArFI1w
- どうやら， jsPsychでの実験中にpie chartといったグラフを提示しようという試みのようです。今更感はありますが，今回はサンプルコードを書いてみました。

- webブラウザ上でグラフを提示するにはchart.jsライブラリというものが簡便かつ美しく表示できるので，こちらを利用します。
- chart.jsを使用するには`<canvas>`タグを利用する必要があります。jsPsychでは`<canvas>`を利用する際にはcanvas-button-responseやcanvas-keyboard-responseといったプラグインを用いるのが有用ですので，その利用方法について簡単にまとめます。

# chart.js
- chart.jsはJavaScriptでグラフ(chart)を表示するためのライブラリです(https://www.tohoho-web.com/ex/chartjs.html)
- 単純な棒グラフや線グラフだけでなく，多くの形式でグラフを作成することができます。
- htmlで段落を示すには`<p>`タグを用い，画像を提示するには`<img>`タグを用いるのと同様に，四角や円といった図形を表示するには`<canvas>`タグを利用します。
- `<canvas>`タグは単純な図形を表示するだけなら簡単ですが，複雑な図形を描くのはかなり大変です。
- グラフを`<canvas>`上で簡単に書けるようにしたライブラリがchart.jsです。

- 利用するためには，jsPsychでプラグインを事前に読み込むのと同様に，chart.jsライブラリを読み込んでおく必要があります。オンラインであれば，以下のようにして読み込みます。

```html
<script src="https://unpkg.com/@jspsych/plugin-html-button-response@1.1.2"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script> // こいつがchart.jsの読み込み
<script src="https://unpkg.com/@jspsych/plugin-canvas-button-response@1.1.2"></script>
```

# jsPsychにおける`<canvas>`
- jsPsychにおいて課題中に図形を表示させるには，canvas-button-responseやcanvas-keyboard-responseプラグインを用いるのが一般的です。
    - 黒木先生が開発したjspsych-psychophysicsも有用です。
    - https://jspsychophysics.hes.kyushu-u.ac.jp/
- canvas-button-responseの使い方は公式サイトから確認してください。(https://www.jspsych.org/7.3/plugins/canvas-button-response/)
## 公式サイトのデモ
- canvas-button-responseプラグインの使用方法は公式サイトにデモがあります。以下のようなコードを書きます。

```js
function filledCirc(canvas, radius, color) {
    var ctx = canvas.getContext("2d");
    ctx.beginPath();
    ctx.arc(150, 150, radius, 0, 2 * Math.PI);
    ctx.fillStyle = color;
    ctx.fill();
}

var circle_1 = {
    type: jsPsychCanvasButtonResponse,
    stimulus: function(c) {
        filledCirc(c, 100, 'blue');
    },
    canvas_size: [300, 300],
    choices: ['Red', 'Green', 'Blue'],
    prompt: '<p>What color is the circle?</p>',
    data: {color: 'blue', radius: 100}
};
```

- このプラグインでは，図形を描くfunctionを事前に作成し(filledCirc)，それをcanvas-button-responseで表示するという流れになります。
- この事前に作成するfunctionでグラフに関する命令を書きます(chart.js)

# サンプルコード
- 今回は実験課題中のFキーとJキーを押した回数とそれぞれの平均反応時間をグラフ化してみます。
    - キー押し回数は円グラフ，反応時間は棒グラフで描画します。

- 例えば，pie chartを表示するには，以下のようなコードを書きます。

```js
function piechart(c, F, J) {
        var ctx = c.getContext("2d");
        let pieConfig = {
          type: 'pie',
          data: {
            labels: ['Fkey','Jkey'],
            datasets: [{
              label: 'キー押し回数',
              data: [F, J], ///F, Jにはそれぞれ，課題中にFキー，Jキーを押した回数が格納されています
              backgroundColor: [
                '#ff0000',
                '#0000ff',
              ]
            }]
          },
        };
        let pieChart = new Chart(ctx, pieConfig);
    }
```
- この`type`の部分を`bar`に変えれば，棒グラフになります。
    - chart.jsの基本的な書き方は日本語でも多くの記事があるので，参考サイトには困らないと思います。

- どのようなデータを用いるのかはjsPsych APIでデータを抽出し，計算すれば良いです。

# Demoとコード
- 以下のURLで円グラフと棒グラフを表示するデモを実施することができます。

- https://takat0-ch.github.io/jsPsych-chart/

- 実際のコードは以下です。

```html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>My experiment</title>
    <script src="https://unpkg.com/jspsych@7.3.3"></script>
    <link href="https://unpkg.com/jspsych@7.3.3/css/jspsych.css" rel="stylesheet" type="text/css" />
    <script src="https://unpkg.com/@jspsych/plugin-html-keyboard-response@1.1.2"></script>
    <script src="https://unpkg.com/@jspsych/plugin-html-button-response@1.1.2"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
    <script src="https://unpkg.com/@jspsych/plugin-canvas-button-response@1.1.2"></script>


</head>

<body></body>
<script>
    /* initialize jsPsych */
    const jsPsych = initJsPsych({
        on_finish: function () {
            jsPsych.data.displayData();
        }
    });

    const timeline = [];

    const ins = {
        type: jsPsychHtmlButtonResponse,
        stimulus: '<p>Demoを始める。</p>',
        choices: ['Start']
    };

    const fix = {
        type: jsPsychHtmlKeyboardResponse,
        stimulus: '',
        choices: "NO_KEYS",
        trial_duration: 1000
    }
    
    const trial = {
        type: jsPsychHtmlKeyboardResponse,
        stimulus: "<p>FかJのどちらかを入力してください。</p>",
        choices: ["f", "j"],
        data: {
            task: 'response'
        },
        on_finish: function (data) {
            data.Fkey = jsPsych.pluginAPI.compareKeys(data.response, 'F');
            data.Jkey = jsPsych.pluginAPI.compareKeys(data.response, 'J');
        }

    };




    const procedures = {
        timeline: [fix, trial],
        repetitions: 10,
        on_finish: function () {
            trials = jsPsych.data.get().filter({ task: 'response' });
            Fkey_trials = trials.filter({ Fkey: true });
            Jkey_trials = trials.filter({ Jkey: true });
            FkeyCount = Fkey_trials.count();
            JkeyCount = Jkey_trials.count();
            Frt = Math.round(Fkey_trials.select('rt').mean());
            Jrt = Math.round(Jkey_trials.select('rt').mean());
        }
    };

    function piechart(c, F, J) {
        var ctx = c.getContext("2d");
        let pieConfig = {
          type: 'pie',
          data: {
            labels: ['Fkey','Jkey'],
            datasets: [{
              label: 'キー押し回数',
              data: [F, J],
              backgroundColor: [
                '#ff0000',
                '#0000ff',
              ]
            }]
          },
        };
        let pieChart = new Chart(ctx, pieConfig);
    }

    function barchart(c, F, J) {
        var ctx = c.getContext("2d");
        let barConfig = {
          type: 'bar',
          data: {
            labels: ['Fkey','Jkey'],
            datasets: [{
              label: '平均反応時間(ms)',
              data: [Frt, Jrt],
              backgroundColor: [
                '#ff0000',
                '#0000ff',
              ]
            }]
          },
        };
        let barChart = new Chart(ctx, barConfig);
    }

    var chart1 = {
        type: jsPsychCanvasButtonResponse,
        stimulus: function(c) {
            piechart(c, FkeyCount, JkeyCount)
        },
        canvas_size: [500, 500],
        choices: ['棒グラフに進む'],
        prompt: '<p>キー押し回数のpie chartはこんな感じ</p>'
    };

    var chart2 = {
        type: jsPsychCanvasButtonResponse,
        stimulus: function(c) {
            barchart(c, Frt, Jrt)
        },
        canvas_size: [500, 500],
        choices: ['終了する'],
        prompt: '<p>それぞれのキー押しの平均反応時間はこんな感じ</p>'
    };



    timeline.push(ins, procedures, chart1, chart2);
    jsPsych.run(timeline);

</script>


</html>
```

- いつかもう少し丁寧に書くかもしれません。