Skip to content

Commit f7b6d9c

Browse files
committed
Merge branch 'no_dataframe_index' of bitbucket.org:janschulz/python-tabulate into feature/data-frame-index
2 parents b96451a + b8c260b commit f7b6d9c

File tree

2 files changed

+110
-6
lines changed

2 files changed

+110
-6
lines changed

tabulate.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,7 @@ def _align_header(header, alignment, width):
598598
return _padleft(width, header)
599599

600600

601-
def _normalize_tabular_data(tabular_data, headers):
601+
def _normalize_tabular_data(tabular_data, headers, index=None):
602602
"""Transform a supported data type to a list of lists, and a list of headers.
603603
604604
Supported tabular data types:
@@ -634,8 +634,10 @@ def _normalize_tabular_data(tabular_data, headers):
634634
# values is a property, has .index => it's likely a pandas.DataFrame (pandas 0.11.0)
635635
keys = tabular_data.keys()
636636
vals = tabular_data.values # values matrix doesn't need to be transposed
637-
names = tabular_data.index
638-
rows = [[v]+list(row) for v,row in zip(names, vals)]
637+
# For DataFrames add an index per default
638+
if index in [None, True]:
639+
index = tabular_data.index
640+
rows = [list(row) for row in vals]
639641
else:
640642
raise ValueError("tabular data doesn't appear to be a dict or a DataFrame")
641643

@@ -687,6 +689,7 @@ def _normalize_tabular_data(tabular_data, headers):
687689
elif headers:
688690
raise ValueError('headers for a list of dicts is not a dict or a keyword')
689691
rows = [[row.get(k) for k in keys] for row in rows]
692+
690693
elif headers == "keys" and len(rows) > 0:
691694
# keys are column indices
692695
headers = list(map(_text_type, range(len(rows[0]))))
@@ -696,6 +699,18 @@ def _normalize_tabular_data(tabular_data, headers):
696699
headers = list(map(_text_type, rows[0])) # headers should be strings
697700
rows = rows[1:]
698701

702+
# Add an index column, either from a supplied list of index values or from the dataframe index
703+
# or simple a running count if index==True
704+
if index:
705+
if index is True:
706+
index = range(len(rows))
707+
elif index:
708+
index = list(index)
709+
if len(index) != len(rows):
710+
raise ValueError('index must be as long as the rows (excluding a header row if '
711+
'"headers=firstrow"')
712+
rows = [[v]+list(row) for v,row in zip(index, rows)]
713+
699714
headers = list(map(_text_type,headers))
700715
rows = list(map(list,rows))
701716

@@ -711,7 +726,7 @@ def _normalize_tabular_data(tabular_data, headers):
711726

712727
def tabulate(tabular_data, headers=(), tablefmt="simple",
713728
floatfmt="g", numalign="decimal", stralign="left",
714-
missingval=""):
729+
missingval="", index=None):
715730
"""Format a fixed width table for pretty printing.
716731
717732
>>> print(tabulate([[1, 2.34], [-56, "8.999"], ["2", "10001"]]))
@@ -743,6 +758,11 @@ def tabulate(tabular_data, headers=(), tablefmt="simple",
743758
are supposed to be names of the last columns. This is consistent
744759
with the plain-text format of R and Pandas' dataframes.
745760
761+
If `index=True` or if `tabular_data` is a pandas.DataFrame, a column with
762+
a row count or the index of the dataframe is shown. `index=False` does not
763+
show an index. If `index` is an iterable, this value is used as the index
764+
row (must be as long as the number of rows).
765+
746766
>>> print(tabulate([["sex","age"],["Alice","F",24],["Bob","M",19]],
747767
... headers="firstrow"))
748768
sex age
@@ -946,7 +966,8 @@ def tabulate(tabular_data, headers=(), tablefmt="simple",
946966
"""
947967
if tabular_data is None:
948968
tabular_data = []
949-
list_of_lists, headers = _normalize_tabular_data(tabular_data, headers)
969+
list_of_lists, headers = _normalize_tabular_data(tabular_data, headers,
970+
index=index)
950971

951972
# optimization: look for ANSI control codes once,
952973
# enable smart width functions only if a control code is found

test/test_output.py

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from __future__ import print_function
66
from __future__ import unicode_literals
77
from tabulate import tabulate, simple_separated_format
8-
from common import assert_equal
8+
from common import assert_equal, assert_raises
99

1010

1111
# _test_table shows
@@ -415,3 +415,86 @@ def test_unaligned_separated():
415415
["name", "score"],
416416
tablefmt=fmt, stralign=None, numalign=None)
417417
assert_equal(expected, result)
418+
419+
420+
def test_pandas_without_index():
421+
"Output: a pandas Dataframe without an index"
422+
try:
423+
import pandas
424+
df = pandas.DataFrame([["one",1],["two",None]],
425+
columns=["string","number"],
426+
index=["a","b"])
427+
expected = "\n".join(
428+
['string number',
429+
'-------- --------',
430+
'one 1',
431+
'two nan'])
432+
result = tabulate(df, headers="keys", index=False)
433+
assert_equal(expected, result)
434+
except ImportError:
435+
print("test_pandas_keys is skipped")
436+
raise SkipTest() # this test is optional
437+
438+
439+
def test_dict_like_with_index():
440+
"Output: a table with a running index"
441+
dd = {"a": range(3), "b": range(101,104)}
442+
# keys' order (hence columns' order) is not deterministic in Python 3
443+
# => we have to consider both possible results as valid
444+
expected = "\n".join([
445+
' a b',
446+
'-- --- ---',
447+
' 0 0 101',
448+
' 1 1 102',
449+
' 2 2 103'])
450+
result = tabulate(dd, "keys", index=True)
451+
assert_equal(result, expected)
452+
453+
454+
def test_list_of_lists_with_index():
455+
"Output: a table with a running index"
456+
dd = zip(*[range(3), range(101,104)])
457+
# keys' order (hence columns' order) is not deterministic in Python 3
458+
# => we have to consider both possible results as valid
459+
expected = "\n".join([
460+
' a b',
461+
'-- --- ---',
462+
' 0 0 101',
463+
' 1 1 102',
464+
' 2 2 103'])
465+
result = tabulate(dd, headers=["a","b"], index=True)
466+
assert_equal(result, expected)
467+
468+
def test_list_of_lists_with_supplied_index():
469+
"Output: a table with a supplied index"
470+
dd = zip(*[range(3), range(101,104)])
471+
# keys' order (hence columns' order) is not deterministic in Python 3
472+
# => we have to consider both possible results as valid
473+
expected = "\n".join([
474+
' a b',
475+
'-- --- ---',
476+
' 1 0 101',
477+
' 2 1 102',
478+
' 3 2 103'])
479+
result = tabulate(dd, headers=["a","b"], index=[1,2,3])
480+
assert_equal(result, expected)
481+
# the index must be as long as the number of rows
482+
assert_raises(ValueError, lambda: tabulate(dd, headers=["a","b"], index=[1,2]))
483+
484+
485+
486+
def test_list_of_lists_with_index_firstrow():
487+
"Output: a table with a running index which takes into account header='firstrow'"
488+
dd = zip(*[["a"]+range(3), ["b"]+range(101,104)])
489+
# keys' order (hence columns' order) is not deterministic in Python 3
490+
# => we have to consider both possible results as valid
491+
expected = "\n".join([
492+
' a b',
493+
'-- --- ---',
494+
' 0 0 101',
495+
' 1 1 102',
496+
' 2 2 103'])
497+
result = tabulate(dd, headers="firstrow", index=True)
498+
assert_equal(result, expected)
499+
# the index must be as long as the number of rows
500+
assert_raises(ValueError, lambda: tabulate(dd, headers="firstrow", index=[1,2]))

0 commit comments

Comments
 (0)