Studio Matrx Monthly · Volume 1 · Issue 3 · August 2026
Amogh N P
 In loving memory of Amogh N P — Architect · Designer · Visionary 
Tabular Data with pandasLesson 4.1
PSD for Architecture, Planning & Urban Design/Module 4 · Working with Design Data

Lesson 4.1 · Working with Design Data

Tabular Data with pandas

The spreadsheet you can program - meet the DataFrame, the single object that holds a whole table and does work on it a column at a time

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

You already live in tables - schedules, BOQs, FF&E lists. pandas is the tool that lets you talk to a whole table at once, in code.

Open almost any deliverable in a practice and you find a table: a door schedule, a room area statement, a bill of quantities, a finishes list. You edit these by hand, cell by cell, and the work scales with the number of rows.

pandas is the library that makes a table a single, programmable object. Instead of clicking through 400 rows, you describe what you want - every room over 15 square metres, the total cost by floor - and pandas does it to the whole table in one line. This lesson introduces the two objects everything rests on, the DataFrame and the Series, and the everyday moves: read a file, look at it, and pick out exactly the rows and columns you need.

DataFrame = table object. Series = one column. read -> look (head/info) -> select. Think whole-column.

Meet pandas - the spreadsheet you can script

pandas is a free Python library for working with tables. If you have ever used a spreadsheet, you already understand the shape of the data it holds: rows and columns, with named headers across the top. What pandas adds is programmability. A spreadsheet makes you point and click at cells; pandas lets you describe an operation once and apply it to an entire column, or the whole table, in a single line - and then run that same instruction on next month's file without changing anything.

That difference matters most exactly where design work hurts: repetition and scale. Filtering a 12-row sample by hand is trivial; filtering, summing and re-sorting a 900-line BOQ every time the quantities change is not. pandas does both with the same effort, which is the leverage this whole course keeps returning to.

pandas is not part of Python's standard library, so you install it once. If you followed Module 3, the pattern is familiar:

python
# in a terminal, once per machine
# pip install pandas openpyxl

import pandas as pd  # the near-universal nickname

print(pd.__version__)

The import pandas as pd line is a convention so strong that nearly every example online uses pd; follow it and your code will read like everyone else's. openpyxl is the helper pandas uses to read and write Excel files - we install it now so Excel just works later. With that one import, the whole toolbox this module builds on is available.

It is worth being honest about where pandas fits. It is not a replacement for design thinking, and it is not always the right tool - a quick edit to a dozen rows is faster in a spreadsheet, and you should just do that. What pandas gives you is a way to make the same data operation once and trust it to run identically on a hundred rows or a hundred thousand, today and next month. That trustworthy repetition is the whole reason this module exists, and everything else - cleaning, scheduling, charting - is built on the humble import you just typed.

A DATAFRAME IS A TABLEcolumns (named, typed fields)indexroomarea_sqmfinish0living24.0oak1kitchen12.5tile2bedroom15.0carpetindex labelsOne row is one record; one column is a Series. The whole grid is a single DataFrame object.
Zoom
A pandas DataFrame drawn as a labelled table. Every column carries a name and a type; every row carries an index label. Ask for data by meaning - the room column, the area column - rather than by counting cells, and pandas keeps the whole table as a single object you can operate on.

The DataFrame and the Series

pandas gives you two objects, and almost everything you do is one of them. A DataFrame is the whole table: rows and columns, with a name on every column and an index label on every row. A Series is a single column pulled out of that table - one named strip of values, still carrying the same row index. Picture a room schedule: the entire schedule is a DataFrame; the area_sqm column on its own is a Series.

You can build a small DataFrame by hand to see the shape, which is handy for testing before you touch a real file:

python
import pandas as pd

rooms = pd.DataFrame({
    "room":     ["living", "kitchen", "bedroom"],
    "area_sqm": [24.0, 12.5, 15.0],
    "finish":   ["oak", "tile", "carpet"],
})
print(rooms)

