jsPsychの出力データをいい感じに整形する

jsPsychは基本的にデータを過不足なく出力しますが、そのままだと後々集計しにくいということがよくあります。それを克服するためのポイントをまとめてみました。

以下の例では、jsPsychのバージョン7.3.0を使用しています。バージョンによって微妙に挙動が変わると思いますのでご注意ください。

[jsPsychのダウンロードページ](https://github.com/jspsych/jsPsych/releases)

なおここでは説明していませんが、日本語が文字化けするときには[こちら](https://qiita.com/kurokida/items/08425f8e6a812f06993d)をご覧ください。

# dataプロパティを使う

jsPsychは画面が切り替わるたびに１行ずつデータを出力します。例えば、教示の画面、注視点を呈示している画面など、実験データを分析するときには不要となるものも大量に出力されます。ですので、分析対象のデータだけを抽出するために関心のある行にラベルをつけておくと便利です。そのためには次のように data プロパティを指定します。

```javascript
  timeline.push({
    type: jsPsychImageButtonResponse,
    stimulus: 'img/happy_face_1.jpg',
    choices: ['Happy', 'Sad'],
    data: {block: 1}
  });

```

`{block: 1}`の部分はみなさんが分かりやすいように書き換えてOKです。上の例の出力結果は、次のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495823/bd8c095d-215f-d915-6116-75f8484e3dd6.png)

こうしておけば、エクセルのフィルター機能などを使って block が1の行だけを抽出することができます。

なお、dataプロパティには以下のようにfunctionを指定することもできます。これはdataの値を実験前に決められず、実験の最中に決める必要があるときに使います。

```javascript
var trial = {
  type: jsPsychSurveyText,
  questions: [{prompt: "Please enter your response."}]
  data: function() { 
    return {difficulty: current_difficulty}; 
  }
}
```

本家のdataに関する解説ページ

- https://www.jspsych.org/tutorials/rt-task/#part-11-tagging-trials-with-additional-data
- https://www.jspsych.org/overview/dynamic-parameters/#storing-changing-variables-in-the-data

# 余計な中括弧{}やコロン:を消す方法

刺激となる人物の画像を見て、その人物の年齢を答える実験を想定しましょう。

コードはこんな感じです。

```javascript
  var survey_page1 = {
    type: jsPsychSurveyText,
    questions: [
      {prompt: 'この人物は何歳くらいに見えますか？', columns: 3, required: true, name: 'Age'}
    ]
  };
```

結果は以下のようになります。たしかに回答された年齢は記録されていますが、このままだと中括弧、クオーテーション、コロンなどがあって扱いにくいですよね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495823/62f5aa36-99c2-ee79-d238-48b518c26f76.png)

こんなときには[addToLast](https://www.jspsych.org/core_library/jspsych-data/#addtolast)を使うと便利です。

```javascript
  var survey_page1 = {
    type: jsPsychSurveyText,
    questions: [
      {prompt: 'この人物は何歳くらいに見えますか？', columns: 3, required: true, name: 'Age'}
    ],
    on_finish: function(data){
      jsPsych.data.get().addToLast(data.response);
    }
  };
```

こうすると以下のようにいい感じで出力されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495823/857a3254-2b82-d0ec-c598-8c877561c1d9.png)

なお、addToLastと似た機能を持つ[addProperties](https://www.jspsych.org/core_library/jspsych-data/#jspsychdataaddproperties)がありますが、こちらは**すべての行**に同じデータが付与されます。例えば参加者を識別するための情報、参加者間要因、参加者が使用しているデバイスなど、すべての行に共通した情報を加えるときに使用します。

# 行がずれる問題を克服する

さて以上を踏まえて、刺激となる人物の画像を見て、最初にその人物の年齢を答えて、その後、人物の印象を答える実験を以下のように作成しました。

```javascript
  var survey_page1 = {
    type: jsPsychSurveyText,
    questions: [
      {prompt: 'この人物は何歳くらいに見えますか？', columns: 3, required: true, name: 'Age'}
    ],
    on_finish: function(data){
      jsPsych.data.get().addToLast(data.response);
    }
  };

  var survey_page2 = {
    type: jsPsychSurveyMultiChoice,
    questions: [
      {prompt: "この人物に対する好感度を答えてください", name: 'feeling', options: ["非常に好ましくない", "やや好ましくない", "どちらでもない", "やや好ましい", "非常に好ましい"], required:true}, 
    ],
    on_finish: function(data){
      jsPsych.data.get().addToLast(data.response);
    }
  };

```

上のコードで問題なく動作しますが、実験結果を見ると以下のように年齢と印象の回答が同一行に入っていません。これだと分析しにくいですよね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495823/8454058c-ada5-75af-c697-65d6cda13944.png)

こんなときには、dataプロパティの中で [getLastTrialData](https://www.jspsych.org/7.3/reference/jspsych-data/#jspsychdatagetlasttrialdata) を使用します。

```javascript
  var survey_page2 = {
    type: jsPsychSurveyMultiChoice,
    questions: [
      {prompt: "この人物に対する好感度を答えてください", name: 'feeling', options: ["非常に好ましくない", "やや好ましくない", "どちらでもない", "やや好ましい", "非常に好ましい"], required:true}, 
    ],
    data: function(){
      return jsPsych.data.getLastTrialData().values()[0].response
    },
    on_finish: function(data){
      jsPsych.data.get().addToLast(data.response);
    }
  };

```

直前の画面（年齢を回答させる試行）のデータを取得して、回答結果をdataプロパティに指定します。このときfunctionを使う必要がある点に注意してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495823/aee503c6-0e18-7e6a-3495-7e9b5ef60cf0.png)

無事に同一行にデータが入りました。

## 補足

上の例では、dataプロパティに直前の試行の反応だけを指定すればよかったのですが、他にも実験条件などを追加したいことがあるかもしれません。
そのときには以下のような書き方ができます。

```javascript
data: function(){
    var tmp_object = {
        file_name: jsPsych.timelineVariable('file_name'),
        block: 1
    }
    return Object.assign(tmp_object, jsPsych.data.getLastTrialData().values()[0].response)
},
```

`jsPsych.data.getLastTrialData().values()[0].response`はオブジェクトなのですが、これとは別に保存したいオブジェクト（`tmp_object`）を作成して、[Object.assign](https://qiita.com/kou_pg_0131/items/16c63879be55b85387aa)で結合します。２つのオブジェクトを結合して新たに作成されたオブジェクトを`return`で返すようにします。

# Chromeのデベロッパーツールのすすめ

以上の説明だけで理解するのは、なかなか大変だと思います。さらに理解を深めたいかたは、上のコードのdataプロパティの箇所を次のように書き換えて実行してみてください。

```javascript
    data: function(){
      console.log(jsPsych.data.getLastTrialData())
      console.log(jsPsych.data.getLastTrialData().values())
      console.log(jsPsych.data.getLastTrialData().values()[0])
      console.log(jsPsych.data.getLastTrialData().values()[0].response)

      return jsPsych.data.getLastTrialData().values()[0].response
    },
```

console.logの内容は、Chromeのデベロッパーツールを使って確認するのがおすすめです。それぞれの行が、いったいどんな内容を出力するかを確認すると、きっと理解が深まるはずです！