edsl 0.1.30__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.
edsl/results/Result.py CHANGED
@@ -126,9 +126,6 @@ class Result(Base, UserDict):
126
126
  self.survey = survey
127
127
  self.question_to_attributes = question_to_attributes
128
128
 
129
- self._combined_dict = None
130
- self._problem_keys = None
131
-
132
129
  ###############
133
130
  # Used in Results
134
131
  ###############
@@ -167,64 +164,25 @@ class Result(Base, UserDict):
167
164
  "answer": self.answer,
168
165
  "prompt": self.prompt,
169
166
  "raw_model_response": self.raw_model_response,
170
- # "iteration": {"iteration": self.iteration},
167
+ "iteration": {"iteration": self.iteration},
171
168
  "question_text": question_text_dict,
172
169
  "question_options": question_options_dict,
173
170
  "question_type": question_type_dict,
174
171
  "comment": comments_dict,
175
172
  }
176
173
 
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
-
185
174
  def code(self):
186
175
  """Return a string of code that can be used to recreate the Result object."""
187
176
  raise NotImplementedError
188
177
 
189
178
  @property
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:
179
+ def combined_dict(self) -> dict[str, Any]:
180
+ """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."""
195
181
  combined = {}
196
- problem_keys = []
197
182
  for key, sub_dict in self.sub_dicts.items():
198
183
  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
-
204
184
  combined.update({key: sub_dict})
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
185
+ return combined
228
186
 
229
187
  def get_value(self, data_type: str, key: str) -> Any:
230
188
  """Return the value for a given data type and key.
@@ -268,13 +226,7 @@ class Result(Base, UserDict):
268
226
  return Result.from_dict(self.to_dict())
269
227
 
270
228
  def __eq__(self, other) -> bool:
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
- """
229
+ """Return True if the Result object is equal to another Result object."""
278
230
  return self.to_dict() == other.to_dict()
279
231
 
280
232
  ###############
edsl/results/Results.py CHANGED
@@ -290,8 +290,7 @@ class Results(UserList, Mixins, Base):
290
290
  ),
291
291
  )
292
292
  except Exception as e:
293
- print(e)
294
- # breakpoint()
293
+ breakpoint()
295
294
  return results
296
295
 
297
296
  ######################
@@ -604,38 +603,6 @@ class Results(UserList, Mixins, Base):
604
603
  self = self.add_column(key, values)
605
604
  return self
606
605
 
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
-
639
606
  def mutate(
640
607
  self, new_var_string: str, functions_dict: Optional[dict] = None
641
608
  ) -> Results:
@@ -668,8 +635,13 @@ class Results(UserList, Mixins, Base):
668
635
  # create the evaluator
669
636
  functions_dict = functions_dict or {}
670
637
 
638
+ def create_evaluator(result: Result) -> EvalWithCompoundTypes:
639
+ return EvalWithCompoundTypes(
640
+ names=result.combined_dict, functions=functions_dict
641
+ )
642
+
671
643
  def new_result(old_result: "Result", var_name: str) -> "Result":
672
- evaluator = self._create_evaluator(old_result, functions_dict)
644
+ evaluator = create_evaluator(old_result)
673
645
  value = evaluator.eval(expression)
674
646
  new_result = old_result.copy()
675
647
  new_result["answer"][var_name] = value
@@ -769,9 +741,6 @@ class Results(UserList, Mixins, Base):
769
741
  >>> results = Results.example()
770
742
  >>> results.select('how_feeling')
771
743
  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']}])
775
744
  """
776
745
 
777
746
  if len(self) == 0:
@@ -829,19 +798,10 @@ class Results(UserList, Mixins, Base):
829
798
  # Return the index of this key in the list_of_keys
830
799
  return items_in_order.index(single_key)
831
800
 
832
- # sorted(new_data, key=sort_by_key_order)
801
+ sorted(new_data, key=sort_by_key_order)
833
802
  from edsl.results.Dataset import Dataset
834
803
 
835
- sorted_new_data = []
836
-
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)
804
+ return Dataset(new_data)
845
805
 
846
806
  def sort_by(self, *columns: str, reverse: bool = False) -> Results:
847
807
  import warnings
@@ -956,29 +916,29 @@ class Results(UserList, Mixins, Base):
956
916
  "You must use '==' instead of '=' in the filter expression."
957
917
  )
958
918
 
919
+ def create_evaluator(result):
920
+ """Create an evaluator for the given result.
921
+ The 'combined_dict' is a mapping of all values for that Result object.
922
+ """
923
+ return EvalWithCompoundTypes(names=result.combined_dict)
924
+
959
925
  try:
960
926
  # iterates through all the results and evaluates the expression
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
- )
927
+ new_data = [
928
+ result
929
+ for result in self.data
930
+ if create_evaluator(result).eval(expression)
931
+ ]
974
932
  except Exception as e:
975
933
  raise ResultsFilterError(
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.""",
934
+ f"""Error in filter. Exception:{e}.
935
+ The expression you provided was: {expression}.
936
+ Please make sure that the expression is a valid Python expression that evaluates to a boolean.
937
+ For example, 'how_feeling == "Great"' is a valid expression, as is 'how_feeling in ["Great", "Terrible"]'.
938
+ However, 'how_feeling = "Great"' is not a valid expression.
939
+
940
+ See https://docs.expectedparrot.com/en/latest/results.html#filtering-results for more details.
941
+ """
982
942
  )
