From 1a1ddee05ee2a7d89efda8c1ab12a9c7be6b01d7 Mon Sep 17 00:00:00 2001 From: Marco Gorelli Date: Thu, 19 Dec 2019 16:36:58 +0000 Subject: [PATCH] :sparkles: Add to_markdown method --- doc/source/whatsnew/v1.0.0.rst | 1 + environment.yml | 1 + pandas/core/frame.py | 24 +++++++++++++++++++++ pandas/tests/io/formats/test_to_markdown.py | 12 +++++++++++ pandas/util/_test_decorators.py | 7 ++++++ requirements-dev.txt | 1 + 6 files changed, 46 insertions(+) create mode 100644 pandas/tests/io/formats/test_to_markdown.py diff --git a/doc/source/whatsnew/v1.0.0.rst b/doc/source/whatsnew/v1.0.0.rst index a31db9712d5b84..1e3a672fd6180f 100644 --- a/doc/source/whatsnew/v1.0.0.rst +++ b/doc/source/whatsnew/v1.0.0.rst @@ -207,6 +207,7 @@ Other enhancements - The ``partition_cols`` argument in :meth:`DataFrame.to_parquet` now accepts a string (:issue:`27117`) - :func:`to_parquet` now appropriately handles the ``schema`` argument for user defined schemas in the pyarrow engine. (:issue: `30270`) - DataFrame constructor preserve `ExtensionArray` dtype with `ExtensionArray` (:issue:`11363`) +- :meth:`DataFrame.to_markdown` added (:issue:`11052`) Build Changes diff --git a/environment.yml b/environment.yml index 2b171d097a6937..674efe9d5f6dc7 100644 --- a/environment.yml +++ b/environment.yml @@ -72,6 +72,7 @@ dependencies: - matplotlib>=2.2.2 # pandas.plotting, Series.plot, DataFrame.plot - numexpr>=2.6.8 - scipy>=1.1 + - tabulate # optional for io - beautifulsoup4>=4.6.0 # pandas.read_html diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 6f760e7ee4ca07..b1311a1b901af5 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -36,6 +36,7 @@ from pandas._config import get_option from pandas._libs import algos as libalgos, lib +from pandas.compat._optional import import_optional_dependency from pandas.compat.numpy import function as nv from pandas.util._decorators import ( Appender, @@ -1964,6 +1965,29 @@ def to_feather(self, path): to_feather(self, path) + def to_markdown(self): + """ + Print a DataFrame in markdown-friendly format. + + .. versionadded:: 1.0 + + Returns + ------- + str + DataFrame in markdown-friendly format. + + Examples + -------- + >>> df = pd.DataFrame(data={'col1': [1, 2], 'col2': [3, 4]}) + >>> print(df.to_markdown()) + | | col1 | col2 | + |---:|-------:|-------:| + | 0 | 1 | 3 | + | 1 | 2 | 4 | + """ + tabulate = import_optional_dependency("tabulate") + return self.pipe(tabulate.tabulate, headers="keys", tablefmt="pipe") + @deprecate_kwarg(old_arg_name="fname", new_arg_name="path") def to_parquet( self, diff --git a/pandas/tests/io/formats/test_to_markdown.py b/pandas/tests/io/formats/test_to_markdown.py new file mode 100644 index 00000000000000..bbbc5955956c75 --- /dev/null +++ b/pandas/tests/io/formats/test_to_markdown.py @@ -0,0 +1,12 @@ +import pandas.util._test_decorators as td + +import pandas as pd + + +@td.skip_if_no_tabulate +def test_to_markdown(): + df = pd.DataFrame([1, 2, 3]) + result = df.to_markdown() + assert ( + result == "| | 0 |\n|---:|----:|\n| 0 | 1 |\n| 1 | 2 |\n| 2 | 3 |" + ) diff --git a/pandas/util/_test_decorators.py b/pandas/util/_test_decorators.py index 0e3ea25bf6fdb5..5192142118c17f 100644 --- a/pandas/util/_test_decorators.py +++ b/pandas/util/_test_decorators.py @@ -119,6 +119,10 @@ def _skip_if_no_scipy(): ) +def _skip_if_no_tabulate(): + return not safe_import("tabulate") + + def skip_if_installed(package: str) -> Callable: """ Skip a test if a package is installed. @@ -193,6 +197,9 @@ def skip_if_no(package: str, min_version: Optional[str] = None) -> Callable: not _USE_NUMEXPR, reason=f"numexpr enabled->{_USE_NUMEXPR}, " f"installed->{_NUMEXPR_INSTALLED}", ) +skip_if_no_tabulate = pytest.mark.skipif( + _skip_if_no_tabulate(), reason="Missing tabulate requirement" +) def skip_if_np_lt(ver_str, reason=None, *args, **kwds): diff --git a/requirements-dev.txt b/requirements-dev.txt index 5f67726a3e476d..6d75a975fd5b5a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -55,6 +55,7 @@ openpyxl<=3.0.1 pyarrow>=0.13.1 pyqt5>=5.9.2 tables>=3.4.2 +tabulate python-snappy s3fs sqlalchemy