Tables, schemas and record batches

PyArrow · Essentials · 12 min

Tables are what you read from files and hand to other libraries, and the schema is the contract between them.

A Table is a set of named columns of equal length; each column is a ChunkedArray. A Schema lists the column names and types, as pa.field objects that can also say whether nulls are allowed and carry metadata.

Build a table with pa.table({"id": [1, 2], "name": ["a", "b"]}), pa.Table.from_pydict(...), or row-wise with pa.Table.from_pylist([{"id": 1, ...}, ...]). Pass schema= to enforce types instead of inferring them. Convert back with to_pydict() or to_pylist().

Inspect with t.schema, t.num_rows, t.num_columns, t.column_names, t.nbytes. Access a column with t["name"] or t.column(0).

Tables are immutable; every operation returns a new table that shares the unchanged column buffers, so it's cheap:

  • t.select(["id"]), t.drop_columns(["x"]), t.rename_columns([...])
  • t.append_column("score", pa.array([...])), t.set_column(i, name, arr)
  • t.slice(offset, length), t.cast(new_schema)
  • pa.concat_tables([t1, t2]) stacks tables with the same schema

A RecordBatch is like a table but each column is a single contiguous array. Batches are the unit of streaming: readers produce them, IPC streams carry them, and t.to_batches() or pa.Table.from_batches(...) move between the two forms.

Example

import pyarrow as pa

schema = pa.schema([
    pa.field("id", pa.int32(), nullable=False),
    pa.field("city", pa.string()),
    pa.field("temp", pa.float32()),
])
t = pa.Table.from_pylist(
    [{"id": 1, "city": "Oslo", "temp": 4.5},
     {"id": 2, "city": "Lima", "temp": None}],
    schema=schema,
)
print(t.num_rows, t.column_names)       # 2 ['id', 'city', 'temp']
print(t.schema.field("id"))             # pyarrow.Field<id: int32 not null>

t2 = t.append_column("hot", pa.array([False, None]))
print(t2.select(["city", "hot"]).to_pylist())
# [{'city': 'Oslo', 'hot': False}, {'city': 'Lima', 'hot': None}]

both = pa.concat_tables([t, t])
print(both.num_rows, both["id"].num_chunks)   # 4 2

Remember

  • Table = named ChunkedArrays; Schema = names, types, nullability
  • Pass an explicit schema= when types matter
  • Operations return new tables that share buffers

Watch out for

  • Concatenating tables whose schemas differ slightly (int32 vs int64): cast first or it fails.

Check yourself

  1. What is the main structural difference between a pa.Table and a pa.RecordBatch?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: arrow.apache.org/docs/python/data.html#tables