983
943
 
984
944
  if len(new_data) == 0:
@@ -989,7 +949,7 @@ class Results(UserList, Mixins, Base):
989
949
  return Results(survey=self.survey, data=new_data, created_columns=None)
990
950
 
991
951
  @classmethod
992
- def example(cls, debug: bool = False, randomize: bool = False) -> Results:
952
+ def example(cls, debug: bool = False) -> Results:
993
953
  """Return an example `Results` object.
994
954
 
995
955
  Example usage:
@@ -1002,7 +962,7 @@ class Results(UserList, Mixins, Base):
1002
962
  from edsl.data.Cache import Cache
1003
963
 
1004
964
  c = Cache()
1005
- job = Jobs.example(randomize=randomize)
965
+ job = Jobs.example()
1006
966
  results = job.run(cache=c, debug=debug)
1007
967
  return results
1008
968
 
@@ -32,9 +32,6 @@ class FileStore(Scenario):
32
32
  def from_dict(cls, d):
33
33
  return cls(d["filename"], d["binary"], d["suffix"], d["base64_string"])
34
34
 
35
- def __repr__(self):
36
- return f"FileStore(filename='{self.filename}', binary='{self.binary}', 'suffix'={self.suffix})"
37
-
38
35
  def encode_file_to_base64_string(self, file_path):
39
36
  try:
40
37
  # Attempt to open the file in text mode
@@ -100,14 +97,8 @@ class FileStore(Scenario):
100
97
  file_like_object = self.base64_to_text_file(self["base64_string"])
101
98
 
102
99
  # Create a named temporary file
103
- mode = "wb" if self.binary else "w"
104
- temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=suffix, mode=mode)
105
-
106
- if self.binary:
107
- temp_file.write(file_like_object.read())
108
- else:
109
- temp_file.write(file_like_object.read())
110
-
100
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=suffix)
101
+ temp_file.write(file_like_object.read())
111
102
  temp_file.close()
112
103
 
113
104
  return temp_file.name
@@ -129,149 +120,11 @@ class CSVFileStore(FileStore):
129
120
  def __init__(self, filename):
130
121
  super().__init__(filename, suffix=".csv")
131
122
 
132
- @classmethod
133
- def example(cls):
134
- from edsl.results.Results import Results
135
-
136
- r = Results.example()
137
- import tempfile
138
-
139
- with tempfile.NamedTemporaryFile(suffix=".csv", delete=False) as f:
140
- r.to_csv(filename=f.name)
141
- return cls(f.name)
142
-
143
- def view(self):
144
- import pandas as pd
145
-
146
- return pd.read_csv(self.to_tempfile())
147
-
148
123
 
149
124
  class PDFFileStore(FileStore):
150
125
  def __init__(self, filename):
151
126
  super().__init__(filename, suffix=".pdf")
152
127
 
153
- def view(self):
154
- pdf_path = self.to_tempfile()
155
- print(f"PDF path: {pdf_path}") # Print the path to ensure it exists
156
- import os
157
- import subprocess
158
-
159
- if os.path.exists(pdf_path):
160
- try:
161
- if os.name == "posix":
162
- # for cool kids
163
- subprocess.run(["open", pdf_path], check=True) # macOS
164
- elif os.name == "nt":
165
- os.startfile(pdf_path) # Windows
166
- else:
167
- subprocess.run(["xdg-open", pdf_path], check=True) # Linux
168
- except Exception as e:
169
- print(f"Error opening PDF: {e}")
170
- else:
171
- print("PDF file was not created successfully.")
172
-
173
- @classmethod
174
- def example(cls):
175
- import textwrap
176
-
177
- pdf_string = textwrap.dedent(
178
- """\
179
- %PDF-1.4
180
- 1 0 obj
181
- << /Type /Catalog /Pages 2 0 R >>
182
- endobj
183
- 2 0 obj
184
- << /Type /Pages /Kids [3 0 R] /Count 1 >>
185
- endobj
186
- 3 0 obj
187
- << /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R >>
188
- endobj
189
- 4 0 obj
190
- << /Length 44 >>
191
- stream
192
- BT
193
- /F1 24 Tf
194
- 100 700 Td
195
- (Hello, World!) Tj
196
- ET
197
- endstream
198
- endobj
199
- 5 0 obj
200
- << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>
201
- endobj
202
- 6 0 obj
203
- << /ProcSet [/PDF /Text] /Font << /F1 5 0 R >> >>
204
- endobj
205
- xref
206
- 0 7
207
- 0000000000 65535 f
208
- 0000000010 00000 n
209
- 0000000053 00000 n
210
- 0000000100 00000 n
211
- 0000000173 00000 n
212
- 0000000232 00000 n
213
- 0000000272 00000 n
214
- trailer
215
- << /Size 7 /Root 1 0 R >>
216
- startxref
217
- 318
218
- %%EOF"""
219
- )
220
- import tempfile
221
-
222
- with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as f:
223
- f.write(pdf_string.encode())
224
- return cls(f.name)
225
-
226
-
227
- class PNGFileStore(FileStore):
228
- def __init__(self, filename):
229
- super().__init__(filename, suffix=".png")
230
-
231
- @classmethod
232
- def example(cls):
233
- import textwrap
234
-
235
- png_string = textwrap.dedent(
236
- """\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x01\x00\x00\x00\x01\x00\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x0cIDAT\x08\xd7c\x00\x01"""
237
- )
238
- import tempfile
239
-
240
- with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f:
241
- f.write(png_string.encode())
242
- return cls(f.name)
243
-
244
- def view(self):
245
- import matplotlib.pyplot as plt
246
- import matplotlib.image as mpimg
247
-
248
- img = mpimg.imread(self.to_tempfile())
249
- plt.imshow(img)
250
- plt.show()
251
-
252
-
253
- class SQLiteFileStore(FileStore):
254
- def __init__(self, filename):
255
- super().__init__(filename, suffix=".sqlite")
256
-
257
- @classmethod
258
- def example(cls):
259
- import sqlite3
260
- import tempfile
261
-
262
- with tempfile.NamedTemporaryFile(suffix=".sqlite", delete=False) as f:
263
- conn = sqlite3.connect(f.name)
264
- c = conn.cursor()
265
- c.execute("""CREATE TABLE stocks (date text)""")
266
- conn.commit()
267
-
268
- def view(self):
269
- import subprocess
270
- import os
271
-
272
- sqlite_path = self.to_tempfile()
273
- os.system(f"sqlite3 {sqlite_path}")
274
-
275
128
 
276
129
  if __name__ == "__main__":
277
130
  # file_path = "../conjure/examples/Ex11-2.sav"
@@ -279,21 +132,9 @@ if __name__ == "__main__":
279
132
  # info = fs.push()
280
133
  # print(info)
281
134
 
282
- # fs = CSVFileStore.example()
283
- # fs.to_tempfile()
284
- # print(fs.view())
285
-
286
- # fs = PDFFileStore.example()
287
- # fs.view()
288
-
289
- # fs = PDFFileStore("paper.pdf")
290
- # fs.view()
291
135
  # from edsl import Conjure
292
136
 
293
- fs = PNGFileStore("robot.png")
294
- fs.view()
295
-
296
137
  # c = Conjure(datafile_name=fs.to_tempfile())
297
- # f = PDFFileStore("paper.pdf")
138
+ f = PDFFileStore("paper.pdf")
298
139
  # print(f.to_tempfile())
299
- # f.push()
140
+ f.push()
@@ -1,17 +1,21 @@
1
1
  """A Scenario is a dictionary with a key/value to parameterize a question."""
2
2
 
3
- from __future__ import annotations
3
+ import time
4
4
  import copy
5
+ from collections import UserDict
6
+ from typing import Union, List, Optional, Generator
5
7
  import base64
6
8
  import hashlib
7
9
  import os
8
- from collections import UserDict
9
- from typing import Union, List, Optional, Generator
10
- from uuid import uuid4
10
+
11
11
  from edsl.Base import Base
12
12
  from edsl.scenarios.ScenarioImageMixin import ScenarioImageMixin
13
13
  from edsl.scenarios.ScenarioHtmlMixin import ScenarioHtmlMixin
14
- from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
14
+
15
+ from edsl.utilities.decorators import (
16
+ add_edsl_version,
17
+ remove_edsl_version,
18
+ )
15
19
 
16
20
 
17
21
  class Scenario(Base, UserDict, ScenarioImageMixin, ScenarioHtmlMixin):
@@ -24,7 +28,9 @@ class Scenario(Base, UserDict, ScenarioImageMixin, ScenarioHtmlMixin):
24
28
 
25
29
  :param data: A dictionary of keys/values for parameterizing questions.
26
30
  """
