edsl 0.1.29.dev6__py3-none-any.whl → 0.1.30.dev1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- edsl/Base.py +6 -3
- edsl/__init__.py +23 -23
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +35 -34
- edsl/agents/AgentList.py +16 -5
- edsl/agents/Invigilator.py +19 -1
- edsl/agents/descriptors.py +2 -1
- edsl/base/Base.py +289 -0
- edsl/config.py +2 -1
- edsl/coop/utils.py +28 -1
- edsl/data/Cache.py +19 -5
- edsl/data/SQLiteDict.py +11 -3
- edsl/jobs/Answers.py +15 -1
- edsl/jobs/Jobs.py +69 -31
- edsl/jobs/buckets/ModelBuckets.py +4 -2
- edsl/jobs/buckets/TokenBucket.py +1 -2
- edsl/jobs/interviews/Interview.py +0 -6
- edsl/jobs/interviews/InterviewTaskBuildingMixin.py +9 -5
- edsl/jobs/runners/JobsRunnerAsyncio.py +12 -16
- edsl/jobs/tasks/TaskHistory.py +4 -3
- edsl/language_models/LanguageModel.py +5 -11
- edsl/language_models/ModelList.py +1 -1
- edsl/language_models/repair.py +8 -7
- edsl/notebooks/Notebook.py +9 -3
- edsl/questions/QuestionBase.py +6 -2
- edsl/questions/QuestionBudget.py +5 -6
- edsl/questions/QuestionCheckBox.py +7 -3
- edsl/questions/QuestionExtract.py +5 -3
- edsl/questions/QuestionFreeText.py +3 -3
- edsl/questions/QuestionFunctional.py +0 -3
- edsl/questions/QuestionList.py +3 -4
- edsl/questions/QuestionMultipleChoice.py +12 -5
- edsl/questions/QuestionNumerical.py +4 -3
- edsl/questions/QuestionRank.py +5 -3
- edsl/questions/__init__.py +4 -3
- edsl/questions/descriptors.py +4 -2
- edsl/results/DatasetExportMixin.py +491 -0
- edsl/results/Result.py +13 -65
- edsl/results/Results.py +91 -39
- edsl/results/ResultsDBMixin.py +7 -3
- edsl/results/ResultsExportMixin.py +22 -537
- edsl/results/ResultsGGMixin.py +3 -3
- edsl/results/ResultsToolsMixin.py +1 -4
- edsl/scenarios/FileStore.py +140 -0
- edsl/scenarios/Scenario.py +5 -6
- edsl/scenarios/ScenarioList.py +17 -8
- edsl/scenarios/ScenarioListExportMixin.py +32 -0
- edsl/scenarios/ScenarioListPdfMixin.py +2 -1
- edsl/scenarios/__init__.py +1 -0
- edsl/surveys/MemoryPlan.py +11 -4
- edsl/surveys/Survey.py +9 -4
- edsl/surveys/SurveyExportMixin.py +4 -2
- edsl/surveys/SurveyFlowVisualizationMixin.py +6 -4
- edsl/utilities/__init__.py +21 -21
- edsl/utilities/interface.py +66 -45
- edsl/utilities/utilities.py +11 -13
- {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dev1.dist-info}/METADATA +1 -1
- {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dev1.dist-info}/RECORD +60 -56
- {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dev1.dist-info}/LICENSE +0 -0
- {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dev1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,491 @@
|
|
1
|
+
"""Mixin class for exporting results."""
|
2
|
+
|
3
|
+
import base64
|
4
|
+
import csv
|
5
|
+
import io
|
6
|
+
|
7
|
+
from typing import Literal, Optional, Union
|
8
|
+
|
9
|
+
|
10
|
+
class DatasetExportMixin:
|
11
|
+
"""Mixin class"""
|
12
|
+
|
13
|
+
def relevant_columns(
|
14
|
+
self, data_type: Optional[str] = None, remove_prefix=False
|
15
|
+
) -> list:
|
16
|
+
"""Return the set of keys that are present in the dataset.
|
17
|
+
|
18
|
+
>>> from edsl.results.Dataset import Dataset
|
19
|
+
>>> d = Dataset([{'a.b':[1,2,3,4]}])
|
20
|
+
>>> d.relevant_columns()
|
21
|
+
['a.b']
|
22
|
+
|
23
|
+
>>> d.relevant_columns(remove_prefix=True)
|
24
|
+
['b']
|
25
|
+
|
26
|
+
>>> from edsl.results import Results; Results.example().select('how_feeling', 'how_feeling_yesterday').relevant_columns()
|
27
|
+
['answer.how_feeling', 'answer.how_feeling_yesterday']
|
28
|
+
"""
|
29
|
+
columns = [list(x.keys())[0] for x in self]
|
30
|
+
# columns = set([list(result.keys())[0] for result in self.data])
|
31
|
+
if remove_prefix:
|
32
|
+
columns = [column.split(".")[-1] for column in columns]
|
33
|
+
|
34
|
+
if data_type:
|
35
|
+
columns = [
|
36
|
+
column for column in columns if column.split(".")[0] == data_type
|
37
|
+
]
|
38
|
+
|
39
|
+
return columns
|
40
|
+
|
41
|
+
def _make_tabular(self, remove_prefix: bool, pretty_labels: Optional[dict] = None):
|
42
|
+
"""Turn the results into a tabular format.
|
43
|
+
|
44
|
+
:param remove_prefix: Whether to remove the prefix from the column names.
|
45
|
+
|
46
|
+
>>> from edsl.results import Results
|
47
|
+
>>> r = Results.example()
|
48
|
+
>>> r.select('how_feeling')._make_tabular(remove_prefix = True)
|
49
|
+
(['how_feeling'], [['OK'], ['Great'], ['Terrible'], ['OK']])
|
50
|
+
|
51
|
+
>>> r.select('how_feeling')._make_tabular(remove_prefix = True, pretty_labels = {'how_feeling': "How are you feeling"})
|
52
|
+
(['How are you feeling'], [['OK'], ['Great'], ['Terrible'], ['OK']])
|
53
|
+
"""
|
54
|
+
d = {}
|
55
|
+
full_header = sorted(list(self.relevant_columns()))
|
56
|
+
for entry in self.data:
|
57
|
+
key, list_of_values = list(entry.items())[0]
|
58
|
+
d[key] = list_of_values
|
59
|
+
if remove_prefix:
|
60
|
+
header = [h.split(".")[-1] for h in full_header]
|
61
|
+
else:
|
62
|
+
header = full_header
|
63
|
+
num_observations = len(list(self[0].values())[0])
|
64
|
+
rows = []
|
65
|
+
# rows.append(header)
|
66
|
+
for i in range(num_observations):
|
67
|
+
row = [d[h][i] for h in full_header]
|
68
|
+
rows.append(row)
|
69
|
+
if pretty_labels is not None:
|
70
|
+
header = [pretty_labels.get(h, h) for h in header]
|
71
|
+
return header, rows
|
72
|
+
|
73
|
+
def print_long(self):
|
74
|
+
"""Print the results in a long format."""
|
75
|
+
for entry in self:
|
76
|
+
key, list_of_values = list(entry.items())[0]
|
77
|
+
for value in list_of_values:
|
78
|
+
print(f"{key}: {value}")
|
79
|
+
|
80
|
+
def print(
|
81
|
+
self,
|
82
|
+
pretty_labels: Optional[dict] = None,
|
83
|
+
filename: Optional[str] = None,
|
84
|
+
format: Literal["rich", "html", "markdown", "latex"] = None,
|
85
|
+
interactive: bool = False,
|
86
|
+
split_at_dot: bool = True,
|
87
|
+
max_rows=None,
|
88
|
+
tee=False,
|
89
|
+
iframe=False,
|
90
|
+
iframe_height: int = 200,
|
91
|
+
iframe_width: int = 600,
|
92
|
+
web=False,
|
93
|
+
) -> None:
|
94
|
+
"""Print the results in a pretty format.
|
95
|
+
|
96
|
+
:param pretty_labels: A dictionary of pretty labels for the columns.
|
97
|
+
:param filename: The filename to save the results to.
|
98
|
+
:param format: The format to print the results in. Options are 'rich', 'html', or 'markdown'.
|
99
|
+
:param interactive: Whether to print the results interactively in a Jupyter notebook.
|
100
|
+
:param split_at_dot: Whether to split the column names at the last dot w/ a newline.
|
101
|
+
|
102
|
+
Example: Print in rich format at the terminal
|
103
|
+
|
104
|
+
>>> from edsl.results import Results
|
105
|
+
>>> r = Results.example()
|
106
|
+
>>> r.select('how_feeling').print(format = "rich")
|
107
|
+
┏━━━━━━━━━━━━━━┓
|
108
|
+
┃ answer ┃
|
109
|
+
┃ .how_feeling ┃
|
110
|
+
┡━━━━━━━━━━━━━━┩
|
111
|
+
│ OK │
|
112
|
+
├──────────────┤
|
113
|
+
│ Great │
|
114
|
+
├──────────────┤
|
115
|
+
│ Terrible │
|
116
|
+
├──────────────┤
|
117
|
+
│ OK │
|
118
|
+
└──────────────┘
|
119
|
+
|
120
|
+
Example: using the pretty_labels parameter
|
121
|
+
|
122
|
+
>>> r.select('how_feeling').print(format="rich", pretty_labels = {'answer.how_feeling': "How are you feeling"})
|
123
|
+
┏━━━━━━━━━━━━━━━━━━━━━┓
|
124
|
+
┃ How are you feeling ┃
|
125
|
+
┡━━━━━━━━━━━━━━━━━━━━━┩
|
126
|
+
│ OK │
|
127
|
+
├─────────────────────┤
|
128
|
+
│ Great │
|
129
|
+
├─────────────────────┤
|
130
|
+
│ Terrible │
|
131
|
+
├─────────────────────┤
|
132
|
+
│ OK │
|
133
|
+
└─────────────────────┘
|
134
|
+
|
135
|
+
Example: printing in markdown format
|
136
|
+
|
137
|
+
>>> r.select('how_feeling').print(format='markdown')
|
138
|
+
| answer.how_feeling |
|
139
|
+
|--|
|
140
|
+
| OK |
|
141
|
+
| Great |
|
142
|
+
| Terrible |
|
143
|
+
| OK |
|
144
|
+
...
|
145
|
+
"""
|
146
|
+
from IPython.display import HTML, display
|
147
|
+
from edsl.utilities.utilities import is_notebook
|
148
|
+
|
149
|
+
if format is None:
|
150
|
+
if is_notebook():
|
151
|
+
format = "html"
|
152
|
+
else:
|
153
|
+
format = "rich"
|
154
|
+
|
155
|
+
if pretty_labels is None:
|
156
|
+
pretty_labels = {}
|
157
|
+
|
158
|
+
if format not in ["rich", "html", "markdown", "latex"]:
|
159
|
+
raise ValueError("format must be one of 'rich', 'html', or 'markdown'.")
|
160
|
+
|
161
|
+
new_data = []
|
162
|
+
for index, entry in enumerate(self):
|
163
|
+
key, list_of_values = list(entry.items())[0]
|
164
|
+
new_data.append({pretty_labels.get(key, key): list_of_values})
|
165
|
+
|
166
|
+
if max_rows is not None:
|
167
|
+
for entry in new_data:
|
168
|
+
for key in entry:
|
169
|
+
actual_rows = len(entry[key])
|
170
|
+
entry[key] = entry[key][:max_rows]
|
171
|
+
# print(f"Showing only the first {max_rows} rows of {actual_rows} rows.")
|
172
|
+
|
173
|
+
if format == "rich":
|
174
|
+
from edsl.utilities.interface import print_dataset_with_rich
|
175
|
+
|
176
|
+
print_dataset_with_rich(
|
177
|
+
new_data, filename=filename, split_at_dot=split_at_dot
|
178
|
+
)
|
179
|
+
elif format == "html":
|
180
|
+
notebook = is_notebook()
|
181
|
+
from edsl.utilities.interface import print_list_of_dicts_as_html_table
|
182
|
+
|
183
|
+
html_source = print_list_of_dicts_as_html_table(
|
184
|
+
new_data, interactive=interactive
|
185
|
+
)
|
186
|
+
if iframe:
|
187
|
+
import html
|
188
|
+
|
189
|
+
height = iframe_height
|
190
|
+
width = iframe_width
|
191
|
+
escaped_output = html.escape(html_source)
|
192
|
+
# escaped_output = html_source
|
193
|
+
iframe = f""""
|
194
|
+
<iframe srcdoc="{ escaped_output }" style="width: {width}px; height: {height}px;"></iframe>
|
195
|
+
"""
|
196
|
+
display(HTML(iframe))
|
197
|
+
elif notebook:
|
198
|
+
display(HTML(html_source))
|
199
|
+
else:
|
200
|
+
from edsl.utilities.interface import view_html
|
201
|
+
|
202
|
+
view_html(html_source)
|
203
|
+
|
204
|
+
elif format == "markdown":
|
205
|
+
from edsl.utilities.interface import print_list_of_dicts_as_markdown_table
|
206
|
+
|
207
|
+
print_list_of_dicts_as_markdown_table(new_data, filename=filename)
|
208
|
+
elif format == "latex":
|
209
|
+
df = self.to_pandas()
|
210
|
+
df.columns = [col.replace("_", " ") for col in df.columns]
|
211
|
+
latex_string = df.to_latex()
|
212
|
+
if filename is not None:
|
213
|
+
with open(filename, "w") as f:
|
214
|
+
f.write(latex_string)
|
215
|
+
else:
|
216
|
+
return latex_string
|
217
|
+
# raise NotImplementedError("Latex format not yet implemented.")
|
218
|
+
# latex_string = create_latex_table_from_data(new_data, filename=filename)
|
219
|
+
# if filename is None:
|
220
|
+
# return latex_string
|
221
|
+
# Not working quite
|
222
|
+
|
223
|
+
else:
|
224
|
+
raise ValueError("format not recognized.")
|
225
|
+
|
226
|
+
if tee:
|
227
|
+
return self
|
228
|
+
|
229
|
+
def to_csv(
|
230
|
+
self,
|
231
|
+
filename: Optional[str] = None,
|
232
|
+
remove_prefix: bool = False,
|
233
|
+
download_link: bool = False,
|
234
|
+
pretty_labels: Optional[dict] = None,
|
235
|
+
):
|
236
|
+
"""Export the results to a CSV file.
|
237
|
+
|
238
|
+
:param filename: The filename to save the CSV file to.
|
239
|
+
:param remove_prefix: Whether to remove the prefix from the column names.
|
240
|
+
:param download_link: Whether to display a download link in a Jupyter notebook.
|
241
|
+
|
242
|
+
Example:
|
243
|
+
|
244
|
+
>>> from edsl.results import Results
|
245
|
+
>>> r = Results.example()
|
246
|
+
>>> r.select('how_feeling').to_csv()
|
247
|
+
'answer.how_feeling\\r\\nOK\\r\\nGreat\\r\\nTerrible\\r\\nOK\\r\\n'
|
248
|
+
"""
|
249
|
+
if pretty_labels is None:
|
250
|
+
pretty_labels = {}
|
251
|
+
header, rows = self._make_tabular(
|
252
|
+
remove_prefix=remove_prefix, pretty_labels=pretty_labels
|
253
|
+
)
|
254
|
+
|
255
|
+
if filename is not None:
|
256
|
+
with open(filename, "w") as f:
|
257
|
+
writer = csv.writer(f)
|
258
|
+
writer.writerow(header)
|
259
|
+
writer.writerows(rows)
|
260
|
+
else:
|
261
|
+
output = io.StringIO()
|
262
|
+
writer = csv.writer(output)
|
263
|
+
writer.writerow(header)
|
264
|
+
writer.writerows(rows)
|
265
|
+
|
266
|
+
if download_link:
|
267
|
+
csv_file = output.getvalue()
|
268
|
+
b64 = base64.b64encode(csv_file.encode()).decode()
|
269
|
+
download_link = f'<a href="data:file/csv;base64,{b64}" download="my_data.csv">Download CSV file</a>'
|
270
|
+
display(HTML(download_link))
|
271
|
+
else:
|
272
|
+
return output.getvalue()
|
273
|
+
|
274
|
+
def to_pandas(self, remove_prefix: bool = False) -> "pd.DataFrame":
|
275
|
+
"""Convert the results to a pandas DataFrame.
|
276
|
+
|
277
|
+
:param remove_prefix: Whether to remove the prefix from the column names.
|
278
|
+
|
279
|
+
>>> from edsl.results import Results
|
280
|
+
>>> r = Results.example()
|
281
|
+
>>> r.select('how_feeling').to_pandas()
|
282
|
+
answer.how_feeling
|
283
|
+
0 OK
|
284
|
+
1 Great
|
285
|
+
2 Terrible
|
286
|
+
3 OK
|
287
|
+
"""
|
288
|
+
import pandas as pd
|
289
|
+
|
290
|
+
csv_string = self.to_csv(remove_prefix=remove_prefix)
|
291
|
+
csv_buffer = io.StringIO(csv_string)
|
292
|
+
df = pd.read_csv(csv_buffer)
|
293
|
+
df_sorted = df.sort_index(axis=1) # Sort columns alphabetically
|
294
|
+
return df_sorted
|
295
|
+
|
296
|
+
def to_scenario_list(self, remove_prefix: bool = True) -> list[dict]:
|
297
|
+
"""Convert the results to a list of dictionaries, one per scenario.
|
298
|
+
|
299
|
+
:param remove_prefix: Whether to remove the prefix from the column names.
|
300
|
+
|
301
|
+
>>> from edsl.results import Results
|
302
|
+
>>> r = Results.example()
|
303
|
+
>>> r.select('how_feeling').to_scenario_list()
|
304
|
+
ScenarioList([Scenario({'how_feeling': 'OK'}), Scenario({'how_feeling': 'Great'}), Scenario({'how_feeling': 'Terrible'}), Scenario({'how_feeling': 'OK'})])
|
305
|
+
"""
|
306
|
+
from edsl import ScenarioList, Scenario
|
307
|
+
|
308
|
+
list_of_dicts = self.to_dicts(remove_prefix=remove_prefix)
|
309
|
+
return ScenarioList([Scenario(d) for d in list_of_dicts])
|
310
|
+
|
311
|
+
def to_agent_list(self, remove_prefix: bool = True):
|
312
|
+
from edsl import AgentList, Agent
|
313
|
+
|
314
|
+
list_of_dicts = self.to_dicts(remove_prefix=remove_prefix)
|
315
|
+
return AgentList([Agent(d) for d in list_of_dicts])
|
316
|
+
|
317
|
+
def to_dicts(self, remove_prefix: bool = True) -> list[dict]:
|
318
|
+
"""Convert the results to a list of dictionaries.
|
319
|
+
|
320
|
+
:param remove_prefix: Whether to remove the prefix from the column names.
|
321
|
+
|
322
|
+
>>> from edsl.results import Results
|
323
|
+
>>> r = Results.example()
|
324
|
+
>>> r.select('how_feeling').to_dicts()
|
325
|
+
[{'how_feeling': 'OK'}, {'how_feeling': 'Great'}, {'how_feeling': 'Terrible'}, {'how_feeling': 'OK'}]
|
326
|
+
|
327
|
+
"""
|
328
|
+
list_of_keys = []
|
329
|
+
list_of_values = []
|
330
|
+
for entry in self:
|
331
|
+
key, values = list(entry.items())[0]
|
332
|
+
list_of_keys.append(key)
|
333
|
+
list_of_values.append(values)
|
334
|
+
|
335
|
+
if remove_prefix:
|
336
|
+
list_of_keys = [key.split(".")[-1] for key in list_of_keys]
|
337
|
+
|
338
|
+
list_of_dicts = []
|
339
|
+
for entries in zip(*list_of_values):
|
340
|
+
list_of_dicts.append(dict(zip(list_of_keys, entries)))
|
341
|
+
|
342
|
+
return list_of_dicts
|
343
|
+
|
344
|
+
def to_list(self, flatten=False, remove_none=False) -> list[list]:
|
345
|
+
"""Convert the results to a list of lists.
|
346
|
+
|
347
|
+
>>> from edsl.results import Results
|
348
|
+
>>> Results.example().select('how_feeling', 'how_feeling_yesterday')
|
349
|
+
Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}, {'answer.how_feeling_yesterday': ['Great', 'Good', 'OK', 'Terrible']}])
|
350
|
+
|
351
|
+
>>> Results.example().select('how_feeling', 'how_feeling_yesterday').to_list()
|
352
|
+
[('OK', 'Great'), ('Great', 'Good'), ('Terrible', 'OK'), ('OK', 'Terrible')]
|
353
|
+
|
354
|
+
>>> r = Results.example()
|
355
|
+
>>> r.select('how_feeling').to_list()
|
356
|
+
['OK', 'Great', 'Terrible', 'OK']
|
357
|
+
"""
|
358
|
+
if len(self.relevant_columns()) > 1 and flatten:
|
359
|
+
raise ValueError(
|
360
|
+
"Cannot flatten a list of lists when there are multiple columns selected."
|
361
|
+
)
|
362
|
+
|
363
|
+
if len(self.relevant_columns()) == 1:
|
364
|
+
# if only one 'column' is selected (which is typical for this method
|
365
|
+
list_to_return = list(self[0].values())[0]
|
366
|
+
else:
|
367
|
+
keys = self.relevant_columns()
|
368
|
+
data = self.to_dicts(remove_prefix=False)
|
369
|
+
list_to_return = []
|
370
|
+
for d in data:
|
371
|
+
list_to_return.append(tuple([d[key] for key in keys]))
|
372
|
+
|
373
|
+
if remove_none:
|
374
|
+
list_to_return = [item for item in list_to_return if item is not None]
|
375
|
+
|
376
|
+
if flatten:
|
377
|
+
new_list = []
|
378
|
+
for item in list_to_return:
|
379
|
+
if isinstance(item, list):
|
380
|
+
new_list.extend(item)
|
381
|
+
else:
|
382
|
+
new_list.append(item)
|
383
|
+
list_to_return = new_list
|
384
|
+
|
385
|
+
return list_to_return
|
386
|
+
|
387
|
+
def html(
|
388
|
+
self, filename: str = None, cta: str = "Open in browser", return_link=False
|
389
|
+
):
|
390
|
+
import os
|
391
|
+
import tempfile
|
392
|
+
from edsl.utilities.utilities import is_notebook
|
393
|
+
from IPython.display import HTML, display
|
394
|
+
from edsl.utilities.utilities import is_notebook
|
395
|
+
|
396
|
+
df = self.to_pandas()
|
397
|
+
|
398
|
+
if filename is None:
|
399
|
+
current_directory = os.getcwd()
|
400
|
+
filename = tempfile.NamedTemporaryFile(
|
401
|
+
"w", delete=False, suffix=".html", dir=current_directory
|
402
|
+
).name
|
403
|
+
|
404
|
+
with open(filename, "w") as f:
|
405
|
+
f.write(df.to_html())
|
406
|
+
|
407
|
+
if is_notebook():
|
408
|
+
html_url = f"/files/{filename}"
|
409
|
+
html_link = f'<a href="{html_url}" target="_blank">{cta}</a>'
|
410
|
+
display(HTML(html_link))
|
411
|
+
else:
|
412
|
+
print(f"Saved to {filename}")
|
413
|
+
import webbrowser
|
414
|
+
import os
|
415
|
+
|
416
|
+
webbrowser.open(f"file://{os.path.abspath(filename)}")
|
417
|
+
|
418
|
+
if return_link:
|
419
|
+
return filename
|
420
|
+
|
421
|
+
def tally(
|
422
|
+
self, *fields: Optional[str], top_n=None, output="dict"
|
423
|
+
) -> Union[dict, "Dataset"]:
|
424
|
+
"""Tally the values of a field or perform a cross-tab of multiple fields.
|
425
|
+
|
426
|
+
:param fields: The field(s) to tally, multiple fields for cross-tabulation.
|
427
|
+
|
428
|
+
>>> from edsl.results import Results
|
429
|
+
>>> r = Results.example()
|
430
|
+
>>> r.select('how_feeling').tally('answer.how_feeling')
|
431
|
+
{'OK': 2, 'Great': 1, 'Terrible': 1}
|
432
|
+
>>> r.select('how_feeling', 'period').tally('how_feeling', 'period')
|
433
|
+
{('OK', 'morning'): 1, ('Great', 'afternoon'): 1, ('Terrible', 'morning'): 1, ('OK', 'afternoon'): 1}
|
434
|
+
"""
|
435
|
+
from collections import Counter
|
436
|
+
|
437
|
+
if len(fields) == 0:
|
438
|
+
fields = self.relevant_columns()
|
439
|
+
|
440
|
+
relevant_columns_without_prefix = [
|
441
|
+
column.split(".")[-1] for column in self.relevant_columns()
|
442
|
+
]
|
443
|
+
|
444
|
+
if not all(
|
445
|
+
f in self.relevant_columns() or f in relevant_columns_without_prefix
|
446
|
+
for f in fields
|
447
|
+
):
|
448
|
+
raise ValueError("One or more specified fields are not in the dataset.")
|
449
|
+
|
450
|
+
if len(fields) == 1:
|
451
|
+
field = fields[0]
|
452
|
+
values = self._key_to_value(field)
|
453
|
+
else:
|
454
|
+
values = list(zip(*(self._key_to_value(field) for field in fields)))
|
455
|
+
|
456
|
+
for value in values:
|
457
|
+
if isinstance(value, list):
|
458
|
+
value = tuple(value)
|
459
|
+
|
460
|
+
tally = dict(Counter(values))
|
461
|
+
sorted_tally = dict(sorted(tally.items(), key=lambda item: -item[1]))
|
462
|
+
if top_n is not None:
|
463
|
+
sorted_tally = dict(list(sorted_tally.items())[:top_n])
|
464
|
+
|
465
|
+
import warnings
|
466
|
+
import textwrap
|
467
|
+
from edsl.results.Dataset import Dataset
|
468
|
+
|
469
|
+
if output == "dict":
|
470
|
+
warnings.warn(
|
471
|
+
textwrap.dedent(
|
472
|
+
"""\
|
473
|
+
The default output from tally will change to Dataset in the future.
|
474
|
+
Use output='Dataset' to get the Dataset object for now.
|
475
|
+
"""
|
476
|
+
)
|
477
|
+
)
|
478
|
+
return sorted_tally
|
479
|
+
elif output == "Dataset":
|
480
|
+
return Dataset(
|
481
|
+
[
|
482
|
+
{"value": list(sorted_tally.keys())},
|
483
|
+
{"count": list(sorted_tally.values())},
|
484
|
+
]
|
485
|
+
)
|
486
|
+
|
487
|
+
|
488
|
+
if __name__ == "__main__":
|
489
|
+
import doctest
|
490
|
+
|
491
|
+
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
edsl/results/Result.py
CHANGED
@@ -3,16 +3,7 @@ from __future__ import annotations
|
|
3
3
|
from collections import UserDict
|
4
4
|
from typing import Any, Type, Callable, Optional
|
5
5
|
from collections import UserDict
|
6
|
-
|
7
|
-
from rich.table import Table
|
8
|
-
|
9
|
-
from IPython.display import display
|
10
|
-
|
11
|
-
from edsl.agents import Agent
|
12
|
-
from edsl.language_models import LanguageModel
|
13
|
-
from edsl.scenarios import Scenario
|
14
6
|
from edsl.Base import Base
|
15
|
-
from edsl.prompts import Prompt
|
16
7
|
from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
|
17
8
|
|
18
9
|
|
@@ -21,6 +12,8 @@ class PromptDict(UserDict):
|
|
21
12
|
|
22
13
|
def rich_print(self):
|
23
14
|
"""Display an object as a table."""
|
15
|
+
from rich.table import Table
|
16
|
+
|
24
17
|
table = Table(title="")
|
25
18
|
table.add_column("Attribute", style="bold")
|
26
19
|
table.add_column("Value")
|
@@ -71,9 +64,9 @@ class Result(Base, UserDict):
|
|
71
64
|
|
72
65
|
def __init__(
|
73
66
|
self,
|
74
|
-
agent: Agent,
|
75
|
-
scenario: Scenario,
|
76
|
-
model: Type[LanguageModel],
|
67
|
+
agent: "Agent",
|
68
|
+
scenario: "Scenario",
|
69
|
+
model: Type["LanguageModel"],
|
77
70
|
iteration: int,
|
78
71
|
answer: str,
|
79
72
|
prompt: dict[str, str] = None,
|
@@ -278,6 +271,12 @@ class Result(Base, UserDict):
|
|
278
271
|
@remove_edsl_version
|
279
272
|
def from_dict(self, json_dict: dict) -> Result:
|
280
273
|
"""Return a Result object from a dictionary representation."""
|
274
|
+
|
275
|
+
from edsl import Agent
|
276
|
+
from edsl import Scenario
|
277
|
+
from edsl.language_models.LanguageModel import LanguageModel
|
278
|
+
from edsl.prompts.Prompt import Prompt
|
279
|
+
|
281
280
|
prompt_data = json_dict.get("prompt", {})
|
282
281
|
prompt_d = {}
|
283
282
|
for prompt_name, prompt_obj in prompt_data.items():
|
@@ -301,6 +300,7 @@ class Result(Base, UserDict):
|
|
301
300
|
"""Display an object as a table."""
|
302
301
|
# from edsl.utilities import print_dict_with_rich
|
303
302
|
from rich import print
|
303
|
+
from rich.table import Table
|
304
304
|
|
305
305
|
table = Table(title="Result")
|
306
306
|
table.add_column("Attribute", style="bold")
|
@@ -325,7 +325,7 @@ class Result(Base, UserDict):
|
|
325
325
|
@classmethod
|
326
326
|
def example(cls):
|
327
327
|
"""Return an example Result object."""
|
328
|
-
from edsl.results import Results
|
328
|
+
from edsl.results.Results import Results
|
329
329
|
|
330
330
|
return Results.example()[0]
|
331
331
|
|
@@ -350,59 +350,7 @@ class Result(Base, UserDict):
|
|
350
350
|
return scoring_function(**params)
|
351
351
|
|
352
352
|
|
353
|
-
def main():
|
354
|
-
"""Run the main function."""
|
355
|
-
from edsl.results.Result import Result
|
356
|
-
import json
|
357
|
-
|
358
|
-
print("Being imported")
|
359
|
-
json_string = """
|
360
|
-
{
|
361
|
-
"agent": {
|
362
|
-
"traits": {
|
363
|
-
"status": "Unhappy"
|
364
|
-
}
|
365
|
-
},
|
366
|
-
"scenario": {
|
367
|
-
"period": "morning"
|
368
|
-
},
|
369
|
-
"model": {
|
370
|
-
"model": "gpt-3.5-turbo",
|
371
|
-
"parameters": {
|
372
|
-
"temperature": 0.5,
|
373
|
-
"max_tokens": 1000,
|
374
|
-
"top_p": 1,
|
375
|
-
"frequency_penalty": 0,
|
376
|
-
"presence_penalty": 0,
|
377
|
-
"use_cache": true
|
378
|
-
}
|
379
|
-
},
|
380
|
-
"iteration": 0,
|
381
|
-
"answer": {
|
382
|
-
"how_feeling": "Bad"
|
383
|
-
},
|
384
|
-
"prompt": {"how_feeling_user_prompt": "How are you feeling today?", "how_feeling_system_prompt": "Answer the question"}
|
385
|
-
}
|
386
|
-
"""
|
387
|
-
|
388
|
-
result = Result.from_dict(json.loads(json_string))
|
389
|
-
|
390
|
-
result.sub_dicts
|
391
|
-
assert result.combined_dict["how_feeling"] == "Bad"
|
392
|
-
|
393
|
-
result.combined_dict
|
394
|
-
assert result.get_value("answer", "how_feeling") == "Bad"
|
395
|
-
|
396
|
-
result.key_to_data_type
|
397
|
-
print(result)
|
398
|
-
|
399
|
-
assert result == result.copy()
|
400
|
-
|
401
|
-
result.to_dict()
|
402
|
-
|
403
|
-
|
404
353
|
if __name__ == "__main__":
|
405
|
-
# print(Result.example())
|
406
354
|
import doctest
|
407
355
|
|
408
356
|
doctest.testmod(optionflags=doctest.ELLIPSIS)
|