Each key in that dictionary becomes a column; the lists become the values down each column. pandas prints it as a neat grid with an automatic index (0, 1, 2) down the left. Two things distinguish this from a plain list of lists. First, columns have names, so you ask for data by meaning (rooms["area_sqm"]) rather than by counting positions. Second, columns have types - pandas knows area_sqm holds numbers and finish holds text, and it will do arithmetic on the numbers and refuse it on the text, catching mistakes early. A DataFrame is, in short, a table that knows what it contains.

The row index deserves a word too, because it quietly makes pandas powerful. Every row carries a label - by default the integers 0, 1, 2, but it could be a room code, a drawing number, or a date - and that label travels with the data through every operation. When you pull out a column as a Series, it keeps its index; when you filter, the surviving rows keep their original labels rather than being renumbered. This means two Series drawn from the same table stay aligned by label, so rooms["length_m"] * rooms["width_m"] multiplies the right length by the right width for each room automatically. You rarely think about the index directly, but it is the machinery that lets you treat a table as one coherent object rather than a loose bag of columns.

A DATAFRAME IS A TABLEcolumns (named, typed fields)indexroomarea_sqmfinish0living24.0oak1kitchen12.5tile2bedroom15.0carpetindex labelsOne row is one record; one column is a Series. The whole grid is a single DataFrame object.
Zoom
A pandas DataFrame drawn as a labelled table. Every column carries a name and a type; every row carries an index label. Ask for data by meaning - the room column, the area column - rather than by counting cells, and pandas keeps the whole table as a single object you can operate on.

DataFrame = the whole table. Series = one column of it, still carrying the row index.

Reading a file - and looking at it first

In real work you rarely type the data; you read it from a file someone sent you. pandas reads the common formats with one function each, and hands you back a DataFrame:

python
import pandas as pd

schedule = pd.read_csv("room_schedule.csv")     # a CSV export
# schedule = pd.read_excel("boq.xlsx", sheet_name="Rooms")  # an Excel sheet

That is genuinely the whole job of loading data - read_csv for comma-separated files, read_excel for spreadsheets, each returning a DataFrame you can work on. Before doing anything else, though, look at what you loaded. Real files surprise you: extra header rows, blank lines, a stray text value in a number column. Three methods are your first reflex every time:

python
schedule.head()      # the first 5 rows - a quick eyeball
schedule.shape       # (rows, columns) - e.g. (412, 6)
schedule.info()      # column names, types, and how many values are missing

head() shows the top of the table so you can confirm it read correctly. shape tells you how big it is - a fast sanity check that you got 412 rooms, not 4. info() is the most useful of the three: it lists every column, the type pandas inferred, and the non-null count, which is how you spot missing data and columns that came in as text when they should be numbers. Building the habit of head() then info() before you compute anything will save you from confidently running numbers on data that was never what you assumed.

ONE COLUMN IS A SERIESroomarea_sqmfinishliving24.0oakkitchen12.5tilebedroom15.0carpetschedule[area_sqm]indexarea_sqm024.0112.5215.0A Series is one named column of values, keeping the same row index it came from.
Zoom
Selecting one column of a DataFrame gives a Series. Ask for schedule[area_sqm] and pandas hands back that single named strip of values, still carrying the same row index - ready for a boolean filter, arithmetic, or a sum.

Selecting rows and columns

Most of what you do with a table is pick out a piece of it. pandas gives you a small, learnable set of ways to select, and they are worth getting straight early. To grab one column as a Series, index it by name; for several columns, pass a list of names and you get a smaller DataFrame back:

python
areas = schedule["area_sqm"]                 # one column -> a Series
subset = schedule[["room", "area_sqm"]]      # two columns -> a DataFrame

To select rows, you use .loc (by label) and .iloc (by position). And the move you will reach for constantly is the boolean filter: write a condition, and pandas keeps only the rows where it is true.

python
schedule.iloc[0]                             # the first row, by position
big = schedule[schedule["area_sqm"] > 15]    # only rooms over 15 sqm
living = schedule.loc[schedule["room"] == "living", "area_sqm"]

Read schedule["area_sqm"] > 15 on its own and you get a column of True/False - one answer per row. Put it back inside the brackets and pandas returns just the True rows. This is the pandas way of thinking: you describe a condition over the whole column at once, rather than writing a loop that checks rooms one by one. The same idea filters a 12-row sample and a 12,000-row register identically. Between column selection, .loc/.iloc, and boolean filters, you can carve any piece you need out of a table - which is the foundation the next lesson builds cleaning and grouping on top of.

ONE COLUMN IS A SERIESroomarea_sqmfinishliving24.0oakkitchen12.5tilebedroom15.0carpetschedule[area_sqm]indexarea_sqm024.0112.5215.0A Series is one named column of values, keeping the same row index it came from.
Zoom
Selecting one column of a DataFrame gives a Series. Ask for schedule[area_sqm] and pandas hands back that single named strip of values, still carrying the same row index - ready for a boolean filter, arithmetic, or a sum.

schedule[condition] keeps the True rows. Describe the whole column, not one row at a time.

Why tables as objects change how you work

The quiet shift pandas asks of you is to stop thinking row-by-row and start thinking whole-column. A spreadsheet trains you to fill one cell, then drag the formula down. pandas lets you say schedule["area_sqm"] * schedule["rate"] and multiply two entire columns element-by-element in one stroke - no dragging, no loop, no off-by-one at the bottom of the range. This is called vectorised thinking, and it is faster to write, faster to run, and far less error-prone than doing the arithmetic by hand.

It also makes your work repeatable. A spreadsheet's logic lives in cells you have to rebuild every time the source changes; a pandas script is a written recipe. Point it at next month's export and the same steps run again, identically, in a second. For a designer that means a schedule or a cost roll-up you produce once and then regenerate for free every revision - the difference between a deliverable that costs you an afternoon each cycle and one that costs you a keystroke.

You do not need to memorise pandas' full vocabulary; it is large, and even daily users look things up. What matters now is the mental model: a table is one object, columns are Series, and you operate on whole columns at once. Hold that, and the rest of this module - cleaning messy data, turning it into schedules and BOQs, and charting it - is variations on the moves you have just met.

Think whole-column, not cell-by-cell. A pandas script is a recipe you re-run for free.

Objects & functions you met in this lesson

pandas DataFrame

A whole table as one object - named, typed columns and an indexed set of rows

The core of the module. Think of it as a spreadsheet you operate on with code, one column at a time.

pandas Series

A single named column of a DataFrame, carrying the row index

You get one when you select a single column. Arithmetic and boolean filters work on the whole Series at once.

pd.read_csv / pd.read_excel

Load a CSV or Excel file into a DataFrame in one call

read_excel needs the openpyxl helper installed. Both surprise you with real data - always look before you compute.

.head() / .info()

Inspect the top rows, and the column names, types and missing-value counts

Your first reflex after loading anything. info() is how you catch numbers that came in as text and columns full of gaps.

Hands-on workshop

Workshop - load a real table and interrogate it

You will take a small room schedule from a CSV into pandas, confirm it read correctly, and pull out a slice - the exact loop of read, look, select that every later lesson repeats.

Python 3 with pandas and openpyxl installed (pip install pandas openpyxl), and any spreadsheet to make the CSV.

Given & goal
Goal: read a CSV, inspect it, and filter it
Inputs: a room_schedule.csv with columns room, area_sqm, finish (make one in any spreadsheet, export as CSV)
Time: ~30 minutes
  1. 1In a spreadsheet, make a small table with columns room, areasqm and finish and about six rows, then export it as roomschedule.csv into your working folder.
  2. 2In a Python file or notebook, write import pandas as pd and load it: schedule = pd.readcsv("roomschedule.csv"). Print schedule to see the grid pandas built.
  3. 3Inspect it: call schedule.head(), schedule.shape and schedule.info(). Confirm area_sqm is a number type (float64) and not text (object) - if it is text, a stray character sneaked in.
  4. 4Select just the areas as a Series with schedule["areasqm"], then select two columns with schedule[["room", "areasqm"]] and notice one is a Series and the other a DataFrame.
  5. 5Filter with a boolean condition: big = schedule[schedule["area_sqm"] > 15]. Print it, then print big.shape to see how many rooms passed.
  6. 6Change the threshold and re-run. Notice you changed one number and re-queried the whole table - no dragging, no rebuilding.

