21.Go-NoGoTask (jsPsych7.3.0)
Go/NoGoTaskを作成してみましょう。
このページは,jsPsychの公式サイト tutorialと
九州大学黒木 大一朗先生のサイト
を参考に作成しました。
ここで紹介するGo/NoGo taskは,青の円がでたらできるだけ早くFキーを押す,オレンジの円が出たらどのキーも押さないというものです。Go/NoGo taskのsequenceは,図1の通りです。青とオレンジがそれぞれ5回提示されます。青とオレンジは,疑似ランダムに提示される様に設定していきます。
https://gyazo.com/fb33a5f5e0271aa71e76c5beb6756003
図1.Go/NoGo taskのシークエンス。
それでは,実際のコードを見ていきます。
まず,ヘッダーからです。
今回は,日本語でwelcomeメッセージやinstruction(計測の説明)を日本語で記述するため,以下のコードをヘッダーに追加します。この一文がなければ日本語で書いた文章は文字化けします。
code:1.html
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
今まで通り,まず,jsPsychのライブラリーを読み込みます。preloadを読み込みます。文章を記述するため,html-keyboard-response.jsを読み込みます。
code:2.html
<script src="dist/jspsych.js"></script>
<script src="dist/plugin-preload.js"></script>
<script src="dist/plugin-html-keyboard-response.js"></script>
次に,図形(青丸とオレンジの丸)を表示してキーボードで反応を取得するので,image-keyboard-response.jsを読み込みます。
code:3.html
<script src="dist/plugin-image-keyboard-response.js"></script>
最後に,<link>を使ってスタイルシートを読み込みます。
code:4.html
<link href="dist/jspsych.css" rel="stylesheet" type="text/css" />
<script>~</script>です。ここでは,具体的に参加者に提示する,welcomeメッセージやinstruction,fixation1など,図1の四角で囲まれたブロックをそれぞれの変数として作成していきます。
作成したら変数を時系列にtimelineに加えていきます。
まず,initJsPsychで,jsPsychという名前の変数を定義しています。
この中に課題が終了した後のデータ処理方法を指定します。
jsPsych.data.displayData('csv');では,CSV形式でデータを画面上に表示します。
jsPsych.data.get().localSave('csv', 'data.csv');では,CSV形式でdata.csvという名前でデータを保存します。
保存先は一般的にはダウンロードフォルダになります。
code:initJsPsych.html
var jsPsych = initJsPsych({
on_finish: function() {
jsPsych.data.displayData('csv'); //終了後CSV形式でデータを画面上に表示
jsPsych.data.get().localSave('csv', 'data.csv');
}
})
次に,timelineの定義です。作成した変数をこの入れ物に入れていきます。
code:create timeline.html
var timeline = [];
次に,preloadです。事前に自動で画像を読み込む様にしておきます。
code: preload.html
var preload = {
type: jsPsychPreload,
auto_preload: true
};
timeline.push(preload);
次は,welcomeメッセージです。
画面中央に「研究にご参加いただきありがとうございます。<br>計測を開始するにはいずれかのキーを押して下さい。」を提示したいと思います。<br>は,改行です。
varを使ってwelcomeを以下の様に定義します。
typeは,jsPsychHtmlKeyboardResponse,stimulusは, "研究にご参加いただきありがとうございます。<br>計測を開始するにはいずれかのキーを押して下さい。"とします。
welcomeを定義したら,timeline.pushでtimelineに加えます。
code:define welcome message.html
/* define welcome message */
var welcome = {
type: jsPsychHtmlKeyboardResponse,
stimulus: "研究に参加いただきありがとうございます。<br>計測を開始するにはいずれかのキーを押して下さい。"
};
timeline.push(welcome);
次に,instructionです。
https://gyazo.com/67b0dfedf6fc132c8dd32b379a3922d1
図2.instruction,説明文と円の関係。
図2の様に,説明文を最初に書き,その後に青とオレンジの円を横に並べます。それぞれの円の下には,参加者がどの様に反応するか,簡単な説明をつけます。最後に,キーを押して実験を開始する様に促します。
ここで使用する画像(blue.pngとorange.png)は,\examples\imgに入っています。
まず,varを使用してinstructionsを定義します。文章を提示するので,typeは,jsPsychHtmlKeyboardResponseとします。stimulusは,<p>を使用します。途中,<strong>青</strong>を用いることで文字を太字で強調します。
次に,jspsych.cssで定義しているスタイルを部分的に変更して,青とオレンジの2つの円を横に並べて表示します。
<div style='width: 700px;'> を使用して,この部分だけ横幅を700ピクセルに変更します。
<div style='float: left;'>を使用して,左側に青い円を配置します。
<div class='float: right;'>を使用して,右側にオレンジの円を配置します。
それぞれの円の下に説明文をつけるので,<p class='small'>を使用して文章を記述します。
</div>を最後に入れることで,元のスタイルの設定に戻ります。
instruction後すぐに計測が開始しない様にpost_trial_gapを使用して,2秒後に開始する様に設定しました。
最後に,instructionsをtimelineに加えます。
code:define instructions trial.html
var instructions = {
type: "html-keyboard-response",
stimulus: "<p>この計測では、画面の中央に色のついた円が呈示されます。<br>" +
"もし円の色が<strong>青</strong>だったら、できるだけ速く「Fキー」を押して下さい。<br>" +
"もし円の色が<strong>オレンジ</strong>だったら、どのキーも押さないで下さい。</p>" +
"<div style='width: 700px;'>"+
"<div style='float: left;'><img src='img/blue.png'></img>" +
"<p class='small'><strong>Fキーを押して下さい。</strong></p></div>" +
"<div class='float: right;'><img src='img/orange.png'></img>" +
"<p class='small'><strong>どのキーも押さないで下さい。</strong></p>"+
"</div>" +
"</div>"+
"<p>計測を開始するにはいずれかのキーを押して下さい。</p>",
post_trial_gap: 2000
};
timeline.push(instructions);
次に,課題開始のfixation1の定義です。
これは,instructionが終わって,いきなり課題が始まらない様に,fixation(固視点)を2秒間提示します。
varを使用して fixation1を定義します。
fixationは,文字「+」を使用しますので,typeは,jsPsychHtmlKeyboardResponseを使用します。stimulusは,「+」,フォントサイズは60pxとしています。この時,参加者のボタン押しは必要ないので,choicesは,"NO_KEYS"とします。2秒間提示したままにするので,trial_durationは,2000とします。計測後,結果のデータcsvファイルで収集するので,提示された刺激の種類を定義します,dataで{task: 'fixation'}と設定します。
最後に,timelineにfixation1を追加してください。
code:fixation1
var fixation1 = {
type: jsPsychHtmlKeyboardResponse,
stimulus: '<div style="font-size:60px;"> + </div>',
choices: "NO_KEYS",
trial_duration: 2000,
data: {task: 'fixation'}
}
timeline.push(fixation1);
次に,図1の2段目からの内容です。課題中の刺激(fixation,Go,NoGo)を作成していきます。疑似ランダムの配列については,fixationと刺激の定義後に設定していきます。
https://gyazo.com/fb33a5f5e0271aa71e76c5beb6756003
図1.Go/NoGo taskのシークエンス。
まずは,課題間のfixationです。
このfixationは,durationを1-2.25秒とすることで参加者に課題のタイミングを予測させないようにします。そのために,fixationを定義する前に,durationをランダムに設定できるfix_durationを定義します。
Math.random()関数は,0–1(0以上1未満)の範囲で擬似乱数を返します。
Math.floorは,引数として与えた数以下の最大の整数(少数点以下切り捨て)を返します。
次に,fixationです。基本的にはfixation1と同様です。違うところは,trial_durationが,fix_durationとなっているところです。
code:fixation.html
var fix_duration = function() {
return Math.floor( Math.random() * 1500 ) + 1000;
}
var fixation = {
type: 'html-keyboard-response',
stimulus: '<div style="font-size:60px;"> + </div>',
choices: jsPsych.NO_KEYS,
trial_duration: fix_duration,
data: {task: 'fixation'}
}
一般的に心理・認知課題では同じ刺激を繰り返し提示します。その都度定義した刺激(変数)をtimeline.pushで書いても良いですが,刺激の順番を参加者毎に擬似ランダムで提示したい場合など以下の様にすると便利です。
ここでは,画像を読み込むための変数と画像を提示する変数をそれぞれ定義します。
まず,画像を読み込むための変数,test_stimuliをvarを使用して定義します。
この中に青とオレンジの円を読み込みます。さらに,参加者のパフォーマンスを評価するため,dataを定義し,その中に課題の種類(task)と正解のキー(correct_response)を,タグ付けします。青い円が提示されたときは,Fキーを押すのでcorrect_response: 'f'とします。オレンジの円が提示されたときは,どのキーも押さないので correct_response: '' ←空としたいのですが,後でjsPsych.pluginAPI.compareKeysを用いてcorrect_responseと反応したキーとを比較しますが,空(null)の比較はできないので,関係のないキーを適当に指定します。ここではcorrect_response: 'q'とします。
code:test_stimuli1.html
var test_stimuli = [
{stimulus: "img/blue.png", data: { test_part: 'test', correct_response: 'f' } },
{stimulus: "img/orange.png",data: { test_part: 'test', correct_response: 'q' }}
];
次に,画像を提示するための変数,testを定義します。
typeは, jsPsychImageKeyboardResponseとします。
stimulusは, jsPsych.timelineVariableを使用し,test_stimuli で定義した'stimulus'を読み込んできます。
choicesは,すべてのキーの反応をとって来るようにしたいので,"ALL_KEYS"とします。
刺激の提示時間 trial_durationは,1500m秒とします。オレンジの円は,ボタン押しをしないので1500m秒経ったら消えるようにします。
提示後の処理 on_finishは,ここで参加者の反応を評価します。
jsPsych.pluginAPI.compareKeysを使って,参加者の押したキーボードの文字(data.response)とcorrect_responseを比較します。そうすることで,correctに正解であればTRUE,不正解であればFALSEが保存されます。
ただし,オレンジの時はcorrect_responseは'q'と定義したので,比較する前に,if...else...を使って,何かしらキーを押したらdata.responseはそのキーをそのまま,無反応の時はdata.responseを'q'とする様に条件分けをします。
code:test_stimuli2.html
var test = {
type: jsPsychImageKeyboardResponse,
stimulus: jsPsych.timelineVariable('stimulus'),
choices: "ALL_KEYS",
data: jsPsych.timelineVariable('data'),
trial_duration: 1500,
on_finish: function (data) {
if (data.response != null) {
} else {
data.response = 'q';
}
data.correct = jsPsych.pluginAPI.compareKeys(data.response, data.correct_response);
},
}
それでは,faxationと刺激(test)ができたので,timelineに入れていきます。
faxiationとtestを計測のたびに並べ替える必要はありません。
test_procedureを以下のように定義することで,青円→fixation→オレンジ円→fixationまたはオレンジ円→fixation→青円→fixationを疑似ランダムに5回繰り返してくれます。
そして,最後にtimeline.pushで,timelineにtest_procedureを忘れないように挿入します。
code:randam.html
// timelineを作成する
var test_procedure = {
timeline_variables: test_stimuli,
repetitions: 5,
randomize_order: true
}
timeline.push(test_procedure);
次は,参加者に結果を返します。
varを使用してdebrief_blockを定義します。文章で結果を返すので typeは,"html-keyboard-response"です。
stimulusは,正答率,青い円が出た時の正答反応時間の平均をfunctionを使って算出します。その後に,文章に値を入れて表示するようにしています。
code:debrief.html
/* define debrief block */
var debrief_block = {
type: jsPsychHtmlKeyboardResponse,
stimulus: function() {
var trials = jsPsych.data.get().filter({task: 'test'});
var correct_trials = trials.filter({correct: true});
var accuracy = Math.round(correct_trials.count() / trials.count() * 100);
var rt = Math.round(correct_trials.select('rt').mean());
return "<p>あなたの正答率は "+accuracy+"% でした。</p>"+
"<p>あなたの反応時間の平均値は <strong>" + rt + "ms</strong> でした。</p>" +
"<p>実験を終了するにはいずれかのキーを押して下さい。</p>" +
"<p>ご協力いただきましてありがとうございました。</p>";
}
};
timeline.push(debrief_block);
最後に,jsPsych.run(timeline);を使って計測を走らせます。
それでは,GoNoGoTask.htmlをresearchに保存後,ダブルクリックして実際に動かしてみましょう。
code: GoNoGoTask.html
<!DOCTYPE html>
<html>
<head>
<title>GoNoGoTask</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="dist/jspsych.js"></script>
<script src="dist/plugin-preload.js"></script>
<script src="dist/plugin-html-keyboard-response.js"></script>
<script src="dist/plugin-image-keyboard-response.js"></script>
<link href="dist/jspsych.css" rel="stylesheet" type="text/css" />
</head>
<body></body>
<script>
/* initialize jsPsych */
var jsPsych = initJsPsych({
on_finish: function () {
jsPsych.data.displayData('csv'); //終了後CSV形式でデータを画面上に表示
jsPsych.data.get().localSave('csv', 'data.csv');
}
});
/* create timeline */
var timeline = [];
var preload = {
type: jsPsychPreload,
auto_preload: true
};
timeline.push(preload);
/* define welcome message */
var welcome = {
type: jsPsychHtmlKeyboardResponse,
stimulus: "研究に参加いただきありがとうございます。<br>計測を開始するにはいずれかのキーを押して下さい。"
};
timeline.push(welcome);
/* define instructions trial */
var instructions = {
type: jsPsychHtmlKeyboardResponse,
stimulus: "<p>この実験では、画面の中央に色のついた円が呈示されます。<br>" +
"もし円の色が<strong>青</strong>だったら、できるだけ速く「Fキー」を押して下さい。<br>" +
"もし円の色が<strong>オレンジ</strong>だったら、どのキーも押さないで下さい。</p>" +
"<div style='width: 700px;'>" +
"<div style='float: left;'><img src='examples/img/blue.png'></img>" +
"<p class='small'><strong>Fキーを押して下さい。</strong></p></div>" +
"<div class='float: right;'><img src='examples/img/orange.png'></img>" +
"<p class='small'><strong>どのキーも押さないで下さい。</strong></p></div>" +
"</div>" +
"<p>実験を開始するにはいずれかのキーを押して下さい。</p>",
post_trial_gap: 2000
};
timeline.push(instructions);
/* define test trial */
// 計測開始前2秒間にfixationを出す。
var fixation1 = {
type: jsPsychHtmlKeyboardResponse,
stimulus: '<div style="font-size:60px;"> + </div>',
choices: "NO_KEYS",
trial_duration: 2000,
data: { test_part: 'fixation' }
}
// choicesは,参加者が使用できるキー
// "NO_KEYS"はどのキーも反応しない
timeline.push(fixation1);
var fix_duration = function () {
return Math.floor(Math.random() * 1500) + 1000;
}
// 1000から2250の値をランダムに返す
// Math.floor 引数として与えた数以下の最大の整数を返す
// Math.random()関数は、0–1(0以上、1未満)の範囲で擬似乱数を返す
var fixation = {
type: jsPsychHtmlKeyboardResponse,
stimulus: '<div style="font-size:60px;"> + </div>',
choices: "NO_KEYS",
trial_duration: fix_duration,
data: { task: 'fixation' }
}
// 全キーの場合は,"ALL_KEYS"
// 刺激の定義
var test_stimuli = [
{ stimulus: "examples/img/blue.png", correct_response: 'f' },
{ stimulus: "examples/img/orange.png", correct_response: 'q' }
];
// 本来はnullだがcompareKeysはnullの比較ができないみたいなので関係ないキーを割り当てる
var test = {
type: jsPsychImageKeyboardResponse,
stimulus: jsPsych.timelineVariable('stimulus'),
choices: "ALL_KEYS",
data: {
task: 'response',
correct_response: jsPsych.timelineVariable('correct_response')
},
trial_duration: 1500,
on_finish: function (data) {
if (data.response != null) {
} else {
data.response = 'q'; // ここで何もキー入力がなければresponseをqとする
}
data.correct = jsPsych.pluginAPI.compareKeys(data.response, data.correct_response);
},
}
// jsPsych.timelineVariable timelineにある変数を取得してくる
// data.response -> 参加者が押したボタン(数字)
// これがdata.correct_responseと一致している場合,data.correctは,trueを返し,そうでない時はfalseを返す。
// timelineを作成する
var test_procedure = {
timeline_variables: test_stimuli,
repetitions: 5,
randomize_order: true
}
timeline.push(test_procedure);
/* define debrief block */
var debrief_block = {
type: jsPsychHtmlKeyboardResponse,
stimulus: function () {
var trials = jsPsych.data.get().filter({ task: 'response' });
var correct_trials = trials.filter({ correct: true });
var accuracy = Math.round(correct_trials.count() / trials.count() * 100);
var rt = Math.round(correct_trials.select('rt').mean());
return "<p>あなたの正答率は " + accuracy + "% でした。</p>" +
"<p>あなたの反応時間の平均値は <strong>" + rt + "ms</strong> でした。</p>" +
"<p>実験を終了するにはいずれかのキーを押して下さい。</p>" +
"<p>ご協力いただきましてありがとうございました。</p>";
}
};
timeline.push(debrief_block);
/* start the experiment */
jsPsych.run(timeline);
</script>
</html>
https://gyazo.com/ad42b4e5fba2e91518e9cc11e5c8afe2
【目次】