Chapter 05

【フランカー課題】課題ブロックの作成

snishiyama
snishiyama
2022.07.30に更新

はじめに

前章では,自作関数を用いて手軽に試行変数を作成できるようにしたとともに,刺激を複数回ランダムに提示できるようにしました。これでフランカー課題の根幹となる部分は完成しました。少しずつ実際の実験らしくしていきましょう。

実験ではしばしば注視点が刺激(系列)の前に提示されます。本章では実験で注視点が提示されるようにする方法を紹介します。その流れで,試行変数のまとまり(ここではブロックと呼ぶことにします)を作成する方法についても紹介します。

全体の始めにだけ注視点を提示する

注視点を導入するためには,それ用の変数を作成し,試行変数の配列に加えればよいです。

注視点用の試行変数は例えば以下のようにします。

const fixation = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: '+',
  choices: 'NO_KEYS', // どのキー入力も受け付けない
  trial_duration: 500, // 提示時間のための設定項目(単位はミリ秒)
};

注視点はキー入力によってスキップされてほしくなので,choicesには'NO_KEYS'が指定されています。こうすると,どのキー入力も受け付けなくなります。キー入力を受け付けない代わりに,一定時間が経過したら注視点は画面からなくなってほしいので,trial_durationという設定を新たに用意し,そこに500を設定しています。

また,これまでのtrial 変数で設定していたpost_trial_gapは設定されていません。このように試行変数に設定できる項目は実は色々あります。設定しなかった場合は jsPsych 側で用意されている既定値が適用されます。例えば,post_trial_gaptrial_durationの既定値は nullです。nullは値がないことをします値で,nullの場合の挙動はそれぞれ,

  • post_trial_gap: nullの場合,gap は 0 になります。つまり,その試行が完了した直後に次の試行が開始されます。
  • trial_duration: nullの場合,試行はキー入力があるまで継続されます。

少し話が逸れていまいました。作成した注視点用の変数fixationを用いて,注視点が提示されるようにしましょう。

試行系列の先頭に注意点を提示する(ランダマイズなし)

まず,もっとも簡単なケースから取り組んでみます。前章の演習 1 で作成したコードに編集していきます。試行の順序をランダマイズせず,さらに全試行の最初のみで注視点が提示される場合,jsPsych.runの引数に指定する試行配列の先頭にfixationを入れればよいです。具体的には,以下のようになります。

const trials = [fixation, trial1, trial2, trial3, trial4];
jsPsych.run(trials);

なんのこっちゃという方は,第 3 章の「刺激の系列提示」という節を見直してみてください。

試行系列の先頭に注意点を提示する(ランダマイズあり)

前章で試行実施順のランダマイズを行ったところなので,試行順序をランダマイズしつつ,最初で 1 度だけ注視点が提示されるようにしていきましょう。

とはいえ,上の方法で作成したtrialsをそのままランダマイズすると,fixationも含めてシャッフルしてしまうので,注視点が先頭以外で表示される可能性が出てきてしまいます。

// これはダメ
const trials = [fixation, trial1, trial2, trial3, trial4];
const trialsRandom = jsPsych.randomization.repeat(trials, 2);
jsPsych.run(trialsRandom);

それではどうすればいいかというと,フランカー試行だけで構成された配列をシャッフルし,その後,その配列の先頭にfixationを追加するといいでしょう。配列への要素の追加は,splice()という配列のメソッドを用いれば可能です。

splice()を以下のように用いると,配列の先頭に要素を追加することができます。

const months = ['Feb', 'March', 'April', 'June'];
months.splice(0, 0, 'Jan');
// months の中身は以下のようになる。
// ['Jan', 'Feb', 'March', 'April', 'June']

つまり,splice()の第 1, 2 引数に0を,第 3 引数に追加したい要素を指定すれば,先頭に要素を追加することができます。本節では,先頭にfixationを入れたいので,以下のようにします。

const trials = [trial1, trial2, trial3, trial4];
const trialsRandom = jsPsych.randomization.repeat(trials, 2);
trialsRandom.splice(0, 0, fixation); // 先頭に fixation 変数を追加
jsPsych.run(trialsRandom);

なお,第 2 引数を0に固定したまま,第 1 引数を他の数値に変更すると,任意の場所に要素を追加することができます。詳しくは以下のページなどを参照してください。

試行ごとに注視点を提示する:ブロックの作成

試行系列全体の始めではなく,試行ごとに注視点を提示したいということもあると思います。ランダマイズを考慮しなければ,以下のように書くことができます。

const trials = [fixation, trial1, fixation, trial2, fixation, trial3, fixation, trial4];
jsPsych.run(trials);

あまり実用的ではなさそうです。fixationと任意の試行変数を,「fixationtrial1」,「fixationtrial2」のようにまとまりとして扱うことができれば,ランダマイズにも対応できそうです。

実は,jsPsych ではそのようなブロック変数を作成することができます。{}の中に,timelineという設定項目(プロパティ)を用意し,そこにまとめたい試行変数の配列を指定します。具体的には以下のとおりです。

