Repository: databricks/koalas

Issue:
DataFrame.dtypes
Raising this to discuss what the handling of the `.dtypes` should be.
Current implementations:
 - Pandas: Returns a pandas Series with index of fieldnames and values as dtypes
 - Spark: Returns a list of tuples with (fieldname, dtype)
 - Koala: Same as Spark

Other Differences:
 - Whether to show indexes in .dtypes:
   - Pandas: Shows only columns and not indexes
   - Spark: Shows all columns, no index concept
   - Koala: Shows columns and indexes
 - Type values:
   - Pandas: Shows strings, arrays, map, etc as `object`
   - Spark: Shows strings, arrays, map, etc in appropriate simpleString notation
   - Koala: Same as spark
 - Dtype notation:
   - Pandas: Shows numpy type for the dtype
   - Spark: Shows simpleString notation
   - Koala: Same as spark



Discussion:
It sort of depends on what users use dtypes for. If they are using it to programmatically get the column names and data types, then it'd make sense to be as much the same as Pandas as possible. If they are just using that to inspect the dataset's schema, then the existing one should be fine.

When you say inspect do you mean if they manually inspect it? 
Wouldn't manually inspection be OK if it's pandas behaviour too? 
Unless there are objections, I think that the behaviour for `dtypes` should be to return the same as pandas. Spark users will have `schema` if they want to have the underlying spark types.

Resolving patch:
diff --git a/databricks/koalas/frame.py b/databricks/koalas/frame.py
--- a/databricks/koalas/frame.py
+++ b/databricks/koalas/frame.py
@@ -413,6 +413,35 @@ def columns(self, names):
         self._sdf = sdf
         self._metadata = self._metadata.copy(column_fields=names)
 
+    @property
+    def dtypes(self):
+        """Return the dtypes in the DataFrame.
+
+        This returns a Series with the data type of each column. The result's index is the original
+        DataFrame's columns. Columns with mixed types are stored with the object dtype.
+
+        :return: :class:`pd.Series` The data type of each column.
+
+        Examples
+        --------
+        >>> df = ks.DataFrame({'a': list('abc'),
+        ...                    'b': list(range(1, 4)),
+        ...                    'c': np.arange(3, 6).astype('i1'),
+        ...                    'd': np.arange(4.0, 7.0, dtype='float64'),
+        ...                    'e': [True, False, True],
+        ...                    'f': pd.date_range('20130101', periods=3)})  # doctest: +SKIP
+        >>> df.dtypes  # doctest: +SKIP
+        a            object
+        b             int64
+        c             int64
+        d           float64
+        e              bool
+        f    datetime64[ns]
+        dtype: object
+        """
+        return pd.Series([self[col].dtype for col in self._metadata.column_fields],
+                         index=self._metadata.column_fields)
+
     @derived_from(pd.DataFrame, ua_args=['axis', 'level', 'numeric_only'])
     def count(self):
         return self._sdf.count()
diff --git a/databricks/koalas/series.py b/databricks/koalas/series.py
--- a/databricks/koalas/series.py
+++ b/databricks/koalas/series.py
@@ -25,7 +25,8 @@
 
 from pyspark import sql as spark
 from pyspark.sql import functions as F
-from pyspark.sql.types import FloatType, DoubleType, LongType, StructType, TimestampType
+from pyspark.sql.types import FloatType, DoubleType, LongType, StructType, TimestampType, \
+    to_arrow_type
 
 from databricks.koalas.dask.utils import derived_from
 from databricks.koalas.frame import DataFrame
@@ -161,8 +162,26 @@ def __sub__(self, other):
 
     @property
     def dtype(self):
-        from databricks.koalas.typing import as_python_type
-        return as_python_type(self.schema.fields[-1].dataType)
+        """Return the dtype object of the underlying data.
+
+        Examples
+        --------
+        >>> s = ks.Series([1, 2, 3])  # doctest: +SKIP
+        >>> s.dtype  # doctest: +SKIP
+        dtype('int64')
+
+        >>> s = ks.Series(list('abc'))  # doctest: +SKIP
+        >>> s.dtype  # doctest: +SKIP
+        dtype('O')
+
+        >>> s = ks.Series(pd.date_range('20130101', periods=3))  # doctest: +SKIP
+        >>> s.dtype  # doctest: +SKIP
+        dtype('<M8[ns]')
+        """
+        if type(self.spark_type) == TimestampType:
+            return np.dtype('datetime64[ns]')
+        else:
+            return np.dtype(to_arrow_type(self.spark_type).to_pandas_dtype())
 
     @property
     def spark_type(self):


Test patch:
diff --git a/databricks/koalas/tests/test_dataframe.py b/databricks/koalas/tests/test_dataframe.py
--- a/databricks/koalas/tests/test_dataframe.py
+++ b/databricks/koalas/tests/test_dataframe.py
@@ -282,6 +282,17 @@ def test_dropna(self):
         with self.assertRaisesRegex(NotImplementedError, msg):
             ddf.dropna(axis='foo')
 
+    def test_dtype(self):
+        pdf = pd.DataFrame({'a': list('abc'),
+                            'b': list(range(1, 4)),
+                            'c': np.arange(3, 6).astype('i1'),
+                            'd': np.arange(4.0, 7.0, dtype='float64'),
+                            'e': [True, False, True],
+                            'f': pd.date_range('20130101', periods=3)})
+        kdf = koalas.from_pandas(pdf)
+        self.assert_eq(kdf, pdf)
+        self.assertTrue((kdf.dtypes == pdf.dtypes).all())
+
     def test_value_counts(self):
         df = pd.DataFrame({'x': [1, 2, 1, 3, 3, np.nan, 1, 4]})
         ddf = koalas.from_pandas(df)