> ## Documentation Index
> Fetch the complete documentation index at: https://docs.expectedparrot.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Skip logic & scenarios

> This notebook provides example EDSL code for using a language model to simulate a survey that uses skip logic: rules for determining which questions are administered based on responses to other questions in the survey.

In the first example below we construct a survey of questions and then add a rule to skip one question based on the response to another question.

In the second example we add some complexity. We first create different “scenarios” (versions) of questions and combine them in a survey. Then we add multiple rules to skip specific versions of the questions based on responses to a particular version of a question.

[EDSL is an open-source library](https://github.com/expectedparrot/edsl) for simulating surveys, experiments and other research with AI agents and large language models. Before running the code below, please ensure that you have [installed the EDSL library](/en/latest/installation) and either [activated remote inference](/en/latest/remote_inference) from your [Expected Parrot account](/en/latest/coop) or [stored API keys](/en/latest/api_keys) for the language models that you want to use with EDSL. Please also see our [documentation page](/en/latest/index) for tips and tutorials on getting started using EDSL.

## Example 1

In the first example below we construct questions, combine them in a survey, and add a rule to skip the second question based on the response to the first question. Then we create `Scenario` objects for contents that will be added to the questions when the survey is run. The effect of this is that the second question will be skipped based on the response to the first question for each individual scenario.

We start by constructing questions:

```python theme={null}
from edsl import QuestionYesNo, QuestionNumerical, QuestionMultipleChoice

q1 = QuestionYesNo(
    question_name = "recent_purchase",
    question_text = "In the last year have you or anyone in your household purchased any `{{ scenario.item }}`?",
)

q2 = QuestionNumerical(
    question_name = "amount",
    question_text = "In the last year, how much did your household spend on `{{ scenario.item }}` (in USD)?"
)

q3 = QuestionMultipleChoice(
    question_name = "next_purchase",
    question_text = "When do you next expect to purchase `{{ scenario.item }}`?",
    question_options = [
        "Never",
        "Within the next month",
        "Within the next year",
        "I do not know"
    ]
)
```

We combine the questions in a survey to administer them together:

```python theme={null}
from edsl import Survey

survey = Survey(questions = [q1, q2, q3])
survey
```

[Survey](/en/latest/surveys) # questions: 3; question\_name list: \['recent\_purchase', 'amount', 'next\_purchase'];

|    | question\_options                                                            | question\_name   | question\_type   | question\_text                                                                             |
| :- | :--------------------------------------------------------------------------- | :--------------- | :--------------- | :----------------------------------------------------------------------------------------- |
| 0  | \['No', 'Yes']                                                               | recent\_purchase | yes\_no          | In the last year have you or anyone in your household purchased any `{{ scenario.item }}`? |
| 1  | nan                                                                          | amount           | numerical        | In the last year, how much did your household spend on `{{ scenario.item }}` (in USD)?     |
| 2  | \['Never', 'Within the next month', 'Within the next year', 'I do not know'] | next\_purchase   | multiple\_choice | When do you next expect to purchase `{{ scenario.item }}`?                                 |

Here we add a rule to skip q2 based on the response to q1:

```python theme={null}
survey = survey.add_skip_rule(q2, "`{{ recent_purchase.answer }}` == 'No'")
```

Next we create scenarios for the “item” to be used with each question:

```python theme={null}
from edsl import Scenario, ScenarioList

s = ScenarioList(
    Scenario({"item":item}) for item in ["electronics", "phones"]
)
```

<Note>
  **Note**:

  Note that we could also use a method for the data type that we are using–this is equivalent:
</Note>

```python theme={null}
s = ScenarioList.from_list("item", ["electronics", "phones"])
s
```

```python theme={null}
ScenarioList scenarios: 2; keys: ['item'];
```

|    | item        |
| :- | :---------- |
| 0  | electronics |
| 1  | phones      |

We can inspect the flow of the survey that has been created with the scenarios that we’re using:

```python theme={null}
survey.by(s).show_flow()
```

<Frame>
  <img src="https://mintlify.s3.us-west-1.amazonaws.com/expectedparrot/images/en/notebook/skip_logic_1.png" alt="../_images/notebooks_skip_logic_scenarios_13_0.png" />
</Frame>

Next we create some agent personas to answer the questions:

```python theme={null}
from edsl import Agent, AgentList

income_levels = ["under $100,000", "$100,000-250,000", "above $250,000"]
ages = [30, 50, 70]

a = AgentList(
    Agent({"annual_income":income, "age":age}) for income in income_levels for age in ages
)
a
```

```python theme={null}
AgentList agents: 9;
```

|    | annual\_income    | age |
| :- | :---------------- | :-- |
| 0  | under \$100,000   | 30  |
| 1  | under \$100,000   | 50  |
| 2  | under \$100,000   | 70  |
| 3  | \$100,000-250,000 | 30  |
| 4  | \$100,000-250,000 | 50  |
| 5  | \$100,000-250,000 | 70  |
| 6  | above \$250,000   | 30  |
| 7  | above \$250,000   | 50  |
| 8  | above \$250,000   | 70  |

Next we select a model to generate the responses ([check available models and pricing](https://www.expectedparrot.com/getting-started/coop-pricing)):

```python theme={null}
from edsl import Model

m = Model("gemini-1.5-flash")
```

We can inspect (or modify) the default parameters of the model that will be used:

```python theme={null}
m
```

```python theme={null}
gemini-1.5-flash
```

|    | key                        | value            |
| :- | :------------------------- | :--------------- |
| 0  | model                      | gemini-1.5-flash |
| 1  | parameters:temperature     | 0.500000         |
| 2  | parameters:topP            | 1                |
| 3  | parameters:topK            | 1                |
| 4  | parameters:maxOutputTokens | 2048             |
| 5  | parameters:stopSequences   | \[]              |
| 6  | inference\_service         | google           |

We run the survey by adding any scenarios, agents and models and then calling the `run`:

```python theme={null}
results = survey.by(s).by(a).by(m).run()
```

We can inspect a list of the columns of the dataset of results that has been generated:

```python theme={null}
results.columns
```

|    | 0                                                                          |
| :- | :------------------------------------------------------------------------- |
| 0  | agent.age                                                                  |
| 1  | agent.agent\_index                                                         |
| 2  | agent.agent\_instruction                                                   |
| 3  | agent.agent\_name                                                          |
| 4  | agent.annual\_income                                                       |
| 5  | answer.amount                                                              |
| 6  | answer.next\_purchase                                                      |
| 7  | answer.recent\_purchase                                                    |
| 8  | cache\_keys.amount\_cache\_key                                             |
| 9  | cache\_keys.next\_purchase\_cache\_key                                     |
| 10 | cache\_keys.recent\_purchase\_cache\_key                                   |
| 11 | cache\_used.amount\_cache\_used                                            |
| 12 | cache\_used.next\_purchase\_cache\_used                                    |
| 13 | cache\_used.recent\_purchase\_cache\_used                                  |
| 14 | comment.amount\_comment                                                    |
| 15 | comment.next\_purchase\_comment                                            |
| 16 | comment.recent\_purchase\_comment                                          |
| 17 | generated\_tokens.amount\_generated\_tokens                                |
| 18 | generated\_tokens.next\_purchase\_generated\_tokens                        |
| 19 | generated\_tokens.recent\_purchase\_generated\_tokens                      |
| 20 | iteration.iteration                                                        |
| 21 | model.inference\_service                                                   |
| 22 | model.maxOutputTokens                                                      |
| 23 | model.model                                                                |
| 24 | model.model\_index                                                         |
| 25 | model.stopSequences                                                        |
| 26 | model.temperature                                                          |
| 27 | model.topK                                                                 |
| 28 | model.topP                                                                 |
| 29 | prompt.amount\_system\_prompt                                              |
| 30 | prompt.amount\_user\_prompt                                                |
| 31 | prompt.next\_purchase\_system\_prompt                                      |
| 32 | prompt.next\_purchase\_user\_prompt                                        |
| 33 | prompt.recent\_purchase\_system\_prompt                                    |
| 34 | prompt.recent\_purchase\_user\_prompt                                      |
| 35 | question\_options.amount\_question\_options                                |
| 36 | question\_options.next\_purchase\_question\_options                        |
| 37 | question\_options.recent\_purchase\_question\_options                      |
| 38 | question\_text.amount\_question\_text                                      |
| 39 | question\_text.next\_purchase\_question\_text                              |
| 40 | question\_text.recent\_purchase\_question\_text                            |
| 41 | question\_type.amount\_question\_type                                      |
| 42 | question\_type.next\_purchase\_question\_type                              |
| 43 | question\_type.recent\_purchase\_question\_type                            |
| 44 | raw\_model\_response.amount\_cost                                          |
| 45 | raw\_model\_response.amount\_input\_price\_per\_million\_tokens            |
| 46 | raw\_model\_response.amount\_input\_tokens                                 |
| 47 | raw\_model\_response.amount\_one\_usd\_buys                                |
| 48 | raw\_model\_response.amount\_output\_price\_per\_million\_tokens           |
| 49 | raw\_model\_response.amount\_output\_tokens                                |
| 50 | raw\_model\_response.amount\_raw\_model\_response                          |
| 51 | raw\_model\_response.next\_purchase\_cost                                  |
| 52 | raw\_model\_response.next\_purchase\_input\_price\_per\_million\_tokens    |
| 53 | raw\_model\_response.next\_purchase\_input\_tokens                         |
| 54 | raw\_model\_response.next\_purchase\_one\_usd\_buys                        |
| 55 | raw\_model\_response.next\_purchase\_output\_price\_per\_million\_tokens   |
| 56 | raw\_model\_response.next\_purchase\_output\_tokens                        |
| 57 | raw\_model\_response.next\_purchase\_raw\_model\_response                  |
| 58 | raw\_model\_response.recent\_purchase\_cost                                |
| 59 | raw\_model\_response.recent\_purchase\_input\_price\_per\_million\_tokens  |
| 60 | raw\_model\_response.recent\_purchase\_input\_tokens                       |
| 61 | raw\_model\_response.recent\_purchase\_one\_usd\_buys                      |
| 62 | raw\_model\_response.recent\_purchase\_output\_price\_per\_million\_tokens |
| 63 | raw\_model\_response.recent\_purchase\_output\_tokens                      |
| 64 | raw\_model\_response.recent\_purchase\_raw\_model\_response                |
| 65 | reasoning\_summary.amount\_reasoning\_summary                              |
| 66 | reasoning\_summary.next\_purchase\_reasoning\_summary                      |
| 67 | reasoning\_summary.recent\_purchase\_reasoning\_summary                    |
| 68 | scenario.item                                                              |
| 69 | scenario.scenario\_index                                                   |

We can select and inspect any components of the results. We can see by a “None” response that a question was skipped:

```python theme={null}
(
    results
    .sort_by("annual_income", "age", "item")
    .select("model", "annual_income", "age", "item", "recent_purchase", "amount", "next_purchase")
)
```

|    | model.model      | agent.annual\_income | agent.age | scenario.item | answer.recent\_purchase | answer.amount | answer.next\_purchase |
| :- | :--------------- | :------------------- | :-------- | :------------ | :---------------------- | :------------ | :-------------------- |
| 0  | gemini-1.5-flash | \$100,000-250,000    | 30        | electronics   | Yes                     | 2500.000000   | Within the next year  |
| 1  | gemini-1.5-flash | \$100,000-250,000    | 30        | phones        | Yes                     | 1200.000000   | Within the next year  |
| 2  | gemini-1.5-flash | \$100,000-250,000    | 50        | electronics   | Yes                     | 2500.000000   | Within the next year  |
| 3  | gemini-1.5-flash | \$100,000-250,000    | 50        | phones        | Yes                     | 1200.000000   | Within the next year  |
| 4  | gemini-1.5-flash | \$100,000-250,000    | 70        | electronics   | Yes                     | 500.000000    | Within the next year  |
| 5  | gemini-1.5-flash | \$100,000-250,000    | 70        | phones        | No                      | nan           | Within the next year  |
| 6  | gemini-1.5-flash | above \$250,000      | 30        | electronics   | Yes                     | 5000.000000   | Within the next year  |
| 7  | gemini-1.5-flash | above \$250,000      | 30        | phones        | Yes                     | 0.000000      | Within the next year  |
| 8  | gemini-1.5-flash | above \$250,000      | 50        | electronics   | Yes                     | 5000.000000   | Within the next year  |
| 9  | gemini-1.5-flash | above \$250,000      | 50        | phones        | Yes                     | 0.000000      | Within the next year  |
| 10 | gemini-1.5-flash | above \$250,000      | 70        | electronics   | Yes                     | 5000.000000   | Within the next year  |
| 11 | gemini-1.5-flash | above \$250,000      | 70        | phones        | No                      | nan           | Never                 |
| 12 | gemini-1.5-flash | under \$100,000      | 30        | electronics   | Yes                     | 250.000000    | Within the next year  |
| 13 | gemini-1.5-flash | under \$100,000      | 30        | phones        | No                      | nan           | Within the next year  |
| 14 | gemini-1.5-flash | under \$100,000      | 50        | electronics   | No                      | nan           | Within the next year  |
| 15 | gemini-1.5-flash | under \$100,000      | 50        | phones        | No                      | nan           | Within the next year  |
| 16 | gemini-1.5-flash | under \$100,000      | 70        | electronics   | No                      | nan           | Within the next year  |
| 17 | gemini-1.5-flash | under \$100,000      | 70        | phones        | No                      | nan           | Never                 |

## Example 2

In the next example, we use the same scenarios to create versions of the questions *before we combine them in a survey*. This allows us to add a skip rule based on a question/scenario combination, as opposed to skipping a question for all scenarios:

```python theme={null}
q1 = QuestionYesNo(
    question_name = "recent_purchase_`{{ scenario.item }}`",
    question_text = "In the last year have you or anyone in your household purchased any `{{ scenario.item }}`?",
)

q2 = QuestionNumerical(
    question_name = "amount_`{{ item }}`",
    question_text = "In the last year, how much did your household spend on `{{ scenario.item }}` (in USD)?"
)

q3 = QuestionMultipleChoice(
    question_name = "next_purchase_`{{ scenario.item }}`",
    question_text = "When do you next expect to purchase `{{ scenario.item }}`?",
    question_options = [
        "Never",
        "Within the next month",
        "Within the next year",
        "I do not know"
    ]
)
```

The `loop` method creates new versions of questions with scenarios already inserted:

```python theme={null}
questions = q1.loop(s) + q2.loop(s) + q3.loop(s)
questions
```

```python theme={null}
[Question('yes_no', question_name = """recent_purchase_electronics""", question_text = """In the last year have you or anyone in your household purchased any electronics?""", question_options = ['No', 'Yes']),
 Question('yes_no', question_name = """recent_purchase_phones""", question_text = """In the last year have you or anyone in your household purchased any phones?""", question_options = ['No', 'Yes']),
 Question('numerical', question_name = """amount_electronics""", question_text = """In the last year, how much did your household spend on electronics (in USD)?""", min_value = None, max_value = None),
 Question('numerical', question_name = """amount_phones""", question_text = """In the last year, how much did your household spend on phones (in USD)?""", min_value = None, max_value = None),
 Question('multiple_choice', question_name = """next_purchase_electronics""", question_text = """When do you next expect to purchase electronics?""", question_options = ['Never', 'Within the next month', 'Within the next year', 'I do not know']),
 Question('multiple_choice', question_name = """next_purchase_phones""", question_text = """When do you next expect to purchase phones?""", question_options = ['Never', 'Within the next month', 'Within the next year', 'I do not know'])]
```

We combine the questions in a survey to administer them together the same as before:

```python theme={null}
survey = Survey(questions)
```

Here we add different rules specifying that questions with one scenario (phones) should be administered or skipped based on the answer to a question with another scenario (electronics):

```python theme={null}
survey = (
    survey
    .add_skip_rule("recent_purchase_phones", "`{{ recent_purchase_electronics.answer }}` == 'No'")
    .add_skip_rule("amount_phones", "`{{ recent_purchase_electronics.answer }}` == 'No'")
    .add_skip_rule("next_purchase_phones", "`{{ recent_purchase_electronics.answer }}` == 'No'")
)
```

```python theme={null}
survey.show_flow()
```

<Frame>
  <img src="https://mintlify.s3.us-west-1.amazonaws.com/expectedparrot/images/en/notebook/skip_logic_2.png" alt="../_images/notebooks_skip_logic_scenarios_34_0.png" />
</Frame>

Here we run the survey with the scenarios, agents and model:

```python theme={null}
results = survey.by(a).by(m).run()
```

There is no “scenario” field in results because the scenarios were already added to questions. Instead, there are separate columns for each version of a question:

```python theme={null}
(
    results
    .sort_by("annual_income", "age")
    .select("model", "annual_income", "age", "recent_purchase_electronics", "amount_electronics", "next_purchase_electronics", "recent_purchase_phones", "amount_phones", "next_purchase_phones")
)
```

|    | model.model      | agent.annual\_income | agent.age | answer.recent\_purchase\_electronics | answer.amount\_electronics | answer.next\_purchase\_electronics | answer.recent\_purchase\_phones | answer.amount\_phones | answer.next\_purchase\_phones |
| :- | :--------------- | :------------------- | :-------- | :----------------------------------- | :------------------------- | :--------------------------------- | :------------------------------ | :-------------------- | :---------------------------- |
| 0  | gemini-1.5-flash | \$100,000-250,000    | 30        | Yes                                  | 2500                       | Within the next year               | Yes                             | 1200.000000           | Within the next year          |
| 1  | gemini-1.5-flash | \$100,000-250,000    | 50        | Yes                                  | 2500                       | Within the next year               | Yes                             | 1200.000000           | Within the next year          |
| 2  | gemini-1.5-flash | \$100,000-250,000    | 70        | Yes                                  | 500                        | Within the next year               | No                              | 0.000000              | Within the next year          |
| 3  | gemini-1.5-flash | above \$250,000      | 30        | Yes                                  | 5000                       | Within the next year               | Yes                             | 0.000000              | Within the next year          |
| 4  | gemini-1.5-flash | above \$250,000      | 50        | Yes                                  | 5000                       | Within the next year               | Yes                             | 0.000000              | Within the next year          |
| 5  | gemini-1.5-flash | above \$250,000      | 70        | Yes                                  | 5000                       | Within the next year               | No                              | 0.000000              | Never                         |
| 6  | gemini-1.5-flash | under \$100,000      | 30        | Yes                                  | 250                        | Within the next year               | No                              | 300.000000            | Within the next year          |
| 7  | gemini-1.5-flash | under \$100,000      | 50        | No                                   | 250                        | Within the next year               | nan                             | nan                   | nan                           |
| 8  | gemini-1.5-flash | under \$100,000      | 70        | No                                   | 0                          | Within the next year               | nan                             | nan                   | nan                           |

## Posting to Expected Parrot

Here we post this notebook to Expected Parrot, a free platform for creating and sharing AI-based research (learn more about [how it works](/en/latest/coop)):

```python theme={null}
from edsl import Notebook

nb = Notebook(path = "skip_logic_scenarios.ipynb")

nb.push(
    description = "Using skip logic with question scenarios",
    alias = "skip-logic-scenarios",
    visibility = "public"
)
```

```json theme={null}
{'description': 'Using skip logic with question scenarios',
 'object_type': 'notebook',
 'url': 'https://www.expectedparrot.com/content/d91d29a1-607e-4a98-a962-a662c7f94399',
 'alias_url': 'https://www.expectedparrot.com/content/RobinHorton/skip-logic-scenarios',
 'uuid': 'd91d29a1-607e-4a98-a962-a662c7f94399',
 'version': '0.1.62.dev1',
 'visibility': 'public'}
```
