Source: https://gometry.monicz.dev/ecosystem/dataframes/

# DataFrames

Convert dataframe columns to [`GeometryArray`](https://gometry.monicz.dev/api/geometry/geometryarray/#gometry.GeometryArray "GeometryArray") for
geometry computation, then attach the results to your dataframe.

| Extra | Storage | Into gometry | Out of gometry |
| --- | --- | --- | --- |
| `pandas` (`gometry[pandas]`, `pandas>=3.0.3`; add `gometry[arrow]` for GeoArrow-backed `ArrowDtype`) | `GeometryExtensionArray`, GeoArrow-backed `ArrowDtype`, or object Series | [`gm.from_pandas(series)`](https://gometry.monicz.dev/api/interop/#gometry.from_pandas "from_pandas") | [`array.to_pandas()`](https://gometry.monicz.dev/api/geometry/geometryarray/#gometry.GeometryArray.to_pandas "to_pandas") |
| `polars` | WKB/EWKB `Binary` Series | [`gm.from_polars(series)`](https://gometry.monicz.dev/api/interop/#gometry.from_polars "from_polars") | [`array.to_polars()`](https://gometry.monicz.dev/api/geometry/geometryarray/#gometry.GeometryArray.to_polars "to_polars") |
| `geopandas` | Shapely geometry column | [`gm.from_geopandas(series_or_frame)`](https://gometry.monicz.dev/api/interop/#gometry.from_geopandas "from_geopandas") | [`array.to_geopandas()`](https://gometry.monicz.dev/api/geometry/geometryarray/#gometry.GeometryArray.to_geopandas "to_geopandas") |

Install the adapters you use:

```bash
uv add "gometry[pandas]"
uv add "gometry[arrow]"
uv add "gometry[polars]"
uv add "gometry[geopandas]"
```

## pandas geometry storage

`GeometryArray.to_pandas()` constructs a Series backed by gometry's concrete
extension array. The Series and gometry array share immutable geometry storage,
so `gm.from_pandas()` returns that native column without a WKB round trip.

`from_pandas()` also imports ordinary object Series whose rows are Geometry
objects, WKB buffers, GeoJSON mappings, or objects exposing
`__geo_interface__`. That path constructs geometry rows rather than sharing
the Series' storage. `None`, `pd.NA`, and pandas-recognized missing values
become missing geometry rows; empty geometries remain values. Use
`from_geopandas()` for a GeoSeries so its CRS metadata is retained.

A pandas Series backed by `pd.ArrowDtype` can also carry a GeoArrow extension
column. `from_pandas()` passes that Arrow column directly to
[`from_arrow`](https://gometry.monicz.dev/api/toplevel/io-interop/#gometry.from_arrow "from_arrow"), preserving chunks, nulls, dimensions, CRS,
and coordinate epoch without converting rows to Python dictionaries. This path
requires `pyarrow` (`gometry[arrow]`); unrelated Arrow dtypes are rejected as
non-geometry input.

```python
import pandas as pd

import gometry as gm

points = gm.points([21.0, 22.0], [52.0, 52.5], crs=4326)
buffered = points.buffer(500)
series = buffered.to_pandas(name="geometry")
restored = gm.from_pandas(series)
typed = pd.Series([gm.Point(23.0, 53.0, crs=4326)], dtype='gometry.geometry')

print("dtype:", series.dtype)
print("registered dtype:", typed.dtype)
print("rows:", len(series))
print("frame:", restored.crs.to_authority())
```

```text
dtype: gometry.geometry
registered dtype: gometry.geometry
rows: 2
frame: ('EPSG', '4326')
```

```python
import pandas as pd
import pyarrow as pa

import gometry as gm

geometry = gm.points([1.0, 2.0], [3.0, 4.0], crs=4326, epoch=2020.0)
arrow = geometry.to_arrow()
chunked = pa.chunked_array([arrow.slice(0, 1), arrow.slice(1)])
series = pd.Series(chunked, dtype=pd.ArrowDtype(chunked.type))
restored = gm.from_pandas(series)

print("dtype:", series.dtype)
print(
    "stored CRS definition preserved:",
    restored.crs.definition == geometry.crs.definition,
)
print("epoch:", restored.epoch)
```

```text
dtype: extension<geoarrow.point<GeoArrowType>>[pyarrow]
stored CRS definition preserved: True
epoch: 2020.0
```

```python
import pandas as pd
import gometry as gm

series = pd.Series(
    [gm.Point(1, 2), pd.NA, {"type": "Point", "coordinates": [3, 4]}],
    dtype=object,
)
geometries = gm.from_pandas(series)
print(geometries.to_wkt())
```

```text
['POINT (1 2)', None, 'POINT (3 4)']
```

`GeometryDtype` registers as `gometry.geometry` when the pandas adapter is
imported. Use that name with `pandas_dtype`, `Series(..., dtype=...)`, and
`astype(...)`; `array.to_pandas()` remains the direct path from gometry.

Missing rows are native `GeometryArray` state. `None` and
`pd.NA` are missing; an empty geometry is a value. pandas operations such as
`dropna`, `factorize`, `groupby`, and `value_counts` use the extension-array
protocol directly.

To calculate a new column, convert once, compute in gometry, then attach the
result:

```python
import gometry as gm

points = gm.points([21.0, 22.0], [52.0, 52.5], crs=4326)
series = points.to_pandas(name="geometry")
frame = series.to_frame()
geometry = gm.from_pandas(series)
frame["area"] = geometry.area
frame["buffered"] = geometry.buffer(500).to_pandas(index=frame.index)
print(frame.columns.tolist())
```

```text
['geometry', 'area', 'buffered']
```

## Polars binary storage

Polars stores the boundary as a `Binary` Series. `to_polars()` encodes the
whole column through native batched WKB; `from_polars()` decodes it in one
native call. Neither direction requires PyArrow.

```python
import gometry as gm

points = gm.points([21.0, 22.0], [52.0, 52.5], crs=4326)
geometries = points.buffer(500)
series = geometries.to_polars(name="geometry")
geometry = gm.from_polars(series)
areas = geometry.area

print("storage:", series.dtype)
print("rows:", series.len())
print("areas:", areas.tolist())
```

```text
storage: Binary
rows: 2
areas: [780985.9506729525, 780897.5753460705]
```

An EPSG CRS rides as an EWKB SRID. Other CRS definitions and coordinate epochs
are not representable in WKB. `drop_crs=True` removes non-EPSG CRS metadata and
`drop_epoch=True` removes the coordinate epoch; restore them explicitly through
`gm.from_polars(..., crs=..., epoch=...)`. Missing rows encode as Polars nulls
and round-trip as missing geometry rows.

gometry registers no Polars Series or Expr namespace. `to_polars()` eagerly
encodes a `GeometryArray` as a binary Series, and `gm.from_polars()` eagerly
decodes the supplied Series into a `GeometryArray` in one native batch. These
conversions run when called; they do not become deferred `LazyFrame`
expressions.

## GeoPandas interchange

`gm.from_geopandas()` decodes a GeoSeries or GeoDataFrame geometry column
through vectorized WKB. `GeometryArray.to_geopandas()` returns a GeoSeries with
the array CRS.

```python
import geopandas as gpd
import gometry as gm

frame = gpd.GeoDataFrame(
    {"name": ["A", "B"]},
    geometry=gpd.points_from_xy([21.0, 22.0], [52.0, 52.5], crs=4326),
)
geometry = gm.from_geopandas(frame)
back = geometry.to_geopandas()
print(type(back).__name__, len(back), back.crs)
```

```text
GeoSeries 2 EPSG:4326
```

This boundary copies. Use the pandas extension path when native shared storage
is the goal.

## Import behavior

`import gometry` imports none of pandas, Polars, GeoPandas, PyArrow, or
lonboard. Accessing a converter imports only its requested optional framework.
No manual setup is needed for the converters.

## See also

- [Arrow & storage](https://gometry.monicz.dev/ecosystem/arrow/) — GeoArrow capsules, PyArrow objects, and GeoParquet.
- [Text & binary formats](https://gometry.monicz.dev/ecosystem/text-formats/) — the WKB/EWKB encoding used by Polars.
- [Ecosystem & interoperability](https://gometry.monicz.dev/ecosystem/) — the complete boundary matrix.
