Skip to content

Instantly share code, notes, and snippets.

@aparrish
Last active July 18, 2023 20:00
Show Gist options
  • Star 26 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save aparrish/73c19a36b9cdcf604d04e95020418cd4 to your computer and use it in GitHub Desktop.
Save aparrish/73c19a36b9cdcf604d04e95020418cd4 to your computer and use it in GitHub Desktop.
Tracery and Python. Code examples released under CC0 https://creativecommons.org/choose/zero/, other text released under CC BY 4.0 https://creativecommons.org/licenses/by/4.0/
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Working with Tracery in Python\n",
"\n",
"By [Allison Parrish](http://www.decontextualize.com)\n",
"\n",
"This tutorial shows you how to use [Tracery](http://tracery.io) in your Python programs. In particular, it shows a handful of useful patterns for incorporating large amounts of data into your Tracery grammars that would be impractical or inconvenient to do with a Tracery generator on its own.\n",
"\n",
"Tracery is an easy-to-use but powerful language and toolset for generating text from grammars made by [Kate Compton](http://www.galaxykate.com/). If you're not familiar with how Tracery works, try [the official tutorial](http://www.crystalcodepalace.com/traceryTut.html) or [this tutorial I wrote](http://air.decontextualize.com/tracery/).\n",
"\n",
"This tutorial is written for Python 2.7, but the code should work on Python 3+ with minimal modification."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Simple example\n",
"\n",
"In order to generate text from a Tracery grammar in Python, you'll need to install the [Tracery Python module](https://pypi.python.org/pypi/tracery). It's easiest to do this with `pip` at the command line, like so:\n",
"\n",
" pip install tracery\n",
" \n",
"(If you get a permissions error, try `pip install --user tracery`.)\n",
"\n",
"Once you've installed the `tracery` module, try the following example program:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hey, solar system!\n"
]
}
],
"source": [
"import tracery\n",
"from tracery.modifiers import base_english\n",
"\n",
"# put your grammar here as the value assigned to \"rules\"\n",
"rules = {\n",
" \"origin\": \"#hello.capitalize#, #location#!\",\n",
" \"hello\": [\"hello\", \"greetings\", \"howdy\", \"hey\"],\n",
" \"location\": [\"world\", \"solar system\", \"galaxy\", \"universe\"]\n",
"}\n",
"\n",
"grammar = tracery.Grammar(rules) # create a grammar object from the rules\n",
"grammar.add_modifiers(base_english) # add pre-programmed modifiers\n",
"print grammar.flatten(\"#origin#\") # and flatten, starting with origin rule"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This program takes a Tracery grammar (in the form of a Python dictionary) and \"flattens\" it, printing its output to standard output. You can take the content of a Tracery grammar you've written and paste it into this Python program as the value being assigned to the variable `rules` (unless your Tracery grammar uses some aspect of JSON formatting that works differently in Python, like Unicode escapes). Run the program from the command line (or in the cell above, if you're viewing this in Jupyter Notebook) and you'll get a line of output from your grammar."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Reading a Tracery grammar from a JSON file\n",
"\n",
"You may already have a set of Tracery grammar files that you want to generate from, or don't want to cut-and-paste the grammar into your Python script. If this is the case, no problem! You can use Python's `json` library to load any file containing a Tracery grammar from a JSON file. The program below shows how this works.\n",
"\n",
"To get the code to run, put the following into a file called `test-grammar.json` in the same directory as your Python interpreter:\n",
"\n",
" {\n",
" \"origin\": \"#hello.capitalize#, #location#!\",\n",
" \"hello\": [\"hello\", \"greetings\", \"howdy\", \"hey\"],\n",
" \"location\": [\"world\", \"solar system\", \"galaxy\", \"universe\"]\n",
" }\n",
" \n",
"Python's `json` module provides functions for reading JSON-formatted data into Python as Python data structures, and exporting Python data structures to JSON format. The `.loads()` function from the module parses a string containing JSON-formatted data and returns the corresponding Python data structure (a dictionary or a list)."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hello, universe!\n",
"Howdy, world!\n",
"Hello, galaxy!\n",
"Hello, world!\n",
"Greetings, galaxy!\n",
"Hey, universe!\n",
"Hello, universe!\n",
"Hey, world!\n",
"Howdy, universe!\n",
"Hey, solar system!\n"
]
}
],
"source": [
"import tracery\n",
"from tracery.modifiers import base_english\n",
"import json\n",
"\n",
"# use json.loads() and open() to read in a JSON file as a Python data structure\n",
"rules = json.loads(open(\"test-grammar.json\").read())\n",
"\n",
"grammar = tracery.Grammar(rules)\n",
"grammar.add_modifiers(base_english)\n",
"\n",
"# print ten random outputs\n",
"for i in range(10):\n",
" print grammar.flatten(\"#origin#\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The above example uses a `for` loop to call the `.flatten()` method multiple times."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Using external data in Tracery rules\n",
"\n",
"An interesting affordance of using Tracery in Python is the ability to *fill in* parts of the grammar using external data. By \"external\" data, what I mean is data that isn't directly in the grammar itself, but data that you insert into the grammar when your program runs. One reason to do this might be to make the output of your grammar dynamic, using (e.g.) data returned from a web API. A simpler reason is simply that large Tracery grammars can be difficult to edit and navigate, especially when you're working with rules that might have hundreds or thousands of possible replacements.\n",
"\n",
"To demonstrate, let's start with the generator discussed in [my Tracery tutorial](http://air.decontextualize.com/tracery/) that generates takes on the \"Dammit Jim, I'm a doctor, not a `OTHER PROFESSION`\" snowclone/trope. Such a grammar might look like this:\n",
"\n",
" {\n",
" \"origin\": \"#interjection#, #name#! I'm a #profession#, not a #profession#!\",\n",
" \"interjection\": [\"alas\", \"congratulations\", \"eureka\", \"fiddlesticks\",\n",
" \"good grief\", \"hallelujah\", \"oops\", \"rats\", \"thanks\", \"whoa\", \"yes\"],\n",
" \"name\": [\"Jim\", \"John\", \"Tom\", \"Steve\", \"Kevin\", \"Gary\", \"George\", \"Larry\"],\n",
" \"profession\": [\n",
" \"accountant\",\n",
" \"butcher\",\n",
" \"economist\",\n",
" \"forest fire prevention specialist\",\n",
" \"mathematician\",\n",
" \"proofreader\",\n",
" \"singer\",\n",
" \"teacher assistant\",\n",
" \"travel agent\",\n",
" \"welder\"\n",
" ]\n",
" }\n",
" \n",
"An immediately recognizable shortcoming of this grammar is that it doesn't have a large number of alternatives. If we want there to be more professions that, dammit Jim, I'm not, we need to type them into the grammar by hand. The selection of names is also woefully small.\n",
"\n",
"It would be nice if we could *supplement* the grammar by adding rule expansions from existing databases. For example, [Corpora](https://github.com/dariusk/corpora/) has [a list of occupations](https://raw.githubusercontent.com/dariusk/corpora/master/data/humans/occupations.json) and [a list of first names](https://raw.githubusercontent.com/dariusk/corpora/master/data/humans/firstNames.json), which we could incorporate into our grammar. One way to do this would be simply to copy/paste the relevant part of the JSON file into the grammar. But we can also load the data directly *into* the grammar using Python.\n",
"\n",
"The program in the following cell specifies a *partial* Tracery grammar in a Python dictionary assigned to variable `rules`. The grammar is then augmented with data loaded from JSON files obtained from Corpora. Using the `json` library, we load the Corpora Project JSON files, find the data we need, and then assign it to new rules in the grammar. To get the example to work, make sure to download [firstNames.json](https://raw.githubusercontent.com/dariusk/corpora/master/data/humans/firstNames.json) and [occupations.json](https://raw.githubusercontent.com/dariusk/corpora/master/data/humans/occupations.json) and put them in the same directory as your Python code.\n",
"\n",
"The key trick here is that when creating the grammar, we refer to rules that don't yet exist. Later in the code, we add those rules (and their associated expansions, from the Corpora Project JSON files) by assigning values to keys in the `rules` dictionary. We're essentially building the grammar up gradually over the course of the program, instead of writing it all at once."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Whoa, Joseph! I'm a textile cutting machine setter, not a literacy teacher!\n"
]
}
],
"source": [
"import tracery\n",
"from tracery.modifiers import base_english\n",
"import json\n",
"\n",
"# the grammar refers to \"name\" and \"profession\" rules. we're not including them in the grammar\n",
"# here, but adding them later on (using corpora project data!)\n",
"rules = {\n",
" \"origin\": \"#interjection.capitalize#, #name#! I'm #profession.a#, not #profession.a#!\",\n",
" \"interjection\": [\"alas\", \"congratulations\", \"eureka\", \"fiddlesticks\",\n",
" \"good grief\", \"hallelujah\", \"oops\", \"rats\", \"thanks\", \"whoa\", \"yes\"],\n",
"}\n",
"\n",
"# load the JSON data from files downloaded from corpora project\n",
"names_data = json.loads(open(\"firstNames.json\").read())\n",
"occupation_data = json.loads(open(\"occupations.json\").read())\n",
"\n",
"# set the values for \"name\" and \"profession\" rules with corpora data\n",
"rules[\"name\"] = names_data[\"firstNames\"]\n",
"rules[\"profession\"] = occupation_data[\"occupations\"]\n",
"\n",
"# generate!\n",
"grammar = tracery.Grammar(rules)\n",
"grammar.add_modifiers(base_english)\n",
"print grammar.flatten(\"#origin#\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> EXERCISE: Write a Tracery grammar that changes its output based on the current time of day. You'll need to use something like [`datetime`](https://docs.python.org/2.7/library/datetime.html#datetime.datetime.now) for this; after you've imported it, the expression `datetime.datetime.now().hour` evaluates to the current hour of the day."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Doing a Tracery \"mail merge\" with CSV data\n",
"\n",
"In [my CSV tutorial](https://gist.github.com/aparrish/f8e7eab47542678a39a39dddbca4ec2f), the final example shows how you might build sentences from data in a CSV file (in particular, a CSV exported from [this spreadsheet](https://docs.google.com/spreadsheets/d/1SmxsgSAcNqYahUXa9-XpecTg-fhy_Ko_-RMD3oT2Ukw/edit?usp=sharing) containing data about the dogs of NYC, originally from [here](https://project.wnyc.org/dogs-of-nyc/)). The method chosen in that example for constructing sentences is suboptimal: there's a lot of just slamming strings together with the `+` operator, which makes it hard to build in variation. It would be nice if we could build a Tracery grammar for generating these sentences instead!\n",
"\n",
"The following example does exactly this. As with the example in the previous section, this example constructs a *partial* Tracery grammar, and then adds rules to the grammar with new information. The difference with this example is that we generate a sentence for multiple data sets—instead of loading in data once at the beginning. For each row in the CSV file, we create a fresh copy of the grammar, then add rule/expansion pairs with the relevant data from the row. Inside the `for` loop, we construct a new grammar object and print the \"flattened\" (i.e. expanded) text."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Woof! Manhattan is where I call home and they call me Buddy. I'm an Afghan Hound and I've got a brindle coat.\n",
"Howdy! My name is Nicole. My coat is black and I'm the friendliest Afghan Hound you'll ever meet. I live in Manhattan. Ruff-ruff!\n",
"Howdy! Yip! My name is Abby and I'm the friendliest Afghan Hound you'll ever meet. I love living in Manhattan and I've got a black coat.\n",
"Woof! Manhattan is where I call home and they call me Chloe. I'm an Afghan Hound and my coat is white.\n",
"Howdy! Woof! My name is Jazzle and I'm an Afghan Hound. I love living in Manhattan and I've got a blond coat.\n",
"Hi! My name is Trouble. My coat is blond and my breed is Afghan Hound. I live in the Bronx. Arf!\n",
"Yip! Manhattan is where I call home and they call me Grace. My breed is Afghan Hound and I've got a cream coat.\n",
"Hi! Bow-wow! My name is Sisu and my breed is Afghan Hound. I'm from Manhattan and my coat is black.\n",
"Hi! My name is Jakie. You could say my coat is white and I'm an Afghan Hound. I love living in Queens. Yip!\n",
"Ruff-ruff! The Bronx is where I call home and they call me Geo. I'm an Afghan Hound and my coat is orange.\n",
"Howdy! My name is Ginger. I've got a tan coat and I'm the cleverest Afghan Hound you'll ever meet. I'm from the Bronx. Ruff-ruff!\n",
"Hi! My name is Misty. My coat is tan and I'm the most playful Afghan Hound you'll ever meet. I live in the Bronx. Ruff-ruff!\n",
"Hello! Ruff-ruff! My name is Troy and I'm an Afghan Hound. Staten Island is where I call home and I've got a blond coat.\n",
"Hey! My name is Nick. You could say my coat is black and I'm the cleverest Afghan Hound you'll ever meet. I live in Queens. Bow-wow!\n",
"Howdy! My name is Prince. My coat is tan and I'm an Afghan Hound. I'm from Queens. Yip!\n",
"Howdy! My name is KIKU-NO-HM. I've got a gray coat and I'm the cutest Akita you'll ever meet. I love living in Manhattan. Arf!\n",
"Hey! Bow-wow! My name is Sasha and I'm the most loyal Akita you'll ever meet. I live in Manhattan and I've got a white coat.\n",
"Howdy! My name is Bernie. I've got a white coat and I'm the cutest Akita you'll ever meet. I'm from Queens. Bow-wow!\n",
"Yip! The Bronx is where I call home and they call me Coffee. I'm the most playful Akita you'll ever meet and I've got a brown coat.\n",
"Hi! Bow-wow! My name is Elsa and my breed is Akita. I love living in Brooklyn and my coat is tan.\n",
"Hey! My name is Jason. I've got a black coat and I'm the strongest Akita you'll ever meet. I live in Queens. Arf!\n",
"Howdy! My name is Socrates. My coat is white and my breed is Akita. I'm from Queens. Ruff-ruff!\n",
"Hi! Bow-wow! My name is Suki and I'm the cutest Akita you'll ever meet. I love living in Staten Island and you could say my coat is rust.\n",
"Bow-wow! I live in Staten Island and they call me Angel. My breed is Akita and you could say my coat is rust.\n",
"Bow-wow! I'm from Brooklyn and they call me Aspen. I'm an Akita and my coat is brown.\n",
"Woof! Queens is where I call home and they call me Bear. I'm an Akita and you could say my coat is black.\n",
"Bow-wow! Queens is where I call home and they call me Buster. I'm the most playful Akita you'll ever meet and my coat is white.\n",
"Arf! I love living in Queens and they call me CHIYO-KYRA. I'm an Akita and you could say my coat is brindle.\n",
"Hey! My name is Chula. I've got a white coat and my breed is Akita. I'm from Queens. Yip!\n",
"Hi! My name is Kita. I've got a rust coat and my breed is Akita. I'm from Brooklyn. Yip!\n",
"Howdy! Arf! My name is Nicki and I'm an Akita. The Bronx is where I call home and my coat is black.\n",
"Hey! My name is Nikita. I've got a white coat and my breed is Akita. I'm from Queens. Woof!\n",
"Hello! Yip! My name is Ralph and I'm the most playful Akita you'll ever meet. I'm from Queens and you could say my coat is tan.\n",
"Howdy! My name is Rambo. My coat is tan and my breed is Akita. I'm from Queens. Yip!\n",
"Arf! I'm from Brooklyn and they call me Shoko. I'm the most playful Akita you'll ever meet and I've got a tan coat.\n",
"Hey! Woof! My name is Bear and my breed is Akita. I love living in the Bronx and you could say my coat is blond.\n",
"Ruff-ruff! I live in Queens and they call me DARIUS. I'm an Akita and you could say my coat is white.\n",
"Howdy! Bow-wow! My name is Lady and my breed is Akita. Queens is where I call home and I've got a tan coat.\n",
"Hi! Woof! My name is Oreo and I'm an Akita. I'm from Staten Island and you could say my coat is black.\n",
"Yip! I live in Queens and they call me Prince. My breed is Akita and you could say my coat is blond.\n",
"Woof! I live in Manhattan and they call me Sabastian. I'm an Akita and I've got a white coat.\n",
"Ruff-ruff! I love living in the Bronx and they call me NICKO. I'm the most playful Akita you'll ever meet and I've got a tan coat.\n",
"Ruff-ruff! I love living in the Bronx and they call me AKIRO. I'm the cleverest Akita you'll ever meet and you could say my coat is tan.\n",
"Hey! My name is Annie. My coat is white and I'm the friendliest Akita you'll ever meet. Manhattan is where I call home. Ruff-ruff!\n",
"Woof! I live in the Bronx and they call me King. My breed is Akita and my coat is brown.\n",
"Hi! Yip! My name is SCOOBYDORA and my breed is Akita. I live in Queens and I've got a brown coat.\n",
"Yip! I'm from Manhattan and they call me TENSHI. My breed is Akita and my coat is tan.\n",
"Woof! I love living in the Bronx and they call me Yuki. My breed is Akita and my coat is black.\n",
"Hey! Yip! My name is Grizz and I'm the strongest Akita you'll ever meet. I live in Manhattan and you could say my coat is rust.\n",
"Hi! Ruff-ruff! My name is Bear and my breed is Akita. Manhattan is where I call home and my coat is white.\n",
"Howdy! Yip! My name is Sukiyaki and I'm an Akita. I live in Manhattan and you could say my coat is brindle.\n",
"Hi! My name is Amy. You could say my coat is brown and my breed is Akita. Manhattan is where I call home. Woof!\n",
"Hello! My name is Asia. You could say my coat is black and I'm the strongest Akita you'll ever meet. I live in the Bronx. Bow-wow!\n",
"Hey! My name is Laki. I've got a black coat and I'm the cutest Akita you'll ever meet. I live in Queens. Bow-wow!\n",
"Bow-wow! Manhattan is where I call home and they call me Lucy. I'm an Akita and I've got a white coat.\n",
"Yip! I'm from Brooklyn and they call me Luna. I'm an Akita and you could say my coat is white.\n",
"Hello! My name is Mugs. I've got a tan coat and my breed is Akita. I love living in Staten Island. Yip!\n",
"Arf! I'm from Queens and they call me Nicki. I'm an Akita and my coat is white.\n",
"Yip! Queens is where I call home and they call me PUKI. I'm the cleverest Akita you'll ever meet and you could say my coat is black.\n",
"Hey! Ruff-ruff! My name is Star and I'm the cleverest Akita you'll ever meet. I love living in Queens and you could say my coat is white.\n",
"Ruff-ruff! I'm from Manhattan and they call me Sydney. I'm the most playful Akita you'll ever meet and my coat is brown.\n",
"Ruff-ruff! The Bronx is where I call home and they call me Yoshi. I'm an Akita and my coat is brindle.\n",
"Hello! Woof! My name is Babe and I'm the cleverest Akita you'll ever meet. I'm from Queens and my coat is white.\n",
"Howdy! Yip! My name is HEAVENSENT and I'm an Akita. I live in Queens and my coat is brown.\n",
"Hello! My name is Kobi. My coat is brindle and I'm the cleverest Akita you'll ever meet. I live in the Bronx. Ruff-ruff!\n",
"Hey! Woof! My name is Nikita and my breed is Akita. I live in Manhattan and my coat is brindle.\n",
"Howdy! Ruff-ruff! My name is Rocky and I'm an Akita. I'm from Queens and you could say my coat is white.\n",
"Arf! I love living in Brooklyn and they call me Romeo. My breed is Akita and my coat is brindle.\n",
"Hello! My name is Tara. I've got a tan coat and my breed is Akita. I live in Manhattan. Yip!\n",
"Woof! Queens is where I call home and they call me Tara. I'm an Akita and my coat is white.\n",
"Hey! My name is Tasha. You could say my coat is brown and I'm an Akita. I love living in Queens. Ruff-ruff!\n",
"Hello! Ruff-ruff! My name is Bella and I'm the strongest Akita you'll ever meet. Staten Island is where I call home and you could say my coat is black.\n",
"Hello! My name is Yoji. My coat is black and I'm an Akita. Queens is where I call home. Bow-wow!\n",
"Bow-wow! I live in Queens and they call me Tyson. My breed is Akita and my coat is white.\n",
"Woof! I live in Staten Island and they call me Cookie. My breed is Akita and I've got a black coat.\n",
"Yip! Queens is where I call home and they call me Jax. My breed is Akita and I've got a black coat.\n",
"Ruff-ruff! I'm from Brooklyn and they call me KELBY. I'm an Akita and my coat is black.\n",
"Hey! Arf! My name is Kiko and I'm the most loyal Akita you'll ever meet. I live in Staten Island and you could say my coat is brown.\n",
"Arf! I'm from Manhattan and they call me n/a. I'm the most playful Akita you'll ever meet and I've got a blond coat.\n",
"Ruff-ruff! Brooklyn is where I call home and they call me Snowball. I'm the cleverest Akita you'll ever meet and I've got a white coat.\n",
"Ruff-ruff! The Bronx is where I call home and they call me Bogie. I'm the strongest Akita you'll ever meet and you could say my coat is black.\n",
"Ruff-ruff! I live in Queens and they call me WON. My breed is Akita and my coat is white.\n",
"Howdy! My name is Ginger. I've got a white coat and my breed is Akita. Brooklyn is where I call home. Bow-wow!\n",
"Hello! My name is Bear. My coat is brindle and I'm the cutest Akita you'll ever meet. I'm from Brooklyn. Woof!\n",
"Yip! I live in the Bronx and they call me Buddy. I'm an Akita and I've got a black coat.\n",
"Hello! My name is Coquito. I've got a tan coat and I'm an Akita. I'm from the Bronx. Yip!\n",
"Howdy! My name is HENESSEY. You could say my coat is black and I'm an Akita. I'm from Brooklyn. Arf!\n",
"Hello! Ruff-ruff! My name is KICHI and my breed is Akita. I love living in Brooklyn and you could say my coat is brown.\n",
"Arf! Queens is where I call home and they call me Yogi. I'm an Akita and my coat is white.\n",
"Woof! I'm from Queens and they call me Brandy. I'm the strongest Akita you'll ever meet and my coat is tan.\n",
"Hello! My name is Chester. You could say my coat is black and my breed is Akita. I live in Queens. Woof!\n",
"Hi! Ruff-ruff! My name is Jazzy and my breed is Akita. I'm from Queens and you could say my coat is blond.\n",
"Hey! My name is Seven. You could say my coat is blond and I'm the cutest Akita you'll ever meet. Queens is where I call home. Yip!\n",
"Hey! My name is KIRA-A. I've got a rust coat and I'm the strongest Akita you'll ever meet. I'm from Brooklyn. Woof!\n",
"Hi! My name is Layla. You could say my coat is black and I'm the most playful Akita you'll ever meet. I love living in Queens. Arf!\n",
"Yip! I live in Brooklyn and they call me Charlie. My breed is Akita and my coat is brindle.\n",
"Bow-wow! Brooklyn is where I call home and they call me Reggie. I'm the cleverest Akita you'll ever meet and my coat is rust.\n",
"Bow-wow! Staten Island is where I call home and they call me Rocco. I'm an Akita and I've got a silver coat.\n",
"Arf! I live in the Bronx and they call me Saki. I'm the friendliest Akita you'll ever meet and my coat is white.\n",
"Hey! My name is Stella. I've got a white coat and my breed is Akita. Brooklyn is where I call home. Woof!\n"
]
}
],
"source": [
"import tracery\n",
"from tracery.modifiers import base_english\n",
"import json\n",
"import csv\n",
"\n",
"# create the \"template\" grammar, which will be copied and augmented for each record of the CSV\n",
"rules = {\n",
" \"origin\": [\n",
" \"#greeting.capitalize#! My name is #name#. #coatdesc.capitalize# and #breeddesc#. #homedesc#. #woof.capitalize#!\",\n",
" \"#greeting.capitalize#! #woof.capitalize#! My name is #name# and #breeddesc#. #homedesc.capitalize# and #coatdesc#.\",\n",
" \"#woof.capitalize#! #homedesc.capitalize# and they call me #name#. #breeddesc.capitalize# and #coatdesc#.\"\n",
" ],\n",
" \"greeting\": [\"hi\", \"howdy\", \"hello\", \"hey\"],\n",
" \"woof\": [\"woof\", \"arf\", \"bow-wow\", \"yip\", \"ruff-ruff\"],\n",
" \"coatdesc\": [\n",
" \"my coat is #color#\",\n",
" \"I've got #color.a# coat\",\n",
" \"you could say my coat is #color#\"\n",
" ],\n",
" \"breeddesc\": [\n",
" \"I'm #breed.a#\",\n",
" \"my breed is #breed#\",\n",
" \"I'm the #superlative# #breed# you'll ever meet\"\n",
" ],\n",
" \"superlative\": [\"cutest\", \"strongest\", \"most playful\", \"friendliest\", \"cleverest\", \"most loyal\"],\n",
" \"homedesc\": [\n",
" \"I'm from #borough#\",\n",
" \"I live in #borough#\",\n",
" \"I love living in #borough#\",\n",
" \"#borough# is where I call home\"\n",
" ]\n",
"}\n",
"\n",
"# iterate over the first 100 rows in the CSV file\n",
"for row in list(csv.DictReader(open(\"dogs-of-nyc.csv\")))[:100]:\n",
" # copy rules so we're not continuously overwriting values\n",
" rules_copy = dict(rules) # make a copy of the rules\n",
" \n",
" # now assign new rule/expansion pairs with the data from the current row\n",
" rules_copy[\"name\"] = row[\"dog_name\"]\n",
" rules_copy[\"color\"] = row[\"dominant_color\"].lower()\n",
" rules_copy[\"breed\"] = row[\"breed\"]\n",
" # little bit of fluency clean-up...\n",
" if row[\"borough\"] == \"Bronx\":\n",
" rules_copy[\"borough\"] = \"the \" + row[\"borough\"]\n",
" else:\n",
" rules_copy[\"borough\"] = row[\"borough\"]\n",
"\n",
" # now generate!\n",
" grammar = tracery.Grammar(rules_copy)\n",
" grammar.add_modifiers(base_english)\n",
" print grammar.flatten(\"#origin#\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As you can see, this technique allows us to combine the expressive strengths of Tracery-based text generation with Python's CSV parser to generate simple \"stories\" from spreadsheet data. Pretty neat!"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment