Studio Matrx Monthly · Volume 1 · Issue 3 · August 2026
Amogh N P
 In loving memory of Amogh N P — Architect · Designer · Visionary 
Data Trees & Grasshopper LogicLesson 6.4
PSD for Architecture, Planning & Urban Design/Module 6 · Scripting Rhino & Grasshopper

Lesson 6.4 · Scripting Rhino & Grasshopper

Data Trees & Grasshopper Logic

The branching data structure at the heart of Grasshopper - how it differs from a Python list, and when code beats components for handling it

14 min Interactive lessonFree · open lessonByAmogh N P· Architect & interior designer
The hook

The data tree is the idea that makes Grasshopper powerful - and the one that makes newcomers cry. It is simpler than it looks.

Ask any Grasshopper user what confused them longest and most will say the same two words: data trees. Components quietly reshape your data into branching structures, and when two branches do not line up, results go wrong in baffling ways. It feels like magic gone hostile.

It is not magic. A data tree is just lists, grouped and labelled - a filing system for your data. This final lesson of the module demystifies it by holding it up against something you already know cold: the plain Python list. Once you see a tree as nested lists with addresses, flatten and graft stop being incantations and become obvious.

Tree = list of lists + addresses. Flatten = one list. Graft = one item each. Match the shapes.

A tree is lists inside a filing system

Start from what you know. A Python list is a flat, ordered run of items - [a, b, c, d] - one after another, no grouping. A Grasshopper data tree is a step up: it holds several lists at once, each kept in a separate branch, and every branch has an address called a path, written in braces like {0;0} or {1;2}.

So where a list is one line of items, a tree is a set of labelled lines:

{0;0}  ->  [a, b, c]
{0;1}  ->  [d, e]
{1;0}  ->  [f]

The whole point of the branches is that structure carries meaning. If each branch is one floor of a building, the tree keeps every floor's columns grouped and labelled by floor - lose the tree and you have one anonymous pile of columns with no idea which belongs where. In Python terms, a tree is closest to a list of lists (or a dictionary keyed by path), and that is exactly how you will handle it in code. The reason Grasshopper uses trees rather than flat lists everywhere is that design data is naturally grouped: panels per facade, rooms per floor, points per curve. The tree remembers the grouping; a flat list forgets it.

The path notation rewards a second look, because the semicolons are not decoration. A path like {0;1} describes a position in a hierarchy: think of it as folder 0, subfolder 1 - trees can nest several levels deep, just as a project nests building, floor, room. Most everyday work stays one or two levels deep, but knowing the path is an address with levels explains why some components add a level (grafting) and others remove one (flattening). When Grasshopper shows you {0;0}, {0;1}, {1;0} in a Panel, it is simply listing the addresses of the branches it is holding - read them like a table of contents for your data, and the tree stops feeling opaque.

DATA TREE vs PYTHON LISTDATA TREE (grouped by path){0;0}abc{0;1}de{1;0}fpaths keep groups apartPYTHON LIST (flat, ordered)abcdef[a, b, c, d, e, f]one run of items,grouping is goneFlatten a tree and you get the list. The hard part of GH scripting is keeping the paths you need.
Zoom
A Grasshopper data tree next to a Python list. The tree keeps items grouped in named branches by path - {0;0}, {0;1} - so structure survives; a flat Python list is just an ordered run of items with no grouping. Flattening a tree throws the grouping away; grafting puts each item in its own branch.

List = one run of items. Tree = many lists, each with an address {branch;index}. Structure = meaning.

Flatten, graft and the matching problem

Two operations reshape trees constantly, and understanding them cures most Grasshopper confusion. Flatten collapses every branch into a single one - it throws the grouping away and gives you one long list. Graft does the opposite - it pushes each item down into its own new branch, so a list of three becomes three branches of one.

Why does this matter so much? Because Grasshopper components match items between inputs by their tree structure. Give a Line component ten start points in one flat branch and ten end points in one flat branch, and it draws ten lines, pairing them in order. But give it ten start points flat and end points grafted into ten separate branches, and the matching goes haywire - you get a tangle, because the shapes do not correspond. The infamous moment when almost right but weirdly duplicated geometry appears is nearly always a flatten-versus-graft mismatch between two inputs.

The practical skill is to picture the tree shape of each input and make them correspond. Two inputs that should pair item-for-item need matching structures. When they do not line up, the fix is usually a Flatten, a Graft or a Simplify on one side - not more components, just the right reshaping. Seeing data trees is, more than anything, the Grasshopper skill.

A concrete example makes the matching rule stick. Suppose you have three rooms, each with its own list of furniture points, held as a tree of three branches, and you want to move each room's furniture by that room's own offset vector. If the points are a tree of three branches and the vectors are a flat list of three, the shapes do not correspond and the Move component will misbehave. Graft the three vectors so each sits in its own branch, and now branch {0} of points meets vector {0}, branch {1} meets vector {1}, and everything lines up. The lesson generalises: when you want per-group behaviour, make the group structures match. That single instinct - look at both trees, make them correspond - resolves the overwhelming majority of Grasshopper mysteries without a line of code.

FLATTEN vs GRAFTsource: {0} = [a, b] , {1} = [c]abcFLATTEN -> one branch{0}abcGRAFT -> one item each{0}a{1}b{2}cMatch the tree shapes of two inputs and components line up items correctly.
Zoom
Two everyday tree operations. Flatten collapses every branch into one, discarding the path structure; graft pushes each item down into its own new branch. Most Grasshopper matching bugs are really a flatten-versus-graft mismatch between two inputs.

Flatten = all one branch. Graft = each item its own branch. Mismatched shapes = wrong matching.

Reading and building trees in a script

When you set a script component input to Tree access (lesson 6.1), your code receives the whole structure as a DataTree object, and you can walk it branch by branch. The pattern is to loop over the tree's paths and pull each branch as a list:

python
# x set to Tree access -> x is a DataTree
totals = []
for i in range(x.BranchCount):
    branch = x.Branch(i)      # this branch as a list
    totals.append(sum(branch))
a = totals

That sums each branch independently - one total per floor, say - which is exactly the kind of per-group operation trees exist for. To build a tree to send back out, you create a DataTree and add items at chosen paths:

python
from Grasshopper import DataTree
from Grasshopper.Kernel.Data import GH_Path

tree = DataTree[object]()
for i, row in enumerate(grid):        # grid is a list of lists
    for value in row:
        tree.Add(value, GH_Path(i))   # branch i gets this row
a = tree

Here every sublist of grid becomes its own branch {i} - you are grafting-by-construction. This is the honest picture of trees in code: a DataTree on the Grasshopper side maps neatly onto a list of lists on the Python side, and Branch(i) and GH_Path(i) are how you cross between them. Most of the time you convert the tree to nested lists, do ordinary Python, and convert back.

That convert-work-convert rhythm is the single most useful habit for tree scripting, and it keeps your code readable. Rather than fighting DataTree methods throughout, many people write a tiny helper that turns a tree into a plain list of lists at the top of the script, do all their thinking in ordinary Python where every skill from Modules 2 and 3 applies, then rebuild a DataTree only at the end for the output. The enumerate in the build example is doing real work: it pairs each sublist with its index i, and that index becomes the branch path, so the output tree mirrors the structure of your Python nesting. Keep the two pictures aligned in your head - branch equals list, path equals index - and moving data across the boundary stops being fiddly.

DATA TREE vs PYTHON LISTDATA TREE (grouped by path){0;0}abc{0;1}de{1;0}fpaths keep groups apartPYTHON LIST (flat, ordered)abcdef[a, b, c, d, e, f]one run of items,grouping is goneFlatten a tree and you get the list. The hard part of GH scripting is keeping the paths you need.
Zoom
A Grasshopper data tree next to a Python list. The tree keeps items grouped in named branches by path - {0;0}, {0;1} - so structure survives; a flat Python list is just an ordered run of items with no grouping. Flattening a tree throws the grouping away; grafting puts each item in its own branch.

Tree access -> DataTree. x.Branch(i) = a list. Build back with DataTree + GH_Path(i).

When code beats components - and when it does not

So when should you drop into a script for tree work, and when should you stay visual? An honest guide, because both extremes waste time.

Code tends to win when the logic is genuinely complex: multi-level conditionals, iteration where each step depends on the last, custom matching rules, or reshaping that would take a long, unreadable chain of Tree components. In a script you can loop over branches, use ordinary Python if and for, and express the intent directly - a paragraph of code instead of twenty tangled components.

Components tend to win for the ordinary flow: standard reshaping (a single Flatten, Graft or Path Mapper), anything you want to see and tweak visually, and work others must read and edit - a wired definition is often more legible to a collaborator than a dense script. Components also give live preview at every step, which is invaluable while you are still finding the shape of a problem.

The mature answer, as in lesson 6.1, is both: reshape and flow with components, and drop a script component in for the one knotty branch of logic that code expresses cleanly. Avoid the two failure modes - a heroic all-visual contraption straining to do what four lines of Python would, and a monolithic script that swallows work the canvas shows more clearly. Match the tool to the stretch, and let the tree structure - not habit - decide.

There is also a middle path worth knowing: you do not have to choose script-or-components for a whole definition, and you rarely should. The best Grasshopper users move fluidly - a Path Mapper here, a two-line script there, a Graft on one input - treating code as one more component that happens to be very flexible. A useful test when you are stuck is to ask, would I rather read this as a picture or as a sentence? Reshaping and flow are pictures; conditional rules and iteration are sentences. Answer honestly for the stretch in front of you and the right tool usually names itself. Over a career, this judgement - not memorised syntax - is what makes someone quick and reliable in Grasshopper, and it is the note this module ends on.

FLATTEN vs GRAFTsource: {0} = [a, b] , {1} = [c]abcFLATTEN -> one branch{0}abcGRAFT -> one item each{0}a{1}b{2}cMatch the tree shapes of two inputs and components line up items correctly.
Zoom
Two everyday tree operations. Flatten collapses every branch into one, discarding the path structure; graft pushes each item down into its own new branch. Most Grasshopper matching bugs are really a flatten-versus-graft mismatch between two inputs.
Tree concepts and how they map to Python

branch

One list of items inside a data tree

Maps to a single Python list. x.Branch(i) hands you branch i as a list you can loop over.

path (GH_Path)

The address of a branch, like {0;1}

Just a label for a branch. Build one with GH_Path(i) when adding items to a DataTree in code.

flatten

Collapse all branches into one flat list

Equivalent to concatenating every sublist. Discards grouping - use when you truly want one list.

graft

Put each item into its own new branch

Wraps each item in its own list. The opposite of flatten; used to make structures correspond.

DataTree

The Grasshopper tree object in a script

Received with Tree access; built with DataTree() plus tree.Add(value, GH_Path(i)). Think list of lists.

Hands-on workshop

Workshop - per-branch totals with a tree

Build a script that receives a data tree of numbers - imagine areas grouped by floor - and returns one sum per branch, keeping the grouping. It cements Tree access, walking branches, and the tree-as-list-of-lists picture.

Rhino 8 with Grasshopper. Entwine or Merge to build the test tree. No external libraries.

Given & goal
Goal: turn a tree of numbers (one branch per floor) into one total per branch
Inputs: a data tree of numbers wired into a script input set to Tree access
Time: ~35 minutes
  1. 1Create a small data tree upstream: use an Entwine component, or several number lists merged, so you have (say) three branches with different counts of numbers - your stand-in for areas per floor.
  2. 2Drop a Python 3 script component, wire the tree into input x, then right-click x and set access to Tree. Confirm your code now receives a DataTree, not a flat list.
  3. 3Loop with for i in range(x.BranchCount):, pull each branch with branch = x.Branch(i), and compute sum(branch). Collect the totals into a Python list.
  4. 4Assign the list of totals to output a and check you get one number per branch. Compare against a visual approach: wire the same tree into a Mass Addition component and confirm the numbers match - proof your script and the components agree.
  5. 5Bonus: instead of returning a flat list, build a DataTree that puts each total back on its original path using GH_Path(i), so downstream components still know which floor each total came from.

