watsonのTradeoff Analyticsをcurlでためしてみる

概要

  • Tradeoff Analytics APIを使うことで、複数の条件からなる選択肢のうち、ベターなものが何かを調べることができます
  • 条件と候補をまとめたJSONをPOSTするだけで、結果を返してくれます
  • 無料枠もあるので、比較的気軽に試せます。

Tradeoff Analytics | IBM Watson Developer Cloud

APIの呼び出し方

もっともシンプルなのはcurlでPOSTリクエストを投げるだけです。

$ curl -X POST --user YOUR_USERNAME:YOUR_PASSWORD --header "Content-Type: application/json" --data @problem.json "https://gateway.watsonplatform.net/tradeoff-analytics/api/v1/dilemmas?generate_visualization=false" | jq .

YOUR_USERNAMEYOUR_PASSWORDは、「サービス資格情報」にあるものを使用します。
%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-11-20-12-32-17

problem.jsonというファイルをPOSTしていますので、ここからはJSONファイルの中身をざっと紹介します。

Request body(JSON)

problem.jsonの中には以下のキーが含まれます。

subject
タイトル
columns
選択の条件ための
options
選択候補

これらの3つを含めたJSONファイルを生成して、POSTします。

from:チュートリアル

7種類のスマートフォンから、自分の条件に合うものを探すというクエリです。

{
  "subject": "phones",
  "columns": [
    {
      "key": "price",
      "type": "numeric",
      "goal": "min",
      "is_objective": true,
      "full_name": "Price",
      "range": {
        "low": 0,
        "high": 400
      },
      "format": "number:2"
    },
    {
      "key": "weight",
      "type": "numeric",
      "goal": "min",
      "is_objective": true,
      "full_name": "Weight",
      "format": "number:0"
    },
    {
      "key": "brand",
      "type": "categorical",
      "goal": "min",
      "is_objective": true,
      "full_name": "Brand",
      "range": [
        "Apple",
        "HTC",
        "Samsung",
        "Sony"
      ],
      "preference": [
        "Samsung",
        "Apple",
        "HTC"
      ]
    },
    {
      "key": "rDate",
      "type": "datetime",
      "goal": "max",
      "full_name": "Release Date",
      "format": "date: 'MMM dd, yyyy'"
    }
  ],
  "options": [
    {
      "key": "1",
      "name": "Samsung Galaxy S4",
      "values": {
        "price": 249,
        "weight": 130,
        "brand": "Samsung",
        "rDate": "2013-04-29T00:00:00Z"
      }
    },
    {
      "key": "2",
      "name": "Apple iPhone 5",
      "values": {
        "price": 349,
        "weight": 112,
        "brand": "Apple",
        "rDate": "2012-09-21T00:00:00Z"
      }
    },
    {
      "key": "3",
      "name": "HTC One",
      "values": {
        "price": 299,
        "weight": 112,
        "brand": "HTC",
        "rDate": "2013-03-01T00:00:00Z"
      }
    },
    {
      "key": "4",
      "name": "Samsung Galaxy S5",
      "values": {
        "price": 349,
        "weight": 135,
        "brand": "Samsung",
        "rDate": "2014-04-29T00:00:00Z"
      }
    },
    {
      "key": "5",
      "name": "Apple iPhone 6",
      "values": {
        "price": 399,
        "weight": 118,
        "brand": "Apple",
        "rDate": "2013-09-21T00:00:00Z"
      }
    },
    {
      "key": "6",
      "name": "Apple iPhone 7",
      "values": {
        "price": 499,
        "weight": 118,
        "brand": "Apple",
        "rDate": "2014-09-21T00:00:00Z"
      }
    },
    {
      "key": "7",
      "name": "Sony Xperia",
      "values": {
        "price": 199,
        "weight": 120,
        "brand": "Sony",
        "rDate": "2014-08-21T00:00:00Z"
      }
    }
  ]
}

columnsの中身をざっくりとまとめるとこんな感じです。

