# Type objects are just values — print them to inspect
print(ma.int64())
print(ma.float32())
print(ma.string())int64
float32
string
Marrow’s type system follows the Apache Arrow specification. Types are represented as DataType objects and can be constructed explicitly or inferred from Python data.
| Constructor | Arrow type | Python equivalent |
|---|---|---|
ma.bool_() |
bool |
bool |
ma.int8() |
int8 |
— |
ma.int16() |
int16 |
— |
ma.int32() |
int32 |
— |
ma.int64() |
int64 |
int |
ma.uint8() |
uint8 |
— |
ma.uint16() |
uint16 |
— |
ma.uint32() |
uint32 |
— |
ma.uint64() |
uint64 |
— |
ma.float16() |
float16 |
— |
ma.float32() |
float32 |
— |
ma.float64() |
float64 |
float |
ma.string() |
utf8 |
str |
ma.binary() |
binary |
bytes |
ma.fixed_size_binary(n) |
fixed-width binary |
— |
ma.null() |
null |
None |
Temporal types take a resolution unit (and, for timestamps, an optional time zone):
| Constructor | Arrow type |
|---|---|
ma.date32() / ma.date64() |
calendar date |
ma.time32(unit) / ma.time64(unit) |
time of day |
ma.timestamp(unit, tz=None) |
timestamp |
ma.duration(unit) |
elapsed time |
ma.year_month_interval() |
months interval |
ma.day_time_interval() |
days + milliseconds interval |
ma.month_day_nano_interval() |
months + days + nanoseconds interval |
Nested and named types are built from helper functions:
| Constructor | Arrow type |
|---|---|
ma.field("name", dtype, nullable, metadata) |
named field descriptor |
ma.struct([field, ...]) |
struct with named fields |
ma.list_(value_type) |
variable-length list |
ma.fixed_size_list_(value_type, size) |
fixed-width list |
When type= is omitted from ma.array(), the type is inferred from the data in a single pass:
print(ma.infer_type([1, 2, 3])) # int64 (Python int)
print(ma.infer_type([1.0, 2.0])) # float64 (Python float)
print(ma.infer_type([True, False])) # bool
print(ma.infer_type(["a", "b", "c"])) # string
print(ma.infer_type([[1, 2], [3, 4]])) # list<int64>
print(ma.infer_type([{"x": 1, "y": 1.5}])) # struct<x: int64, y: float64>int64
float64
bool
string
list<int64>
struct<x: int64, y: float64>
int → int64float → float64bool → boolstr → stringlist / tuple → list(T) where T is inferred from child elementsdict → struct(field1: T1, field2: T2, ...) — field names taken from keys, types inferred from valuesNone mixed with typed values → typed array with a null at that positionInference requires at least one non-None element to determine the type. An all-None list needs an explicit type:
--------------------------------------------------------------------------- ArrowInvalid Traceback (most recent call last) Cell In[4], line 2 1 # This raises — can't infer type from all Nones ----> 2 ma.array([None, None]) File ~/work/marrow/marrow/docs/guide/../../python/marrow/arrays.py:263, in array(obj, type) 261 def array(obj, type=None): 262 """An :class:`Array` from a Python sequence, or from an Arrow producer.""" --> 263 return Array.wrap(_ma.array(unwrap(obj), unwrap(type))) File ~/work/marrow/marrow/docs/guide/../../python/marrow/errors.py:122, in _wrap.<locals>.call(*args, **kwargs) 120 if translated is None: 121 raise --> 122 raise translated from None ArrowInvalid: cannot build array: sequence is empty or all-None (provide type= explicitly)
PrimitiveArray[int64]([NULL, NULL])
null count: 2
Mixing incompatible Python types also raises:
--------------------------------------------------------------------------- ArrowTypeError Traceback (most recent call last) Cell In[6], line 1 ----> 1 ma.array([1, "two", 3]) # int and str are incompatible File ~/work/marrow/marrow/docs/guide/../../python/marrow/arrays.py:263, in array(obj, type) 261 def array(obj, type=None): 262 """An :class:`Array` from a Python sequence, or from an Arrow producer.""" --> 263 return Array.wrap(_ma.array(unwrap(obj), unwrap(type))) File ~/work/marrow/marrow/docs/guide/../../python/marrow/errors.py:122, in _wrap.<locals>.call(*args, **kwargs) 120 if translated is None: 121 raise --> 122 raise translated from None ArrowTypeError: cannot mix string and numeric types
Providing type= skips inference and coerces the data:
PrimitiveArray[int32]([1, 2, 3, 4])
PrimitiveArray[float32]([1.0, 2.0, 3.0])
Explicit types are also faster — no inference pass over the data.
Structs group multiple named fields into a single array. Use ma.field() and ma.struct() to build a schema, then pass it as type=:
struct<name: string, score: float64, rank: int32>
StructArray({'name': StringArray([Alice, Bob, Carol]), 'score': PrimitiveArray[float64]([9.5, 8.2, NULL]), 'rank': PrimitiveArray[int32]([1, 2, 3])})
List arrays hold variable-length sequences. Type inference handles the common cases automatically:
list<int64>
list<float64>
For full control, build the nested type explicitly with ma.list_(value_type) or ma.fixed_size_list_(value_type, size) and pass it as type=: