edsl 0.1.47__py3-none-any.whl → 0.1.48__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/__init__.py +44 -39
- edsl/__version__.py +1 -1
- edsl/agents/__init__.py +4 -2
- edsl/agents/{Agent.py → agent.py} +442 -152
- edsl/agents/{AgentList.py → agent_list.py} +220 -162
- edsl/agents/descriptors.py +46 -7
- edsl/{exceptions/agents.py → agents/exceptions.py} +3 -12
- edsl/base/__init__.py +75 -0
- edsl/base/base_class.py +1303 -0
- edsl/base/data_transfer_models.py +114 -0
- edsl/base/enums.py +215 -0
- edsl/base.py +8 -0
- edsl/buckets/__init__.py +25 -0
- edsl/buckets/bucket_collection.py +324 -0
- edsl/buckets/model_buckets.py +206 -0
- edsl/buckets/token_bucket.py +502 -0
- edsl/{jobs/buckets/TokenBucketAPI.py → buckets/token_bucket_api.py} +1 -1
- edsl/buckets/token_bucket_client.py +509 -0
- edsl/caching/__init__.py +20 -0
- edsl/caching/cache.py +814 -0
- edsl/caching/cache_entry.py +427 -0
- edsl/{data/CacheHandler.py → caching/cache_handler.py} +14 -15
- edsl/caching/exceptions.py +24 -0
- edsl/caching/orm.py +30 -0
- edsl/{data/RemoteCacheSync.py → caching/remote_cache_sync.py} +3 -3
- edsl/caching/sql_dict.py +441 -0
- edsl/config/__init__.py +8 -0
- edsl/config/config_class.py +177 -0
- edsl/config.py +4 -176
- edsl/conversation/Conversation.py +7 -7
- edsl/conversation/car_buying.py +4 -4
- edsl/conversation/chips.py +6 -6
- edsl/coop/__init__.py +25 -2
- edsl/coop/coop.py +303 -67
- edsl/coop/{ExpectedParrotKeyHandler.py → ep_key_handling.py} +86 -10
- edsl/coop/exceptions.py +62 -0
- edsl/coop/price_fetcher.py +126 -0
- edsl/coop/utils.py +89 -24
- edsl/data_transfer_models.py +5 -72
- edsl/dataset/__init__.py +10 -0
- edsl/{results/Dataset.py → dataset/dataset.py} +116 -36
- edsl/{results/DatasetExportMixin.py → dataset/dataset_operations_mixin.py} +606 -122
- edsl/{results/DatasetTree.py → dataset/dataset_tree.py} +156 -75
- edsl/{results/TableDisplay.py → dataset/display/table_display.py} +18 -7
- edsl/{results → dataset/display}/table_renderers.py +58 -2
- edsl/{results → dataset}/file_exports.py +4 -5
- edsl/{results → dataset}/smart_objects.py +2 -2
- edsl/enums.py +5 -205
- edsl/inference_services/__init__.py +5 -0
- edsl/inference_services/{AvailableModelCacheHandler.py → available_model_cache_handler.py} +2 -3
- edsl/inference_services/{AvailableModelFetcher.py → available_model_fetcher.py} +8 -14
- edsl/inference_services/data_structures.py +3 -2
- edsl/{exceptions/inference_services.py → inference_services/exceptions.py} +1 -1
- edsl/inference_services/{InferenceServiceABC.py → inference_service_abc.py} +1 -1
- edsl/inference_services/{InferenceServicesCollection.py → inference_services_collection.py} +8 -7
- edsl/inference_services/registry.py +4 -41
- edsl/inference_services/{ServiceAvailability.py → service_availability.py} +5 -25
- edsl/inference_services/services/__init__.py +31 -0
- edsl/inference_services/{AnthropicService.py → services/anthropic_service.py} +3 -3
- edsl/inference_services/{AwsBedrock.py → services/aws_bedrock.py} +2 -2
- edsl/inference_services/{AzureAI.py → services/azure_ai.py} +2 -2
- edsl/inference_services/{DeepInfraService.py → services/deep_infra_service.py} +1 -3
- edsl/inference_services/{DeepSeekService.py → services/deep_seek_service.py} +2 -4
- edsl/inference_services/{GoogleService.py → services/google_service.py} +5 -4
- edsl/inference_services/{GroqService.py → services/groq_service.py} +1 -1
- edsl/inference_services/{MistralAIService.py → services/mistral_ai_service.py} +3 -3
- edsl/inference_services/{OllamaService.py → services/ollama_service.py} +1 -7
- edsl/inference_services/{OpenAIService.py → services/open_ai_service.py} +5 -6
- edsl/inference_services/{PerplexityService.py → services/perplexity_service.py} +3 -7
- edsl/inference_services/{TestService.py → services/test_service.py} +7 -6
- edsl/inference_services/{TogetherAIService.py → services/together_ai_service.py} +2 -6
- edsl/inference_services/{XAIService.py → services/xai_service.py} +1 -1
- edsl/inference_services/write_available.py +1 -2
- edsl/instructions/__init__.py +6 -0
- edsl/{surveys/instructions/Instruction.py → instructions/instruction.py} +11 -6
- edsl/{surveys/instructions/InstructionCollection.py → instructions/instruction_collection.py} +10 -5
- edsl/{surveys/InstructionHandler.py → instructions/instruction_handler.py} +3 -3
- edsl/{jobs/interviews → interviews}/ReportErrors.py +2 -2
- edsl/interviews/__init__.py +4 -0
- edsl/{jobs/AnswerQuestionFunctionConstructor.py → interviews/answering_function.py} +45 -18
- edsl/{jobs/interviews/InterviewExceptionEntry.py → interviews/exception_tracking.py} +107 -22
- edsl/interviews/interview.py +638 -0
- edsl/{jobs/interviews/InterviewStatusDictionary.py → interviews/interview_status_dictionary.py} +21 -12
- edsl/{jobs/interviews/InterviewStatusLog.py → interviews/interview_status_log.py} +16 -7
- edsl/{jobs/InterviewTaskManager.py → interviews/interview_task_manager.py} +12 -7
- edsl/{jobs/RequestTokenEstimator.py → interviews/request_token_estimator.py} +8 -3
- edsl/{jobs/interviews/InterviewStatistic.py → interviews/statistics.py} +36 -10
- edsl/invigilators/__init__.py +38 -0
- edsl/invigilators/invigilator_base.py +477 -0
- edsl/{agents/Invigilator.py → invigilators/invigilators.py} +263 -10
- edsl/invigilators/prompt_constructor.py +476 -0
- edsl/{agents → invigilators}/prompt_helpers.py +2 -1
- edsl/{agents/QuestionInstructionPromptBuilder.py → invigilators/question_instructions_prompt_builder.py} +18 -13
- edsl/{agents → invigilators}/question_option_processor.py +96 -21
- edsl/{agents/QuestionTemplateReplacementsBuilder.py → invigilators/question_template_replacements_builder.py} +64 -12
- edsl/jobs/__init__.py +7 -1
- edsl/jobs/async_interview_runner.py +99 -35
- edsl/jobs/check_survey_scenario_compatibility.py +7 -5
- edsl/jobs/data_structures.py +153 -22
- edsl/{exceptions/jobs.py → jobs/exceptions.py} +2 -1
- edsl/jobs/{FetchInvigilator.py → fetch_invigilator.py} +4 -4
- edsl/jobs/{loggers/HTMLTableJobLogger.py → html_table_job_logger.py} +6 -2
- edsl/jobs/{Jobs.py → jobs.py} +313 -167
- edsl/jobs/{JobsChecks.py → jobs_checks.py} +15 -7
- edsl/jobs/{JobsComponentConstructor.py → jobs_component_constructor.py} +19 -17
- edsl/jobs/{InterviewsConstructor.py → jobs_interview_constructor.py} +10 -5
- edsl/jobs/jobs_pricing_estimation.py +347 -0
- edsl/jobs/{JobsRemoteInferenceLogger.py → jobs_remote_inference_logger.py} +4 -3
- edsl/jobs/jobs_runner_asyncio.py +282 -0
- edsl/jobs/{JobsRemoteInferenceHandler.py → remote_inference.py} +19 -22
- edsl/jobs/results_exceptions_handler.py +2 -2
- edsl/key_management/__init__.py +28 -0
- edsl/key_management/key_lookup.py +161 -0
- edsl/{language_models/key_management/KeyLookupBuilder.py → key_management/key_lookup_builder.py} +118 -47
- edsl/key_management/key_lookup_collection.py +82 -0
- edsl/key_management/models.py +218 -0
- edsl/language_models/__init__.py +7 -2
- edsl/language_models/{ComputeCost.py → compute_cost.py} +18 -3
- edsl/{exceptions/language_models.py → language_models/exceptions.py} +2 -1
- edsl/language_models/language_model.py +1080 -0
- edsl/language_models/model.py +10 -25
- edsl/language_models/{ModelList.py → model_list.py} +9 -14
- edsl/language_models/{RawResponseHandler.py → raw_response_handler.py} +1 -1
- edsl/language_models/{RegisterLanguageModelsMeta.py → registry.py} +1 -1
- edsl/language_models/repair.py +4 -4
- edsl/language_models/utilities.py +4 -4
- edsl/notebooks/__init__.py +3 -1
- edsl/notebooks/{Notebook.py → notebook.py} +7 -8
- edsl/prompts/__init__.py +1 -1
- edsl/{exceptions/prompts.py → prompts/exceptions.py} +3 -1
- edsl/prompts/{Prompt.py → prompt.py} +101 -95
- edsl/questions/HTMLQuestion.py +1 -1
- edsl/questions/__init__.py +154 -25
- edsl/questions/answer_validator_mixin.py +1 -1
- edsl/questions/compose_questions.py +4 -3
- edsl/questions/derived/question_likert_five.py +166 -0
- edsl/questions/derived/{QuestionLinearScale.py → question_linear_scale.py} +4 -4
- edsl/questions/derived/{QuestionTopK.py → question_top_k.py} +4 -4
- edsl/questions/derived/{QuestionYesNo.py → question_yes_no.py} +4 -5
- edsl/questions/descriptors.py +24 -30
- edsl/questions/loop_processor.py +65 -19
- edsl/questions/question_base.py +881 -0
- edsl/questions/question_base_gen_mixin.py +15 -16
- edsl/questions/{QuestionBasePromptsMixin.py → question_base_prompts_mixin.py} +2 -2
- edsl/questions/{QuestionBudget.py → question_budget.py} +3 -4
- edsl/questions/{QuestionCheckBox.py → question_check_box.py} +16 -16
- edsl/questions/{QuestionDict.py → question_dict.py} +39 -5
- edsl/questions/{QuestionExtract.py → question_extract.py} +9 -9
- edsl/questions/question_free_text.py +282 -0
- edsl/questions/{QuestionFunctional.py → question_functional.py} +6 -5
- edsl/questions/{QuestionList.py → question_list.py} +6 -7
- edsl/questions/{QuestionMatrix.py → question_matrix.py} +6 -5
- edsl/questions/{QuestionMultipleChoice.py → question_multiple_choice.py} +126 -21
- edsl/questions/{QuestionNumerical.py → question_numerical.py} +5 -5
- edsl/questions/{QuestionRank.py → question_rank.py} +6 -6
- edsl/questions/question_registry.py +4 -9
- edsl/questions/register_questions_meta.py +8 -4
- edsl/questions/response_validator_abc.py +17 -16
- edsl/results/__init__.py +4 -1
- edsl/{exceptions/results.py → results/exceptions.py} +1 -1
- edsl/results/report.py +197 -0
- edsl/results/{Result.py → result.py} +131 -45
- edsl/results/{Results.py → results.py} +365 -220
- edsl/results/results_selector.py +344 -25
- edsl/scenarios/__init__.py +30 -3
- edsl/scenarios/{ConstructDownloadLink.py → construct_download_link.py} +7 -0
- edsl/scenarios/directory_scanner.py +156 -13
- edsl/scenarios/document_chunker.py +186 -0
- edsl/scenarios/exceptions.py +101 -0
- edsl/scenarios/file_methods.py +2 -3
- edsl/scenarios/{FileStore.py → file_store.py} +275 -189
- edsl/scenarios/handlers/__init__.py +14 -14
- edsl/scenarios/handlers/{csv.py → csv_file_store.py} +1 -2
- edsl/scenarios/handlers/{docx.py → docx_file_store.py} +8 -7
- edsl/scenarios/handlers/{html.py → html_file_store.py} +1 -2
- edsl/scenarios/handlers/{jpeg.py → jpeg_file_store.py} +1 -1
- edsl/scenarios/handlers/{json.py → json_file_store.py} +1 -1
- edsl/scenarios/handlers/latex_file_store.py +5 -0
- edsl/scenarios/handlers/{md.py → md_file_store.py} +1 -1
- edsl/scenarios/handlers/{pdf.py → pdf_file_store.py} +2 -2
- edsl/scenarios/handlers/{png.py → png_file_store.py} +1 -1
- edsl/scenarios/handlers/{pptx.py → pptx_file_store.py} +8 -7
- edsl/scenarios/handlers/{py.py → py_file_store.py} +1 -3
- edsl/scenarios/handlers/{sql.py → sql_file_store.py} +2 -1
- edsl/scenarios/handlers/{sqlite.py → sqlite_file_store.py} +2 -3
- edsl/scenarios/handlers/{txt.py → txt_file_store.py} +1 -1
- edsl/scenarios/scenario.py +928 -0
- edsl/scenarios/scenario_join.py +18 -5
- edsl/scenarios/{ScenarioList.py → scenario_list.py} +294 -106
- edsl/scenarios/{ScenarioListPdfMixin.py → scenario_list_pdf_tools.py} +16 -15
- edsl/scenarios/scenario_selector.py +5 -1
- edsl/study/ObjectEntry.py +2 -2
- edsl/study/SnapShot.py +5 -5
- edsl/study/Study.py +18 -19
- edsl/study/__init__.py +6 -4
- edsl/surveys/__init__.py +7 -4
- edsl/surveys/dag/__init__.py +2 -0
- edsl/surveys/{ConstructDAG.py → dag/construct_dag.py} +3 -3
- edsl/surveys/{DAG.py → dag/dag.py} +13 -10
- edsl/surveys/descriptors.py +1 -1
- edsl/surveys/{EditSurvey.py → edit_survey.py} +9 -9
- edsl/{exceptions/surveys.py → surveys/exceptions.py} +1 -2
- edsl/surveys/memory/__init__.py +3 -0
- edsl/surveys/{MemoryPlan.py → memory/memory_plan.py} +10 -9
- edsl/surveys/rules/__init__.py +3 -0
- edsl/surveys/{Rule.py → rules/rule.py} +103 -43
- edsl/surveys/{RuleCollection.py → rules/rule_collection.py} +21 -30
- edsl/surveys/{RuleManager.py → rules/rule_manager.py} +19 -13
- edsl/surveys/survey.py +1743 -0
- edsl/surveys/{SurveyExportMixin.py → survey_export.py} +22 -27
- edsl/surveys/{SurveyFlowVisualization.py → survey_flow_visualization.py} +11 -2
- edsl/surveys/{Simulator.py → survey_simulator.py} +10 -3
- edsl/tasks/__init__.py +32 -0
- edsl/{jobs/tasks/QuestionTaskCreator.py → tasks/question_task_creator.py} +115 -57
- edsl/tasks/task_creators.py +135 -0
- edsl/{jobs/tasks/TaskHistory.py → tasks/task_history.py} +86 -47
- edsl/{jobs/tasks → tasks}/task_status_enum.py +91 -7
- edsl/tasks/task_status_log.py +85 -0
- edsl/tokens/__init__.py +2 -0
- edsl/tokens/interview_token_usage.py +53 -0
- edsl/utilities/PrettyList.py +1 -1
- edsl/utilities/SystemInfo.py +25 -22
- edsl/utilities/__init__.py +29 -21
- edsl/utilities/gcp_bucket/__init__.py +2 -0
- edsl/utilities/gcp_bucket/cloud_storage.py +99 -96
- edsl/utilities/interface.py +44 -536
- edsl/{results/MarkdownToPDF.py → utilities/markdown_to_pdf.py} +13 -5
- edsl/utilities/repair_functions.py +1 -1
- {edsl-0.1.47.dist-info → edsl-0.1.48.dist-info}/METADATA +1 -1
- edsl-0.1.48.dist-info/RECORD +347 -0
- edsl/Base.py +0 -493
- edsl/BaseDiff.py +0 -260
- edsl/agents/InvigilatorBase.py +0 -260
- edsl/agents/PromptConstructor.py +0 -318
- edsl/coop/PriceFetcher.py +0 -54
- edsl/data/Cache.py +0 -582
- edsl/data/CacheEntry.py +0 -238
- edsl/data/SQLiteDict.py +0 -292
- edsl/data/__init__.py +0 -5
- edsl/data/orm.py +0 -10
- edsl/exceptions/cache.py +0 -5
- edsl/exceptions/coop.py +0 -14
- edsl/exceptions/data.py +0 -14
- edsl/exceptions/scenarios.py +0 -29
- edsl/jobs/Answers.py +0 -43
- edsl/jobs/JobsPrompts.py +0 -354
- edsl/jobs/buckets/BucketCollection.py +0 -134
- edsl/jobs/buckets/ModelBuckets.py +0 -65
- edsl/jobs/buckets/TokenBucket.py +0 -283
- edsl/jobs/buckets/TokenBucketClient.py +0 -191
- edsl/jobs/interviews/Interview.py +0 -395
- edsl/jobs/interviews/InterviewExceptionCollection.py +0 -99
- edsl/jobs/interviews/InterviewStatisticsCollection.py +0 -25
- edsl/jobs/runners/JobsRunnerAsyncio.py +0 -163
- edsl/jobs/runners/JobsRunnerStatusData.py +0 -0
- edsl/jobs/tasks/TaskCreators.py +0 -64
- edsl/jobs/tasks/TaskStatusLog.py +0 -23
- edsl/jobs/tokens/InterviewTokenUsage.py +0 -27
- edsl/language_models/LanguageModel.py +0 -635
- edsl/language_models/ServiceDataSources.py +0 -0
- edsl/language_models/key_management/KeyLookup.py +0 -63
- edsl/language_models/key_management/KeyLookupCollection.py +0 -38
- edsl/language_models/key_management/models.py +0 -137
- edsl/questions/QuestionBase.py +0 -544
- edsl/questions/QuestionFreeText.py +0 -130
- edsl/questions/derived/QuestionLikertFive.py +0 -76
- edsl/results/ResultsExportMixin.py +0 -45
- edsl/results/TextEditor.py +0 -50
- edsl/results/results_fetch_mixin.py +0 -33
- edsl/results/results_tools_mixin.py +0 -98
- edsl/scenarios/DocumentChunker.py +0 -104
- edsl/scenarios/Scenario.py +0 -548
- edsl/scenarios/ScenarioHtmlMixin.py +0 -65
- edsl/scenarios/ScenarioListExportMixin.py +0 -45
- edsl/scenarios/handlers/latex.py +0 -5
- edsl/shared.py +0 -1
- edsl/surveys/Survey.py +0 -1301
- edsl/surveys/SurveyQualtricsImport.py +0 -284
- edsl/surveys/SurveyToApp.py +0 -141
- edsl/surveys/instructions/__init__.py +0 -0
- edsl/tools/__init__.py +0 -1
- edsl/tools/clusters.py +0 -192
- edsl/tools/embeddings.py +0 -27
- edsl/tools/embeddings_plotting.py +0 -118
- edsl/tools/plotting.py +0 -112
- edsl/tools/summarize.py +0 -18
- edsl/utilities/data/Registry.py +0 -6
- edsl/utilities/data/__init__.py +0 -1
- edsl/utilities/data/scooter_results.json +0 -1
- edsl-0.1.47.dist-info/RECORD +0 -354
- /edsl/coop/{CoopFunctionsMixin.py → coop_functions.py} +0 -0
- /edsl/{results → dataset/display}/CSSParameterizer.py +0 -0
- /edsl/{language_models/key_management → dataset/display}/__init__.py +0 -0
- /edsl/{results → dataset/display}/table_data_class.py +0 -0
- /edsl/{results → dataset/display}/table_display.css +0 -0
- /edsl/{results/ResultsGGMixin.py → dataset/r/ggplot.py} +0 -0
- /edsl/{results → dataset}/tree_explore.py +0 -0
- /edsl/{surveys/instructions/ChangeInstruction.py → instructions/change_instruction.py} +0 -0
- /edsl/{jobs/interviews → interviews}/interview_status_enum.py +0 -0
- /edsl/jobs/{runners/JobsRunnerStatus.py → jobs_runner_status.py} +0 -0
- /edsl/language_models/{PriceManager.py → price_manager.py} +0 -0
- /edsl/language_models/{fake_openai_call.py → unused/fake_openai_call.py} +0 -0
- /edsl/language_models/{fake_openai_service.py → unused/fake_openai_service.py} +0 -0
- /edsl/notebooks/{NotebookToLaTeX.py → notebook_to_latex.py} +0 -0
- /edsl/{exceptions/questions.py → questions/exceptions.py} +0 -0
- /edsl/questions/{SimpleAskMixin.py → simple_ask_mixin.py} +0 -0
- /edsl/surveys/{Memory.py → memory/memory.py} +0 -0
- /edsl/surveys/{MemoryManagement.py → memory/memory_management.py} +0 -0
- /edsl/surveys/{SurveyCSS.py → survey_css.py} +0 -0
- /edsl/{jobs/tokens/TokenUsage.py → tokens/token_usage.py} +0 -0
- /edsl/{results/MarkdownToDocx.py → utilities/markdown_to_docx.py} +0 -0
- /edsl/{TemplateLoader.py → utilities/template_loader.py} +0 -0
- {edsl-0.1.47.dist-info → edsl-0.1.48.dist-info}/LICENSE +0 -0
- {edsl-0.1.47.dist-info → edsl-0.1.48.dist-info}/WHEEL +0 -0
@@ -1,18 +1,67 @@
|
|
1
1
|
"""
|
2
|
-
The Results
|
3
|
-
|
2
|
+
The Results module provides tools for working with collections of Result objects.
|
3
|
+
|
4
|
+
The Results class is the primary container for analyzing and manipulating data obtained
|
5
|
+
from running surveys with language models. It implements a powerful data analysis interface
|
6
|
+
with methods for filtering, selecting, mutating, and visualizing your results, similar to
|
7
|
+
data manipulation libraries like dplyr or pandas.
|
8
|
+
|
9
|
+
Key components:
|
10
|
+
|
11
|
+
1. Results - A collection of Result objects with methods for data analysis and manipulation
|
12
|
+
2. Report - A flexible reporting system for generating formatted output from Results
|
13
|
+
3. Selectors - Tools for efficiently extracting specific data from Results
|
14
|
+
|
15
|
+
The Results class is not typically instantiated directly; instead, it's returned by the
|
16
|
+
run() method of a Job object. Once you have a Results object, you can use its methods
|
17
|
+
to analyze and extract insights from your survey data.
|
18
|
+
|
19
|
+
Example workflow:
|
20
|
+
```python
|
21
|
+
# Run a job and get results
|
22
|
+
results = job.run()
|
23
|
+
|
24
|
+
# Filter to a subset of results
|
25
|
+
filtered = results.filter("how_feeling == 'Great'")
|
26
|
+
|
27
|
+
# Select specific columns for analysis
|
28
|
+
data = filtered.select("how_feeling", "agent.status")
|
29
|
+
|
30
|
+
# Create a new derived column
|
31
|
+
with_sentiment = results.mutate("sentiment = 1 if how_feeling == 'Great' else 0")
|
32
|
+
|
33
|
+
# Generate a report
|
34
|
+
report = Report(results, fields=["answer.how_feeling", "answer.sentiment"])
|
35
|
+
print(report.generate())
|
36
|
+
```
|
4
37
|
"""
|
5
38
|
|
6
39
|
from __future__ import annotations
|
7
40
|
import json
|
8
41
|
import random
|
42
|
+
import warnings
|
9
43
|
from collections import UserList, defaultdict
|
10
44
|
from typing import Optional, Callable, Any, Type, Union, List, TYPE_CHECKING
|
11
|
-
|
12
45
|
from bisect import bisect_left
|
13
46
|
|
14
|
-
from
|
15
|
-
|
47
|
+
from ..base import Base
|
48
|
+
|
49
|
+
if TYPE_CHECKING:
|
50
|
+
from ..surveys import Survey
|
51
|
+
from ..data import Cache
|
52
|
+
from ..agents import AgentList
|
53
|
+
from ..language_models import Model
|
54
|
+
from ..scenarios import ScenarioList
|
55
|
+
from ..results import Result
|
56
|
+
from ..tasks import TaskHistory
|
57
|
+
from ..language_models import ModelList
|
58
|
+
from simpleeval import EvalWithCompoundTypes
|
59
|
+
from ..dataset import Dataset
|
60
|
+
|
61
|
+
from ..utilities import remove_edsl_version, dict_hash
|
62
|
+
from ..dataset import ResultsOperationsMixin
|
63
|
+
|
64
|
+
from .exceptions import (
|
16
65
|
ResultsError,
|
17
66
|
ResultsBadMutationstringError,
|
18
67
|
ResultsColumnNotFoundError,
|
@@ -22,22 +71,6 @@ from edsl.exceptions.results import (
|
|
22
71
|
ResultsDeserializationError,
|
23
72
|
)
|
24
73
|
|
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 GGPlotMethod
|
38
|
-
from edsl.results.results_fetch_mixin import ResultsFetchMixin
|
39
|
-
from edsl.utilities.remove_edsl_version import remove_edsl_version
|
40
|
-
|
41
74
|
def ensure_fetched(method):
|
42
75
|
"""A decorator that checks if remote data is loaded, and if not, attempts to fetch it."""
|
43
76
|
def wrapper(self, *args, **kwargs):
|
@@ -80,7 +113,7 @@ def ensure_ready(method):
|
|
80
113
|
|
81
114
|
class NotReadyObject:
|
82
115
|
"""A placeholder object that prints a message when any attribute is accessed."""
|
83
|
-
def __init__(self, name: str, job_info: RemoteJobInfo):
|
116
|
+
def __init__(self, name: str, job_info: 'RemoteJobInfo'):
|
84
117
|
self.name = name
|
85
118
|
self.job_info = job_info
|
86
119
|
#print(f"Not ready to call {name}")
|
@@ -97,39 +130,40 @@ class NotReadyObject:
|
|
97
130
|
def __call__(self, *args, **kwargs):
|
98
131
|
return self
|
99
132
|
|
100
|
-
class Mixins(
|
101
|
-
ResultsExportMixin,
|
102
|
-
ResultsFetchMixin,
|
103
|
-
# ResultsGGMixin,
|
104
|
-
):
|
105
|
-
def long(self):
|
106
|
-
return self.table().long()
|
107
|
-
|
108
|
-
def print_long(self, max_rows: int = None) -> None:
|
109
|
-
"""Print the results in long format.
|
110
|
-
|
111
|
-
>>> from edsl.results import Results
|
112
|
-
>>> r = Results.example()
|
113
|
-
>>> r.select('how_feeling').print_long(max_rows = 2)
|
114
|
-
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━┓
|
115
|
-
┃ Result index ┃ Key ┃ Value ┃
|
116
|
-
┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━┩
|
117
|
-
│ 0 │ how_feeling │ OK │
|
118
|
-
│ 1 │ how_feeling │ Great │
|
119
|
-
└──────────────┴─────────────┴───────┘
|
120
|
-
"""
|
121
|
-
from edsl.utilities.interface import print_results_long
|
122
|
-
|
123
|
-
print_results_long(self, max_rows=max_rows)
|
124
|
-
|
125
133
|
|
126
|
-
class Results(UserList,
|
134
|
+
class Results(UserList, ResultsOperationsMixin, Base):
|
127
135
|
"""
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
It
|
132
|
-
|
136
|
+
A collection of Result objects with powerful data analysis capabilities.
|
137
|
+
|
138
|
+
The Results class is the primary container for working with data from EDSL surveys.
|
139
|
+
It provides a rich set of methods for data analysis, transformation, and visualization
|
140
|
+
inspired by data manipulation libraries like dplyr and pandas. The Results class
|
141
|
+
implements a functional, fluent interface for data manipulation where each method
|
142
|
+
returns a new Results object, allowing method chaining.
|
143
|
+
|
144
|
+
Key features:
|
145
|
+
|
146
|
+
- List-like interface for accessing individual Result objects
|
147
|
+
- Selection of specific data columns with `select()`
|
148
|
+
- Filtering results with boolean expressions using `filter()`
|
149
|
+
- Creating new derived columns with `mutate()`
|
150
|
+
- Recoding values with `recode()` and `answer_truncate()`
|
151
|
+
- Sorting results with `order_by()`
|
152
|
+
- Converting to other formats (dataset, table, pandas DataFrame)
|
153
|
+
- Serialization for storage and retrieval
|
154
|
+
- Support for remote execution and result retrieval
|
155
|
+
|
156
|
+
Results objects have a hierarchical structure with the following components:
|
157
|
+
|
158
|
+
1. Each Results object contains multiple Result objects
|
159
|
+
2. Each Result object contains data organized by type (agent, scenario, model, answer, etc.)
|
160
|
+
3. Each data type contains multiple attributes (e.g., "how_feeling" in the answer type)
|
161
|
+
|
162
|
+
You can access data in a Results object using dot notation (`answer.how_feeling`) or
|
163
|
+
using just the attribute name if it's not ambiguous (`how_feeling`).
|
164
|
+
|
165
|
+
The Results class also tracks "created columns" - new derived values that aren't
|
166
|
+
part of the original data but were created through transformations.
|
133
167
|
"""
|
134
168
|
|
135
169
|
__documentation__ = "https://docs.expectedparrot.com/en/latest/results.html"
|
@@ -151,19 +185,6 @@ class Results(UserList, Mixins, Base):
|
|
151
185
|
"cache_keys",
|
152
186
|
]
|
153
187
|
|
154
|
-
def ggplot2(
|
155
|
-
self,
|
156
|
-
ggplot_code: str,
|
157
|
-
shape="wide",
|
158
|
-
sql: str = None,
|
159
|
-
remove_prefix: bool = True,
|
160
|
-
debug: bool = False,
|
161
|
-
height=4,
|
162
|
-
width=6,
|
163
|
-
factor_orders: Optional[dict] = None,
|
164
|
-
):
|
165
|
-
return GGPlotMethod(self).ggplot2(ggplot_code, shape, sql, remove_prefix, debug, height, width, factor_orders)
|
166
|
-
|
167
188
|
@classmethod
|
168
189
|
def from_job_info(cls, job_info: dict) -> Results:
|
169
190
|
"""
|
@@ -196,8 +217,8 @@ class Results(UserList, Mixins, Base):
|
|
196
217
|
self.completed = True
|
197
218
|
self._fetching = False
|
198
219
|
super().__init__(data)
|
199
|
-
from
|
200
|
-
from
|
220
|
+
from ..caching import Cache
|
221
|
+
from ..tasks import TaskHistory
|
201
222
|
|
202
223
|
self.survey = survey
|
203
224
|
self.created_columns = created_columns or []
|
@@ -210,6 +231,47 @@ class Results(UserList, Mixins, Base):
|
|
210
231
|
if hasattr(self, "_add_output_functions"):
|
211
232
|
self._add_output_functions()
|
212
233
|
|
234
|
+
def long(self):
|
235
|
+
return self.table().long()
|
236
|
+
|
237
|
+
def print_long(self, max_rows: int = None) -> None:
|
238
|
+
"""Print the results in long format.
|
239
|
+
|
240
|
+
>>> from edsl.results import Results
|
241
|
+
>>> r = Results.example()
|
242
|
+
>>> r.select('how_feeling').print_long(max_rows = 2)
|
243
|
+
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━┓
|
244
|
+
┃ Result index ┃ Key ┃ Value ┃
|
245
|
+
┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━┩
|
246
|
+
│ 0 │ how_feeling │ OK │
|
247
|
+
│ 1 │ how_feeling │ Great │
|
248
|
+
└──────────────┴─────────────┴───────┘
|
249
|
+
"""
|
250
|
+
from edsl.utilities.interface import print_results_long
|
251
|
+
|
252
|
+
print_results_long(self, max_rows=max_rows)
|
253
|
+
|
254
|
+
|
255
|
+
def _fetch_list(self, data_type: str, key: str) -> list:
|
256
|
+
"""
|
257
|
+
Return a list of values from the data for a given data type and key.
|
258
|
+
|
259
|
+
Uses the filtered data, not the original data.
|
260
|
+
|
261
|
+
Example:
|
262
|
+
|
263
|
+
>>> from edsl.results import Results
|
264
|
+
>>> r = Results.example()
|
265
|
+
>>> r._fetch_list('answer', 'how_feeling')
|
266
|
+
['OK', 'Great', 'Terrible', 'OK']
|
267
|
+
"""
|
268
|
+
returned_list = []
|
269
|
+
for row in self.data:
|
270
|
+
returned_list.append(row.sub_dicts[data_type].get(key, None))
|
271
|
+
|
272
|
+
return returned_list
|
273
|
+
|
274
|
+
|
213
275
|
def _summary(self) -> dict:
|
214
276
|
import reprlib
|
215
277
|
|
@@ -280,83 +342,83 @@ class Results(UserList, Mixins, Base):
|
|
280
342
|
|
281
343
|
return total_cost
|
282
344
|
|
283
|
-
def leaves(self):
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
def tree(self, node_list: Optional[List[str]] = None):
|
290
|
-
|
291
|
-
|
292
|
-
def interactive_tree(
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
) -> dict:
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
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
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
345
|
+
# def leaves(self):
|
346
|
+
# leaves = []
|
347
|
+
# for result in self:
|
348
|
+
# leaves.extend(result.leaves())
|
349
|
+
# return leaves
|
350
|
+
|
351
|
+
# def tree(self, node_list: Optional[List[str]] = None):
|
352
|
+
# return self.to_scenario_list().tree(node_list)
|
353
|
+
|
354
|
+
# def interactive_tree(
|
355
|
+
# self,
|
356
|
+
# fold_attributes: Optional[List[str]] = None,
|
357
|
+
# drop: Optional[List[str]] = None,
|
358
|
+
# open_file=True,
|
359
|
+
# ) -> dict:
|
360
|
+
# """Return the results as a tree."""
|
361
|
+
# from edsl.results.tree_explore import FoldableHTMLTableGenerator
|
362
|
+
|
363
|
+
# if drop is None:
|
364
|
+
# drop = []
|
365
|
+
|
366
|
+
# valid_attributes = [
|
367
|
+
# "model",
|
368
|
+
# "scenario",
|
369
|
+
# "agent",
|
370
|
+
# "answer",
|
371
|
+
# "question",
|
372
|
+
# "iteration",
|
373
|
+
# ]
|
374
|
+
# if fold_attributes is None:
|
375
|
+
# fold_attributes = []
|
376
|
+
|
377
|
+
# for attribute in fold_attributes:
|
378
|
+
# if attribute not in valid_attributes:
|
379
|
+
# raise ValueError(
|
380
|
+
# f"Invalid fold attribute: {attribute}; must be in {valid_attributes}"
|
381
|
+
# )
|
382
|
+
# data = self.leaves()
|
383
|
+
# generator = FoldableHTMLTableGenerator(data)
|
384
|
+
# tree = generator.tree(fold_attributes=fold_attributes, drop=drop)
|
385
|
+
# html_content = generator.generate_html(tree, fold_attributes)
|
386
|
+
# import tempfile
|
387
|
+
# from edsl.utilities.utilities import is_notebook
|
388
|
+
|
389
|
+
# from IPython.display import display, HTML
|
390
|
+
|
391
|
+
# if is_notebook():
|
392
|
+
# import html
|
393
|
+
# from IPython.display import display, HTML
|
394
|
+
|
395
|
+
# height = 1000
|
396
|
+
# width = 1000
|
397
|
+
# escaped_output = html.escape(html_content)
|
398
|
+
# # escaped_output = rendered_html
|
399
|
+
# iframe = f""""
|
400
|
+
# <iframe srcdoc="{ escaped_output }" style="width: {width}px; height: {height}px;"></iframe>
|
401
|
+
# """
|
402
|
+
# display(HTML(iframe))
|
403
|
+
# return None
|
404
|
+
|
405
|
+
# with tempfile.NamedTemporaryFile(suffix=".html", delete=False) as f:
|
406
|
+
# f.write(html_content.encode())
|
407
|
+
# print(f"HTML file has been generated: {f.name}")
|
408
|
+
|
409
|
+
# if open_file:
|
410
|
+
# import webbrowser
|
411
|
+
# import time
|
412
|
+
|
413
|
+
# time.sleep(1) # Wait for 1 second
|
414
|
+
# # webbrowser.open(f.name)
|
415
|
+
# import os
|
416
|
+
|
417
|
+
# filename = f.name
|
418
|
+
# webbrowser.open(f"file://{os.path.abspath(filename)}")
|
419
|
+
|
420
|
+
# else:
|
421
|
+
# return html_content
|
360
422
|
|
361
423
|
def code(self):
|
362
424
|
raise NotImplementedError
|
@@ -454,6 +516,9 @@ class Results(UserList, Mixins, Base):
|
|
454
516
|
print_parameters=print_parameters,
|
455
517
|
)
|
456
518
|
)
|
519
|
+
|
520
|
+
def to_dataset(self) -> 'Dataset':
|
521
|
+
return self.select()
|
457
522
|
|
458
523
|
def to_dict(
|
459
524
|
self,
|
@@ -463,7 +528,7 @@ class Results(UserList, Mixins, Base):
|
|
463
528
|
include_task_history: bool = False,
|
464
529
|
include_cache_info: bool = True,
|
465
530
|
) -> dict[str, Any]:
|
466
|
-
from
|
531
|
+
from ..caching import Cache
|
467
532
|
|
468
533
|
if sort:
|
469
534
|
data = sorted([result for result in self.data], key=lambda x: hash(x))
|
@@ -525,8 +590,7 @@ class Results(UserList, Mixins, Base):
|
|
525
590
|
return self.task_history.has_unfixed_exceptions
|
526
591
|
|
527
592
|
def __hash__(self) -> int:
|
528
|
-
|
529
|
-
|
593
|
+
|
530
594
|
return dict_hash(
|
531
595
|
self.to_dict(sort=True, add_edsl_version=False, include_cache_info=False)
|
532
596
|
)
|
@@ -575,11 +639,11 @@ class Results(UserList, Mixins, Base):
|
|
575
639
|
>>> r == r2
|
576
640
|
True
|
577
641
|
"""
|
578
|
-
from
|
579
|
-
from
|
580
|
-
from
|
581
|
-
from
|
582
|
-
from
|
642
|
+
from ..surveys import Survey
|
643
|
+
from ..caching import Cache
|
644
|
+
from ..results import Result
|
645
|
+
from ..tasks import TaskHistory
|
646
|
+
from ..agents import Agent
|
583
647
|
|
584
648
|
survey = Survey.from_dict(data["survey"])
|
585
649
|
results_data = [Result.from_dict(r) for r in data["data"]]
|
@@ -692,7 +756,7 @@ class Results(UserList, Mixins, Base):
|
|
692
756
|
>>> r.agents
|
693
757
|
AgentList([Agent(traits = {'status': 'Joyful'}), Agent(traits = {'status': 'Joyful'}), Agent(traits = {'status': 'Sad'}), Agent(traits = {'status': 'Sad'})])
|
694
758
|
"""
|
695
|
-
from edsl.agents
|
759
|
+
from edsl.agents import AgentList
|
696
760
|
|
697
761
|
return AgentList([r.agent for r in self.data])
|
698
762
|
|
@@ -706,7 +770,7 @@ class Results(UserList, Mixins, Base):
|
|
706
770
|
>>> r.models[0]
|
707
771
|
Model(model_name = ...)
|
708
772
|
"""
|
709
|
-
from
|
773
|
+
from ..language_models import ModelList
|
710
774
|
|
711
775
|
return ModelList([r.model for r in self.data])
|
712
776
|
|
@@ -723,7 +787,7 @@ class Results(UserList, Mixins, Base):
|
|
723
787
|
>>> r.scenarios
|
724
788
|
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})])
|
725
789
|
"""
|
726
|
-
from
|
790
|
+
from ..scenarios import ScenarioList
|
727
791
|
|
728
792
|
return ScenarioList([r.scenario for r in self.data])
|
729
793
|
|
@@ -935,18 +999,49 @@ class Results(UserList, Mixins, Base):
|
|
935
999
|
self, new_var_string: str, functions_dict: Optional[dict] = None
|
936
1000
|
) -> Results:
|
937
1001
|
"""
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
1002
|
+
Create a new column based on a computational expression.
|
1003
|
+
|
1004
|
+
The mutate method allows you to create new derived variables based on existing data.
|
1005
|
+
You provide an assignment expression where the left side is the new column name
|
1006
|
+
and the right side is a Python expression that computes the value. The expression
|
1007
|
+
can reference any existing columns in the Results object.
|
1008
|
+
|
1009
|
+
Parameters:
|
1010
|
+
new_var_string: A string containing an assignment expression in the form
|
1011
|
+
"new_column_name = expression". The expression can reference
|
1012
|
+
any existing column and use standard Python syntax.
|
1013
|
+
functions_dict: Optional dictionary of custom functions that can be used in
|
1014
|
+
the expression. Keys are function names, values are function objects.
|
1015
|
+
|
1016
|
+
Returns:
|
1017
|
+
A new Results object with the additional column.
|
1018
|
+
|
1019
|
+
Notes:
|
1020
|
+
- The expression must contain an equals sign (=) separating the new column name
|
1021
|
+
from the computation expression
|
1022
|
+
- The new column name must be a valid Python variable name
|
1023
|
+
- The expression is evaluated for each Result object individually
|
1024
|
+
- The expression can access any data in the Result object using the column names
|
1025
|
+
- New columns are added to the "answer" data type
|
1026
|
+
- Created columns are tracked in the `created_columns` property
|
1027
|
+
|
1028
|
+
Examples:
|
1029
|
+
>>> r = Results.example()
|
1030
|
+
|
1031
|
+
# Create a simple derived column
|
1032
|
+
>>> r.mutate('how_feeling_x = how_feeling + "x"').select('how_feeling_x')
|
1033
|
+
Dataset([{'answer.how_feeling_x': ['OKx', 'Greatx', 'Terriblex', 'OKx']}])
|
1034
|
+
|
1035
|
+
# Create a binary indicator column
|
1036
|
+
>>> r.mutate('is_great = 1 if how_feeling == "Great" else 0').select('is_great')
|
1037
|
+
Dataset([{'answer.is_great': [0, 1, 0, 0]}])
|
1038
|
+
|
1039
|
+
# Create a column with custom functions
|
1040
|
+
>>> def sentiment(text):
|
1041
|
+
... return len(text) > 5
|
1042
|
+
>>> r.mutate('is_long = sentiment(how_feeling)',
|
1043
|
+
... functions_dict={'sentiment': sentiment}).select('is_long')
|
1044
|
+
Dataset([{'answer.is_long': [False, False, True, False]}])
|
950
1045
|
"""
|
951
1046
|
# extract the variable name and the expression
|
952
1047
|
if "=" not in new_var_string:
|
@@ -1078,23 +1173,55 @@ class Results(UserList, Mixins, Base):
|
|
1078
1173
|
return Results(survey=self.survey, data=new_data, created_columns=None)
|
1079
1174
|
|
1080
1175
|
@ensure_ready
|
1081
|
-
def select(self, *columns: Union[str, list[str]]) ->
|
1176
|
+
def select(self, *columns: Union[str, list[str]]) -> 'Dataset':
|
1082
1177
|
"""
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1178
|
+
Extract specific columns from the Results into a Dataset.
|
1179
|
+
|
1180
|
+
This method allows you to select specific columns from the Results object
|
1181
|
+
and transforms the data into a Dataset for further analysis and visualization.
|
1182
|
+
A Dataset is a more general-purpose data structure optimized for analysis
|
1183
|
+
operations rather than the hierarchical structure of Result objects.
|
1184
|
+
|
1185
|
+
Parameters:
|
1186
|
+
*columns: Column names to select. Each column can be:
|
1187
|
+
- A simple attribute name (e.g., "how_feeling")
|
1188
|
+
- A fully qualified name with type (e.g., "answer.how_feeling")
|
1189
|
+
- A wildcard pattern (e.g., "answer.*" to select all answer fields)
|
1190
|
+
If no columns are provided, selects all data.
|
1191
|
+
|
1192
|
+
Returns:
|
1193
|
+
A Dataset object containing the selected data.
|
1194
|
+
|
1195
|
+
Notes:
|
1196
|
+
- Column names are automatically disambiguated if needed
|
1197
|
+
- When column names are ambiguous, specify the full path with data type
|
1198
|
+
- You can use wildcard patterns with "*" to select multiple related fields
|
1199
|
+
- Selecting with no arguments returns all data
|
1200
|
+
- Results are restructured in a columnar format in the Dataset
|
1201
|
+
|
1202
|
+
Examples:
|
1203
|
+
>>> results = Results.example()
|
1204
|
+
|
1205
|
+
# Select a single column by name
|
1206
|
+
>>> results.select('how_feeling')
|
1207
|
+
Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}])
|
1208
|
+
|
1209
|
+
# Select multiple columns
|
1210
|
+
>>> ds = results.select('how_feeling', 'how_feeling_yesterday')
|
1211
|
+
>>> sorted([list(d.keys())[0] for d in ds])
|
1212
|
+
['answer.how_feeling', 'answer.how_feeling_yesterday']
|
1213
|
+
|
1214
|
+
# Using fully qualified names with data type
|
1215
|
+
>>> results.select('answer.how_feeling')
|
1216
|
+
Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}])
|
1217
|
+
|
1218
|
+
# Using partial matching for column names
|
1219
|
+
>>> results.select('answer.how_feeling_y')
|
1220
|
+
Dataset([{'answer.how_feeling_yesterday': ['Great', 'Good', 'OK', 'Terrible']}])
|
1221
|
+
|
1222
|
+
# Select all columns (same as calling select with no arguments)
|
1223
|
+
>>> results.select('*.*')
|
1224
|
+
Dataset([...])
|
1098
1225
|
"""
|
1099
1226
|
|
1100
1227
|
from edsl.results.results_selector import Selector
|
@@ -1114,8 +1241,6 @@ class Results(UserList, Mixins, Base):
|
|
1114
1241
|
@ensure_ready
|
1115
1242
|
def sort_by(self, *columns: str, reverse: bool = False) -> Results:
|
1116
1243
|
"""Sort the results by one or more columns."""
|
1117
|
-
import warnings
|
1118
|
-
|
1119
1244
|
warnings.warn(
|
1120
1245
|
"sort_by is deprecated. Use order_by instead.", DeprecationWarning
|
1121
1246
|
)
|
@@ -1167,29 +1292,50 @@ class Results(UserList, Mixins, Base):
|
|
1167
1292
|
@ensure_ready
|
1168
1293
|
def filter(self, expression: str) -> Results:
|
1169
1294
|
"""
|
1170
|
-
Filter based on
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
The
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1295
|
+
Filter results based on a boolean expression.
|
1296
|
+
|
1297
|
+
This method evaluates a boolean expression against each Result object in the
|
1298
|
+
collection and returns a new Results object containing only those that match.
|
1299
|
+
The expression can reference any column in the data and supports standard
|
1300
|
+
Python operators and syntax.
|
1301
|
+
|
1302
|
+
Parameters:
|
1303
|
+
expression: A string containing a Python expression that evaluates to a boolean.
|
1304
|
+
The expression is applied to each Result object individually.
|
1305
|
+
|
1306
|
+
Returns:
|
1307
|
+
A new Results object containing only the Result objects that satisfy the expression.
|
1308
|
+
|
1309
|
+
Notes:
|
1310
|
+
- Column names can be specified with or without their data type prefix
|
1311
|
+
(e.g., both "how_feeling" and "answer.how_feeling" work if unambiguous)
|
1312
|
+
- You must use double equals (==) for equality comparison, not single equals (=)
|
1313
|
+
- You can use logical operators like 'and', 'or', 'not'
|
1314
|
+
- You can use comparison operators like '==', '!=', '>', '<', '>=', '<='
|
1315
|
+
- You can use membership tests with 'in'
|
1316
|
+
- You can use string methods like '.startswith()', '.contains()', etc.
|
1317
|
+
|
1318
|
+
Examples:
|
1319
|
+
>>> r = Results.example()
|
1320
|
+
|
1321
|
+
# Simple equality filter
|
1322
|
+
>>> r.filter("how_feeling == 'Great'").select('how_feeling')
|
1323
|
+
Dataset([{'answer.how_feeling': ['Great']}])
|
1324
|
+
|
1325
|
+
# Using OR condition
|
1326
|
+
>>> r.filter("how_feeling == 'Great' or how_feeling == 'Terrible'").select('how_feeling')
|
1327
|
+
Dataset([{'answer.how_feeling': ['Great', 'Terrible']}])
|
1328
|
+
|
1329
|
+
# Filter on agent properties
|
1330
|
+
>>> r.filter("agent.status == 'Joyful'").select('agent.status')
|
1331
|
+
Dataset([{'agent.status': ['Joyful', 'Joyful']}])
|
1332
|
+
|
1333
|
+
# Common error: using = instead of ==
|
1334
|
+
>>> try:
|
1335
|
+
... r.filter("how_feeling = 'Great'")
|
1336
|
+
... except Exception as e:
|
1337
|
+
... print("ResultsFilterError: You must use '==' instead of '=' in the filter expression.")
|
1338
|
+
ResultsFilterError: You must use '==' instead of '=' in the filter expression.
|
1193
1339
|
"""
|
1194
1340
|
|
1195
1341
|
def has_single_equals(string):
|
@@ -1247,8 +1393,8 @@ class Results(UserList, Mixins, Base):
|
|
1247
1393
|
|
1248
1394
|
:param debug: if False, uses actual API calls
|
1249
1395
|
"""
|
1250
|
-
from
|
1251
|
-
from
|
1396
|
+
from ..jobs import Jobs
|
1397
|
+
from ..caching import Cache
|
1252
1398
|
|
1253
1399
|
c = Cache()
|
1254
1400
|
job = Jobs.example(randomize=randomize)
|
@@ -1311,8 +1457,8 @@ class Results(UserList, Mixins, Base):
|
|
1311
1457
|
"""
|
1312
1458
|
#print("Calling fetch_remote")
|
1313
1459
|
try:
|
1314
|
-
from
|
1315
|
-
from
|
1460
|
+
from ..coop import Coop
|
1461
|
+
from ..jobs import JobsRemoteInferenceHandler
|
1316
1462
|
|
1317
1463
|
# Get the remote job data
|
1318
1464
|
remote_job_data = JobsRemoteInferenceHandler.check_status(job_info.job_uuid)
|
@@ -1359,7 +1505,7 @@ class Results(UserList, Mixins, Base):
|
|
1359
1505
|
if not hasattr(self, "job_info"):
|
1360
1506
|
raise ResultsError("No job info available - this Results object wasn't created from a remote job")
|
1361
1507
|
|
1362
|
-
from
|
1508
|
+
from ..jobs import JobsRemoteInferenceHandler
|
1363
1509
|
|
1364
1510
|
try:
|
1365
1511
|
# Get the remote job data
|
@@ -1383,10 +1529,10 @@ class Results(UserList, Mixins, Base):
|
|
1383
1529
|
"""Run a survey to spot issues and suggest improvements for prompts that had no model response, returning a new Results object.
|
1384
1530
|
Future version: Allow user to optionally pass a list of questions to review, regardless of whether they had a null model response.
|
1385
1531
|
"""
|
1386
|
-
from
|
1387
|
-
from
|
1388
|
-
from
|
1389
|
-
from
|
1532
|
+
from ..questions import QuestionFreeText, QuestionDict
|
1533
|
+
from ..surveys import Survey
|
1534
|
+
from ..scenarios import Scenario, ScenarioList
|
1535
|
+
from ..language_models import Model, ModelList
|
1390
1536
|
import pandas as pd
|
1391
1537
|
|
1392
1538
|
df = self.select("agent.*", "scenario.*", "answer.*", "raw_model_response.*", "prompt.*").to_pandas()
|
@@ -1440,7 +1586,7 @@ class Results(UserList, Mixins, Base):
|
|
1440
1586
|
|
1441
1587
|
def main(): # pragma: no cover
|
1442
1588
|
"""Call the OpenAI API credits."""
|
1443
|
-
from
|
1589
|
+
from ..results import Results
|
1444
1590
|
|
1445
1591
|
results = Results.example(debug=True)
|
1446
1592
|
print(results.filter("how_feeling == 'Great'").select("how_feeling"))
|
@@ -1449,5 +1595,4 @@ def main(): # pragma: no cover
|
|
1449
1595
|
|
1450
1596
|
if __name__ == "__main__":
|
1451
1597
|
import doctest
|
1452
|
-
|
1453
1598
|
doctest.testmod(optionflags=doctest.ELLIPSIS)
|