gjo-calibration/strmlt.py

174 lines
6.9 KiB
Python
Raw Normal View History

2021-05-31 18:59:24 +00:00
import numpy as np
import pandas as pd
import streamlit as st
import uncurl
from calibration import overconfidence
from firebase_requests import get_forecasts, get_resolutions
from gjo_requests import get_resolved_questions
from plotting import plotly_calibration, plotly_calibration_odds
2021-06-01 14:41:22 +00:00
2021-05-31 18:59:24 +00:00
if __name__ == "__main__":
2021-06-01 14:28:05 +00:00
st.set_page_config(page_title="How calibrated are you?", page_icon="🦊")
st.title("🦊 How calibrated are you?")
2021-05-31 18:59:24 +00:00
# ---
2021-06-01 14:28:35 +00:00
st.sidebar.header("Welcome!")
st.sidebar.write("Good calibration is vital for good judgemental forecasting. "
"When a calibrated forecaster predicts 70% on 10 quesions, we actually expect "
"around 7 of these to resolve positively. Unfortunately, there is "
2021-06-01 15:18:59 +00:00
"no easy way to see which fraction of our 70% forecasts resolves "
"positively on Good Judgement Open. I hence made this web app.")
2021-06-01 14:28:35 +00:00
st.sidebar.subheader("On cURL")
st.sidebar.write("I use your cookies for gathering information from GJO: which questions did you forecast on; what did you forecast on; how did they resolve.")
2021-06-01 15:18:59 +00:00
st.sidebar.write("I do not use them for other purposes, neither do I store them. The code is on [github](https://github.com/yagudin/gjo-calibration).")
2021-06-01 14:28:35 +00:00
st.sidebar.write("""
1. Go to e.g [gjopen.com/questions](gjopen.com/questions) in a new tab in Chrome or in Firefox.
2. Press `Ctrl + Shift + I`, and then navigate to the "Network" tab.
3. Click on Reload, or reload the page.
4. Right click on the first request, which loads the "questions" document. Click Copy, then "copy as cURL". Paste the results here.
""")
2021-06-01 15:18:59 +00:00
st.sidebar.write("Nuño Sempere made [video instructions](https://www.youtube.com/watch?v=_G3FNzYNPCs) for an earlier version of the web app.")
2021-06-01 14:28:35 +00:00
# st.sidebar.subheader("On plots and methodology")
# st.sidebar.write("""
# - I generate two calibration curves: one in linear space and another one in 'odds' space (hopefully it will be easier to see how well calibrated you are around probabilities close to 0 and 1).
# - I generate plots with a modified [sklearn.calibration.calibration_curve](https://scikit-learn.org/stable/modules/generated/sklearn.calibration.calibration_curve.html), basically it groups points into bins and computes the proportions of samples resolving positively and the mean predicted probabilities.
# - The confidence intervals are a standart deviations wide.
# - If you hover over a datapoint you can see precise coordinates (x, y) and number of samples (N) contributing to it.
# """)
st.sidebar.subheader("Authorship and acknowledgments")
2021-06-01 15:18:59 +00:00
st.sidebar.write("This web app was built by [Misha Yagudin](mailto:mike.yagudin@gmail.com). I am grateful to [Nuño Sempere](https://nunosempere.github.io/) for providing feedback. All errors are my own.")
2021-05-31 18:59:24 +00:00
# ---
2021-06-01 14:28:35 +00:00
2021-05-31 18:59:24 +00:00
platform = st.selectbox(
"Which platform are you using?",
["Good Judgement Open", "CSET Foretell"],
)
platform_url = {
"Good Judgement Open": "https://www.gjopen.com",
"CSET Foretell": "https://www.cset-foretell.com",
}[platform]
uid = st.number_input("What is your user ID?", min_value=1, value=28899)
uid = str(uid)
2021-05-31 19:33:53 +00:00
curl_value = """curl 'https://www.gjopen.com/' \\
-H 'authority: www.gjopen.com' \\
-H 'cache-control: max-age=0' \\
-H 'sec-ch-ua: "something-something-about-your-browser"' \\
-H 'sec-ch-ua-mobile: ?0' \\
-H 'dnt: 1' \\
-H 'upgrade-insecure-requests: 1' \
-H 'user-agent: Mozilla/5.0 something-something-about-your-PC' \
-H 'accept: text/html...' \
-H 'sec-fetch-site: none' \\
-H 'sec-fetch-mode: navigate' \\
-H 'sec-fetch-user: ?1' \\
-H 'sec-fetch-dest: document' \\
-H 'accept-language: en-US,en;q=0.9,ru;q=0.8' \\
-H 'cookie: a-very-long-mysterious-string' \\
--compressed"""
2021-05-31 18:59:24 +00:00
curl_command = st.text_area(
2021-06-01 15:16:35 +00:00
"Om Nom Nom Nom... Paste cURL here, if confused see the sidebar for the instructions.", value=curl_value
2021-05-31 18:59:24 +00:00
)
2021-05-31 19:33:53 +00:00
if curl_command == curl_value:
st.warning('Please input your cURL (see the sidebar for the instructions :-)')
st.stop()
2021-05-31 18:59:24 +00:00
try:
curl_command = curl_command.replace("\\", "")
2021-05-31 19:33:53 +00:00
curl_content = uncurl.parse_context(curl_command)
headers, cookies = curl_content.headers, curl_content.cookies
except SystemExit:
2021-06-01 14:41:22 +00:00
st.warning("It seems like something is wrong with the cURL you provided: see the sidebar for the instructions.")
st.stop()
2021-05-31 18:59:24 +00:00
# ---
2021-05-31 18:59:24 +00:00
with st.spinner('Loading resolved questions...'):
2021-05-31 19:33:53 +00:00
questions = get_resolved_questions(uid, platform_url, headers, cookies)
2021-05-31 18:59:24 +00:00
st.write(f"- {len(questions)} questions you forecasted on have resolved.")
2021-05-31 18:59:24 +00:00
# ---
# TODO: Make a progress bar..?
2021-05-31 18:59:24 +00:00
with st.spinner('Loading your forecasts...'):
2021-05-31 19:33:53 +00:00
forecasts = get_forecasts(uid, questions, platform_url, headers, cookies)
with st.spinner("Loading questions's resolutions..."):
2021-05-31 19:33:53 +00:00
resolutions = get_resolutions(questions, platform_url, headers, cookies)
2021-05-31 18:59:24 +00:00
# ---
2021-05-31 18:59:24 +00:00
num_forecasts = sum(len(f) for f in forecasts.values())
st.write(
f"- You've made {num_forecasts} forecasts on these {len(questions)} questions."
)
2021-05-31 18:59:24 +00:00
flatten = lambda t: [item for sublist in t for item in sublist]
# y_true = flatten(resolutions[q]["y_true"] for q in questions for _ in forecasts[q])
# y_pred = flatten(f["y_pred"] for q in questions for f in forecasts[q])
2021-05-31 18:59:24 +00:00
# Note that I am "double counting" each prediction.
# if st.checkbox("Drop last"):
y_true = flatten(
resolutions[q]["y_true"][:-1] for q in questions for _ in forecasts[q]
)
y_pred = flatten(f["y_pred"][:-1] for q in questions for f in forecasts[q])
2021-05-31 18:59:24 +00:00
2021-06-01 14:29:57 +00:00
y_true, y_pred = np.array(y_true), np.array(y_pred)
2021-05-31 18:59:24 +00:00
2021-06-01 14:29:57 +00:00
order = np.arange(len(y_true))
np.random.default_rng(0).shuffle(order)
y_true, y_pred = y_true[order], y_pred[order]
2021-05-31 18:59:24 +00:00
st.write(f"- Which gives us {len(y_pred)} datapoints to work with.")
2021-05-31 18:59:24 +00:00
# ---
2021-05-31 18:59:24 +00:00
strategy_select = st.selectbox(
"Which binning stranegy do you prefer?",
[
"I want bins to have identical widths",
"I want bins to have the same number of samples",
],
)
strategy = {
"I want bins to have identical widths": "uniform",
"I want bins to have the same number of samples": "quantile",
}[strategy_select]
recommended_n_bins = int(np.sqrt(len(y_pred))) if strategy == "quantile" else 20 + 1
n_bins = st.number_input(
"How many bins do you want me to display?",
min_value=1,
value=recommended_n_bins,
)
# ---
2021-05-31 19:33:53 +00:00
fig = plotly_calibration(y_true, y_pred, n_bins=n_bins, strategy=strategy)
st.plotly_chart(fig, use_container_width=True)
2021-05-31 19:33:53 +00:00
fig = plotly_calibration_odds(y_true, y_pred, n_bins=n_bins, strategy=strategy)
st.plotly_chart(fig, use_container_width=True)
2021-05-31 18:59:24 +00:00
# overconf = overconfidence(y_true, y_pred)
# st.write(f"Your over/under- confidence score is {overconf:.2f}.")