Repository: unionai-oss/pandera

Issue:
Coercion Converting Non-String Object to String
I'm trying to validate a DataFrame which has a column of `dict`s, and I'm also using Pandera to coerce some types.  Pandera version 0.5.1 worked as expected, but version 0.6.2 converts the object to a string.  I looked through the issues and docs and didn't see this discussed (unless I missed it!).  Which behavior is expected?  Am I missing something obvious here?

Thanks for Pandera, BTW!

```python
import pandas as pd
import pandera as pa

schema = pa.DataFrameSchema({"like_int": pa.Column(pa.Int), "like_dict": pa.Column(pa.Object)}, coerce=True, transformer=None, index=pa.Index(pa.Int))

df = pd.DataFrame(data=[[1, {'a': 1, 'b': 2}], [2, {'c': 3, 'd': 4}]], index=[7, 8], columns=['like_int', 'like_dict'])
df_val = schema.validate(df)

# Pandera version = 0.5.1 -> df_val['like_dict'][7] = {'a': 1, 'b': 2} (i.e. a dict)
# Pandera version = 0.6.2 -> df_val['like_dict'][7] = "{'a': 1, 'b': 2}" (i.e. a str)
```



Discussion:
hey @stevemarin, thanks for submitting this bug! This is a regression with respect to the `pa.Object` enum value. Will make a fix for this.

Alternatively, you can specify `object` or `"object"` as the data type, and it should preserve the `dict` type:
```python
schema = pa.DataFrameSchema(
    {"like_int": pa.Column(pa.Int), "like_dict": pa.Column(object)},
    coerce=True,
    transformer=None,
    index=pa.Index(pa.Int),
)
```
That did the trick and was simpler than my `ast.literal_eval` approach!  Thank you!

Resolving patch:
diff --git a/docs/source/conf.py b/docs/source/conf.py
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -78,11 +78,10 @@
 }
 
 # copy CONTRIBUTING.md docs into source directory
+root_dir = os.path.dirname(__file__)
 shutil.copyfile(
-    os.path.join(
-        os.path.dirname(__file__), "..", "..", ".github", "CONTRIBUTING.md"
-    ),
-    "CONTRIBUTING.md",
+    os.path.join(root_dir, "..", "..", ".github", "CONTRIBUTING.md"),
+    os.path.join(root_dir, "CONTRIBUTING.md"),
 )
 
 # Add any paths that contain templates here, relative to this directory.
diff --git a/pandera/schemas.py b/pandera/schemas.py
--- a/pandera/schemas.py
+++ b/pandera/schemas.py
@@ -1631,9 +1631,10 @@ def coerce_dtype(self, obj: Union[pd.Series, pd.Index]) -> pd.Series:
         :returns: ``Series`` with coerced data type
         """
         if (
-            self._pandas_dtype is dtypes.PandasDtype.String
+            self._pandas_dtype is PandasDtype.String
             or self._pandas_dtype is str
             or self._pandas_dtype == "str"
+            and self._pandas_dtype is not PandasDtype.Object
         ):
             # only coerce non-null elements to string, make sure series is of
             # object dtype


Test patch:
diff --git a/tests/core/test_schemas.py b/tests/core/test_schemas.py
--- a/tests/core/test_schemas.py
+++ b/tests/core/test_schemas.py
@@ -592,6 +592,26 @@ def test_coerce_dtype_nullable_str(
             assert pd.isna(element)
 
 
+@pytest.mark.parametrize(
+    "data, expected_type",
+    [
+        [{"a": 1, "b": 2, "c": 3}, dict],
+        [[1, 2, 3, 4], list],
+        [[1, {"a": 5}], list],
+        [{1, 2, 3}, set],
+    ],
+)
+@pytest.mark.parametrize("dtype", ["object", object, Object])
+def test_coerce_object_dtype(data, expected_type, dtype):
+    """Test coercing on object dtype."""
+    schema = DataFrameSchema({"col": Column(dtype)}, coerce=True)
+    df = pd.DataFrame({"col": [data] * 3})
+    validated_df = schema(df)
+    assert isinstance(validated_df, pd.DataFrame)
+    for _, x in validated_df["col"].iteritems():
+        assert isinstance(x, expected_type)
+
+
 def test_no_dtype_dataframe():
     """Test how nullability is handled in DataFrameSchemas where no type is
     specified."""