This tutorial is part of the skills labs within Interactive Data Science and Visualization.

AI Advanced (Knowledge)

Other formats: Markdown version

Using advanced AI techniques for data visualization from your browser including llms.txt, markdown task files, and iterative prompting.
Contents

Setup

For this tutorial, we will work entirely from the browser. Make sure to have the web version of your assistant ready to go and use the Sketchingpy online sketchbook.

Importing Knowledge

First, let's tell our AI assistant about the latest version of Sketchingpy which we will use to perform the drawing.

Most AI assistants can easily work with llms.txt just by giving a web path to where the file or files can be found. Generally both an llms-full.txt which is a longer but self-contained document is available along with llms.txt which is more of an index which can be used to find other information. We will stick with llms-full.txt for now! That in mind, let's go ahead and tell our assistant to pull in that information:

Hello! We will complete a series of tasks together using Sketchingpy. Please read https://sketchingpy.org/llms-full.txt?v=20250915 before we start so that you know how to use the library.

Note that some assistants might have limited internet access. If that's the case, you can download llms-full.txt files and add them as attachments to your chat. For more information, see the Sketchingpy llms documentation.

Scaffolding

Alright we should be ready to take our first step which is just to sketch out the outlines of the code we want to have draw this visualization. This will let us dictate the code's architecture and then fill it in progressively through subsequent tasks.

That objective in sight, consider that many AI assistants use Markdown as a way to converse with the user and to internally represent conversation. Indeed, agent files are just markdown! Anyway, this is a lightweight way to structure text where # means a top level header, ## means a sub-header, ** means bold, the tick character means code, and - means a list. There are resources to learn how to use Markdown with its many features but we will try to stick to that for now. Specifically, we will make Markdown files each with an individual task to be completed by our AI assistant.

All that in mind, let's make a new text file called 01_scaffold.md. Then read and place the following inside:

# Table presenter Please create a class TablePresenter which takes in a sketch and which has stubbed methods and make docstrings for the given description (please use Google-style docstrings). Please have these simply throw NotImplementedError as we will fill them in as we go along. Please wrap at 80 characters. `__init__(self, sketch: sketchingpy.Sketch2D, data: list[dict])` Create a presenter to draw a visualization of the given data into the given sketch where records in the data look like {"year": 1980, "wolves": 50, "moose": 664}. `draw(self)` Draw the table with inline numbers with bars below that text. This will make a "tornado" chart which has one series (wolves) going left and the other (moose) going right. `_draw_top(self)` Draw the top row in black text for Year, Wolves, and Moose. `_draw_bottom(self)` Draw the bottom scale showing 0 to 500 for wolves and 0 to 1800 for moose. `_draw_body(self)` Draw year, wolves, and moose: - Year is on the left-most column centered. - Wolves is on the next column right aligned with bars going left. - Moose is on the far right column with left aligned but bars going right. `_get_y_pos(self, year: int) -> float` Get the y position corresponding to a year which is to be used in all series. `_get_length(self, series: str, count: int) -> float` Get the length of the bar to show for a series (wolves, moose) in pixels. Error thrown if the series is unknown or unsupported. `_get_color(self, series: str) -> str` Get the hex code to use for a given series (wolves, moose, year). Error thrown if the series is unknown or unsupported. `_get_max(self, series: str) -> float` Get the maximum count for a series (wolves, moose, year). Error thrown if the series is unknown or unsupported.

Attach your markdown file as an attachment to your chat and ask your assistant to complete that task.

Great! Next, please read and complete the attached task file. Please prepare a Python script.

When done, take the Python code generated and double check it! Maybe confirm that, even if it doesn't do anything yet, that the script runs and finishes without error. This is a similar cycle of iteration we saw in our earlier experiments with AI! A NotImplementedError may be anticipated.

Data

Alright we have our scaffold here, let's bring in our data. Following the same flow, let's make a 02_data.md:

