Skip to content

Commit d3122e7

Browse files
jeromedockesdierickxsimon
authored andcommitted
add .skb.get_vars() (skrub-data#1646)
1 parent 6454baa commit d3122e7

4 files changed

Lines changed: 103 additions & 0 deletions

File tree

CHANGES.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ New features
1616
etc. :pr:`1642` by :user:`Jérôme Dockès <jeromedockes>`.
1717
- TableReport now displays the mean statistic for boolean columns.
1818
:pr:`1647` by :user:`Abdelhakim Benechehab <abenechehab>`.
19+
- :meth:`DataOp.skb.get_vars` allows inspecting all the variables, or all the
20+
named dataops, in a :class:`DataOp`. This lets us easily know what keys should
21+
be present in the ``environment`` dictionary we pass to
22+
:meth:`DataOp.skb.eval` or to :meth:`SkrubLearner.fit`,
23+
:meth:`SkrubLearner.predict`, etc. .
24+
:pr:`1646` by :user:`Jérôme Dockès <jeromedockes>`.
1925
- :meth:`DataOp.skb.iter_cv_splits` iterates over the training and testing
2026
environments produced by a CV splitter -- similar to
2127
:meth:`DataOp.skb.train_test_split` but for multiple cross-validation splits.

doc/api_reference.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@
233233
"DataOp.skb.freeze_after_fit",
234234
"DataOp.skb.full_report",
235235
"DataOp.skb.get_data",
236+
"DataOp.skb.get_vars",
236237
"DataOp.skb.make_learner",
237238
"DataOp.skb.make_grid_search",
238239
"DataOp.skb.make_randomized_search",

skrub/_data_ops/_skrub_namespace.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,92 @@ def get_data(self):
11901190
data[impl.name] = impl.value
11911191
return data
11921192

1193+
def get_vars(self, all_named_ops=False):
1194+
"""
1195+
Get all the variables used in the DataOp.
1196+
1197+
Parameters
1198+
----------
1199+
all_named_ops : bool, default = False
1200+
If False, return only actual variables (DataOps created with
1201+
:func:`var()`, :func:`X()` or :func:`y()`). If False, return all
1202+
nodes that have a name (ie for which a value can be passed in the
1203+
environment).
1204+
1205+
Returns
1206+
-------
1207+
dict :
1208+
Keys are names, and values the corresponding DataOp.
1209+
1210+
Examples
1211+
--------
1212+
>>> import skrub
1213+
1214+
>>> a = skrub.var("a")
1215+
>>> b = skrub.var("b")
1216+
>>> c = (a + b).skb.set_name("c")
1217+
>>> d = c + c
1218+
>>> d
1219+
<BinOp: add>
1220+
1221+
Our DataOp, `d`, contains 2 variables: "a" and "b":
1222+
1223+
>>> d.skb.get_vars()
1224+
{'a': <Var 'a'>, 'b': <Var 'b'>}
1225+
1226+
Those are the keys for which we need to provide values in the
1227+
environment when evaluating `d`:
1228+
1229+
>>> d.skb.eval({"a": 10, "b": 3}) # (10 + 3) + (10 + 3) = 26
1230+
26
1231+
1232+
In addition, we set a name on the internal node `c`. It is not a
1233+
variable, and normally it is computed as `(a + b)`. But as it has a
1234+
name, we can override its output by passing a value for "c" in the
1235+
environment. When we do, the computation of `c` never happens (nor of
1236+
`a` or `b`, here, because they are only used to compute `c`) -- it is
1237+
bypassed and the provided value is used instead.
1238+
1239+
>>> d.skb.eval({"c": 7}) # 7 + 7 = 14
1240+
14
1241+
1242+
If we want ``get_vars`` to also list nodes like our example ``c`` which
1243+
have a name and can be passed in the environment, we pass
1244+
``all_named_ops=True``:
1245+
1246+
>>> d.skb.get_vars(all_named_ops=True)
1247+
{'a': <Var 'a'>, 'b': <Var 'b'>, 'c': <c | BinOp: add>}
1248+
1249+
Note ``get_vars`` can be particularly useful when we have a learner
1250+
(e.g. loaded from a pickle file) and we want to check what inputs we
1251+
should pass to its methods such as ``fit`` and ``transform``:
1252+
1253+
>>> learner = d.skb.make_learner()
1254+
>>> list(learner.data_op.skb.get_vars().keys())
1255+
['a', 'b']
1256+
1257+
The output above tells us what keys the dict we pass to
1258+
``learner.fit()`` should contain:
1259+
1260+
>>> learner.fit({'a': 2, 'b': 3})
1261+
SkrubLearner(data_op=<BinOp: add>)
1262+
"""
1263+
from ._data_ops import Var
1264+
from ._evaluation import nodes
1265+
1266+
named_nodes = {
1267+
name: op
1268+
for op in nodes(self._data_op)
1269+
if (name := op._skrub_impl.name) is not None
1270+
}
1271+
if all_named_ops:
1272+
return named_nodes
1273+
return {
1274+
name: op
1275+
for name, op in named_nodes.items()
1276+
if isinstance(op._skrub_impl, Var)
1277+
}
1278+
11931279
def draw_graph(self):
11941280
"""Get an SVG string representing the computation graph.
11951281

skrub/_data_ops/tests/test_data_ops.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,3 +629,13 @@ def test_class_skb():
629629
from skrub._data_ops._skrub_namespace import SkrubNamespace
630630

631631
assert skrub.DataOp.skb is SkrubNamespace
632+
633+
634+
def test_get_vars():
635+
a = skrub.var("a")
636+
b = skrub.var("b")
637+
c = (a + b).skb.set_name("c")
638+
d = c + c
639+
assert list(d.skb.get_vars().keys()) == ["a", "b"]
640+
assert d.skb.get_vars()["a"] is a
641+
assert list(d.skb.get_vars(all_named_ops=True).keys()) == ["a", "b", "c"]

0 commit comments

Comments
 (0)