edsl 0.1.38.dev4__py3-none-any.whl → 0.1.39__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 +197 -116
- edsl/__init__.py +15 -7
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +351 -147
- edsl/agents/AgentList.py +211 -73
- edsl/agents/Invigilator.py +101 -50
- edsl/agents/InvigilatorBase.py +62 -70
- edsl/agents/PromptConstructor.py +143 -225
- edsl/agents/QuestionInstructionPromptBuilder.py +128 -0
- edsl/agents/QuestionTemplateReplacementsBuilder.py +137 -0
- edsl/agents/__init__.py +0 -1
- edsl/agents/prompt_helpers.py +3 -3
- edsl/agents/question_option_processor.py +172 -0
- edsl/auto/AutoStudy.py +18 -5
- edsl/auto/StageBase.py +53 -40
- edsl/auto/StageQuestions.py +2 -1
- edsl/auto/utilities.py +0 -6
- edsl/config.py +22 -2
- edsl/conversation/car_buying.py +2 -1
- edsl/coop/CoopFunctionsMixin.py +15 -0
- edsl/coop/ExpectedParrotKeyHandler.py +125 -0
- edsl/coop/PriceFetcher.py +1 -1
- edsl/coop/coop.py +125 -47
- edsl/coop/utils.py +14 -14
- edsl/data/Cache.py +45 -27
- edsl/data/CacheEntry.py +12 -15
- edsl/data/CacheHandler.py +31 -12
- edsl/data/RemoteCacheSync.py +154 -46
- edsl/data/__init__.py +4 -3
- edsl/data_transfer_models.py +2 -1
- edsl/enums.py +27 -0
- edsl/exceptions/__init__.py +50 -50
- edsl/exceptions/agents.py +12 -0
- edsl/exceptions/inference_services.py +5 -0
- edsl/exceptions/questions.py +24 -6
- edsl/exceptions/scenarios.py +7 -0
- edsl/inference_services/AnthropicService.py +38 -19
- edsl/inference_services/AvailableModelCacheHandler.py +184 -0
- edsl/inference_services/AvailableModelFetcher.py +215 -0
- edsl/inference_services/AwsBedrock.py +0 -2
- edsl/inference_services/AzureAI.py +0 -2
- edsl/inference_services/GoogleService.py +7 -12
- edsl/inference_services/InferenceServiceABC.py +18 -85
- edsl/inference_services/InferenceServicesCollection.py +120 -79
- edsl/inference_services/MistralAIService.py +0 -3
- edsl/inference_services/OpenAIService.py +47 -35
- edsl/inference_services/PerplexityService.py +0 -3
- edsl/inference_services/ServiceAvailability.py +135 -0
- edsl/inference_services/TestService.py +11 -10
- edsl/inference_services/TogetherAIService.py +5 -3
- edsl/inference_services/data_structures.py +134 -0
- edsl/jobs/AnswerQuestionFunctionConstructor.py +223 -0
- edsl/jobs/Answers.py +1 -14
- edsl/jobs/FetchInvigilator.py +47 -0
- edsl/jobs/InterviewTaskManager.py +98 -0
- edsl/jobs/InterviewsConstructor.py +50 -0
- edsl/jobs/Jobs.py +356 -431
- edsl/jobs/JobsChecks.py +35 -10
- edsl/jobs/JobsComponentConstructor.py +189 -0
- edsl/jobs/JobsPrompts.py +6 -4
- edsl/jobs/JobsRemoteInferenceHandler.py +205 -133
- edsl/jobs/JobsRemoteInferenceLogger.py +239 -0
- edsl/jobs/RequestTokenEstimator.py +30 -0
- edsl/jobs/async_interview_runner.py +138 -0
- edsl/jobs/buckets/BucketCollection.py +44 -3
- edsl/jobs/buckets/TokenBucket.py +53 -21
- edsl/jobs/buckets/TokenBucketAPI.py +211 -0
- edsl/jobs/buckets/TokenBucketClient.py +191 -0
- edsl/jobs/check_survey_scenario_compatibility.py +85 -0
- edsl/jobs/data_structures.py +120 -0
- edsl/jobs/decorators.py +35 -0
- edsl/jobs/interviews/Interview.py +143 -408
- edsl/jobs/jobs_status_enums.py +9 -0
- edsl/jobs/loggers/HTMLTableJobLogger.py +304 -0
- edsl/jobs/results_exceptions_handler.py +98 -0
- edsl/jobs/runners/JobsRunnerAsyncio.py +88 -403
- edsl/jobs/runners/JobsRunnerStatus.py +133 -165
- edsl/jobs/tasks/QuestionTaskCreator.py +21 -19
- edsl/jobs/tasks/TaskHistory.py +38 -18
- edsl/jobs/tasks/task_status_enum.py +0 -2
- edsl/language_models/ComputeCost.py +63 -0
- edsl/language_models/LanguageModel.py +194 -236
- edsl/language_models/ModelList.py +28 -19
- edsl/language_models/PriceManager.py +127 -0
- edsl/language_models/RawResponseHandler.py +106 -0
- edsl/language_models/ServiceDataSources.py +0 -0
- edsl/language_models/__init__.py +1 -2
- edsl/language_models/key_management/KeyLookup.py +63 -0
- edsl/language_models/key_management/KeyLookupBuilder.py +273 -0
- edsl/language_models/key_management/KeyLookupCollection.py +38 -0
- edsl/language_models/key_management/__init__.py +0 -0
- edsl/language_models/key_management/models.py +131 -0
- edsl/language_models/model.py +256 -0
- edsl/language_models/repair.py +2 -2
- edsl/language_models/utilities.py +5 -4
- edsl/notebooks/Notebook.py +19 -14
- edsl/notebooks/NotebookToLaTeX.py +142 -0
- edsl/prompts/Prompt.py +29 -39
- edsl/questions/ExceptionExplainer.py +77 -0
- edsl/questions/HTMLQuestion.py +103 -0
- edsl/questions/QuestionBase.py +68 -214
- edsl/questions/QuestionBasePromptsMixin.py +7 -3
- edsl/questions/QuestionBudget.py +1 -1
- edsl/questions/QuestionCheckBox.py +3 -3
- edsl/questions/QuestionExtract.py +5 -7
- edsl/questions/QuestionFreeText.py +2 -3
- edsl/questions/QuestionList.py +10 -18
- edsl/questions/QuestionMatrix.py +265 -0
- edsl/questions/QuestionMultipleChoice.py +67 -23
- edsl/questions/QuestionNumerical.py +2 -4
- edsl/questions/QuestionRank.py +7 -17
- edsl/questions/SimpleAskMixin.py +4 -3
- edsl/questions/__init__.py +2 -1
- edsl/questions/{AnswerValidatorMixin.py → answer_validator_mixin.py} +47 -2
- edsl/questions/data_structures.py +20 -0
- edsl/questions/derived/QuestionLinearScale.py +6 -3
- edsl/questions/derived/QuestionTopK.py +1 -1
- edsl/questions/descriptors.py +17 -3
- edsl/questions/loop_processor.py +149 -0
- edsl/questions/{QuestionBaseGenMixin.py → question_base_gen_mixin.py} +57 -50
- edsl/questions/question_registry.py +1 -1
- edsl/questions/{ResponseValidatorABC.py → response_validator_abc.py} +40 -26
- edsl/questions/response_validator_factory.py +34 -0
- edsl/questions/templates/matrix/__init__.py +1 -0
- edsl/questions/templates/matrix/answering_instructions.jinja +5 -0
- edsl/questions/templates/matrix/question_presentation.jinja +20 -0
- edsl/results/CSSParameterizer.py +1 -1
- edsl/results/Dataset.py +170 -7
- edsl/results/DatasetExportMixin.py +168 -305
- edsl/results/DatasetTree.py +28 -8
- edsl/results/MarkdownToDocx.py +122 -0
- edsl/results/MarkdownToPDF.py +111 -0
- edsl/results/Result.py +298 -206
- edsl/results/Results.py +149 -131
- edsl/results/ResultsExportMixin.py +2 -0
- edsl/results/TableDisplay.py +98 -171
- edsl/results/TextEditor.py +50 -0
- edsl/results/__init__.py +1 -1
- edsl/results/file_exports.py +252 -0
- edsl/results/{Selector.py → results_selector.py} +23 -13
- edsl/results/smart_objects.py +96 -0
- edsl/results/table_data_class.py +12 -0
- edsl/results/table_renderers.py +118 -0
- edsl/scenarios/ConstructDownloadLink.py +109 -0
- edsl/scenarios/DocumentChunker.py +102 -0
- edsl/scenarios/DocxScenario.py +16 -0
- edsl/scenarios/FileStore.py +150 -239
- edsl/scenarios/PdfExtractor.py +40 -0
- edsl/scenarios/Scenario.py +90 -193
- edsl/scenarios/ScenarioHtmlMixin.py +4 -3
- edsl/scenarios/ScenarioList.py +415 -244
- edsl/scenarios/ScenarioListExportMixin.py +0 -7
- edsl/scenarios/ScenarioListPdfMixin.py +15 -37
- edsl/scenarios/__init__.py +1 -2
- edsl/scenarios/directory_scanner.py +96 -0
- edsl/scenarios/file_methods.py +85 -0
- edsl/scenarios/handlers/__init__.py +13 -0
- edsl/scenarios/handlers/csv.py +49 -0
- edsl/scenarios/handlers/docx.py +76 -0
- edsl/scenarios/handlers/html.py +37 -0
- edsl/scenarios/handlers/json.py +111 -0
- edsl/scenarios/handlers/latex.py +5 -0
- edsl/scenarios/handlers/md.py +51 -0
- edsl/scenarios/handlers/pdf.py +68 -0
- edsl/scenarios/handlers/png.py +39 -0
- edsl/scenarios/handlers/pptx.py +105 -0
- edsl/scenarios/handlers/py.py +294 -0
- edsl/scenarios/handlers/sql.py +313 -0
- edsl/scenarios/handlers/sqlite.py +149 -0
- edsl/scenarios/handlers/txt.py +33 -0
- edsl/scenarios/{ScenarioJoin.py → scenario_join.py} +10 -6
- edsl/scenarios/scenario_selector.py +156 -0
- edsl/study/ObjectEntry.py +1 -1
- edsl/study/SnapShot.py +1 -1
- edsl/study/Study.py +5 -12
- edsl/surveys/ConstructDAG.py +92 -0
- edsl/surveys/EditSurvey.py +221 -0
- edsl/surveys/InstructionHandler.py +100 -0
- edsl/surveys/MemoryManagement.py +72 -0
- edsl/surveys/Rule.py +5 -4
- edsl/surveys/RuleCollection.py +25 -27
- edsl/surveys/RuleManager.py +172 -0
- edsl/surveys/Simulator.py +75 -0
- edsl/surveys/Survey.py +270 -791
- edsl/surveys/SurveyCSS.py +20 -8
- edsl/surveys/{SurveyFlowVisualizationMixin.py → SurveyFlowVisualization.py} +11 -9
- edsl/surveys/SurveyToApp.py +141 -0
- edsl/surveys/__init__.py +4 -2
- edsl/surveys/descriptors.py +6 -2
- edsl/surveys/instructions/ChangeInstruction.py +1 -2
- edsl/surveys/instructions/Instruction.py +4 -13
- edsl/surveys/instructions/InstructionCollection.py +11 -6
- edsl/templates/error_reporting/interview_details.html +1 -1
- edsl/templates/error_reporting/report.html +1 -1
- edsl/tools/plotting.py +1 -1
- edsl/utilities/PrettyList.py +56 -0
- edsl/utilities/is_notebook.py +18 -0
- edsl/utilities/is_valid_variable_name.py +11 -0
- edsl/utilities/remove_edsl_version.py +24 -0
- edsl/utilities/utilities.py +35 -23
- {edsl-0.1.38.dev4.dist-info → edsl-0.1.39.dist-info}/METADATA +12 -10
- edsl-0.1.39.dist-info/RECORD +358 -0
- {edsl-0.1.38.dev4.dist-info → edsl-0.1.39.dist-info}/WHEEL +1 -1
- edsl/language_models/KeyLookup.py +0 -30
- edsl/language_models/registry.py +0 -190
- edsl/language_models/unused/ReplicateBase.py +0 -83
- edsl/results/ResultsDBMixin.py +0 -238
- edsl-0.1.38.dev4.dist-info/RECORD +0 -277
- /edsl/questions/{RegisterQuestionsMeta.py → register_questions_meta.py} +0 -0
- /edsl/results/{ResultsFetchMixin.py → results_fetch_mixin.py} +0 -0
- /edsl/results/{ResultsToolsMixin.py → results_tools_mixin.py} +0 -0
- {edsl-0.1.38.dev4.dist-info → edsl-0.1.39.dist-info}/LICENSE +0 -0
edsl/results/Results.py
CHANGED
@@ -9,13 +9,9 @@ import random
|
|
9
9
|
from collections import UserList, defaultdict
|
10
10
|
from typing import Optional, Callable, Any, Type, Union, List, TYPE_CHECKING
|
11
11
|
|
12
|
-
|
13
|
-
from edsl import Survey, Cache, AgentList, ModelList, ScenarioList
|
14
|
-
from edsl.results.Result import Result
|
15
|
-
from edsl.jobs.tasks.TaskHistory import TaskHistory
|
16
|
-
|
17
|
-
from simpleeval import EvalWithCompoundTypes
|
12
|
+
from bisect import bisect_left
|
18
13
|
|
14
|
+
from edsl.Base import Base
|
19
15
|
from edsl.exceptions.results import (
|
20
16
|
ResultsError,
|
21
17
|
ResultsBadMutationstringError,
|
@@ -26,25 +22,27 @@ from edsl.exceptions.results import (
|
|
26
22
|
ResultsDeserializationError,
|
27
23
|
)
|
28
24
|
|
25
|
+
if TYPE_CHECKING:
|
26
|
+
from edsl.surveys.Survey import Survey
|
27
|
+
from edsl.data.Cache import Cache
|
28
|
+
from edsl.agents.AgentList import AgentList
|
29
|
+
from edsl.language_models.model import Model
|
30
|
+
from edsl.scenarios.ScenarioList import ScenarioList
|
31
|
+
from edsl.results.Result import Result
|
32
|
+
from edsl.jobs.tasks.TaskHistory import TaskHistory
|
33
|
+
from edsl.language_models.ModelList import ModelList
|
34
|
+
from simpleeval import EvalWithCompoundTypes
|
35
|
+
|
29
36
|
from edsl.results.ResultsExportMixin import ResultsExportMixin
|
30
|
-
from edsl.results.ResultsToolsMixin import ResultsToolsMixin
|
31
|
-
from edsl.results.ResultsDBMixin import ResultsDBMixin
|
32
37
|
from edsl.results.ResultsGGMixin import ResultsGGMixin
|
33
|
-
from edsl.results.
|
34
|
-
|
35
|
-
from edsl.utilities.decorators import remove_edsl_version
|
36
|
-
from edsl.utilities.utilities import dict_hash
|
37
|
-
|
38
|
-
|
39
|
-
from edsl.Base import Base
|
38
|
+
from edsl.results.results_fetch_mixin import ResultsFetchMixin
|
39
|
+
from edsl.utilities.remove_edsl_version import remove_edsl_version
|
40
40
|
|
41
41
|
|
42
42
|
class Mixins(
|
43
43
|
ResultsExportMixin,
|
44
|
-
ResultsDBMixin,
|
45
44
|
ResultsFetchMixin,
|
46
45
|
ResultsGGMixin,
|
47
|
-
ResultsToolsMixin,
|
48
46
|
):
|
49
47
|
def long(self):
|
50
48
|
return self.table().long()
|
@@ -91,6 +89,7 @@ class Results(UserList, Mixins, Base):
|
|
91
89
|
"question_type",
|
92
90
|
"comment",
|
93
91
|
"generated_tokens",
|
92
|
+
"cache_used",
|
94
93
|
]
|
95
94
|
|
96
95
|
def __init__(
|
@@ -129,22 +128,43 @@ class Results(UserList, Mixins, Base):
|
|
129
128
|
def _summary(self) -> dict:
|
130
129
|
import reprlib
|
131
130
|
|
132
|
-
# import yaml
|
133
|
-
|
134
131
|
d = {
|
135
|
-
"
|
136
|
-
|
137
|
-
"
|
138
|
-
"
|
139
|
-
"
|
140
|
-
"# Scenarios": len(set(self.scenarios)),
|
141
|
-
"Survey Length (# questions)": len(self.survey),
|
132
|
+
"observations": len(self),
|
133
|
+
"agents": len(set(self.agents)),
|
134
|
+
"models": len(set(self.models)),
|
135
|
+
"scenarios": len(set(self.scenarios)),
|
136
|
+
"questions": len(self.survey),
|
142
137
|
"Survey question names": reprlib.repr(self.survey.question_names),
|
143
|
-
"Object hash": hash(self),
|
144
138
|
}
|
145
139
|
return d
|
146
140
|
|
147
|
-
def
|
141
|
+
def insert(self, item):
|
142
|
+
item_order = getattr(item, "order", None)
|
143
|
+
if item_order is not None:
|
144
|
+
# Get list of orders, putting None at the end
|
145
|
+
orders = [getattr(x, "order", None) for x in self]
|
146
|
+
# Filter to just the non-None orders for bisect
|
147
|
+
sorted_orders = [x for x in orders if x is not None]
|
148
|
+
if sorted_orders:
|
149
|
+
index = bisect_left(sorted_orders, item_order)
|
150
|
+
# Account for any None values before this position
|
151
|
+
index += orders[:index].count(None)
|
152
|
+
else:
|
153
|
+
# If no sorted items yet, insert before any unordered items
|
154
|
+
index = 0
|
155
|
+
self.data.insert(index, item)
|
156
|
+
else:
|
157
|
+
# No order - append to end
|
158
|
+
self.data.append(item)
|
159
|
+
|
160
|
+
def append(self, item):
|
161
|
+
self.insert(item)
|
162
|
+
|
163
|
+
def extend(self, other):
|
164
|
+
for item in other:
|
165
|
+
self.insert(item)
|
166
|
+
|
167
|
+
def compute_job_cost(self, include_cached_responses_in_cost: bool = False) -> float:
|
148
168
|
"""
|
149
169
|
Computes the cost of a completed job in USD.
|
150
170
|
"""
|
@@ -258,24 +278,6 @@ class Results(UserList, Mixins, Base):
|
|
258
278
|
|
259
279
|
raise TypeError("Invalid argument type")
|
260
280
|
|
261
|
-
def _update_results(self) -> None:
|
262
|
-
from edsl import Agent, Scenario
|
263
|
-
from edsl.language_models import LanguageModel
|
264
|
-
from edsl.results import Result
|
265
|
-
|
266
|
-
if self._job_uuid and len(self.data) < self._total_results:
|
267
|
-
results = [
|
268
|
-
Result(
|
269
|
-
agent=Agent.from_dict(json.loads(r.agent)),
|
270
|
-
scenario=Scenario.from_dict(json.loads(r.scenario)),
|
271
|
-
model=LanguageModel.from_dict(json.loads(r.model)),
|
272
|
-
iteration=1,
|
273
|
-
answer=json.loads(r.answer),
|
274
|
-
)
|
275
|
-
for r in CRUD.read_results(self._job_uuid)
|
276
|
-
]
|
277
|
-
self.data = results
|
278
|
-
|
279
281
|
def __add__(self, other: Results) -> Results:
|
280
282
|
"""Add two Results objects together.
|
281
283
|
They must have the same survey and created columns.
|
@@ -303,13 +305,10 @@ class Results(UserList, Mixins, Base):
|
|
303
305
|
)
|
304
306
|
|
305
307
|
def __repr__(self) -> str:
|
306
|
-
|
307
|
-
|
308
|
-
return f"Results(data = {reprlib.repr(self.data)}, survey = {repr(self.survey)}, created_columns = {self.created_columns})"
|
308
|
+
return f"Results(data = {self.data}, survey = {repr(self.survey)}, created_columns = {self.created_columns})"
|
309
309
|
|
310
310
|
def table(
|
311
311
|
self,
|
312
|
-
# selector_string: Optional[str] = "*.*",
|
313
312
|
*fields,
|
314
313
|
tablefmt: Optional[str] = None,
|
315
314
|
pretty_labels: Optional[dict] = None,
|
@@ -345,28 +344,14 @@ class Results(UserList, Mixins, Base):
|
|
345
344
|
print_parameters=print_parameters,
|
346
345
|
)
|
347
346
|
)
|
348
|
-
# return (
|
349
|
-
# self.select(f"{selector_string}")
|
350
|
-
# .to_scenario_list()
|
351
|
-
# .table(*fields, tablefmt=tablefmt)
|
352
|
-
# )
|
353
|
-
|
354
|
-
def _repr_html_(self) -> str:
|
355
|
-
d = self._summary()
|
356
|
-
from edsl import Scenario
|
357
|
-
|
358
|
-
footer = f"<a href={self.__documentation__}>(docs)</a>"
|
359
|
-
|
360
|
-
s = Scenario(d)
|
361
|
-
td = s.to_dataset().table(tablefmt="html")
|
362
|
-
return td._repr_html_() + footer
|
363
347
|
|
364
348
|
def to_dict(
|
365
349
|
self,
|
366
|
-
sort=False,
|
367
|
-
add_edsl_version=False,
|
368
|
-
include_cache=False,
|
369
|
-
include_task_history=False,
|
350
|
+
sort: bool = False,
|
351
|
+
add_edsl_version: bool = False,
|
352
|
+
include_cache: bool = False,
|
353
|
+
include_task_history: bool = False,
|
354
|
+
include_cache_info: bool = True,
|
370
355
|
) -> dict[str, Any]:
|
371
356
|
from edsl.data.Cache import Cache
|
372
357
|
|
@@ -377,7 +362,11 @@ class Results(UserList, Mixins, Base):
|
|
377
362
|
|
378
363
|
d = {
|
379
364
|
"data": [
|
380
|
-
result.to_dict(
|
365
|
+
result.to_dict(
|
366
|
+
add_edsl_version=add_edsl_version,
|
367
|
+
include_cache_info=include_cache_info,
|
368
|
+
)
|
369
|
+
for result in data
|
381
370
|
],
|
382
371
|
"survey": self.survey.to_dict(add_edsl_version=add_edsl_version),
|
383
372
|
"created_columns": self.created_columns,
|
@@ -404,7 +393,7 @@ class Results(UserList, Mixins, Base):
|
|
404
393
|
|
405
394
|
return d
|
406
395
|
|
407
|
-
def compare(self, other_results):
|
396
|
+
def compare(self, other_results: Results) -> dict:
|
408
397
|
"""
|
409
398
|
Compare two Results objects and return the differences.
|
410
399
|
"""
|
@@ -422,11 +411,15 @@ class Results(UserList, Mixins, Base):
|
|
422
411
|
}
|
423
412
|
|
424
413
|
@property
|
425
|
-
def has_unfixed_exceptions(self):
|
414
|
+
def has_unfixed_exceptions(self) -> bool:
|
426
415
|
return self.task_history.has_unfixed_exceptions
|
427
416
|
|
428
417
|
def __hash__(self) -> int:
|
429
|
-
|
418
|
+
from edsl.utilities.utilities import dict_hash
|
419
|
+
|
420
|
+
return dict_hash(
|
421
|
+
self.to_dict(sort=True, add_edsl_version=False, include_cache_info=False)
|
422
|
+
)
|
430
423
|
|
431
424
|
@property
|
432
425
|
def hashes(self) -> set:
|
@@ -472,32 +465,35 @@ class Results(UserList, Mixins, Base):
|
|
472
465
|
>>> r == r2
|
473
466
|
True
|
474
467
|
"""
|
475
|
-
from edsl import Survey
|
468
|
+
from edsl.surveys.Survey import Survey
|
469
|
+
from edsl.data.Cache import Cache
|
476
470
|
from edsl.results.Result import Result
|
477
471
|
from edsl.jobs.tasks.TaskHistory import TaskHistory
|
472
|
+
from edsl.agents.Agent import Agent
|
473
|
+
|
474
|
+
survey = Survey.from_dict(data["survey"])
|
475
|
+
results_data = [Result.from_dict(r) for r in data["data"]]
|
476
|
+
created_columns = data.get("created_columns", None)
|
477
|
+
cache = Cache.from_dict(data.get("cache")) if "cache" in data else Cache()
|
478
|
+
task_history = (
|
479
|
+
TaskHistory.from_dict(data.get("task_history"))
|
480
|
+
if "task_history" in data
|
481
|
+
else TaskHistory(interviews=[])
|
482
|
+
)
|
483
|
+
params = {
|
484
|
+
"survey": survey,
|
485
|
+
"data": results_data,
|
486
|
+
"created_columns": created_columns,
|
487
|
+
"cache": cache,
|
488
|
+
"task_history": task_history,
|
489
|
+
}
|
478
490
|
|
479
491
|
try:
|
480
|
-
results = cls(
|
481
|
-
survey=Survey.from_dict(data["survey"]),
|
482
|
-
data=[Result.from_dict(r) for r in data["data"]],
|
483
|
-
created_columns=data.get("created_columns", None),
|
484
|
-
cache=(
|
485
|
-
Cache.from_dict(data.get("cache")) if "cache" in data else Cache()
|
486
|
-
),
|
487
|
-
task_history=(
|
488
|
-
TaskHistory.from_dict(data.get("task_history"))
|
489
|
-
if "task_history" in data
|
490
|
-
else TaskHistory(interviews=[])
|
491
|
-
),
|
492
|
-
)
|
492
|
+
results = cls(**params)
|
493
493
|
except Exception as e:
|
494
494
|
raise ResultsDeserializationError(f"Error in Results.from_dict: {e}")
|
495
495
|
return results
|
496
496
|
|
497
|
-
######################
|
498
|
-
## Convenience methods
|
499
|
-
## & Report methods
|
500
|
-
######################
|
501
497
|
@property
|
502
498
|
def _key_to_data_type(self) -> dict[str, str]:
|
503
499
|
"""
|
@@ -544,10 +540,12 @@ class Results(UserList, Mixins, Base):
|
|
544
540
|
|
545
541
|
>>> r = Results.example()
|
546
542
|
>>> r.columns
|
547
|
-
['agent.
|
543
|
+
['agent.agent_index', ...]
|
548
544
|
"""
|
549
545
|
column_names = [f"{v}.{k}" for k, v in self._key_to_data_type.items()]
|
550
|
-
|
546
|
+
from edsl.utilities.PrettyList import PrettyList
|
547
|
+
|
548
|
+
return PrettyList(sorted(column_names))
|
551
549
|
|
552
550
|
@property
|
553
551
|
def answer_keys(self) -> dict[str, str]:
|
@@ -567,7 +565,7 @@ class Results(UserList, Mixins, Base):
|
|
567
565
|
answer_keys = self._data_type_to_keys["answer"]
|
568
566
|
answer_keys = {k for k in answer_keys if "_comment" not in k}
|
569
567
|
questions_text = [
|
570
|
-
self.survey.
|
568
|
+
self.survey._get_question_by_name(k).question_text for k in answer_keys
|
571
569
|
]
|
572
570
|
short_question_text = [shorten_string(q, 80) for q in questions_text]
|
573
571
|
initial_dict = dict(zip(answer_keys, short_question_text))
|
@@ -584,7 +582,7 @@ class Results(UserList, Mixins, Base):
|
|
584
582
|
>>> r.agents
|
585
583
|
AgentList([Agent(traits = {'status': 'Joyful'}), Agent(traits = {'status': 'Joyful'}), Agent(traits = {'status': 'Sad'}), Agent(traits = {'status': 'Sad'})])
|
586
584
|
"""
|
587
|
-
from edsl import AgentList
|
585
|
+
from edsl.agents.AgentList import AgentList
|
588
586
|
|
589
587
|
return AgentList([r.agent for r in self.data])
|
590
588
|
|
@@ -598,10 +596,13 @@ class Results(UserList, Mixins, Base):
|
|
598
596
|
>>> r.models[0]
|
599
597
|
Model(model_name = ...)
|
600
598
|
"""
|
601
|
-
from edsl import ModelList
|
599
|
+
from edsl.language_models.ModelList import ModelList
|
602
600
|
|
603
601
|
return ModelList([r.model for r in self.data])
|
604
602
|
|
603
|
+
def __eq__(self, other):
|
604
|
+
return hash(self) == hash(other)
|
605
|
+
|
605
606
|
@property
|
606
607
|
def scenarios(self) -> ScenarioList:
|
607
608
|
"""Return a list of all of the scenarios in the Results.
|
@@ -610,9 +611,9 @@ class Results(UserList, Mixins, Base):
|
|
610
611
|
|
611
612
|
>>> r = Results.example()
|
612
613
|
>>> r.scenarios
|
613
|
-
ScenarioList([Scenario({'period': 'morning'}), Scenario({'period': 'afternoon'}), Scenario({'period': 'morning'}), Scenario({'period': 'afternoon'})])
|
614
|
+
ScenarioList([Scenario({'period': 'morning', 'scenario_index': 0}), Scenario({'period': 'afternoon', 'scenario_index': 1}), Scenario({'period': 'morning', 'scenario_index': 0}), Scenario({'period': 'afternoon', 'scenario_index': 1})])
|
614
615
|
"""
|
615
|
-
from edsl import ScenarioList
|
616
|
+
from edsl.scenarios.ScenarioList import ScenarioList
|
616
617
|
|
617
618
|
return ScenarioList([r.scenario for r in self.data])
|
618
619
|
|
@@ -624,7 +625,7 @@ class Results(UserList, Mixins, Base):
|
|
624
625
|
|
625
626
|
>>> r = Results.example()
|
626
627
|
>>> r.agent_keys
|
627
|
-
['agent_instruction', 'agent_name', 'status']
|
628
|
+
['agent_index', 'agent_instruction', 'agent_name', 'status']
|
628
629
|
"""
|
629
630
|
return sorted(self._data_type_to_keys["agent"])
|
630
631
|
|
@@ -634,7 +635,7 @@ class Results(UserList, Mixins, Base):
|
|
634
635
|
|
635
636
|
>>> r = Results.example()
|
636
637
|
>>> r.model_keys
|
637
|
-
['frequency_penalty', 'logprobs', 'max_tokens', 'model', 'presence_penalty', 'temperature', 'top_logprobs', 'top_p']
|
638
|
+
['frequency_penalty', 'logprobs', 'max_tokens', 'model', 'model_index', 'presence_penalty', 'temperature', 'top_logprobs', 'top_p']
|
638
639
|
"""
|
639
640
|
return sorted(self._data_type_to_keys["model"])
|
640
641
|
|
@@ -644,7 +645,7 @@ class Results(UserList, Mixins, Base):
|
|
644
645
|
|
645
646
|
>>> r = Results.example()
|
646
647
|
>>> r.scenario_keys
|
647
|
-
['period']
|
648
|
+
['period', 'scenario_index']
|
648
649
|
"""
|
649
650
|
return sorted(self._data_type_to_keys["scenario"])
|
650
651
|
|
@@ -670,7 +671,7 @@ class Results(UserList, Mixins, Base):
|
|
670
671
|
|
671
672
|
>>> r = Results.example()
|
672
673
|
>>> r.all_keys
|
673
|
-
['
|
674
|
+
['agent_index', ...]
|
674
675
|
"""
|
675
676
|
answer_keys = set(self.answer_keys)
|
676
677
|
all_keys = (
|
@@ -691,13 +692,19 @@ class Results(UserList, Mixins, Base):
|
|
691
692
|
"""
|
692
693
|
return self.data[0]
|
693
694
|
|
694
|
-
def answer_truncate(
|
695
|
+
def answer_truncate(
|
696
|
+
self, column: str, top_n: int = 5, new_var_name: str = None
|
697
|
+
) -> Results:
|
695
698
|
"""Create a new variable that truncates the answers to the top_n.
|
696
699
|
|
697
700
|
:param column: The column to truncate.
|
698
701
|
:param top_n: The number of top answers to keep.
|
699
702
|
:param new_var_name: The name of the new variable. If None, it is the original name + '_truncated'.
|
700
703
|
|
704
|
+
Example:
|
705
|
+
>>> r = Results.example()
|
706
|
+
>>> r.answer_truncate('how_feeling', top_n = 2).select('how_feeling', 'how_feeling_truncated')
|
707
|
+
Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}, {'answer.how_feeling_truncated': ['Other', 'Other', 'Other', 'Other']}])
|
701
708
|
|
702
709
|
|
703
710
|
"""
|
@@ -777,7 +784,7 @@ class Results(UserList, Mixins, Base):
|
|
777
784
|
@staticmethod
|
778
785
|
def _create_evaluator(
|
779
786
|
result: Result, functions_dict: Optional[dict] = None
|
780
|
-
) -> EvalWithCompoundTypes:
|
787
|
+
) -> "EvalWithCompoundTypes":
|
781
788
|
"""Create an evaluator for the expression.
|
782
789
|
|
783
790
|
>>> from unittest.mock import Mock
|
@@ -800,6 +807,8 @@ class Results(UserList, Mixins, Base):
|
|
800
807
|
...
|
801
808
|
simpleeval.NameNotDefined: 'how_feeling' is not defined for expression 'how_feeling== 'OK''
|
802
809
|
"""
|
810
|
+
from simpleeval import EvalWithCompoundTypes
|
811
|
+
|
803
812
|
if functions_dict is None:
|
804
813
|
functions_dict = {}
|
805
814
|
evaluator = EvalWithCompoundTypes(
|
@@ -858,6 +867,26 @@ class Results(UserList, Mixins, Base):
|
|
858
867
|
created_columns=self.created_columns + [var_name],
|
859
868
|
)
|
860
869
|
|
870
|
+
def add_column(self, column_name: str, values: list) -> Results:
|
871
|
+
"""Adds columns to Results
|
872
|
+
|
873
|
+
>>> r = Results.example()
|
874
|
+
>>> r.add_column('a', [1,2,3, 4]).select('a')
|
875
|
+
Dataset([{'answer.a': [1, 2, 3, 4]}])
|
876
|
+
"""
|
877
|
+
|
878
|
+
assert len(values) == len(
|
879
|
+
self.data
|
880
|
+
), "The number of values must match the number of results."
|
881
|
+
new_results = self.data.copy()
|
882
|
+
for i, result in enumerate(new_results):
|
883
|
+
result["answer"][column_name] = values[i]
|
884
|
+
return Results(
|
885
|
+
survey=self.survey,
|
886
|
+
data=new_results,
|
887
|
+
created_columns=self.created_columns + [column_name],
|
888
|
+
)
|
889
|
+
|
861
890
|
def rename(self, old_name: str, new_name: str) -> Results:
|
862
891
|
"""Rename an answer column in a Results object.
|
863
892
|
|
@@ -896,7 +925,7 @@ class Results(UserList, Mixins, Base):
|
|
896
925
|
n: Optional[int] = None,
|
897
926
|
frac: Optional[float] = None,
|
898
927
|
with_replacement: bool = True,
|
899
|
-
seed: Optional[str] =
|
928
|
+
seed: Optional[str] = None,
|
900
929
|
) -> Results:
|
901
930
|
"""Sample the results.
|
902
931
|
|
@@ -911,7 +940,7 @@ class Results(UserList, Mixins, Base):
|
|
911
940
|
>>> len(r.sample(2))
|
912
941
|
2
|
913
942
|
"""
|
914
|
-
if seed
|
943
|
+
if seed:
|
915
944
|
random.seed(seed)
|
916
945
|
|
917
946
|
if n is None and frac is None:
|
@@ -949,7 +978,7 @@ class Results(UserList, Mixins, Base):
|
|
949
978
|
Dataset([{'answer.how_feeling_yesterday': ['Great', 'Good', 'OK', 'Terrible']}])
|
950
979
|
"""
|
951
980
|
|
952
|
-
from edsl.results.
|
981
|
+
from edsl.results.results_selector import Selector
|
953
982
|
|
954
983
|
if len(self) == 0:
|
955
984
|
raise Exception("No data to select from---the Results object is empty.")
|
@@ -964,6 +993,7 @@ class Results(UserList, Mixins, Base):
|
|
964
993
|
return selector.select(*columns)
|
965
994
|
|
966
995
|
def sort_by(self, *columns: str, reverse: bool = False) -> Results:
|
996
|
+
"""Sort the results by one or more columns."""
|
967
997
|
import warnings
|
968
998
|
|
969
999
|
warnings.warn(
|
@@ -972,6 +1002,7 @@ class Results(UserList, Mixins, Base):
|
|
972
1002
|
return self.order_by(*columns, reverse=reverse)
|
973
1003
|
|
974
1004
|
def _parse_column(self, column: str) -> tuple[str, str]:
|
1005
|
+
"""Parse a column name into a data type and key."""
|
975
1006
|
if "." in column:
|
976
1007
|
return column.split(".")
|
977
1008
|
return self._key_to_data_type[column], column
|
@@ -987,20 +1018,12 @@ class Results(UserList, Mixins, Base):
|
|
987
1018
|
Example:
|
988
1019
|
|
989
1020
|
>>> r = Results.example()
|
990
|
-
>>> r.sort_by('how_feeling', reverse=False).select('how_feeling')
|
991
|
-
answer.how_feeling
|
992
|
-
|
993
|
-
|
994
|
-
OK
|
995
|
-
|
996
|
-
Terrible
|
997
|
-
>>> r.sort_by('how_feeling', reverse=True).select('how_feeling').print()
|
998
|
-
answer.how_feeling
|
999
|
-
--------------------
|
1000
|
-
Terrible
|
1001
|
-
OK
|
1002
|
-
OK
|
1003
|
-
Great
|
1021
|
+
>>> r.sort_by('how_feeling', reverse=False).select('how_feeling')
|
1022
|
+
Dataset([{'answer.how_feeling': ['Great', 'OK', 'OK', 'Terrible']}])
|
1023
|
+
|
1024
|
+
>>> r.sort_by('how_feeling', reverse=True).select('how_feeling')
|
1025
|
+
Dataset([{'answer.how_feeling': ['Terrible', 'OK', 'OK', 'Great']}])
|
1026
|
+
|
1004
1027
|
"""
|
1005
1028
|
|
1006
1029
|
def to_numeric_if_possible(v):
|
@@ -1032,24 +1055,19 @@ class Results(UserList, Mixins, Base):
|
|
1032
1055
|
Example usage: Create an example `Results` instance and apply filters to it:
|
1033
1056
|
|
1034
1057
|
>>> r = Results.example()
|
1035
|
-
>>> r.filter("how_feeling == 'Great'").select('how_feeling')
|
1036
|
-
answer.how_feeling
|
1037
|
-
--------------------
|
1038
|
-
Great
|
1058
|
+
>>> r.filter("how_feeling == 'Great'").select('how_feeling')
|
1059
|
+
Dataset([{'answer.how_feeling': ['Great']}])
|
1039
1060
|
|
1040
1061
|
Example usage: Using an OR operator in the filter expression.
|
1041
1062
|
|
1042
|
-
>>> r = Results.example().filter("how_feeling = 'Great'").select('how_feeling')
|
1063
|
+
>>> r = Results.example().filter("how_feeling = 'Great'").select('how_feeling')
|
1043
1064
|
Traceback (most recent call last):
|
1044
1065
|
...
|
1045
1066
|
edsl.exceptions.results.ResultsFilterError: You must use '==' instead of '=' in the filter expression.
|
1046
1067
|
...
|
1047
1068
|
|
1048
|
-
>>> r.filter("how_feeling == 'Great' or how_feeling == 'Terrible'").select('how_feeling')
|
1049
|
-
answer.how_feeling
|
1050
|
-
--------------------
|
1051
|
-
Great
|
1052
|
-
Terrible
|
1069
|
+
>>> r.filter("how_feeling == 'Great' or how_feeling == 'Terrible'").select('how_feeling')
|
1070
|
+
Dataset([{'answer.how_feeling': ['Great', 'Terrible']}])
|
1053
1071
|
"""
|
1054
1072
|
|
1055
1073
|
def has_single_equals(string):
|
@@ -14,6 +14,8 @@ def to_dataset(func):
|
|
14
14
|
"""Return the function with the Results object converted to a Dataset object."""
|
15
15
|
if self.__class__.__name__ == "Results":
|
16
16
|
return func(self.select(), *args, **kwargs)
|
17
|
+
elif self.__class__.__name__ == "AgentList":
|
18
|
+
return func(self.to_dataset(), *args, **kwargs)
|
17
19
|
else:
|
18
20
|
return func(self, *args, **kwargs)
|
19
21
|
|