edsl 0.1.29.dev3__py3-none-any.whl → 0.1.30__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.
Files changed (75) hide show
  1. edsl/Base.py +18 -18
  2. edsl/__init__.py +23 -23
  3. edsl/__version__.py +1 -1
  4. edsl/agents/Agent.py +79 -41
  5. edsl/agents/AgentList.py +26 -26
  6. edsl/agents/Invigilator.py +19 -2
  7. edsl/agents/InvigilatorBase.py +15 -10
  8. edsl/agents/PromptConstructionMixin.py +342 -100
  9. edsl/agents/descriptors.py +2 -1
  10. edsl/base/Base.py +289 -0
  11. edsl/config.py +2 -1
  12. edsl/conjure/InputData.py +39 -8
  13. edsl/conversation/car_buying.py +1 -1
  14. edsl/coop/coop.py +187 -150
  15. edsl/coop/utils.py +43 -75
  16. edsl/data/Cache.py +41 -18
  17. edsl/data/CacheEntry.py +6 -7
  18. edsl/data/SQLiteDict.py +11 -3
  19. edsl/data_transfer_models.py +4 -0
  20. edsl/jobs/Answers.py +15 -1
  21. edsl/jobs/Jobs.py +108 -49
  22. edsl/jobs/buckets/ModelBuckets.py +14 -2
  23. edsl/jobs/buckets/TokenBucket.py +32 -5
  24. edsl/jobs/interviews/Interview.py +99 -79
  25. edsl/jobs/interviews/InterviewTaskBuildingMixin.py +19 -24
  26. edsl/jobs/runners/JobsRunnerAsyncio.py +16 -16
  27. edsl/jobs/tasks/QuestionTaskCreator.py +10 -6
  28. edsl/jobs/tasks/TaskHistory.py +4 -3
  29. edsl/language_models/LanguageModel.py +17 -17
  30. edsl/language_models/ModelList.py +1 -1
  31. edsl/language_models/repair.py +8 -7
  32. edsl/notebooks/Notebook.py +47 -10
  33. edsl/prompts/Prompt.py +31 -19
  34. edsl/questions/QuestionBase.py +38 -13
  35. edsl/questions/QuestionBudget.py +5 -6
  36. edsl/questions/QuestionCheckBox.py +7 -3
  37. edsl/questions/QuestionExtract.py +5 -3
  38. edsl/questions/QuestionFreeText.py +7 -5
  39. edsl/questions/QuestionFunctional.py +34 -5
  40. edsl/questions/QuestionList.py +3 -4
  41. edsl/questions/QuestionMultipleChoice.py +68 -12
  42. edsl/questions/QuestionNumerical.py +4 -3
  43. edsl/questions/QuestionRank.py +5 -3
  44. edsl/questions/__init__.py +4 -3
  45. edsl/questions/descriptors.py +46 -4
  46. edsl/questions/question_registry.py +20 -31
  47. edsl/questions/settings.py +1 -1
  48. edsl/results/Dataset.py +31 -0
  49. edsl/results/DatasetExportMixin.py +570 -0
  50. edsl/results/Result.py +66 -70
  51. edsl/results/Results.py +160 -68
  52. edsl/results/ResultsDBMixin.py +7 -3
  53. edsl/results/ResultsExportMixin.py +22 -537
  54. edsl/results/ResultsGGMixin.py +3 -3
  55. edsl/results/ResultsToolsMixin.py +5 -5
  56. edsl/scenarios/FileStore.py +299 -0
  57. edsl/scenarios/Scenario.py +16 -24
  58. edsl/scenarios/ScenarioList.py +42 -17
  59. edsl/scenarios/ScenarioListExportMixin.py +32 -0
  60. edsl/scenarios/ScenarioListPdfMixin.py +2 -1
  61. edsl/scenarios/__init__.py +1 -0
  62. edsl/study/Study.py +8 -16
  63. edsl/surveys/MemoryPlan.py +11 -4
  64. edsl/surveys/Survey.py +88 -17
  65. edsl/surveys/SurveyExportMixin.py +4 -2
  66. edsl/surveys/SurveyFlowVisualizationMixin.py +6 -4
  67. edsl/tools/plotting.py +4 -2
  68. edsl/utilities/__init__.py +21 -21
  69. edsl/utilities/interface.py +66 -45
  70. edsl/utilities/utilities.py +11 -13
  71. {edsl-0.1.29.dev3.dist-info → edsl-0.1.30.dist-info}/METADATA +11 -10
  72. {edsl-0.1.29.dev3.dist-info → edsl-0.1.30.dist-info}/RECORD +74 -71
  73. {edsl-0.1.29.dev3.dist-info → edsl-0.1.30.dist-info}/WHEEL +1 -1
  74. edsl-0.1.29.dev3.dist-info/entry_points.txt +0 -3
  75. {edsl-0.1.29.dev3.dist-info → edsl-0.1.30.dist-info}/LICENSE +0 -0
