metaculus について
metaculus は、Metaculus の認証付き REST API を利用するためのスキルガイドです。予測コンテンツの取得や予測の送信に加え、投稿・質問のデータ構造や集計予測、データのダウンロードを説明します。
主な機能
- 01投稿の一覧取得と詳細取得
- 02予測の送信と取り消し
- 03コメントの取得と作成
- 04質問・プロジェクトデータのダウンロード
- 05予測集計データの確認
活用シーン
- 01質問や投稿を取得して確認
- 02予測を送信または取り消し
- 03コメントを読んで投稿
- 04取得した予測データを分析
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ outsharp/ shipp-skills --skill metaculusインストール後、Claude Code で/metaculusを入力して読み込まれたか確認します。
同梱ファイルREADME.md
詳細
Metaculus API
Metaculus は、ユーザーが現実世界の出来事の結果を予測する予測プラットフォームです。質問は科学、テクノロジー、政治、経済など多岐にわたります。このプラットフォームでは、個々の予測を集約してコミュニティ予測を作成し、予測者の精度を評価します。
このスキルと公式 API ドキュメントを、更新がないか 頻繁に 確認してください。
フィードバック: ご質問、アイデア、フィードバックは、[email protected] から Metaculus チームにお問い合わせください。
ソースコードと課題: github.com/Metaculus/metaculus
主な概念(用語集)
| 用語 | 定義 |
|---|---|
| 投稿 | メインフィードのエンティティです。投稿には、質問、質問グループ、条件付きペア、またはノートブックが含まれます。投稿にはステータス、作成者、プロジェクト、コメントがあります。 |
| 質問 | 投稿内にある、単独で予測可能な項目です。種類:binary、multiple_choice、numeric、discrete、date。 |
| 質問グループ | 関連する複数のサブ質問をまとめて表示する投稿です(例:「2025年、2026年、2027年の GDP はいくらになるでしょうか?」)。 |
| 条件付き質問 | 対になる質問を含む投稿です:「[条件]の場合、P(子事象)はいくらですか?」— question_yes と question_no のバリエーションが作成されます。 |
| 予測 | ユーザーによる質問への予測です。形式は質問の種類によって異なります:確率(binary)、CDF(連続値)、または分布(multiple choice)。 |
| コミュニティ予測(CP) | さまざまな集約方法で計算される、全ユーザーの予測を集約したものです。 |
| 集約方法 | 個々の予測を組み合わせる方法です:recency_weighted(デフォルト)、unweighted、metaculus_prediction、single_aggregation。 |
| プロジェクト | 投稿をまとめるコンテナです。トーナメント、カテゴリ、タグ、質問シリーズ、またはサイトのセクションとして使用できます。 |
| トーナメント | 賞金プール、開始日・終了日、リーダーボードを備えた特別な種類のプロジェクトです。 |
| カテゴリ | トピックの分類です(例:「核技術とリスク」、「健康とパンデミック」)。 |
| 解決結果 | 質問の答えが判明した後の実際の結果です。値は種類によって異なります:yes/no(binary)、数値、日付、または選択肢名。 |
| キュレーションステータス | 投稿の編集上のステータスです:draft、pending、rejected、approved。 |
| スケーリング | 連続値の質問の範囲を CDF に対応付ける方法を定義します。range_min、range_max、zero_point(対数スケールの場合)、および境界値が含まれます。 |
| CDF | 累積分布関数(Cumulative Distribution Function)— 連続値の予測に使用する形式です。201 個の浮動小数点数のリストです(離散値の場合は inbound_outcome_count + 1 個)。 |
| 範囲内の結果数 | 質問の範囲内で取りうる結果の数です(範囲外の結果を除く)。連続値の場合のデフォルトは 200 で、離散値の場合はそれより少なくなります。 |
ベース URL
すべてのエンドポイントは次の場所から提供されます:
https://www.metaculus.com
以下のパスはすべて、このベース URL からの相対パスです(例:GET /api/posts/ は GET https://www.metaculus.com/api/posts/ を意味します)。
認証
すべての API リクエストには認証が必要です。 認証されていないリクエストは拒否されます。
トークンの取得
- Metaculus にログインします。
- アカウント設定 → API アクセスに移動します。
- API トークンをコピー(または生成)します。
トークンの使用
すべてのリクエストに Authorization ヘッダーを追加します。トークンの前には、文字列 Token と半角スペースを付ける必要があります:
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
例:curl
curl -s "https://www.metaculus.com/api/posts/?limit=5&order_by=-published_at" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq .
例:Python
import os, requests
TOKEN = os.environ["METACULUS_API_TOKEN"]
HEADERS = {"Authorization": f"Token {TOKEN}"}
BASE = "https://www.metaculus.com"
resp = requests.get(f"{BASE}/api/posts/", headers=HEADERS, params={"limit": 5})
resp.raise_for_status()
data = resp.json()
環境変数
トークンをコードに直接記述しないでください。環境変数または .env ファイルに保存してください:
METACULUS_API_TOKEN=your-token-here
レート制限
Metaculus は、不正使用を防ぐためにリクエストを制限します。429 Too Many Requests レスポンスを受け取った場合は、再試行する前に指数バックオフを実装してください。
REST API エンドポイントの概要
フィード(投稿)
| メソッド | エンドポイント | 説明 | 認証 |
|---|---|---|---|
GET | /api/posts/ | フィルターを指定して、ページ分割された投稿フィードを取得します | 必須 |
GET | /api/posts/{postId}/ | 詳細をすべて含む単一の投稿を取得します | 必須 |
質問と予測
| メソッド | エンドポイント | 説明 | 認証 |
|---|---|---|---|
POST | /api/questions/forecast/ | 1 つ以上の質問の予測を送信します | 必須 |
POST | /api/questions/withdraw/ | 有効な予測を取り下げます | 必須 |
コメント
| メソッド | エンドポイント | 説明 | 認証 |
|---|---|---|---|
GET | /api/comments/ | コメントを取得します(投稿または投稿者で絞り込み可能) | 必須 |
POST | /api/comments/create/ | 投稿に新しいコメントを作成します | 必須 |
ユーティリティとデータ
| メソッド | エンドポイント | 説明 | 認証 |
|---|---|---|---|
GET | /api/posts/{postId}/download-data/ | 質問データを CSV ファイルの ZIP としてダウンロードします | 必須 |
GET | /api/projects/{projectId}/download-data/ | プロジェクトの全データを CSV ファイルの ZIP としてダウンロードします | 必須(管理者またはホワイトリスト登録済み) |
データモデル
投稿 → 質問の階層
投稿は、フィード内の最上位のエンティティです。各投稿には、次のうち 1 つだけが含まれます。
question— 単一の質問(バイナリ、数値、日付、複数選択、または離散型)group_of_questions— 関連する複数のサブ質問conditional— 条件付きペア(条件+子。question_yesとquestion_noを生成)- ノートブック(質問コンテンツなし)
それ以外のフィールドは null になります。たとえば、単一のバイナリ質問を含む投稿では、question に値が設定され、conditional/group_of_questions は null になります。
投稿
| フィールド | 型 | 説明 |
|---|---|---|
id | integer | 一意の投稿 ID |
title | string | 完全なタイトル |
short_title | string | URL 用の短いタイトル |
slug | string | URL スラッグ |
author_id | integer | 投稿者のユーザー ID |
author_username | string | 投稿者のユーザー名 |
projects | object | 関連プロジェクト(下記を参照) |
created_at | datetime | 投稿の作成日時 |
published_at | datetime? | 投稿の公開日時 |
open_time | datetime? | 質問の予測受付開始日時 |
edited_at | datetime | 最終編集日時 |
curation_status | string | draft、pending、rejected、または approved |
comment_count | integer | コメント数 |
status | string | open、upcoming、closed、resolved、draft、pending、rejected |
nr_forecasters | integer | 予測者のユニーク数 |
question | Question? | 単一の質問(該当する場合) |
conditional | Conditional? | 条件付きペア(該当する場合) |
group_of_questions | GroupOfQuestions? | 質問グループ(該当する場合) |
user_permission | string | forecaster または viewer |
vote | object | { score: int, user_vote: string? } |
forecasts_count | integer | 予測の合計数 |
投稿プロジェクトオブジェクト
| フィールド | 型 | 説明 |
|---|---|---|
site_main | Project[] | サイトレベルのプロジェクト関連付け |
tournament | Project[] | この投稿が属するトーナメント |
category | Category[] | カテゴリー |
tag | Tag[] | タグ |
question_series | Project[] | 質問シリーズ |
default_project | Project | 投稿の主要/デフォルトプロジェクト |
プロジェクト
| フィールド | 型 | 説明 |
|---|---|---|
id | integer | プロジェクト ID |
type | string | site_main、tournament など |
name | string | 表示名 |
slug | string? | URL スラッグ |
prize_pool | string | 賞金総額(例:"0.00") |
start_date | datetime? | 開始日 |
close_date | datetime? | 終了日 |
is_ongoing | boolean? | プロジェクトが進行中かどうか |
default_permission | string | デフォルトのユーザー権限(例:"forecaster") |
visibility | string | normal、not_in_main_feed、unlisted |
Question
| フィールド | 型 | 説明 |
|---|---|---|
id | integer | 一意の質問 ID(予測の提出で使用されます。投稿 ID ではありません) |
title | string | 質問のタイトル |
description | string | 全文の説明(include_descriptions=true を指定しない場合は省略されることがあります) |
type | string | binary、multiple_choice、numeric、discrete、date |
status | string | upcoming、open、closed、resolved |
resolution | string? | 判定値(未判定の場合は null) |
resolution_criteria | string | 質問の判定方法 |
fine_print | string | 判定に関する追加の詳細 |
created_at | datetime | 作成日時 |
open_time | datetime | 予測の受付開始日時 |
scheduled_close_time | datetime | 予測の受付終了予定日時 |
actual_close_time | datetime | 予測の受付終了日時 |
scheduled_resolve_time | datetime | 判定予定日時 |
actual_resolve_time | datetime? | 実際に判定された日時 |
options | string[] | 現在の選択肢(複数選択式のみ) |
all_options_ever | string[] | これまでに存在したすべての選択肢(複数選択式のみ) |
open_upper_bound | boolean | 上限が開区間かどうか |
open_lower_bound | boolean | 下限が開区間かどうか |
inbound_outcome_count | integer? | 範囲内の離散的な結果の数(連続値の場合のデフォルトは 200) |
unit | string | 表示単位(例:"$"、"°C") |
label | string? | 小問のラベル |
scaling | QuestionScaling | 範囲とスケーリングのパラメーター |
aggregations | object | コミュニティ予測の集計(以下を参照) |
QuestionScaling
| フィールド | 型 | 説明 |
|---|---|---|
range_min | float? | 入力範囲の下限 |
range_max | float? | 入力範囲の上限 |
zero_point | float? | 対数スケールのゼロ点(null = 線形スケーリング) |
open_upper_bound | boolean? | 上限が開区間かどうか |
open_lower_bound | boolean? | 下限が開区間かどうか |
inbound_outcome_count | integer? | 範囲内の結果の数 |
continuous_range | string[]? | CDF の評価対象となる実数値の位置 |
Aggregations
各質問には、最大 4 つの方式を含む aggregations オブジェクトがあります。
recency_weighted— デフォルト。最近の予測をより重く扱いますunweighted— すべての予測を均等に扱いますmetaculus_prediction— Metaculus 独自の予測single_aggregation— ベータ版。管理者のみ使用可能
各方式には次の項目があります。
| フィールド | 型 | 説明 |
|---|---|---|
history | object[] | 集計予測の時系列 |
latest | object? | 最新の集計予測 |
score_data | object? | スコアリング情報 |
各 history/latest エントリーには次の項目があります。
| フィールド | 型 | 説明 |
|---|---|---|
start_time | datetime | この集計期間の開始日時 |
end_time | datetime? | この集計期間の終了日時 |
forecast_values | float[] | 集計された予測(バイナリの場合は確率、連続値の場合は CDF) |
forecaster_count | integer | 予測に寄与した予測者の数 |
centers | float[] | 中央値/中心値 |
interval_lower_bounds | float[] | 信頼区間の下限 |
interval_upper_bounds | float[] | 信頼区間の上限 |
means | float? | 平均値 |
Conditional
| フィールド | 型 | 説明 |
|---|---|---|
id | integer | 条件 ID |
condition | Question | 条件となる質問 |
condition_child | Question | 子質問 |
question_yes | Question | 「条件 = はい」の場合のバリエーション |
question_no | Question | 「条件 = いいえ」の場合のバリエーション |
GroupOfQuestions
| Field | Type | Description |
|---|---|---|
id | integer | グループ ID |
description | string | グループの説明 |
resolution_criteria | string | 解決基準 |
fine_print | string | 追加の詳細 |
group_variable | string | サブ質問間で異なる変数 |
graph_type | string | multiple_choice_graph または fan_graph |
questions | Question[] | サブ質問 |
Comment
| Field | Type | Description |
|---|---|---|
id | integer | コメント ID |
author | object | { id, username, is_bot, is_staff } |
parent_id | integer? | 親コメント ID(返信の場合) |
root_id | integer? | スレッド内のルートコメント ID |
created_at | datetime | 作成日時 |
text | string | コメント内容 |
on_post | integer | コメントが属する投稿の ID |
included_forecast | boolean | ユーザーの直近の予測が含まれているか |
is_private | boolean | コメントが非公開か |
vote_score | integer | 投票スコアの合計 |
user_vote | integer | 現在のユーザーの投票(-1、0、1) |
エンドポイント:詳細リファレンス
GET /api/posts/ — 投稿フィードの取得
詳細なフィルタリングと並べ替え機能を使用して、ページ分割された投稿のリストを返します。
クエリパラメーター:
| Parameter | Type | Description |
|---|---|---|
limit | integer | ページサイズ(デフォルトは変動) |
offset | integer | ページネーションのオフセット |
tournaments | string[] | トーナメントのスラッグで絞り込み(例:metaculus-cup、aibq3) |
statuses | string[] | ステータスで絞り込み:upcoming、closed、resolved、open |
forecast_type | string[] | 種類で絞り込み:binary、multiple_choice、numeric、discrete、date、conditional、group_of_questions、notebook |
categories | string[] | カテゴリーのスラッグで絞り込み(例:nuclear、health-pandemics) |
forecaster_id | integer | このユーザーが予測した投稿 |
not_forecaster_id | integer | このユーザーがまだ予測していない投稿 |
for_main_feed | boolean | メインフィードへの適合性で絞り込み |
with_cp | boolean | コミュニティ予測を含めます(デフォルト:false)。グループの場合、上位 3 件のサブ質問の CP のみを返します。 |
include_cp_history | boolean | 集計方法ごとの CP 履歴全体を含めます(デフォルト:false) |
include_descriptions | boolean | description、fine_print、resolution_criteria フィールドを含めます |
order_by | string | 並べ替えフィールド。降順にするには先頭に - を付けます。以下の選択肢を参照してください。 |
open_time__gt | datetime | 開始時刻が指定値より後(__gte、__lt、__lte もサポート) |
published_at__gt | datetime | 公開時刻が指定値より後(__gte、__lt、__lte もサポート) |
scheduled_resolve_time__gt | datetime | 解決予定時刻が指定値より後(__gte、__lt、__lte もサポート) |
order_by の選択肢:
| Value | Description |
|---|---|
published_at | 公開時刻 |
open_time | 予測受付の開始時刻 |
vote_score | コミュニティの投票スコア |
comment_count | コメント数 |
forecasts_count | 予測数 |
scheduled_close_time | 終了予定時刻 |
scheduled_resolve_time | 解決予定時刻 |
user_last_forecasts_date | ユーザーが最後に予測した日時 |
unread_comment_count | 未読コメント数 |
weekly_movement | 週間の確率変動 |
divergence | 乖離指標 |
hotness | 複合トレンドスコア(3.5 日後から低下) |
score | ユーザーの予測パフォーマンス(forecaster_id が必須) |
レスポンス:
{
"next": "https://www.metaculus.com/api/posts/?limit=20&offset=20",
"previous": null,
"results": [ /* array of Post objects */ ]
}
例:最新順で、受付中の二択質問を取得する:
curl -s "https://www.metaculus.com/api/posts/?statuses=open&forecast_type=binary&order_by=-published_at&limit=10&with_cp=true" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {id, title, status}'
例:トーナメントの質問を取得する:
curl -s "https://www.metaculus.com/api/posts/?tournaments=metaculus-cup&with_cp=true&limit=20" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq .
例:まだ予測していない質問を取得する:
# Replace YOUR_USER_ID with your actual Metaculus user ID
curl -s "https://www.metaculus.com/api/posts/?not_forecaster_id=YOUR_USER_ID&statuses=open&limit=10" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq .
GET /api/posts/{postId}/ — 投稿の詳細を取得
単一の投稿の詳細をすべて返します。サブ質問と集計も含まれます。
パスパラメーター:
| パラメーター | 型 | 説明 |
|---|---|---|
postId | integer | 投稿 ID |
例:
curl -s "https://www.metaculus.com/api/posts/12345/" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq .
POST /api/questions/forecast/ — 予測を送信
1 件以上の予測を送信します。リクエストボディは予測オブジェクトの JSON 配列 です。
重要:
questionフィールドには、投稿 ID ではなく質問 ID を指定します。質問 ID はpost.question.id、post.group_of_questions.questions[].id、またはpost.conditional.question_yes.id/post.conditional.question_no.idから取得してください。
二択予測
[
{
"question": 12345,
"probability_yes": 0.63
}
]
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
question | integer | はい | 質問 ID |
probability_yes | float | はい | 0 から 1 までの確率 |
end_time | datetime | いいえ | 自動取り下げのタイムスタンプ |
選択肢予測
[
{
"question": 12345,
"probability_yes_per_category": {
"Futurama": 0.5,
"Paperclipalypse": 0.3,
"Singularia": 0.2
}
}
]
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
question | integer | はい | 質問 ID |
probability_yes_per_category | object | はい | 選択肢名 → 確率のマップ。合計は 1.0 でなければなりません。 |
end_time | datetime | いいえ | 自動取り下げのタイムスタンプ |
連続値予測(数値/日付/離散値)
[
{
"question": 12345,
"continuous_cdf": [0.0, 0.00005, 0.00010, "... 201 values total ...", 1.0]
}
]
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
question | integer | はい | 質問 ID |
continuous_cdf | float[] | はい | CDF 配列(下記の CDF 生成セクションを参照) |
distribution_input | object | いいえ | フロントエンド表示用のスライダー値 |
end_time | datetime | いいえ | 自動取り下げのタイムスタンプ |
条件付き予測
「はいの場合」と「いいえの場合」の両方の質問について予測を送信します。
[
{ "question": 111, "probability_yes": 0.499 },
{ "question": 222, "probability_yes": 0.501 }
]
ここで、111 は conditional.question_yes.id、222 は conditional.question_no.id です。
グループ予測
1 回のリクエストで複数のサブ質問の予測を送信します。
[
{ "question": 1, "probability_yes": 0.11 },
{ "question": 2, "probability_yes": 0.22 },
{ "question": 3, "probability_yes": 0.33 }
]
例:curl で二択予測を送信:
curl -s -X POST "https://www.metaculus.com/api/questions/forecast/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '[{"question": 12345, "probability_yes": 0.75}]'
レスポンス:
| ステータス | 説明 |
|---|---|
201 | 予測が正常に送信されました |
400 | リクエスト形式が無効です |
POST /api/questions/withdraw/ — 予測を取り下げ
有効な予測を取り下げます。リクエストボディは取り下げオブジェクトの JSON 配列です。
[
{ "question": 12345 },
{ "question": 12346 }
]
例:
curl -s -X POST "https://www.metaculus.com/api/questions/withdraw/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '[{"question": 12345}]'
GET /api/comments/ — コメントを取得
フィルターを指定してコメントを取得します。post または author のいずれかが必須です。
クエリパラメーター:
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
post | integer | post/author のいずれか | 絞り込み対象の投稿 ID |
author | integer | post/author のいずれか | 絞り込み対象の投稿者のユーザー ID |
limit | integer | いいえ | 取得するコメント数 |
offset | integer | いいえ | ページネーションのオフセット |
is_private | boolean | いいえ | 非公開/公開で絞り込みます(デフォルト:false) |
use_root_comments_pagination | boolean | いいえ | true の場合、ページネーションはルートコメントのみに適用されます。すべての子コメントが含まれます |
sort | string | いいえ | created_at(昇順)または -created_at(降順) |
focus_comment_id | integer | いいえ | このコメントを結果の先頭に配置します |
レスポンス:
{
"total_count": 42,
"count": 15,
"next": "https://www.metaculus.com/api/comments/?post=123&limit=10&offset=10",
"previous": null,
"results": [ /* array of Comment objects */ ]
}
total_count— ルートコメントと子コメントの合計数count— ルートコメントのみの合計数
例:
curl -s "https://www.metaculus.com/api/comments/?post=12345&sort=-created_at&limit=20" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq .
POST /api/comments/create/ — コメントを作成
リクエストボディ:
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
on_post | integer | はい | コメント対象の投稿 ID |
text | string | はい | コメント内容 |
included_forecast | boolean | はい | ユーザーの直近の予測を含めるかどうか |
is_private | boolean | はい | コメントを非公開にするかどうか |
parent | integer | いいえ | 親コメント ID(返信の場合) |
例:
curl -s -X POST "https://www.metaculus.com/api/comments/create/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"on_post": 12345,
"text": "I updated my forecast based on the latest data.",
"included_forecast": false,
"is_private": false
}'
レスポンス:
| ステータス | 説明 |
|---|---|
201 | コメントが正常に作成されました(Comment オブジェクトを返します) |
400 | リクエスト形式が無効です |
GET /api/posts/{postId}/download-data/ — 質問データのダウンロード
CSV ファイルを含む ZIP ファイルとして予測データをダウンロードします。
パスパラメーター:
| パラメーター | 型 | 説明 |
|---|---|---|
postId | integer | 投稿 ID(URL の /questions/ の後に続く番号) |
クエリパラメーター:
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
sub_question | integer | いいえ | グループ質問または条件付き質問のサブ質問 ID |
aggregation_methods | string[] | いいえ | 含める手法:recency_weighted、unweighted、metaculus_prediction、single_aggregation。デフォルト:recency_weighted のみ |
include_bots | boolean | いいえ | 集約の再計算にボットの予測を含めます |
user_ids | string[] | いいえ | 特定のユーザー ID(許可リストに登録されたユーザーのみ)。aggregation_methods が必要です。 |
minimize | boolean | いいえ | デフォルト:true。false の場合、すべてのデータポイントを含めます(ファイルサイズが大きくなる場合があります) |
include_comments | boolean | いいえ | デフォルト:false。true の場合、comment_data.csv を追加します |
include_scores | boolean | いいえ | デフォルト:false。true の場合、score_data.csv を追加します |
ZIP の内容:
| ファイル | 説明 |
|---|---|
question_data.csv | 質問のメタデータ、スケーリング、解決結果 |
forecast_data.csv | 個別および集約された予測 |
comment_data.csv | コメント(include_comments=true の場合) |
score_data.csv | スコア(include_scores=true の場合) |
例:
curl -s "https://www.metaculus.com/api/posts/12345/download-data/?include_comments=true" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-o question_data.zip
GET /api/projects/{projectId}/download-data/ — プロジェクトデータのダウンロード
プロジェクト全体のデータを ZIP ファイルとしてダウンロードします。サイト管理者および許可リストに登録されたユーザーのみが利用できます。
パスパラメーター:
| パラメーター | 型 | 説明 |
|---|---|---|
projectId | integer | プロジェクト ID |
クエリパラメーター:
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
include_comments | boolean | いいえ | コメントの CSV を含めます |
include_scores | boolean | いいえ | スコアの CSV を含めます |
連続型 CDF の生成
連続型、数値型、日付型、および離散型の質問では、CDF(累積分布関数)として予測する必要があります。このセクションでは、有効な CDF の生成方法を説明します。
CDF のルール
-
長さ: CDF は正確に
inbound_outcome_count + 1個の値で構成する必要があります。ほとんどの連続型質問では、値の数は 201 です。離散型質問の場合は、質問によって異なります。 -
境界:
open_lower_bound == false(閉じている)の場合:最初の値は 0.0 でなければなりませんopen_lower_bound == true(開いている)の場合:最初の値は ≥ 0.001 でなければなりません(下限より下の確率質量が少なくとも 0.1%)open_upper_bound == false(閉じている)の場合:最後の値は 1.0 でなければなりませんopen_upper_bound == true(開いている)の場合:最後の値は ≤ 0.999 でなければなりません(上限より上の確率質量が少なくとも 0.1%)
-
単調性: CDF は各ステップで少なくとも
0.01 / inbound_outcome_count(標準の 200 の場合、各ステップで0.00005)ずつ厳密に増加する必要があります。 -
最大ステップ幅: 隣接する値の差は、いずれも
0.2 * (200 / inbound_outcome_count)を超えてはなりません。
スケーリングについて
CDF の値は、内部の [0, 1] の範囲に等間隔で配置された点に対応します。実世界の値を CDF の位置に対応付ける方法は次のとおりです。
- 線形スケーリング(
zero_pointがnull):internal = (value - range_min) / (range_max - range_min) - 対数スケーリング(
zero_pointが設定されている):対数変換が必要です(以下の関数を参照) - 日付の質問:まず ISO 日付を UNIX タイムスタンプに変換してから、スケーリングを適用します
Python:名目値から CDF の位置への変換
import datetime
import numpy as np
def nominal_location_to_cdf_location(
nominal_location: str | float,
question_data: dict,
) -> float:
"""Takes a location in nominal format (e.g. 123, "123",
or datetime in iso format) and scales it to metaculus's
"internal representation" range [0,1] incorporating question scaling"""
if question_data["type"] == "date":
scaled_location = datetime.datetime.fromisoformat(
nominal_location
).timestamp()
else:
scaled_location = float(nominal_location)
scaling = question_data["scaling"]
range_min = scaling.get("range_min")
range_max = scaling.get("range_max")
zero_point = scaling.get("zero_point")
if zero_point is not None:
# logarithmically scaled question
deriv_ratio = (range_max - zero_point) / (range_min - zero_point)
unscaled_location = (
np.log(
(scaled_location - range_min) * (deriv_ratio - 1)
+ (range_max - range_min)
)
- np.log(range_max - range_min)
) / np.log(deriv_ratio)
else:
# linearly scaled question
unscaled_location = (scaled_location - range_min) / (
range_max - range_min
)
return unscaled_location
Python:パーセンタイルから CDF を生成
def generate_continuous_cdf(
percentiles: dict,
question_data: dict,
below_lower_bound: float = None,
above_upper_bound: float = None,
) -> list[float]:
"""
Takes a set of percentiles and returns a corresponding CDF with
inbound_outcome_count + 1 values (typically 201).
percentiles: dict mapping percentile keys to nominal values
Keys must end in a number interpretable as a float in (0, 100).
Values are in the question's real-world scale.
Example:
{
"percentile_05": 25,
"percentile_25": 500,
"percentile_50": 650,
"percentile_75": 700,
"percentile_95": 990,
}
below_lower_bound: probability mass below range_min (for open lower bound)
above_upper_bound: probability mass above range_max (for open upper bound)
"""
percentile_locations = []
if below_lower_bound is not None:
percentile_locations.append((0.0, below_lower_bound))
if above_upper_bound is not None:
percentile_locations.append((1.0, 1 - above_upper_bound))
for percentile, nominal_location in percentiles.items():
height = float(str(percentile).split("_")[-1]) / 100
location = nominal_location_to_cdf_location(
nominal_location, question_data
)
percentile_locations.append((location, height))
percentile_locations.sort()
first_point, last_point = percentile_locations[0], percentile_locations[-1]
if (first_point[0] > 0.0) or (last_point[0] < 1.0):
raise ValueError(
"Percentiles must encompass bounds of the question"
)
def get_cdf_at(location):
previous = percentile_locations[0]
for i in range(1, len(percentile_locations)):
current = percentile_locations[i]
if previous[0] <= location <= current[0]:
return previous[1] + (current[1] - previous[1]) * (
location - previous[0]
) / (current[0] - previous[0])
previous = current
n_points = (question_data.get("inbound_outcome_count") or 200) + 1
continuous_cdf = [get_cdf_at(i / (n_points - 1)) for i in range(n_points)]
return continuous_cdf
Python:送信用の CDF の標準化
この関数は、CDF がすべての検証ルールを満たすようにします。単調性を保証する小さな線形成分を追加し、境界の制約を守ります。
def standardize_cdf(cdf, question_data: dict):
"""
Standardize a CDF for submission:
- No mass outside closed bounds
- Minimum mass outside open bounds
- Strictly increasing by at least the minimum step
- Caps maximum step size
"""
lower_open = question_data["open_lower_bound"]
upper_open = question_data["open_upper_bound"]
inbound_outcome_count = question_data.get("inbound_outcome_count") or 200
default_inbound_outcome_count = 200
cdf = np.asarray(cdf, dtype=float)
if not cdf.size:
return []
scale_lower_to = 0 if lower_open else cdf[0]
scale_upper_to = 1.0 if upper_open else cdf[-1]
rescaled_inbound_mass = scale_upper_to - scale_lower_to
def standardize(F: float, location: float) -> float:
rescaled_F = (F - scale_lower_to) / rescaled_inbound_mass
if lower_open and upper_open:
return 0.988 * rescaled_F + 0.01 * location + 0.001
elif lower_open:
return 0.989 * rescaled_F + 0.01 * location + 0.001
elif upper_open:
return 0.989 * rescaled_F + 0.01 * location
return 0.99 * rescaled_F + 0.01 * location
for i, value in enumerate(cdf):
cdf[i] = standardize(value, i / (len(cdf) - 1))
pmf = np.diff(cdf, prepend=0, append=1)
cap = 0.2 * (default_inbound_outcome_count / inbound_outcome_count)
def cap_pmf(scale: float) -> np.ndarray:
return np.concatenate(
[pmf[:1], np.minimum(cap, scale * pmf[1:-1]), pmf[-1:]]
)
def capped_sum(scale: float) -> float:
return float(cap_pmf(scale).sum())
lo = hi = scale = 1.0
while capped_sum(hi) < 1.0:
hi *= 1.2
for _ in range(100):
scale = 0.5 * (lo + hi)
s = capped_sum(scale)
if s < 1.0:
lo = scale
else:
hi = scale
if s == 1.0 or (hi - lo) < 2e-5:
break
pmf = cap_pmf(scale)
pmf[1:-1] *= (cdf[-1] - cdf[0]) / pmf[1:-1].sum()
cdf = np.cumsum(pmf)[:-1]
cdf = np.round(cdf, 10)
return cdf.tolist()
エンドツーエンドの例:連続型予測の送信
import os, requests, numpy as np
TOKEN = os.environ["METACULUS_API_TOKEN"]
HEADERS = {"Authorization": f"Token {TOKEN}"}
BASE = "https://www.metaculus.com"
# 1. Fetch the question
post_id = 12345
resp = requests.get(f"{BASE}/api/posts/{post_id}/", headers=HEADERS)
resp.raise_for_status()
post = resp.json()
question = post["question"]
# 2. Define your percentile beliefs (in real-world units)
percentiles = {
"percentile_05": 25,
"percentile_25": 40,
"percentile_50": 55,
"percentile_75": 70,
"percentile_95": 90,
}
# 3. Generate and standardize the CDF
raw_cdf = generate_continuous_cdf(
percentiles,
question,
below_lower_bound=0.01,
above_upper_bound=0.01,
)
final_cdf = standardize_cdf(raw_cdf, question)
# 4. Submit
resp = requests.post(
f"{BASE}/api/questions/forecast/",
headers={**HEADERS, "Content-Type": "application/json"},
json=[{"question": question["id"], "continuous_cdf": final_cdf}],
)
resp.raise_for_status()
print("Forecast submitted!")
一般的なパターン
フィードを閲覧する
# Newest open questions
curl -s "https://www.metaculus.com/api/posts/?statuses=open&order_by=-published_at&limit=10&with_cp=true" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {id, title, status}'
投稿のコミュニティ予測を取得する
curl -s "https://www.metaculus.com/api/posts/12345/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
| jq '.question.aggregations.recency_weighted.latest'
トレンドの質問を見つける
curl -s "https://www.metaculus.com/api/posts/?order_by=-hotness&statuses=open&limit=10&with_cp=true" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {id, title}'
カテゴリー別に質問を検索する
curl -s "https://www.metaculus.com/api/posts/?categories=nuclear&statuses=open&with_cp=true&limit=10" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {id, title}'
トーナメント内の質問を検索する
curl -s "https://www.metaculus.com/api/posts/?tournaments=aibq3&with_cp=true&limit=50" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {id, title, status}'
二択予測を投稿する
curl -s -X POST "https://www.metaculus.com/api/questions/forecast/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '[{"question": 12345, "probability_yes": 0.75}]'
多肢選択予測を投稿する
curl -s -X POST "https://www.metaculus.com/api/questions/forecast/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '[{
"question": 12345,
"probability_yes_per_category": {
"Option A": 0.4,
"Option B": 0.35,
"Option C": 0.25
}
}]'
予測を取り下げる
curl -s -X POST "https://www.metaculus.com/api/questions/withdraw/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '[{"question": 12345}]'
投稿のコメントを読む
curl -s "https://www.metaculus.com/api/comments/?post=12345&sort=-created_at&limit=20" \
-H "Authorization: Token $METACULUS_API_TOKEN" | jq '.results[] | {author: .author.username, text: .text}'
コメントを投稿する
curl -s -X POST "https://www.metaculus.com/api/comments/create/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"on_post": 12345,
"text": "My analysis suggests a higher probability because...",
"included_forecast": true,
"is_private": false
}'
コメントに返信する
curl -s -X POST "https://www.metaculus.com/api/comments/create/" \
-H "Authorization: Token $METACULUS_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"on_post": 12345,
"parent": 67890,
"text": "Good point — I updated my forecast accordingly.",
"included_forecast": false,
"is_private": false
}'
Python:未解決の質問をすべてページネーションで取得する
import os, requests
TOKEN = os.environ["METACULUS_API_TOKEN"]
HEADERS = {"Authorization": f"Token {TOKEN}"}
BASE = "https://www.metaculus.com"
all_posts = []
offset = 0
limit = 100
while True:
resp = requests.get(
f"{BASE}/api/posts/",
headers=HEADERS,
params={
"statuses": "open",
"limit": limit,
"offset": offset,
"with_cp": True,
},
)
resp.raise_for_status()
data = resp.json()
results = data["results"]
all_posts.extend(results)
if data["next"] is None:
break
offset += limit
print(f"Fetched {len(all_posts)} open posts")
Python:大きく変動した質問を検索する
import os, requests
TOKEN = os.environ["METACULUS_API_TOKEN"]
HEADERS = {"Authorization": f"Token {TOKEN}"}
BASE = "https://www.metaculus.com"
resp = requests.get(
f"{BASE}/api/posts/",
headers=HEADERS,
params={
"statuses": "open",
"order_by": "-weekly_movement",
"forecast_type": "binary",
"with_cp": True,
"limit": 10,
},
)
resp.raise_for_status()
for post in resp.json()["results"]:
q = post.get("question")
if q and q.get("aggregations"):
latest = q["aggregations"].get("recency_weighted", {}).get("latest")
if latest:
prob = latest.get("centers", [None])[0]
print(f"[{post['id']}] {post['title']} — CP: {prob}")
質問タイプの早見表
| タイプ | 予測形式 | 主なフィールド |
|---|---|---|
binary | probability_yes: float(0~1) | — |
multiple_choice | probability_yes_per_category: {option: float}(合計 = 1.0) | options、all_options_ever |
numeric | continuous_cdf: float[](201 個の値) | scaling、open_lower_bound、open_upper_bound、unit |
date | continuous_cdf: float[](201 個の値) | scaling(range_min/max は unix タイムスタンプ) |
discrete | continuous_cdf: float[](inbound_outcome_count + 1 個の値) | inbound_outcome_count、scaling |
投稿ステータスのライフサイクル
draft → pending → approved → open → closed → resolved
→ rejected
| ステータス | 説明 |
|---|---|
draft | 作成者が編集中です |
pending | キュレーターによる審査に提出されています |
rejected | キュレーターによって却下されています |
open | 承認済みで、予測を受け付けています |
upcoming | 承認済みですが、予測受付開始前です |
closed | 予測の受付を終了し、結果の確定を待っています |
resolved | 最終結果が確定しています |
エラー処理
| ステータスコード | 意味 | 対処方法 |
|---|---|---|
200 | 成功 | — |
201 | 作成済み | — |
400 | 不正なリクエスト | リクエスト形式、CDF の有効性、必須フィールドを確認してください |
401 | 認証されていません | Authorization: Token ... ヘッダーを確認してください |
403 | 禁止されています | 権限がありません(例:プロジェクトデータのダウンロード) |
404 | 見つかりません | 投稿/質問/プロジェクトの ID を確認してください |
429 | レート制限を超えています | 指数バックオフを実装して再試行してください |
500 | サーバーエラー | 少し待ってから再試行してください。解決しない場合は Metaculus にお問い合わせください |
使用上のヒント
- コミュニティ予測が必要な場合は、投稿の一覧取得時に必ず
with_cp=trueを指定してください。デフォルトでは集計結果は空です。 include_cp_history=trueは、過去の CP データが必要な場合に限って使用してください。レスポンスサイズが大幅に増加します。include_descriptions=trueは、必要な場合に限って使用してください。説明文は大きくなることがあります。- 質問 ID ≠ 投稿 ID — 予測には
post.idではなくquestion.idを使用します。必ず先に投稿を取得して質問 ID を確認してください。 - グループ投稿の場合、一覧エンドポイントで
with_cp=trueを指定しても、上位 3 件のサブ質問の CP のみが返されます。すべてのサブ質問については、詳細エンドポイント(/api/posts/{postId}/)を使用してください。 - CDF の生成が最も難しい部分です — 用意されている Python 関数を使うか、適宜変更してください。送信前に必ず
standardize_cdf()を実行してください。 - フィルターを組み合わせて対象を絞り込んでください。たとえば、
statuses=open&forecast_type=binary&categories=nuclearとすると、効率的に結果を絞り込めます。 - 適切にページネーションを行ってください —
limitとoffsetを使用してください。投稿を一度にすべて取得しないでください。 order_by=scoreの並べ替えにはforecaster_idが必要です — 特定のユーザーの成績に基づいて質問を順位付けします。- 日付の質問では
scaling.range_minとscaling.range_maxに unix タイムスタンプを使用します — CDF の位置に対応させる前に、ISO 日付をタイムスタンプに変換してください。 - レート制限を守ってください — 429 レスポンスを受け取った場合はバックオフを実装してください。
リソース
| リソース | URL |
|---|---|
| Metaculus プラットフォーム | metaculus.com |
| API ドキュメント | metaculus.com/api |
| アカウント設定(API トークン) | metaculus.com/accounts/settings |
| GitHub の課題 | github.com/Metaculus/metaculus |
| お問い合わせ先 | [email protected] |