-
Notifications
You must be signed in to change notification settings - Fork 24
Retrieve, plot, and sample from Metaculus community prediction distributions #61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…g some names missing from some rows
…d values. Getting some errors.
…on for the log test question is about exactly 1 when it should be around 30k
…he proper domain, but seems weird that normalize's domain can't take things to the left of the question interval
…es much better at plotting the logs of the values directly, for whatever reason. So I should figure out how to do that instead.
| samples = np.maximum(samples, epsilon) | ||
| samples = samples / self.question_range["min"] | ||
| return np.log(samples) / np.log(self.deriv_ratio) | ||
| def normalized_from_true_value(self, true_value) -> float: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes are per advice from Max (Metaculus programmer)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅ A review job has been created and sent to the PullRequest network.
Check the status or cancel PullRequest code review here - or - cancel by adding [!pr] to the title of the pull request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the general architecture of things is looking pretty good. Although, there are some things, which could be cleaned up a little bit.
From a high-level, it may make sense to have more of the distribution calculations in torch to avoid converting back and forth as much, but that is manage-able.
Reviewed with ❤️ by PullRequest
| sample_above_range = abs(np.random.logistic( | ||
| 1, 0.02)) | ||
| sample_in_range = ppl.sample(self.community_dist_in_range( | ||
| )) / float(len(self.prediction_histogram)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(note that one way to avoid discussion around formatting would be to use black for this project:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I like the idea of switching to Black and created an issue for it: https://github.com/oughtinc/ergo/issues/80
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no missing argument I don't think -- note that we're using a different function to grab a sample than for the other 2
yeah I think self.prediction_histogram is always non-empty -- would probably rather that we just get an error if it's not so that we know that something unexpected happened (rather than handling that case)
ergo/metaculus.py
Outdated
|
|
||
| def sample_normalized_community(self): | ||
| sample_below_range = -1 * \ | ||
| abs(np.random.logistic(0, 0.02)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ergo/metaculus.py
Outdated
| columns = ["id", "name", "title", "resolve_time"] | ||
| data = [] | ||
| show_names = False | ||
| for question in questions: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For more pythonic-refactor, you could try using the any() builtin function instead. It takes in an iterable and as long as one item in the iterable is True, it will return true. Also if the iterable is empty, it will return False.
It would simplify lines 105-109 to this:
show_names = any(q.name for q in questions)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol yeah!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for not just saying, "wtf is this, just use any!" :p
ergo/ppl.py
Outdated
| model = name_count(model) | ||
| samples: Dict[str, List[float]] = {} | ||
| samples: List[Dict[str, float]] = [] | ||
| for i in tqdm.trange(num_samples): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
|
||
| return ax | ||
|
|
||
| def show_submission(self, samples, show_community=False): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I left this as a loose end. See https://github.com/oughtinc/ergo/issues/64
ergo/metaculus.py
Outdated
| # {'prediction': ['high minus low must be at least 0.01']}" | ||
| high = max(min(distribution.cdf(1), 0.99), | ||
| 0.0099) if self.high_open else 1 | ||
| low + 0.01) if self.high_open else 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ergo/metaculus.py
Outdated
|
|
||
| def sample_normalized_community(self): | ||
| sample_below_range = -1 * \ | ||
| abs(np.random.logistic(0, 0.02)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| def denormalize_samples(self, samples): | ||
| raise NotImplementedError("This should be implemented by a subclass") | ||
|
|
||
| @functools.lru_cache(None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd caution against using functools.lru_cache for class methods[1]. Is prediction_histogram only set once? Would @functools.cached_property be a better choice?
[1] https://stackoverflow.com/questions/33672412/python-functools-lru-cache-with-class-methods-release-object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that the cache will hold onto the class instance in memory?
I don't see that causing performance problems soon, so I'm probably inclined to just leave it in
|
|
||
| self.show_prediction(submission, samples) | ||
|
|
||
| def show_community_prediction(self, only_show_this=True): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I left this as a loose end. See #64
ergo/metaculus.py
Outdated
| @functools.lru_cache(None) | ||
| def community_dist_in_range(self): | ||
| # distribution on integers referencing 0...(len(self.prediction_histogram)-1) | ||
| df = pd.DataFrame(self.prediction_histogram, columns=["x", "y1", "y2"]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like we don't need to create a DataFrame if we are just going to create a torch Tensor. Perhaps,
y2 = [p[2] for p in self.prediction_histogram]
return dist.Categorical(probs=torch.Tensor(y2))
or
t = torch.Tensor(self.prediction_histogram)
return dist.Categorical(probs=t[:,2])
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks neat! Added some style comments and perhaps the graphs in the notebook can be made easier to read at a glance with more human readable units for number of occurrences on the x-axis
Reviewed with ❤️ by PullRequest
ergo/metaculus.py
Outdated
|
|
||
| @functools.lru_cache(None) | ||
| def community_dist_in_range(self): | ||
| # distribution on integers referencing 0...(len(self.prediction_histogram)-1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ergo/metaculus.py
Outdated
| import ergo.logistic as logistic | ||
| import ergo.ppl as ppl | ||
|
|
||
| from typing import Optional, List, Any, Dict, Tuple |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ergo/metaculus.py
Outdated
| import torch | ||
| import requests | ||
| import pendulum | ||
| import scipy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| "\n", | ||
| "print([log_tick for log_tick in pyplot.xticks()])\n", | ||
| "\n", | ||
| "pyplot.xticks(pyplot.xticks()[0], [f\"{math.exp(log_tick):.1e}\" for log_tick in pyplot.xticks()[0]], rotation=\"vertical\")\n", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps a LogFormatter could make the x ticks more legible here?
ergo/metaculus.py
Outdated
| latest_prediction = self.my_predictions["predictions"][-1]["d"] | ||
| return self.get_submission_from_json(latest_prediction) | ||
|
|
||
| def show_community_prediction(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved all of the stubs to the bottoms of their respective classes
I suspect that doing more than that will be more trouble than it's worth
ergo/metaculus.py
Outdated
| return dist.Categorical(probs=torch.Tensor(df["y2"])) | ||
|
|
||
| def sample_normalized_community(self): | ||
| sample_below_range = -1 * \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
@stuhlmueller -- I made the vast majority of the changes requested by PullRequest, and commented on most others about why I didn't make the change. OK to merge this once CI passes? |
Codecov Report
@@ Coverage Diff @@
## master #61 +/- ##
=======================================
Coverage 76.08% 76.08%
=======================================
Files 7 7
Lines 623 623
=======================================
Hits 474 474
Misses 149 149 Continue to review full report at Codecov.
|



These features are used in
notebooks/community_distributions_v2.ipynb. You should probably start there to understand what's going on.Main changes:
semi-related changes:
Unrelated changes:
Some things are still a bit rough:
But I think it's probably better to go ahead and merge this and then fix those
(note that I've assigned the last 3 to myself to work on soon)