edsl/results/Result.py CHANGED
@@ -3,16 +3,7 @@ from __future__ import annotations
3
3
  from collections import UserDict
4
4
  from typing import Any, Type, Callable, Optional
5
5
  from collections import UserDict
6
-
7
- from rich.table import Table
8
-
9
- from IPython.display import display
10
-
11
- from edsl.agents import Agent
12
- from edsl.language_models import LanguageModel
13
- from edsl.scenarios import Scenario
14
6
  from edsl.Base import Base
15
- from edsl.prompts import Prompt
16
7
  from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
17
8
 
18
9
 
@@ -21,6 +12,8 @@ class PromptDict(UserDict):
21
12
 
22
13
  def rich_print(self):
23
14
  """Display an object as a table."""
15
+ from rich.table import Table
16
+
24
17
  table = Table(title="")
25
18
  table.add_column("Attribute", style="bold")
26
19
  table.add_column("Value")
@@ -71,9 +64,9 @@ class Result(Base, UserDict):
71
64
 
72
65
  def __init__(
73
66
  self,
74
- agent: Agent,
75
- scenario: Scenario,
76
- model: Type[LanguageModel],
67
+ agent: "Agent",
68
+ scenario: "Scenario",
69
+ model: Type["LanguageModel"],
77
70
  iteration: int,
78
71
  answer: str,
79
72
  prompt: dict[str, str] = None,
@@ -133,6 +126,9 @@ class Result(Base, UserDict):
133
126
  self.survey = survey
134
127
  self.question_to_attributes = question_to_attributes
135
128
 
129
+ self._combined_dict = None
130
+ self._problem_keys = None
131
+
136
132
  ###############
137
133
  # Used in Results
138
134
  ###############
@@ -171,25 +167,64 @@ class Result(Base, UserDict):
171
167
  "answer": self.answer,
172
168
  "prompt": self.prompt,
173
169
  "raw_model_response": self.raw_model_response,
174
- "iteration": {"iteration": self.iteration},
170
+ # "iteration": {"iteration": self.iteration},
175
171
  "question_text": question_text_dict,
176
172
  "question_options": question_options_dict,
177
173
  "question_type": question_type_dict,
178
174
  "comment": comments_dict,
179
175
  }
180
176
 
177
+ def check_expression(self, expression) -> None:
178
+ for key in self.problem_keys:
179
+ if key in expression and not key + "." in expression:
180
+ raise ValueError(
181
+ f"Key by iself {key} is problematic. Use the full key {key + '.' + key} name instead."
182
+ )
183
+ return None
184
+
181
185
  def code(self):
182
186
  """Return a string of code that can be used to recreate the Result object."""
183
187
  raise NotImplementedError
184
188
 
185
189
  @property
186
- def combined_dict(self) -> dict[str, Any]:
187
- """Return a dictionary that includes all sub_dicts, but also puts the key-value pairs in each sub_dict as a key_value pair in the combined dictionary."""
190
+ def problem_keys(self):
191
+ """Return a list of keys that are problematic."""
192
+ return self._problem_keys
193
+
194
+ def _compute_combined_dict_and_problem_keys(self) -> None:
188
195
  combined = {}
196
+ problem_keys = []
189
197
  for key, sub_dict in self.sub_dicts.items():
