API

Tradeoff AnalyticsでAMIMOTOマネージドの最適プランを調べてみる

Tradeoff Analyticsについて Tradeoff Analyticsについては下の記事で紹介書いてるので、参考にしてください。 https://wp-kyoto.cdn.rabify.me/try-to-r […]

広告ここから
広告ここまで

Tradeoff Analyticsについて

Tradeoff Analyticsについては下の記事で紹介書いてるので、参考にしてください。
https://wp-kyoto.cdn.rabify.me/try-to-run-tradeoff-analytics/

どうせなら身近なものでテストしてみたいなと思ったので、ウチのサービスを題材にしてみました。

やってみたいこと

「価格・PV・WordPressインストール数とDBタイプの4種類から、希望条件に近いプランのリスト」を出すということを試してみます。

リクエストJSONを作る

最終的には1つのJSONにまとめますが、とりあえずパーツごとに作ります。

プロダクトリストを作る

まずは候補リストから。
候補はoptionsの中にリストとしてまとめていきます。

データソース

データはプラン一覧から拾ってきます。
https://ja.amimoto-ami.com/plans/hosting/single-instance-plan/
https://ja.amimoto-ami.com/plans/hosting/multi-instances-plan/

JSON

valuesの中に、価格・PV・DBタイプ・WordPressインストール数を入れてます。

"options": [
    {
        "key": "1",
        "name": "t2.micro",
        "values": {
            "price": 3000,
            "pv": 100000,
            "db": "EC2",
            "wp": 3
        }
    },{
        "key": "2",
        "name": "t2.small",
        "values": {
            "price": 6000,
            "pv": 300000,
            "db": "EC2",
            "wp": 3
        }
    },{
        "key": "3",
        "name": "t2.medium",
        "values": {
            "price": 15000,
            "pv": 500000,
            "db": "EC2",
            "wp": 3
        }
    },{
        "key": "4",
        "name": "c4.large",
        "values": {
            "price": 20000,
            "pv": 1000000,
            "db": "EC2",
            "wp": 5
        }
    },{
        "key": "5",
        "name": "c4.8large",
        "values": {
            "price": 350000,
            "pv": 20000000,
            "db": "EC2",
            "wp": 5
        }
    },{
        "key": "6",
        "name": "w-Small",
        "values": {
            "price": 80000,
            "pv": 3000000,
            "db": "RDS-Single",
            "wp": 5
        }
    },{
        "key": "7",
        "name": "w-Large",
        "values": {
            "price": 120000,
            "pv": 6000000,
            "db": "RDS-Single",
            "wp": 5
        }
    },{
        "key": "8",
        "name": "w-XLarge",
        "values": {
            "price": 160000,
            "pv": 10000000,
            "db": "RDS-Single",
            "wp": 10
        }
    },{
        "key": "9",
        "name": "w-2XLarge",
        "values": {
            "price": 320000,
            "pv": 20000000,
            "db": "RDS-Single",
            "wp": 10
        }
    },{
        "key": "10",
        "name": "HA-Small",
        "values": {
            "price": 120000,
            "pv": 3000000,
            "db": "RDS-Multi",
            "wp": 5
        }
    },{
        "key": "11",
        "name": "HA-Large",
        "values": {
            "price": 180000,
            "pv": 6000000,
            "db": "RDS-Multi",
            "wp": 5
        }
    },{
        "key": "12",
        "name": "HA-XLarge",
        "values": {
            "price": 200000,
            "pv": 10000000,
            "db": "RDS-Multi",
            "wp": 10
        }
    },{
        "key": "13",
        "name": "HA-2XLarge",
        "values": {
            "price": 460000,
            "pv": 20000000,
            "db": "RDS-Multi",
            "wp": 10
        }
    }
]

検索条件を入れる

columnsキーの中に検索候補を入れていきます。

