Tutorial 17: Data Structures And Probability
Introduction
In this tutorial, we look at using the itable object for data storage, data transformation and histogram tracking. The itable object can be drawn into, or can be set using various formulae. We will also look at using the uzi object to quickly generate many bang
messages or streams of ascending numbers, both of which can be used to quickly create lots of algorithmically generated data.
A two-dimensional data structure (like itable) gives us the opportunity to create data transformations that are easy to set up. These transfer functions allow you to create irregular curves, probability tables that weigh the output based on the input, and random patterns that are easy to visualize. Additionally, the introduction of the uzi object for the quick generation of procedural messages helps us find a way to create the functions necessary to load these itable objects in interesting ways.
The itable object used in this tutorial is virtually equivalent to the table object, which has its own window and can be typed into a regular object box. The itable is used here simply because it appears embedded visually in a patcher, rather than in a separate editor window.
To open the tutorial patch please open 17mDataStructuresAndProbability.maxpat
from the the zip archive, which is available for download at the top of this page.
Basic data structures with itable
In this tutorial's patcher, you will see a few display/drawing patches (labeled with numbers) and several small calculations (labeled with letters). We will start working with the patch area labeled 1. This patch features an itable object; an object that provides a visual interface to a two-dimensional data structure called a table. The table / itable structure gives us an X/Y combination and a simple interface to this data: send in an number that represents an index on the X axis, and you will receive the corresponding Y axis value.
When you first open the patch, the itable contains a random distribution of values. If we move the slider at the top of the patch, it scans the itable values, and outputs the Y value from its left outlet for the X position represented by the slider. We have this connected to a number box and slider, so we can see the results of the transfer function described by the itable. Next to the input slider are a pair of message boxes with two-integer lists; these will set the location of the first integer (X) to the value of the second integer (Y). Thus, with the first message box, the value at location 50
will be set to 80
. If we click on this message box, then move the slider (or number box) to 50
, we can see that the output of the itable is 80
. Likewise, after we click on the second message box (100 20
), we can see that an input value of 100
outputs a value of 20
.
The clear
message “erases” the itable view, but values are still intrinsic to the itable; when cleared, the itable simply assigns a 0
value to every possible data point. Another way to change the values of the itable is to draw within the itable itself. If you place the mouse inside the itable and click-drag the mouse, you can draw in the function contents you desire. If you need to make straight lines, you can hold down the shift key while you move the mouse, and click the mouse to commit the new line. Using these drawing commands gives you an easy way to manually create interesting transfer functions. (Note however, that there is only one Y value for any X. You cannot draw a line that overlaps itself vertically.)
Below patch 1 are three small patches that utilize uzi objects to generate all of the points in the itable. If you click on the Abutton, a function is created where the input value is the same as the output value. Clicking on B produces the opposite: input values ranging from 0-127
are converted into a 127-0
range. The Cbutton produces a random distribution similar to the starting contents of the itable object.
Creating formulas with uzi
Let’s look more closely at these function-generating routines, and particularly at the uzi object. The uzi object, as its name implies, is made to rapidly fire off bang
messages. A single bang
to an uzi object’s input will generate as many bang
messages as are specified in the object's argument; therefore, uzi128
will send out 128``bang
messages from the left outlet. In order to keep track of the current bang
number, the rightmost outlet produces a count (starting with 1
) – this output is what we are using for the index to the itable object. In patch segment A, this count is reduced by 1
(so that it begins with 0
, the start of the itable range), and used for both the X and Y values, giving us the straight line output for a function.
The B segment is similar, except that the Y value uses the !- object to subtract the incoming value from 127
. This results in a similar ramp, but heading in the opposite direction – the lowest X values have the highest Y values. The C segment uses a random object to create random values as part of the output list. It has to use the swap object (which simply "swaps" the left and right values it receives) to reverse the order of the list contents so that the random object's output is used for the Y value instead of the X value.
Let’s alter the B segment a little to see how different functions could be created. If we change the !- object to be a division (/) object with an argument of 4
, we will reduce the range of the Y values to only 1/4th of the vertical range. Likewise, if we multiply (*) the value by 2
, we will hit the Y range maximum halfway through the X values. Obviously, with a little manipulation, you can use uzi and a little math to create many complex functions.
Histograms with table
The right-hand patch area (labeled 2) provides an interesting use of the itable system, as well as several formula patches and a new object called histo. The histo object performs a histogram function: it keeps a count of how many times a number has come into its input. Whenever a value comes into the input, the object produces the number of times it has been received out its right outlet followed immediately by the value itself out its left outlet - it can then be fed directly into the itable object, where the left inlet becomes the X and the right inlet becomes the Y. If you use the slider at the top of the patch to “scratch” in a lot of numbers, you will see the itable object show the relative distribution of the values you have sent. If you want to clear the itable and the histo objects, you can hit the return key on your keyboard (ASCII 13
).
Create probability distributions
As with our first drawing patch, we have a number of smaller patch areas that can be used to generate functions into the itable object. However, instead of drawing discrete functions, these generate fuzzy distributions by sending random and semi-random values into the histo object; this histogram is then fed to the itable to generate a probability map. Segments D and E use random and drunk, respectively, to generate the values. Clicking on the attached button objects repeatedly will show how each affects the distribution curve – random hits all of the values fairly evenly, but drunk is much more lumpy in its distribution. Again, we can clear the histogram at any time with the return key.
- Open the
More_ops
subpatch
Patch segments F and G take a somewhat different approach. Segment F generates two random numbers, then uses the minimum object to choose the lower of the two numbers. This will tend to emphasize the lower numbers in the range; repeatedly clicking on the connected button will show the lower numbers being used more often than the higher values. Segment G does the opposite: it uses the higher of two random numbers by putting them into the maximum object. Thus, repeatedly clicking on the connected button will tend to generate more high numbers.
Finally, segment H creates an average of 3 random numbers by generating the numbers, adding them together, then dividing the sum by 3
. If you clear the itable and repeatedly hit the Hbutton, you will see a “bell-shaped” curve being generated, since the average of three random numbers will tend to be in the middle of the range. This is a simple type of Gaussian (or normal) distribution, and is commonly found in statistical research when most subjects fall within the middle of a range of possible values.
Using the probability distribution
Turn on the metro object with the toggle at the top of patch 2; this will cause bang
messages to be sent to the itable object. This produces a random “quantile” output to be generated – a random but weighted output value from the itable object. The result is that the lcd area below the itable will begin drawing small circles where the density is roughly equivalent to the distribution curve shown in the itable routine. If you want to clear the lcd, you can hit the space bar (ASCII 32
) to restart drawing. (The Y
location of every dort is random, so the proability curve shows in the left to right density.)
Leaving the metro running while using the right side uzi function generators should cause the drawing to simulate the distribution curves in the itable object. To test more radical curves, you can draw sharp lines within the itable object to force certain output curves; you can also use the slider to “shade” in parts of the equation, causing the drawing output to shade in segments in response to the new distribution curve.
Summary
Many different processes depend on the ability to create functions that transform an input range into an output range. In previous tutorials, we saw how the scale object could do this for us. However, if we want to manually change the distribution of values, or if we want to use a formula for data transformation, we need to use a more complex system. As we’ve seen in this tutorial, the table / itable objects serve this purpose perfectly, giving us the ability to either manufacture or manually draw a transfer function.
Using the histo object, we also saw how we can generate the sort of distribution curves that serve as common random techniques in generative art and stochastic music. Whether the fuzziness of true random generation, or the tendencies found with more discrete math, these curves can be used to both generate and guide results to a desired outcome.