190
198
  combined.update(sub_dict)
199
+ # in some cases, the sub_dict might have keys that conflict with the main dict
200
+ if key in combined:
201
+ # The key is already in the combined dict
202
+ problem_keys = problem_keys + [key]
203
+
191
204
  combined.update({key: sub_dict})
192
- return combined
205
+ # I *think* this allows us to do do things like "answer.how_feelling" i.e., that the evaluator can use
206
+ # dot notation to access the subdicts.
207
+ self._combined_dict = combined
208
+ self._problem_keys = problem_keys
209
+
210
+ @property
211
+ def combined_dict(self) -> dict[str, Any]:
212
+ """Return a dictionary that includes all sub_dicts, but also puts the key-value pairs in each sub_dict as a key_value pair in the combined dictionary.
213
+
214
+ >>> r = Result.example()
215
+ >>> r.combined_dict['how_feeling']
216
+ 'OK'
217
+ """
218
+ if self._combined_dict is None or self._problem_keys is None:
219
+ self._compute_combined_dict_and_problem_keys()
220
+ return self._combined_dict
221
+
222
+ @property
223
+ def problem_keys(self):
224
+ """Return a list of keys that are problematic."""
225
+ if self._combined_dict is None or self._problem_keys is None:
226
+ self._compute_combined_dict_and_problem_keys()
227
+ return self._problem_keys
193
228
 
194
229
  def get_value(self, data_type: str, key: str) -> Any:
