Skip to content

Commit f86780f

Browse files
committed
Add learner.load_dataframe
1 parent 25286f6 commit f86780f

File tree

8 files changed

+129
-25
lines changed

8 files changed

+129
-25
lines changed

adaptive/learner/average_learner.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
from adaptive.learner.base_learner import BaseLearner
1010
from adaptive.notebook_integration import ensure_holoviews
1111
from adaptive.types import Float, Real
12-
from adaptive.utils import assign_defaults, cache_latest
12+
from adaptive.utils import (
13+
assign_defaults,
14+
cache_latest,
15+
partial_function_from_dataframe,
16+
)
1317

1418
try:
1519
import pandas
@@ -92,6 +96,20 @@ def to_dataframe(
9296
assign_defaults(self.function, df, function_prefix)
9397
return df
9498

99+
def load_dataframe(
100+
self,
101+
df,
102+
with_default_function_args: bool = True,
103+
function_prefix: str = "function.",
104+
seed_name: str = "seed",
105+
y_name: str = "y",
106+
):
107+
self.tell_many(df[seed_name].values, df[y_name].values)
108+
if with_default_function_args:
109+
self.function = partial_function_from_dataframe(
110+
self.function, df, function_prefix
111+
)
112+
95113
def ask(self, n: int, tell_pending: bool = True) -> tuple[list[int], list[Float]]:
96114
points = list(range(self.n_requested, self.n_requested + n))
97115

adaptive/learner/average_learner1D.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from adaptive.learner.learner1D import Learner1D, _get_intervals
1616
from adaptive.notebook_integration import ensure_holoviews
1717
from adaptive.types import Real
18-
from adaptive.utils import assign_defaults
18+
from adaptive.utils import assign_defaults, partial_function_from_dataframe
1919

2020
try:
2121
import pandas
@@ -174,6 +174,21 @@ def to_dataframe(
174174
assign_defaults(self.function, df, function_prefix)
175175
return df
176176

177+
def load_dataframe(
178+
self,
179+
df,
180+
with_default_function_args: bool = True,
181+
function_prefix: str = "function.",
182+
seed_name: str = "seed",
183+
x_name: str = "x",
184+
y_name: str = "y",
185+
):
186+
self.tell_many(df[[seed_name, x_name]].values, df[y_name].values)
187+
if with_default_function_args:
188+
self.function = partial_function_from_dataframe(
189+
self.function, df, function_prefix
190+
)
191+
177192
def ask(self, n: int, tell_pending: bool = True) -> tuple[Points, list[float]]:
178193
"""Return 'n' points that are expected to maximally reduce the loss."""
179194
# If some point is undersampled, resample it

adaptive/learner/learner1D.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616
from adaptive.learner.triangulation import simplex_volume_in_embedding
1717
from adaptive.notebook_integration import ensure_holoviews
1818
from adaptive.types import Float, Int, Real
19-
from adaptive.utils import assign_defaults, cache_latest
19+
from adaptive.utils import (
20+
assign_defaults,
21+
cache_latest,
22+
partial_function_from_dataframe,
23+
)
2024

2125
try:
2226
import pandas
@@ -339,12 +343,26 @@ def to_dataframe(
339343
if not with_pandas:
340344
raise ImportError("pandas is not installed.")
341345
xs, ys = zip(*sorted(self.data.items())) if self.data else ([], [])
342-
df = pandas.DataFrame(xs, columns=["x"])
346+
df = pandas.DataFrame(xs, columns=[x_name])
343347
df[y_name] = ys
344348
if with_default_function_args:
345349
assign_defaults(self.function, df, function_prefix)
346350
return df
347351

352+
def load_dataframe(
353+
self,
354+
df,
355+
with_default_function_args: bool = True,
356+
function_prefix: str = "function.",
357+
x_name: str = "x",
358+
y_name: str = "y",
359+
):
360+
self.tell_many(df[x_name].values, df[y_name].values)
361+
if with_default_function_args:
362+
self.function = partial_function_from_dataframe(
363+
self.function, df, function_prefix
364+
)
365+
348366
@property
349367
def npoints(self) -> int:
350368
"""Number of evaluated points."""

adaptive/learner/learner2D.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
from adaptive.learner.base_learner import BaseLearner
1212
from adaptive.learner.triangulation import simplex_volume_in_embedding
1313
from adaptive.notebook_integration import ensure_holoviews
14-
from adaptive.utils import assign_defaults, cache_latest
14+
from adaptive.utils import (
15+
assign_defaults,
16+
cache_latest,
17+
partial_function_from_dataframe,
18+
)
1519

1620
try:
1721
import pandas
@@ -409,6 +413,21 @@ def to_dataframe(
409413
assign_defaults(self.function, df, function_prefix)
410414
return df
411415

416+
def load_dataframe(
417+
self,
418+
df,
419+
with_default_function_args: bool = True,
420+
function_prefix: str = "function.",
421+
x_name: str = "x",
422+
y_name: str = "y",
423+
z_name: str = "z",
424+
):
425+
self.tell_many(df[[x_name, y_name]].values, df[z_name].values)
426+
if with_default_function_args:
427+
self.function = partial_function_from_dataframe(
428+
self.function, df, function_prefix
429+
)
430+
412431
def _scale(self, points):
413432
points = np.asarray(points, dtype=float)
414433
return (points - self.xy_mean) / self.xy_scale

adaptive/learner/learnerND.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@
2121
simplex_volume_in_embedding,
2222
)
2323
from adaptive.notebook_integration import ensure_holoviews, ensure_plotly
24-
from adaptive.utils import assign_defaults, cache_latest, restore
24+
from adaptive.utils import (
25+
assign_defaults,
26+
cache_latest,
27+
partial_function_from_dataframe,
28+
restore,
29+
)
2530

2631
try:
2732
import pandas
@@ -418,6 +423,20 @@ def to_dataframe(
418423
assign_defaults(self.function, df, function_prefix)
419424
return df
420425

426+
def load_dataframe(
427+
self,
428+
df,
429+
with_default_function_args: bool = True,
430+
function_prefix: str = "function.",
431+
point_names: tuple[str, ...] = ("x", "y", "z"),
432+
value_name: str = "value",
433+
):
434+
self.tell_many(df[list(point_names)].values, df[value_name].values)
435+
if with_default_function_args:
436+
self.function = partial_function_from_dataframe(
437+
self.function, df, function_prefix
438+
)
439+
421440
@property
422441
def bounds_are_done(self):
423442
return all(p in self.data for p in self._bounds_points)

adaptive/learner/sequence_learner.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from sortedcontainers import SortedDict, SortedSet
55

66
from adaptive.learner.base_learner import BaseLearner
7-
from adaptive.utils import assign_defaults
7+
from adaptive.utils import assign_defaults, partial_function_from_dataframe
88

99
try:
1010
import pandas
@@ -148,6 +148,21 @@ def to_dataframe(
148148
assign_defaults(self.function, df, function_prefix)
149149
return df
150150

151+
def load_dataframe(
152+
self,
153+
df,
154+
with_default_function_args: bool = True,
155+
function_prefix: str = "function.",
156+
index_name: str = "i",
157+
x_name: str = "x",
158+
y_name: str = "y",
159+
):
160+
self.tell_many(df[[index_name, x_name]].values, df[y_name].values)
161+
if with_default_function_args:
162+
self.function = partial_function_from_dataframe(
163+
self.function, df, function_prefix
164+
)
165+
151166
def _get_data(self):
152167
return self.data
153168

adaptive/tests/test_learners.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -722,22 +722,7 @@ def test_to_dataframe(learner_type, f, learner_kwargs):
722722

723723
# Add points from the DataFrame to a new empty learner
724724
learner2 = learner_type(generate_random_parametrization(f), **learner_kwargs)
725-
726-
if learner_type is Learner1D:
727-
learner2.tell_many(df["x"], df["y"])
728-
elif learner_type is Learner2D:
729-
learner2.tell_many(df[["x", "y"]].values, df["z"])
730-
elif learner_type is LearnerND:
731-
point_names = list(kw["point_names"])
732-
learner2.tell_many(df[point_names].values, df["value"])
733-
elif learner_type is AverageLearner:
734-
learner2.tell_many(df["seed"].values, df["y"])
735-
elif learner_type is AverageLearner1D:
736-
learner2.tell_many(df[["seed", "x"]].values, df["y"])
737-
elif learner_type is SequenceLearner:
738-
learner2.tell_many(df[["i", "x"]].values, df["y"])
739-
else:
740-
raise NotImplementedError()
725+
learner2.load_dataframe(df, **kw)
741726

742727
# Test this for a learner in a BalancingLearner
743728
learners = [

adaptive/utils.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def __call__(self, *args, **kwargs):
101101
return obj
102102

103103

104-
def _default_parameters(function, function_prefix="", start_index=1):
104+
def _default_parameters(function, function_prefix: str = "", start_index: int = 1):
105105
sig = inspect.signature(function)
106106
defaults = {
107107
f"{function_prefix}{k}": v.default
@@ -111,7 +111,22 @@ def _default_parameters(function, function_prefix="", start_index=1):
111111
return defaults
112112

113113

114-
def assign_defaults(function, df, function_prefix="", start_index=1):
114+
def assign_defaults(function, df, function_prefix: str = "", start_index: int = 1):
115115
defaults = _default_parameters(function, function_prefix, start_index)
116116
for k, v in defaults.items():
117117
df[k] = len(df) * [v]
118+
119+
120+
def partial_function_from_dataframe(function, df, function_prefix: str = ""):
121+
kwargs = {}
122+
for col in df.columns:
123+
if col.startswith(function_prefix):
124+
k = col.split(function_prefix, 1)[1]
125+
vs = df[col]
126+
v, *rest = vs.unique()
127+
if rest:
128+
raise ValueError(f"The column '{col}' can only have one value.")
129+
kwargs[k] = v
130+
if not kwargs:
131+
return function
132+
return functools.partial(function, **kwargs)

0 commit comments

Comments
 (0)