1

I'm using python-tabulate to print data having cells containing lists. Can I customize how python-tabulate formats cell contents, so lists are formatted in a different way?

I would like to avoid having to manually pre-process (convert the lists to string) the data passed to tabulate.

If tabulate won't allow this, are there alternative libraries where I can define more fine-grained formatting options?

Sample code:

from tabulate import tabulate

# Minimal sample. My output comes from an API and contains much more data
table = [['Sun', [1, 2, 3]], ['Moon', [4, 5, 6]]]
print(tabulate(table, tablefmt='plain', headers=['Planet', 'Value']))

Output:

Planet    Value
Sun       [1, 2, 3]
Moon      [4, 5, 6]

How I would like the output formatted:

Planet    Value
Sun       1,2,3
Moon      4,5,6
Wolkenarchitekt
  • 20,170
  • 29
  • 111
  • 174

2 Answers2

2

I would suggest you to try different library. I checked the DOC and didnt find anything related.

Or have function for it (you didnt ask for it though)

from tabulate import tabulate

def delist(lst):
    return ",".join([str(item) for item in lst]) 

table = [['Sun', delist([1, 2, 3])], ['Moon', delist([4, 5, 6])]]
print(tabulate(table, tablefmt='plain', headers=['Planet', 'Value']))
>>>
Planet    Value
Sun       1,2,3
Moon      4,5,6

Last option, maybe little bit hopeless one... is to take tabulate class and integrate in it this function. But its a bit overkill

Martin
  • 3,333
  • 2
  • 18
  • 39
1

Since I could not find a way how to configure tabulate accordingly, one solution would be to monkey patch tabulates internal _format method.

Original method (tabulate 0.8.2):

def _format(val, valtype, floatfmt, missingval="", has_invisible=True):
    """Format a value accoding to its type.

    Unicode is supported:

    >>> hrow = ['\u0431\u0443\u043a\u0432\u0430', '\u0446\u0438\u0444\u0440\u0430'] ; \
        tbl = [['\u0430\u0437', 2], ['\u0431\u0443\u043a\u0438', 4]] ; \
        good_result = '\\u0431\\u0443\\u043a\\u0432\\u0430      \\u0446\\u0438\\u0444\\u0440\\u0430\\n-------  -------\\n\\u0430\\u0437             2\\n\\u0431\\u0443\\u043a\\u0438           4' ; \
        tabulate(tbl, headers=hrow) == good_result
    True

    """
    if val is None:
        return missingval

    if valtype in [int, _text_type]:
        return "{0}".format(val)
    elif valtype is _binary_type:
        try:
            return _text_type(val, "ascii")
        except TypeError:
            return _text_type(val)
    elif valtype is float:
        is_a_colored_number = has_invisible and isinstance(val, (_text_type, _binary_type))
        if is_a_colored_number:
            raw_val = _strip_invisible(val)
            formatted_val = format(float(raw_val), floatfmt)
            return val.replace(raw_val, formatted_val)
        else:
            return format(float(val), floatfmt)
    else:
        return "{0}".format(val)

My modified version:

# tabulate_extensions.py
from tabulate import _text_type, _binary_type, _strip_invisible


def _format_extended(val, valtype, floatfmt, missingval="", has_invisible=True):
    """Format a value accoding to its type.

    Unicode is supported:

    >>> hrow = ['\u0431\u0443\u043a\u0432\u0430', '\u0446\u0438\u0444\u0440\u0430'] ; \
        tbl = [['\u0430\u0437', 2], ['\u0431\u0443\u043a\u0438', 4]] ; \
        good_result = '\\u0431\\u0443\\u043a\\u0432\\u0430      \\u0446\\u0438\\u0444\\u0440\\u0430\\n-------  -------\\n\\u0430\\u0437             2\\n\\u0431\\u0443\\u043a\\u0438           4' ; \
        tabulate(tbl, headers=hrow) == good_result
    True

    """
    if val is None:
        return missingval

    if valtype in [int, _text_type]:
        # Change list formatting [1,2,3] -> 1,2,3
        if type(val) == list:
            val = ','.join([str(x) for x in val])
        return "{0}".format(val)
    elif valtype is _binary_type:
        try:
            return _text_type(val, "ascii")
        except TypeError:
            return _text_type(val)
    elif valtype is float:
        is_a_colored_number = has_invisible and isinstance(val, (_text_type, _binary_type))
        if is_a_colored_number:
            raw_val = _strip_invisible(val)
            formatted_val = format(float(raw_val), floatfmt)
            return val.replace(raw_val, formatted_val)
        else:
            return format(float(val), floatfmt)
    else:
        return "{0}".format(val)

In my code, I replace tabulates internal method with mine like this:

from mypkg.tabulate_extensions import _format_extended

tabulate._format = _format_extended

Output is now as desired. Good thing is that I can now extend formatting of other cell types (like dictionaries) in any way I want.

Wolkenarchitekt
  • 20,170
  • 29
  • 111
  • 174