195
230
  """Return the value for a given data type and key.
@@ -233,7 +268,13 @@ class Result(Base, UserDict):
233
268
  return Result.from_dict(self.to_dict())
234
269
 
235
270
  def __eq__(self, other) -> bool:
236
- """Return True if the Result object is equal to another Result object."""
271
+ """Return True if the Result object is equal to another Result object.
272
+
273
+ >>> r = Result.example()
274
+ >>> r == r
275
+ True
276
+
277
+ """
237
278
  return self.to_dict() == other.to_dict()
238
279
 
239
280
  ###############
@@ -278,6 +319,12 @@ class Result(Base, UserDict):
278
319
  @remove_edsl_version
279
320
  def from_dict(self, json_dict: dict) -> Result:
280
321
  """Return a Result object from a dictionary representation."""
322
+
323
+ from edsl import Agent
324
+ from edsl import Scenario
325
+ from edsl.language_models.LanguageModel import LanguageModel
326
+ from edsl.prompts.Prompt import Prompt
327
+
281
328
  prompt_data = json_dict.get("prompt", {})
282
329
  prompt_d = {}
283
330
  for prompt_name, prompt_obj in prompt_data.items():
@@ -301,6 +348,7 @@ class Result(Base, UserDict):
301
348
  """Display an object as a table."""
302
349
  # from edsl.utilities import print_dict_with_rich
303
350
  from rich import print
351
+ from rich.table import Table
304
352
 
305
353
  table = Table(title="Result")
306
354
  table.add_column("Attribute", style="bold")
@@ -325,7 +373,7 @@ class Result(Base, UserDict):
325
373
  @classmethod
326
374
  def example(cls):
327
375
  """Return an example Result object."""
328
- from edsl.results import Results
376
+ from edsl.results.Results import Results
329
377
 
330
378
  return Results.example()[0]
331
379
 
@@ -350,59 +398,7 @@ class Result(Base, UserDict):
350
398
  return scoring_function(**params)
351
399
 
352
400
 
353
- def main():
354
- """Run the main function."""
355
- from edsl.results.Result import Result
356
- import json
357
-
358
- print("Being imported")
359
- json_string = """
360
- {
361
- "agent": {
362
- "traits": {
363
- "status": "Unhappy"
364
- }
365
- },
366
- "scenario": {
367
- "period": "morning"
368
- },
369
- "model": {
370
- "model": "gpt-3.5-turbo",
371
- "parameters": {
372
- "temperature": 0.5,
373
- "max_tokens": 1000,
374
- "top_p": 1,
375
- "frequency_penalty": 0,
376
- "presence_penalty": 0,
377
- "use_cache": true
378
- }
379
- },
380
- "iteration": 0,
381
- "answer": {
382
- "how_feeling": "Bad"
383
- },
384
- "prompt": {"how_feeling_user_prompt": "How are you feeling today?", "how_feeling_system_prompt": "Answer the question"}
385
- }
386
- """
387
-
388
- result = Result.from_dict(json.loads(json_string))
389
-
390
- result.sub_dicts
391
- assert result.combined_dict["how_feeling"] == "Bad"
392
-
393
- result.combined_dict
394
- assert result.get_value("answer", "how_feeling") == "Bad"
395
-
396
- result.key_to_data_type
397
- print(result)
398
-
399
- assert result == result.copy()
400
-
401
- result.to_dict()
402
-
403
-
404
401
  if __name__ == "__main__":
405
- # print(Result.example())
406
402
  import doctest
407
403
 
408
404
  doctest.testmod(optionflags=doctest.ELLIPSIS)
edsl/results/Results.py CHANGED
@@ -5,16 +5,10 @@ It is not typically instantiated directly, but is returned by the run method of
5
5
 
6
6
  from __future__ import annotations
7
7
  import json
8
- import hashlib
9
8
  import random
10
9
  from collections import UserList, defaultdict
11
10
  from typing import Optional, Callable, Any, Type, Union, List
12
11
 
13
- from pygments import highlight
14
- from pygments.lexers import JsonLexer
15
- from pygments.formatters import HtmlFormatter
16
- from IPython.display import HTML
17
-
18
12
  from simpleeval import EvalWithCompoundTypes
19
13
 
20
14
  from edsl.exceptions.results import (
@@ -24,29 +18,17 @@ from edsl.exceptions.results import (
24
18
  ResultsMutateError,
25
19
  ResultsFilterError,
26
20
  )
27
- from edsl.agents import Agent, AgentList
28
- from edsl.language_models.LanguageModel import LanguageModel
29
- from edsl.results.Dataset import Dataset
30
- from edsl.results.Result import Result
21
+
31
22
  from edsl.results.ResultsExportMixin import ResultsExportMixin
32
- from edsl.scenarios import Scenario
33
-
34
- # from edsl.scenarios.ScenarioList import ScenarioList
35
- from edsl.surveys import Survey
36
- from edsl.data.Cache import Cache
37
- from edsl.utilities import (
38
- is_valid_variable_name,
39
- shorten_string,
40
- )
41
- from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
42
- from edsl.utilities.utilities import dict_hash
43
23
  from edsl.results.ResultsToolsMixin import ResultsToolsMixin
44
-
45
24
  from edsl.results.ResultsDBMixin import ResultsDBMixin
46
25
  from edsl.results.ResultsGGMixin import ResultsGGMixin
26
+ from edsl.results.ResultsFetchMixin import ResultsFetchMixin
27
+
28
+ from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
29
+ from edsl.utilities.utilities import dict_hash
47
30
 
48
31
  from edsl.Base import Base
49
- from edsl.results.ResultsFetchMixin import ResultsFetchMixin
50
32
 
51
33
 
52
34
  class Mixins(
@@ -56,7 +38,22 @@ class Mixins(
56
38
  ResultsGGMixin,
57
39
  ResultsToolsMixin,
58
40
  ):
59
- pass
41
+ def print_long(self, max_rows=None) -> None:
42
+ """Print the results in long format.
43
+
44
+ >>> from edsl.results import Results
45
+ >>> r = Results.example()
46
+ >>> r.select('how_feeling').print_long(max_rows = 2)
47
+ ┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━┓
48
+ ┃ Result index ┃ Key ┃ Value ┃
49
+ ┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━┩
50
+ │ 0 │ how_feeling │ OK │
51
+ │ 1 │ how_feeling │ Great │
52
+ └──────────────┴─────────────┴───────┘
53
+ """
54
+ from edsl.utilities.interface import print_results_long
55
+
56
+ print_results_long(self, max_rows=max_rows)
60
57
 
61
58
 
62
59
  class Results(UserList, Mixins, Base):
@@ -84,10 +81,10 @@ class Results(UserList, Mixins, Base):
84
81
 
85
82
  def __init__(
86
83
  self,
87
- survey: Optional[Survey] = None,
88
- data: Optional[list[Result]] = None,
84
+ survey: Optional["Survey"] = None,
85
+ data: Optional[list["Result"]] = None,
89
86
  created_columns: Optional[list[str]] = None,
90
- cache: Optional[Cache] = None,
87
+ cache: Optional["Cache"] = None,
91
88
  job_uuid: Optional[str] = None,
92
89
  total_results: Optional[int] = None,
93
90
  ):
@@ -100,6 +97,8 @@ class Results(UserList, Mixins, Base):
100
97
  :param total_results: An integer representing the total number of results.
101
98
  """
102
99
  super().__init__(data)
100
+ from edsl.data.Cache import Cache
101
+
103
102
  self.survey = survey
104
103
  self.created_columns = created_columns or []
105
104
  self._job_uuid = job_uuid
@@ -125,6 +124,10 @@ class Results(UserList, Mixins, Base):
125
124
  raise TypeError("Invalid argument type")
126
125
 
127
126
  def _update_results(self) -> None:
127
+ from edsl import Agent, Scenario
128
+ from edsl.language_models import LanguageModel
129
+ from edsl.results import Result
130
+
128
131
  if self._job_uuid and len(self.data) < self._total_results:
129
132
  results = [
130
133
  Result(
@@ -168,7 +171,13 @@ class Results(UserList, Mixins, Base):
168
171
  return f"Results(data = {self.data}, survey = {repr(self.survey)}, created_columns = {self.created_columns})"
169
172
 
170
173
  def _repr_html_(self) -> str:
174
+ from IPython.display import HTML
175
+
171
176
  json_str = json.dumps(self.to_dict()["data"], indent=4)
177
+ from pygments import highlight
178
+ from pygments.lexers import JsonLexer
179
+ from pygments.formatters import HtmlFormatter
180
+
172
181
  formatted_json = highlight(
173
182
  json_str,
174
183
  JsonLexer(),
@@ -177,6 +186,8 @@ class Results(UserList, Mixins, Base):
177
186
  return HTML(formatted_json).data
178
187
 
179
188
  def _to_dict(self, sort=False):
189
+ from edsl.data.Cache import Cache
190
+
180
191
  if sort:
181
192
  data = sorted([result for result in self.data], key=lambda x: hash(x))
182
193
  else:
@@ -226,6 +237,31 @@ class Results(UserList, Mixins, Base):
226
237
  def hashes(self) -> set:
227
238
  return set(hash(result) for result in self.data)
228
239
 
240
+ def sample(self, n: int) -> "Results":
241
+ """Return a random sample of the results.
242
+
243
+ :param n: The number of samples to return.
244
+
245
+ >>> from edsl.results import Results
246
+ >>> r = Results.example()
247
+ >>> len(r.sample(2))
248
+ 2
249
+ """
250
+ indices = None
251
+
252
+ for entry in self:
253
+ key, values = list(entry.items())[0]
254
+ if indices is None: # gets the indices for the first time
255
+ indices = list(range(len(values)))
256
+ sampled_indices = random.sample(indices, n)
257
+ if n > len(indices):
258
+ raise ValueError(
259
+ f"Cannot sample {n} items from a list of length {len(indices)}."
260
+ )
261
+ entry[key] = [values[i] for i in sampled_indices]
262
+
263
+ return self
264
+
229
265
  @classmethod
230
266
  @remove_edsl_version
231
267
  def from_dict(cls, data: dict[str, Any]) -> Results:
@@ -241,12 +277,21 @@ class Results(UserList, Mixins, Base):
241
277
  >>> r == r2
242
278
  True
243
279
  """
244
- results = cls(
245
- survey=Survey.from_dict(data["survey"]),
246
- data=[Result.from_dict(r) for r in data["data"]],
247
- created_columns=data.get("created_columns", None),
248
- cache=Cache.from_dict(data.get("cache")) if "cache" in data else Cache(),
249
- )
280
+ from edsl import Survey, Cache
281
+ from edsl.results.Result import Result
282
+
283
+ try:
284
+ results = cls(
285
+ survey=Survey.from_dict(data["survey"]),
286
+ data=[Result.from_dict(r) for r in data["data"]],
287
+ created_columns=data.get("created_columns", None),
288
+ cache=(
289
+ Cache.from_dict(data.get("cache")) if "cache" in data else Cache()
290
+ ),
291
+ )
292
+ except Exception as e:
293
+ print(e)
294
+ # breakpoint()
250
295
  return results
251
296
 
252
297
  ######################
@@ -313,6 +358,8 @@ class Results(UserList, Mixins, Base):
313
358
  >>> r.answer_keys
314
359
  {'how_feeling': 'How are you this {{ period }}?', 'how_feeling_yesterday': 'How were you feeling yesterday {{ period }}?'}
315
360
  """
361
+ from edsl.utilities.utilities import shorten_string
362
+
316
363
  if not self.survey:
317
364
  raise Exception("Survey is not defined so no answer keys are available.")
318
365
 
@@ -327,7 +374,7 @@ class Results(UserList, Mixins, Base):
327
374
  return sorted_dict
328
375
 
329
376
  @property
330
- def agents(self) -> AgentList:
377
+ def agents(self) -> "AgentList":
331
378
  """Return a list of all of the agents in the Results.
332
379
 
333
380
  Example:
@@ -336,10 +383,12 @@ class Results(UserList, Mixins, Base):
336
383
  >>> r.agents
337
384
  AgentList([Agent(traits = {'status': 'Joyful'}), Agent(traits = {'status': 'Joyful'}), Agent(traits = {'status': 'Sad'}), Agent(traits = {'status': 'Sad'})])
338
385
  """
386
+ from edsl import AgentList
387
+
339
388
  return AgentList([r.agent for r in self.data])
340
389
 
341
390
  @property
342
- def models(self) -> list[Type[LanguageModel]]:
391
+ def models(self) -> list[Type["LanguageModel"]]:
343
392
  """Return a list of all of the models in the Results.
344
393
 
345
394
  Example:
@@ -461,7 +510,7 @@ class Results(UserList, Mixins, Base):
461
510
  )
462
511
  return data_type, key
463
512
 
464
- def first(self) -> Result:
513
+ def first(self) -> "Result":
465
514
  """Return the first observation in the results.
466
515
 
467
516
  Example:
@@ -555,6 +604,38 @@ class Results(UserList, Mixins, Base):
555
604
  self = self.add_column(key, values)
556
605
  return self
557
606
 
607
+ @staticmethod
608
+ def _create_evaluator(
609
+ result: Result, functions_dict: Optional[dict] = None
610
+ ) -> EvalWithCompoundTypes:
611
+ """Create an evaluator for the expression.
612
+
613
+ >>> from unittest.mock import Mock
614
+ >>> result = Mock()
615
+ >>> result.combined_dict = {'how_feeling': 'OK'}
616
+
617
+ >>> evaluator = Results._create_evaluator(result = result, functions_dict = {})
618
+ >>> evaluator.eval("how_feeling == 'OK'")
619
+ True
620
+
621
+ >>> result.combined_dict = {'answer': {'how_feeling': 'OK'}}
622
+ >>> evaluator = Results._create_evaluator(result = result, functions_dict = {})
623
+ >>> evaluator.eval("answer.how_feeling== 'OK'")
624
+ True
625
+
626
+ Note that you need to refer to the answer dictionary in the expression.
627
+
628
+ >>> evaluator.eval("how_feeling== 'OK'")
629
+ Traceback (most recent call last):
630
+ ...
631
+ simpleeval.NameNotDefined: 'how_feeling' is not defined for expression 'how_feeling== 'OK''
632
+ """
633
+ if functions_dict is None:
634
+ functions_dict = {}
635
+ return EvalWithCompoundTypes(
636
+ names=result.combined_dict, functions=functions_dict
637
+ )
638
+
558
639
  def mutate(
559
640
  self, new_var_string: str, functions_dict: Optional[dict] = None
560
641
  ) -> Results:
@@ -579,19 +660,16 @@ class Results(UserList, Mixins, Base):
579
660
  )
580
661
  raw_var_name, expression = new_var_string.split("=", 1)
581
662
  var_name = raw_var_name.strip()
663
+ from edsl.utilities.utilities import is_valid_variable_name
664
+
582
665
  if not is_valid_variable_name(var_name):
583
666
  raise ResultsInvalidNameError(f"{var_name} is not a valid variable name.")
584
667
 
585
668
  # create the evaluator
586
669
  functions_dict = functions_dict or {}
587
670
 
588
- def create_evaluator(result: Result) -> EvalWithCompoundTypes:
589
- return EvalWithCompoundTypes(
590
- names=result.combined_dict, functions=functions_dict
591
- )
592
-
593
- def new_result(old_result: Result, var_name: str) -> Result:
594
- evaluator = create_evaluator(old_result)
671
+ def new_result(old_result: "Result", var_name: str) -> "Result":
672
+ evaluator = self._create_evaluator(old_result, functions_dict)
595
673
  value = evaluator.eval(expression)
596
674
  new_result = old_result.copy()
597
675
  new_result["answer"][var_name] = value
@@ -680,7 +758,7 @@ class Results(UserList, Mixins, Base):
680
758
 
681
759
  return Results(survey=self.survey, data=new_data, created_columns=None)
682
760
 
683
- def select(self, *columns: Union[str, list[str]]) -> Dataset:
761
+ def select(self, *columns: Union[str, list[str]]) -> "Dataset":
684
762
  """
685
763
  Select data from the results and format it.
686
764
 
@@ -691,7 +769,11 @@ class Results(UserList, Mixins, Base):
691
769
  >>> results = Results.example()
692
770
  >>> results.select('how_feeling')
693
771
  Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}])
772
+
773
+ >>> results.select('how_feeling', 'model', 'how_feeling')
774
+ Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}, {'model.model': ['gpt-4-1106-preview', 'gpt-4-1106-preview', 'gpt-4-1106-preview', 'gpt-4-1106-preview']}, {'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}])
694
775
  """
776
+
695
777
  if len(self) == 0:
696
778
  raise Exception("No data to select from---the Results object is empty.")
697
779
 
@@ -747,9 +829,19 @@ class Results(UserList, Mixins, Base):
747
829
  # Return the index of this key in the list_of_keys
748
830
  return items_in_order.index(single_key)
749
831
 
750
- sorted(new_data, key=sort_by_key_order)
832
+ # sorted(new_data, key=sort_by_key_order)
833
+ from edsl.results.Dataset import Dataset
834
+
835
+ sorted_new_data = []
751
836
 
752
- return Dataset(new_data)
837
+ # WORKS but slow
838
+ for key in items_in_order:
839
+ for d in new_data:
840
+ if key in d:
841
+ sorted_new_data.append(d)
842
+ break
843
+
844
+ return Dataset(sorted_new_data)
753
845
 
754
846
  def sort_by(self, *columns: str, reverse: bool = False) -> Results:
755
847
  import warnings
@@ -864,29 +956,29 @@ class Results(UserList, Mixins, Base):
864
956
  "You must use '==' instead of '=' in the filter expression."
865
957
  )
866
958
 
867
- def create_evaluator(result):
868
- """Create an evaluator for the given result.
869
- The 'combined_dict' is a mapping of all values for that Result object.
870
- """
871
- return EvalWithCompoundTypes(names=result.combined_dict)
872
-
873
959
  try:
874
960
  # iterates through all the results and evaluates the expression
875
- new_data = [
876
- result
877
- for result in self.data
878
- if create_evaluator(result).eval(expression)
879
- ]
961
+ new_data = []
962
+ for result in self.data:
963
+ evaluator = self._create_evaluator(result)
964
+ result.check_expression(expression) # check expression
965
+ if evaluator.eval(expression):
966
+ new_data.append(result)
967
+
968
+ except ValueError as e:
969
+ raise ResultsFilterError(
970
+ f"Error in filter. Exception:{e}",
971
+ f"The expression you provided was: {expression}",
972
+ "See https://docs.expectedparrot.com/en/latest/results.html#filtering-results for more details.",
973
+ )
880
974
  except Exception as e:
881
975
  raise ResultsFilterError(
882
- f"""Error in filter. Exception:{e}.
883
- The expression you provided was: {expression}.
884
- Please make sure that the expression is a valid Python expression that evaluates to a boolean.
885
- For example, 'how_feeling == "Great"' is a valid expression, as is 'how_feeling in ["Great", "Terrible"]'.
886
- However, 'how_feeling = "Great"' is not a valid expression.
887
-
888
- See https://docs.expectedparrot.com/en/latest/results.html#filtering-results for more details.
889
- """
976
+ f"""Error in filter. Exception:{e}.""",
977
+ f"""The expression you provided was: {expression}.""",
978
+ """Please make sure that the expression is a valid Python expression that evaluates to a boolean.""",
979
+ """For example, 'how_feeling == "Great"' is a valid expression, as is 'how_feeling in ["Great", "Terrible"]'., """,
980
+ """However, 'how_feeling = "Great"' is not a valid expression.""",
981
+ """See https://docs.expectedparrot.com/en/latest/results.html#filtering-results for more details.""",
890
982
  )
891
983
 
892
984
  if len(new_data) == 0:
@@ -897,7 +989,7 @@ class Results(UserList, Mixins, Base):
897
989
  return Results(survey=self.survey, data=new_data, created_columns=None)
898
990
 
899
991
  @classmethod
900
- def example(cls, debug: bool = False) -> Results:
992
+ def example(cls, debug: bool = False, randomize: bool = False) -> Results:
901
993
  """Return an example `Results` object.
902
994
 
903
995
  Example usage:
@@ -906,11 +998,11 @@ class Results(UserList, Mixins, Base):
906
998
 
907
999
  :param debug: if False, uses actual API calls
908
1000
  """
909
- from edsl.jobs import Jobs
1001
+ from edsl.jobs.Jobs import Jobs
910
1002
  from edsl.data.Cache import Cache
911
1003
 
912
1004
  c = Cache()
913
- job = Jobs.example()
1005
+ job = Jobs.example(randomize=randomize)
914
1006
  results = job.run(cache=c, debug=debug)
915
1007
  return results
916
1008
 
@@ -1,8 +1,6 @@
1
1
  """Mixin for working with SQLite respresentation of a 'Results' object."""
2
2
 
3
- import pandas as pd
4
3
  import sqlite3
5
- from sqlalchemy import create_engine
6
4
  from enum import Enum
7
5
  from typing import Literal, Union, Optional
8
6
 
@@ -92,6 +90,8 @@ class ResultsDBMixin:
92
90
  conn.commit()
93
91
  return conn
94
92
  elif shape == SQLDataShape.WIDE:
93
+ from sqlalchemy import create_engine
94
+
95
95
  engine = create_engine("sqlite:///:memory:")
96
96
  df = self.to_pandas(remove_prefix=remove_prefix)
97
97
  df.to_sql("self", engine, index=False, if_exists="replace")
@@ -121,7 +121,7 @@ class ResultsDBMixin:
121
121
  to_list=False,
122
122
  to_latex=False,
123
123
  filename: Optional[str] = None,
124
- ) -> Union[pd.DataFrame, str]:
124
+ ) -> Union["pd.DataFrame", str]:
125
125
  """Execute a SQL query and return the results as a DataFrame.
126
126
 
127
127
  :param query: The SQL query to execute
@@ -151,6 +151,8 @@ class ResultsDBMixin:
151
151
  2 Terrible
152
152
  3 OK
153
153
  """
154
+ import pandas as pd
155
+
154
156
  shape_enum = self._get_shape_enum(shape)
155
157
 
156
158
  conn = self._db(shape=shape_enum, remove_prefix=remove_prefix)
@@ -205,6 +207,8 @@ class ResultsDBMixin:
205
207
  ...
206
208
  <BLANKLINE>
207
209
  """
210
+ import pandas as pd
211
+
208
212
  shape_enum = self._get_shape_enum(shape)
209
213
  conn = self._db(shape=shape_enum, remove_prefix=remove_prefix)
210
214