edsl 0.1.37.dev5__py3-none-any.whl → 0.1.38__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- edsl/Base.py +63 -34
- edsl/BaseDiff.py +7 -7
- edsl/__init__.py +2 -1
- edsl/__version__.py +1 -1
- edsl/agents/Agent.py +23 -11
- edsl/agents/AgentList.py +86 -23
- edsl/agents/Invigilator.py +18 -7
- edsl/agents/InvigilatorBase.py +0 -19
- edsl/agents/PromptConstructor.py +5 -4
- edsl/auto/SurveyCreatorPipeline.py +1 -1
- edsl/auto/utilities.py +1 -1
- edsl/base/Base.py +3 -13
- edsl/config.py +8 -0
- edsl/coop/coop.py +89 -19
- edsl/data/Cache.py +45 -17
- edsl/data/CacheEntry.py +8 -3
- edsl/data/RemoteCacheSync.py +0 -19
- edsl/enums.py +2 -0
- edsl/exceptions/agents.py +4 -0
- edsl/exceptions/cache.py +5 -0
- edsl/inference_services/GoogleService.py +7 -15
- edsl/inference_services/PerplexityService.py +163 -0
- edsl/inference_services/registry.py +2 -0
- edsl/jobs/Jobs.py +110 -559
- edsl/jobs/JobsChecks.py +147 -0
- edsl/jobs/JobsPrompts.py +268 -0
- edsl/jobs/JobsRemoteInferenceHandler.py +239 -0
- edsl/jobs/buckets/TokenBucket.py +3 -0
- edsl/jobs/interviews/Interview.py +7 -7
- edsl/jobs/runners/JobsRunnerAsyncio.py +156 -28
- edsl/jobs/runners/JobsRunnerStatus.py +194 -196
- edsl/jobs/tasks/TaskHistory.py +27 -19
- edsl/language_models/LanguageModel.py +52 -90
- edsl/language_models/ModelList.py +67 -14
- edsl/language_models/registry.py +57 -4
- edsl/notebooks/Notebook.py +7 -8
- edsl/prompts/Prompt.py +8 -3
- edsl/questions/QuestionBase.py +38 -30
- edsl/questions/QuestionBaseGenMixin.py +1 -1
- edsl/questions/QuestionBasePromptsMixin.py +0 -17
- edsl/questions/QuestionExtract.py +3 -4
- edsl/questions/QuestionFunctional.py +10 -3
- edsl/questions/derived/QuestionTopK.py +2 -0
- edsl/questions/question_registry.py +36 -6
- edsl/results/CSSParameterizer.py +108 -0
- edsl/results/Dataset.py +146 -15
- edsl/results/DatasetExportMixin.py +231 -217
- edsl/results/DatasetTree.py +134 -4
- edsl/results/Result.py +31 -16
- edsl/results/Results.py +159 -65
- edsl/results/TableDisplay.py +198 -0
- edsl/results/table_display.css +78 -0
- edsl/scenarios/FileStore.py +187 -13
- edsl/scenarios/Scenario.py +73 -18
- edsl/scenarios/ScenarioJoin.py +127 -0
- edsl/scenarios/ScenarioList.py +251 -76
- edsl/surveys/MemoryPlan.py +1 -1
- edsl/surveys/Rule.py +1 -5
- edsl/surveys/RuleCollection.py +1 -1
- edsl/surveys/Survey.py +25 -19
- edsl/surveys/SurveyFlowVisualizationMixin.py +67 -9
- edsl/surveys/instructions/ChangeInstruction.py +9 -7
- edsl/surveys/instructions/Instruction.py +21 -7
- edsl/templates/error_reporting/interview_details.html +3 -3
- edsl/templates/error_reporting/interviews.html +18 -9
- edsl/{conjure → utilities}/naming_utilities.py +1 -1
- edsl/utilities/utilities.py +15 -0
- {edsl-0.1.37.dev5.dist-info → edsl-0.1.38.dist-info}/METADATA +2 -1
- {edsl-0.1.37.dev5.dist-info → edsl-0.1.38.dist-info}/RECORD +71 -77
- edsl/conjure/AgentConstructionMixin.py +0 -160
- edsl/conjure/Conjure.py +0 -62
- edsl/conjure/InputData.py +0 -659
- edsl/conjure/InputDataCSV.py +0 -48
- edsl/conjure/InputDataMixinQuestionStats.py +0 -182
- edsl/conjure/InputDataPyRead.py +0 -91
- edsl/conjure/InputDataSPSS.py +0 -8
- edsl/conjure/InputDataStata.py +0 -8
- edsl/conjure/QuestionOptionMixin.py +0 -76
- edsl/conjure/QuestionTypeMixin.py +0 -23
- edsl/conjure/RawQuestion.py +0 -65
- edsl/conjure/SurveyResponses.py +0 -7
- edsl/conjure/__init__.py +0 -9
- edsl/conjure/examples/placeholder.txt +0 -0
- edsl/conjure/utilities.py +0 -201
- {edsl-0.1.37.dev5.dist-info → edsl-0.1.38.dist-info}/LICENSE +0 -0
- {edsl-0.1.37.dev5.dist-info → edsl-0.1.38.dist-info}/WHEEL +0 -0
@@ -138,7 +138,7 @@ class QuestionBaseGenMixin:
|
|
138
138
|
if exclude_components is None:
|
139
139
|
exclude_components = ["question_name", "question_type"]
|
140
140
|
|
141
|
-
d = copy.deepcopy(self.
|
141
|
+
d = copy.deepcopy(self.to_dict(add_edsl_version=False))
|
142
142
|
for key, value in d.items():
|
143
143
|
if key in exclude_components:
|
144
144
|
continue
|
@@ -126,7 +126,6 @@ class QuestionBasePromptsMixin:
|
|
126
126
|
|
127
127
|
@classmethod
|
128
128
|
def default_question_presentation(cls):
|
129
|
-
# template_text = cls._read_template("question_presentation.jinja")
|
130
129
|
template_text = template_manager.get_template(
|
131
130
|
cls.question_type, "question_presentation.jinja"
|
132
131
|
)
|
@@ -142,22 +141,6 @@ class QuestionBasePromptsMixin:
|
|
142
141
|
def answering_instructions(self, value) -> None:
|
143
142
|
self._answering_instructions = value
|
144
143
|
|
145
|
-
# @classmethod
|
146
|
-
# def default_answering_instructions(cls) -> str:
|
147
|
-
# with resources.open_text(
|
148
|
-
# f"edsl.questions.templates.{cls.question_type}",
|
149
|
-
# "answering_instructions.jinja",
|
150
|
-
# ) as file:
|
151
|
-
# return Prompt(text=file.read())
|
152
|
-
|
153
|
-
# @classmethod
|
154
|
-
# def default_question_presentation(cls):
|
155
|
-
# with resources.open_text(
|
156
|
-
# f"edsl.questions.templates.{cls.question_type}",
|
157
|
-
# "question_presentation.jinja",
|
158
|
-
# ) as file:
|
159
|
-
# return Prompt(text=file.read())
|
160
|
-
|
161
144
|
@property
|
162
145
|
def question_presentation(self):
|
163
146
|
if self._question_presentation is None:
|
@@ -1,4 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
+
import json
|
3
|
+
import re
|
4
|
+
|
2
5
|
from typing import Any, Optional, Dict
|
3
6
|
from edsl.questions.QuestionBase import QuestionBase
|
4
7
|
from edsl.questions.descriptors import AnswerTemplateDescriptor
|
@@ -11,9 +14,6 @@ from edsl.questions.decorators import inject_exception
|
|
11
14
|
from typing import Dict, Any
|
12
15
|
from pydantic import create_model, Field
|
13
16
|
|
14
|
-
import json
|
15
|
-
import re
|
16
|
-
|
17
17
|
|
18
18
|
def extract_json(text, expected_keys, verbose=False):
|
19
19
|
# Escape special regex characters in keys
|
@@ -112,7 +112,6 @@ class QuestionExtract(QuestionBase):
|
|
112
112
|
|
113
113
|
:param question_name: The name of the question.
|
114
114
|
:param question_text: The text of the question.
|
115
|
-
:param question_options: The options the respondent should select from.
|
116
115
|
:param answer_template: The template for the answer.
|
117
116
|
"""
|
118
117
|
self.question_name = question_name
|
@@ -108,15 +108,22 @@ class QuestionFunctional(QuestionBase):
|
|
108
108
|
def question_html_content(self) -> str:
|
109
109
|
return "NA for QuestionFunctional"
|
110
110
|
|
111
|
-
@add_edsl_version
|
112
|
-
def to_dict(self):
|
113
|
-
|
111
|
+
# @add_edsl_version
|
112
|
+
def to_dict(self, add_edsl_version=True):
|
113
|
+
d = {
|
114
114
|
"question_name": self.question_name,
|
115
115
|
"function_source_code": self.function_source_code,
|
116
116
|
"question_type": "functional",
|
117
117
|
"requires_loop": self.requires_loop,
|
118
118
|
"function_name": self.function_name,
|
119
119
|
}
|
120
|
+
if add_edsl_version:
|
121
|
+
from edsl import __version__
|
122
|
+
|
123
|
+
d["edsl_version"] = __version__
|
124
|
+
d["edsl_class_name"] = self.__class__.__name__
|
125
|
+
|
126
|
+
return d
|
120
127
|
|
121
128
|
@classmethod
|
122
129
|
def example(cls):
|
@@ -21,6 +21,7 @@ class QuestionTopK(QuestionCheckBox):
|
|
21
21
|
question_presentation: Optional[str] = None,
|
22
22
|
answering_instructions: Optional[str] = None,
|
23
23
|
include_comment: Optional[bool] = True,
|
24
|
+
use_code: Optional[bool] = True,
|
24
25
|
):
|
25
26
|
"""Initialize the question.
|
26
27
|
|
@@ -39,6 +40,7 @@ class QuestionTopK(QuestionCheckBox):
|
|
39
40
|
question_presentation=question_presentation,
|
40
41
|
answering_instructions=answering_instructions,
|
41
42
|
include_comment=include_comment,
|
43
|
+
use_code=use_code,
|
42
44
|
)
|
43
45
|
if min_selections != max_selections:
|
44
46
|
raise QuestionCreationValidationError(
|
@@ -90,6 +90,22 @@ class Question(metaclass=Meta):
|
|
90
90
|
coop = Coop()
|
91
91
|
return coop.patch(uuid, url, description, value, visibility)
|
92
92
|
|
93
|
+
@classmethod
|
94
|
+
def list_question_types(cls):
|
95
|
+
"""Return a list of available question types.
|
96
|
+
|
97
|
+
>>> from edsl import Question
|
98
|
+
>>> Question.list_question_types()
|
99
|
+
['checkbox', 'extract', 'free_text', 'functional', 'likert_five', 'linear_scale', 'list', 'multiple_choice', 'numerical', 'rank', 'top_k', 'yes_no']
|
100
|
+
"""
|
101
|
+
return [
|
102
|
+
q
|
103
|
+
for q in sorted(
|
104
|
+
list(RegisterQuestionsMeta.question_types_to_classes().keys())
|
105
|
+
)
|
106
|
+
if q not in ["budget"]
|
107
|
+
]
|
108
|
+
|
93
109
|
@classmethod
|
94
110
|
def available(cls, show_class_names: bool = False) -> Union[list, dict]:
|
95
111
|
"""Return a list of available question types.
|
@@ -98,18 +114,32 @@ class Question(metaclass=Meta):
|
|
98
114
|
|
99
115
|
Example usage:
|
100
116
|
|
101
|
-
>>> from edsl import Question
|
102
|
-
>>> Question.available()
|
103
|
-
['checkbox', 'extract', 'free_text', 'functional', 'likert_five', 'linear_scale', 'list', 'multiple_choice', 'numerical', 'rank', 'top_k', 'yes_no']
|
104
117
|
"""
|
118
|
+
from edsl.results.Dataset import Dataset
|
119
|
+
|
105
120
|
exclude = ["budget"]
|
106
121
|
if show_class_names:
|
107
122
|
return RegisterQuestionsMeta.question_types_to_classes()
|
108
123
|
else:
|
109
|
-
question_list =
|
110
|
-
|
124
|
+
question_list = [
|
125
|
+
q
|
126
|
+
for q in sorted(
|
127
|
+
set(RegisterQuestionsMeta.question_types_to_classes().keys())
|
128
|
+
)
|
129
|
+
if q not in exclude
|
130
|
+
]
|
131
|
+
d = RegisterQuestionsMeta.question_types_to_classes()
|
132
|
+
question_classes = [d[q] for q in question_list]
|
133
|
+
example_questions = [repr(q.example()) for q in question_classes]
|
134
|
+
|
135
|
+
return Dataset(
|
136
|
+
[
|
137
|
+
{"question_type": [q for q in question_list]},
|
138
|
+
{"question_class": [q.__name__ for q in question_classes]},
|
139
|
+
{"example_question": example_questions},
|
140
|
+
],
|
141
|
+
print_parameters={"containerHeight": "auto"},
|
111
142
|
)
|
112
|
-
return [q for q in question_list if q not in exclude]
|
113
143
|
|
114
144
|
|
115
145
|
def get_question_class(question_type):
|
@@ -0,0 +1,108 @@
|
|
1
|
+
import re
|
2
|
+
from typing import Dict, Set, Optional
|
3
|
+
|
4
|
+
|
5
|
+
class CSSParameterizer:
|
6
|
+
"""A utility class to parameterize CSS with custom properties (variables)."""
|
7
|
+
|
8
|
+
def __init__(self, css_content: str):
|
9
|
+
"""
|
10
|
+
Initialize with CSS content to be parameterized.
|
11
|
+
|
12
|
+
Args:
|
13
|
+
css_content (str): The CSS content containing var() declarations
|
14
|
+
"""
|
15
|
+
self.css_content = css_content
|
16
|
+
self._extract_variables()
|
17
|
+
|
18
|
+
def _extract_variables(self) -> None:
|
19
|
+
"""Extract all CSS custom properties (variables) from the CSS content."""
|
20
|
+
# Find all var(...) declarations in the CSS
|
21
|
+
var_pattern = r"var\((--[a-zA-Z0-9-]+)\)"
|
22
|
+
self.variables = set(re.findall(var_pattern, self.css_content))
|
23
|
+
|
24
|
+
def _validate_parameters(self, parameters: Dict[str, str]) -> Set[str]:
|
25
|
+
"""
|
26
|
+
Validate the provided parameters against the CSS variables.
|
27
|
+
|
28
|
+
Args:
|
29
|
+
parameters (Dict[str, str]): Dictionary of variable names and their values
|
30
|
+
|
31
|
+
Returns:
|
32
|
+
Set[str]: Set of missing variables
|
33
|
+
"""
|
34
|
+
# Convert parameter keys to CSS variable format if they don't already have --
|
35
|
+
formatted_params = {
|
36
|
+
f"--{k}" if not k.startswith("--") else k for k in parameters.keys()
|
37
|
+
}
|
38
|
+
|
39
|
+
# print("Variables from CSS:", self.variables)
|
40
|
+
# print("Formatted parameters:", formatted_params)
|
41
|
+
|
42
|
+
# Find missing and extra variables
|
43
|
+
missing_vars = self.variables - formatted_params
|
44
|
+
extra_vars = formatted_params - self.variables
|
45
|
+
|
46
|
+
if extra_vars:
|
47
|
+
print(f"Warning: Found unused parameters: {extra_vars}")
|
48
|
+
|
49
|
+
return missing_vars
|
50
|
+
|
51
|
+
def generate_root(self, **parameters: str) -> Optional[str]:
|
52
|
+
"""
|
53
|
+
Generate a :root block with the provided parameters.
|
54
|
+
|
55
|
+
Args:
|
56
|
+
**parameters: Keyword arguments where keys are variable names and values are their values
|
57
|
+
|
58
|
+
Returns:
|
59
|
+
str: Generated :root block with variables, or None if validation fails
|
60
|
+
|
61
|
+
Example:
|
62
|
+
>>> css = "body { height: var(--bodyHeight); }"
|
63
|
+
>>> parameterizer = CSSParameterizer(css)
|
64
|
+
>>> parameterizer.apply_parameters({'bodyHeight':"100vh"})
|
65
|
+
':root {\\n --bodyHeight: 100vh;\\n}\\n\\nbody { height: var(--bodyHeight); }'
|
66
|
+
"""
|
67
|
+
missing_vars = self._validate_parameters(parameters)
|
68
|
+
|
69
|
+
if missing_vars:
|
70
|
+
print(f"Error: Missing required variables: {missing_vars}")
|
71
|
+
return None
|
72
|
+
|
73
|
+
# Format parameters with -- prefix if not present
|
74
|
+
formatted_params = {
|
75
|
+
f"--{k}" if not k.startswith("--") else k: v for k, v in parameters.items()
|
76
|
+
}
|
77
|
+
|
78
|
+
# Generate the :root block
|
79
|
+
root_block = [":root {"]
|
80
|
+
for var_name, value in sorted(formatted_params.items()):
|
81
|
+
if var_name in self.variables:
|
82
|
+
root_block.append(f" {var_name}: {value};")
|
83
|
+
root_block.append("}")
|
84
|
+
|
85
|
+
return "\n".join(root_block)
|
86
|
+
|
87
|
+
def apply_parameters(self, parameters: dict) -> Optional[str]:
|
88
|
+
"""
|
89
|
+
Generate the complete CSS with the :root block and original CSS content.
|
90
|
+
|
91
|
+
Args:
|
92
|
+
**parameters: Keyword arguments where keys are variable names and values are their values
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
str: Complete CSS with :root block and original content, or None if validation fails
|
96
|
+
"""
|
97
|
+
root_block = self.generate_root(**parameters)
|
98
|
+
if root_block is None:
|
99
|
+
return None
|
100
|
+
|
101
|
+
return f"{root_block}\n\n{self.css_content}"
|
102
|
+
|
103
|
+
|
104
|
+
# Example usage
|
105
|
+
if __name__ == "__main__":
|
106
|
+
import doctest
|
107
|
+
|
108
|
+
doctest.testmod()
|
edsl/results/Dataset.py
CHANGED
@@ -5,19 +5,23 @@ import random
|
|
5
5
|
import json
|
6
6
|
from collections import UserList
|
7
7
|
from typing import Any, Union, Optional
|
8
|
-
|
8
|
+
import sys
|
9
9
|
import numpy as np
|
10
10
|
|
11
11
|
from edsl.results.ResultsExportMixin import ResultsExportMixin
|
12
12
|
from edsl.results.DatasetTree import Tree
|
13
|
+
from edsl.results.TableDisplay import TableDisplay
|
13
14
|
|
14
15
|
|
15
16
|
class Dataset(UserList, ResultsExportMixin):
|
16
17
|
"""A class to represent a dataset of observations."""
|
17
18
|
|
18
|
-
def __init__(
|
19
|
+
def __init__(
|
20
|
+
self, data: list[dict[str, Any]] = None, print_parameters: Optional[dict] = None
|
21
|
+
):
|
19
22
|
"""Initialize the dataset with the given data."""
|
20
23
|
super().__init__(data)
|
24
|
+
self.print_parameters = print_parameters
|
21
25
|
|
22
26
|
def __len__(self) -> int:
|
23
27
|
"""Return the number of observations in the dataset.
|
@@ -32,7 +36,7 @@ class Dataset(UserList, ResultsExportMixin):
|
|
32
36
|
_, values = list(self.data[0].items())[0]
|
33
37
|
return len(values)
|
34
38
|
|
35
|
-
def keys(self):
|
39
|
+
def keys(self) -> list[str]:
|
36
40
|
"""Return the keys of the first observation in the dataset.
|
37
41
|
|
38
42
|
>>> d = Dataset([{'a.b':[1,2,3,4]}])
|
@@ -41,10 +45,45 @@ class Dataset(UserList, ResultsExportMixin):
|
|
41
45
|
"""
|
42
46
|
return [list(o.keys())[0] for o in self]
|
43
47
|
|
48
|
+
def filter(self, expression):
|
49
|
+
return self.to_scenario_list().filter(expression).to_dataset()
|
50
|
+
|
44
51
|
def __repr__(self) -> str:
|
45
52
|
"""Return a string representation of the dataset."""
|
46
53
|
return f"Dataset({self.data})"
|
47
54
|
|
55
|
+
def write(self, filename: str, tablefmt: Optional[str] = None) -> None:
|
56
|
+
return self.table(tablefmt=tablefmt).write(filename)
|
57
|
+
|
58
|
+
def _repr_html_(self):
|
59
|
+
# headers, data = self._tabular()
|
60
|
+
return self.table(print_parameters=self.print_parameters)._repr_html_()
|
61
|
+
# return TableDisplay(headers=headers, data=data, raw_data_set=self)
|
62
|
+
|
63
|
+
def _tabular(self) -> tuple[list[str], list[list[Any]]]:
|
64
|
+
# Extract headers
|
65
|
+
headers = []
|
66
|
+
for entry in self.data:
|
67
|
+
headers.extend(entry.keys())
|
68
|
+
headers = list(dict.fromkeys(headers)) # Ensure unique headers
|
69
|
+
|
70
|
+
# Extract data
|
71
|
+
max_len = max(len(values) for entry in self.data for values in entry.values())
|
72
|
+
rows = []
|
73
|
+
for i in range(max_len):
|
74
|
+
row = []
|
75
|
+
for header in headers:
|
76
|
+
for entry in self.data:
|
77
|
+
if header in entry:
|
78
|
+
values = entry[header]
|
79
|
+
row.append(values[i] if i < len(values) else None)
|
80
|
+
break
|
81
|
+
else:
|
82
|
+
row.append(None) # Default to None if header is missing
|
83
|
+
rows.append(row)
|
84
|
+
|
85
|
+
return headers, rows
|
86
|
+
|
48
87
|
def _key_to_value(self, key: str) -> Any:
|
49
88
|
"""Retrieve the value associated with the given key from the dataset.
|
50
89
|
|
@@ -89,7 +128,25 @@ class Dataset(UserList, ResultsExportMixin):
|
|
89
128
|
|
90
129
|
return get_values(self.data[0])[0]
|
91
130
|
|
92
|
-
def
|
131
|
+
def print(self, pretty_labels=None, **kwargs):
|
132
|
+
if "format" in kwargs:
|
133
|
+
if kwargs["format"] not in ["html", "markdown", "rich", "latex"]:
|
134
|
+
raise ValueError(f"Format '{kwargs['format']}' not supported.")
|
135
|
+
if pretty_labels is None:
|
136
|
+
pretty_labels = {}
|
137
|
+
else:
|
138
|
+
return self.rename(pretty_labels).print(**kwargs)
|
139
|
+
return self.table()
|
140
|
+
|
141
|
+
def rename(self, rename_dic) -> Dataset:
|
142
|
+
new_data = []
|
143
|
+
for observation in self.data:
|
144
|
+
key, values = list(observation.items())[0]
|
145
|
+
new_key = rename_dic.get(key, key)
|
146
|
+
new_data.append({new_key: values})
|
147
|
+
return Dataset(new_data)
|
148
|
+
|
149
|
+
def select(self, *keys) -> Dataset:
|
93
150
|
"""Return a new dataset with only the selected keys.
|
94
151
|
|
95
152
|
:param keys: The keys to select.
|
@@ -122,12 +179,6 @@ class Dataset(UserList, ResultsExportMixin):
|
|
122
179
|
json.dumps(self.data)
|
123
180
|
) # janky but I want to make sure it's serializable & deserializable
|
124
181
|
|
125
|
-
def _repr_html_(self) -> str:
|
126
|
-
"""Return an HTML representation of the dataset."""
|
127
|
-
from edsl.utilities.utilities import data_to_html
|
128
|
-
|
129
|
-
return data_to_html(self.data)
|
130
|
-
|
131
182
|
def shuffle(self, seed=None) -> Dataset:
|
132
183
|
"""Return a new dataset with the observations shuffled.
|
133
184
|
|
@@ -149,6 +200,9 @@ class Dataset(UserList, ResultsExportMixin):
|
|
149
200
|
|
150
201
|
return self
|
151
202
|
|
203
|
+
def expand(self, field):
|
204
|
+
return self.to_scenario_list().expand(field).to_dataset()
|
205
|
+
|
152
206
|
def sample(
|
153
207
|
self,
|
154
208
|
n: int = None,
|
@@ -267,15 +321,92 @@ class Dataset(UserList, ResultsExportMixin):
|
|
267
321
|
|
268
322
|
return Dataset(new_data)
|
269
323
|
|
270
|
-
|
271
|
-
def tree(self):
|
324
|
+
def tree(self, node_order: Optional[list[str]] = None) -> Tree:
|
272
325
|
"""Return a tree representation of the dataset.
|
273
326
|
|
274
327
|
>>> d = Dataset([{'a':[1,2,3,4]}, {'b':[4,3,2,1]}])
|
275
|
-
>>> d.tree
|
276
|
-
Tree
|
328
|
+
>>> d.tree()
|
329
|
+
Tree(Dataset({'a': [1, 2, 3, 4], 'b': [4, 3, 2, 1]}))
|
277
330
|
"""
|
278
|
-
return Tree(self)
|
331
|
+
return Tree(self, node_order=node_order)
|
332
|
+
|
333
|
+
def table(
|
334
|
+
self,
|
335
|
+
*fields,
|
336
|
+
tablefmt: Optional[str] = None,
|
337
|
+
max_rows: Optional[int] = None,
|
338
|
+
pretty_labels=None,
|
339
|
+
print_parameters: Optional[dict] = None,
|
340
|
+
):
|
341
|
+
if pretty_labels is not None:
|
342
|
+
new_fields = []
|
343
|
+
for field in fields:
|
344
|
+
new_fields.append(pretty_labels.get(field, field))
|
345
|
+
return self.rename(pretty_labels).table(
|
346
|
+
*new_fields, tablefmt=tablefmt, max_rows=max_rows
|
347
|
+
)
|
348
|
+
|
349
|
+
self.print_parameters = print_parameters
|
350
|
+
|
351
|
+
headers, data = self._tabular()
|
352
|
+
|
353
|
+
if tablefmt is not None:
|
354
|
+
from tabulate import tabulate_formats
|
355
|
+
|
356
|
+
if tablefmt not in tabulate_formats:
|
357
|
+
print(
|
358
|
+
f"Error: The following table format is not supported: {tablefmt}",
|
359
|
+
file=sys.stderr,
|
360
|
+
)
|
361
|
+
print(f"\nAvailable formats are: {tabulate_formats}", file=sys.stderr)
|
362
|
+
return None
|
363
|
+
|
364
|
+
if max_rows:
|
365
|
+
if len(data) < max_rows:
|
366
|
+
max_rows = None
|
367
|
+
|
368
|
+
if fields:
|
369
|
+
full_data = data
|
370
|
+
data = []
|
371
|
+
indices = []
|
372
|
+
for field in fields:
|
373
|
+
if field not in headers:
|
374
|
+
print(
|
375
|
+
f"Error: The following field was not found: {field}",
|
376
|
+
file=sys.stderr,
|
377
|
+
)
|
378
|
+
print(f"\nAvailable fields are: {headers}", file=sys.stderr)
|
379
|
+
|
380
|
+
# Optional: Suggest similar fields using difflib
|
381
|
+
import difflib
|
382
|
+
|
383
|
+
matches = difflib.get_close_matches(field, headers)
|
384
|
+
if matches:
|
385
|
+
print(f"\nDid you mean: {matches[0]} ?", file=sys.stderr)
|
386
|
+
return None
|
387
|
+
indices.append(headers.index(field))
|
388
|
+
headers = fields
|
389
|
+
for row in full_data:
|
390
|
+
data.append([row[i] for i in indices])
|
391
|
+
|
392
|
+
if max_rows is not None:
|
393
|
+
if max_rows > len(data):
|
394
|
+
raise ValueError(
|
395
|
+
"max_rows cannot be greater than the number of rows in the dataset."
|
396
|
+
)
|
397
|
+
last_line = data[-1]
|
398
|
+
spaces = len(data[max_rows])
|
399
|
+
filler_line = ["." for i in range(spaces)]
|
400
|
+
data = data[:max_rows]
|
401
|
+
data.append(filler_line)
|
402
|
+
data.append(last_line)
|
403
|
+
|
404
|
+
return TableDisplay(
|
405
|
+
data=data, headers=headers, tablefmt=tablefmt, raw_data_set=self
|
406
|
+
)
|
407
|
+
|
408
|
+
def summary(self):
|
409
|
+
return Dataset([{"num_observations": [len(self)], "keys": [self.keys()]}])
|
279
410
|
|
280
411
|
@classmethod
|
281
412
|
def example(self):
|