Repository: databricks/koalas

Issue:
DataFrame.info
Implement `DataFrame.info`.

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.info.html
`memory usage` is not needed for Koalas.


Discussion:
Hi @ueshin ! I was thinking about how to implement this in spark. I think null counts would be problematic since counting (and printing) nulls for every column is quite expensive in spark. Any suggestion here? Besides, I can start working on this and send a PR that adds a method to [koalas.DataFrame](https://github.com/databricks/koalas/blob/master/databricks/koalas/frame.py#L271)
@burakyilmaz321 please go ahead for other APIs. For this API, I think we can calculate via using Pyspark API (e.g., select(count(column.isNotNull)))` 

Resolving patch:
diff --git a/databricks/koalas/frame.py b/databricks/koalas/frame.py
--- a/databricks/koalas/frame.py
+++ b/databricks/koalas/frame.py
@@ -8036,6 +8036,109 @@ def idxmin(self, axis=0):
 
         return ks.from_pandas(pdf.idxmin())
 
+    def info(
+        self, verbose=None, buf=None, max_cols=None, null_counts=None
+    ):
+        """
+        Print a concise summary of a DataFrame.
+
+        This method prints information about a DataFrame including
+        the index dtype and column dtypes, non-null values and memory usage.
+
+        Parameters
+        ----------
+        verbose : bool, optional
+            Whether to print the full summary.
+        buf : writable buffer, defaults to sys.stdout
+            Where to send the output. By default, the output is printed to
+            sys.stdout. Pass a writable buffer if you need to further process
+            the output.
+        max_cols : int, optional
+            When to switch from the verbose to the truncated output. If the
+            DataFrame has more than `max_cols` columns, the truncated output
+            is used.
+        null_counts : bool, optional
+            Whether to show the non-null counts.
+
+        Returns
+        -------
+        None
+            This method prints a summary of a DataFrame and returns None.
+
+        See Also
+        --------
+        DataFrame.describe: Generate descriptive statistics of DataFrame
+            columns.
+
+        Examples
+        --------
+        >>> int_values = [1, 2, 3, 4, 5]
+        >>> text_values = ['alpha', 'beta', 'gamma', 'delta', 'epsilon']
+        >>> float_values = [0.0, 0.25, 0.5, 0.75, 1.0]
+        >>> df = ks.DataFrame(
+        ...     {"int_col": int_values, "text_col": text_values, "float_col": float_values},
+        ...     columns=['int_col', 'text_col', 'float_col'])
+        >>> df
+           int_col text_col  float_col
+        0        1    alpha       0.00
+        1        2     beta       0.25
+        2        3    gamma       0.50
+        3        4    delta       0.75
+        4        5  epsilon       1.00
+
+        Prints information of all columns:
+
+        >>> df.info(verbose=True)  # doctest: +SKIP
+        <class 'databricks.koalas.frame.DataFrame'>
+        Index: 5 entries, 0 to 4
+        Data columns (total 3 columns):
+        int_col      5 non-null int64
+        text_col     5 non-null object
+        float_col    5 non-null float64
+        dtypes: float64(1), int64(1), object(1)
+
+        Prints a summary of columns count and its dtypes but not per column
+        information:
+
+        >>> df.info(verbose=False)  # doctest: +SKIP
+        <class 'databricks.koalas.frame.DataFrame'>
+        Index: 5 entries, 0 to 4
+        Columns: 3 entries, int_col to float_col
+        dtypes: float64(1), int64(1), object(1)
+
+        Pipe output of DataFrame.info to buffer instead of sys.stdout, get
+        buffer content and writes to a text file:
+
+        >>> import io
+        >>> buffer = io.StringIO()
+        >>> df.info(buf=buffer)
+        >>> s = buffer.getvalue()
+        >>> with open('%s/info.txt' % path, "w",
+        ...           encoding="utf-8") as f:
+        ...     _ = f.write(s)
+        >>> with open('%s/info.txt' % path) as f:
+        ...     f.readlines()  # doctest: +NORMALIZE_WHITESPACE, +ELLIPSIS
+        [...databricks.koalas.frame.DataFrame...,
+        'Index: 5 entries, 0 to 4\\n',
+        'Data columns (total 3 columns):\\n',
+        'int_col      5 non-null int64\\n',
+        'text_col     5 non-null object\\n',
+        'float_col    5 non-null float64\\n',
+        'dtypes: float64(1), int64(1), object(1)']
+        """
+        # To avoid pandas' existing config affects Koalas.
+        # TODO: should we have corresponding Koalas configs?
+        with pd.option_context(
+                'display.max_info_columns', sys.maxsize,
+                'display.max_info_rows', sys.maxsize):
+            try:
+                self._data = self  # hack to use pandas' info as is.
+                return pd.DataFrame.info(
+                    self, verbose=verbose, buf=buf, max_cols=max_cols,
+                    memory_usage=False, null_counts=null_counts)
+            finally:
+                del self._data
+
     # TODO: fix parameter 'axis' and 'numeric_only' to work same as pandas'
     def quantile(self, q=0.5, axis=0, numeric_only=True, accuracy=10000):
         """
diff --git a/databricks/koalas/indexes.py b/databricks/koalas/indexes.py
--- a/databricks/koalas/indexes.py
+++ b/databricks/koalas/indexes.py
@@ -25,6 +25,7 @@
 import numpy as np
 from pandas.api.types import is_list_like, is_interval_dtype, is_bool_dtype, \
     is_categorical_dtype, is_integer_dtype, is_float_dtype, is_numeric_dtype, is_object_dtype
+from pandas.io.formats.printing import pprint_thing
 
 from pyspark import sql as spark
 from pyspark.sql import functions as F
@@ -104,6 +105,34 @@ def _with_new_scol(self, scol: spark.Column) -> 'Index':
         """
         return Index(self._kdf, scol=scol)
 
+    # This method is used via `DataFrame.info` API internally.
+    def _summary(self, name=None):
+        """
+        Return a summarized representation.
+
+        Parameters
+        ----------
+        name : str
+            name to use in the summary representation
+
+        Returns
+        -------
+        String with a summarized representation of the index
+        """
+        head, tail, total_count = self._kdf._sdf.select(
+            F.first(self._scol),
+            F.last(self._scol),
+            F.count(F.expr("*"))).first()
+
+        if total_count > 0:
+            index_summary = ", %s to %s" % (pprint_thing(head), pprint_thing(tail))
+        else:
+            index_summary = ""
+
+        if name is None:
+            name = type(self).__name__
+        return "%s: %s entries%s" % (name, total_count, index_summary)
+
     @property
     def size(self) -> int:
         """
diff --git a/databricks/koalas/missing/frame.py b/databricks/koalas/missing/frame.py
--- a/databricks/koalas/missing/frame.py
+++ b/databricks/koalas/missing/frame.py
@@ -57,7 +57,6 @@ class _MissingPandasLikeDataFrame(object):
     ewm = unsupported_function('ewm')
     first = unsupported_function('first')
     infer_objects = unsupported_function('infer_objects')
-    info = unsupported_function('info')
     insert = unsupported_function('insert')
     interpolate = unsupported_function('interpolate')
     itertuples = unsupported_function('itertuples')


Test patch: