.. _chapter-loop-tips: 繰り返し方法を工夫しよう―傾きの対比と同化 ======================================================================= この章の実験の概要 ------------------------------ この章では、傾きの対比の実験を題材として、実験を実施する度に条件ファイルを変更したり、繰り返しを多重に用いることによってやや複雑な計画の実験を実現したりする方法を学びます。 さっそく実験の内容を確認しましょう。 :numref:`fig-simul-contrast` に縞模様の刺激が描かれていますが、このように正弦波上に明るさが変化する縞模様の刺激をグレーティングと呼びます。今回の実験では、大小2個の円形のグレーティング刺激を実験に使用します。スクリーン中央に :numref:`fig-simul-contrast` のように二つの刺激の中心が一致するように重ねます。グレーティングの模様がスクリーンの垂直方向にぴったり一致しているのを0度の方向として、大きいグレーティング刺激の向きを反時計回り、または時計回りに20度傾けます。そして小さいグレーティング刺激の向きを0度±5度の範囲で変化させて、小さいグレーティング刺激が0度より反時計回り、時計回りのどちらに傾いているかを実験参加者に判断させます。「小さい方のグレーティング刺激」などといちいち書くのは面倒ですので、小さい方のグレーティング刺激を「テスト刺激」、大きい方のグレーティング刺激を「コンテクスト刺激」と呼ぶことにしましょう。コンテクスト刺激がテスト刺激の傾きの知覚にどのような影響を与えるかを調べるのが実験の目的です。 実験の作成を始める前に刺激の大きさや反応方法などの詳細を決める必要がありますが、それ以前にこの刺激は前章までに学んだ知識で作成できそうにありません。PsychoPy Builderにはこの刺激を描くためにぴったりなGratingコンポーネントというコンポーネントが用意されているので、まずはその使い方を学びましょう。 .. _fig-simul-contrast: .. figure:: fig04/simul-contrast.png :width: 80% この章の実験。実験参加者は、テスト刺激の縞が垂直方向より反時計回りに傾いているか、時計回りに傾いているかを判断します。テスト刺激の周囲のコンテクスト刺激が判断に与える影響を明らかにするのが目的です。 .. _section-grating-component: Gratingコンポーネント --------------------------------- Gratingコンポーネントは、簡単に言うと縞模様の視覚刺激を描くためのコンポーネントです。視知覚の研究においては基本刺激とでも言うべき重要な刺激ですし、視覚認知の研究でもまた頻繁に用いられます。 **[開始]** や **[終了]** で刺激の有効な時間を指定したり、 **[位置 [x, y] $]** で位置を指定したりするなど、大半のプロパティはPolygonコンポーネントやTextコンポーネントと共通です。GratingコンポーネントがPolygonおよびTextコンポーネントと大きく異なるのは「テクスチャ」タブです。本節ではこのタブの項目について解説します( **[テクスチャ]** は複雑なので「 :numref:`{number}:{name} ` 」で詳しく取り上げます)。 :numref:`fig-grating-samples` は **[マスク]** 、 **[位相 (周期に対する比) $]** 、 **[空間周波数 $]** を変化させるとどのような刺激が提示されるかを示したものです。 **[マスク]** は刺激を切り抜くプロパティで、None、circle、gaussを指定することができます。空白にしておくと長方形の刺激が描画され、circleにすると楕円状に切り抜かれた刺激が描画されます。gaussは2次元Gauss関数の値に従ってコントラストが変調された縞模様を描きます。Gauss関数と言われてもピンと来ない方は正規分布の密度関数を思い出してください。正規分布の密度関数はGauss関数の一種であり、「Gauss関数の値に従ってコントラストが変調される」とはあの正規分布の密度関数のように中心から周辺に向かってなだらかに縞模様の薄れていくということです。正弦波を正規分布で変調した刺激はGaborパッチと呼ばれ、視知覚の実験では非常によく用いられます。 続いて **[空間周波数 $]** ですが、この値を大きくすると縞模様の密度が高くなります。より厳密な表現を用いれば、空間周波数(spatial frequency)とは一定の空間範囲に描かれる模様の繰り返し回数のことです。 **[空間の単位]** がcmとdegの場合には、刺激の幅1.0に含まれる縞模様の数に対応します。例えば刺激の幅が5.0cmで **[空間周波数 $]** が0.4ならば5.0×0.4=2.0、つまり刺激には2周期分の縞模様が描かれます( :numref:`fig-grating-samples` 中段左)。 **[空間の単位]** がnormとheightの場合は、刺激に描かれる縞模様の数に直接対応します。つまり、 **[空間周波数 $]** が2.0なら刺激の幅の値がいくらであろうと常に2周期分の縞が描かれます。 **[空間の単位]** がpixの場合は複雑で、「 **[空間周波数 $]** の値×刺激の幅」の周期分の縞が描かれます。刺激の幅が200pixであれば、 **[空間周波数 $]** に0.01を指定すれば200×0.01で2周期分の縞になります。 **[位相 (周期に対する比) $]** は、縞模様の位相を指定するパラメータです。Gratingコンポーネントは初期設定では刺激の中心で明るさが最大になるように縞模様が描かれますが、位相を指定すると明るさが最大になる位置をずらすことができます。単位は1周期に対する比であり、0.5ずらすとちょうど明暗が反転します( :numref:`fig-grating-samples` 下段の左端と右端)。 .. _fig-grating-samples: .. figure:: fig04/grating-samples.png :width: 70% Gratingコンポーネントのプロパティのうち、 **[マスク]** (上段)、 **[空間周波数 $]** (中段)、 **[位相 (周期に対する比) $]** を変化させた例。いずれも **[サイズ [w, h] $]** は[5.0,5.0]で、 **[単位]** はcmを指定しています。 **[テクスチャの解像度 $]** と **[補間]** の効果を示したのが :numref:`fig-resolution-interpolate` です。グレーティングコンポーネントを大きく拡大した図が描かれていますが、まず左側の二つをご覧ください。上が **[テクスチャの解像度 $]** が初期値の128、下が512の時の結果です。512の時の結果と比べると、128の時には刺激の境界も縞模様もぼけています。この「ぼけ」は、PsychoPyがグレーティングを描くときには内部で縞模様の画像データ(テクスチャ)を作成し、それを **[サイズ [w, h] $]** で指定された大きさに拡大するために生じます。 **[テクスチャの解像度 $]** はテクスチャの解像度を指定するプロパティで、128であれば128×128ピクセル、512であれば512×512ピクセルのテクスチャを生成します。値が高い方が大きく拡大した時のぼけが小さく済みますが、その代わりにPCのグラフィック描画機能に負担をかけます。小さなグレーティング刺激を多数描画する場合は、描画の負担を小さくするために **[テクスチャの解像度 $]** を低く設定するべきです。逆に今回の実験のように大きなグレーティング刺激をせいぜい2、3個描画する場合は高い値を設定するとよいでしょう。描画負担は使用しているPCのグラフィック性能に大きく依存しますので、グラフィック性能が高いPCであれば512に設定して多数のグレーティングを描いても問題は生じません。 **[補間]** は、グレーティングを拡大した時の補間方法を指定します。補間方法と言われてもピンと来ないかもしれませんが、 :numref:`fig-resolution-interpolate` 右の二つの拡大図を比べてください。上は刺激の境界がぼやけていますが、下は境界がくっきりしていてカクカクしています。上が **[補間]** に「一次」を指定した例で、拡大した時に足りない色情報を周囲のピクセルと滑らかにつながるように補います。結果として、このように大きく拡大した場合はぼけが目立ってしまいます。一方「最近傍」を指定した場合は、元のテクスチャで最も近いピクセルの色を使用しますので、ぼけが生じない代わりにカクカクになってしまいます。滑らかにする方法は使用するPCのグラフィック機能によって異なりますので、自分が使用するPCでどちらの方がよい出力が得られるか各自で確認してください。 .. _fig-resolution-interpolate: .. figure:: fig04/resolution-interpolate.png :width: 80% Gratingコンポーネントの **[テクスチャの解像度 $]** および **[補間]** オプションの効果。 なお、「外観」タブの **[前景色]** はすでにTextコンポーネントで解説しましたが、Gratingコンポーネントの場合は縞模様を描画しますので少々複雑です。Gratingコンポーネントの色は、 **[前景色]** の値を **[テクスチャ]** で指定された波形に掛け算することによって決まります。つまり、 **[テクスチャ]** がsinで **[前景色]** の値が1, 1, 1であれば-1から+1まで変化します。 **[前景色]** が0.5, 0.5, 0.5であれば-0.5から+0.5まで変化します。-0.5, -0.5, -0.5であれば、0.5, 0.5, 0.5のときと同様に-0.5から+0.5まで変化しますが、負の値を掛け算していますので明暗が0.5, 0.5, 0.5の時と反転します。 1, 0, 0のようにRGB各成分の値が異なる場合も、それぞれの成分に波形が掛け算されます。1, 0, 0の場合は-1, 0, 0から1, 0, 0まで変化するということです。 **[前景色]** にredやgreenといった色名が指定され場合は、PsychoPyの内部でこれらの色名をRGBに変換したうえで波形との掛け算が行われます。 ずいぶん脱線が長くなってしまいました。以上の解説を踏まえて、実験手続きの詳細を決定して実験を作成しましょう。 チェックリスト - Gratingコンポーネントを用いて長方形、または楕円形に縞模様が描かれた刺激を提示することができる。 - Gratingコンポーネントで **[空間の単位]** がcm、deg、pix、norm、heightのいずれの場合においても、「幅xに対してy周期分の縞模様」を描けるように **[空間周波数 $]** の値を決定できる(x、yは正の数値)。 - Gratingコンポーネントで描かれる縞模様を初期設定の状態からずらして描画することができる。 - Gratingコンポーネントで描画処理の負担を軽くするためにテクスチャの解像度を下げることができる。 - Gratingコンポーネントを大きく表示するときに画質を高めるためにテクスチャの解像度を上げることができる。 - Gratingコンポーネントの色を指定したときに、何色の縞模様が描かれるのかをこたえることができる。 パラメータを決定しよう ----------------------------------- それでは実験に用いる刺激のパラメータを決定しましょう。まず、テスト刺激とコンテクスト刺激の大きさと縞模様を :numref:`fig-simul-param` のように設定することにします。 コンテクスト刺激の **[サイズ [w, h] $]** と **[空間周波数 $]** はそれぞれテスト刺激の3倍の値が設定されていて、同じ幅の縞が描かれるようにしてあります。いずれの刺激も **[マスク]** にcircleを指定して円形にします。 **[前景色]** はいずれも0.5, 0.5, 0.5にしておきましょう。 .. _fig-simul-param: .. figure:: fig04/simul-param.png :width: 80% 刺激のパラメータ 今回の実験で一番重要なパラメータはテスト刺激とコンテクスト刺激の方向です。すでに概要で述べたとおり、コンテクスト刺激には反時計回りに20度( **[回転角度 $]** =20)傾いたものと、時計回りに20度 **[回転角度 $]** =-20傾いたものの2種類を用います。それぞれのコンテクスト刺激に対して、-5度から1度間隔で5度までの計11種類のテスト刺激を組み合わせることにしましょう。それぞれの組み合わせに対して5回ずつ、無作為な順序に実験参加者に提示して、判断させることにします。試行数はコンテクスト刺激2種類×テスト刺激11種類×繰り返し5回=110試行です。 続いて :numref:`fig-simul-contrast-procedure` に実験の手続きを示します。まず実験が始まったら、反応方法の教示をスクリーンに提示します。反応方法は、テスト刺激が時計回りに傾いているように見えたらカーソルキーの右、反時計回りに傾いているように見えたらカーソルキーの左を押すものとします。教示画面は各自で自由に作成していただいてよいと思いますが、刺激の例を示しながら反応するキーを示すとよいと思います。 教示画面でカーソルキーの左右いずれかを押すと実験が始まります。各試行はまず0.5秒間の空白のスクリーンから始まり、続けて刺激を提示します。実験参加者がカーソルキーの左右いずれかを押して反応するまで刺激を提示し続け、キーが押されたら直ちに次の試行に進みます。全試行終了すれば実験は終了です。 :numref:`fig-simul-contrast-procedure` では示していませんが、最後に「実験は終了しました」などのメッセージを表示するのも良いでしょう。 .. _fig-simul-contrast-procedure: .. figure:: fig04/simul-contrast-procedure.png :width: 80% 実験の手続き 以上が手続きです。いよいよ実験を作成しますが、:numref:`第%s章 ` の実験と比べて使用するコンポーネントが多いので便利なテクニックを紹介しながら作業を進めましょう。 コンポーネントのコピーを活用して実験を作成しよう -------------------------------------------------------------------------- では、Builderを開いて作業を開始します。実験はexp04.psyexpというファイル名で保存するものとします。 - 準備作業 - この章の実験のためのフォルダを作成して、その中にexp04.psyexpという名前で新しい実験を保存する。 - 実験設定ダイアログの「入力」タブの **[キーボードバックエンド]** がPsychToolboxか確認する。 準備ができたら、まずtrialルーチンで以下の作業をしてください。 - trialルーチン - Gratingコンポーネントを1つ配置し、以下の設定をする。 - 「基本」タブの **[名前]** を ``testStim`` とし、 **[開始]** を「時刻 (秒)」で ``0.5`` に、 **[終了]** を空白にする - 「レイアウト」タブの **[サイズ [w, h] $]** を ``(0.2,0.2)`` にする。 **[回転角度 $]** に ``testDir`` と入力して、「繰り返し毎に更新」にする。 - 「外観」タブの **[前景色]** を ``0.5, 0.5, 0.5`` にする。 - 「テクスチャ」タブの **[テクスチャ]** が ``sin`` であることを確認する。 **[マスク]** を ``circle`` に、 **[空間周波数 $]** を ``5.0`` にする。 **[テクスチャの解像度 $]** を 512にする。 これでテスト刺激が完成しました。続いてコンテキスト刺激を作成しなければいけませんが、 **[名前]** と **[サイズ [w, h] $]** 、 **[回転角度 $]** 以外はテスト刺激と設定が同じです。このような場合には、コンポーネントのコピー機能を使用すると作業が楽になります。 .. _fig-copy-component: .. figure:: fig04/copy-component.png :width: 80% コンポーネントのコピー :numref:`fig-copy-component` にコピーの手順を示します。まず図の左上のように、コピーしたいコンポーネントにマウスカーソルを重ねて右クリックし、メニューから「コピー」を選択します。その後、コピーを作成したいルーチンを表示します。今はtrialルーチンに作成したいので、そのままで結構です。ルーチンの余白部分でマウスを右クリックすると、「貼り付け(testStim)」というメニューが表示されます(図の中段左)。選択すると図の右上のようにコンポーネントの名前をたずねるダイアログが表示されます。Builderでは同一の名前を持つコンポーネントを複数作ることができませんので、新しい名前をつけなければいけません。名前を空欄のままOKをクリックするとtestStim_2のように元のコンポーネントの名前の後ろに数字を添えた名前となりますが、ここはcontextStimという名前にしておきましょう。新しい名前を入力してOKをクリックすれば、図の中段右のようにコンポーネントのコピーが完了します。 なお、「貼り付け」のメニューを表示するために右クリックをする際に、配置済みの他のコンポーネント上で右クリックすると、図の下段のように削除や順番変更といった項目の中に「上に貼り付け()」「下に貼り付け()」という項目が表示されます(括弧内はコピー中のコンポーネント名)。これを利用すると、たくさんのコンポーネントを配置しているルーチンで、他のコンポーネントの順番を考慮して狙った位置に貼り付けることができます。複雑な実験を作るときにはとても便利なテクニックなので覚えておきましょう。 コンポーネントのコピーが終了したら、contextStimのパラメータを変更します。 - trialルーチン - contextStimの設定を以下のように変更する。 - 「レイアウト」タブの **[サイズ [w, h] $]** を ``(0.6, 0.6)`` に、 **[回転角度 $]** をcontextDirにする。 - 「テクスチャ」タブの **[空間周波数 $]** を ``15.0`` にする。 - contextStimの上にtestStimが表示されるようにコンポーネントの順番を並び替える。 これで刺激は完成です。あとは参加者の反応を計測するためにKeyboardコンポーネントを置いておきましょう。 - trialルーチン - Keyboardコンポーネントをひとつ配置し、以下のように設定する。 - 「基本」タブの **[開始]** を「時刻 (秒)」で ``0.5`` に、 **[終了]** を空白にする。 - 「データ」タブの **[検出するキー $]** を ``'left', 'right'`` にする。 **[正答を記録]** をチェックして、 **[正答]** に ``$correctAns`` と入力する。 続いて実験の開始時に表示する教示画面を作成します。 :numref:`fig-simul-contrast` のように時計回り、反時計回りの刺激の実例を表示するとわかりやすいでしょう。 :numref:`fig-simul-contrast` のような画面を作成するとなると、Gratingコンポーネントを4個配置しなければいけません。たった今覚えたコンポーネントのコピーを使えば楽ができますが、前章で紹介したルーチンのコピーを使うとさらに手順を省略できます。 :numref:`fig-copy-routine` のようにtrialルーチンをinstructionという名前でコピーしましょう。 .. _fig-copy-routine: .. figure:: fig04/copy-routine.png :width: 80% ルーチンのコピー。 コピー元のtrialsルーチンと同じコンポーネントが含まれていること、コンポーネントの名前には(名前が重複しないように)番号が自動的につけられていることを確認してください。これでかなり作業を楽することができました。後はinstructionルーチンで以下のように作業してください。 - instructionルーチン - **フローの先頭に挿入する** (忘れがちなので注意!)。 - contextStim_2の **[名前]** を ``left_large`` とし、 **[開始]** を「時刻 (秒)」で ``0.0`` にする。 **[位置 [x, y] $]** を ``(-0.4, 0)`` とする。 **[回転角度 $]** を ``20.0`` にし、「更新しない」にする。 - testStim_2の **[名前]** を ``left_small`` とし、 **[開始]** を「時刻 (秒)」で ``0.0`` にする。 **[位置 [x, y] $]** を ``(-0.4, 0)`` とする。 **[回転角度 $]** を ``-10.0`` にし、「更新しない」にする。 - left_largeをコピーして **[名前]** を ``right_large`` にする。 **[位置 [x, y] $]** を ``(0.4, 0)`` とする。 - left_smallをコピーして **[名前]** を ``right_small`` にする。 **[位置 [x, y] $]** を ``(0.4, 0)`` 、 **[回転角度 $]** を ``10.0`` にする。 - left_largeの上にleft_small、right_largeの上にright_smallが描かれるように各コンポーネントが並んでいることを確認する。 これで刺激は完成です。次に進む前に、便利な機能をひとつ覚えておきましょう。 Builderのメニューの「実験」から「実験内を検索...」を選択してください。「実験内を検索...」というタイトルのダイアログが開くので、ダイアログの一番上の虫眼鏡のアイコンと「検索」と書かれている入力欄に(0.6, 0.6)と入力してEnterキーを押してみてください。ここまで解説通りに作業してきたなら、 :numref:`fig-search-experiment` 右上のように入力欄の下に何か表示されたはずです。 この欄をよく見ると、上にComponent, パラメータ, 値と見出しがあります。これは(0.6, 0.6)という値が設定されているパラメータを実験内から探し出して、該当するコンポーネントの名前とパラメータ名をリストアップしてくれているのです。 (0.6, 0.6)はグレーティング刺激の大きい方の **[サイズ [w, h] $]** に指定した値なので、instructionルーチンのleft_largeとright_large、trialルーチンのcontextStimが検出されています。 :numref:`fig-search-experiment` 右下は0.4を検索した例です。0.4はinstructionルーチンに配置した刺激のX座標で、左側に配置したものは-0.4、右側に配置したものは0.4なのでした。 検索の結果、正しくleft_large, left_small, right_large, right_smallの4つが検出されていて、leftとついているものは-0.4、rightとついているものは0.4になっていることが確認できます。 修正する必要がある場合は、検索結果の項目をクリックするとそのパラメータの設定ダイアログを直接開くことができます。 .. _fig-search-experiment: .. figure:: fig04/search-experiment.png :width: 80% 各コンポーネントのパラメータに設定した値を検索し、その値を持つコンポーネントとパラメータの一覧を得ることができます。検出された項目をクリックすると、該当するパラメータの設定ダイアログを直接開くこともできます(検索結果のダイアログは閉じてしまいます)。 複雑な視覚刺激を描画する場合や、多数の刺激を描画する場合は、複数の視覚刺激コンポーネントを適切に組み合わせることが不可欠です。しかし、パラメータの編集はひとつひとつ順番にしかできないので、うっかりひとつ設定し忘れたり間違った値を入力したりといったミスがよく起きます。 入力内容に間違いがないかを確認したいとき、もしくは間違えていると思うのだけどもどれを間違えたのかわからないときなどに、この「実験内を検索...」をうまく使うと作業が楽になります。 ひとつ残念なのは、入力内容が正確に一致しないと検出されないことです。例えば :numref:`fig-search-experiment` 右上の例は[0.6, 0.6]と書いてもサイズの指定として有効ですが、(0.6, 0.6)と検索すると見つかりません。まあこれは仕方がないと思うのですが、(0.6,0.6)でも見つかりません(カンマと次の0の間にスペースが無い)。他にも0と0.0なども値としては同じですが検索では区別されます。普段から「括弧は( )を使う」、「カンマの後ろには半角スペースをひとつ入れる」、「0は0.0と書く」など自分なりのルールを決めておくのが理想ですが、0.6とだけ入力すれば[0.6, 0.6]も(0.6, 0.6)も(0.6,0.6)もすべて検出されますから、工夫次第で対応できるでしょう。 検索機能についての解説はこのくらいにして、実験の作成に戻りましょう。次はinstructionにテキストを追加します。 - instructionルーチン - Textコンポーネントを1個配置して、 **[名前]** を ``TextInst`` とする。 - **[文字列]** に ``カーソルキーの左右いずれかを押すと始まります`` 等のメッセージを入力する。画面の大きさに応じて文を短くするなり改行するなりするとよい。 - **[文字の高さ $]** を0.04にする(各自のPC画面の大きさに応じて調節すると良い)。 - **[位置 [x, y] $]** に ``(0, -0.4)`` と入力する。 - TextInstをコピーしてtextCCの名前で貼り付ける。 - TextCCの **[文字列]** に ``反時計回り`` と入力し、 **[位置 [x, y] $]** を ``(-0.4, -0.35)`` にする。 - TextInstをコピーしてtextCの名前で貼り付ける。 - TextCの **[文字列]** に ``時計回り`` と入力し、 **[位置 [x, y] $]** を ``(0.4, -0.35)`` にする。 最後にKeyboardコンポーネントの調整をします。 - instructionルーチン - 配置済みのKeyboardコンポーネント(標準ではkey_resp_2という名前になっているはず)の **[開始]** を「時刻 (秒)」で ``0.0`` にする。 **[記録]** を「なし」にして、 **[正答を記録]** のチェックを外す。 あとは条件ファイルを作成して、ループを挿入しましょう。 - exp04_20.xlsx (条件ファイル) - testDir、contextDir、correctAnsの3パラメータを設定する。 - 実験手続きの説明を満たすようにtestDirに11種類の、contextDirに2種類の値を入力する。 - すべての行のcorrectAnsにrightを入力する。 - psyexpファイルと同じフォルダに保存する。 - trialsループ(作成する) - trialルーチンのみを繰り返すように挿入する。 - **[繰り返し回数 $]** が5(初期値)になっていることを確認する。 - **[条件]** の欄の右側のファイル選択ボタンをクリックしてexp04_20.xlsxを選択する。 さて、これで各ブロックの手続きは完成です。条件ファイルの3つめの項目「すべての行のcorrectAnsにrightを入力する」に注目してください。テスト刺激が時計回りに傾いている時にカーソルキーの右を押すのですから、testDirが正の値の時のみrightが正答のはずです。しかし、この実験では敢えてすべての試行の正答をrightとした方が楽にデータ処理できるのです。次節でこの点について補足します。 反応の記録方法を工夫しよう ---------------------------------------------- この章の実験の手続きは、心理物理学的測定法のひとつである恒常法の手続きです。恒常法を用いた実験のデータ分析でよく用いられる方法が心理物理曲線の作成です。 :numref:`fig-psychometric-function` に今回の実験で得られる心理物理曲線の例を示します。横軸はテスト刺激の傾き、縦軸に時計回りに傾いているという反応の頻度です。物理的な刺激と反応が一致していれば、横軸の0度を境界として左側では縦軸の値は0.0、右側では1.0となりますが、グラフは0.0から1.0へなだらかに上昇する曲線を描いています。グラフが0.0から1.0へ変化する範囲が左右に寄っていれば、実験参加者の判断が全体的に偏っていたことがわかります。また、この範囲が左右に広がっていれば、反時計回りか時計回りかの判断が難しい課題であったことがわかります。 .. _fig-psychometric-function: .. figure:: fig04/psychometric-function.png :width: 80% 心理物理曲線の例。横軸にテスト刺激の傾き、縦軸に時計回りに傾いているという反応の頻度をプロットしています。折れ線グラフはそれぞれコンテクスト刺激の傾きが-20度と20度の条件に対応しています。 心理物理曲線を描く時には、実験参加者の反応が刺激の物理的特性と一致していたか否かは関係がありません。ですから、PsychoPyで実験参加者の反応を記録する時に、両者が一致していたかを記録しても何の役にも立ちません。前節のように、「right(=時計回りに傾いている)」という反応をしていたかを記録するようにしておけば、正答率の値がそのまま心理物理曲線の縦軸の値として利用できます。分析の手間が大幅に省けます。便利なテクニックですので、ぜひ覚えておいてください。 チェックリスト - 恒常法の実験において、正答率がそのまま心理物理曲線の縦軸の値として使用できるように正答を定義できる。 .. _section-select-cndfile-expinfodialog: 実験情報ダイアログで条件ファイルを指定しよう --------------------------------------------------------- この節からがいよいよこの章の本題です。exp04.psyexpではコンテクスト刺激の傾きとして-20度と20度を用いましたが、これらの条件に加えて-70度と70度傾いた条件のデータも取りたいとします。さらに、-20度/20度のコンテクストを使う試行と、-70度/70度のコンテクスト刺激を使う試行はそれぞれまとめて実施することにします。つまり、実験を二つのブロックに分割し、一方のブロックではコンテクスト刺激はすべて-20度/20度、もう一方のブロックではすべて-70度/70度とします。 コンテクスト刺激の種類で実験をブロック化せず、全部無作為な順番で実施するのであれば、条件ファイルに-70度/70度の条件に対応する行を追加するだけで対応できます。しかし、ブロック化するのであればこの方法は使えません。一番簡単な方法は、-70度/70度条件に対応する新たな条件ファイル(exp04_70.xlsx)を作り、このexp04_70.xlsxを条件ファイルとして使用する実験をもう一個作成するというものでしょう。まあ別にこの方法で乗り切っても構わないのですが、せっかくですのでひとつの実験ファイルで二つの条件ファイルを切り替える方法を習得しましょう。 .. _fig-add-expinfo-fields: .. figure:: fig04/add-expinfo-fields.png :width: 80% 実験情報ダイアログに項目を追加する手順。 使用する条件ファイル名をはじめ、実験のパラメータを実験開始時に指定するには実験情報ダイアログを使用します。初期状態では実験情報ダイアログにはsessionとparticipantの2項目しかありませんが、実験設定ダイアログから項目を追加することができます。 :numref:`fig-add-expinfo-fields` は実験情報ダイアログに項目を追加する手順を示しています。exp04.psyexpを開いて設定ダイアログを開いてみましょう。 **[実験情報ダイアログを表示]** のチェックを外している人はチェックしなおしておいてください。初期状態では実験設定ダイアログの **[実験情報ダイアログ]** にsessionとparticipantという項目が表示されていて、その右側に[+]、[-]が書かれたボタンが表示されています。[+]ボタンを押すと、その行の下に新しい行が追加されます。[-]を押すと、その行が削除されます。participantとsessionのどちらの行の下に追加しても、動作には違いがありません。 では、[+]を押して新しい行を追加して、「フィールド」にcndFileName、「初期値」にexp04\_.xlxsと入力してください。入力したら実験を実行してみましょう。すると、 :numref:`fig-add-expinfo-fields` の一番下の図のように実験情報ダイアログにcndFileNameという項目が追加されていて、exp04\_.xlsxという文字列が最初から入力されているはずです。「フィールド」は実験情報ダイアログに表示する項目の名前、「初期値」はその項目に最初から入力されている文字列(初期値)に対応しています。participantのように「初期値」が空白の場合は、実験情報ダイアログでも空白になります。 実験情報ダイアログの項目名は、条件ファイルのパラメータ名と異なり、空白文字(スペース)や#、$といった記号を含むことができます。日本語の文字列でも指定できますが、表示が乱れることがありますのであまりお勧めしません。項目が実験情報ダイアログに表示される順番は、従来のバージョンではPsychoPyが自動的に決定されてしまって自由に指定できなかったのですが、現在のバージョンでは実験設定ダイアログで入力した通りの順番となります(2020.2.6で確認)。 実験情報ダイアログで入力した値は、条件ファイルに記述されたパラメータのようにBuilder内部で参照することができます。ただし、条件ファイルの場合とは書き方が異なっていて、expInfo['パラメータ名']という具合に書きます。この書き方の意味を理解するためにはPythonの文法を理解する必要がありますので、ここではとりあえず「こう書くんだ」と覚えておいてください。 それでは、実験情報ダイアログを利用して「ひとつの実験ファイルで二つの条件ファイルを切り替える」という問題に挑戦してみましょう。先ほどのcndFileNameという項目をexp04.psyexpに追加した状態から作業を続けます。trialsループのプロパティを開いて、 **[条件]** を$expInfo['cndFileName']に変更してください。 **[条件]** には$が付いていないので、条件ファイルからパラメータを読む時と同様に$を付けないといけない点に注意してください( :numref:`fig-set-conditionfile-from-expinfo` )。これで、実験実行時に実験情報ダイアログのcndFileNameの項目に入力された名前の条件ファイルを開くようになりました。 .. _fig-set-conditionfile-from-expinfo: .. figure:: fig04/set-conditionfile-from-expinfo.png :width: 60% **[条件]** に実験情報ダイアログの項目を指定します。先頭の$と項目名の前後の'を忘れないように注意してください。 続いて、Builderをいったん離れてコンテクスト刺激が-70度/70度傾いている条件の条件ファイルを作成しましょう。これは単にexp04_20.xlsxを開いて、contextDirの列の-20を-70に、20を70に書き換えて別名で保存するだけです。ここではexp04_70.xlsxという名前で保存しておきましょう。元のexp04_20.xlsxも引き続き使用しますので、上書き保存しないように注意してください。exp04_20.xlsx、exp04_70.xlsxの両方ともexp04.psyexpと同じフォルダに置いてください。 以上で実行時に条件ファイルを切り替えられる実験の作成は終了です。PsychoPyに戻ってexp04.psyexpを実行しましょう。実験情報ダイアログのcndFileNameにexp04_20.xlsxと入力すれば、-20度/20度、exp04_70.xlsxと入力すれば-70度/70度の条件の試行が始まります。最初からexp04\_.xlsxという文字列が入力されているので、_の後に20または70と入力するだけでよいはずです。ぜひ、-70度/70度の実験を最後まで行って、-20度/20度の実験結果と比較してみてください。 チェックリスト - 実験情報ダイアログの項目を追加、削除することができる。 - 実験情報ダイアログの項目の初期値を設定することができる。 - 実験情報ダイアログの項目名から、その値を利用するためのBuilder内における表記に変換することができる。 - 条件ファイル名を実験情報ダイアログの項目から取り出してループのプロパティに設定することができる。 .. _section-select-list-expinfodialog: 実験情報ダイアログで項目を選択できるようにしよう ------------------------------------------------------- 前節で実験情報ダイアログを使って条件ファイルを切り替えできるようになりましたが、実際に使用してみた感想はいかがでしょうか? 確かに便利なのですが、条件ファイル名を入力するのが面倒ではないでしょうか。今回の例のように入力する内容がいくつかの決まったパターンしかない場合、いちいちキーボードから入力するのではなくドロップダウンメニューから選択できるようにする機能がBuilderには用意されています。 .. _fig-expinfo-dropdown-menu: .. figure:: fig04/expinfo-dropdown-menu.png :width: 80% 実験情報ダイアログの項目を選択式にする さっそく使ってみましょう。exp04.psyexpを開いて設定ダイアログを開き、先ほど編集した **[実験情報ダイアログ]** のcndFileNameの項目を以下のように変更します。[ ]や' '、,などの記号に注意して入力してください。 .. code-block:: python ['exp04_20.xlsx', 'exp04_70.xlsx'] 入力できたら実験を実行してみましょう。 :numref:`fig-expinfo-dropdown-menu` に示すように、cndFileNameの項目がexp04_20.xlsxとexp04_70.xlsxのどちらかを選べるようになりました。この形式をドロップダウンメニューと呼びます。これで条件ファイルの切り替えが少し楽になったのではないでしょうか。 さて、動作を確認したところで、この機能を使うためのポイントを確認しておきましょう。 **[実験情報ダイアログ]** の項目の「初期値」が以下の条件を満たすとき、その項目はドロップダウンメニューと解釈されます(上級者向け:厳密には「初期値」に記入されている内容がPythonのlistオブジェクトとして解釈可能であることが条件)。 1. メニューの各項目が **' '** または **" "** で囲まれていて、かつ **,** で区切られている。 2. 1.の内容が **[** と **]** で囲まれている。 **[** の前や **]** の後ろには文字があってはいけない。 1番目の条件は、Keyboardコンポーネントの **[検出するキー $]** で'left', 'right'という具合にキー名を **' '** または **" "** で囲ってカンマ区切りで並べたとの同じように書くということです。いろいろな値を追加して練習してみてください。 チェックリスト - 実験情報ダイアログの項目をドロップダウンメニューにできる。 多重繰り返しを活用しよう ----------------------------------------- 今回の実験のようにある要因(ここではコンテクスト刺激の傾き角度)で試行がブロック化されている場合、一人の実験参加者が両方のブロックへ参加する実験計画を用いることもあれば(参加者内計画)、一人の実験参加者はいずれか一方のブロックしか参加しない計画を用いることもあります(参加者間計画)。前節の実験情報ダイアログで実験条件を指定する方法は、参加者内計画でも参加者間計画でも柔軟に対応できますが、実験者がいちいち条件ファイル名を設定しないといけないので面倒です。面倒なだけならいいのですが、条件ファイル名を間違えてしまうかもしれません。参加者内計画の場合、一方の条件を実行したら次のブロックは必ず残りの一方の条件です。Builderに自動的に二つの条件ファイルを読み込んで実行させるように改造してみましょう。 .. _fig-set-conditionfile-for-nested-loop: .. figure:: fig04/set-conditionfile-for-nested-loop.png :width: 80% 読み込む条件ファイルを変更しながら繰り返すことによって、-20度/20度と-70度/70度の条件を連続して実行する実験を作成します。 :numref:`fig-set-conditionfile-for-nested-loop` に改造の方針を示します。前節までの状態では、 :numref:`fig-set-conditionfile-for-nested-loop` の上の図のように、条件ファイル名を変更しながら2回exp04.psyexpを実行しなければいけません。これは、今まで作ってきた実験における「刺激の色や傾きを変更しながら刺激提示を繰り返す」という作業とよく似ています。ということは、刺激の色や傾きを条件ファイルから読み込んで代入しながら繰り返すことができたように、 :numref:`fig-set-conditionfile-for-nested-loop` 下のようにすれば条件ファイルの名前を別の条件ファイルから読み込んで代入しながら繰り返すことができるはずです。 .. _fig-add-outer-loop: .. figure:: fig04/add-outer-loop.png :width: 80% 多重に繰り返しを挿入します。 さっそく改造してみましょう。Builderでexp04.psyexpを開いて、 :numref:`fig-add-outer-loop` のようにblocksというループを挿入してください。Blocksループの始点はinstructionルーチンの後ろでも構わないのですが、後ろに挿入してしまうと1回目のtrialsループが終わった直後に2回目のtrialsループが始まってしまうため、実験参加者に対する予告なしにいきなり-20度/20度条件と-70度/70度条件が切り替わってしまいます。instructionルーチンをbolorksループに含むようにしておけば、条件が切り替わる前に教示画面が再び表示されますので、切り替わりがわかりやすいでしょう。さらにわかりやすくするためには、trialルーチンの前にこれから始まる条件を表示するためのルーチンを挿入すると良いでしょうが、これは練習問題とします。bocksループのプロパティでは、 **[繰り返し回数 $]** を1に設定して、 **[条件]** にexp04blocks.xlsxと入力しておきます。 **[Loopの種類]** はrandomのままでよいでしょう。これで新しいループの追加は完了です。 続いて、blocksループのための条件ファイル、exp04blocks.xlsxを用意しましょう。この条件ファイルでは :numref:`fig-set-conditionfile-automatically` 左上のようにexp04_20.xlsxとexp04_70.xlsxの二つの値を持つcndFileNameというパラメータを設定します。続いてtrialsループでcndFileNameに基づいて条件ファイルを読み込むように設定するために、trialsループの **[条件]** を$cndFileNameに変更します( :numref:`fig-set-conditionfile-automatically` 右上)。実験情報ダイアログで条件ファイル名を入力する必要はなくなったので、実験設定ダイアログを開いて実験情報ダイアログからcndFileNameの行を削除しておきましょう( :numref:`fig-set-conditionfile-automatically` 下)。 .. _fig-set-conditionfile-automatically: .. figure:: fig04/set-conditionfile-automatically.png :width: 80% blocksループを使って条件ファイルを切り替えるようにtrialsループなどを修正します。 以上で改造は終了です。exp04.psyexpを実行して、自動的に-20度/20度条件と-70度/70度条件が続けて実行されることを確認してください。blocksループの **[Loopの種類]** をrandomのままにしたので、-20度/20度条件と-70度/70度条件のどちらが先に実行されるかは毎回無作為に決定される点に注意してください。 なお、ループのプロパティには **[試行を繰り返す]** という項目がありますが、この項目は多重繰り返しを使ったときのデータ保存形式に関係があります。詳しくは「 :numref:`{number}:{name} ` 」を参照してください。 チェックリスト - 多重繰り返しを挿入できる。 - 多重繰り返しの内側のループで条件ファイル名を外側のループの条件ファイルから読み込んで設定することができる。 .. _section-disable-routines-components: 動作確認のために一部の動作をスキップしよう --------------------------------------------------------------------- 前節で、-20度/20度条件と-70度/70度条件を一回の実験実行でおこないました。「exp04.psyexpを実行して、自動的に-20度/20度条件と-70度/70度条件が続けて実行されることを確認してください」と書きましたが、皆さん実際にしていただけましたか? 少なくとも一方の条件が終了してもう一方の条件が始まるところまでは実行しないと動作確認になりませんが、そこまでたどり着くには110試行を行わないといけません。1試行ごとに0.5秒待って1回キーを押さないといけないのでかなり面倒です。 こういう時に便利なのがRoutineペインの左上にある「Routineの設定」と書かれているボタンです。このボタンをクリックすると、 :numref:`fig-routine-property-dialog` のようなダイアログが開きます。 これはルーチンのプロパティ設定ダイアログで、コンポーネントのプロパティを設定するのと同様にルーチンのさまざまな設定を変更できます。 「基本」、「Flow」、「ウィンドウ」、「データ」、「テスト」の5つのタブがありますが、本節で紹介しておきたいのは2つです(「 :numref:`{number}:{name} ` にも「Rouitineの設定」を利用するテクニックが登場します)。 .. _fig-routine-property-dialog: .. figure:: fig04/routine-property-dialog.png :width: 80% ルーチンのプロパティ設定ダイアログ まず「テスト」タブをクリックすると、 **[ルーチンの無効化]** という項目があります( :numref:`fig-disable-limit-routine` 左上)。この項目にチェックすると、 :numref:`fig-disable-limit-routine` 右上のように、フローに配置されている当該ルーチンがグレーで表示され、実験実行時に実行されなくなります。 この例では、instructionルーチンが終了した後trialsループに入りますが、trialルーチンが無効化されていてループの中身が空っぽなので一瞬で終了してしまいます。その結果、trailsループもあっという間に終了してblocksループの2周目に入り、あたかもinstrctionルーチンが続けて2回実行されたかのような動作になります。 もうひとつ紹介しておきたいのは、「Flow」タブの **タイムアウト** という項目です( :numref:`fig-disable-limit-routine` 左下)。 タイムアウトを設定すると、ルーチン内にまだ終了していないコンポーネントが存在していても強制的にルーチンが終了します。 例えばtrialルーチンで :numref:`fig-disable-limit-routine` 左下のように **タイムアウト** を「実行時間 (秒)」で0.6に設定すると、 :numref:`fig-disable-limit-routine` 右下のようにルーチンのタイムラインの0.6秒のところにオレンジ色の縦線が引かれ、コンポーネントの有効時間が0.6秒を越えて残っていてもグレーで表示されます。 この状態で実験を実行するとtrialルーチンが0.6秒で終了してしまうので、trialsループに入った後0.5秒間の空白画面と0.1秒間の刺激描画が何も操作しなくても繰り返されていきます。 **[ルーチンの無効化]** のように完全にルーチンが実行されなくなってしまうと、動作確認として不十分と言うか、本当にきちんと動作しているのか不安になりますが、一瞬だけ刺激を表示しておくと「ああ、確かにここで刺激が表示されるんだ」と確認しつつ、自分自身でキー押し操作をすることなくblocksループの2回目の実行まで進めることができます。 **[ルーチンの無効化]** と場面に応じて使い分けるとよいでしょう。 .. _fig-disable-limit-routine: .. figure:: fig04/disable-limit-routine.png :width: 80% ルーチンのプロパティ設定によるルーチン実行スキップ。 **[ルーチンの無効化]** をチェックすると、実行時にそのルーチンはまったく実行されなくなります。 なお、今回の実験ではtrailsループのなかにルーチンは1つしかありませんが、多数のルーチンがある場合にひとつひとつ **[ルーチンの無効化]** をチェックしていくのは面倒です。このような場合はループの **[繰り返し回数]** を0にすると、「ループが0回実行される = 1度も実行されない」ということで、ループ内のルーチンをまとめてスキップすることができます。このテクニックも覚えておくとよいでしょう。 関連テクニックとして、コンポーネントのプロパティ設定ダイアログにも「テスト」タブがあり、 **[コンポーネントの無効化]** という項目があることも記しておきます( :numref:`fig-disable-component` )。 これは「あるルーチンに含まれる一部のコンポーネントだけ実行したくない」場合に利用できます。 例えば「実験を実行してみるとエラーで停止してしまうのだけれど、どのコンポーネントが原因なのかわからない」場合などに便利です。原因不明のエラーで困っている時に、あるコンポーネントを無効化して実行するとエラーが発生しないのなら、きっとそのコンポーネントがエラーに関与しているはずです。中級者から上級者向けの機能かも知れませんが、こんな機能があるということを頭の片隅に置いておくと役に立つかもしれません。 .. _fig-disable-component: .. figure:: fig04/disable-component.png :width: 80% 「テスト」タブの **[コンポーネントの無効化]** をチェックするとコンポーネントを一時的に無効化できます。 チェックリスト - 一時的に、特定のルーチンを実行しないようにすることができる。 - ルーチンを、まだ実行中のコンポーネントが残っていても指定した実行時間で強制的に中断させることができる。 - 一時的に、特定のループを実行しないようにすることができる。 .. _section-counterbalance-component: 参加者間でカウンターバランスをとろう (中級) --------------------------------------------------------------------- 以上で多重ループを用いて複数のブロックを一回の実験でまとめて実行するという目標が達成できましたが、現状では-20度/20度条件と-70度/70度条件のどちらが先に実行されるかはランダムに決定されます。 「本当にランダムならすべての選択肢がほぼ均等な頻度で生じるはずだ」と考えている人は結構多いのですが、実際に試してみると意外と偏りがあるものです。 本章の実験を20名の参加者に実行してもらったとして、「-20度/20度条件が先に実行された人は20名中3名しかいなかった」ということが十分に起こり得ます。 しかし、心理学実験では「各参加者にランダムに実行順序を割り当てつつ、各順序が割り当てられた人数を等しくしたい」といったことがよくあります。 解決策のひとつは、「 :numref:`{number}:{name} ` 」および「 :numref:`{number}:{name} ` 」で覚えたテクニックを使って、実験開始時に-20度/20度条件を先にするか-70度/70度条件を先にするか選べるようにすることです。 具体的には、blocksループの条件ファイル名を実験情報ダイアログから取得することにして、blocksループ用の条件ファイルをexp04_20.xlsx、exp04_70.xlsxの順に並べたものと、exp04_70.xlsx、exp04_20.xlsxの順に並べたものの2種類を用意します。そして、条件ファイルに並べた順番通りにブロックを繰り返すようにblocksループの **Loopの種類** をsequentialにするのがポイントです(:numref:`fig-practice-select-condition-order-hint`)。 これらの条件ファイルを「 :numref:`{number}:{name} ` 」のように実験情報ダイアログで選択できるようにすればよいでしょう。これらは練習問題としておきます。 .. _fig-practice-select-condition-order-hint: .. figure:: fig04/practice-select-condition-order-hint.png :width: 80% ブロックの順序を実験実行時に選択できるようにします。 このように人が手作業で管理する方法は、さまざまな事態に臨機応変に対応できる利点もあるのですが、心理学実験の面倒な手続きをPCに任せよう!というのが本書のテーマなので、なんとかPC任せにしてしまいたいところです。そこで登場するのがPsychoPy 2024から導入されたCounterbalanceルーチンです。ルーチンという名前ですが、他のコンポーネントと同様コンポーネントペインから選択します(「カスタム」カテゴリの中にあります)。 すると :numref:`fig-counterbalance-routine` のように、ルーチンペインにcounterbalanceという新しいルーチンが追加されて、ルーチンペインいっぱいにcounterbalanceルーチンのプロパティ設定ダイアログが表示されます。 このルーチンは通常のルーチンとは異なり、コンポーネントを配置することができませんので、 :numref:`fig-counterbalance-routine` の右側のコンポーネントペインに並んだコンポーネントが薄い色で表示されてクリックできないようになっています。 ルーチンペインの表示をtrialルーチンなどの通常ルーチンに切り替えるとコンポーネントを置けるようになります。 .. _fig-counterbalance-routine: .. figure:: fig04/counterbalance-routine.png :width: 80% Counterbalanceルーチン。通常のコンポーネントと異なり、コンポーネントペインのCounterbalanceルーチンのアイコンをクリックするとルーチンが新たに追加され、ルーチンペインにプロパティ設定ダイアログが表示されます。 Counterbalanceルーチンの設定をおこなうには、 **スロット** と **グループ** という用語を理解しておく必要があります(:numref:`fig-group-slot`)。 スロットは区画のことで、ここでは「実験1回」を指しています。グループはスロットをまとめて入れておく器のようなものと考えてください。 :numref:`fig-group-slot` 左上は、「0」、「1」とラベルをつけられた2つのグループに5個ずつスロットが入っています。実験を1回実行するごとに(正確にはCounterbalanceルーチンが実行されるごとに)、ランダムに選ばれたいずれかのグループからスロットがひとつ消費されます。10回実験を実行するとスロットの残りは0個になります。 :numref:`fig-group-slot` 右上に「全てのグループのスロットが空になるとグループはNoneになる」とありますが、NoneはPythonで「ない」ということを表す値です(これまでにも「 :numref:`{number}:{name} ` 」で塗りつぶさないことを指定する時などに出てきました)。 ここまで説明したらなんとなく想像できた方もおられると思いますが、Counterbalanceルーチンを実行した後に「どのグループからスロットが取り出されたか」に応じて条件ファイルを切り替えらえたら、参加者間のカウンターバランスが実現できます。 .. _fig-group-slot: .. figure:: fig04/group-slot.png :width: 80% Counterbalanceコンポーネントの働き スロットとグループの説明を終えたところで、Countebalanceルーチンのプロパティを説明していきましょう。 **[グループの定義...]** はグループの名前と個数、スロットの個数をどのように定義するかを選択します。「グループ数」を選ぶと下に **[グループ数]** と **[グループごとのスロット]** というプロパティが表示され、ここに数値を入力して定義することができます。 「条件ファイル (ローカルのみ有効)」を選ぶと **[条件]** というプロパティが表示され、xlsxファイルやCSVファイルを使用してグループを定義できます。こちらの方が柔軟な定義ができるので、本章ではこちらの方法を解説します。 **[繰り返し回数]** はちょっと後回しにして先に **[使いつくした場合に実験を終了]** を解説しましょう。これは文字通り、スロットが使いつくされて残っていないのに実験を実行すると、このルーチンのところで強制的に実験が終了してしまうということです。通常はチェックしておくと良いでしょう。 チェックしなかった場合は「どのグループも選択されていない」という状態でフローの次のルーチンへ進んでしまいます。 スロットの残りがない時にいきなり実験を終了するのではなく、なにか処理をおこないたい場合に便利ですが、Builderの実験がどのようにPythonのコードに変換されるかを理解したうえでコードを書かないといけないので上級者向けです。 **[使いつくした場合に実験を終了]** をチェックしているという前提で **[繰り返し回数]** について説明します。 :numref:`fig-group-slot` 左下は「グループ数2、スロット数6で繰り返し回数1」と設定した場合で、2×6=12回実験を実行すると残りスロットが0になります。繰り返し回数が1なので、13回目の実験を実行するとスロットの残りがないので **[使いつくした場合に実験を終了]** の設定に従って実験が強制的に終了します。 :numref:`fig-group-slot` 右下は「グループ数2、スロット数3で繰り返し回数2」と設定した場合で、2×3=6回実験を実行すると残りスロットが0になりますが、繰り返し回数が2なのでスロットがすべて満たされた状態に戻ります。さらに2×3=6回、合計12回実験を実行すると再びスロットが0になり、次(13回目)の実行では強制的に終了します。 結果的にどちらの設定でもグループ1を6回、グループ2を6回の12回実行されますが、 :numref:`fig-group-slot` 左下の例では「グループ1が6回連続した後、グループ2が6回連続する」といったことが起こり得ます。 :numref:`fig-group-slot` 右下の例ではそういったことは起こりません。 **[Loopの種類]** のfullRandomとrandomの違いに似ています(「 :numref:`{number}:{name} ` 」)。 .. _fig-counterbalance-cndfile: .. figure:: fig04/counterbalance-cndfile.png :width: 80% xlsxファイルでCounterbalanceコンポーネントのグループとスロットを定義できます。 :numref:`fig-counterbalance-cndfile` は「条件ファイル (ローカルのみ有効)」でグループとスロットを定義する時のxlsxファイルの例です。 **groupとcapというパラメータが必須** で、 groupは各グループにつけるラベル、capは各グループに割り当てるスロット数です。この例では「70度条件から」と「20度条件から」というグループが定義され、「70度条件から」には4、「20度条件から」には6のスロットが割り当てられています。条件ファイルを用いると、このようにグループによって異なるスロット数を割り当てることが可能です。 :numref:`fig-counterbalance-cndfile` にはblockFileというパラメータも定義されていますが、これはグループに応じて変化させたいパラメータとして使用できます。 この例では「70度条件から」にexp04_70.xlsx、「20度条件から」にexp04_20.xlsxというファイル名を割り当てることによって、blocksループで使用する条件ファイルを切り替えることを意図しています。 Excelはひとつのセルにかなり長い文字列(例えば複数の改行を含む100文字以上の文字列)を入力できるので、 **グループごとに教示を切り替えるといったことも可能** でしょう。 せっかくですので、この条件ファイルを使ってexp04.psyexpを改造してみましょう。 - 条件ファイルの準備 - :numref:`fig-counterbalance-cndfile` の内容のxlsxファイルを作成し、exp04groups.xlsxという名前でexp04.psyexpと同じフォルダに保存する。 - :numref:`fig-practice-select-condition-order-hint` に示されているexp04_70_20.xlsxとexp04_20_70.xlsxを作成し、exp04.psyexpと同じフォルダに保存する(先ほどの練習問題を済ませていたらそのまま流用できます)。 ― Counterbalanceルーチンの配置 - Builderでexp04.psyexpを開き、Counterbalanceルーチンを追加(コンポーネントペインのCounterbalanceルーチンのアイコンをクリック)して以下の通り設定する。 - **[グループの定義...]** を「条件ファイル (ローカルのみ有効)」に変更する。 - **[条件]** にexp04groups.xlsxを指定する(このプロパティは **[グループの定義...]** を「条件ファイル (ローカルのみ有効)」にしないと表示されません)。 - **[繰り返し回数]** が1であり、 **[使いつくした場合に実験を終了]** がチェックされていることを確認する。 - フローペインの「Routineを挿入」をクリックすると、挿入できるルーチンの一覧にCounterbalanceという名前のルーチンがあるので、そのルーチンを選択してフローの先頭(instructionルーチンの前)に挿入する。 - blocksループの変更 - **[Loopの種類]** をsequentialにする(:numref:`fig-practice-select-condition-order-hint` の作業をしていたら既にsequentialになっている)。 - blocksループのプロパティを開き、 **[条件]** に ``$counterbalance.params['blockFile']`` と入力する。 blocksループの **[条件]** に入力している内容を説明するとかなり話が長くなるので、ここはとりあえずCounterbalanceコンポーネントの **[名前]** に.params[]と付けると、exp04groups.xlsxで定義したカウンターバランスのパラメータを参照できると覚えておいてください。[]の中に' 'で囲んでパラメータ名を書くのはexpInfoと同じですね。 - その他 - 動作確認を短時間で済ませるため、trialsループの **[繰り返し回数]** を1にしておく。 - 先ほどの練習問題を済ませているなら、実験情報ダイアログにxp04_70_20.xlsxとexp04_20_70.xlsxを選択するための項目が追加されているはずなので、実験設定ダイアログからその項目を削除しておく。 以上で作業は終わりです。 ちょっと大変ですが、実験を10回実行して、exp04groups.xlsxのcapに設定した通りに「70度条件からが始まる実験が4回、20度条件から始まる実験が6回」になることを確認しましょう。trialsルーチンのプロパティ設定ダイアログから **[タイムアウト]** を設定すると、キーを押さなくても次々と試行が進むので楽です。 **実験にエラーがあって途中で止まってしまった場合でも、Counterbalanceルーチンが実行されていれば1回とカウントされる** ので注意してください。 さらに、11回目を実行しようとすると Attempting to measure frame rate of screen, please wait... のメッセージの後にすぐ実験が終了してしまうことも確認してください。終了してしまった後、Runnerの「標準出力」のタブを見ると、 :numref:`fig-no-slot` のように「カウンターバランス counterbalance のスロットが使いつくされたため, 実験を終了しました」と出力されているのがわかります(counterbalanceのところにはCounterbalanceコンポーネントの **[名前]** が入ります)。 エラーで止まってしまった回があって「70度条件からが始まる実験が4回、20度条件から始まる実験が6回」だったのかきちんと確認できなかった場合は、exp04.psyexpファイルがあるフォルダを開いてみてください。shelf.jsonという名前のファイルができているはずです。ここに残りの繰り返し回数と各グループの残りスロットの情報が保存されているので、 **shelf.jsonを削除すると最初からカウントし直しになります。** 実験を作成している途中でグループの定義を変更した時や、実際の研究場面で「何回か動作確認をして問題なさそうだから、いよいよ次から本番」という時にも、shelf.jsonを削除するとよいでしょう。Counterbalanceコンポーネントを使うときにはよく覚えておいてください。shelf.jsonの内容や、xlsxファイルを使わないグループの定義方法などは「 :numref:`{number}:{name} ` 」を参照してください。 .. _fig-no-slot: .. figure:: fig04/no-slot.png :width: 80% すべてのスロットを使いつくして実験が終了した場合、Runnerの標準出力にメッセージが表示されます。 これで参加者間のカウンターバランスをPCに管理してもらうことができるようになりました。話の区切りもちょうどいいので、ここで本章を終わりにしたいと思います。 最後のCounterbalanceコンポーネントの解説で、counterbalance.params['blockFile']という入力内容の解説を十分にできませんでした。これがどういう意味を持つのかをきちんと説明しようとすると、どうしてもPythonの文法に触れる必要があります。次章から、Builderの実験の中でPythonのコードを利用する方法を学んでいきましょう。 チェックリスト - Counterbalanceルーチンにおけるグループとスロットの意味を説明できる。 - Counterbalanceルーチンで各グループに異なるスロット数を設定できる。 - xlsxファイルでグループとスロットを定義する際に、追加で定義したパラメータの値を利用することができる。 - Counterbalanceルーチンで管理している各グループのスロットの残りをリセットすることができる。 この章のトピックス --------------------------------------------------- .. _topic-grating-texture-property: Gratingコンポーネントの **[テクスチャ]** プロパティについて ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 視知覚の研究でグレーティングと言うと一般的に正弦波状に明るさが変調された刺激を指すのですが、PsychoPyのGratingコンポーネントはより一般的な「同一のパターンが2次元に繰り返される刺激」を描く機能を持っています。 :numref:`fig-grating-texture-sample1` は **[テクスチャ]** に指定できる値を示しています。sinは正弦波、sawは鋸波、sqrは矩形波、triは三角波を描きます。Noneを指定すると、 **[前景色]** で指定された色で塗りつぶします。これらの波は1次元、すなわち一方向にのみ明るさが変化しますが、sinXsinでは垂直方向と水平方向に変化する2次元の波を描きます。同様にsqrXsqr、gauss、radRamp、raisedCosといった波形を指定できます。垂直方向と水平方向の空間周波数を別々に指定するためには、 **[空間周波数 $]** に刺激の大きさを指定する時と同様の記法を用います。 :numref:`fig-grating-texture-sample2` 左上はsqrXsqrの波形に **[空間周波数 $]** として[2,3]、[3,2]を指定した時の出力を示しています(単位はheight)。図からわかるように、第1の値が水平方向、第2の値が垂直方向に対応しています。 .. _fig-grating-texture-sample1: .. figure:: fig04/grating-texture-sample1.png :width: 80% Gratingコンポーネントの **[テクスチャ]** として指定できる値。これらの他に画像ファイル名を指定することができます。いずれも **[空間の単位]** はheightで **[空間周波数 $]** は3.0を指定しています。 .. _fig-grating-texture-sample2: .. figure:: fig04/grating-texture-sample2.png :width: 80% **[空間周波数 $]** に2つの値を指定する例と(上段の左側2つ)、 **[テクスチャ]** に画像ファイルを指定する例(上段の右側2つ)、 **[マスク]** に画像ファイルを指定する例(下段)。 **[テクスチャ]** および **[マスク]** には、画像ファイルを指定することもできます。 :numref:`fig-grating-texture-sample2` 右上は、PsychoPyのアイコン画像を保存したicon.pngというファイルを **[テクスチャ]** に指定した例を示しています。 **[テクスチャの解像度 $]** の値が2の冪乗(64や128など)であったように、PsychoPy内部ではテクスチャは縦横の解像度が2の冪乗の正方形でなければいけません。それ以外の解像度の画像を指定するとPsychoPyが内部的に拡大縮小を行いますので、正確さを期する場合は最初から2の冪乗の正方形の画像ファイルを作成することが大切です。 **[マスク]** にモノクロの画像ファイルを指定すると、 **[マスク]** 画像のピクセルの明るさがテクスチャの透明度となります。文章では説明しにくいので :numref:`fig-grating-texture-sample2` 下段をご覧ください。mask1.png、mask2.pngという2種類のモノクロ画像をマスクとして用意しました。 **[マスク]** にmask1.pngを、 **[テクスチャ]** に先ほどのicon.pngを指定した例が :numref:`fig-grating-texture-sample2` 下段左から3番目です。mask1.pngの黒色の部分が透明になって、背景の灰色が見えています。透明になっていることがわかりやすいように、白色と青色のPolygonコンポーネントを背後に配置しています。 :numref:`fig-grating-texture-sample2` 下段の右端は、 **[テクスチャ]** をsinにして、 **[マスク]** にmask2.pngを指定した例を示しています。mask2.pngが黒色から白色に向かって滑らかに変化しているので、縞模様が滑らかに透明から不透明へと変化しています。 .. _topic-in-trials-property: ループの **[試行を繰り返す]** プロパティについて ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ループの **[試行を繰り返す]** プロパティは、実験設定ダイアログで「xlsx形式のデータを保存」または「CSV形式のデータを保存(summaries)」をチェックして出力されるデータファイルの形式を設定するものです。 「xlsx形式のデータを保存」をチェックしてexp04c.psyexpを実行することによって作成されるxlsxファイルの例を :numref:`fig-nestedloop-xlsx-output-not-checked` に示します。ただし、 **[試行を繰り返す]** はチェックしていないものとします。 .. _fig-nestedloop-xlsx-output-not-checked: .. figure:: fig04/nestedloop-xlsx-output-not-checked.png :width: 70% 多重ループを用いた実験によって作成されるxlsx記録ファイルの例( **[試行を繰り返す]** をチェックしない場合)。 .. _tbl-nextedloop-xlsx-sheet-name: .. csv-table:: 多重ループによって作成されるxlsx記録ファイルのシート名 (内側のループ名がtrialsの場合) trials, 最初に実行されたtrialsループ trials1, 2回目に実行されたtrialsループ …, … trialsN (Nは自然数), N+1回目に実行されたtrialsループ trialsとtrials1というシートが作成されていますが、これは :numref:`tbl-nextedloop-xlsx-sheet-name` に示すようにそれぞれtrialsループの1回目、2回目に対応しています。シートの内容を確認すると、 :numref:`fig-nestedloop-xlsx-output-not-checked` の例の場合はtrialシートのcontextDirの列が20または-20なので、第1ブロックは-20度/20度条件であったことがわかります。同様に、trial1シートのcontextDirの列を確認すると大2ブロックは-70度/70度条件であったことがわかります。 今回の実験ではblocksループによるtrialsループの繰り返しは2回のみでしたが、仮にblocksループによる繰り返しが20回に及ぶ場合はtrials、trials1、…、trials19と20枚もシートが作成されます。あまりシート数が多くなるとExcel上での作業が大変ですので、繰り返し回数が多くなる場合は実験を分割することも検討すべきです。 続いてblocksループの **[試行を繰り返す]** をチェックした場合に出力されるxlsx記録ファイルの例を :numref:`fig-nestedloop-xlsx-output-checked` に示します。trials、trials1というシートができるのはチェックを外した場合と同じですが、それに加えてblocksというシートが含まれています。このシートには、blocksループの繰り返しにおいて、exp04blocks.xlsxから読み込んだ条件のどの行が使われたかが記録されています。別にこのシートが存在していてはいけないわけではないのですが、trials、trials1のシートから読み取ることができる情報しか含まれていないので、無駄なシートといえます。今回のようにblocksループが一番外側のループだったら1枚余分なシートが増えるだけですが、さらに外側にループが組まれている実験では何枚も無駄なシートが作成されてしまいます。このような場合は **[試行を繰り返す]** のチェックを外してシート数を抑えることが有効です。 .. _fig-nestedloop-xlsx-output-checked: .. figure:: fig04/nestedloop-xlsx-output-checked.png :width: 80% 多重ループを用いた実験によって作成されるxlsx記録ファイルの例( **[試行を繰り返す]** をチェックした場合)。 なお、外側のループ内にループに含まれないルーチンが存在していて、そのルーチンでの刺激や反応を記録する必要がある場合は、 **[試行を繰り返す]** のチェックを外してはいけません。具体的には、 :numref:`fig-nestedloop-non-looped-routine` のtestルーチンのようなものがblocksループ内にある場合です。このような場合に blocksループの **[試行を繰り返す]** のチェックを外してしまうと、testルーチンで用いられた条件や、testルーチンにおける参加者の反応などが記録されません。チェックを外していいか自信がない場合、xlsx記録ファイルのシート数を気にしないのであれば **[試行を繰り返す]** のチェックはつけたままにしておくというのも一つの手だと思います。 .. _fig-nestedloop-non-looped-routine: .. figure:: fig04/nestedloop-non-looped-routine.png :width: 80% **[試行を繰り返す]** のチェックを外してはいけない例 .. _topic-shelf-json: shelf.jsonおよびCounterbalanceコンポーネントに関する補足 (上級) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ shelf.jsonは拡張子が示す通りJSON形式のテキストファイルです。テキストエディタで簡単に内容を確認、編集することができます。今回の実験で作成されるshelf.jsonの内容は以下のようになります。 .. code-block:: json { "counterbalance": { "_reps": 1, "70\u5ea6\u6761\u4ef6\u304b\u3089": 3, "20\u5ea6\u6761\u4ef6\u304b\u3089": 1 } } 2行目の"counterbalance"はCounterbalanceルーチンの **[名前]** に対応しています。 3行目の_repsは残りの繰り返し回数です。 4行目と5行目は日本語の文字がUnicodeエスケープシーケンスで表記されていますが、条件ファイルで指定されているグループ名(「70度条件から」と「20度条件から」)と残りスロット数を表しています。 通常はshelf.jsonの内容をエディタで開いて確認する必要はありませんが、いざというときにエディタで開く可能性も考慮するならグループ名は半角英数文字でつけた方が読みやすいでしょう。 少しわかりにくいのですが、「残り繰り返し回数」はすべてのスロットが使いつくされて次の繰り返しに入る時に1減少します。つまり、Codeコンポーネントの **[繰り返し回数]** が3に設定されているなら、1回目の繰り返しでのすべてのスロットが使いつくされた直後にshelf.jsonを確認すると、_repsはまだ3のままです(全グループのスロットは0になっている)。 次に1回実験を実行した時に初めて_repsが2になります。3回目の繰り返しのすべてのスロットを使いつくした後には、_repsが1(0ではなく)で全グループのスロットは0になっています。 通常、shelf.jsonを直接編集する必要はないはずですが、何らかの理由で編集しなければならなくなった時は注意してください。 **[グループの定義...]** で「グループ数」にすると、 **[グループ数]** と **[グループごとのスロット]** でグループを定義します。グループ名は自動的に0からグループ数-1までの数字が割り当てられます(データ型はintではなくstrなのでご注意ください)。各グループのスロット数は全て **[グループごとのスロット]** の値となります。 このように、「グループ数」による定義ではグループ名を自由につけたりグループごとに異なるスロット数を割り当てることができません。また、本文のblockFileのように付随するパラメータを定義することもできません。 その代わりに、(1)xlsxファイルを必要としない、(2)オンライン実験でも使用できるという利点があります。xlsxファイルを使う方法はオンライン実験に対応していませんので、オンライン実験でCounterbalanceコンポーネントを使用するには「グループ数」による定義しか選択肢がありません。 「グループ数」による定義では付随するパラメータを定義できないので、本章のように読み込ませたい条件ファイル名を変化させたい場合は、グループ名から条件ファイル名を得るコードを書く必要があります。 グループ名はcounterbalanceオブジェクトのgroupというデータ属性で直接参照することができます。Counterbalanceルーチンの **[名前]** がcounterbalanceならcounterbalance.groupです。 これを利用して以下のようなコードをCodeコンポーネントを使って挿入すればよいいでしょう。 .. code-block:: JavaScript if ((counterbalance.group === "0")) { blockFile = "exp04_20_70.xlsx"; } else { blockFile = "exp04_70_20.xlsx"; }