# Load data In this next step, we should gather the required data and give our sketch access to that file. ## Gather data Please write a `wolvesMoose.csv` which contains the table at https://www.nps.gov/isro/learn/nature/wolf-moose-populations.htm with the columns year, wolves, and moose. ## Add to script Next, I would like to update our script to create a sketch that is 400 pixels wide by 650 pixels tall. Let's have this cleared with a background of color #FFFFFF. Let's then use the data layer to load the file `wolvesMoose.csv` which we will plan to pass into the class we just constructed along with the sketch. Please initialize an instance of our presenter and call its draw method before calling show on the sketch. We do not need interactivity on this sketch, it should just write to a `wolvesMoose.png` file. Please leave our class stubbed except in the constructor where we should save data and sketch as private instance variables. Please leave all of this in one script file. ## Notes If needed, prior task markdown files are present in this directory like `01_scaffold.md`.

As before, make sure to review the output and run the script. Note that, if your agent cannot access the information from the internet, you can copy from that table into spreadsheet software and provide it as a CSV directly.

Draw Outlines

Now that we have gotten through so much of that set up, we are in a really good position to start doing some drawing. Let's make a 03_draw_outline.md:

# Draw outline Let's draw the axes for our sketch. Let's not use push / pop style or transform. This should be able to run just to draw the top and bottom of the sketch. ## Define constants At the top of the file after imports, let's define some constants: - Let's move our min and max values for wolves and moose to constants `MIN_WOLVES` (0), `MAX_WOLVES` (50), `MIN_MOOSE` (0), and `MAX_MOOSE` (2400). - Let's also define the colors we want to use as `BACKGROUND_COLOR` (#F0F0F0), `AXIS_COLOR` (#333333), `WOLVES_COLOR` (#D95F02), and `MOOSE_COLOR` (#7570B3). - Let's also use the `Public Sans` font (`FONT`) with `AXIS_SIZE` as 15px (15) and `BODY_SIZE` as 13px (13). - Let's also create some common spacing where the center of the first column for year is at x coordinate of 55px (`YEAR_X = 55`), the right align target for wolves is x coordinate of 245px (`WOLVES_X = 245`) and the align target for moose is 255px (`MOOSE_X = 255`). The width of the year column should be 50px (`YEAR_WIDTH`) and columns for wolves and moose should be 140px wide (but no constant needed since we will use the value inline). - Let's also use `WIDTH` and `HEIGHT` for sketch dimensions. - Let's say that the header and footer height (`AXIS_HEIGHT`) is 30px. Please group the constants together by similar function and sort alphabetically within the groups. ## Draw Next, let's go ahead and have `draw` start with calling `_draw_top` and `_draw_bottom`. Let's then take care of drawing both the top and bottom. ## Draw Top Let's then implement `_draw_top` to use axis color and to draw the text Year, Wolves, Moose at the center, right, and left coordinate for those three columns at 50px y coordinate from the top (with 20px reserved for title). Let's set those back vertically by 5px and have a horizontal line in axis color drawn spanning the extent of the column for each. Please use `AXIS_SIZE` for all text in this section. Text should use fill and no stroke while line should use no fill and stroke. _Note_: We should have text aligned to bottom 45px from the top and the line at 50px from the top of the sketch. ## Draw Bottom Let's then implement `_draw_bottom` to use axis color to draw a horizontal line 30px y from the bottom spanning the length of each of the three columns. Below the wolves and moose columns let's draw the min and max for each with: - 0 at the far right for wolves and max at the far left of the column span - 0 at the far left for moose and max at the far right of the column span Please use `BODY_SIZE` for text in the bottom. Text should use fill and no stroke (clear_stroke) while line should use no fill and stroke. Please draw the text color using the series specific color but use `AXIS_COLOR` for year. To do this, please implement `_get_color` and use the series name to get the color to use. ## Notes Please use `AXIS_HEIGHT` in both draw bottom and draw top. Also, if needed, prior task markdown files are present in this directory like `01_scaffold.md`. Please use the font at http://mooc.interactivedatascience.courses/support/web/PublicSans- Regular.otf.