項目 条件
価格 0 ~ 400の間
重さ できるだけ軽いもの
ブランド サムスン・アップル・HTCのどれか
リリース日 できるだけ新しいもの

戻り値

リクエストが成功した場合、以下のようなJSONが返ってきます。

{
  "problem": {
    "columns": [
      {
        "type": "numeric",
        "key": "price",
        "full_name": "Price",
        "range": {
          "low": 0,
          "high": 400
        },
        "format": "number:2",
        "goal": "min",
        "is_objective": true
      },
      {
        "type": "numeric",
        "key": "weight",
        "full_name": "Weight",
        "format": "number:0",
        "goal": "min",
        "is_objective": true
      },
      {
        "type": "categorical",
        "key": "brand",
        "full_name": "Brand",
        "range": [
          "Apple",
          "HTC",
          "Samsung",
          "Sony"
        ],
        "goal": "min",
        "preference": [
          "Samsung",
          "Apple",
          "HTC"
        ],
        "is_objective": true
      },
      {
        "type": "datetime",
        "key": "rDate",
        "full_name": "Release Date",
        "format": "date: 'MMM dd, yyyy'",
        "goal": "max",
        "is_objective": false
      }
    ],
    "subject": "phones",
    "options": [
      {
        "key": "1",
        "name": "Samsung Galaxy S4",
        "values": {
          "price": 249,
          "weight": 130,
          "brand": "Samsung",
          "rDate": "2013-04-29T00:00:00Z"
        }
      },
      {
        "key": "2",
        "name": "Apple iPhone 5",
        "values": {
          "price": 349,
          "weight": 112,
          "brand": "Apple",
          "rDate": "2012-09-21T00:00:00Z"
        }
      },
      {
        "key": "3",
        "name": "HTC One",
        "values": {
          "price": 299,
          "weight": 112,
          "brand": "HTC",
          "rDate": "2013-03-01T00:00:00Z"
        }
      },
      {
        "key": "4",
        "name": "Samsung Galaxy S5",
        "values": {
          "price": 349,
          "weight": 135,
          "brand": "Samsung",
          "rDate": "2014-04-29T00:00:00Z"
        }
      },
      {
        "key": "5",
        "name": "Apple iPhone 6",
        "values": {
          "price": 399,
          "weight": 118,
          "brand": "Apple",
          "rDate": "2013-09-21T00:00:00Z"
        }
      },
      {
        "key": "6",
        "name": "Apple iPhone 7",
        "values": {
          "price": 499,
          "weight": 118,
          "brand": "Apple",
          "rDate": "2014-09-21T00:00:00Z"
        }
      },
      {
        "key": "7",
        "name": "Sony Xperia",
        "values": {
          "price": 199,
          "weight": 120,
          "brand": "Sony",
          "rDate": "2014-08-21T00:00:00Z"
        }
      }
    ]
  },
  "resolution": {
    "solutions": [
      {
        "solution_ref": "1",
        "status": "FRONT"
      },
      {
        "solution_ref": "2",
        "status": "FRONT"
      },
      {
        "solution_ref": "3",
        "status": "FRONT"
      },
      {
        "solution_ref": "4",
        "status": "EXCLUDED"
      },
      {
        "solution_ref": "5",
        "status": "EXCLUDED"
      },
      {
        "solution_ref": "6",
        "status": "INCOMPLETE",
        "status_cause": {
          "message": "A column of a option is out of range. Option \"6\" has a value in column \"price\" which is:\"499\" while the column range\" is: [0.0,400.0]",
          "error_code": "RANGE_MISMATCH",
          "tokens": [
            "price",
            "499",
            "[0.0,400.0]"
          ]
        }
      },
      {
        "solution_ref": "7",
        "status": "DOES_NOT_MEET_PREFERENCE",
        "status_cause": {
          "message": "Option \"7\" has a value that does not meet preference for column \"brand\"",
          "error_code": "DOES_NOT_MEET_PREFERENCE",
          "tokens": [
            "brand"
          ]
        }
      }
    ]
  }
}

どこを見ればいいか?

code>resolutionという値がJSONの中に含まれています。
この値の中に、それぞれのoptionsが条件に一致するものか否かが格納されています。

resolutionのみjqでピックアップした例

$ curl -X POST --user YOUR_USERNAME:YOUR_PASSWORD --header "Content-Type: application/json" --data @problem.json "https://gateway.watsonplatform.net/tradeoff-analytics/api/v1/dilemmas?generate_visualization=false | jq ".resolution"
{
  "solutions": [
    {
      "solution_ref": "1",
      "status": "FRONT"
    },
    {
      "solution_ref": "2",
      "status": "FRONT"
    },
    {
      "solution_ref": "3",
      "status": "FRONT"
    },
    {
      "solution_ref": "4",
      "status": "EXCLUDED"
    },
    {
      "solution_ref": "5",
      "status": "EXCLUDED"
    },
    {
      "solution_ref": "6",
      "status": "INCOMPLETE",
      "status_cause": {
        "message": "A column of a option is out of range. Option \"6\" has a value in column \"price\" which is:\"499\" while the column range\" is: [0.0,400.0]",
        "error_code": "RANGE_MISMATCH",
        "tokens": [
          "price",
          "499",
          "[0.0,400.0]"
        ]
      }
    },
    {
      "solution_ref": "7",
      "status": "DOES_NOT_MEET_PREFERENCE",
      "status_cause": {
        "message": "Option \"7\" has a value that does not meet preference for column \"brand\"",
        "error_code": "DOES_NOT_MEET_PREFERENCE",
        "tokens": [
          "brand"
        ]
      }
    }
  ]
}

各項目のstatusというパラメータを確認することで、検索条件に一致するものか否かを確認できます。

Front
条件に一致する選択肢
Excluded
条件に一致しない選択肢
Incomplete / DOES_NOT_MEET_PREFERENCE
optionsのパラメータに問題があり、判定できなかった選択肢

上記の例の場合、以下の3台が検索候補に一致すると判断されています。

  • ID:1 – Samsung Galaxy S4
  • ID:2 – Apple iPhone 5
  • ID:3 – HTC One

日本語は使えるのか?

「すごいのはわかったけど、日本語認識しないなら用はないかな」という人もいるんじゃないでしょうか。安心してください。日本語もちゃんと動きます。

SamsungとAppleを日本語表記にしたサンプル

念のため、存在しない値をわざと追加しておきます。

{
  "subject": "phones",
  "columns": [
    {
      "key": "price",
      "type": "numeric",
      "goal": "min",
      "is_objective": true,
      "full_name": "Price",
      "range": {
        "low": 0,
        "high": 400
      },
      "format": "number:2"
    },
    {
      "key": "weight",
      "type": "numeric",
      "goal": "min",
      "is_objective": true,
      "full_name": "Weight",
      "format": "number:0"
    },
    {
      "key": "brand",
      "type": "categorical",
      "goal": "min",
      "is_objective": true,
      "full_name": "Brand",
      "range": [
        "アップル",
        "HTC",
        "サムスン",
        "Sony"
      ],
      "preference": [
        "サムスン",
        "アップル",
        "HTC"
      ]
    },
    {
      "key": "rDate",
      "type": "datetime",
      "goal": "max",
      "full_name": "Release Date",
      "format": "date: 'MMM dd, yyyy'"
    }
  ],
  "options": [
    {
      "key": "1",
      "name": "サムスン Galaxy S4",
      "values": {
        "price": 249,
        "weight": 130,
        "brand": "サムスン",
        "rDate": "2013-04-29T00:00:00Z"
      }
    },
    {
      "key": "2",
      "name": "アップル iPhone 5",
      "values": {
        "price": 349,
        "weight": 112,
        "brand": "アップル",
        "rDate": "2012-09-21T00:00:00Z"
      }
    },
    {
      "key": "3",
      "name": "HTC One",
      "values": {
        "price": 299,
        "weight": 112,
        "brand": "HTC",
        "rDate": "2013-03-01T00:00:00Z"
      }
    },
    {
      "key": "4",
      "name": "サムスン Galaxy S5",
      "values": {
        "price": 349,
        "weight": 135,
        "brand": "サムスン",
        "rDate": "2014-04-29T00:00:00Z"
      }
    },
    {
      "key": "5",
      "name": "アップル iPhone 6",
      "values": {
        "price": 399,
        "weight": 118,
        "brand": "アップル",
        "rDate": "2013-09-21T00:00:00Z"
      }
    },
    {
      "key": "6",
      "name": "アップル iPhone 7",
      "values": {
        "price": 499,
        "weight": 118,
        "brand": "アップル",
        "rDate": "2014-09-21T00:00:00Z"
      }
    },
    {
      "key": "7",
      "name": "Sony Xperia",
      "values": {
        "price": 199,
        "weight": 120,
        "brand": "Sony",
        "rDate": "2014-08-21T00:00:00Z"
      }
    },
    {
      "key": "8",
      "name": "失敗するサンプル",
      "values": {
        "price": 199,
        "weight": 120,
        "brand": "エラー",
        "rDate": "2014-08-21T00:00:00Z"
      }
    }
  ]
}

