Repository: geopandas/geopandas
Pull request #3391 (2024-07-23 20:00:52)

DOC: Document use of `use_arrow=True` for faster IO
A quick draft for https://github.com/geopandas/geopandas/issues/3386

m-richards: I'm getting deja vu writing this but I can't seem to find the thread, would it make sense for us to have a `geopandas.options.io_engine='pyogrio_arrow'` or similar in addition to the environment variable?
kylebarron: What do you think about that edit?
theroggy: General remark: might be purely personal, but I've only read/scanned the user manual once a few years ago when I started to use `geopandas`. When I want to find information about a function I go look in the documentation of that function rather than the user manual.

Maybe it is useful to add one sentence in the `read_file` documentation/notes that there are options to improve performance + a link "more information can be found here"?
martinfleis: > Maybe it is useful to add one sentence in the read_file documentation/notes that there are options to improve performance + a link "more information can be found here"?

+1
m-richards: Just pushed a commit with the two suggested additional comments, hopefully I've got the relative paths right referencing the docs.

Patch:
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
deleted file mode 100644
index cea47a1c..00000000
--- a/.github/FUNDING.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-github: numfocus
-custom: https://numfocus.org/donate-for-geopandas
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 100c14f3..b9d6dacd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -40,6 +40,12 @@ Deprecations and compatibility notes:
 - The `GeoSeries.select` method wrapping the pandas `Series.select` method has been removed.
   The upstream method no longer exists in all supported version of pandas (#3394).
 
+New features and improvements:
+
+- Added `geopandas.accessors` module. Import this module to register a
+  `pandas.Series.geo` accessor, which exposes GeoSeries methods via pandas's
+  extension mechanism (#3272).
+
 ## Version 1.0.1 (July 2, 2024)
 
 Bug fixes:
diff --git a/doc/source/docs/user_guide/io.rst b/doc/source/docs/user_guide/io.rst
index 35f60f87..7d79e8c2 100644
--- a/doc/source/docs/user_guide/io.rst
+++ b/doc/source/docs/user_guide/io.rst
@@ -26,6 +26,11 @@ without extra arguments, but for more help, type::
     import pyogrio; help(pyogrio.read_dataframe)
     import fiona; help(fiona.open)
 
+.. note::
+    For faster data reading, pass ``use_arrow=True`` when using the default pyogrio engine. This can be 2-4 times faster than the default reading behavior and works with all drivers. See `pyogrio.read_dataframe <https://pyogrio.readthedocs.io/en/latest/api.html#pyogrio.read_dataframe>`_ for full details.
+
+    Note that this requires the ``pyarrow`` dependency to exist in your environment.
+
 Among other things, one can explicitly set the driver (shapefile, GeoJSON) with
 the ``driver`` keyword, or pick a single layer from a multi-layered file with
 the ``layer`` keyword::
@@ -76,6 +81,15 @@ You can also read path objects::
     path_object = pathlib.Path(filename)
     df = geopandas.read_file(path_object)
 
+Using Arrow for faster reading
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For faster data reading, pass ``use_arrow=True`` when using the default pyogrio engine. This can be 2-4 times faster than the default reading behavior and works with all drivers. See `pyogrio.read_dataframe <https://pyogrio.readthedocs.io/en/latest/api.html#pyogrio.read_dataframe>`_ for full details.
+
+It is also possible to enable this by default by setting the environment variable ``PYOGRIO_USE_ARROW=1`` (which will also enable writing data using arrow).
+
+Note that this requires the ``pyarrow`` dependency to exist in your environment.
+
 Reading subsets of the data
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -203,6 +217,11 @@ For a full list of supported formats, type ``import pyogrio; pyogrio.list_driver
 In addition, GeoDataFrames can be uploaded to `PostGIS <https://postgis.net/>`__ database (starting with GeoPandas 0.8)
 by using the :meth:`geopandas.GeoDataFrame.to_postgis` method.
 
+.. note::
+    For faster data writing, pass ``use_arrow=True`` when using the default pyogrio engine. This can be 2-4 times faster than the default writing behavior and works with all drivers. See `pyogrio.write_dataframe <https://pyogrio.readthedocs.io/en/latest/api.html#pyogrio.write_dataframe>`_ for full details.
+
+    Note that this requires the ``pyarrow`` dependency to exist in your environment.
+
 .. note::
 
     GeoDataFrame can contain more field types than supported by most of the file formats. For example tuples or lists
@@ -219,6 +238,10 @@ by using the :meth:`geopandas.GeoDataFrame.to_postgis` method.
 
     countries_gdf.to_file("countries.shp")
 
+**Writing to Shapefile with via Arrow**::
+
+    countries_gdf.to_file("countries.shp", use_arrow=True)
+
 **Writing to GeoJSON**::
 
     countries_gdf.to_file("countries.geojson", driver='GeoJSON')
@@ -257,7 +280,7 @@ Apache Parquet and Feather file formats
 
 .. versionadded:: 0.8.0
 
-GeoPandas supports writing and reading the Apache Parquet and Feather file
+GeoPandas supports writing and reading the Apache Parquet (`GeoParquet <https://geoparquet.org/>`__) and Feather file
 formats.
 
 `Apache Parquet <https://parquet.apache.org/>`__ is an efficient, columnar
@@ -267,7 +290,7 @@ representation of the `Apache Arrow <https://arrow.apache.org/>`__ memory
 format, an open standard for in-memory columnar data.
 
 The :func:`geopandas.read_parquet`, :func:`geopandas.read_feather`,
-:meth:`GeoDataFrame.to_parquet` and :meth:`GeoDataFrame.to_feather` methods
+:meth:`geopandas.GeoDataFrame.to_parquet` and :meth:`geopandas.GeoDataFrame.to_feather` methods
 enable fast roundtrip from GeoPandas to those binary file formats, preserving
 the spatial information.
 
diff --git a/doc/source/gallery/pandas_accessor.ipynb b/doc/source/gallery/pandas_accessor.ipynb
new file mode 100644
index 00000000..a125eaca
--- /dev/null
+++ b/doc/source/gallery/pandas_accessor.ipynb
@@ -0,0 +1,139 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Using GeoPandas via the pandas \"geo\" accessor\n",
+    "\n",
+    "GeoPandas can be used from pandas with the \"geo\" accessor, which is registered on `pandas.Series` when `geopandas.accessors` is first imported."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Import geopandas.accessors to register the \"geo\" accessor.\n",
+    "import geopandas.accessors  # noqa # pylint: disable=unused-import\n",
+    "import pandas as pd\n",
+    "from shapely import Point"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Using the Series.geo accessor\n",
+    "\n",
+    "Construct a Series using the GeometryDtype."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "0    POINT (1 2)\n",
+       "1    POINT (3 4)\n",
+       "2    POINT (5 6)\n",
+       "dtype: geometry"
+      ]
+     },
+     "execution_count": 2,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "s = pd.Series(\n",
+    "    [Point(1, 2), Point(3, 4), Point(5, 6)],\n",
+    "    dtype='geometry',\n",
+    ")\n",
+    "s"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "All GeoSeries methods and properties are available via the \"geo\" accessor."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "0    1.0\n",
+       "1    3.0\n",
+       "2    5.0\n",
+       "dtype: float64"
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "s.geo.x"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "0     4.472136\n",
+       "1    10.000000\n",
+       "2    15.620499\n",
+       "dtype: float64"
+      ]
+     },
+     "execution_count": 4,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "s2 = pd.Series(\n",
+    "    [Point(-1, -2), Point(-3, -4), Point(-5, -6)],\n",
+    "    dtype='geometry',\n",
+    ")\n",
+    "s.geo.distance(s2)"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "dev-3.11",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.2"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/geopandas/accessors.py b/geopandas/accessors.py
new file mode 100644
index 00000000..cd8f8e0e
--- /dev/null
+++ b/geopandas/accessors.py
@@ -0,0 +1,28 @@
+"""
+Accessors for accessing GeoPandas functionality via pandas extension dtypes
+"""
+
+import pandas.api.extensions
+
+from geopandas import GeoSeries
+from geopandas.array import GeometryDtype
+
+
+@pandas.api.extensions.register_series_accessor("geo")
+class GeoSeriesAccessor:
+    """Series.geo accessor to expose GeoSeries methods on pandas Series.
+
+    Parameters
+    ----------
+    series : pandas.Series
+        A Series with geometry dtype.
+    """
+
+    def __init__(self, series):
+        if not isinstance(series.dtype, GeometryDtype):
+            raise AttributeError("Can only use .geo accessor with GeometryDtype values")
+
+        self._geoseries = GeoSeries(series)
+
+    def __getattr__(self, name):
+        return getattr(self._geoseries, name)
diff --git a/geopandas/base.py b/geopandas/base.py
index a2dfdb00..2d4efbab 100644
--- a/geopandas/base.py
+++ b/geopandas/base.py
@@ -34,6 +34,18 @@ def _delegate_binary_method(op, this, other, align, *args, **kwargs):
     else:
         maybe_warn = False
     this = this.geometry
+
+    # Use same alignment logic, regardless of if `other` is Series or GeoSeries.
+    if (
+        not isinstance(other, GeoPandasBase)
+        and isinstance(other, pd.Series)
+        and isinstance(other.dtype, GeometryDtype)
+    ):
+        # Avoid circular imports by importing here.
+        import geopandas.geoseries
+
+        other = geopandas.geoseries.GeoSeries(other)
+
     if isinstance(other, GeoPandasBase):
         if align and not this.index.equals(other.index):
             if maybe_warn:
diff --git a/geopandas/geodataframe.py b/geopandas/geodataframe.py
index 846c93ef..807d2a00 100644
--- a/geopandas/geodataframe.py
+++ b/geopandas/geodataframe.py
@@ -1221,7 +1221,7 @@ properties': {'col1': 'name1'}, 'geometry': {'type': 'Point', 'coordinates': (1.
 
         See https://geoarrow.org/ for details on the GeoArrow specification.
 
-        This functions returns a generic Arrow data object implementing
+        This function returns a generic Arrow data object implementing
         the `Arrow PyCapsule Protocol`_ (i.e. having an ``__arrow_c_stream__``
         method). This object can then be consumed by your Arrow implementation
         of choice that supports this protocol.
@@ -2210,35 +2210,6 @@ default 'snappy'
 
         return df
 
-    # overrides the pandas astype method to ensure the correct return type
-    # should be removable when pandas 1.4 is dropped
-    def astype(self, dtype, copy=None, errors="raise", **kwargs):
-        """
-        Cast a pandas object to a specified dtype ``dtype``.
-        Returns a GeoDataFrame when the geometry column is kept as geometries,
-        otherwise returns a pandas DataFrame.
-        See the pandas.DataFrame.astype docstring for more details.
-        Returns
-        -------
-        GeoDataFrame or DataFrame
-        """
-        if not PANDAS_GE_30 and copy is None:
-            copy = True
-        if copy is not None:
-            kwargs["copy"] = copy
-
-        df = super().astype(dtype, errors=errors, **kwargs)
-
-        try:
-            geoms = df[self._geometry_column_name]
-            if is_geometry_type(geoms):
-                return geopandas.GeoDataFrame(df, geometry=self._geometry_column_name)
-        except KeyError:
-            pass
-        # if the geometry column is converted to non-geometries or did not exist
-        # do not return a GeoDataFrame
-        return pd.DataFrame(df)
-
     def to_postgis(
         self,
         name,
diff --git a/geopandas/io/file.py b/geopandas/io/file.py
index 274b19c8..b2dfd9b5 100644
--- a/geopandas/io/file.py
+++ b/geopandas/io/file.py
@@ -264,6 +264,11 @@ def _read_file(
     may fail. In this case, the proper encoding can be specified explicitly
     by using the encoding keyword parameter, e.g. ``encoding='utf-8'``.
 
+    For faster data reading with the default pyogrio engine when
+    pyarrow is installed, pass ``use_arrow=True`` as an argument. See the User
+    Guide page :doc:`../../user_guide/io` for details.
+
+
     When specifying a URL, geopandas will check if the server supports reading
     partial data and in that case pass the URL as is to the underlying engine,
     which will then use the network file system handler of GDAL to read from
diff --git a/geopandas/tools/clip.py b/geopandas/tools/clip.py
index d1386f30..7567ad96 100644
--- a/geopandas/tools/clip.py
+++ b/geopandas/tools/clip.py
@@ -24,13 +24,29 @@ from geopandas.array import (
 
 
 def _mask_is_list_like_rectangle(mask):
+    """
+    Check if the input mask is list-like and not an instance of
+    specific geometric types.
+
+    Parameters
+    ----------
+    mask : GeoDataFrame, GeoSeries, (Multi)Polygon, list-like
+        Polygon vector layer used to clip ``gdf``.
+
+    Returns
+    -------
+    bool
+        True if `mask` is list-like and not an instance of `GeoDataFrame`,
+        `GeoSeries`, `Polygon`, or `MultiPolygon`, otherwise False.
+    """
     return pandas.api.types.is_list_like(mask) and not isinstance(
         mask, GeoDataFrame | GeoSeries | Polygon | MultiPolygon
     )
 
 
 def _clip_gdf_with_mask(gdf, mask, sort=False):
-    """Clip geometry to the polygon/rectangle extent.
+    """
+    Clip geometry to the polygon/rectangle extent.
 
     Clip an input GeoDataFrame to the polygon extent of the polygon
     parameter.
@@ -163,17 +179,17 @@ def clip(gdf, mask, keep_geom_type=False, sort=False):
     if not isinstance(gdf, GeoDataFrame | GeoSeries):
         raise TypeError(f"'gdf' should be GeoDataFrame or GeoSeries, got {type(gdf)}")
 
-    mask_is_list_like = _mask_is_list_like_rectangle(mask)
+    clipping_by_rectangle = _mask_is_list_like_rectangle(mask)
     if (
         not isinstance(mask, GeoDataFrame | GeoSeries | Polygon | MultiPolygon)
-        and not mask_is_list_like
+        and not clipping_by_rectangle
     ):
         raise TypeError(
             "'mask' should be GeoDataFrame, GeoSeries,"
             f"(Multi)Polygon or list-like, got {type(mask)}"
         )
 
-    if mask_is_list_like and len(mask) != 4:
+    if clipping_by_rectangle and len(mask) != 4:
         raise TypeError(
             "If 'mask' is list-like, it must have four values (minx, miny, maxx, maxy)"
         )
@@ -184,7 +200,7 @@ def clip(gdf, mask, keep_geom_type=False, sort=False):
 
     if isinstance(mask, GeoDataFrame | GeoSeries):
         box_mask = mask.total_bounds
-    elif mask_is_list_like:
+    elif clipping_by_rectangle:
         box_mask = mask
     else:
         # Avoid empty tuple returned by .bounds when geometry is empty. A tuple of
diff --git a/pyproject.toml b/pyproject.toml
index 842f1c34..9717a007 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -31,7 +31,7 @@ dependencies = [
 
 [project.optional-dependencies]
 all = [
-    "psycopg-binary>=3.1.0",
+    "psycopg[binary]>=3.1.0",
     "SQLAlchemy>=2.0",
     "geopy",
     "matplotlib>=3.7",
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 8f9395a0..0eca77c7 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -8,7 +8,7 @@ packaging
 
 # geodatabase access
 psycopg2-binary>=2.8.0
-psycopg-binary>=3.1.0
+psycopg[binary]>=3.1.0
 SQLAlchemy>=2.0
 
 # geocoding


Test patch:
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index d167d253..f7f05710 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -96,4 +96,4 @@ jobs:
         run: |
           pytest -v --color=yes --doctest-only geopandas --ignore=geopandas/datasets
 
-      - uses: codecov/codecov-action@v4
+      - uses: codecov/codecov-action@v5
diff --git a/geopandas/tests/test_explore.py b/geopandas/tests/test_explore.py
index 88b757d5..03cf5e52 100644
--- a/geopandas/tests/test_explore.py
+++ b/geopandas/tests/test_explore.py
@@ -22,6 +22,7 @@ from matplotlib import cm, colors
 
 BRANCA_05 = Version(branca.__version__) > Version("0.4.2")
 FOLIUM_G_014 = Version(folium.__version__) > Version("0.14.0")
+FOLIUM_GE_019 = Version(folium.__version__) >= Version("0.19.0")
 
 
 @pytest.fixture(scope="class")
@@ -70,7 +71,10 @@ class TestExplore:
             pytest.approx(2.842170943040401e-14, rel=1e-6),
         ]
         assert m.options["zoom"] == 10
-        assert m.options["zoomControl"] is True
+        if not FOLIUM_GE_019:
+            assert m.options["zoomControl"] is True
+        else:
+            assert m.options["zoom_control"] is True
         assert m.position == "relative"
         assert m.height == (100.0, "%")
         assert m.width == (100.0, "%")
@@ -93,7 +97,10 @@ class TestExplore:
             pytest.approx(-73.9778006856748, rel=1e-6),
         ]
         assert m.options["zoom"] == 10
-        assert m.options["zoomControl"] is False
+        if not FOLIUM_GE_019:
+            assert m.options["zoomControl"] is False
+        else:
+            assert m.options["zoom_control"] is False
         assert m.height == (200.0, "px")
         assert m.width == (200.0, "px")
 
@@ -751,7 +758,7 @@ class TestExplore:
             'attribution":"\\u0026copy;\\u003cahref=\\"https://www.openstreetmap.org'
             in out_str
         )
-        assert '"maxZoom":20,"minZoom":0' in out_str
+        assert '"maxZoom":20' in out_str
 
     @pytest.mark.skipif(not HAS_PYPROJ, reason="requires pyproj")
     def test_xyzservices_query_name(self):
@@ -768,7 +775,7 @@ class TestExplore:
             'attribution":"\\u0026copy;\\u003cahref=\\"https://www.openstreetmap.org'
             in out_str
         )
-        assert '"maxZoom":20,"minZoom":0' in out_str
+        assert '"maxZoom":20' in out_str
 
     @pytest.mark.skipif(not HAS_PYPROJ, reason="requires pyproj")
     def test_xyzservices_providers_min_zoom_override(self):
@@ -779,7 +786,7 @@ class TestExplore:
         )
         out_str = self._fetch_map_string(m)
 
