In [1]:
%matplotlib inline
from bdbcontrib import Population
In [2]:
help(Population)
Help on function Population in module bdbcontrib:
Population(*args, **kwargs)
Create a Population object, wrapping a bayeslite.BayesDB.
name : str REQUIRED.
A name for the population, should use letters and underscores only.
This will also be used as a table name in the bdb, and %t in queries
will expand to this name. %g in queries will expand to the current
population metamodel, also based on this name.
csv_path : str
The path to a comma-separated values file. If specified, will be used
to populate the bdb. It must exist and be both readable and non-empty.
df : pandas.DataFrame
If specified, these data will be used to populate the bdb, superseding
any csv_path. It must not be empty.
bdb_path : str
If specified, store data and analysis results here. If no other data
source (csv or df) is specified, then it must already have been
populated. If not specified, we will use a volatile in-memory bdb.
logger : object
Something on which we can call .info or .warn to send messages to the
user. By default a bayeslite.loggers.BqlLogger, but could be QuietLogger
(only results), SilentLogger (nothing), IpyLogger, CaptureLogger,
LoggingLogger, or anything else that implements the BqlLogger interface.
session_capture_name : String
Signing up with your name and email and sending your session details
to the MIT Probabilistic Computing Group helps build a community of
support and helps improve your user experience. You can save your choice
in a file called 'bayesdb-session-capture-opt.txt' in the directory
where you run the software, or any parent directory. This option
overrides any setting in such a file. Any string is interpreted as
opting in to sending session details. False is interpreted as opting
out. You must choose. If you choose to use an organization name or
email, then please send a note to bayesdb@mit.edu to help us connect
your sessions to you.
If you encounter a bug, or something surprising, please include your
session capture name in your report.
If you opt out, you still allow us to count how often users opt out.
DO NOT USE THIS SOFTWARE FOR HIPAA-COVERED, PERSONALLY IDENTIFIABLE,
OR SIMILARLY SENSITIVE DATA! Opting out does not guarantee security.
In [ ]:
s = Population("satellites", bdb_path="examples/satellites/satellites.bdb")
ERROR:
Please set session_capture_name option to Population
to either opt-in or opt-out of sending details of your usage of
this software to the MIT Probabilistic Computing Group.
If you see this in one of our example notebooks,
return to the starting page, the Index.ipynb, to
make that choice.
In [61]:
s = Population("satellites", bdb_path="satellites.bdb", session_capture_name="Demo")
BQL [SELECT * FROM bayesdb_generator] ()
In [5]:
help(s)
Help on Population in module bdbcontrib.population object:
class Population(__builtin__.object)
| Generative Population Model, wraps a BayesDB, and tracks one population.
|
| Methods defined here:
|
| __init__(self, name, csv_path=None, bdb_path=None, df=None, logger=None, session_capture_name=None)
| Create a Population object, wrapping a bayeslite.BayesDB.
|
| name : str REQUIRED.
| A name for the population, should use letters and underscores only.
| This will also be used as a table name in the bdb, and %t in queries
| will expand to this name. %g in queries will expand to the current
| population metamodel, also based on this name.
| csv_path : str
| The path to a comma-separated values file. If specified, will be used
| to populate the bdb. It must exist and be both readable and non-empty.
| df : pandas.DataFrame
| If specified, these data will be used to populate the bdb, superseding
| any csv_path. It must not be empty.
| bdb_path : str
| If specified, store data and analysis results here. If no other data
| source (csv or df) is specified, then it must already have been
| populated. If not specified, we will use a volatile in-memory bdb.
| logger : object
| Something on which we can call .info or .warn to send messages to the
| user. By default a bayeslite.loggers.BqlLogger, but could be QuietLogger
| (only results), SilentLogger (nothing), IpyLogger, CaptureLogger,
| LoggingLogger, or anything else that implements the BqlLogger interface.
| session_capture_name : String
| Signing up with your name and email and sending your session details
| to the MIT Probabilistic Computing Group helps build a community of
| support and helps improve your user experience. You can save your choice
| in a file called 'bayesdb-session-capture-opt.txt' in the directory
| where you run the software, or any parent directory. This option
| overrides any setting in such a file. Any string is interpreted as
| opting in to sending session details. False is interpreted as opting
| out. You must choose. If you choose to use an organization name or
| email, then please send a note to bayesdb@mit.edu to help us connect
| your sessions to you.
|
| If you encounter a bug, or something surprising, please include your
| session capture name in your report.
|
| If you opt out, you still allow us to count how often users opt out.
|
| DO NOT USE THIS SOFTWARE FOR HIPAA-COVERED, PERSONALLY IDENTIFIABLE,
| OR SIMILARLY SENSITIVE DATA! Opting out does not guarantee security.
|
| analysis_status(self)
| Return the count of models for each number of iterations run.
|
| analyze(self, models=100, minutes=0, iterations=0, checkpoint=0)
| Run analysis.
|
| models : integer
| The number of models bounds the accuracy of predictive probabilities.
| With ten models, then you get one decimal digit of interpretability,
| with a hundred models, you get two, and so on.
| minutes : integer
| How long you want to let it run.
| iterations : integer
| How many iterations to let it run.
|
| Returns:
| A report indicating how many models have seen how many iterations,
| and other info about model stability.
|
| barplot = as_population_method(self, *args, **kwargs)
| Make bar-plot from categories and their heights.
|
| First column specifies names; second column specifies heights.
|
| Parameters
| ----------
|
| df : a pandas.DataFrame or BQL query.
|
| Returns
| ----------
| figure: matplotlib.figure.Figure
|
| cardinality = as_population_method(self, *args, **kwargs)
| Compute the number of unique values in the columns of a table.
|
| Parameters
| ----------
|
| table : str
| Name of table.
| cols : list<str>, optional
| Columns to compute the unique values. Defaults to all.
|
| Returns
| -------
| counts : list<tuple<str,int>>
| A list of tuples of the form [(col_1, cardinality_1), ...]
|
| check_representation(self)
|
| describe_generator = as_population_method(self, *args, **kwargs)
| Returns a DataFrame containing description of `generator_name`.
|
| Examples
| --------
|
| >>> bdbcontrib.describe_generator(bdb, 'employees_gen')
| id | name | tabname | metamodel
| ---+---------------+-----------+----------
| 3 | employees_gen | employees | crosscat
|
| describe_generator_columns = as_population_method(self, *args, **kwargs)
| Returns a DataFrame containing description of the columns
| modeled by `generator_name`.
|
| Examples
| --------
|
| >>> bdbcontrib.describe_generator_columns(bdb, 'employees_gen')
| colno | name | stattype
| ------+---------+-------------
| 0 | name | categorical
| 1 | age | numerical
| 2 | weight | numerical
| 3 | height | numerical
|
| describe_generator_models = as_population_method(self, *args, **kwargs)
| Returns a DataFrame containing description of the models
| in `generator_name`.
|
| Examples
| --------
|
| >>> bdbcontrib.describe_generator_models(bdb, 'employees_gen')
| modelno | iterations
| --------+-----------
| 0 | 100
|
| describe_table = as_population_method(self, *args, **kwargs)
| Returns a DataFrame containing description of `table_name`.
|
| Examples
| --------
| >>> bdbcontrib.describe_table(bdb, 'employees')
| tabname | colno | name
| ----------+-------+--------
| employees | 0 | name
| employees | 1 | age
| employees | 2 | weight
| employees | 3 | height
|
| get_column_stattype = as_population_method(self, *args, **kwargs)
|
| heatmap = as_population_method(self, *args, **kwargs)
| Plot clustered heatmaps for the given dependencies.
|
| Parameters
| ----------
|
| deps : a pandas.DataFrame or BQL query.
| Must have columns=['generator_id', 'name0', 'name1', 'value'],
| I.e. the result of a BQL query of the form 'ESTIMATE ... PAIRWISE ...'.
| E.g., DEPENDENCE PROBABILITY, MUTUAL INFORMATION, COVARIANCE, etc.
|
| **kwargs : dict
| Passed to zmatrix: vmin, vmax, row_ordering, col_ordering
|
| Returns a seaborn.clustermap (a kind of matplotlib.Figure)
|
| histogram = as_population_method(self, *args, **kwargs)
| Plot histogram of one- or two-column table.
|
| If two-column, subdivide the first column according to labels in
| the second column
|
| Parameters
| ----------
|
| df : a pandas.DataFrame or BQL query.
| nbins : int, optional
| Number of bins in the histogram.
| normed : bool, optional
| If True, normalizes the the area of the histogram (or each
| sub-histogram if df has two columns) to 1.
|
| Returns
| ----------
| figure: matplotlib.figure.Figure
|
| initialize(self)
|
| initialize_session_capture(self, name)
|
| interpret_bql(self, query_string)
| Replace %t and %g as appropriate.
|
| mi_hist = as_population_method(self, *args, **kwargs)
| Histogram of estimated mutual information between the two columns for each
| of a generator's model instances.
|
| Parameters
| ----------
| bdb : bayeslite.BayesDB
| Active BayesDB instance.
| generator_name : str
| Name of the generator to compute MI(col1;col2)
| col1, col2 : str
| Name of the columns to compute MI(col1;col2)
| num_samples : int, optional
| Number of samples to use in the Monte Carlo estimate of MI.
| bins : int, optional
| Number of bins in the histogram.
|
| Returns
| -------
| figure : matplotlib.figure.Figure
|
| nullify = as_population_method(self, *args, **kwargs)
| Replace specified values in a SQL table with ``NULL``.
|
| Parameters
| ----------
|
| table : str
| The name of the table on which to act
| value : stringable
| The value to replace with ``NULL``
|
| Examples
| --------
| >>> import bayeslite
| >>> from bdbcontrib import plotutils
| >>> with bayeslite.bayesdb_open('mydb.bdb') as bdb:
| >>> bdbcontrib.nullify(bdb, 'mytable', 'NaN')
|
| pairplot = as_population_method(self, *args, **kwargs)
| Plot array of plots for all pairs of columns.
|
| Plots continuous-continuous pairs as scatter (optional KDE contour).
| Plots continuous-categorical pairs as violinplot.
| Plots categorical-categorical pairs as heatmap.
|
| Parameters
| ----------
|
| df : a pandas.DataFrame or BQL query.
|
| show_contour : bool, optional
| If True, KDE contours are plotted on top of scatter plots
| and histograms.
| show_missing : bool, optional
| If True, rows with one missing value are plotted as lines on scatter
| plots.
| colorby : str, optional
| Name of a column to use to color data points in histograms and scatter
| plots.
| show_full : bool, optional
| Show full pairwise plots, rather than only lower triangular plots.
| **kwargs : dict, optional
| Options to pass through to underlying plotter for pairs.
|
| Returns
| -------
| figure : matplotlib.figure.Figure
|
| pairplot_vars = as_population_method(self, *args, **kwargs)
| Use pairplot to show the given variables.
|
| See help(pairplot) for more plot options.
|
| Parameters
| ----------
|
| varnames: list of one or more variables to plot.
|
| colorby: categorical variable to color all of the plots by.
|
| Returns
| -------
| figure: a matplotlib.figure.Figure
|
| per_model_analysis_status(self)
| Return the number of iterations for each model.
|
| q = as_population_method(self, *args, **kwargs)
| Execute the `bql` query on the `bdb` instance.
|
| Parameters
| ----------
|
| bql : A BQL query.
| You can also use %t and %g for the population-associated table name and
| the name of the population's generative-population-model-instance-generator,
| respectively.
| bindings : Values to safely fill in for '?' in the BQL query.
|
| Returns
| -------
| df : pandas.DataFrame
| Table of results as a pandas dataframe.
|
| query = as_population_method(self, *args, **kwargs)
| Execute the `bql` query on the `bdb` instance.
|
| Parameters
| ----------
|
| bql : A BQL query.
| You can also use %t and %g for the population-associated table name and
| the name of the population's generative-population-model-instance-generator,
| respectively.
| bindings : Values to safely fill in for '?' in the BQL query.
|
| Returns
| -------
| df : pandas.DataFrame
| Table of results as a pandas dataframe.
|
| quick_describe_columns = as_population_method(self, *args, **kwargs)
| Returns a DataFrame containing description of the columns
| modeled by `generator_name`.
|
| Examples
| --------
|
| >>> bdbcontrib.describe_generator_columns(bdb, 'employees_gen')
| colno | name | stattype
| ------+---------+-------------
| 0 | name | categorical
| 1 | age | numerical
| 2 | weight | numerical
| 3 | height | numerical
|
| quick_explore_vars = as_population_method(self, *args, **kwargs)
| Show dependence probabilities and neighborhoods based on those.
|
| varnames: list of strings
| At least two column names to look at dependence probabilities of,
| and to explore neighborhoods of.
| nsimilar: positive integer
| The size of the neighborhood to explore.
|
| quick_similar_rows = as_population_method(self, *args, **kwargs)
| Explore rows similar to the identified one.
|
| identify_row_by : dict
| Dictionary of column names to their values. These will be turned into
| a WHERE clause in BQL, and must identify one unique row.
| nsimilar : positive integer
| The number of similar rows to retrieve.
|
| reset(self)
|
| specifier_to_df(self, spec)
|
| sql_tracing(self, turn_on=True)
| Trace underlying SQL, for debugging.
|
| vartype = as_population_method(self, *args, **kwargs)
|
| ----------------------------------------------------------------------
| Class methods defined here:
|
| method_imports(cls) from __builtin__.type
| Runs decorators that add methods to Population.
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
In [8]:
from bdbcontrib.population_method import population_method
help(population_method)
Help on function population_method in module bdbcontrib.population_method:
population_method(**argspec_transforms)
Create a method in the Population class to encapsulate this function.
Fill in documentation holes, but otherwise leave the function itself
untouched for library use.
For the types below, "arg" is a zero-indexed positional argument number
or a keyword argument name as a string. For keyword arguments, if the
caller provides a value, we leave it untouched. Positional arguments may
either be kept and transformed, or hidden from the argument list. If
the transform takes both a population and an arg, then it is kept and the
arg is transformed. If it takes only a population, then it is hidden (there
is no corresponding positional argument in the method, the documentation
line is removed, and other positional arguments shift to accommodate).
__PT_DOC__
In your method's docstring, use the above names with underscores to
get their appropriate documentation (if you want it replaced). For
example, use __population_name__ in place of the lines where you
would put the population name parameter's description, after its name.
For those transformations that do not have an [arg], if their documentation
line starts with the parameter name, then a colon, then the double
underscored transformation name, then the entire line is removed.
For example, a documentation line like
table : __population_name__
would be entirely removed from the method docstring, because that argument
is filled in using the population instance, rather than being requested
from the caller.
In [12]:
from bdbcontrib.population_method import PT_DOC
print PT_DOC
population:
Passes on the population object itself.
interpret_bql: [argspec]
Fills in %t and %g in a BQL query.
specifier_to_df: [argspec]
Queries to create a dataframe from BQL if need be.
population_to_bdb:
Provides the population's BayesDB instance.
population_name:
Provides the population's name.
generator_name:
Provides the population's generative population model's name.
logger:
Provides a logger to which one can .info, .warn, .plot, etc.
In [9]:
@population_method(population=0)
def my_name(self):
return self.name
s.my_name()
Out[9]:
'satellites'
In [18]:
@population_method(population_name='myname')
def name_length_plus_k(k, myname="name"):
return k + len(myname)
{'method with default:': s.name_length_plus_k(100),
'method with override:': s.name_length_plus_k(100, myname="foo"),
'bare function with default:': name_length_plus_k(100),
'bare function with override:': name_length_plus_k(100, myname="Asher Lev")}
Out[18]:
{'bare function with default:': 104,
'bare function with override:': 109,
'method with default:': 110,
'method with override:': 103}
In [22]:
@population_method(specifier_to_df=0)
def only(df):
assert (1, 1) == df.shape
return df.ix[0, 0]
s.only('SELECT COUNT(*) FROM %t')
BQL [SELECT COUNT(*) FROM "satellites"] ()
Out[22]:
1167.0
In [23]:
only('SELECT COUNT(*) FROM %t')
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-23-5278af9e7bd2> in <module>()
----> 1 only('SELECT COUNT(*) FROM %t')
<ipython-input-22-1439b8b0482d> in only(df)
1 @population_method(specifier_to_df=0)
2 def only(df):
----> 3 assert (1, 1) == df.shape
4 return df.ix[0, 0]
5 s.only('SELECT COUNT(*) FROM %t')
AttributeError: 'str' object has no attribute 'shape'
In [24]:
only(s.q('SELECT COUNT(*) FROM %t'))
BQL [SELECT COUNT(*) FROM "satellites"] ()
Out[24]:
1167.0
In [26]:
df = s.q('SELECT COUNT(*) FROM %t')
s.only(df)
BQL [SELECT COUNT(*) FROM "satellites"] ()
Out[26]:
1167.0
In [29]:
@population_method(no_such_hook=0)
def foo():
return 42
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-29-5fe6a42728c6> in <module>()
----> 1 @population_method(no_such_hook=0)
2 def foo():
3 return 42
/Users/probcomp/pc/bdbcontrib/build/lib/bdbcontrib/population_method.pyc in decorator(fn)
325 '''
326 def decorator(fn):
--> 327 xfrms = compile_argspec_transforms(fn, argspec_transforms)
328 def as_population_method(self, *args, **kwargs):
329 with logged_query(query_string=fn.__code__.co_name,
/Users/probcomp/pc/bdbcontrib/build/lib/bdbcontrib/population_method.pyc in compile_argspec_transforms(fn, argspecs)
142 optional_names = co_varnames[num_required:]
143 for key, argspec in argspecs.iteritems():
--> 144 xform = ARGSPEC_TRANSFORMS[key]
145 for argitem in (argspec if hasattr(argspec, '__iter__') else [argspec]):
146 if isinstance(argitem, int):
KeyError: 'no_such_hook'
In [30]:
@population_method(population_name=1)
def foo(bar):
return 42
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-30-65a4cde6bdd5> in <module>()
----> 1 @population_method(population_name=1)
2 def foo(bar):
3 return 42
/Users/probcomp/pc/bdbcontrib/build/lib/bdbcontrib/population_method.pyc in decorator(fn)
325 '''
326 def decorator(fn):
--> 327 xfrms = compile_argspec_transforms(fn, argspec_transforms)
328 def as_population_method(self, *args, **kwargs):
329 with logged_query(query_string=fn.__code__.co_name,
/Users/probcomp/pc/bdbcontrib/build/lib/bdbcontrib/population_method.pyc in compile_argspec_transforms(fn, argspecs)
147 if argitem >= num_required or argitem < 0:
148 raise IndexError(
--> 149 'Invalid positional specifier for %s: %d' % (fn.__name__, argitem))
150 if required_transformers[argitem] is not None:
151 raise IndexError(
IndexError: Invalid positional specifier for foo: 1
In [31]:
@population_method(population_name="pub")
def foo(bar):
return 42
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-31-6ee78d7cf92c> in <module>()
----> 1 @population_method(population_name="pub")
2 def foo(bar):
3 return 42
/Users/probcomp/pc/bdbcontrib/build/lib/bdbcontrib/population_method.pyc in decorator(fn)
325 '''
326 def decorator(fn):
--> 327 xfrms = compile_argspec_transforms(fn, argspec_transforms)
328 def as_population_method(self, *args, **kwargs):
329 with logged_query(query_string=fn.__code__.co_name,
/Users/probcomp/pc/bdbcontrib/build/lib/bdbcontrib/population_method.pyc in compile_argspec_transforms(fn, argspecs)
155 if argitem not in optional_names:
156 raise IndexError(
--> 157 'No such optional argument for %s: %s' % (fn.__name__, argitem))
158 if argitem in optional_transformers:
159 raise IndexError(
IndexError: No such optional argument for foo: pub
In [32]:
s.only(3, 4, 5)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-32-44a0bbc54993> in <module>()
----> 1 s.only(3, 4, 5)
/Users/probcomp/pc/bdbcontrib/build/lib/bdbcontrib/population_method.pyc in as_population_method(self, *args, **kwargs)
331 name=self.session_capture_name):
332 self.check_representation()
--> 333 (dargs, dkwargs) = apply_argspec_transforms(self, xfrms, args, kwargs)
334 result = None
335 try:
/Users/probcomp/pc/bdbcontrib/build/lib/bdbcontrib/population_method.pyc in apply_argspec_transforms(pop, argspecs, args, kwargs)
268 else:
269 raise TypeError("%s() takes at most %d arguments (%d given)" %
--> 270 (argspecs['name'], max_args, len(args) + len(kwargs)))
271
272 new_args = []
TypeError: __main__.only() takes at most 1 arguments (3 given)
In [33]:
s.only()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-33-dc91630dc2cb> in <module>()
----> 1 s.only()
/Users/probcomp/pc/bdbcontrib/build/lib/bdbcontrib/population_method.pyc in as_population_method(self, *args, **kwargs)
331 name=self.session_capture_name):
332 self.check_representation()
--> 333 (dargs, dkwargs) = apply_argspec_transforms(self, xfrms, args, kwargs)
334 result = None
335 try:
/Users/probcomp/pc/bdbcontrib/build/lib/bdbcontrib/population_method.pyc in apply_argspec_transforms(pop, argspecs, args, kwargs)
246 raise TypeError("%s() takes at least %d arguments (%d given)" %
247 (argspecs['name'], len(required_names),
--> 248 len(args) + len(kwargs)))
249
250 new_varargs = []
TypeError: __main__.only() takes at least 1 arguments (0 given)
In [35]:
s.only(bar="five") # I hate this message, but it's the one python gives for similar errors on regular functions...
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-35-8c488c1ecc21> in <module>()
----> 1 s.only(bar="five") # I hate this message, but it's the one python gives for similar errors on regular functions...
/Users/probcomp/pc/bdbcontrib/build/lib/bdbcontrib/population_method.pyc in as_population_method(self, *args, **kwargs)
331 name=self.session_capture_name):
332 self.check_representation()
--> 333 (dargs, dkwargs) = apply_argspec_transforms(self, xfrms, args, kwargs)
334 result = None
335 try:
/Users/probcomp/pc/bdbcontrib/build/lib/bdbcontrib/population_method.pyc in apply_argspec_transforms(pop, argspecs, args, kwargs)
246 raise TypeError("%s() takes at least %d arguments (%d given)" %
247 (argspecs['name'], len(required_names),
--> 248 len(args) + len(kwargs)))
249
250 new_varargs = []
TypeError: __main__.only() takes at least 1 arguments (1 given)
In [57]:
@population_method(population_name=0, specifier_to_df=[1, 2])
def quux(name, df1, df2, number):
"""This is documentation!
name: __population_name__
df1: __specifier_to_df__
The first thing.
df2: __specifier_to_df__
The second thing.
number: some numerical thing.
"""
return '\n\n'.join([str(i) for i in [name, df1, df2, number]])
help(s.quux)
Help on method as_population_method in module bdbcontrib.population_method:
as_population_method(self, *args, **kwargs) method of bdbcontrib.population.Population instance
This is documentation!
df1: a pandas.DataFrame or BQL query.
The first thing.
df2: a pandas.DataFrame or BQL query.
The second thing.
number: some numerical thing.
In [58]:
help(quux)
Help on function quux in module __main__:
quux(name, df1, df2, number)
This is documentation!
name: str
the name of the relevant table in the bdb.
df1: a pandas.DataFrame.
The first thing.
df2: a pandas.DataFrame.
The second thing.
number: some numerical thing.
In [59]:
print s.quux('SELECT COUNT(*) FROM %t',
'ESTIMATE DEPENDENCE PROBABILITY FROM PAIRWISE COLUMNS OF %g FOR "Apogee_km", "Perigee_km"',
42)
BQL [SELECT COUNT(*) FROM "satellites"] ()
BQL [ESTIMATE DEPENDENCE PROBABILITY FROM PAIRWISE COLUMNS OF "satellites_cc" FOR "Apogee_km", "Perigee_km"] ()
satellites
"COUNT"(*)
0 1167
generator_id name0 name1 value
0 1 Perigee_km Perigee_km 1
1 1 Perigee_km Apogee_km 1
2 1 Apogee_km Perigee_km 1
3 1 Apogee_km Apogee_km 1
42
In [ ]:
In [ ]:
Content source: probcomp/bdbcontrib
Similar notebooks: