def describe(self, percentiles: Optional[List[float]]=None) -> 'DataFrame':
    "\n        Generate descriptive statistics that summarize the central tendency,\n        dispersion and shape of a dataset's distribution, excluding\n        ``NaN`` values.\n\n        Analyzes both numeric and object series, as well\n        as ``DataFrame`` column sets of mixed data types. The output\n        will vary depending on what is provided. Refer to the notes\n        below for more detail.\n\n        Parameters\n        ----------\n        percentiles : list of ``float`` in range [0.0, 1.0], default [0.25, 0.5, 0.75]\n            A list of percentiles to be computed.\n\n        Returns\n        -------\n        DataFrame\n            Summary statistics of the Dataframe provided.\n\n        See Also\n        --------\n        DataFrame.count: Count number of non-NA/null observations.\n        DataFrame.max: Maximum of the values in the object.\n        DataFrame.min: Minimum of the values in the object.\n        DataFrame.mean: Mean of the values.\n        DataFrame.std: Standard deviation of the observations.\n\n        Notes\n        -----\n        For numeric data, the result's index will include ``count``,\n        ``mean``, ``std``, ``min``, ``25%``, ``50%``, ``75%``, ``max``.\n\n        For object data (e.g. strings or timestamps), the result’s index will include\n        ``count``, ``unique``, ``top``, and ``freq``.\n        The ``top`` is the most common value. The ``freq`` is the most common value’s frequency.\n        Timestamps also include the ``first`` and ``last`` items.\n\n        Examples\n        --------\n        Describing a numeric ``Series``.\n\n        >>> s = ps.Series([1, 2, 3])\n        >>> s.describe()\n        count    3.0\n        mean     2.0\n        std      1.0\n        min      1.0\n        25%      1.0\n        50%      2.0\n        75%      3.0\n        max      3.0\n        dtype: float64\n\n        Describing a ``DataFrame``. Only numeric fields are returned.\n\n        >>> df = ps.DataFrame({'numeric1': [1, 2, 3],\n        ...                    'numeric2': [4.0, 5.0, 6.0],\n        ...                    'object': ['a', 'b', 'c']\n        ...                   },\n        ...                   columns=['numeric1', 'numeric2', 'object'])\n        >>> df.describe()\n               numeric1  numeric2\n        count       3.0       3.0\n        mean        2.0       5.0\n        std         1.0       1.0\n        min         1.0       4.0\n        25%         1.0       4.0\n        50%         2.0       5.0\n        75%         3.0       6.0\n        max         3.0       6.0\n\n        For multi-index columns:\n\n        >>> df.columns = [('num', 'a'), ('num', 'b'), ('obj', 'c')]\n        >>> df.describe()  # doctest: +NORMALIZE_WHITESPACE\n               num\n                 a    b\n        count  3.0  3.0\n        mean   2.0  5.0\n        std    1.0  1.0\n        min    1.0  4.0\n        25%    1.0  4.0\n        50%    2.0  5.0\n        75%    3.0  6.0\n        max    3.0  6.0\n\n        >>> df[('num', 'b')].describe()\n        count    3.0\n        mean     5.0\n        std      1.0\n        min      4.0\n        25%      4.0\n        50%      5.0\n        75%      6.0\n        max      6.0\n        Name: (num, b), dtype: float64\n\n        Describing a ``DataFrame`` and selecting custom percentiles.\n\n        >>> df = ps.DataFrame({'numeric1': [1, 2, 3],\n        ...                    'numeric2': [4.0, 5.0, 6.0]\n        ...                   },\n        ...                   columns=['numeric1', 'numeric2'])\n        >>> df.describe(percentiles = [0.85, 0.15])\n               numeric1  numeric2\n        count       3.0       3.0\n        mean        2.0       5.0\n        std         1.0       1.0\n        min         1.0       4.0\n        15%         1.0       4.0\n        50%         2.0       5.0\n        85%         3.0       6.0\n        max         3.0       6.0\n\n        Describing a column from a ``DataFrame`` by accessing it as\n        an attribute.\n\n        >>> df.numeric1.describe()\n        count    3.0\n        mean     2.0\n        std      1.0\n        min      1.0\n        25%      1.0\n        50%      2.0\n        75%      3.0\n        max      3.0\n        Name: numeric1, dtype: float64\n\n        Describing a column from a ``DataFrame`` by accessing it as\n        an attribute and selecting custom percentiles.\n\n        >>> df.numeric1.describe(percentiles = [0.85, 0.15])\n        count    3.0\n        mean     2.0\n        std      1.0\n        min      1.0\n        15%      1.0\n        50%      2.0\n        85%      3.0\n        max      3.0\n        Name: numeric1, dtype: float64\n        "
    psser_numeric: List[Series] = []
    psser_string: List[Series] = []
    psser_timestamp: List[Series] = []
    spark_data_types: List[DataType] = []
    column_labels: Optional[List[Label]] = []
    column_names: List[str] = []
    for label in self._internal.column_labels:
        psser = self._psser_for(label)
        spark_data_type = psser.spark.data_type
        if isinstance(spark_data_type, NumericType):
            psser_numeric.append(psser)
            column_labels.append(label)
            spark_data_types.append(spark_data_type)
        elif isinstance(spark_data_type, (TimestampType, TimestampNTZType)):
            psser_timestamp.append(psser)
            column_labels.append(label)
            spark_data_types.append(spark_data_type)
        else:
            psser_string.append(psser)
            column_names.append(self._internal.spark_column_name_for(label))
    if (percentiles is not None):
        if any((((p < 0.0) or (p > 1.0)) for p in percentiles)):
            raise ValueError('Percentiles should all be in the interval [0, 1]')
        percentiles = ((percentiles + [0.5]) if (0.5 not in percentiles) else percentiles)
    else:
        percentiles = [0.25, 0.5, 0.75]
    is_all_string_type = ((len(psser_numeric) == 0) and (len(psser_timestamp) == 0) and (len(psser_string) > 0))
    is_all_numeric_type = ((len(psser_numeric) > 0) and (len(psser_timestamp) == 0))
    has_timestamp_type = (len(psser_timestamp) > 0)
    has_numeric_type = (len(psser_numeric) > 0)
    if is_all_string_type:
        internal = self._internal.resolved_copy
        exprs_string = [internal.spark_column_for(psser._column_label) for psser in psser_string]
        sdf = internal.spark_frame.select(*exprs_string)
        (counts, uniques) = map((lambda x: x[1:]), sdf.summary('count', 'count_distinct').take(2))
        if ((len(counts) == 0) or (counts[0] == '0')):
            data = dict()
            for psser in psser_string:
                data[psser.name] = [0, 0, np.nan, np.nan]
            return DataFrame(data, index=['count', 'unique', 'top', 'freq'])
        tops = []
        freqs = []
        for column in exprs_string:
            (top, freq) = sdf.groupby(column).count().sort('count', ascending=False).first()
            tops.append(str(top))
            freqs.append(str(freq))
        stats = [counts, uniques, tops, freqs]
        stats_names = ['count', 'unique', 'top', 'freq']
        result: DataFrame = DataFrame(data=stats, index=stats_names, columns=column_names)
    elif is_all_numeric_type:
        exprs_numeric = [psser._dtype_op.nan_to_null(psser).spark.column for psser in psser_numeric]
        formatted_perc = ['{:.0%}'.format(p) for p in sorted(percentiles)]
        stats = ['count', 'mean', 'stddev', 'min', *formatted_perc, 'max']
        sdf = self._internal.spark_frame.select(*exprs_numeric).summary(*stats)
        sdf = sdf.replace('stddev', 'std', subset=['summary'])
        internal = InternalFrame(spark_frame=sdf, index_spark_columns=[scol_for(sdf, 'summary')], column_labels=column_labels, data_spark_columns=[scol_for(sdf, self._internal.spark_column_name_for(label)) for label in column_labels])
        result = DataFrame(internal).astype('float64')
    elif has_timestamp_type:
        internal = self._internal.resolved_copy
        column_names = [internal.spark_column_name_for(column_label) for column_label in column_labels]
        column_length = len(column_labels)
        count_exprs = map(F.count, column_names)
        min_exprs = map(F.min, column_names)
        perc_exprs = chain(*[map(F.percentile_approx, column_names, ([percentile] * column_length)) for percentile in percentiles])
        max_exprs = map(F.max, column_names)
        mean_exprs = []
        for (column_name, spark_data_type) in zip(column_names, spark_data_types):
            mean_exprs.append(F.mean(column_name).astype(spark_data_type))
        exprs = [*count_exprs, *mean_exprs, *min_exprs, *perc_exprs, *max_exprs]
        formatted_perc = ['{:.0%}'.format(p) for p in sorted(percentiles)]
        stats_names = ['count', 'mean', 'min', *formatted_perc, 'max']
        if has_numeric_type:
            std_exprs = []
            for (label, spark_data_type) in zip(column_labels, spark_data_types):
                column_name = label[0]
                if isinstance(spark_data_type, (TimestampType, TimestampNTZType)):
                    std_exprs.append(F.lit(None).alias('stddev_samp({})'.format(column_name)))
                else:
                    std_exprs.append(F.stddev(column_name))
            exprs.extend(std_exprs)
            stats_names.append('std')
        sdf = internal.spark_frame.select(exprs)
        stat_values = sdf.first()
        num_stats = int((len(exprs) / column_length))
        column_name_stats_kv: Dict[(str, List[str])] = defaultdict(list)
        for (i, column_name) in enumerate(column_names):
            for first_stat_idx in range(num_stats):
                column_name_stats_kv[column_name].append(stat_values[((first_stat_idx * column_length) + i)])
        for (key, spark_data_type) in zip(column_name_stats_kv, spark_data_types):
            if isinstance(spark_data_type, (TimestampType, TimestampNTZType)):
                column_name_stats_kv[key] = [str(value) for value in column_name_stats_kv[key]]
        result: DataFrame = DataFrame(data=column_name_stats_kv, index=stats_names, columns=column_names)
    else:
        raise ValueError('Cannot describe a DataFrame without columns')
    return result
Generate descriptive statistics that summarize the central tendency,
dispersion and shape of a dataset's distribution, excluding
``NaN`` values.

Analyzes both numeric and object series, as well
as ``DataFrame`` column sets of mixed data types. The output
will vary depending on what is provided. Refer to the notes
below for more detail.

Parameters
----------
percentiles : list of ``float`` in range [0.0, 1.0], default [0.25, 0.5, 0.75]
    A list of percentiles to be computed.

Returns
-------
DataFrame
    Summary statistics of the Dataframe provided.

See Also
--------
DataFrame.count: Count number of non-NA/null observations.
DataFrame.max: Maximum of the values in the object.
DataFrame.min: Minimum of the values in the object.
DataFrame.mean: Mean of the values.
DataFrame.std: Standard deviation of the observations.

Notes
-----
For numeric data, the result's index will include ``count``,
``mean``, ``std``, ``min``, ``25%``, ``50%``, ``75%``, ``max``.

For object data (e.g. strings or timestamps), the result’s index will include
``count``, ``unique``, ``top``, and ``freq``.
The ``top`` is the most common value. The ``freq`` is the most common value’s frequency.
Timestamps also include the ``first`` and ``last`` items.

Examples
--------
Describing a numeric ``Series``.

>>> s = ps.Series([1, 2, 3])
>>> s.describe()
count    3.0
mean     2.0
std      1.0
min      1.0
25%      1.0
50%      2.0
75%      3.0
max      3.0
dtype: float64

Describing a ``DataFrame``. Only numeric fields are returned.

>>> df = ps.DataFrame({'numeric1': [1, 2, 3],
...                    'numeric2': [4.0, 5.0, 6.0],
...                    'object': ['a', 'b', 'c']
...                   },
...                   columns=['numeric1', 'numeric2', 'object'])
>>> df.describe()
       numeric1  numeric2
count       3.0       3.0
mean        2.0       5.0
std         1.0       1.0
min         1.0       4.0
25%         1.0       4.0
50%         2.0       5.0
75%         3.0       6.0
max         3.0       6.0

For multi-index columns:

>>> df.columns = [('num', 'a'), ('num', 'b'), ('obj', 'c')]
>>> df.describe()  # doctest: +NORMALIZE_WHITESPACE
       num
         a    b
count  3.0  3.0
mean   2.0  5.0
std    1.0  1.0
min    1.0  4.0
25%    1.0  4.0
50%    2.0  5.0
75%    3.0  6.0
max    3.0  6.0

>>> df[('num', 'b')].describe()
count    3.0
mean     5.0
std      1.0
min      4.0
25%      4.0
50%      5.0
75%      6.0
max      6.0
Name: (num, b), dtype: float64

Describing a ``DataFrame`` and selecting custom percentiles.

>>> df = ps.DataFrame({'numeric1': [1, 2, 3],
...                    'numeric2': [4.0, 5.0, 6.0]
...                   },
...                   columns=['numeric1', 'numeric2'])
>>> df.describe(percentiles = [0.85, 0.15])
       numeric1  numeric2