Please execute this and check the result using your chat-based workflow. Don't forget that your assistant can also help with edits like if something feels too cramped or not quite right! For example:

The 1980 seems to start too far down. Can you have the years start maybe 15 pixels higher?

Draw Years

Next, we can start filling in some of the elements from the data themselves, starting with the years. Let's make a 04_draw_years.md:

# Draw years Next I would like to draw the years as the first part of _draw_series. ## Method Let's loop through the data we saved and get that year's y coordinate can be found by using _get_y_pos which should do a linear scaling from the top of the sketch to the bottom: `top_line + padding + (year - MIN_YEAR) / (MAX_YEAR - MIN_YEAR) * (bottom_line - top_line - 2 * padding)` where `top_line = 50`, `bottom_line = HEIGHT - AXIS_HEIGHT`, and `padding = 15`. Let's add MIN_YEAR and MAX_YEAR to represent that we are going from 1979 to 2019. ## Notes If needed, prior task markdown files are present in this directory like `01_scaffold.md`.

As before, give this a shot by attaching the file and continuing your chat.

Draw Body

Alright let's get the rest of the data visible. In 05_draw_body.md:

# Draw body Next, I would like to implement `_get_length` and support drawing the labels and bars for the other two columns in `draw`. ## Method Let's start with `_get_length`. This should determine how wide the bar should be in pixels based on if the data point is for wolves or for moose. This should be a linear scale similar to our year scale but without the extra padding. Next, let's have the count for the species drawn at the y position for year and right aligned for wolves / left aligned for moose but in the color corresponding to the series. This will create the "tornado" that makes it easier to see one species trading off for the other. This should happen 5 pixels above the y position with bottom alignment. Then, at the y position itself, we should draw the rectangle using rect mode of corner where the rectangle is 3 pixels tall. ## Notes If needed, prior task markdown files are present in this directory like `01_scaffold.md`.

Continue and we should have a pretty good visualization! However, there are a few things we might want to tighten up since we have so much control over drawing. Once more, you might be seeing some things to improve and you can ask for help like:

I think we are almost there... the text is a bit high off of the corresponding bar. Can you bring them closer together and make the text right above the bars like 2px smaller (ensure the year text still uses the original size)? Let's also shift the bar down just maybe 2 pixels.

Finishing Touches

Finishing off this iterative process, let's highlight the max values from both series and clean up the title / labeling. Consider this 06_finish.md:

# Finish To finish off the visualization, we will add highlighting and clean up on spacing / labels. ## Highlighting Let's add a dictionary called `self._observed_maxes` which has entries for wolves and moose. Let's use the `max` function over a `map` to get the max observed value for each: `max(map(lambda x: int(x), map(lambda x: x['Wolves'], data)))`. Let's then, while iterating in `_draw_body` see if the value for the series matches the maximum value for the series and, if it is, let's have the fill for that text for just that data point for just that series alone be `AXIS_COLOR`. ## Spacing / labels Let's use 18px `AXIS_COLOR` text at the top of the sketch that says "Isle Royale National Park" as the title, drawn at y position 21 with center and bottom alignment. Let's also ensure the year labels are bottom aligned and 5 pixels above the y position for the year. ## Notes If needed, prior task markdown files are present in this directory like `01_scaffold.md`.

Take a look at the result and, if anything needs tweaks, be sure to ask your assistant to resolve anything final or use this as a chance to modify the code directly!

Reflection

This tutorial offers some techniques to engage AI on difficult or long tasks where you can break up elements into individual steps and then ask your assistant to work through those steps. This co-iteration with AI remains essential so that you can retain an active hand in ensuring a positive overall outcome, guiding assistants in their work as they go. Indeed, thanks to things like llms.txt, you can also invite assistants into tasks where they can use the latest information available.

Granted, what we discussed today may not necessarily be required for each task:

Consider these as valuable ingredients as you craft your engagement with AI. However, as with all things in engineering, you have to decide what is best for the task at hand.

Citations