edsl 0.1.39.dev3__py3-none-any.whl → 0.1.39.dev5__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 +413 -332
- edsl/BaseDiff.py +260 -260
- edsl/TemplateLoader.py +24 -24
- edsl/__init__.py +57 -49
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +1071 -867
- edsl/agents/AgentList.py +551 -413
- edsl/agents/Invigilator.py +284 -233
- edsl/agents/InvigilatorBase.py +257 -270
- edsl/agents/PromptConstructor.py +272 -354
- edsl/agents/QuestionInstructionPromptBuilder.py +128 -0
- edsl/agents/QuestionTemplateReplacementsBuilder.py +137 -0
- edsl/agents/__init__.py +2 -3
- edsl/agents/descriptors.py +99 -99
- edsl/agents/prompt_helpers.py +129 -129
- edsl/agents/question_option_processor.py +172 -0
- edsl/auto/AutoStudy.py +130 -117
- edsl/auto/StageBase.py +243 -230
- edsl/auto/StageGenerateSurvey.py +178 -178
- edsl/auto/StageLabelQuestions.py +125 -125
- edsl/auto/StagePersona.py +61 -61
- edsl/auto/StagePersonaDimensionValueRanges.py +88 -88
- edsl/auto/StagePersonaDimensionValues.py +74 -74
- edsl/auto/StagePersonaDimensions.py +69 -69
- edsl/auto/StageQuestions.py +74 -73
- edsl/auto/SurveyCreatorPipeline.py +21 -21
- edsl/auto/utilities.py +218 -224
- edsl/base/Base.py +279 -279
- edsl/config.py +177 -157
- edsl/conversation/Conversation.py +290 -290
- edsl/conversation/car_buying.py +59 -58
- edsl/conversation/chips.py +95 -95
- edsl/conversation/mug_negotiation.py +81 -81
- edsl/conversation/next_speaker_utilities.py +93 -93
- edsl/coop/CoopFunctionsMixin.py +15 -0
- edsl/coop/ExpectedParrotKeyHandler.py +125 -0
- edsl/coop/PriceFetcher.py +54 -54
- edsl/coop/__init__.py +2 -2
- edsl/coop/coop.py +1106 -1028
- edsl/coop/utils.py +131 -131
- edsl/data/Cache.py +573 -555
- edsl/data/CacheEntry.py +230 -233
- edsl/data/CacheHandler.py +168 -149
- edsl/data/RemoteCacheSync.py +186 -78
- edsl/data/SQLiteDict.py +292 -292
- edsl/data/__init__.py +5 -4
- edsl/data/orm.py +10 -10
- edsl/data_transfer_models.py +74 -73
- edsl/enums.py +202 -175
- edsl/exceptions/BaseException.py +21 -21
- edsl/exceptions/__init__.py +54 -54
- edsl/exceptions/agents.py +54 -42
- edsl/exceptions/cache.py +5 -5
- edsl/exceptions/configuration.py +16 -16
- edsl/exceptions/coop.py +10 -10
- edsl/exceptions/data.py +14 -14
- edsl/exceptions/general.py +34 -34
- edsl/exceptions/inference_services.py +5 -0
- edsl/exceptions/jobs.py +33 -33
- edsl/exceptions/language_models.py +63 -63
- edsl/exceptions/prompts.py +15 -15
- edsl/exceptions/questions.py +109 -91
- edsl/exceptions/results.py +29 -29
- edsl/exceptions/scenarios.py +29 -22
- edsl/exceptions/surveys.py +37 -37
- edsl/inference_services/AnthropicService.py +106 -87
- edsl/inference_services/AvailableModelCacheHandler.py +184 -0
- edsl/inference_services/AvailableModelFetcher.py +215 -0
- edsl/inference_services/AwsBedrock.py +118 -120
- edsl/inference_services/AzureAI.py +215 -217
- edsl/inference_services/DeepInfraService.py +18 -18
- edsl/inference_services/GoogleService.py +143 -148
- edsl/inference_services/GroqService.py +20 -20
- edsl/inference_services/InferenceServiceABC.py +80 -147
- edsl/inference_services/InferenceServicesCollection.py +138 -97
- edsl/inference_services/MistralAIService.py +120 -123
- edsl/inference_services/OllamaService.py +18 -18
- edsl/inference_services/OpenAIService.py +236 -224
- edsl/inference_services/PerplexityService.py +160 -163
- edsl/inference_services/ServiceAvailability.py +135 -0
- edsl/inference_services/TestService.py +90 -89
- edsl/inference_services/TogetherAIService.py +172 -170
- edsl/inference_services/data_structures.py +134 -0
- edsl/inference_services/models_available_cache.py +118 -118
- edsl/inference_services/rate_limits_cache.py +25 -25
- edsl/inference_services/registry.py +41 -41
- edsl/inference_services/write_available.py +10 -10
- edsl/jobs/AnswerQuestionFunctionConstructor.py +223 -0
- edsl/jobs/Answers.py +43 -56
- edsl/jobs/FetchInvigilator.py +47 -0
- edsl/jobs/InterviewTaskManager.py +98 -0
- edsl/jobs/InterviewsConstructor.py +50 -0
- edsl/jobs/Jobs.py +823 -898
- edsl/jobs/JobsChecks.py +172 -147
- edsl/jobs/JobsComponentConstructor.py +189 -0
- edsl/jobs/JobsPrompts.py +270 -268
- edsl/jobs/JobsRemoteInferenceHandler.py +311 -239
- edsl/jobs/JobsRemoteInferenceLogger.py +239 -0
- edsl/jobs/RequestTokenEstimator.py +30 -0
- edsl/jobs/__init__.py +1 -1
- edsl/jobs/async_interview_runner.py +138 -0
- edsl/jobs/buckets/BucketCollection.py +104 -63
- edsl/jobs/buckets/ModelBuckets.py +65 -65
- edsl/jobs/buckets/TokenBucket.py +283 -251
- 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 +396 -661
- edsl/jobs/interviews/InterviewExceptionCollection.py +99 -99
- edsl/jobs/interviews/InterviewExceptionEntry.py +186 -186
- edsl/jobs/interviews/InterviewStatistic.py +63 -63
- edsl/jobs/interviews/InterviewStatisticsCollection.py +25 -25
- edsl/jobs/interviews/InterviewStatusDictionary.py +78 -78
- edsl/jobs/interviews/InterviewStatusLog.py +92 -92
- edsl/jobs/interviews/ReportErrors.py +66 -66
- edsl/jobs/interviews/interview_status_enum.py +9 -9
- 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 +151 -466
- edsl/jobs/runners/JobsRunnerStatus.py +297 -330
- edsl/jobs/tasks/QuestionTaskCreator.py +244 -242
- edsl/jobs/tasks/TaskCreators.py +64 -64
- edsl/jobs/tasks/TaskHistory.py +470 -450
- edsl/jobs/tasks/TaskStatusLog.py +23 -23
- edsl/jobs/tasks/task_status_enum.py +161 -163
- edsl/jobs/tokens/InterviewTokenUsage.py +27 -27
- edsl/jobs/tokens/TokenUsage.py +34 -34
- edsl/language_models/ComputeCost.py +63 -0
- edsl/language_models/LanguageModel.py +626 -668
- edsl/language_models/ModelList.py +164 -155
- edsl/language_models/PriceManager.py +127 -0
- edsl/language_models/RawResponseHandler.py +106 -0
- edsl/language_models/RegisterLanguageModelsMeta.py +184 -184
- edsl/language_models/ServiceDataSources.py +0 -0
- edsl/language_models/__init__.py +2 -3
- edsl/language_models/fake_openai_call.py +15 -15
- edsl/language_models/fake_openai_service.py +61 -61
- 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 +156 -156
- edsl/language_models/utilities.py +65 -64
- edsl/notebooks/Notebook.py +263 -258
- edsl/notebooks/NotebookToLaTeX.py +142 -0
- edsl/notebooks/__init__.py +1 -1
- edsl/prompts/Prompt.py +352 -362
- edsl/prompts/__init__.py +2 -2
- edsl/questions/ExceptionExplainer.py +77 -0
- edsl/questions/HTMLQuestion.py +103 -0
- edsl/questions/QuestionBase.py +518 -664
- edsl/questions/QuestionBasePromptsMixin.py +221 -217
- edsl/questions/QuestionBudget.py +227 -227
- edsl/questions/QuestionCheckBox.py +359 -359
- edsl/questions/QuestionExtract.py +180 -182
- edsl/questions/QuestionFreeText.py +113 -114
- edsl/questions/QuestionFunctional.py +166 -166
- edsl/questions/QuestionList.py +223 -231
- edsl/questions/QuestionMatrix.py +265 -0
- edsl/questions/QuestionMultipleChoice.py +330 -286
- edsl/questions/QuestionNumerical.py +151 -153
- edsl/questions/QuestionRank.py +314 -324
- edsl/questions/Quick.py +41 -41
- edsl/questions/SimpleAskMixin.py +74 -73
- edsl/questions/__init__.py +27 -26
- edsl/questions/{AnswerValidatorMixin.py → answer_validator_mixin.py} +334 -289
- edsl/questions/compose_questions.py +98 -98
- edsl/questions/data_structures.py +20 -0
- edsl/questions/decorators.py +21 -21
- edsl/questions/derived/QuestionLikertFive.py +76 -76
- edsl/questions/derived/QuestionLinearScale.py +90 -87
- edsl/questions/derived/QuestionTopK.py +93 -93
- edsl/questions/derived/QuestionYesNo.py +82 -82
- edsl/questions/descriptors.py +427 -413
- edsl/questions/loop_processor.py +149 -0
- edsl/questions/prompt_templates/question_budget.jinja +13 -13
- edsl/questions/prompt_templates/question_checkbox.jinja +32 -32
- edsl/questions/prompt_templates/question_extract.jinja +11 -11
- edsl/questions/prompt_templates/question_free_text.jinja +3 -3
- edsl/questions/prompt_templates/question_linear_scale.jinja +11 -11
- edsl/questions/prompt_templates/question_list.jinja +17 -17
- edsl/questions/prompt_templates/question_multiple_choice.jinja +33 -33
- edsl/questions/prompt_templates/question_numerical.jinja +36 -36
- edsl/questions/{QuestionBaseGenMixin.py → question_base_gen_mixin.py} +168 -161
- edsl/questions/question_registry.py +177 -177
- edsl/questions/{RegisterQuestionsMeta.py → register_questions_meta.py} +71 -71
- edsl/questions/{ResponseValidatorABC.py → response_validator_abc.py} +188 -174
- edsl/questions/response_validator_factory.py +34 -0
- edsl/questions/settings.py +12 -12
- edsl/questions/templates/budget/answering_instructions.jinja +7 -7
- edsl/questions/templates/budget/question_presentation.jinja +7 -7
- edsl/questions/templates/checkbox/answering_instructions.jinja +10 -10
- edsl/questions/templates/checkbox/question_presentation.jinja +22 -22
- edsl/questions/templates/extract/answering_instructions.jinja +7 -7
- edsl/questions/templates/likert_five/answering_instructions.jinja +10 -10
- edsl/questions/templates/likert_five/question_presentation.jinja +11 -11
- edsl/questions/templates/linear_scale/answering_instructions.jinja +5 -5
- edsl/questions/templates/linear_scale/question_presentation.jinja +5 -5
- edsl/questions/templates/list/answering_instructions.jinja +3 -3
- edsl/questions/templates/list/question_presentation.jinja +5 -5
- 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/questions/templates/multiple_choice/answering_instructions.jinja +9 -9
- edsl/questions/templates/multiple_choice/question_presentation.jinja +11 -11
- edsl/questions/templates/numerical/answering_instructions.jinja +6 -6
- edsl/questions/templates/numerical/question_presentation.jinja +6 -6
- edsl/questions/templates/rank/answering_instructions.jinja +11 -11
- edsl/questions/templates/rank/question_presentation.jinja +15 -15
- edsl/questions/templates/top_k/answering_instructions.jinja +8 -8
- edsl/questions/templates/top_k/question_presentation.jinja +22 -22
- edsl/questions/templates/yes_no/answering_instructions.jinja +6 -6
- edsl/questions/templates/yes_no/question_presentation.jinja +11 -11
- edsl/results/CSSParameterizer.py +108 -108
- edsl/results/Dataset.py +587 -424
- edsl/results/DatasetExportMixin.py +594 -731
- edsl/results/DatasetTree.py +295 -275
- edsl/results/MarkdownToDocx.py +122 -0
- edsl/results/MarkdownToPDF.py +111 -0
- edsl/results/Result.py +557 -465
- edsl/results/Results.py +1183 -1165
- edsl/results/ResultsExportMixin.py +45 -43
- edsl/results/ResultsGGMixin.py +121 -121
- edsl/results/TableDisplay.py +125 -198
- edsl/results/TextEditor.py +50 -0
- edsl/results/__init__.py +2 -2
- edsl/results/file_exports.py +252 -0
- edsl/results/{ResultsFetchMixin.py → results_fetch_mixin.py} +33 -33
- edsl/results/{Selector.py → results_selector.py} +145 -135
- edsl/results/{ResultsToolsMixin.py → results_tools_mixin.py} +98 -98
- edsl/results/smart_objects.py +96 -0
- edsl/results/table_data_class.py +12 -0
- edsl/results/table_display.css +77 -77
- edsl/results/table_renderers.py +118 -0
- edsl/results/tree_explore.py +115 -115
- edsl/scenarios/ConstructDownloadLink.py +109 -0
- edsl/scenarios/DocumentChunker.py +102 -0
- edsl/scenarios/DocxScenario.py +16 -0
- edsl/scenarios/FileStore.py +511 -632
- edsl/scenarios/PdfExtractor.py +40 -0
- edsl/scenarios/Scenario.py +498 -601
- edsl/scenarios/ScenarioHtmlMixin.py +65 -64
- edsl/scenarios/ScenarioList.py +1458 -1287
- edsl/scenarios/ScenarioListExportMixin.py +45 -52
- edsl/scenarios/ScenarioListPdfMixin.py +239 -261
- edsl/scenarios/__init__.py +3 -4
- 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 +38 -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} +131 -127
- edsl/scenarios/scenario_selector.py +156 -0
- edsl/shared.py +1 -1
- edsl/study/ObjectEntry.py +173 -173
- edsl/study/ProofOfWork.py +113 -113
- edsl/study/SnapShot.py +80 -80
- edsl/study/Study.py +521 -528
- edsl/study/__init__.py +4 -4
- edsl/surveys/ConstructDAG.py +92 -0
- edsl/surveys/DAG.py +148 -148
- edsl/surveys/EditSurvey.py +221 -0
- edsl/surveys/InstructionHandler.py +100 -0
- edsl/surveys/Memory.py +31 -31
- edsl/surveys/MemoryManagement.py +72 -0
- edsl/surveys/MemoryPlan.py +244 -244
- edsl/surveys/Rule.py +327 -326
- edsl/surveys/RuleCollection.py +385 -387
- edsl/surveys/RuleManager.py +172 -0
- edsl/surveys/Simulator.py +75 -0
- edsl/surveys/Survey.py +1280 -1801
- edsl/surveys/SurveyCSS.py +273 -261
- edsl/surveys/SurveyExportMixin.py +259 -259
- edsl/surveys/{SurveyFlowVisualizationMixin.py → SurveyFlowVisualization.py} +181 -179
- edsl/surveys/SurveyQualtricsImport.py +284 -284
- edsl/surveys/SurveyToApp.py +141 -0
- edsl/surveys/__init__.py +5 -3
- edsl/surveys/base.py +53 -53
- edsl/surveys/descriptors.py +60 -56
- edsl/surveys/instructions/ChangeInstruction.py +48 -49
- edsl/surveys/instructions/Instruction.py +56 -65
- edsl/surveys/instructions/InstructionCollection.py +82 -77
- edsl/templates/error_reporting/base.html +23 -23
- edsl/templates/error_reporting/exceptions_by_model.html +34 -34
- edsl/templates/error_reporting/exceptions_by_question_name.html +16 -16
- edsl/templates/error_reporting/exceptions_by_type.html +16 -16
- edsl/templates/error_reporting/interview_details.html +115 -115
- edsl/templates/error_reporting/interviews.html +19 -19
- edsl/templates/error_reporting/overview.html +4 -4
- edsl/templates/error_reporting/performance_plot.html +1 -1
- edsl/templates/error_reporting/report.css +73 -73
- edsl/templates/error_reporting/report.html +117 -117
- edsl/templates/error_reporting/report.js +25 -25
- edsl/tools/__init__.py +1 -1
- edsl/tools/clusters.py +192 -192
- edsl/tools/embeddings.py +27 -27
- edsl/tools/embeddings_plotting.py +118 -118
- edsl/tools/plotting.py +112 -112
- edsl/tools/summarize.py +18 -18
- edsl/utilities/PrettyList.py +56 -0
- edsl/utilities/SystemInfo.py +28 -28
- edsl/utilities/__init__.py +22 -22
- edsl/utilities/ast_utilities.py +25 -25
- edsl/utilities/data/Registry.py +6 -6
- edsl/utilities/data/__init__.py +1 -1
- edsl/utilities/data/scooter_results.json +1 -1
- edsl/utilities/decorators.py +77 -77
- edsl/utilities/gcp_bucket/cloud_storage.py +96 -96
- edsl/utilities/interface.py +627 -627
- edsl/utilities/is_notebook.py +18 -0
- edsl/utilities/is_valid_variable_name.py +11 -0
- edsl/utilities/naming_utilities.py +263 -263
- edsl/utilities/remove_edsl_version.py +24 -0
- edsl/utilities/repair_functions.py +28 -28
- edsl/utilities/restricted_python.py +70 -70
- edsl/utilities/utilities.py +436 -424
- {edsl-0.1.39.dev3.dist-info → edsl-0.1.39.dev5.dist-info}/LICENSE +21 -21
- {edsl-0.1.39.dev3.dist-info → edsl-0.1.39.dev5.dist-info}/METADATA +13 -11
- edsl-0.1.39.dev5.dist-info/RECORD +358 -0
- {edsl-0.1.39.dev3.dist-info → edsl-0.1.39.dev5.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.39.dev3.dist-info/RECORD +0 -277
edsl/results/Results.py
CHANGED
@@ -1,1165 +1,1183 @@
|
|
1
|
-
"""
|
2
|
-
The Results object is the result of running a survey.
|
3
|
-
It is not typically instantiated directly, but is returned by the run method of a `Job` object.
|
4
|
-
"""
|
5
|
-
|
6
|
-
from __future__ import annotations
|
7
|
-
import json
|
8
|
-
import random
|
9
|
-
from collections import UserList, defaultdict
|
10
|
-
from typing import Optional, Callable, Any, Type, Union, List, TYPE_CHECKING
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
from edsl.
|
30
|
-
from edsl.
|
31
|
-
from edsl.results.
|
32
|
-
from edsl.
|
33
|
-
from edsl.
|
34
|
-
|
35
|
-
|
36
|
-
from edsl.
|
37
|
-
|
38
|
-
|
39
|
-
from edsl.
|
40
|
-
|
41
|
-
|
42
|
-
class Mixins(
|
43
|
-
ResultsExportMixin,
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
>>>
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
class
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
It
|
75
|
-
|
76
|
-
|
77
|
-
""
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
"
|
83
|
-
"
|
84
|
-
"
|
85
|
-
"
|
86
|
-
"
|
87
|
-
"
|
88
|
-
"
|
89
|
-
"
|
90
|
-
"
|
91
|
-
"
|
92
|
-
"
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
:param
|
109
|
-
:param
|
110
|
-
:param
|
111
|
-
:param
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
from edsl.
|
116
|
-
|
117
|
-
|
118
|
-
self.
|
119
|
-
self.
|
120
|
-
self.
|
121
|
-
self.
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
"
|
136
|
-
|
137
|
-
"
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
self.
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
>>>
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
return f"Results(data = {
|
309
|
-
|
310
|
-
def table(
|
311
|
-
self,
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
data_type
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
.
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
from edsl import
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
if
|
386
|
-
d.update(
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
@property
|
425
|
-
def
|
426
|
-
return self.
|
427
|
-
|
428
|
-
def
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
""
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
)
|
493
|
-
except Exception as e:
|
494
|
-
raise ResultsDeserializationError(f"Error in Results.from_dict: {e}")
|
495
|
-
return results
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
AgentList
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
>>> r.
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
>>> r.
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
>>> r.
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
>>> r.
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
>>> r.
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
>>> r.
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
.union(self.
|
679
|
-
.union(self.
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
>>> r.
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
data
|
761
|
-
|
762
|
-
)
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
>>> result
|
792
|
-
>>>
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
>>>
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
evaluator
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
:
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
)
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1
|
+
"""
|
2
|
+
The Results object is the result of running a survey.
|
3
|
+
It is not typically instantiated directly, but is returned by the run method of a `Job` object.
|
4
|
+
"""
|
5
|
+
|
6
|
+
from __future__ import annotations
|
7
|
+
import json
|
8
|
+
import random
|
9
|
+
from collections import UserList, defaultdict
|
10
|
+
from typing import Optional, Callable, Any, Type, Union, List, TYPE_CHECKING
|
11
|
+
|
12
|
+
from bisect import bisect_left
|
13
|
+
|
14
|
+
from edsl.Base import Base
|
15
|
+
from edsl.exceptions.results import (
|
16
|
+
ResultsError,
|
17
|
+
ResultsBadMutationstringError,
|
18
|
+
ResultsColumnNotFoundError,
|
19
|
+
ResultsInvalidNameError,
|
20
|
+
ResultsMutateError,
|
21
|
+
ResultsFilterError,
|
22
|
+
ResultsDeserializationError,
|
23
|
+
)
|
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
|
+
|
36
|
+
from edsl.results.ResultsExportMixin import ResultsExportMixin
|
37
|
+
from edsl.results.ResultsGGMixin import ResultsGGMixin
|
38
|
+
from edsl.results.results_fetch_mixin import ResultsFetchMixin
|
39
|
+
from edsl.utilities.remove_edsl_version import remove_edsl_version
|
40
|
+
|
41
|
+
|
42
|
+
class Mixins(
|
43
|
+
ResultsExportMixin,
|
44
|
+
ResultsFetchMixin,
|
45
|
+
ResultsGGMixin,
|
46
|
+
):
|
47
|
+
def long(self):
|
48
|
+
return self.table().long()
|
49
|
+
|
50
|
+
def print_long(self, max_rows: int = None) -> None:
|
51
|
+
"""Print the results in long format.
|
52
|
+
|
53
|
+
>>> from edsl.results import Results
|
54
|
+
>>> r = Results.example()
|
55
|
+
>>> r.select('how_feeling').print_long(max_rows = 2)
|
56
|
+
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━┓
|
57
|
+
┃ Result index ┃ Key ┃ Value ┃
|
58
|
+
┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━┩
|
59
|
+
│ 0 │ how_feeling │ OK │
|
60
|
+
│ 1 │ how_feeling │ Great │
|
61
|
+
└──────────────┴─────────────┴───────┘
|
62
|
+
"""
|
63
|
+
from edsl.utilities.interface import print_results_long
|
64
|
+
|
65
|
+
print_results_long(self, max_rows=max_rows)
|
66
|
+
|
67
|
+
|
68
|
+
class Results(UserList, Mixins, Base):
|
69
|
+
"""
|
70
|
+
This class is a UserList of Result objects.
|
71
|
+
|
72
|
+
It is instantiated with a `Survey` and a list of `Result` objects.
|
73
|
+
It can be manipulated in various ways with select, filter, mutate, etc.
|
74
|
+
It also has a list of created_columns, which are columns that have been created with `mutate` and are not part of the original data.
|
75
|
+
"""
|
76
|
+
|
77
|
+
__documentation__ = "https://docs.expectedparrot.com/en/latest/results.html"
|
78
|
+
|
79
|
+
known_data_types = [
|
80
|
+
"answer",
|
81
|
+
"scenario",
|
82
|
+
"agent",
|
83
|
+
"model",
|
84
|
+
"prompt",
|
85
|
+
"raw_model_response",
|
86
|
+
"iteration",
|
87
|
+
"question_text",
|
88
|
+
"question_options",
|
89
|
+
"question_type",
|
90
|
+
"comment",
|
91
|
+
"generated_tokens",
|
92
|
+
"cache_used",
|
93
|
+
]
|
94
|
+
|
95
|
+
def __init__(
|
96
|
+
self,
|
97
|
+
survey: Optional[Survey] = None,
|
98
|
+
data: Optional[list[Result]] = None,
|
99
|
+
created_columns: Optional[list[str]] = None,
|
100
|
+
cache: Optional[Cache] = None,
|
101
|
+
job_uuid: Optional[str] = None,
|
102
|
+
total_results: Optional[int] = None,
|
103
|
+
task_history: Optional[TaskHistory] = None,
|
104
|
+
):
|
105
|
+
"""Instantiate a `Results` object with a survey and a list of `Result` objects.
|
106
|
+
|
107
|
+
:param survey: A Survey object.
|
108
|
+
:param data: A list of Result objects.
|
109
|
+
:param created_columns: A list of strings that are created columns.
|
110
|
+
:param job_uuid: A string representing the job UUID.
|
111
|
+
:param total_results: An integer representing the total number of results.
|
112
|
+
"""
|
113
|
+
super().__init__(data)
|
114
|
+
from edsl.data.Cache import Cache
|
115
|
+
from edsl.jobs.tasks.TaskHistory import TaskHistory
|
116
|
+
|
117
|
+
self.survey = survey
|
118
|
+
self.created_columns = created_columns or []
|
119
|
+
self._job_uuid = job_uuid
|
120
|
+
self._total_results = total_results
|
121
|
+
self.cache = cache or Cache()
|
122
|
+
|
123
|
+
self.task_history = task_history or TaskHistory(interviews=[])
|
124
|
+
|
125
|
+
if hasattr(self, "_add_output_functions"):
|
126
|
+
self._add_output_functions()
|
127
|
+
|
128
|
+
def _summary(self) -> dict:
|
129
|
+
import reprlib
|
130
|
+
|
131
|
+
d = {
|
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),
|
137
|
+
"Survey question names": reprlib.repr(self.survey.question_names),
|
138
|
+
}
|
139
|
+
return d
|
140
|
+
|
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:
|
168
|
+
"""
|
169
|
+
Computes the cost of a completed job in USD.
|
170
|
+
"""
|
171
|
+
total_cost = 0
|
172
|
+
for result in self:
|
173
|
+
for key in result.raw_model_response:
|
174
|
+
if key.endswith("_cost"):
|
175
|
+
result_cost = result.raw_model_response[key]
|
176
|
+
|
177
|
+
question_name = key.removesuffix("_cost")
|
178
|
+
cache_used = result.cache_used_dict[question_name]
|
179
|
+
|
180
|
+
if isinstance(result_cost, (int, float)):
|
181
|
+
if include_cached_responses_in_cost:
|
182
|
+
total_cost += result_cost
|
183
|
+
elif not include_cached_responses_in_cost and not cache_used:
|
184
|
+
total_cost += result_cost
|
185
|
+
|
186
|
+
return total_cost
|
187
|
+
|
188
|
+
def leaves(self):
|
189
|
+
leaves = []
|
190
|
+
for result in self:
|
191
|
+
leaves.extend(result.leaves())
|
192
|
+
return leaves
|
193
|
+
|
194
|
+
def tree(self, node_list: Optional[List[str]] = None):
|
195
|
+
return self.to_scenario_list().tree(node_list)
|
196
|
+
|
197
|
+
def interactive_tree(
|
198
|
+
self,
|
199
|
+
fold_attributes: Optional[List[str]] = None,
|
200
|
+
drop: Optional[List[str]] = None,
|
201
|
+
open_file=True,
|
202
|
+
) -> dict:
|
203
|
+
"""Return the results as a tree."""
|
204
|
+
from edsl.results.tree_explore import FoldableHTMLTableGenerator
|
205
|
+
|
206
|
+
if drop is None:
|
207
|
+
drop = []
|
208
|
+
|
209
|
+
valid_attributes = [
|
210
|
+
"model",
|
211
|
+
"scenario",
|
212
|
+
"agent",
|
213
|
+
"answer",
|
214
|
+
"question",
|
215
|
+
"iteration",
|
216
|
+
]
|
217
|
+
if fold_attributes is None:
|
218
|
+
fold_attributes = []
|
219
|
+
|
220
|
+
for attribute in fold_attributes:
|
221
|
+
if attribute not in valid_attributes:
|
222
|
+
raise ValueError(
|
223
|
+
f"Invalid fold attribute: {attribute}; must be in {valid_attributes}"
|
224
|
+
)
|
225
|
+
data = self.leaves()
|
226
|
+
generator = FoldableHTMLTableGenerator(data)
|
227
|
+
tree = generator.tree(fold_attributes=fold_attributes, drop=drop)
|
228
|
+
html_content = generator.generate_html(tree, fold_attributes)
|
229
|
+
import tempfile
|
230
|
+
from edsl.utilities.utilities import is_notebook
|
231
|
+
|
232
|
+
from IPython.display import display, HTML
|
233
|
+
|
234
|
+
if is_notebook():
|
235
|
+
import html
|
236
|
+
from IPython.display import display, HTML
|
237
|
+
|
238
|
+
height = 1000
|
239
|
+
width = 1000
|
240
|
+
escaped_output = html.escape(html_content)
|
241
|
+
# escaped_output = rendered_html
|
242
|
+
iframe = f""""
|
243
|
+
<iframe srcdoc="{ escaped_output }" style="width: {width}px; height: {height}px;"></iframe>
|
244
|
+
"""
|
245
|
+
display(HTML(iframe))
|
246
|
+
return None
|
247
|
+
|
248
|
+
with tempfile.NamedTemporaryFile(suffix=".html", delete=False) as f:
|
249
|
+
f.write(html_content.encode())
|
250
|
+
print(f"HTML file has been generated: {f.name}")
|
251
|
+
|
252
|
+
if open_file:
|
253
|
+
import webbrowser
|
254
|
+
import time
|
255
|
+
|
256
|
+
time.sleep(1) # Wait for 1 second
|
257
|
+
# webbrowser.open(f.name)
|
258
|
+
import os
|
259
|
+
|
260
|
+
filename = f.name
|
261
|
+
webbrowser.open(f"file://{os.path.abspath(filename)}")
|
262
|
+
|
263
|
+
else:
|
264
|
+
return html_content
|
265
|
+
|
266
|
+
def code(self):
|
267
|
+
raise NotImplementedError
|
268
|
+
|
269
|
+
def __getitem__(self, i):
|
270
|
+
if isinstance(i, int):
|
271
|
+
return self.data[i]
|
272
|
+
|
273
|
+
if isinstance(i, slice):
|
274
|
+
return self.__class__(survey=self.survey, data=self.data[i])
|
275
|
+
|
276
|
+
if isinstance(i, str):
|
277
|
+
return self.to_dict()[i]
|
278
|
+
|
279
|
+
raise TypeError("Invalid argument type")
|
280
|
+
|
281
|
+
def __add__(self, other: Results) -> Results:
|
282
|
+
"""Add two Results objects together.
|
283
|
+
They must have the same survey and created columns.
|
284
|
+
:param other: A Results object.
|
285
|
+
|
286
|
+
Example:
|
287
|
+
|
288
|
+
>>> r = Results.example()
|
289
|
+
>>> r2 = Results.example()
|
290
|
+
>>> r3 = r + r2
|
291
|
+
"""
|
292
|
+
if self.survey != other.survey:
|
293
|
+
raise ResultsError(
|
294
|
+
"The surveys are not the same so the the results cannot be added together."
|
295
|
+
)
|
296
|
+
if self.created_columns != other.created_columns:
|
297
|
+
raise ResultsError(
|
298
|
+
"The created columns are not the same so they cannot be added together."
|
299
|
+
)
|
300
|
+
|
301
|
+
return Results(
|
302
|
+
survey=self.survey,
|
303
|
+
data=self.data + other.data,
|
304
|
+
created_columns=self.created_columns,
|
305
|
+
)
|
306
|
+
|
307
|
+
def __repr__(self) -> str:
|
308
|
+
return f"Results(data = {self.data}, survey = {repr(self.survey)}, created_columns = {self.created_columns})"
|
309
|
+
|
310
|
+
def table(
|
311
|
+
self,
|
312
|
+
*fields,
|
313
|
+
tablefmt: Optional[str] = None,
|
314
|
+
pretty_labels: Optional[dict] = None,
|
315
|
+
print_parameters: Optional[dict] = None,
|
316
|
+
):
|
317
|
+
new_fields = []
|
318
|
+
for field in fields:
|
319
|
+
if "." in field:
|
320
|
+
data_type, key = field.split(".")
|
321
|
+
if data_type not in self.known_data_types:
|
322
|
+
raise ResultsInvalidNameError(
|
323
|
+
f"{data_type} is not a valid data type. Must be in {self.known_data_types}"
|
324
|
+
)
|
325
|
+
if key == "*":
|
326
|
+
for k in self._data_type_to_keys[data_type]:
|
327
|
+
new_fields.append(k)
|
328
|
+
else:
|
329
|
+
if key not in self._key_to_data_type:
|
330
|
+
raise ResultsColumnNotFoundError(
|
331
|
+
f"{key} is not a valid key. Must be in {self._key_to_data_type}"
|
332
|
+
)
|
333
|
+
new_fields.append(key)
|
334
|
+
else:
|
335
|
+
new_fields.append(field)
|
336
|
+
|
337
|
+
return (
|
338
|
+
self.to_scenario_list()
|
339
|
+
.to_dataset()
|
340
|
+
.table(
|
341
|
+
*new_fields,
|
342
|
+
tablefmt=tablefmt,
|
343
|
+
pretty_labels=pretty_labels,
|
344
|
+
print_parameters=print_parameters,
|
345
|
+
)
|
346
|
+
)
|
347
|
+
|
348
|
+
def to_dict(
|
349
|
+
self,
|
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,
|
355
|
+
) -> dict[str, Any]:
|
356
|
+
from edsl.data.Cache import Cache
|
357
|
+
|
358
|
+
if sort:
|
359
|
+
data = sorted([result for result in self.data], key=lambda x: hash(x))
|
360
|
+
else:
|
361
|
+
data = [result for result in self.data]
|
362
|
+
|
363
|
+
d = {
|
364
|
+
"data": [
|
365
|
+
result.to_dict(
|
366
|
+
add_edsl_version=add_edsl_version,
|
367
|
+
include_cache_info=include_cache_info,
|
368
|
+
)
|
369
|
+
for result in data
|
370
|
+
],
|
371
|
+
"survey": self.survey.to_dict(add_edsl_version=add_edsl_version),
|
372
|
+
"created_columns": self.created_columns,
|
373
|
+
}
|
374
|
+
if include_cache:
|
375
|
+
d.update(
|
376
|
+
{
|
377
|
+
"cache": (
|
378
|
+
Cache()
|
379
|
+
if not hasattr(self, "cache")
|
380
|
+
else self.cache.to_dict(add_edsl_version=add_edsl_version)
|
381
|
+
)
|
382
|
+
}
|
383
|
+
)
|
384
|
+
|
385
|
+
if self.task_history.has_unfixed_exceptions or include_task_history:
|
386
|
+
d.update({"task_history": self.task_history.to_dict()})
|
387
|
+
|
388
|
+
if add_edsl_version:
|
389
|
+
from edsl import __version__
|
390
|
+
|
391
|
+
d["edsl_version"] = __version__
|
392
|
+
d["edsl_class_name"] = "Results"
|
393
|
+
|
394
|
+
return d
|
395
|
+
|
396
|
+
def compare(self, other_results: Results) -> dict:
|
397
|
+
"""
|
398
|
+
Compare two Results objects and return the differences.
|
399
|
+
"""
|
400
|
+
hashes_0 = [hash(result) for result in self]
|
401
|
+
hashes_1 = [hash(result) for result in other_results]
|
402
|
+
|
403
|
+
in_self_but_not_other = set(hashes_0).difference(set(hashes_1))
|
404
|
+
in_other_but_not_self = set(hashes_1).difference(set(hashes_0))
|
405
|
+
|
406
|
+
indicies_self = [hashes_0.index(h) for h in in_self_but_not_other]
|
407
|
+
indices_other = [hashes_1.index(h) for h in in_other_but_not_self]
|
408
|
+
return {
|
409
|
+
"a_not_b": [self[i] for i in indicies_self],
|
410
|
+
"b_not_a": [other_results[i] for i in indices_other],
|
411
|
+
}
|
412
|
+
|
413
|
+
@property
|
414
|
+
def has_unfixed_exceptions(self) -> bool:
|
415
|
+
return self.task_history.has_unfixed_exceptions
|
416
|
+
|
417
|
+
def __hash__(self) -> int:
|
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
|
+
)
|
423
|
+
|
424
|
+
@property
|
425
|
+
def hashes(self) -> set:
|
426
|
+
return set(hash(result) for result in self.data)
|
427
|
+
|
428
|
+
def sample(self, n: int) -> Results:
|
429
|
+
"""Return a random sample of the results.
|
430
|
+
|
431
|
+
:param n: The number of samples to return.
|
432
|
+
|
433
|
+
>>> from edsl.results import Results
|
434
|
+
>>> r = Results.example()
|
435
|
+
>>> len(r.sample(2))
|
436
|
+
2
|
437
|
+
"""
|
438
|
+
indices = None
|
439
|
+
|
440
|
+
for entry in self:
|
441
|
+
key, values = list(entry.items())[0]
|
442
|
+
if indices is None: # gets the indices for the first time
|
443
|
+
indices = list(range(len(values)))
|
444
|
+
sampled_indices = random.sample(indices, n)
|
445
|
+
if n > len(indices):
|
446
|
+
raise ResultsError(
|
447
|
+
f"Cannot sample {n} items from a list of length {len(indices)}."
|
448
|
+
)
|
449
|
+
entry[key] = [values[i] for i in sampled_indices]
|
450
|
+
|
451
|
+
return self
|
452
|
+
|
453
|
+
@classmethod
|
454
|
+
@remove_edsl_version
|
455
|
+
def from_dict(cls, data: dict[str, Any]) -> Results:
|
456
|
+
"""Convert a dictionary to a Results object.
|
457
|
+
|
458
|
+
:param data: A dictionary representation of a Results object.
|
459
|
+
|
460
|
+
Example:
|
461
|
+
|
462
|
+
>>> r = Results.example()
|
463
|
+
>>> d = r.to_dict()
|
464
|
+
>>> r2 = Results.from_dict(d)
|
465
|
+
>>> r == r2
|
466
|
+
True
|
467
|
+
"""
|
468
|
+
from edsl.surveys.Survey import Survey
|
469
|
+
from edsl.data.Cache import Cache
|
470
|
+
from edsl.results.Result import Result
|
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
|
+
}
|
490
|
+
|
491
|
+
try:
|
492
|
+
results = cls(**params)
|
493
|
+
except Exception as e:
|
494
|
+
raise ResultsDeserializationError(f"Error in Results.from_dict: {e}")
|
495
|
+
return results
|
496
|
+
|
497
|
+
@property
|
498
|
+
def _key_to_data_type(self) -> dict[str, str]:
|
499
|
+
"""
|
500
|
+
Return a mapping of keys (how_feeling, status, etc.) to strings representing data types.
|
501
|
+
|
502
|
+
Objects such as Agent, Answer, Model, Scenario, etc.
|
503
|
+
- Uses the key_to_data_type property of the Result class.
|
504
|
+
- Includes any columns that the user has created with `mutate`
|
505
|
+
"""
|
506
|
+
d: dict = {}
|
507
|
+
for result in self.data:
|
508
|
+
d.update(result.key_to_data_type)
|
509
|
+
for column in self.created_columns:
|
510
|
+
d[column] = "answer"
|
511
|
+
|
512
|
+
return d
|
513
|
+
|
514
|
+
@property
|
515
|
+
def _data_type_to_keys(self) -> dict[str, str]:
|
516
|
+
"""
|
517
|
+
Return a mapping of strings representing data types (objects such as Agent, Answer, Model, Scenario, etc.) to keys (how_feeling, status, etc.)
|
518
|
+
- Uses the key_to_data_type property of the Result class.
|
519
|
+
- Includes any columns that the user has created with `mutate`
|
520
|
+
|
521
|
+
Example:
|
522
|
+
|
523
|
+
>>> r = Results.example()
|
524
|
+
>>> r._data_type_to_keys
|
525
|
+
defaultdict(...
|
526
|
+
"""
|
527
|
+
d: dict = defaultdict(set)
|
528
|
+
for result in self.data:
|
529
|
+
for key, value in result.key_to_data_type.items():
|
530
|
+
d[value] = d[value].union(set({key}))
|
531
|
+
for column in self.created_columns:
|
532
|
+
d["answer"] = d["answer"].union(set({column}))
|
533
|
+
return d
|
534
|
+
|
535
|
+
@property
|
536
|
+
def columns(self) -> list[str]:
|
537
|
+
"""Return a list of all of the columns that are in the Results.
|
538
|
+
|
539
|
+
Example:
|
540
|
+
|
541
|
+
>>> r = Results.example()
|
542
|
+
>>> r.columns
|
543
|
+
['agent.agent_index', ...]
|
544
|
+
"""
|
545
|
+
column_names = [f"{v}.{k}" for k, v in self._key_to_data_type.items()]
|
546
|
+
from edsl.utilities.PrettyList import PrettyList
|
547
|
+
|
548
|
+
return PrettyList(sorted(column_names))
|
549
|
+
|
550
|
+
@property
|
551
|
+
def answer_keys(self) -> dict[str, str]:
|
552
|
+
"""Return a mapping of answer keys to question text.
|
553
|
+
|
554
|
+
Example:
|
555
|
+
|
556
|
+
>>> r = Results.example()
|
557
|
+
>>> r.answer_keys
|
558
|
+
{'how_feeling': 'How are you this {{ period }}?', 'how_feeling_yesterday': 'How were you feeling yesterday {{ period }}?'}
|
559
|
+
"""
|
560
|
+
from edsl.utilities.utilities import shorten_string
|
561
|
+
|
562
|
+
if not self.survey:
|
563
|
+
raise ResultsError("Survey is not defined so no answer keys are available.")
|
564
|
+
|
565
|
+
answer_keys = self._data_type_to_keys["answer"]
|
566
|
+
answer_keys = {k for k in answer_keys if "_comment" not in k}
|
567
|
+
questions_text = [
|
568
|
+
self.survey._get_question_by_name(k).question_text for k in answer_keys
|
569
|
+
]
|
570
|
+
short_question_text = [shorten_string(q, 80) for q in questions_text]
|
571
|
+
initial_dict = dict(zip(answer_keys, short_question_text))
|
572
|
+
sorted_dict = {key: initial_dict[key] for key in sorted(initial_dict)}
|
573
|
+
return sorted_dict
|
574
|
+
|
575
|
+
@property
|
576
|
+
def agents(self) -> AgentList:
|
577
|
+
"""Return a list of all of the agents in the Results.
|
578
|
+
|
579
|
+
Example:
|
580
|
+
|
581
|
+
>>> r = Results.example()
|
582
|
+
>>> r.agents
|
583
|
+
AgentList([Agent(traits = {'status': 'Joyful'}), Agent(traits = {'status': 'Joyful'}), Agent(traits = {'status': 'Sad'}), Agent(traits = {'status': 'Sad'})])
|
584
|
+
"""
|
585
|
+
from edsl.agents.AgentList import AgentList
|
586
|
+
|
587
|
+
return AgentList([r.agent for r in self.data])
|
588
|
+
|
589
|
+
@property
|
590
|
+
def models(self) -> ModelList:
|
591
|
+
"""Return a list of all of the models in the Results.
|
592
|
+
|
593
|
+
Example:
|
594
|
+
|
595
|
+
>>> r = Results.example()
|
596
|
+
>>> r.models[0]
|
597
|
+
Model(model_name = ...)
|
598
|
+
"""
|
599
|
+
from edsl.language_models.ModelList import ModelList
|
600
|
+
|
601
|
+
return ModelList([r.model for r in self.data])
|
602
|
+
|
603
|
+
def __eq__(self, other):
|
604
|
+
return hash(self) == hash(other)
|
605
|
+
|
606
|
+
@property
|
607
|
+
def scenarios(self) -> ScenarioList:
|
608
|
+
"""Return a list of all of the scenarios in the Results.
|
609
|
+
|
610
|
+
Example:
|
611
|
+
|
612
|
+
>>> r = Results.example()
|
613
|
+
>>> r.scenarios
|
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})])
|
615
|
+
"""
|
616
|
+
from edsl.scenarios.ScenarioList import ScenarioList
|
617
|
+
|
618
|
+
return ScenarioList([r.scenario for r in self.data])
|
619
|
+
|
620
|
+
@property
|
621
|
+
def agent_keys(self) -> list[str]:
|
622
|
+
"""Return a set of all of the keys that are in the Agent data.
|
623
|
+
|
624
|
+
Example:
|
625
|
+
|
626
|
+
>>> r = Results.example()
|
627
|
+
>>> r.agent_keys
|
628
|
+
['agent_index', 'agent_instruction', 'agent_name', 'status']
|
629
|
+
"""
|
630
|
+
return sorted(self._data_type_to_keys["agent"])
|
631
|
+
|
632
|
+
@property
|
633
|
+
def model_keys(self) -> list[str]:
|
634
|
+
"""Return a set of all of the keys that are in the LanguageModel data.
|
635
|
+
|
636
|
+
>>> r = Results.example()
|
637
|
+
>>> r.model_keys
|
638
|
+
['frequency_penalty', 'logprobs', 'max_tokens', 'model', 'model_index', 'presence_penalty', 'temperature', 'top_logprobs', 'top_p']
|
639
|
+
"""
|
640
|
+
return sorted(self._data_type_to_keys["model"])
|
641
|
+
|
642
|
+
@property
|
643
|
+
def scenario_keys(self) -> list[str]:
|
644
|
+
"""Return a set of all of the keys that are in the Scenario data.
|
645
|
+
|
646
|
+
>>> r = Results.example()
|
647
|
+
>>> r.scenario_keys
|
648
|
+
['period', 'scenario_index']
|
649
|
+
"""
|
650
|
+
return sorted(self._data_type_to_keys["scenario"])
|
651
|
+
|
652
|
+
@property
|
653
|
+
def question_names(self) -> list[str]:
|
654
|
+
"""Return a list of all of the question names.
|
655
|
+
|
656
|
+
Example:
|
657
|
+
|
658
|
+
>>> r = Results.example()
|
659
|
+
>>> r.question_names
|
660
|
+
['how_feeling', 'how_feeling_yesterday']
|
661
|
+
"""
|
662
|
+
if self.survey is None:
|
663
|
+
return []
|
664
|
+
return sorted(list(self.survey.question_names))
|
665
|
+
|
666
|
+
@property
|
667
|
+
def all_keys(self) -> list[str]:
|
668
|
+
"""Return a set of all of the keys that are in the Results.
|
669
|
+
|
670
|
+
Example:
|
671
|
+
|
672
|
+
>>> r = Results.example()
|
673
|
+
>>> r.all_keys
|
674
|
+
['agent_index', ...]
|
675
|
+
"""
|
676
|
+
answer_keys = set(self.answer_keys)
|
677
|
+
all_keys = (
|
678
|
+
answer_keys.union(self.agent_keys)
|
679
|
+
.union(self.scenario_keys)
|
680
|
+
.union(self.model_keys)
|
681
|
+
)
|
682
|
+
return sorted(list(all_keys))
|
683
|
+
|
684
|
+
def first(self) -> Result:
|
685
|
+
"""Return the first observation in the results.
|
686
|
+
|
687
|
+
Example:
|
688
|
+
|
689
|
+
>>> r = Results.example()
|
690
|
+
>>> r.first()
|
691
|
+
Result(agent...
|
692
|
+
"""
|
693
|
+
return self.data[0]
|
694
|
+
|
695
|
+
def answer_truncate(
|
696
|
+
self, column: str, top_n: int = 5, new_var_name: str = None
|
697
|
+
) -> Results:
|
698
|
+
"""Create a new variable that truncates the answers to the top_n.
|
699
|
+
|
700
|
+
:param column: The column to truncate.
|
701
|
+
:param top_n: The number of top answers to keep.
|
702
|
+
:param new_var_name: The name of the new variable. If None, it is the original name + '_truncated'.
|
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']}])
|
708
|
+
|
709
|
+
|
710
|
+
"""
|
711
|
+
if new_var_name is None:
|
712
|
+
new_var_name = column + "_truncated"
|
713
|
+
answers = list(self.select(column).tally().keys())
|
714
|
+
|
715
|
+
def f(x):
|
716
|
+
if x in answers[:top_n]:
|
717
|
+
return x
|
718
|
+
else:
|
719
|
+
return "Other"
|
720
|
+
|
721
|
+
return self.recode(column, recode_function=f, new_var_name=new_var_name)
|
722
|
+
|
723
|
+
def recode(
|
724
|
+
self, column: str, recode_function: Optional[Callable], new_var_name=None
|
725
|
+
) -> Results:
|
726
|
+
"""
|
727
|
+
Recode a column in the Results object.
|
728
|
+
|
729
|
+
>>> r = Results.example()
|
730
|
+
>>> r.recode('how_feeling', recode_function = lambda x: 1 if x == 'Great' else 0).select('how_feeling', 'how_feeling_recoded')
|
731
|
+
Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}, {'answer.how_feeling_recoded': [0, 1, 0, 0]}])
|
732
|
+
"""
|
733
|
+
|
734
|
+
if new_var_name is None:
|
735
|
+
new_var_name = column + "_recoded"
|
736
|
+
new_data = []
|
737
|
+
for result in self.data:
|
738
|
+
new_result = result.copy()
|
739
|
+
value = new_result.get_value("answer", column)
|
740
|
+
# breakpoint()
|
741
|
+
new_result["answer"][new_var_name] = recode_function(value)
|
742
|
+
new_data.append(new_result)
|
743
|
+
|
744
|
+
# print("Created new variable", new_var_name)
|
745
|
+
return Results(
|
746
|
+
survey=self.survey,
|
747
|
+
data=new_data,
|
748
|
+
created_columns=self.created_columns + [new_var_name],
|
749
|
+
)
|
750
|
+
|
751
|
+
def add_column(self, column_name: str, values: list) -> Results:
|
752
|
+
"""Adds columns to Results
|
753
|
+
|
754
|
+
>>> r = Results.example()
|
755
|
+
>>> r.add_column('a', [1,2,3, 4]).select('a')
|
756
|
+
Dataset([{'answer.a': [1, 2, 3, 4]}])
|
757
|
+
"""
|
758
|
+
|
759
|
+
assert len(values) == len(
|
760
|
+
self.data
|
761
|
+
), "The number of values must match the number of results."
|
762
|
+
new_results = self.data.copy()
|
763
|
+
for i, result in enumerate(new_results):
|
764
|
+
result["answer"][column_name] = values[i]
|
765
|
+
return Results(
|
766
|
+
survey=self.survey,
|
767
|
+
data=new_results,
|
768
|
+
created_columns=self.created_columns + [column_name],
|
769
|
+
)
|
770
|
+
|
771
|
+
def add_columns_from_dict(self, columns: List[dict]) -> Results:
|
772
|
+
"""Adds columns to Results from a list of dictionaries.
|
773
|
+
|
774
|
+
>>> r = Results.example()
|
775
|
+
>>> r.add_columns_from_dict([{'a': 1, 'b': 2}, {'a': 3, 'b': 4}, {'a':3, 'b':2}, {'a':3, 'b':2}]).select('a', 'b')
|
776
|
+
Dataset([{'answer.a': [1, 3, 3, 3]}, {'answer.b': [2, 4, 2, 2]}])
|
777
|
+
"""
|
778
|
+
keys = list(columns[0].keys())
|
779
|
+
for key in keys:
|
780
|
+
values = [d[key] for d in columns]
|
781
|
+
self = self.add_column(key, values)
|
782
|
+
return self
|
783
|
+
|
784
|
+
@staticmethod
|
785
|
+
def _create_evaluator(
|
786
|
+
result: Result, functions_dict: Optional[dict] = None
|
787
|
+
) -> "EvalWithCompoundTypes":
|
788
|
+
"""Create an evaluator for the expression.
|
789
|
+
|
790
|
+
>>> from unittest.mock import Mock
|
791
|
+
>>> result = Mock()
|
792
|
+
>>> result.combined_dict = {'how_feeling': 'OK'}
|
793
|
+
|
794
|
+
>>> evaluator = Results._create_evaluator(result = result, functions_dict = {})
|
795
|
+
>>> evaluator.eval("how_feeling == 'OK'")
|
796
|
+
True
|
797
|
+
|
798
|
+
>>> result.combined_dict = {'answer': {'how_feeling': 'OK'}}
|
799
|
+
>>> evaluator = Results._create_evaluator(result = result, functions_dict = {})
|
800
|
+
>>> evaluator.eval("answer.how_feeling== 'OK'")
|
801
|
+
True
|
802
|
+
|
803
|
+
Note that you need to refer to the answer dictionary in the expression.
|
804
|
+
|
805
|
+
>>> evaluator.eval("how_feeling== 'OK'")
|
806
|
+
Traceback (most recent call last):
|
807
|
+
...
|
808
|
+
simpleeval.NameNotDefined: 'how_feeling' is not defined for expression 'how_feeling== 'OK''
|
809
|
+
"""
|
810
|
+
from simpleeval import EvalWithCompoundTypes
|
811
|
+
|
812
|
+
if functions_dict is None:
|
813
|
+
functions_dict = {}
|
814
|
+
evaluator = EvalWithCompoundTypes(
|
815
|
+
names=result.combined_dict, functions=functions_dict
|
816
|
+
)
|
817
|
+
evaluator.functions.update(int=int, float=float)
|
818
|
+
return evaluator
|
819
|
+
|
820
|
+
def mutate(
|
821
|
+
self, new_var_string: str, functions_dict: Optional[dict] = None
|
822
|
+
) -> Results:
|
823
|
+
"""
|
824
|
+
Creates a value in the Results object as if has been asked as part of the survey.
|
825
|
+
|
826
|
+
:param new_var_string: A string that is a valid Python expression.
|
827
|
+
:param functions_dict: A dictionary of functions that can be used in the expression. The keys are the function names and the values are the functions themselves.
|
828
|
+
|
829
|
+
It splits the new_var_string at the "=" and uses simple_eval
|
830
|
+
|
831
|
+
Example:
|
832
|
+
|
833
|
+
>>> r = Results.example()
|
834
|
+
>>> r.mutate('how_feeling_x = how_feeling + "x"').select('how_feeling_x')
|
835
|
+
Dataset([{'answer.how_feeling_x': ...
|
836
|
+
"""
|
837
|
+
# extract the variable name and the expression
|
838
|
+
if "=" not in new_var_string:
|
839
|
+
raise ResultsBadMutationstringError(
|
840
|
+
f"Mutate requires an '=' in the string, but '{new_var_string}' doesn't have one."
|
841
|
+
)
|
842
|
+
raw_var_name, expression = new_var_string.split("=", 1)
|
843
|
+
var_name = raw_var_name.strip()
|
844
|
+
from edsl.utilities.utilities import is_valid_variable_name
|
845
|
+
|
846
|
+
if not is_valid_variable_name(var_name):
|
847
|
+
raise ResultsInvalidNameError(f"{var_name} is not a valid variable name.")
|
848
|
+
|
849
|
+
# create the evaluator
|
850
|
+
functions_dict = functions_dict or {}
|
851
|
+
|
852
|
+
def new_result(old_result: "Result", var_name: str) -> "Result":
|
853
|
+
evaluator = self._create_evaluator(old_result, functions_dict)
|
854
|
+
value = evaluator.eval(expression)
|
855
|
+
new_result = old_result.copy()
|
856
|
+
new_result["answer"][var_name] = value
|
857
|
+
return new_result
|
858
|
+
|
859
|
+
try:
|
860
|
+
new_data = [new_result(result, var_name) for result in self.data]
|
861
|
+
except Exception as e:
|
862
|
+
raise ResultsMutateError(f"Error in mutate. Exception:{e}")
|
863
|
+
|
864
|
+
return Results(
|
865
|
+
survey=self.survey,
|
866
|
+
data=new_data,
|
867
|
+
created_columns=self.created_columns + [var_name],
|
868
|
+
)
|
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
|
+
|
890
|
+
def rename(self, old_name: str, new_name: str) -> Results:
|
891
|
+
"""Rename an answer column in a Results object.
|
892
|
+
|
893
|
+
>>> s = Results.example()
|
894
|
+
>>> s.rename('how_feeling', 'how_feeling_new').select('how_feeling_new')
|
895
|
+
Dataset([{'answer.how_feeling_new': ['OK', 'Great', 'Terrible', 'OK']}])
|
896
|
+
|
897
|
+
# TODO: Should we allow renaming of scenario fields as well? Probably.
|
898
|
+
|
899
|
+
"""
|
900
|
+
|
901
|
+
for obs in self.data:
|
902
|
+
obs["answer"][new_name] = obs["answer"][old_name]
|
903
|
+
del obs["answer"][old_name]
|
904
|
+
|
905
|
+
return self
|
906
|
+
|
907
|
+
def shuffle(self, seed: Optional[str] = "edsl") -> Results:
|
908
|
+
"""Shuffle the results.
|
909
|
+
|
910
|
+
Example:
|
911
|
+
|
912
|
+
>>> r = Results.example()
|
913
|
+
>>> r.shuffle(seed = 1)[0]
|
914
|
+
Result(...)
|
915
|
+
"""
|
916
|
+
if seed != "edsl":
|
917
|
+
seed = random.seed(seed)
|
918
|
+
|
919
|
+
new_data = self.data.copy()
|
920
|
+
random.shuffle(new_data)
|
921
|
+
return Results(survey=self.survey, data=new_data, created_columns=None)
|
922
|
+
|
923
|
+
def sample(
|
924
|
+
self,
|
925
|
+
n: Optional[int] = None,
|
926
|
+
frac: Optional[float] = None,
|
927
|
+
with_replacement: bool = True,
|
928
|
+
seed: Optional[str] = None,
|
929
|
+
) -> Results:
|
930
|
+
"""Sample the results.
|
931
|
+
|
932
|
+
:param n: An integer representing the number of samples to take.
|
933
|
+
:param frac: A float representing the fraction of samples to take.
|
934
|
+
:param with_replacement: A boolean representing whether to sample with replacement.
|
935
|
+
:param seed: An integer representing the seed for the random number generator.
|
936
|
+
|
937
|
+
Example:
|
938
|
+
|
939
|
+
>>> r = Results.example()
|
940
|
+
>>> len(r.sample(2))
|
941
|
+
2
|
942
|
+
"""
|
943
|
+
if seed:
|
944
|
+
random.seed(seed)
|
945
|
+
|
946
|
+
if n is None and frac is None:
|
947
|
+
raise Exception("You must specify either n or frac.")
|
948
|
+
|
949
|
+
if n is not None and frac is not None:
|
950
|
+
raise Exception("You cannot specify both n and frac.")
|
951
|
+
|
952
|
+
if frac is not None and n is None:
|
953
|
+
n = int(frac * len(self.data))
|
954
|
+
|
955
|
+
if with_replacement:
|
956
|
+
new_data = random.choices(self.data, k=n)
|
957
|
+
else:
|
958
|
+
new_data = random.sample(self.data, n)
|
959
|
+
|
960
|
+
return Results(survey=self.survey, data=new_data, created_columns=None)
|
961
|
+
|
962
|
+
def select(self, *columns: Union[str, list[str]]) -> Results:
|
963
|
+
"""
|
964
|
+
Select data from the results and format it.
|
965
|
+
|
966
|
+
:param columns: A list of strings, each of which is a column name. The column name can be a single key, e.g. "how_feeling", or a dot-separated string, e.g. "answer.how_feeling".
|
967
|
+
|
968
|
+
Example:
|
969
|
+
|
970
|
+
>>> results = Results.example()
|
971
|
+
>>> results.select('how_feeling')
|
972
|
+
Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}])
|
973
|
+
|
974
|
+
>>> results.select('how_feeling', 'model', 'how_feeling')
|
975
|
+
Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}, {'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}, {'model.model': ['...', '...', '...', '...']}, {'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}, {'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}])
|
976
|
+
|
977
|
+
>>> from edsl import Results; r = Results.example(); r.select('answer.how_feeling_y')
|
978
|
+
Dataset([{'answer.how_feeling_yesterday': ['Great', 'Good', 'OK', 'Terrible']}])
|
979
|
+
"""
|
980
|
+
|
981
|
+
from edsl.results.results_selector import Selector
|
982
|
+
|
983
|
+
if len(self) == 0:
|
984
|
+
raise Exception("No data to select from---the Results object is empty.")
|
985
|
+
|
986
|
+
selector = Selector(
|
987
|
+
known_data_types=self.known_data_types,
|
988
|
+
data_type_to_keys=self._data_type_to_keys,
|
989
|
+
key_to_data_type=self._key_to_data_type,
|
990
|
+
fetch_list_func=self._fetch_list,
|
991
|
+
columns=self.columns,
|
992
|
+
)
|
993
|
+
return selector.select(*columns)
|
994
|
+
|
995
|
+
def sort_by(self, *columns: str, reverse: bool = False) -> Results:
|
996
|
+
"""Sort the results by one or more columns."""
|
997
|
+
import warnings
|
998
|
+
|
999
|
+
warnings.warn(
|
1000
|
+
"sort_by is deprecated. Use order_by instead.", DeprecationWarning
|
1001
|
+
)
|
1002
|
+
return self.order_by(*columns, reverse=reverse)
|
1003
|
+
|
1004
|
+
def _parse_column(self, column: str) -> tuple[str, str]:
|
1005
|
+
"""Parse a column name into a data type and key."""
|
1006
|
+
if "." in column:
|
1007
|
+
return column.split(".")
|
1008
|
+
return self._key_to_data_type[column], column
|
1009
|
+
|
1010
|
+
def order_by(self, *columns: str, reverse: bool = False) -> Results:
|
1011
|
+
"""Sort the results by one or more columns.
|
1012
|
+
|
1013
|
+
:param columns: One or more column names as strings.
|
1014
|
+
:param reverse: A boolean that determines whether to sort in reverse order.
|
1015
|
+
|
1016
|
+
Each column name can be a single key, e.g. "how_feeling", or a dot-separated string, e.g. "answer.how_feeling".
|
1017
|
+
|
1018
|
+
Example:
|
1019
|
+
|
1020
|
+
>>> r = Results.example()
|
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
|
+
|
1027
|
+
"""
|
1028
|
+
|
1029
|
+
def to_numeric_if_possible(v):
|
1030
|
+
try:
|
1031
|
+
return float(v)
|
1032
|
+
except:
|
1033
|
+
return v
|
1034
|
+
|
1035
|
+
def sort_key(item):
|
1036
|
+
key_components = []
|
1037
|
+
for col in columns:
|
1038
|
+
data_type, key = self._parse_column(col)
|
1039
|
+
value = item.get_value(data_type, key)
|
1040
|
+
key_components.append(to_numeric_if_possible(value))
|
1041
|
+
return tuple(key_components)
|
1042
|
+
|
1043
|
+
new_data = sorted(self.data, key=sort_key, reverse=reverse)
|
1044
|
+
return Results(survey=self.survey, data=new_data, created_columns=None)
|
1045
|
+
|
1046
|
+
def filter(self, expression: str) -> Results:
|
1047
|
+
"""
|
1048
|
+
Filter based on the given expression and returns the filtered `Results`.
|
1049
|
+
|
1050
|
+
:param expression: A string expression that evaluates to a boolean. The expression is applied to each element in `Results` to determine whether it should be included in the filtered results.
|
1051
|
+
|
1052
|
+
The `expression` parameter is a string that must resolve to a boolean value when evaluated against each element in `Results`.
|
1053
|
+
This expression is used to determine which elements to include in the returned `Results`.
|
1054
|
+
|
1055
|
+
Example usage: Create an example `Results` instance and apply filters to it:
|
1056
|
+
|
1057
|
+
>>> r = Results.example()
|
1058
|
+
>>> r.filter("how_feeling == 'Great'").select('how_feeling')
|
1059
|
+
Dataset([{'answer.how_feeling': ['Great']}])
|
1060
|
+
|
1061
|
+
Example usage: Using an OR operator in the filter expression.
|
1062
|
+
|
1063
|
+
>>> r = Results.example().filter("how_feeling = 'Great'").select('how_feeling')
|
1064
|
+
Traceback (most recent call last):
|
1065
|
+
...
|
1066
|
+
edsl.exceptions.results.ResultsFilterError: You must use '==' instead of '=' in the filter expression.
|
1067
|
+
...
|
1068
|
+
|
1069
|
+
>>> r.filter("how_feeling == 'Great' or how_feeling == 'Terrible'").select('how_feeling')
|
1070
|
+
Dataset([{'answer.how_feeling': ['Great', 'Terrible']}])
|
1071
|
+
"""
|
1072
|
+
|
1073
|
+
def has_single_equals(string):
|
1074
|
+
if "!=" in string:
|
1075
|
+
return False
|
1076
|
+
if "=" in string and not (
|
1077
|
+
"==" in string or "<=" in string or ">=" in string
|
1078
|
+
):
|
1079
|
+
return True
|
1080
|
+
|
1081
|
+
if has_single_equals(expression):
|
1082
|
+
raise ResultsFilterError(
|
1083
|
+
"You must use '==' instead of '=' in the filter expression."
|
1084
|
+
)
|
1085
|
+
|
1086
|
+
try:
|
1087
|
+
# iterates through all the results and evaluates the expression
|
1088
|
+
new_data = []
|
1089
|
+
for result in self.data:
|
1090
|
+
evaluator = self._create_evaluator(result)
|
1091
|
+
result.check_expression(expression) # check expression
|
1092
|
+
if evaluator.eval(expression):
|
1093
|
+
new_data.append(result)
|
1094
|
+
|
1095
|
+
except ValueError as e:
|
1096
|
+
raise ResultsFilterError(
|
1097
|
+
f"Error in filter. Exception:{e}",
|
1098
|
+
f"The expression you provided was: {expression}",
|
1099
|
+
"See https://docs.expectedparrot.com/en/latest/results.html#filtering-results for more details.",
|
1100
|
+
)
|
1101
|
+
except Exception as e:
|
1102
|
+
raise ResultsFilterError(
|
1103
|
+
f"""Error in filter. Exception:{e}.""",
|
1104
|
+
f"""The expression you provided was: {expression}.""",
|
1105
|
+
"""Please make sure that the expression is a valid Python expression that evaluates to a boolean.""",
|
1106
|
+
"""For example, 'how_feeling == "Great"' is a valid expression, as is 'how_feeling in ["Great", "Terrible"]'., """,
|
1107
|
+
"""However, 'how_feeling = "Great"' is not a valid expression.""",
|
1108
|
+
"""See https://docs.expectedparrot.com/en/latest/results.html#filtering-results for more details.""",
|
1109
|
+
)
|
1110
|
+
|
1111
|
+
if len(new_data) == 0:
|
1112
|
+
import warnings
|
1113
|
+
|
1114
|
+
warnings.warn("No results remain after applying the filter.")
|
1115
|
+
|
1116
|
+
return Results(survey=self.survey, data=new_data, created_columns=None)
|
1117
|
+
|
1118
|
+
@classmethod
|
1119
|
+
def example(cls, randomize: bool = False) -> Results:
|
1120
|
+
"""Return an example `Results` object.
|
1121
|
+
|
1122
|
+
Example usage:
|
1123
|
+
|
1124
|
+
>>> r = Results.example()
|
1125
|
+
|
1126
|
+
:param debug: if False, uses actual API calls
|
1127
|
+
"""
|
1128
|
+
from edsl.jobs.Jobs import Jobs
|
1129
|
+
from edsl.data.Cache import Cache
|
1130
|
+
|
1131
|
+
c = Cache()
|
1132
|
+
job = Jobs.example(randomize=randomize)
|
1133
|
+
results = job.run(
|
1134
|
+
cache=c,
|
1135
|
+
stop_on_exception=True,
|
1136
|
+
skip_retry=True,
|
1137
|
+
raise_validation_errors=True,
|
1138
|
+
disable_remote_cache=True,
|
1139
|
+
disable_remote_inference=True,
|
1140
|
+
)
|
1141
|
+
return results
|
1142
|
+
|
1143
|
+
def rich_print(self):
|
1144
|
+
"""Display an object as a table."""
|
1145
|
+
pass
|
1146
|
+
|
1147
|
+
def __str__(self):
|
1148
|
+
data = self.to_dict()["data"]
|
1149
|
+
return json.dumps(data, indent=4)
|
1150
|
+
|
1151
|
+
def show_exceptions(self, traceback=False):
|
1152
|
+
"""Print the exceptions."""
|
1153
|
+
if hasattr(self, "task_history"):
|
1154
|
+
self.task_history.show_exceptions(traceback)
|
1155
|
+
else:
|
1156
|
+
print("No exceptions to show.")
|
1157
|
+
|
1158
|
+
def score(self, f: Callable) -> list:
|
1159
|
+
"""Score the results using in a function.
|
1160
|
+
|
1161
|
+
:param f: A function that takes values from a Resul object and returns a score.
|
1162
|
+
|
1163
|
+
>>> r = Results.example()
|
1164
|
+
>>> def f(status): return 1 if status == 'Joyful' else 0
|
1165
|
+
>>> r.score(f)
|
1166
|
+
[1, 1, 0, 0]
|
1167
|
+
"""
|
1168
|
+
return [r.score(f) for r in self.data]
|
1169
|
+
|
1170
|
+
|
1171
|
+
def main(): # pragma: no cover
|
1172
|
+
"""Call the OpenAI API credits."""
|
1173
|
+
from edsl.results.Results import Results
|
1174
|
+
|
1175
|
+
results = Results.example(debug=True)
|
1176
|
+
print(results.filter("how_feeling == 'Great'").select("how_feeling"))
|
1177
|
+
print(results.mutate("how_feeling_x = how_feeling + 'x'").select("how_feeling_x"))
|
1178
|
+
|
1179
|
+
|
1180
|
+
if __name__ == "__main__":
|
1181
|
+
import doctest
|
1182
|
+
|
1183
|
+
doctest.testmod(optionflags=doctest.ELLIPSIS)
|