Day 1 · Python basics & your first LLM call

Day 1 — Linguistic Data Analysis II

How to use this notebook

This is your single submission for the day. It has two parts:

  • Part A · Tutorial — a hands-on Python primer, then your first call to a language model.
  • Part B · Corpus Lab — short Python exercises you complete and self-check.

You only edit the cells marked ✏️ YOU EDIT. Cells marked 🔧 Library cell are pre-written — run them, don’t change them.

➡️ Work top to bottom. When you’re done, Runtime → Run all, then File → Download → Download .ipynb and submit that file.

Part A · Tutorial — a 15-minute Python primer

No prior Python needed. Run each cell, read the output, then change a value and re-run to see what happens. Everything this week is built from these few ideas.

1. Variables & data types

A variable is a name for a value. Python’s core types you’ll use all week: string (str, text), integer (int) and float (decimal number), list (an ordered sequence), and dictionary (dict, key → value pairs).

Show code
sentence = "The cat sat on the mat."   # str  — text, in quotes
word_count = 6                          # int  — whole number
score = 4.5                             # float — decimal number
levels = ["A1", "A2", "B1"]            # list — ordered, square brackets
item = {"id": 1, "text": sentence, "label": "A1"}  # dict — key: value

print(type(sentence), type(word_count), type(score))
print("levels[0] =", levels[0])         # lists are indexed from 0
print("item['label'] =", item["label"])  # look up a dict value by its key

2. if statements — make a decision

Run one branch or another depending on a condition. Indentation (4 spaces) is how Python groups the lines that belong to each branch.

Show code
score = 4.5
if score < 4:
    band = "Low"
elif score < 7:
    band = "Mid"
else:
    band = "High"
print("score", score, "→ band", band)

3. for loops — do something to every item

Loop over a list, or over a dictionary’s items. This is exactly how we’ll process every sentence in a dataset.

Show code
for level in levels:
    print("level:", level)

print("---")
for key, value in item.items():
    print(key, "→", value)

4. Functions — name a reusable piece of code

A function takes inputs (arguments) and returns a result. Define once, call as often as you like.

Show code
def band_of(score):
    """Turn a numeric score into a Low/Mid/High band."""
    if score < 4:
        return "Low"
    elif score < 7:
        return "Mid"
    return "High"

print(band_of(2), band_of(5), band_of(9))

5. Basic input / output

print(...) shows a value. To read data, we mostly parse JSON — the {id, text, label} shape you’ll see all week. (input() also reads from the keyboard, but it pauses Run all, so we avoid it here.)

Show code
raw = '[{"id": 1, "text": "Hello.", "label": "A1"}, {"id": 2, "text": "Nevertheless, the findings were inconclusive.", "label": "C1"}]'
data = json.loads(raw)          # JSON text → Python list of dicts
print("number of items:", len(data))
for row in data:
    print(row["id"], row["label"], "|", row["text"])

# (Reading from the keyboard — left commented so Run all does not pause:)
# name = input("Your name: ")
# print("Hello,", name)

6. Meet the model — your first LLM call

Run the setup cell (loads the LLM backend — in Colab that’s the free built-in Gemini, no key needed), then send the model a prompt with generate_text(...).

Show code
#@title 📦 Setup — run me first { display-mode: "form" }
# Imports + the LLM backend. No pip install needed in Colab.
import json, re, urllib.request, os
from sklearn.metrics import classification_report, confusion_matrix
import pandas as pd, seaborn as sns, matplotlib.pyplot as plt

MODEL_ID = "gemini-2.5-flash"   # pinned model for the reproducible (API) backend

def _resolve_gemini_key():
    """Find a Gemini API key: Colab Secrets first (not auto-exported to env), then env."""
    try:
        from google.colab import userdata      # only exists in Colab
        key = userdata.get("GEMINI_API_KEY")
        if key:
            return key
    except Exception:
        pass                                    # not in Colab, or secret not set
    return os.environ.get("GEMINI_API_KEY")

def _make_api_backend(key):
    """Reproducible backend: Gemini API with temperature=0 + a fixed seed."""
    from google import genai
    from google.genai import types
    client = genai.Client(api_key=key)
    cfg = types.GenerateContentConfig(temperature=0, seed=42)
    return (lambda p: client.models.generate_content(
                model=MODEL_ID, contents=p, config=cfg).text,
            f"Gemini API ({MODEL_ID}, temperature=0, seed=42)")

# Prefer the API key when set (reproducible); else fall back to colab.ai (demo).
_key = _resolve_gemini_key()
if _key:
    generate_text, _backend = _make_api_backend(_key)
else:
    try:
        from google.colab import ai            # Colab's built-in Gemini — no key
        generate_text, _backend = (lambda p: ai.generate_text(p)), "Colab Gemini (demo, non-reproducible)"
    except ImportError:
        raise RuntimeError(
            "No LLM backend found. Run this notebook in Google Colab (free built-in "
            "Gemini, no key needed), or set GEMINI_API_KEY — in Colab via the Secrets "
            "panel, or as an environment variable when running locally. "
            "See resources/tools/gemini-api-key.md.")
print(f"Setup done. LLM backend: {_backend}. scikit-learn ready.")

✏️ YOU EDIT — change the prompt and re-run.

Show code
reply = generate_text("In one sentence, what is applied linguistics?")
print(reply)

Part B · Corpus Lab — Python practice

Fill in each function so it does what its docstring says (replace the raise NotImplementedError(...) line). Then run the self-check cell at the bottom until every line prints ✅. No grader needed — the checks are your grader.

Show code
# ✏️ YOU EDIT — replace each NotImplementedError with your code.

def label_of(item):
    """Return the value stored under the key "label" in the dict `item`.
    Example: label_of({"id": 1, "text": "Hi", "label": "A1"}) -> "A1".
    """
    raise NotImplementedError("Return item['label'].")


def long_words(words, n):
    """Return a LIST of the words whose length is greater than n.
    Example: long_words(["a", "cat", "elephant"], 3) -> ["elephant"].
    """
    # HINT: build a result list; loop with `for w in words:`; keep w if len(w) > n.
    raise NotImplementedError("Return the words longer than n characters.")


def count_labels(items):
    """Given a list of {id, text, label} dicts, return a dict mapping each
    label to how many times it appears.
    Example: count_labels([{"label":"A1"}, {"label":"A1"}, {"label":"B1"}])
             -> {"A1": 2, "B1": 1}.
    """
    # HINT: start with counts = {}; for each item, add 1 to counts[label]
    #       (use counts.get(label, 0) + 1 so the first time starts at 0).
    raise NotImplementedError("Count how many items carry each label.")
Show code
#@title 🔎 Self-check — run me { display-mode: "form" }
sample = [{"id": 1, "text": "Hi.", "label": "A1"},
          {"id": 2, "text": "Hello there.", "label": "A1"},
          {"id": 3, "text": "Nevertheless...", "label": "C1"}]
checks = [
    ("label_of", label_of(sample[0]) == "A1"),
    ("long_words", long_words(["a", "cat", "elephant"], 3) == ["elephant"]),
    ("count_labels", count_labels(sample) == {"A1": 2, "C1": 1}),
]
for name, ok in checks:
    print(("✅" if ok else "❌"), name)
print("All passed ✅" if all(ok for _, ok in checks)
      else "Some checks failed — fix them and re-run.")

✅ Before you submit

  1. Runtime → Run all and check every cell ran without error.
  2. Part A outputs are visible (tables / charts / the model’s answers).
  3. Part B self-check prints ✅ (or your TODO answers are filled in).
  4. File → Download → Download .ipynb and upload that one file.