日本語が混ざった場合の結果

ちゃんと日本語を認識して、columnsで定義されていないbrandを指定した8番目のアイテムにはエラーを返しています。

$ curl -X POST --user YOUR_USERNAME:YOUR_PASSWORD --header "Content-Type: application/json" --data @problem.json "https://gateway.watsonplatform.net/tradeoff-analytics/api/v1/dilemmas?generate_visualization=false | jq ".resolution"
{
  "solutions": [
    {
      "solution_ref": "1",
      "status": "FRONT"
    },
    {
      "solution_ref": "2",
      "status": "FRONT"
    },
    {
      "solution_ref": "3",
      "status": "FRONT"
    },
    {
      "solution_ref": "4",
      "status": "EXCLUDED"
    },
    {
      "solution_ref": "5",
      "status": "EXCLUDED"
    },
    {
      "solution_ref": "6",
      "status": "INCOMPLETE",
      "status_cause": {
        "message": "A column of a option is out of range. Option \"6\" has a value in column \"price\" which is:\"499\" while the column range\" is: [0.0,400.0]",
        "error_code": "RANGE_MISMATCH",
        "tokens": [
          "price",
          "499",
          "[0.0,400.0]"
        ]
      }
    },
    {
      "solution_ref": "7",
      "status": "DOES_NOT_MEET_PREFERENCE",
      "status_cause": {
        "message": "Option \"7\" has a value that does not meet preference for column \"brand\"",
        "error_code": "DOES_NOT_MEET_PREFERENCE",
        "tokens": [
          "brand"
        ]
      }
    },
    {
      "solution_ref": "8",
      "status": "INCOMPLETE",
      "status_cause": {
        "message": "A column of a option is out of range. Option \"8\" has a value in column \"brand\" which is:\"エラー\" while the column range\" is: [\"アップル\",\"HTC\",\"サムスン\",\"Sony\"]",
        "error_code": "RANGE_MISMATCH",
        "tokens": [
          "brand",
          "エラー",
          "[\"アップル\",\"HTC\",\"サムスン\",\"Sony\"]"
        ]
      }
    }
  ]
}

Follow me!

Okamoto Hidetaka
デジタルキューブのインフラエンジニア。勉強会に和太鼓の練習から直行することが多く「太鼓の人」とかよばれてます。 思いつきで公式ディレクトリにテーマやプラグインをアップしたりテーマレビューやったりしています。 AWS / WordPress / LinkedOpenData周りで活動していて、APIをどうこうして何か作るというのが多いです。 ひとこと
mautic is open source marketing automation