const block1 = {
  timeline: [fixation, trial1],
};
const block2 = {
  timeline: [fixation, trial2],
};

// block3, 4 も同様に

jsPsych.run([block1, block2]);
// block3, block4も定義されていれば
// jsPsych.run([block1, block2, block3, block4]);

前章で取り上げたように,ブロック変数を予め配列にしておけば,ブロックの実施順もランダマイズすることができます。

const blocks = [block1, block2];
const blocksRandom = jsPsych.randomization.repeat(blocks, 2);
jsPsych.run(blocksRandom);

これで,各試行の前に注視点が表示される場合についてもランダマイズ対応ができるようなりました。ひとつの懸念点は,各ブロック変数をほぼコピペで作成していることです。timelineで指定されている配列の試行変数のみが違うだけで,それ以外の,fixationも含めた記述は同じなので,関数化したほうが管理などが楽そうです。関数化については前章で扱ったので,その復習も兼ねて,演習として取り組んでみましょう。

演習

  • ブロックの作成を関数化しよう
演習のコード例

ここではコード例を 2 つ記載しています。作成した関数を何度も呼び出すのであれば,一つ目よりも 2 つ目のほうが,コード量が少ないです。一見,現状のコード量が少なくなる 2 つ目の方が良さそうに見えますが,長い目で管理コストを考えると,1 つ目の実装のほうがいいかもしれません(後述)。前章のmap() がよく分かっていないのであれば,2 つ目がいいでしょう。最終的には好みです。

// createTrialの定義は省略
// fixationの定義も createBlock よりも前にしておく

const createBlock = (trl) => {
  const block = {
    timeline: [fixation, trl],
  };
  return block;
};

const trial1 = createTrial('<<<<<');
const trial2 = createTrial('>>>>>');
const trial3 = createTrial('<<><<');
const trial4 = createTrial('>><>>');

const block1 = createBlock(trial1);
const block2 = createBlock(trial2);
const block3 = createBlock(trial3);
const block4 = createBlock(trial4);

const blocks = [block1, block2, block3, block4];
const blocksRandom = jsPsych.randomization.repeat(blocks, 2);
jsPsych.run(blocksRandom);

createTrial()createBlockの中に入れると関数を呼び出す回数を減らすことができ,コードもより簡潔になります。

const createBlock = (stim) => {
  const trial = createTrial(stim);
  const block = {
    timeline: [fixation, trial],
  };
  return block;
};

const block1 = createBlock('<<<<<');
const block2 = createBlock('>>>>>');
const block3 = createBlock('<<><<');
const block4 = createBlock('>><>>');

const blocks = [block1, block2, block3, block4];
const blocksRandom = jsPsych.randomization.repeat(blocks, 2);
jsPsych.run(blocksRandom);

createTrialを関数内で実行しているので,createBlockの引数はcreateTrialの引数として与える「刺激」になっていることに注意してください。なお,一つ目の実装では,createBlockの引数は,fixationの後に並べたい試行変数です。

全体を示すを以下のようになります。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <script src="../jspsych/dist/jspsych.js"></script>
    <script src="../jspsych/dist/plugin-html-keyboard-response.js"></script>
    <link rel="stylesheet" href="../jspsych/dist/jspsych.css" />
  </head>
  <body></body>
  <script>
    const jsPsych = initJsPsych();

    const createTrial = (stim) => {
      const trial = {
        type: jsPsychHtmlKeyboardResponse, // 試行のタイプ指定
        stimulus: stim, // 提示する刺激
        choices: ['f', 'j'],
        post_trial_gap: 500,
      };
      return trial;
    };

    const fixation = {
      type: jsPsychHtmlKeyboardResponse,
      stimulus: '+',
      choices: 'NO_KEYS', // どのキー入力も受け付けない
      trial_duration: 500, // 提示時間のための設定項目(単位はミリ秒)
    };

    const createBlock = (stim) => {
      const trial = createTrial(stim);
      const block = {
        timeline: [fixation, trial],
      };
      return block;
    };

    const block1 = createBlock('<<<<<');
    const block2 = createBlock('>>>>>');
    const block3 = createBlock('<<><<');
    const block4 = createBlock('>><>>');

    const blocks = [block1, block2, block3, block4];
    const blocksRandom = jsPsych.randomization.repeat(blocks, 2);

    jsPsych.run(blocksRandom);
  </script>
</html>

もし,前章で紹介したmap()を駆使できるのであれば,一つ目のcreateBlock()の実装のほうがいいかもしれません。2 つ目の実装だと,createTrial()が受け取れる引数の数が増えるとそれに応じてcreateBlockの引数も増やす必要があるからです。

おわりに

本章では,試行ごとに注視点を提示することを題材に,課題ブロックの作成方法について解説しました。注視点の提示タイミングは課題によって変わりますが,そうではない場面でも,課題ブロックを作ることが必要になってくることもあるはずなので,覚えておくよいと思います。次回は文字サイズの変更に取り組みます。