Repository: geopandas/geopandas

Issue:
REGR: apply over rows raises AttributeError when accessing single element
When using `apply` on a GeoDataFrame, accessing a single element within the loop raises an error. It is a new error coming from latest commits, possibly related to #2282.

```py
import geopandas

df = geopandas.read_file(geopandas.datasets.get_path("naturalearth_lowres"))
df.apply(lambda row: row.continent, axis=1)

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/var/folders/2f/fhks6w_d0k556plcv3rfmshw0000gn/T/ipykernel_17461/1449003612.py in <module>
      2 
      3 df = geopandas.read_file(geopandas.datasets.get_path("naturalearth_lowres"))
----> 4 df.apply(lambda row: row.continent, axis=1)

~/Git/geopandas/geopandas/geodataframe.py in apply(self, func, axis, raw, result_type, args, **kwargs)
   1409         )
   1410         # Reconstruct gdf if it was lost by apply
-> 1411         if self._geometry_column_name in result.columns:
   1412             # axis=1 apply will split GeometryDType to object, try and cast back
   1413             try:

~/mambaforge/envs/geo_dev/lib/python3.9/site-packages/pandas/core/generic.py in __getattr__(self, name)
   5485         ):
   5486             return self[name]
-> 5487         return object.__getattribute__(self, name)
   5488 
   5489     def __setattr__(self, name: str, value) -> None:

AttributeError: 'Series' object has no attribute 'columns'
```

The expected output here is a Series as 0.10.2 returns:
```py
0            Oceania
1             Africa
2             Africa
3      North America
4      North America
           ...      
172           Europe
173           Europe
174           Europe
175    North America
176           Africa
Length: 177, dtype: object
```

cc @m-richards 


Discussion:
This is also caused by https://github.com/geopandas/geopandas/pull/2060/, but a different issue as https://github.com/geopandas/geopandas/issues/2282

(@martinfleis thanks for those reports btw! it's good that we didn't end up merging that just before the 0.10 release :))

It's related to this part of the diff: https://github.com/geopandas/geopandas/pull/2060/files#diff-2bf1533512765e9a8c8ca4992b8bc2828c4f5567d9a853f160f1dbd0ea473c20L1393-R1420. The `self._geometry_column_name in result.columns` was previously guarded by an `if isinstance(result, GeoDataFrame)`, so we only checked `result.columns` if `result` actually has columns. 
I suppose adding back that check should fix this.

Resolving patch:
diff --git a/geopandas/geodataframe.py b/geopandas/geodataframe.py
--- a/geopandas/geodataframe.py
+++ b/geopandas/geodataframe.py
@@ -1408,7 +1408,10 @@ def apply(self, func, axis=0, raw=False, result_type=None, args=(), **kwargs):
             func, axis=axis, raw=raw, result_type=result_type, args=args, **kwargs
         )
         # Reconstruct gdf if it was lost by apply
-        if self._geometry_column_name in result.columns:
+        if (
+            isinstance(result, DataFrame)
+            and self._geometry_column_name in result.columns
+        ):
             # axis=1 apply will split GeometryDType to object, try and cast back
             try:
                 result = result.set_geometry(self._geometry_column_name)


Test patch:
diff --git a/geopandas/tests/test_pandas_methods.py b/geopandas/tests/test_pandas_methods.py
--- a/geopandas/tests/test_pandas_methods.py
+++ b/geopandas/tests/test_pandas_methods.py
@@ -603,6 +603,15 @@ def test_apply_preserves_geom_col_name(df):
     assert result.geometry.name == "geom"
 
 
+def test_df_apply_returning_series(df):
+    # https://github.com/geopandas/geopandas/issues/2283
+    result = df.apply(lambda row: row.geometry, axis=1)
+    assert_geoseries_equal(result, df.geometry, check_crs=False)
+
+    result = df.apply(lambda row: row.value1, axis=1)
+    assert_series_equal(result, df["value1"].rename(None))
+
+
 @pytest.mark.skipif(not compat.PANDAS_GE_10, reason="attrs introduced in pandas 1.0")
 def test_preserve_attrs(df):
     # https://github.com/geopandas/geopandas/issues/1654