edsl 0.1.29.dev6__py3-none-any.whl → 0.1.30.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. edsl/Base.py +6 -3
  2. edsl/__init__.py +23 -23
  3. edsl/__version__.py +1 -1
  4. edsl/agents/Agent.py +35 -34
  5. edsl/agents/AgentList.py +16 -5
  6. edsl/agents/Invigilator.py +19 -1
  7. edsl/agents/descriptors.py +2 -1
  8. edsl/base/Base.py +289 -0
  9. edsl/config.py +2 -1
  10. edsl/coop/utils.py +28 -1
  11. edsl/data/Cache.py +19 -5
  12. edsl/data/SQLiteDict.py +11 -3
  13. edsl/jobs/Answers.py +15 -1
  14. edsl/jobs/Jobs.py +69 -31
  15. edsl/jobs/buckets/ModelBuckets.py +4 -2
  16. edsl/jobs/buckets/TokenBucket.py +1 -2
  17. edsl/jobs/interviews/Interview.py +0 -6
  18. edsl/jobs/interviews/InterviewTaskBuildingMixin.py +9 -5
  19. edsl/jobs/runners/JobsRunnerAsyncio.py +12 -16
  20. edsl/jobs/tasks/TaskHistory.py +4 -3
  21. edsl/language_models/LanguageModel.py +5 -11
  22. edsl/language_models/ModelList.py +1 -1
  23. edsl/language_models/repair.py +8 -7
  24. edsl/notebooks/Notebook.py +9 -3
  25. edsl/questions/QuestionBase.py +6 -2
  26. edsl/questions/QuestionBudget.py +5 -6
  27. edsl/questions/QuestionCheckBox.py +7 -3
  28. edsl/questions/QuestionExtract.py +5 -3
  29. edsl/questions/QuestionFreeText.py +3 -3
  30. edsl/questions/QuestionFunctional.py +0 -3
  31. edsl/questions/QuestionList.py +3 -4
  32. edsl/questions/QuestionMultipleChoice.py +12 -5
  33. edsl/questions/QuestionNumerical.py +4 -3
  34. edsl/questions/QuestionRank.py +5 -3
  35. edsl/questions/__init__.py +4 -3
  36. edsl/questions/descriptors.py +4 -2
  37. edsl/results/DatasetExportMixin.py +491 -0
  38. edsl/results/Result.py +13 -65
  39. edsl/results/Results.py +91 -39
  40. edsl/results/ResultsDBMixin.py +7 -3
  41. edsl/results/ResultsExportMixin.py +22 -537
  42. edsl/results/ResultsGGMixin.py +3 -3
  43. edsl/results/ResultsToolsMixin.py +1 -4
  44. edsl/scenarios/FileStore.py +140 -0
  45. edsl/scenarios/Scenario.py +5 -6
  46. edsl/scenarios/ScenarioList.py +17 -8
  47. edsl/scenarios/ScenarioListExportMixin.py +32 -0
  48. edsl/scenarios/ScenarioListPdfMixin.py +2 -1
  49. edsl/scenarios/__init__.py +1 -0
  50. edsl/surveys/MemoryPlan.py +11 -4
  51. edsl/surveys/Survey.py +9 -4
  52. edsl/surveys/SurveyExportMixin.py +4 -2
  53. edsl/surveys/SurveyFlowVisualizationMixin.py +6 -4
  54. edsl/utilities/__init__.py +21 -21
  55. edsl/utilities/interface.py +66 -45
  56. edsl/utilities/utilities.py +11 -13
  57. {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dev1.dist-info}/METADATA +1 -1
  58. {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dev1.dist-info}/RECORD +60 -56
  59. {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dev1.dist-info}/LICENSE +0 -0
  60. {edsl-0.1.29.dev6.dist-info → edsl-0.1.30.dev1.dist-info}/WHEEL +0 -0
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,20 @@ 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
+ breakpoint()
250
294
  return results
251
295
 
252
296
  ######################
@@ -313,6 +357,8 @@ class Results(UserList, Mixins, Base):
313
357
  >>> r.answer_keys
