Description
Pandas version checks
-
I have checked that this issue has not already been reported.
-
I have confirmed this bug exists on the latest version of pandas.
-
I have confirmed this bug exists on the main branch of pandas.
Reproducible Example
import pandas as pd
import numpy as np
results = pd.DataFrame({"col1": [np.array(["hello","world"], dtype=object), "world"]})
results.to_markdown()
Issue Description
When a dataframe contains a cell with type numpy.array, the to_markdown function will fail. The exact issue is due to differing behavior between any other value and numpy arrays, but the reason I think this is a pandas issue is because an assumption is made about the value:
def _is_separating_line(row):
row_type = type(row)
is_sl = (row_type == list or row_type == str) and (
(len(row) >= 1 and row[0] == SEPARATING_LINE) # <- compares row[0] to a string
or (len(row) >= 2 and row[1] == SEPARATING_LINE)
)
return is_sl
Anything that isn't a string will normally result in this comparison resolving to False
, but this should be explicit to avoid strange datatypes causing undefined behavior.
Stack trace:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[69], line 3
1 import numpy as np
2 results = pd.DataFrame({"col1": [np.array(["hello","world"], dtype=object), "world"]})
----> 3 results.to_markdown()
File [...]\.venv\Lib\site-packages\pandas\util\_decorators.py:333, in deprecate_nonkeyword_arguments.<locals>.decorate.<locals>.wrapper(*args, **kwargs)
327 if len(args) > num_allow_args:
328 warnings.warn(
329 msg.format(arguments=_format_argument_list(allow_args)),
330 FutureWarning,
331 stacklevel=find_stack_level(),
332 )
--> 333 return func(*args, **kwargs)
File [...]\.venv\Lib\site-packages\pandas\core\frame.py:2984, in DataFrame.to_markdown(self, buf, mode, index, storage_options, **kwargs)
2982 kwargs.setdefault("showindex", index)
2983 tabulate = import_optional_dependency("tabulate")
-> 2984 result = tabulate.tabulate(self, **kwargs)
2985 if buf is None:
2986 return result
File [...]\.venv\Lib\site-packages\tabulate\__init__.py:2048, in tabulate(tabular_data, headers, tablefmt, floatfmt, intfmt, numalign, stralign, missingval, showindex, disable_numparse, colalign, maxcolwidths, rowalign, maxheadercolwidths)
2045 if tabular_data is None:
2046 tabular_data = []
-> 2048 list_of_lists, headers = _normalize_tabular_data(
2049 tabular_data, headers, showindex=showindex
2050 )
2051 list_of_lists, separating_lines = _remove_separating_lines(list_of_lists)
2053 if maxcolwidths is not None:
File [...]\.venv\Lib\site-packages\tabulate\__init__.py:1471, in _normalize_tabular_data(tabular_data, headers, showindex)
1469 headers = list(map(str, headers))
1470 # rows = list(map(list, rows))
-> 1471 rows = list(map(lambda r: r if _is_separating_line(r) else list(r), rows))
1473 # add or remove an index column
1474 showindex_is_a_str = type(showindex) in [str, bytes]
File [...]\.venv\Lib\site-packages\tabulate\__init__.py:1471, in _normalize_tabular_data.<locals>.<lambda>(r)
1469 headers = list(map(str, headers))
1470 # rows = list(map(list, rows))
-> 1471 rows = list(map(lambda r: r if _is_separating_line(r) else list(r), rows))
1473 # add or remove an index column
1474 showindex_is_a_str = type(showindex) in [str, bytes]
File [...]\.venv\Lib\site-packages\tabulate\__init__.py:107, in _is_separating_line(row)
104 def _is_separating_line(row):
105 row_type = type(row)
106 is_sl = (row_type == list or row_type == str) and (
--> 107 (len(row) >= 1 and row[0] == SEPARATING_LINE)
108 or (len(row) >= 2 and row[1] == SEPARATING_LINE)
109 )
110 return is_sl
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Expected Behavior
to_markdown converts this numpy array to string and outputting a markdown normally. Instead there's an exception
Installed Versions
INSTALLED VERSIONS
commit : 0691c5c
python : 3.13.1
python-bits : 64
OS : Windows
OS-release : 11
Version : 10.0.26100
machine : AMD64
processor : Intel64 Family 6 Model 140 Stepping 1, GenuineIntel
byteorder : little
LC_ALL : None
LANG : None
LOCALE : English_United States.1252
pandas : 2.2.3
numpy : 2.2.4
pytz : 2025.2
dateutil : 2.9.0.post0
pip : 25.0.1
Cython : None
sphinx : None
IPython : 9.0.2
adbc-driver-postgresql: None
adbc-driver-sqlite : None
bs4 : 4.13.3
blosc : None
bottleneck : None
dataframe-api-compat : None
fastparquet : None
fsspec : None
html5lib : None
hypothesis : None
gcsfs : None
jinja2 : 3.1.6
lxml.etree : None
matplotlib : 3.10.1
numba : None
numexpr : None
odfpy : None
openpyxl : None
pandas_gbq : None
psycopg2 : None
pymysql : None
pyarrow : 19.0.1
pyreadstat : None
pytest : None
python-calamine : None
pyxlsb : None
s3fs : None
scipy : None
sqlalchemy : None
tables : None
tabulate : 0.9.0
xarray : None
xlrd : None
xlsxwriter : None
zstandard : None
tzdata : 2025.2
qtpy : None
pyqt5 : None