Repository navigation
XLSX/XLS: keep repeated header names instead of Total.1, Item.1 - #2709
Cindy (cindykrafft) wants to merge 3 commits into
Conversation
pd.read_excel parsed cells stored as text into numbers, so 02134 became 2134, +15551234 became 15551234 and 007 became 7.0. Read with dtype=object and infer_objects() so text stays text and numeric columns render as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVn4SeAXPsUajXT2rtHQfo
pandas renames duplicate column labels. When a label looks renamed, re-read the sheet's raw first row and use the original labels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVn4SeAXPsUajXT2rtHQfo
Bicheng (Kenneth) (BichengWang)
left a comment
There was a problem hiding this comment.
Reviewed 20b92625a3bcd4252a4b4a4430dc74cd9e5dd1ca (both commits), with one nonblocking coverage gap:
Repeated NA-like text headers still receive suffixes. The raw reread at _xlsx_converter.py:60–63 uses pandas' default NA parsing, so _restore_header sees missing values for literal NA, N/A, and NULL headers and retains their mangled names. For example, this workbook still produces | NA | NA.1 |:
import io
import openpyxl
from markitdown import MarkItDown, StreamInfo
workbook = openpyxl.Workbook()
workbook.active.append(["NA", "NA"])
workbook.active.append([1, 2])
stream = io.BytesIO()
workbook.save(stream)
stream.seek(0)
print(MarkItDown().convert_stream(
stream, stream_info=StreamInfo(extension=".xlsx")
).markdown)The same reproduction yields | N/A | N/A.1 | and | NULL | NULL.1 |. The helper docstring acknowledges this fallback, but the PR's stated behavior has this exception. Consider disabling NA parsing for the header reread while retaining a separate fallback for genuinely blank header cells, with parametrized coverage for these labels. Reading only the first row would also avoid parsing the entire sheet again.
Validation on Python 3.12.15 / pandas 3.0.6: core suite 1053 passed, 57 skipped with GITHUB_ACTIONS=1; OCR suite 108 passed; pre-commit run --all-files passed. These used direct pytest runs in an offline environment. The three additional header assertions above failed as described; header-only, numeric, date, and mixed text/numeric header probes converted successfully. I did not generate new .xls fixtures.
The header re-read used pandas' default NA parsing, so repeated headers such as "NA", "N/A" or "NULL" were read as missing and kept their renamed NA.1 / N/A.1 / NULL.1 names. Re-read only the first row with NA parsing off; blank header cells still keep pandas' "Unnamed: n" names, including when the first row is narrower than the sheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVn4SeAXPsUajXT2rtHQfo
|
Thanks for checking this so thoroughly. You're right; that was a gap. 2cee2ab addresses it:
Validation (Python 3.12, pandas 3.0.6): core suite 1059 passed, 57 skipped with Disclosure: this fix and reply were produced by an AI agent (Claude Code), which ran the reproduction and tests quoted above. |
pandas renames duplicate column labels, and pandas 2 removed the option to turn that off, so a header row
Item | Total | Total | Itemis rendered asItem | Total | Total.1 | Item.1. The Markdown table no longer matches the spreadsheet.When a label looks renamed, this re-reads that sheet's raw first row and uses the original labels. Eleven other sheet shapes compared give identical output.
This builds on the XLSX text-cells PR; the branch contains both commits, so please merge that one first or review just the last commit.
Tests
test_xlsx_repeated_header_names_are_keptfails on main (| Item | Total | Total.2 | Item.1 | Total.1 | NA | Unnamed: 6 |) and passes with the change.packages/markitdown1098 passed, 12 skipped;packages/markitdown-ocr108 passed. black clean.Disclosure: this was found, reproduced and patched by an AI agent (Claude Code), which ran the reproduction and tests quoted above. It has not been separately reviewed by a human.