-        assert '"maxZoom":20,"minZoom":3' in out_str
+        assert '"minZoom":3' in out_str
 
     @pytest.mark.skipif(not HAS_PYPROJ, reason="requires pyproj")
     def test_xyzservices_providers_max_zoom_override(self):
@@ -790,7 +797,7 @@ class TestExplore:
         )
         out_str = self._fetch_map_string(m)
 
-        assert '"maxZoom":12,"minZoom":0' in out_str
+        assert '"maxZoom":12' in out_str
 
     @pytest.mark.skipif(not HAS_PYPROJ, reason="requires pyproj")
     def test_xyzservices_providers_both_zooms_override(self):
@@ -803,7 +810,8 @@ class TestExplore:
         )
         out_str = self._fetch_map_string(m)
 
-        assert '"maxZoom":12,"minZoom":3' in out_str
+        assert '"maxZoom":12' in out_str
+        assert '"minZoom":3' in out_str
 
     def test_linearrings(self):
         rings = self.nybb.explode(index_parts=True).exterior
@@ -979,9 +987,15 @@ class TestExplore:
     def test_map_kwds(self):
         def check():
             out_str = self._fetch_map_string(m)
-            assert "zoomControl:false" in out_str
-            assert "dragging:false" in out_str
-            assert "scrollWheelZoom:false" in out_str
+            if not FOLIUM_GE_019:
+                assert m.options["zoomControl"] is False
+            else:
+                assert m.options["zoom_control"] is False
+            assert "dragging:false" in out_str or '"dragging":false' in out_str
+            assert (
+                "scrollWheelZoom:false" in out_str
+                or '"scrollWheelZoom":false' in out_str
+            )
 
         # check that folium and leaflet Map() parameters can be passed
         m = self.world.explore(
diff --git a/geopandas/tests/test_pandas_accessor.py b/geopandas/tests/test_pandas_accessor.py
new file mode 100644
index 00000000..ad81b550
--- /dev/null
+++ b/geopandas/tests/test_pandas_accessor.py
@@ -0,0 +1,73 @@
+"""Ensure geopandas methods can be accessed via pandas 'geo' accessor."""
+
+import re
+
+import pandas as pd
+
+from shapely.geometry import Point
+
+# Use this import to register the "geo" accessor.
+import geopandas.accessors  # noqa: F401 # pylint: disable=unused-import
+from geopandas.array import GeometryDtype
+
+import pandas.testing
+import pytest
+
+
+@pytest.fixture
+def s():
+    return pd.Series(
+        [Point(x, y) for x, y in zip(range(3), range(3))], dtype=GeometryDtype()
+    )
+
+
+@pytest.fixture
+def s2():
+    return pd.Series(
+        [Point(x, y + 1) for x, y in zip(range(3), range(3))], dtype=GeometryDtype()
+    )
+
+
+def test_series_geo_buffer(s):
+    """Ensure returned geometry values have the expected dtype."""
+    got = s.geo.buffer(0.2)
+    assert isinstance(got.dtype, GeometryDtype)
+
+    # Double-check that the .geo accessor works on the result.
+    radius = got.geo.minimum_bounding_radius()
+    for row in radius.index:
+        assert radius[row] >= 0.1999  # Allow for some rounding error.
+
+
+def test_series_geo_distance(s, s2):
+    got = s.geo.distance(s2)
+    pandas.testing.assert_series_equal(
+        got,
+        pd.Series([1.0, 1.0, 1.0]),
+    )
+
+
+def test_series_geo_x(s):
+    x = s.geo.x
+    pandas.testing.assert_series_equal(
+        x,
+        pd.Series([0.0, 1.0, 2.0]),
+    )
+
+
+def test_series_geo_x_attributeerror_for_not_geo_dtype():
+    s = pd.Series([1, 2, 3])
+
+    with pytest.raises(
+        AttributeError,
+        match=re.escape("Can only use .geo accessor with GeometryDtype values"),
+    ):
+        s.geo.x
+
+
+def test_series_geo_y(s):
+    y = s.geo.y
+    pandas.testing.assert_series_equal(
+        y,
+        pd.Series([0.0, 1.0, 2.0]),
+    )