You’ll walk away with
A short script that reads room_schedule.csv, prints head() and info(), and prints only the rooms over a size threshold you can change in one place.

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

A model's data is a table long before it is a drawing. Room area statements, door and window schedules, area-by-use roll-ups for a planning submission - each is a DataFrame waiting to happen. Read the export once, and pandas lets you filter to the rooms that fail a code minimum, or total gross area by floor, without rebuilding a spreadsheet every revision. The read-look-select reflex here is the base you will script every schedule on.

For the interior designerScripts for data, schedules & layouts

Your FF&E and finishes lists are exactly what pandas is best at. A specification workbook with 300 line items - selecting all the joinery, or every item from one supplier, or just the tagged-for-approval rows - is a boolean filter, not an afternoon of scrolling. Load the workbook with read_excel, run head() and info() to catch the blank rows and stray text a real spec always has, and pick the slice you need in a line.

For the studentA hireable computational skill

pandas is the single most employable line on this whole course. Almost every computational-design, BIM-data and analysis role assumes you can load a table and query it. Practise on anything - a CSV of your studio's areas, a public dataset, an exam timetable - because the DataFrame you learn here is identical to the one used across the GIS and data courses in this Academy. Get read, head, info and boolean filtering into your fingers and you have a genuinely transferable skill.

Misconception check

pandas is just a slower, more complicated spreadsheet - if I know Excel I do not need it.

They overlap, but they are good at different things. A spreadsheet is unbeatable for quick, visual, one-off editing where you want to see and touch every cell. pandas wins the moment the work is repeated or large: a written pandas recipe runs the same steps on next month's file untouched, handles hundreds of thousands of rows a spreadsheet would choke on, and never suffers the silent broken-formula and dragged-range errors that creep into big workbooks. It is not a replacement for Excel so much as the tool for the jobs where Excel stops scaling - and because pandas reads and writes Excel files, you can keep the spreadsheet as the interface and use pandas as the engine underneath.
Try it

Do it yourself

Reason about the objects and the moves - imagine the DataFrame in your head.

  1. 1In one sentence, what is the difference between a DataFrame and a Series?
  2. 2Which function reads a comma-separated file into a DataFrame, and which reads an Excel sheet?
  3. 3What does schedule.info() tell you that schedule.head() does not?
  4. 4Write the expression that keeps only the rows where the area_sqm column is greater than 20.
  5. 5Why does asking for one column give a Series but asking for a list of columns give a DataFrame?
Take this with you

The one line to carry out

A table is one programmable object: read it with read_csv or read_excel, look at it with head() and info(), and carve out any piece with column selection and boolean filters. Once you think in whole columns instead of cells, a schedule becomes a recipe you re-run for free.
Take it further
References & further reading

Peer-reviewed journals & authoritative standards

  1. 01pandas - Python Data Analysis Librarypandas.pydata.org, 2026.
  2. 02pandas (software)Wikipedia, 2026.
  3. 03Comma-separated valuesWikipedia, 2026.
  4. 04NumPyWikipedia, 2026.
Related lessons
Recap
pandas turns a table into a single object you can program: the DataFrame is the whole table, and a Series is one named column of it. You load files with readcsv and readexcel, and you always look first with head() and info() to catch missing values and mistyped columns. Selecting by column name, by position with iloc/loc, and above all by boolean condition lets you pull any slice you need - describing the whole column at once rather than looping row by row.
Carry forward →

Real files are rarely clean - blank cells, wrong types, duplicates and inconsistent labels are the norm. Next we turn that mess into usable data: filtering, sorting, filling gaps, adding computed columns, and grouping to get the totals a schedule actually needs.

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 →