Basis

Deze pagina bevat uitvoerbare code.

Wat een tekst over zichzelf zegt, korter

Opdracht: Wat een tekst over zichzelf zegt, korter

De gegeven code uit de opgave:

# Dit notebook staat in solutions/ en de teksten in problems/assets/teksten/,
# vandaar het pad terug. In de opgave zelf is dat "assets/teksten/".
TEKSTEN = "../problems/assets/teksten/"
LEESTEKENS = ".,!?;:"


def read_text(filename):
    """Leest een heel bestand en geeft de inhoud terug als één string."""
    with open(filename) as file:
        return file.read()


def word_list(text):
    """Geeft de woorden van text in kleine letters, zonder leestekens."""
    schoon = ""

    for teken in text.lower():
        if teken in LEESTEKENS:
            schoon = schoon + " "
        else:
            schoon = schoon + teken

    return schoon.split()

Stap 1: long_words(words, n)

def long_words(words, n):
    """Geeft de woorden uit words die langer zijn dan n letters."""
    return [word for word in words if len(word) > n]


assert long_words(["de", "vuurtoren", "draait"], 5) == ["vuurtoren", "draait"]
assert long_words([], 5) == []
assert long_words(word_list(read_text(TEKSTEN + "vuurtoren.txt")), 11) == [
    "zesenvijftig",
    "tweehonderdzesendertig",
    "vuurtorenwachter",
    "geschiedenis",
    "tegenwoordig",
]

Stap 2: count_words(words)

def count_words(words):
    """Telt hoe vaak elk woord voorkomt."""
    # Een lus: die loopt de woorden één keer langs en werkt per stap één
    # telling bij. De comprehension loopt voor elk woord de hele lijst opnieuw
    # langs.
    counts = {}

    for word in words:
        counts[word] = counts.get(word, 0) + 1

    return counts


assert count_words(["spam", "spam", "taart"]) == {"spam": 2, "taart": 1}
assert count_words([]) == {}
assert count_words(word_list(read_text(TEKSTEN + "kort.txt")))["taarten"] == 3

De dict comprehension geeft dezelfde dictionary, maar words.count(word) loopt de hele lijst langs, en dat gebeurt voor elk verschillend woord opnieuw. Voor vuurtoren.txt zijn dat 134 rondes door 227 woorden, tegen één ronde voor de lus. .get uit week 1 maakt de lus korter; de if ... else uit week 1 is net zo goed.

Stap 3: words_used_once(counts)

def words_used_once(counts):
    """Telt hoeveel woorden precies één keer voorkomen."""
    return sum([1 for count in counts.values() if count == 1])


assert words_used_once({"spam": 2, "taart": 1}) == 1
assert words_used_once({}) == 0
assert (
    words_used_once(count_words(word_list(read_text(TEKSTEN + "vuurtoren.txt")))) == 94
)

counts.values() geeft alleen de aantallen, want de woorden doen hier niet mee. len([count for count in counts.values() if count == 1]) telt ook, en is net zo goed.

Stap 4: ngram_loop(word, n)

def ngram_loop(word, n):
    """Geeft de overlappende stukjes van n letters van word, op volgorde."""
    result = []

    for i in range(len(word) - n + 1):
        result.append(word[i : i + n])

    return result


def ngram(word, n):
    """Geeft de overlappende stukjes van n letters van word, op volgorde."""
    return [word[i : i + n] for i in range(len(word) - n + 1)]


assert ngram_loop("toren", 2) == ["to", "or", "re", "en"]
assert ngram_loop("toren", 5) == ["toren"]
assert ngram_loop("toren", 6) == []
for word in ["", "a", "vuurtoren"]:
    for n in [1, 2, 3]:
        assert ngram_loop(word, n) == ngram(word, n)

De vierkante haken worden result = [] en return result, het deel na for wordt de lus, en wat vooraan stond, wordt de append. Is n groter dan het woord, dan is len(word) - n + 1 nul of negatief; range levert dan niets op, en beide versies geven [].

Stap 5: count_table(counts)

def count_table(counts):
    """Geeft de telling als lijst van lijsten [woord, aantal]."""
    return [[word, count] for word, count in counts.items()]


assert count_table({"spam": 2, "taart": 1}) == [["spam", 2], ["taart", 1]]
assert count_table({}) == []
assert count_table(count_words(word_list(read_text(TEKSTEN + "vuurtoren.txt"))))[0] == [
    "de",
    8,
]

Een lijst van lijsten met één for: wat erin komt, is zelf een lijst.

Stap 6: share(table, n_words, minimum)

def share(table, n_words, minimum):
    """Geeft [woord, percentage] voor de woorden die minstens minimum keer voorkomen."""
    return [[word, 100 * count // n_words] for word, count in table if count >= minimum]


assert share([["de", 3], ["toren", 1]], 4, 2) == [["de", 75]]
assert share([], 4, 2) == []
words = word_list(read_text(TEKSTEN + "kort.txt"))
assert share(count_table(count_words(words)), len(words), 3) == [
    ["ik", 15],
    ["taarten", 15],
    ["en", 15],
]

De tabel wordt uitgepakt in word en count. Het filter achteraan laat de zeldzame woorden weg, en vooraan komt een nieuwe lijst met het percentage in plaats van het aantal. // rondt naar beneden af, dus 100 * 3 // 20 is 15.