"columns": [
        {
            "key": "price",
            "type": "numeric",
            "goal": "min",
            "is_objective": true,
            "full_name": "Price",
            "range": {
                "low": 0,
                "high": 300000
            },
            "format": "currency: 'JPY¥' : 2"
        },{
            "key": "pv",
            "type": "numeric",
            "goal": "max",
            "is_objective": true,
            "full_name": "Monthly Pageview",
            "range": {
                "low": 10000000,
                "high": 20000000
            },
            "format": "number:2"
        },{
          "key": "db",
          "type": "categorical",
          "goal": "min",
          "is_objective": true,
          "full_name": "Database Type",
          "range": [
            "EC2",
            "RDS-Single",
            "RDS-Multi"
          ],
          "preference": [
            "EC2",
            "RDS-Single",
            "RDS-Multi"
          ]
        },{
            "key": "wp",
            "type": "numeric",
            "goal": "max",
            "is_objective": true,
            "full_name": "WordPress installation ",
            "range": {
                "low": 1,
                "high": 10
            }
        }
    ]

検索条件を表にまとめるとこうなります。

項目名 範囲
価格 0~300,000円
PV 10,000,000~20,000,000 PV
DBタイプ EC2内DB > RDS1台 > RDSマルチAZ
WPインストール数 1 ~ 10
categoricalの分類方法

type:categoricalは、preferenceの配列順序でレコメンド度合いを取ります。
goalをmaxにした場合は配列の後ろの方にあるものを優先、minにした場合は前にあるものを優先します。

JSONにまとめる

最後にsubjectにタイトルを付けて、JSONにまとめます。
まとめたものが以下の通り。

{
    "subject": "amimoto",
    "columns": [
        {
            "key": "price",
            "type": "numeric",
            "goal": "min",
            "is_objective": true,
            "full_name": "Price",
            "range": {
                "low": 0,
                "high": 300000
            },
            "format": "currency: 'JPY¥' : 2"
        },{
            "key": "pv",
            "type": "numeric",
            "goal": "max",
            "is_objective": true,
            "full_name": "Monthly Pageview",
            "range": {
                "low": 10000000,
                "high": 20000000
            },
            "format": "number:2"
        },{
          "key": "db",
          "type": "categorical",
          "goal": "min",
          "is_objective": true,
          "full_name": "Database Type",
          "range": [
            "EC2",
            "RDS-Single",
            "RDS-Multi"
          ],
          "preference": [
            "EC2",
            "RDS-Single",
            "RDS-Multi"
          ]
        },{
            "key": "wp",
            "type": "numeric",
            "goal": "max",
            "is_objective": true,
            "full_name": "WordPress installation ",
            "range": {
                "low": 1,
                "high": 10
            }
        }
    ],
    "options": [
        {
            "key": "1",
            "name": "t2.micro",
            "values": {
                "price": 3000,
                "pv": 100000,
                "db": "EC2",
                "wp": 3
            }
        },{
            "key": "2",
            "name": "t2.small",
            "values": {
                "price": 6000,
                "pv": 300000,
                "db": "EC2",
                "wp": 3
            }
        },{
            "key": "3",
            "name": "t2.medium",
            "values": {
                "price": 15000,
                "pv": 500000,
                "db": "EC2",
                "wp": 3
            }
        },{
            "key": "4",
            "name": "c4.large",
            "values": {
                "price": 20000,
                "pv": 1000000,
                "db": "EC2",
                "wp": 5
            }
        },{
            "key": "5",
            "name": "c4.8large",
            "values": {
                "price": 350000,
                "pv": 20000000,
                "db": "EC2",
                "wp": 5
            }
        },{
            "key": "6",
            "name": "w-Small",
            "values": {
                "price": 80000,
                "pv": 3000000,
                "db": "RDS-Single",
                "wp": 5
            }
        },{
            "key": "7",
            "name": "w-Large",
            "values": {
                "price": 120000,
                "pv": 6000000,
                "db": "RDS-Single",
                "wp": 5
            }
        },{
            "key": "8",
            "name": "w-XLarge",
            "values": {
                "price": 160000,
                "pv": 10000000,
                "db": "RDS-Single",
                "wp": 10
            }
        },{
            "key": "9",
            "name": "w-2XLarge",
            "values": {
                "price": 320000,
                "pv": 20000000,
                "db": "RDS-Single",
                "wp": 10
            }
        },{
            "key": "10",
            "name": "HA-Small",
            "values": {
                "price": 120000,
                "pv": 3000000,
                "db": "RDS-Multi",
                "wp": 5
            }
        },{
            "key": "11",
            "name": "HA-Large",
            "values": {
                "price": 180000,
                "pv": 6000000,
                "db": "RDS-Multi",
                "wp": 5
            }
        },{
            "key": "12",
            "name": "HA-XLarge",
            "values": {
                "price": 200000,
                "pv": 10000000,
                "db": "RDS-Multi",
                "wp": 10
            }
        },{
            "key": "13",
            "name": "HA-2XLarge",
            "values": {
                "price": 460000,
                "pv": 20000000,
                "db": "RDS-Multi",
                "wp": 10
            }
        }
    ]
}

