BUG: read_excel index-name detection for non-contiguous MultiIndex header - #66802
BUG: read_excel index-name detection for non-contiguous MultiIndex header#66802aleks-drozy wants to merge 1 commit into
Conversation
|
Thanks for the pull request, @aleks-drozy! It's linked to #66373, but that issue isn't assigned to you yet. To make sure two people don't unknowingly work on the same thing, we ask contributors to claim an issue first. Just comment |
…header)+1 for non-contiguous header lists For a non-contiguous header list like header=[0, 2], BaseExcelReader._parse_sheet looked at data[len(header)] (row 2, the header row itself) to decide whether the row right after the header holds MultiIndex index names, instead of data[max(header) + 1] (row 3, the actual next row). This misclassified has_index_names, which shifted the forward-fill offset for a MultiIndex index_col by one row and could leak the index-name text into the actual index values. The fix mirrors the offset = 1 + max(header) computation a few lines below in the same function, which already handles non-contiguous headers correctly for the forward-fill offset itself. Related to pandas-dev#66373: the issue's reported symptom (wrong columns from a header=[0, 2] read) does not reproduce against a well-formed xlsx -- the attached test.xlsx is internally corrupted (its sharedStrings.xml is missing the "h2" string, so B1 duplicates A1's shared-string reference). This fixes an adjacent, real bug in the same code path found while investigating that report.
2565a2c to
3271e12
Compare
|
@aleks-drozy , thanks very much about investigation!
Thanks. |
|
Thanks for the review, @kuraga.
Both engines produce clean index names/values with no leakage or shift. That's because |
Investigation
While looking into a reported
read_excelissue withheader=[0, 2], I found the attached.xlsxreproduction file was internally corrupted: its ownsharedStrings.xmlis missing the string"h2", so cellB1ends up pointing at the same shared-string index asA1-- the file simply does not contain the data its screenshot implies. Re-creating a clean.xlsxwith the same claimed values (header=[0, 2], non-contiguous, matching column layout) and reading it withread_excelproduces output that matchesread_csvon equivalent data exactly. So the originally reported symptom looks like a corrupted-attachment artifact, not a live bug.The real, adjacent bug this PR fixes
While investigating, I found a genuine bug in the same code path:
BaseExcelReader._parse_sheetinpandas/io/excel/_base.py.For a non-contiguous
headerlist (e.g.header=[0, 2]), the code that decides whether the row right after the header holds MultiIndex index names did:len(header)(2) is not the row index immediately following the last header row for a non-contiguous header --max(header) + 1(3) is. So this looked atdata[2], which is the second header row itself, instead ofdata[3], the actual row after the header. That corruptshas_index_namesdetection, which in turn shifts the forward-fill offset used for aMultiIndexindex_colby one row, and can leak the index-name text into the actual index values.The fix mirrors the
offset = 1 + max(header)computation a few lines below in the very same function, which already handles non-contiguous headers correctly for the forward-fill offset itself -- this brings the index-name-row lookup a few lines above it in line with that existing, correct logic.Testing
Added
test_read_excel_noncontiguous_header_index_namesinpandas/tests/io/excel/test_readers.py, using a minimal.xlsxbuilt withopenpyxlin the test itself (not a corrupted attachment). It usesheader=[0, 2]with aMultiIndexindex_col, an intervening non-header row, and a genuine index-name row, and asserts the resulting index values/names are correct.Confirmed the test fails before this fix (the corrupted
has_index_namesdetection leaks the index-name text"ilvl1"into an actual index value that should stay blank/NaN) and passes after it. Ran the fullpandas/tests/io/excel/slice locally; no regressions (the only failures/errors present are pre-existing onmain, unrelated to this change -- anopenpyxlversion mismatch in an unrelated writer test and a network-dependenttest_read_from_http_urltest).Added a whatsnew entry under
doc/source/whatsnew/v3.1.0.rst(I/O section).