count       3.0       3.0
mean        2.0       5.0
std         1.0       1.0
min         1.0       4.0
15%         1.0       4.0
50%         2.0       5.0
85%         3.0       6.0
max         3.0       6.0

Describing a column from a ``DataFrame`` by accessing it as
an attribute.

>>> df.numeric1.describe()
count    3.0
mean     2.0
std      1.0
min      1.0
25%      1.0
50%      2.0
75%      3.0
max      3.0
Name: numeric1, dtype: float64

Describing a column from a ``DataFrame`` by accessing it as
an attribute and selecting custom percentiles.

>>> df.numeric1.describe(percentiles = [0.85, 0.15])
count    3.0
mean     2.0
std      1.0
min      1.0
15%      1.0
50%      2.0
85%      3.0
max      3.0
Name: numeric1, dtype: float64
def describe(self, percentiles: Optional[List[float]]=None) -> 'DataFrame':
    "\n        Generate descriptive statistics that summarize the central tendency,\n        dispersion and shape of a dataset's distribution, excluding\n        ``NaN`` values.\n\n        Analyzes both numeric and object series, as well\n        as ``DataFrame`` column sets of mixed data types. The output\n        will vary depending on what is provided. Refer to the notes\n        below for more detail.\n\n        Parameters\n        ----------\n        percentiles : list of ``float`` in range [0.0, 1.0], default [0.25, 0.5, 0.75]\n            A list of percentiles to be computed.\n\n        Returns\n        -------\n        DataFrame\n            Summary statistics of the Dataframe provided.\n\n        See Also\n        --------\n        DataFrame.count: Count number of non-NA/null observations.\n        DataFrame.max: Maximum of the values in the object.\n        DataFrame.min: Minimum of the values in the object.\n        DataFrame.mean: Mean of the values.\n        DataFrame.std: Standard deviation of the observations.\n\n        Notes\n        -----\n        For numeric data, the result's index will include ``count``,\n        ``mean``, ``std``, ``min``, ``25%``, ``50%``, ``75%``, ``max``.\n\n        For object data (e.g. strings or timestamps), the result’s index will include\n        ``count``, ``unique``, ``top``, and ``freq``.\n        The ``top`` is the most common value. The ``freq`` is the most common value’s frequency.\n        Timestamps also include the ``first`` and ``last`` items.\n\n        Examples\n        --------\n        Describing a numeric ``Series``.\n\n        >>> s = ps.Series([1, 2, 3])\n        >>> s.describe()\n        count    3.0\n        mean     2.0\n        std      1.0\n        min      1.0\n        25%      1.0\n        50%      2.0\n        75%      3.0\n        max      3.0\n        dtype: float64\n\n        Describing a ``DataFrame``. Only numeric fields are returned.\n\n        >>> df = ps.DataFrame({'numeric1': [1, 2, 3],\n        ...                    'numeric2': [4.0, 5.0, 6.0],\n        ...                    'object': ['a', 'b', 'c']\n        ...                   },\n        ...                   columns=['numeric1', 'numeric2', 'object'])\n        >>> df.describe()\n               numeric1  numeric2\n        count       3.0       3.0\n        mean        2.0       5.0\n        std         1.0       1.0\n        min         1.0       4.0\n        25%         1.0       4.0\n        50%         2.0       5.0\n        75%         3.0       6.0\n        max         3.0       6.0\n\n        For multi-index columns:\n\n        >>> df.columns = [('num', 'a'), ('num', 'b'), ('obj', 'c')]\n        >>> df.describe()  # doctest: +NORMALIZE_WHITESPACE\n               num\n                 a    b\n        count  3.0  3.0\n        mean   2.0  5.0\n        std    1.0  1.0\n        min    1.0  4.0\n        25%    1.0  4.0\n        50%    2.0  5.0\n        75%    3.0  6.0\n        max    3.0  6.0\n\n        >>> df[('num', 'b')].describe()\n        count    3.0\n        mean     5.0\n        std      1.0\n        min      4.0\n        25%      4.0\n        50%      5.0\n        75%      6.0\n        max      6.0\n        Name: (num, b), dtype: float64\n\n        Describing a ``DataFrame`` and selecting custom percentiles.\n\n        >>> df = ps.DataFrame({'numeric1': [1, 2, 3],\n        ...                    'numeric2': [4.0, 5.0, 6.0]\n        ...                   },\n        ...                   columns=['numeric1', 'numeric2'])\n        >>> df.describe(percentiles = [0.85, 0.15])\n               numeric1  numeric2\n        count       3.0       3.0\n        mean        2.0       5.0\n        std         1.0       1.0\n        min         1.0       4.0\n        15%         1.0       4.0\n        50%         2.0       5.0\n        85%         3.0       6.0\n        max         3.0       6.0\n\n        Describing a column from a ``DataFrame`` by accessing it as\n        an attribute.\n\n        >>> df.numeric1.describe()\n        count    3.0\n        mean     2.0\n        std      1.0\n        min      1.0\n        25%      1.0\n        50%      2.0\n        75%      3.0\n        max      3.0\n        Name: numeric1, dtype: float64\n\n        Describing a column from a ``DataFrame`` by accessing it as\n        an attribute and selecting custom percentiles.\n\n        >>> df.numeric1.describe(percentiles = [0.85, 0.15])\n        count    3.0\n        mean     2.0\n        std      1.0\n        min      1.0\n        15%      1.0\n        50%      2.0\n        85%      3.0\n        max      3.0\n        Name: numeric1, dtype: float64\n        "
    psser_numeric: List[Series] = []
    psser_string: List[Series] = []
    psser_timestamp: List[Series] = []
    spark_data_types: List[DataType] = []
    column_labels: Optional[List[Label]] = []
    column_names: List[str] = []
    for label in self._internal.column_labels:
        psser = self._psser_for(label)
        spark_data_type = psser.spark.data_type
        if isinstance(spark_data_type, NumericType):
            psser_numeric.append(psser)
            column_labels.append(label)
            spark_data_types.append(spark_data_type)
        elif isinstance(spark_data_type, (TimestampType, TimestampNTZType)):
            psser_timestamp.append(psser)
            column_labels.append(label)
            spark_data_types.append(spark_data_type)
        else:
            psser_string.append(psser)
            column_names.append(self._internal.spark_column_name_for(label))
    if (percentiles is not None):
        if any((((p < 0.0) or (p > 1.0)) for p in percentiles)):
            raise ValueError('Percentiles should all be in the interval [0, 1]')
        percentiles = ((percentiles + [0.5]) if (0.5 not in percentiles) else percentiles)
    else:
        percentiles = [0.25, 0.5, 0.75]
    is_all_string_type = ((len(psser_numeric) == 0) and (len(psser_timestamp) == 0) and (len(psser_string) > 0))
    is_all_numeric_type = ((len(psser_numeric) > 0) and (len(psser_timestamp) == 0))
    has_timestamp_type = (len(psser_timestamp) > 0)
    has_numeric_type = (len(psser_numeric) > 0)
    if is_all_string_type:
        internal = self._internal.resolved_copy
        exprs_string = [internal.spark_column_for(psser._column_label) for psser in psser_string]
        sdf = internal.spark_frame.select(*exprs_string)
        (counts, uniques) = map((lambda x: x[1:]), sdf.summary('count', 'count_distinct').take(2))
        if ((len(counts) == 0) or (counts[0] == '0')):
            data = dict()
            for psser in psser_string:
                data[psser.name] = [0, 0, np.nan, np.nan]
            return DataFrame(data, index=['count', 'unique', 'top', 'freq'])
        tops = []
        freqs = []
        for column in exprs_string:
            (top, freq) = sdf.groupby(column).count().sort('count', ascending=False).first()
            tops.append(str(top))
            freqs.append(str(freq))
        stats = [counts, uniques, tops, freqs]
        stats_names = ['count', 'unique', 'top', 'freq']
        result: DataFrame = DataFrame(data=stats, index=stats_names, columns=column_names)
    elif is_all_numeric_type:
        exprs_numeric = [psser._dtype_op.nan_to_null(psser).spark.column for psser in psser_numeric]
        formatted_perc = ['{:.0%}'.format(p) for p in sorted(percentiles)]
        stats = ['count', 'mean', 'stddev', 'min', *formatted_perc, 'max']
        sdf = self._internal.spark_frame.select(*exprs_numeric).summary(*stats)
        sdf = sdf.replace('stddev', 'std', subset=['summary'])
        internal = InternalFrame(spark_frame=sdf, index_spark_columns=[scol_for(sdf, 'summary')], column_labels=column_labels, data_spark_columns=[scol_for(sdf, self._internal.spark_column_name_for(label)) for label in column_labels])
        result = DataFrame(internal).astype('float64')
    elif has_timestamp_type:
        internal = self._internal.resolved_copy
        column_names = [internal.spark_column_name_for(column_label) for column_label in column_labels]
        column_length = len(column_labels)
        count_exprs = map(F.count, column_names)
        min_exprs = map(F.min, column_names)
        perc_exprs = chain(*[map(F.percentile_approx, column_names, ([percentile] * column_length)) for percentile in percentiles])
        max_exprs = map(F.max, column_names)
        mean_exprs = []
        for (column_name, spark_data_type) in zip(column_names, spark_data_types):
            mean_exprs.append(F.mean(column_name).astype(spark_data_type))
        exprs = [*count_exprs, *mean_exprs, *min_exprs, *perc_exprs, *max_exprs]
        formatted_perc = ['{:.0%}'.format(p) for p in sorted(percentiles)]
        stats_names = ['count', 'mean', 'min', *formatted_perc, 'max']
        if has_numeric_type:
            std_exprs = []
            for (label, spark_data_type) in zip(column_labels, spark_data_types):
                column_name = label[0]
                if isinstance(spark_data_type, (TimestampType, TimestampNTZType)):
                    std_exprs.append(F.lit(None).alias('stddev_samp({})'.format(column_name)))
                else:
                    std_exprs.append(F.stddev(column_name))
            exprs.extend(std_exprs)
            stats_names.append('std')
        sdf = internal.spark_frame.select(exprs)
        stat_values = sdf.first()
        num_stats = int((len(exprs) / column_length))
        column_name_stats_kv: Dict[(str, List[str])] = defaultdict(list)
        for (i, column_name) in enumerate(column_names):
            for first_stat_idx in range(num_stats):
                column_name_stats_kv[column_name].append(stat_values[((first_stat_idx * column_length) + i)])
        for (key, spark_data_type) in zip(column_name_stats_kv, spark_data_types):
            if isinstance(spark_data_type, (TimestampType, TimestampNTZType)):
                column_name_stats_kv[key] = [str(value) for value in column_name_stats_kv[key]]
        result: DataFrame = DataFrame(data=column_name_stats_kv, index=stats_names, columns=column_names)
    else:
        raise ValueError('Cannot describe a DataFrame without columns')
    return result