あとはこれをPOSTでwatson APIにぶん投げます。

Tradeoff Analytics APIで分析する

検索パラメータができたので、あとはAPIを使って解析します。

$ 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で見たい部分だけ抜き出します。

$ 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": "INCOMPLETE",
      "status_cause": {
        "message": "A column of a option is out of range. Option \"1\" has a value in column \"pv\" which is:\"100000\" while the column range\" is: [1.0E7,2.0E7]",
        "error_code": "RANGE_MISMATCH",
        "tokens": [
          "pv",
          "100000",
          "[1.0E7,2.0E7]"
        ]
      }
    },
    {
      "solution_ref": "2",
      "status": "INCOMPLETE",
      "status_cause": {
        "message": "A column of a option is out of range. Option \"2\" has a value in column \"pv\" which is:\"300000\" while the column range\" is: [1.0E7,2.0E7]",
        "error_code": "RANGE_MISMATCH",
        "tokens": [
          "pv",
          "300000",
          "[1.0E7,2.0E7]"
        ]
      }
    },
    {
      "solution_ref": "3",
      "status": "INCOMPLETE",
      "status_cause": {
        "message": "A column of a option is out of range. Option \"3\" has a value in column \"pv\" which is:\"500000\" while the column range\" is: [1.0E7,2.0E7]",
        "error_code": "RANGE_MISMATCH",
        "tokens": [
          "pv",
          "500000",
          "[1.0E7,2.0E7]"
        ]
      }
    },
    {
      "solution_ref": "4",
      "status": "INCOMPLETE",
      "status_cause": {
        "message": "A column of a option is out of range. Option \"4\" has a value in column \"pv\" which is:\"1000000\" while the column range\" is: [1.0E7,2.0E7]",
        "error_code": "RANGE_MISMATCH",
        "tokens": [
          "pv",
          "1000000",
          "[1.0E7,2.0E7]"
        ]
      }
    },
    {
      "solution_ref": "5",
      "status": "INCOMPLETE",
      "status_cause": {
        "message": "A column of a option is out of range. Option \"5\" has a value in column \"price\" which is:\"350000\" while the column range\" is: [0.0,300000.0]",
        "error_code": "RANGE_MISMATCH",
        "tokens": [
          "price",
          "350000",
          "[0.0,300000.0]"
        ]
      }
    },
    {
      "solution_ref": "6",
      "status": "INCOMPLETE",
      "status_cause": {
        "message": "A column of a option is out of range. Option \"6\" has a value in column \"pv\" which is:\"3000000\" while the column range\" is: [1.0E7,2.0E7]",
        "error_code": "RANGE_MISMATCH",
        "tokens": [
          "pv",
          "3000000",
          "[1.0E7,2.0E7]"
        ]
      }
    },
    {
      "solution_ref": "7",
      "status": "INCOMPLETE",
      "status_cause": {
        "message": "A column of a option is out of range. Option \"7\" has a value in column \"pv\" which is:\"6000000\" while the column range\" is: [1.0E7,2.0E7]",
        "error_code": "RANGE_MISMATCH",
        "tokens": [
          "pv",
          "6000000",
          "[1.0E7,2.0E7]"
        ]
      }
    },
    {
      "solution_ref": "8",
      "status": "FRONT"
    },
    {
      "solution_ref": "9",
      "status": "INCOMPLETE",
      "status_cause": {
        "message": "A column of a option is out of range. Option \"9\" has a value in column \"price\" which is:\"320000\" while the column range\" is: [0.0,300000.0]",
        "error_code": "RANGE_MISMATCH",
        "tokens": [
          "price",
          "320000",
          "[0.0,300000.0]"
        ]
      }
    },
    {
      "solution_ref": "10",
      "status": "INCOMPLETE",
      "status_cause": {
        "message": "A column of a option is out of range. Option \"10\" has a value in column \"pv\" which is:\"3000000\" while the column range\" is: [1.0E7,2.0E7]",
        "error_code": "RANGE_MISMATCH",
        "tokens": [
          "pv",
          "3000000",
          "[1.0E7,2.0E7]"
        ]
      }
    },
    {
      "solution_ref": "11",
      "status": "INCOMPLETE",
      "status_cause": {
        "message": "A column of a option is out of range. Option \"11\" has a value in column \"pv\" which is:\"6000000\" while the column range\" is: [1.0E7,2.0E7]",
        "error_code": "RANGE_MISMATCH",
        "tokens": [
          "pv",
          "6000000",
          "[1.0E7,2.0E7]"
        ]
      }
    },
    {
      "solution_ref": "12",
      "status": "EXCLUDED"
    },
    {
      "solution_ref": "13",
      "status": "INCOMPLETE",
      "status_cause": {
        "message": "A column of a option is out of range. Option \"13\" has a value in column \"price\" which is:\"460000\" while the column range\" is: [0.0,300000.0]",
        "error_code": "RANGE_MISMATCH",
        "tokens": [
          "price",
          "460000",
          "[0.0,300000.0]"
        ]
      }
    }
  ]
}

solutions.statusを確認していくと、solutions.solution_refが8のオブジェクトのみFRONTで、それ以外はINCOMPLETEEXCLUDEDになっています。

INCOMPLETEはrangeの範囲外の値が設定されているもの、EXCLUDEDはTradeoff Analyticsが最適解で無いと判断したもので、FRONTとなっているものがTradeoff Analyticsからレコメンドされるアイテムです。

ということで今回はsolutions.solution_refが8のプランが希望する条件に一致する様子です。
solutions.solution_refoptions.keyの値と同じなので、options.keyが8のアイテムを見てみましょう。

{
        "key": "8",
        "name": "w-XLarge",
        "values": {
          "price": 160000,
          "pv": 10000000,
          "db": "RDS-Single",
          "wp": 10
        }
      }

と、いうことで

項目名 範囲
価格 0~300,000円
PV 10,000,000~20,000,000 PV
DBタイプ EC2内DB > RDS1台 > RDSマルチAZ
WPインストール数 1 ~ 10

の条件に一致するプランは「w-XLarge」だということになりました。

"solution_ref": "12"EXCLUDEDになっていますが、これはDBタイプが「RDS-Multi」と一番低い優先順位になっているためだと思われます。

{
    "key": "12",
    "name": "HA-XLarge",
    "values": {
        "price": 200000,
        "pv": 10000000,
        "db": "RDS-Multi",
        "wp": 10
    }
}

optionskey: dbgoalをminではなくmaxに変更すると、このHA-XLargeプランもFRONTとなります。

まとめ

「複数の条件から最適なプランを探す」というのはシステム化するのが手間な印象がありましたが、Tradeoff Analyticsを使うことでJSONさえ作ればかなり簡単に作れることがわかりました。
あとはJSON生成の自動化やプランレコメンド調査用のUIを作るところのノウハウなどが集まるようになれば、もっと便利になるかなと思います。

ブックマークや限定記事(予定)など

WP Kyotoサポーター募集中

WordPressやフロントエンドアプリのホスティング、Algolia・AWSなどのサービス利用料を支援する「WP Kyotoサポーター」を募集しています。
月額または年額の有料プランを契約すると、ブックマーク機能などのサポーター限定機能がご利用いただけます。

14日間のトライアルも用意しておりますので、「このサイトよく見るな」という方はぜひご検討ください。

広告ここから
広告ここまで

Related Category posts