Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ENH] Add to_markdown method #30350

Merged
merged 24 commits into from
Dec 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
16a3328
:sparkles: Add to_markdown method
Dec 19, 2019
8eb96ec
:pushpin: put tabulate in #optional for io, pin dependency
Dec 20, 2019
00fd8a4
:recycle: remove call to DataFrame.pipe in DataFrame.to_markdown
Dec 20, 2019
65e9f1b
:pushpin: add tabulate to travis-38.yaml
Dec 20, 2019
a273560
:pencil: add DataFrame.to_markdown to API reference file
Dec 20, 2019
14e36e8
:sparkles: add **kwargs to DataFrame.to_markdown
Dec 20, 2019
ee07c68
:white_check_mark: update tests so they work with **kwargs, set skip_…
Dec 20, 2019
d99a54f
:sparkles: add to_markdown to Series
Dec 20, 2019
57dfb7b
:pencil: document to_markdown in Series API reference
Dec 20, 2019
ccb132b
:white_check_mark: update tests so they test Series.to_markdown as well
Dec 20, 2019
bac632e
:arrow_down: Set tabulate dependency at 0.8, before which tests fail
Dec 20, 2019
557e6dd
:pencil: update failing docstring
Dec 20, 2019
01260f2
:pushpin: set tabulate dependency at 0.8.0, not 0.8
Dec 20, 2019
b32d54d
:sparkles: add buf and mode arguments to to_markdown
Dec 20, 2019
68e84d6
:white_check_mark: update tests so they use buf
Dec 20, 2019
882768b
:fire: remove skip_if_no_tabulate, due to module-level fixture
Dec 20, 2019
f46edb1
:pencil: add tabulate to install.rst and _optional, capitalise Markdown
Dec 20, 2019
32d8762
:push_pin: add tabulate unpinned to travis-37.yaml
Dec 20, 2019
c5c3768
:bug: dont all get_filepath_or_buffer with buf equal to sys.stdout
MarcoGorelli Dec 21, 2019
df7880c
:ok_hand: return string if buf is set to None, use shared doc, change…
MarcoGorelli Dec 21, 2019
039e6a9
:pushpin: pin tabulate at 0.8.3
MarcoGorelli Dec 21, 2019
093d63a
:pencil: add kwargs to parameters
Dec 23, 2019
ec77816
:pencil: link to tabulate docs in install.rst
MarcoGorelli Dec 27, 2019
32650f7
:pencil: fix merge conflict in whatsnew
Dec 27, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/deps/travis-37.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ dependencies:
- pyarrow
- pytz
- s3fs
- tabulate
- pyreadstat
- pip
- pip:
Expand Down
1 change: 1 addition & 0 deletions ci/deps/travis-38.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ dependencies:
- nomkl
- pytz
- pip
- tabulate==0.8.3
5 changes: 4 additions & 1 deletion doc/source/getting_started/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,8 @@ Optional dependencies
~~~~~~~~~~~~~~~~~~~~~

Pandas has many optional dependencies that are only used for specific methods.
For example, :func:`pandas.read_hdf` requires the ``pytables`` package. If the
For example, :func:`pandas.read_hdf` requires the ``pytables`` package, while
:meth:`DataFrame.to_markdown` requires the ``tabulate`` package. If the
optional dependency is not installed, pandas will raise an ``ImportError`` when
the method requiring that dependency is called.

Expand Down Expand Up @@ -264,6 +265,7 @@ pyreadstat SPSS files (.sav) reading
pytables 3.4.2 HDF5 reading / writing
qtpy Clipboard I/O
s3fs 0.3.0 Amazon S3 access
tabulate 0.8.3 Printing in Markdown-friendly format (see `tabulate`_)
xarray 0.8.2 pandas-like API for N-dimensional data
xclip Clipboard I/O on linux
xlrd 1.1.0 Excel reading
Expand Down Expand Up @@ -301,3 +303,4 @@ top-level :func:`~pandas.read_html` function:
.. _html5lib: https://github.com/html5lib/html5lib-python
.. _BeautifulSoup4: http://www.crummy.com/software/BeautifulSoup
.. _lxml: http://lxml.de
.. _tabulate: https://github.com/astanin/python-tabulate
1 change: 1 addition & 0 deletions doc/source/reference/frame.rst
Original file line number Diff line number Diff line change
Expand Up @@ -361,4 +361,5 @@ Serialization / IO / conversion
DataFrame.to_records
DataFrame.to_string
DataFrame.to_clipboard
DataFrame.to_markdown
DataFrame.style
1 change: 1 addition & 0 deletions doc/source/reference/series.rst
Original file line number Diff line number Diff line change
Expand Up @@ -578,3 +578,4 @@ Serialization / IO / conversion
Series.to_string
Series.to_clipboard
Series.to_latex
Series.to_markdown
1 change: 1 addition & 0 deletions doc/source/whatsnew/v1.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ Other enhancements
- :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.sort_values` and :meth:`Series.sort_values` have gained ``ignore_index`` keyword to be able to reset index after sorting (:issue:`30114`)
- :meth:`DataFrame.to_markdown` and :meth:`Series.to_markdown` added (:issue:`11052`)


Build Changes
Expand Down
1 change: 1 addition & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,6 @@ dependencies:
- sqlalchemy # pandas.read_sql, DataFrame.to_sql
- xarray # DataFrame.to_xarray
- pyreadstat # pandas.read_spss
- tabulate>=0.8.3 # DataFrame.to_markdown
- pip:
- git+https://github.com/pandas-dev/pandas-sphinx-theme.git@master
1 change: 1 addition & 0 deletions pandas/compat/_optional.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"scipy": "0.19.0",
"sqlalchemy": "1.1.4",
"tables": "3.4.2",
"tabulate": "0.8.3",
"xarray": "0.8.2",
"xlrd": "1.1.0",
"xlwt": "1.2.0",
Expand Down
33 changes: 33 additions & 0 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import sys
from textwrap import dedent
from typing import (
IO,
Any,
FrozenSet,
Hashable,
Expand All @@ -37,6 +38,7 @@

from pandas._libs import algos as libalgos, lib
from pandas._typing import Axes, Dtype, FilePathOrBuffer
from pandas.compat._optional import import_optional_dependency
from pandas.compat.numpy import function as nv
from pandas.util._decorators import (
Appender,
Expand Down Expand Up @@ -118,6 +120,7 @@
from pandas.core.ops.missing import dispatch_fill_zeros
from pandas.core.series import Series

from pandas.io.common import get_filepath_or_buffer
from pandas.io.formats import console, format as fmt
from pandas.io.formats.printing import pprint_thing
import pandas.plotting
Expand Down Expand Up @@ -1964,6 +1967,36 @@ def to_feather(self, path):

to_feather(self, path)

@Appender(
"""
Examples
--------
>>> df = pd.DataFrame(
... data={"animal_1": ["elk", "pig"], "animal_2": ["dog", "quetzal"]}
... )
>>> print(df.to_markdown())
| | animal_1 | animal_2 |
|---:|:-----------|:-----------|
| 0 | elk | dog |
| 1 | pig | quetzal |
"""
)
@Substitution(klass="DataFrame")
@Appender(_shared_docs["to_markdown"])
def to_markdown(
self, buf: Optional[IO[str]] = None, mode: Optional[str] = None, **kwargs,
) -> Optional[str]:
kwargs.setdefault("headers", "keys")
kwargs.setdefault("tablefmt", "pipe")
tabulate = import_optional_dependency("tabulate")
result = tabulate.tabulate(self, **kwargs)
if buf is None:
return result
buf, _, _, _ = get_filepath_or_buffer(buf, mode=mode)
assert buf is not None # Help mypy.
buf.writelines(result)
return None

@deprecate_kwarg(old_arg_name="fname", new_arg_name="path")
def to_parquet(
self,
Expand Down
24 changes: 24 additions & 0 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -1970,6 +1970,30 @@ def _repr_data_resource_(self):
# ----------------------------------------------------------------------
# I/O Methods

_shared_docs[
"to_markdown"
] = """
Print %(klass)s in Markdown-friendly format.

.. versionadded:: 1.0.0

Parameters
----------
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.
mode : str, optional
Mode in which file is opened.
**kwargs
These parameters will be passed to `tabulate`.

Returns
-------
str
%(klass)s in Markdown-friendly format.
"""

_shared_docs[
"to_excel"
] = """
Expand Down
24 changes: 23 additions & 1 deletion pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from io import StringIO
from shutil import get_terminal_size
from textwrap import dedent
from typing import Any, Callable, Hashable, List, Optional
from typing import IO, Any, Callable, Hashable, List, Optional
import warnings

import numpy as np
Expand Down Expand Up @@ -59,6 +59,7 @@
is_empty_data,
sanitize_array,
)
from pandas.core.generic import _shared_docs
from pandas.core.indexers import maybe_convert_indices
from pandas.core.indexes.accessors import CombinedDatetimelikeProperties
from pandas.core.indexes.api import (
Expand Down Expand Up @@ -1439,6 +1440,27 @@ def to_string(
with open(buf, "w") as f:
f.write(result)

@Appender(
"""
MarcoGorelli marked this conversation as resolved.
Show resolved Hide resolved
Examples
--------
>>> s = pd.Series(["elk", "pig", "dog", "quetzal"], name="animal")
>>> print(s.to_markdown())
| | animal |
|---:|:---------|
| 0 | elk |
| 1 | pig |
| 2 | dog |
| 3 | quetzal |
"""
)
@Substitution(klass="Series")
@Appender(_shared_docs["to_markdown"])
def to_markdown(
self, buf: Optional[IO[str]] = None, mode: Optional[str] = None, **kwargs,
) -> Optional[str]:
return self.to_frame().to_markdown(buf, mode, **kwargs)

# ----------------------------------------------------------------------

def items(self):
Expand Down
55 changes: 55 additions & 0 deletions pandas/tests/io/formats/test_to_markdown.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from io import StringIO

import pytest

import pandas as pd

pytest.importorskip("tabulate")


def test_simple():
buf = StringIO()
df = pd.DataFrame([1, 2, 3])
df.to_markdown(buf=buf)
result = buf.getvalue()
assert (
result == "| | 0 |\n|---:|----:|\n| 0 | 1 |\n| 1 | 2 |\n| 2 | 3 |"
)


def test_other_tablefmt():
buf = StringIO()
df = pd.DataFrame([1, 2, 3])
df.to_markdown(buf=buf, tablefmt="jira")
result = buf.getvalue()
assert result == "|| || 0 ||\n| 0 | 1 |\n| 1 | 2 |\n| 2 | 3 |"


def test_other_headers():
buf = StringIO()
df = pd.DataFrame([1, 2, 3])
df.to_markdown(buf=buf, headers=["foo", "bar"])
result = buf.getvalue()
assert result == (
"| foo | bar |\n|------:|------:|\n| 0 "
"| 1 |\n| 1 | 2 |\n| 2 | 3 |"
)


def test_series():
buf = StringIO()
s = pd.Series([1, 2, 3], name="foo")
s.to_markdown(buf=buf)
result = buf.getvalue()
assert result == (
"| | foo |\n|---:|------:|\n| 0 | 1 "
"|\n| 1 | 2 |\n| 2 | 3 |"
)


def test_no_buf(capsys):
df = pd.DataFrame([1, 2, 3])
result = df.to_markdown()
assert (
result == "| | 0 |\n|---:|----:|\n| 0 | 1 |\n| 1 | 2 |\n| 2 | 3 |"
)
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,5 @@ s3fs
sqlalchemy
xarray
pyreadstat
tabulate>=0.8.3
git+https://github.com/pandas-dev/pandas-sphinx-theme.git@master