27
- self.data = data if data is not None else {}
31
+ if data is None:
32
+ data = {}
33
+ self.data = data
28
34
  self.name = name
29
35
 
30
36
  def replicate(self, n: int) -> "ScenarioList":
@@ -409,16 +415,17 @@ class Scenario(Base, UserDict, ScenarioImageMixin, ScenarioHtmlMixin):
409
415
  return table
410
416
 
411
417
  @classmethod
412
- def example(cls, randomize: bool = False) -> Scenario:
413
- """
414
- Returns an example Scenario instance.
418
+ def example(cls) -> "Scenario":
419
+ """Return an example scenario.
420
+
421
+ Example:
415
422
 
416
- :param randomize: If True, adds a random string to the value of the example key.
423
+ >>> Scenario.example()
424
+ Scenario({'persona': 'A reseacher studying whether LLMs can be used to generate surveys.'})
417
425
  """
418
- addition = "" if not randomize else str(uuid4())
419
426
  return cls(
420
427
  {
421
- "persona": f"A reseacher studying whether LLMs can be used to generate surveys.{addition}",
428
+ "persona": "A reseacher studying whether LLMs can be used to generate surveys."
422
429
  }
423
430
  )
424
431
 
@@ -5,8 +5,10 @@ import csv
5
5
  import random
6
6
  from collections import UserList, Counter
7
7
  from collections.abc import Iterable
8
- from simpleeval import EvalWithCompoundTypes
9
8
  from typing import Any, Optional, Union, List
9
+
10
+ from simpleeval import EvalWithCompoundTypes
11
+
10
12
  from edsl.Base import Base
11
13
  from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
12
14
  from edsl.scenarios.Scenario import Scenario
@@ -440,13 +442,9 @@ class ScenarioList(Base, UserList, ScenarioListMixin):
440
442
  return lines
441
443
 
442
444
  @classmethod
443
- def example(cls, randomize: bool = False) -> ScenarioList:
444
- """
445
- Return an example ScenarioList instance.
446
-
447
- :params randomize: If True, use Scenario's randomize method to randomize the values.
448
- """
449
- return cls([Scenario.example(randomize), Scenario.example(randomize)])
445
+ def example(cls) -> ScenarioList:
446
+ """Return an example of the `ScenarioList`."""
447
+ return cls([Scenario.example(), Scenario.example()])
450
448
 
451
449
  def rich_print(self) -> None:
452
450
  """Display an object as a table."""
edsl/study/Study.py CHANGED
@@ -6,12 +6,14 @@ import platform
6
6
  import socket
7
7
  from datetime import datetime
8
8
  from typing import Dict, Optional, Union
9
- from uuid import UUID, uuid4
10
9
  from edsl import Cache, set_session_cache, unset_session_cache
11
10
  from edsl.utilities.utilities import dict_hash
12
11
  from edsl.study.ObjectEntry import ObjectEntry
13
12
  from edsl.study.ProofOfWork import ProofOfWork
14
13
  from edsl.study.SnapShot import SnapShot
14
+ from uuid import UUID
15
+
16
+ # from edsl.Base import Base
15
17
 
16
18
 
17
19
  class Study:
@@ -400,14 +402,14 @@ class Study:
400
402
  return diff
401
403
 
402
404
  @classmethod
403
- def example(cls, verbose=False, randomize=False):
405
+ def example(cls, verbose=False):
404
406
  import tempfile
405
407
 
406
408
  study_file = tempfile.NamedTemporaryFile()
407
409
  with cls(filename=study_file.name, verbose=verbose) as study:
408
410
  from edsl import QuestionFreeText
409
411
 
410
- q = QuestionFreeText.example(randomize=randomize)
412
+ q = QuestionFreeText.example()
411
413
  return study
412
414
 
413
415
  @classmethod
@@ -461,13 +463,13 @@ class Study:
461
463
  else:
462
464
  self.objects[oe.hash] = oe
463
465
 
464
- def push(self) -> dict:
466
+ def push(self, refresh=False) -> None:
465
467
  """Push the objects to coop."""
466
468
 
467
469
  from edsl import Coop
468
470
 
469
471
  coop = Coop()
470
- return coop.create(self, description=self.description)
472
+ coop.create(self, description=self.description)
471
473
 
472
474
  @classmethod
473
475
  def pull(cls, uuid: Optional[Union[str, UUID]] = None, url: Optional[str] = None):