314
358
  {'how_feeling': 'How are you this {{ period }}?', 'how_feeling_yesterday': 'How were you feeling yesterday {{ period }}?'}
315
359
  """
360
+ from edsl.utilities.utilities import shorten_string
361
+
316
362
  if not self.survey:
317
363
  raise Exception("Survey is not defined so no answer keys are available.")
318
364
 
@@ -327,7 +373,7 @@ class Results(UserList, Mixins, Base):
327
373
  return sorted_dict
328
374
 
329
375
  @property
330
- def agents(self) -> AgentList:
376
+ def agents(self) -> "AgentList":
331
377
  """Return a list of all of the agents in the Results.
332
378
 
333
379
  Example:
@@ -336,10 +382,12 @@ class Results(UserList, Mixins, Base):
336
382
  >>> r.agents
337
383
  AgentList([Agent(traits = {'status': 'Joyful'}), Agent(traits = {'status': 'Joyful'}), Agent(traits = {'status': 'Sad'}), Agent(traits = {'status': 'Sad'})])
338
384
  """
385
+ from edsl import AgentList
386
+
339
387
  return AgentList([r.agent for r in self.data])
340
388
 
341
389
  @property
342
- def models(self) -> list[Type[LanguageModel]]:
390
+ def models(self) -> list[Type["LanguageModel"]]:
343
391
  """Return a list of all of the models in the Results.
344
392
 
345
393
  Example:
@@ -461,7 +509,7 @@ class Results(UserList, Mixins, Base):
461
509
  )
462
510
  return data_type, key
463
511
 
464
- def first(self) -> Result:
512
+ def first(self) -> "Result":
465
513
  """Return the first observation in the results.
466
514
 
467
515
  Example:
@@ -579,6 +627,8 @@ class Results(UserList, Mixins, Base):
579
627
  )
580
628
  raw_var_name, expression = new_var_string.split("=", 1)
581
629
  var_name = raw_var_name.strip()
630
+ from edsl.utilities.utilities import is_valid_variable_name
631
+
582
632
  if not is_valid_variable_name(var_name):
583
633
  raise ResultsInvalidNameError(f"{var_name} is not a valid variable name.")
584
634
 
@@ -590,7 +640,7 @@ class Results(UserList, Mixins, Base):
590
640
  names=result.combined_dict, functions=functions_dict
591
641
  )
592
642
 
593
- def new_result(old_result: Result, var_name: str) -> Result:
643
+ def new_result(old_result: "Result", var_name: str) -> "Result":
594
644
  evaluator = create_evaluator(old_result)
595
645
  value = evaluator.eval(expression)
596
646
  new_result = old_result.copy()
@@ -680,7 +730,7 @@ class Results(UserList, Mixins, Base):
680
730
 
681
731
  return Results(survey=self.survey, data=new_data, created_columns=None)
682
732
 
683
- def select(self, *columns: Union[str, list[str]]) -> Dataset:
733
+ def select(self, *columns: Union[str, list[str]]) -> "Dataset":
684
734
  """
685
735
  Select data from the results and format it.
686
736
 
@@ -692,6 +742,7 @@ class Results(UserList, Mixins, Base):
692
742
  >>> results.select('how_feeling')
693
743
  Dataset([{'answer.how_feeling': ['OK', 'Great', 'Terrible', 'OK']}])
694
744
  """
745
+
695
746
  if len(self) == 0:
696
747
  raise Exception("No data to select from---the Results object is empty.")
697
748
 
@@ -748,6 +799,7 @@ class Results(UserList, Mixins, Base):
748
799
  return items_in_order.index(single_key)
749
800
 
750
801
  sorted(new_data, key=sort_by_key_order)
802
+ from edsl.results.Dataset import Dataset
751
803
 
752
804
  return Dataset(new_data)
753
805
 
@@ -906,7 +958,7 @@ class Results(UserList, Mixins, Base):
906
958
 
907
959
  :param debug: if False, uses actual API calls
908
960
  """
909
- from edsl.jobs import Jobs
961
+ from edsl.jobs.Jobs import Jobs
910
962
  from edsl.data.Cache import Cache
911
963
 
912
964
  c = Cache()
@@ -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