You’ll walk away with
A Grasshopper Python 3 component, input set to Tree access, that returns one sum per branch of an incoming tree - optionally rebuilt as a tree so the per-floor grouping is preserved downstream.

The worked example

Three altitudes on the same idea

Read the band that fits you — or all three.

For the architectAutomate busywork & build custom tools

Trees are how Grasshopper keeps your project organised - panels per facade, columns per floor, rooms per level - and reading them is the skill that unlocks serious definitions. When a facade script mysteriously duplicates or drops panels, it is almost always a tree mismatch, and the fix is a Flatten or Graft, not more components. Learn to picture the branch structure of each input and the hardest part of Grasshopper becomes routine.

For the interior designerScripts for data, schedules & layouts

Even a modest layout definition uses trees the moment you group things - fixtures per room, tiles per surface, items per zone. You rarely need to build trees in code, but you do need to recognise when a Flatten or Graft will make two lists line up, so your lighting points actually match your ceiling regions. Understanding the list-versus-tree distinction turns Grasshopper from unpredictable to controllable for everyday interiors work.

For the studentA hireable computational skill

Data trees are the concept that separates people who dabble in Grasshopper from those who are fluent, and employers can tell the difference fast. Being able to explain a path, flatten versus graft, and when to reach for a Tree-access script marks you as genuinely computational. It maps directly onto the list-of-lists thinking from Module 2, so you already have the mental model - you are just learning Grasshopper's vocabulary for it.

Misconception check

Data trees are a weird Grasshopper thing with no relation to normal programming - I just have to memorise flatten and graft.

Rote-memorising the buttons is exactly why trees stay confusing. A data tree is not exotic: it is a list of lists with an address on each sublist - a structure you already met as nested lists in Module 2. A branch is a list; a path like {0;1} is just its label; flattening is concatenating all the sublists into one; grafting is wrapping each item in its own sublist. Once you map the Grasshopper vocabulary onto ordinary Python nesting, the operations stop being magic spells and become obvious data manipulations you can reason about. The people who stay stuck are the ones treating trees as buttons to memorise; the people who get fluent are the ones who see the nested-list structure underneath and think about it directly.
Try it

Do it yourself

Think in branches and paths.

  1. 1In plain Python terms, what everyday data structure is a data tree most like?
  2. 2What does Flatten do to a tree, and what does Graft do - describe each in one sentence.
  3. 3Two inputs to a component produce oddly duplicated geometry. What is the most likely cause and the usual fix?
  4. 4With an input set to Tree access, how do you get branch number i as a list you can sum?
  5. 5Give one situation where a script beats components for tree work, and one where components are the better choice.
Take this with you

The one line to carry out

A data tree is a list of lists with an address on each branch; flatten concatenates them, graft wraps each item alone, and most matching bugs are a shape mismatch between two inputs. See the nested structure and Grasshopper stops being magic.
Take it further
References & further reading

Peer-reviewed journals & authoritative standards

  1. 01Grasshopper 3DWikipedia, 2026.
  2. 02List (abstract data type)Wikipedia, 2026.
  3. 03Associative arrayWikipedia, 2026.
  4. 04Parametric designWikipedia, 2026.
Related lessons
Recap
A Grasshopper data tree is nested lists with addresses - each branch a list, each path a label - and it exists because design data is naturally grouped. Flatten collapses branches into one list; graft gives every item its own branch; and because components match inputs by tree structure, most baffling results are a flatten-versus-graft mismatch. In a script, Tree access hands you a DataTree you walk with Branch(i) and rebuild with GH_Path(i) - a structure that maps cleanly onto Python lists of lists. Use code for knotty tree logic, components for ordinary flow, and both together for real work.
Carry forward →

That completes scripting Rhino and Grasshopper - from the script component to geometry to the trees that organise it. Next module moves to the other giant of design software: Python inside Revit and Dynamo, where the same skills meet BIM data and the Revit API.

A

The author

Amogh N P

Architect, interior designer, and creative polymath. Studio Matrx began in his notebooks — his vision of design made honest, useful, and open to everyone. Its Academy is written and taught in his memory, and free, forever.

More about Amogh →