[{"data":1,"prerenderedAt":2284},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-records-that-span-multiple-rows-in-excel":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-records-that-span-multiple-rows-in-excel":2251,"ancestors:\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-records-that-span-multiple-rows-in-excel":2260,"children:\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-records-that-span-multiple-rows-in-excel":2265,"related:\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-records-that-span-multiple-rows-in-excel":2266},{"id":4,"title":5,"body":6,"dateModified":2217,"datePublished":2217,"description":2218,"extension":2219,"faq":2220,"keyword":2229,"kind":2230,"meta":2231,"navigation":415,"path":2239,"seo":2240,"slug":2243,"stem":2244,"topics":2245,"type":2249,"__hash__":2250},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-records-that-span-multiple-rows-in-excel\u002Findex.md","Parse Records That Span Multiple Rows in Excel with pandas",{"type":7,"value":8,"toc":2204},"minimark",[9,24,352,357,384,387,703,707,710,750,753,835,838,1052,1056,1059,1181,1200,1204,1207,1368,1374,1378,1381,1625,1632,1636,1643,1802,1813,1977,1981,1984,2088,2095,2099,2132,2136,2139,2143,2149,2155,2161,2167,2171,2200],[10,11,12,13,17,18,23],"p",{},"Some exports put one record on several rows. A bank statement wraps a long description onto the next two lines, leaving the date and amount blank there. A contact list from an old system writes one field per row — Name, Email, Phone — with a blank line between people. Read either with ",[14,15,16],"code",{},"pd.read_excel"," and every line becomes a row, so descriptions are cut into fragments and contacts are scattered across rows. This guide, part of ",[19,20,22],"a",{"href":21},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002F","Extracting Data from Messy Excel Sheets",", rebuilds one row per record for both layouts and checks that nothing was attached to the wrong record.",[25,26,34,35,34,39,34,43,34,51,34,60,34,64,34,71,34,75,34,79,34,83,34,87,34,91,34,96,34,98,34,102,34,104,34,107,34,109,34,112,34,115,34,119,34,121,34,125,34,127,34,130,34,132,34,135,34,138,34,141,34,143,34,145,34,148,34,150,34,153,34,157,34,159,34,161,34,164,34,166,34,169,34,173,34,175,34,178,34,180,34,183,34,185,34,188,34,191,34,195,34,197,34,200,34,202,34,205,34,207,34,210,34,213,34,217,34,219,34,221,34,224,34,226,34,229,34,233,34,236,34,239,34,242,34,245,34,248,34,250,34,253,34,255,34,257,34,259,34,261,34,263,34,265,34,267,34,269,34,271,34,273,34,275,34,278,34,280,34,282,34,284,34,286,34,288,34,290,34,292,34,294,34,296,34,298,34,300,34,302,34,304,34,306,34,308,34,311,34,313,34,315,34,321,34,325,34,333,34,338,34,346],"svg",{"viewBox":27,"role":28,"ariaLabelledBy":29,"xmlns":32,"style":33},"3 0 753 276","img",[30,31],"mr-overview-t","mr-overview-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:753px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[36,37,38],"title",{"id":30},"Continuation lines folded back into their records",[40,41,42],"desc",{"id":31},"The statement has six lines. The card payment's description continues on two more lines with no date or amount, and the transfer's description continues on one. After grouping, there are three transactions, each with its full description, date and amount.",[44,45],"rect",{"x":46,"y":47,"width":48,"height":49,"fill":50},"3","0","753","276","#ffffff",[44,52],{"x":53,"y":54,"width":55,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"20.0","40.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[44,61],{"x":62,"y":54,"width":63,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"52.0","50.5",[65,66,70],"text",{"x":67,"y":68,"style":69},"77.2","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[44,72],{"x":73,"y":54,"width":74,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"102.5","119.1",[65,76,78],{"x":77,"y":68,"style":69},"162.0","B",[44,80],{"x":81,"y":54,"width":82,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"221.5","61.9",[65,84,86],{"x":85,"y":68,"style":69},"252.5","C",[44,88],{"x":53,"y":89,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},"64.0","28.0",[65,92,95],{"x":93,"y":94,"style":69},"36.0","83.0","1",[44,97],{"x":62,"y":89,"width":63,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,99,101],{"x":67,"y":94,"style":100},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Date",[44,103],{"x":73,"y":89,"width":74,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,105,106],{"x":77,"y":94,"style":100},"Description",[44,108],{"x":81,"y":89,"width":82,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,110,111],{"x":85,"y":94,"style":100},"Amount",[44,113],{"x":53,"y":114,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},"92.0",[65,116,118],{"x":93,"y":117,"style":69},"111.0","2",[44,120],{"x":62,"y":114,"width":63,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,122,124],{"x":67,"y":117,"style":123},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","03-02",[44,126],{"x":73,"y":114,"width":74,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,128,129],{"x":77,"y":117,"style":123},"CARD PAYMENT TO",[44,131],{"x":81,"y":114,"width":82,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,133,134],{"x":85,"y":117,"style":123},"-42.10",[44,136],{"x":53,"y":137,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},"120.0",[65,139,46],{"x":93,"y":140,"style":69},"139.0",[44,142],{"x":62,"y":137,"width":63,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[44,144],{"x":73,"y":137,"width":74,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,146,147],{"x":77,"y":140,"style":123},"GREEN GROCER LTD",[44,149],{"x":81,"y":137,"width":82,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[44,151],{"x":53,"y":152,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},"148.0",[65,154,156],{"x":93,"y":155,"style":69},"167.0","4",[44,158],{"x":62,"y":152,"width":63,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[44,160],{"x":73,"y":152,"width":74,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,162,163],{"x":77,"y":155,"style":123},"REF 88213",[44,165],{"x":81,"y":152,"width":82,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[44,167],{"x":53,"y":168,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},"176.0",[65,170,172],{"x":93,"y":171,"style":69},"195.0","5",[44,174],{"x":62,"y":168,"width":63,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,176,177],{"x":67,"y":171,"style":123},"03-03",[44,179],{"x":73,"y":168,"width":74,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,181,182],{"x":77,"y":171,"style":123},"SALARY MARCH",[44,184],{"x":81,"y":168,"width":82,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,186,187],{"x":85,"y":171,"style":123},"2500.00",[44,189],{"x":53,"y":190,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},"204.0",[65,192,194],{"x":93,"y":193,"style":69},"223.0","6",[44,196],{"x":62,"y":190,"width":63,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,198,199],{"x":67,"y":193,"style":123},"03-04",[44,201],{"x":73,"y":190,"width":74,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,203,204],{"x":77,"y":193,"style":123},"TRANSFER TO",[44,206],{"x":81,"y":190,"width":82,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,208,209],{"x":85,"y":193,"style":123},"-300.00",[44,211],{"x":53,"y":212,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},"232.0",[65,214,216],{"x":93,"y":215,"style":69},"251.0","7",[44,218],{"x":62,"y":212,"width":63,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[44,220],{"x":73,"y":212,"width":74,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,222,223],{"x":77,"y":215,"style":123},"SAVINGS ACCOUNT",[44,225],{"x":81,"y":212,"width":82,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[44,227],{"x":228,"y":54,"width":55,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"484.7",[44,230],{"x":231,"y":54,"width":232,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"516.7","50.4",[65,234,70],{"x":235,"y":68,"style":69},"541.9",[44,237],{"x":238,"y":54,"width":117,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"567.1",[65,240,78],{"x":241,"y":68,"style":69},"622.6",[44,243],{"x":244,"y":54,"width":82,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"678.1",[65,246,86],{"x":247,"y":68,"style":69},"709.1",[44,249],{"x":228,"y":89,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},[65,251,95],{"x":252,"y":94,"style":69},"500.7",[44,254],{"x":231,"y":89,"width":232,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,256,101],{"x":235,"y":94,"style":100},[44,258],{"x":238,"y":89,"width":117,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,260,106],{"x":241,"y":94,"style":100},[44,262],{"x":244,"y":89,"width":82,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,264,111],{"x":247,"y":94,"style":100},[44,266],{"x":228,"y":114,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},[65,268,118],{"x":252,"y":117,"style":69},[44,270],{"x":231,"y":114,"width":232,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,272,124],{"x":235,"y":117,"style":123},[44,274],{"x":238,"y":114,"width":117,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,276,277],{"x":241,"y":117,"style":123},"CARD PAYMENT…",[44,279],{"x":244,"y":114,"width":82,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,281,134],{"x":247,"y":117,"style":123},[44,283],{"x":228,"y":137,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},[65,285,46],{"x":252,"y":140,"style":69},[44,287],{"x":231,"y":137,"width":232,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,289,177],{"x":235,"y":140,"style":123},[44,291],{"x":238,"y":137,"width":117,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,293,182],{"x":241,"y":140,"style":123},[44,295],{"x":244,"y":137,"width":82,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,297,187],{"x":247,"y":140,"style":123},[44,299],{"x":228,"y":152,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},[65,301,156],{"x":252,"y":155,"style":69},[44,303],{"x":231,"y":152,"width":232,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,305,199],{"x":235,"y":155,"style":123},[44,307],{"x":238,"y":152,"width":117,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,309,310],{"x":241,"y":155,"style":123},"TRANSFER…",[44,312],{"x":244,"y":152,"width":82,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,314,209],{"x":247,"y":155,"style":123},[65,316,320],{"x":317,"y":318,"style":319},"151.7","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","before",[65,322,324],{"x":323,"y":318,"style":319},"612.4","after",[326,327],"line",{"x1":328,"y1":329,"x2":330,"y2":329,"stroke":331,"style":332},"293.4","108.0","470.7","var(--brand,#5b5cf0)","stroke-width:2px",[334,335],"polygon",{"points":336,"fill":337},"474.7,108.0 465.7,113.0 465.7,103.0","#5b5cf0",[44,339],{"x":340,"y":341,"width":342,"height":343,"rx":344,"fill":50,"stroke":58,"style":345},"319.6","75.0","129.0","22.0","11","stroke-width:1.2px",[65,347,351],{"x":348,"y":349,"style":350},"384.1","90.0","font-size:11.5px;font-weight:400;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","group by record",[353,354,356],"h2",{"id":355},"prerequisites","Prerequisites",[358,359,364],"pre",{"className":360,"code":361,"language":362,"meta":363,"style":363},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[14,365,366],{"__ignoreMap":363},[367,368,370,374,378,381],"span",{"class":326,"line":369},1,[367,371,373],{"class":372},"sMTad","pip",[367,375,377],{"class":376},"srMev"," install",[367,379,380],{"class":376}," pandas",[367,382,383],{"class":376}," openpyxl\n",[10,385,386],{},"A statement export with wrapped descriptions:",[358,388,392],{"className":389,"code":390,"language":391,"meta":363,"style":363},"language-python shiki shiki-themes github-light github-dark-high-contrast","import datetime as dt\n\nimport pandas as pd\nfrom openpyxl import Workbook\n\nwb = Workbook()\nws = wb.active\nfor row in [[\"Date\", \"Description\", \"Amount\"],\n            [dt.datetime(2026, 3, 2), \"CARD PAYMENT TO\", -42.10],\n            [None, \"GREEN GROCER LTD\", None],\n            [None, \"REF 88213\", None],\n            [dt.datetime(2026, 3, 3), \"SALARY MARCH\", 2500],\n            [dt.datetime(2026, 3, 4), \"TRANSFER TO\", -300],\n            [None, \"SAVINGS ACCOUNT\", None]]:\n    ws.append(row)\nwb.save(\"statement.xlsx\")\nlines = pd.read_excel(\"statement.xlsx\")\nprint(len(lines))            # 6 lines for 3 transactions\n","python",[14,393,394,410,417,430,444,449,461,472,504,538,558,576,603,632,651,657,669,684],{"__ignoreMap":363},[367,395,396,400,404,407],{"class":326,"line":369},[367,397,399],{"class":398},"s-kum","import",[367,401,403],{"class":402},"skGVy"," datetime ",[367,405,406],{"class":398},"as",[367,408,409],{"class":402}," dt\n",[367,411,413],{"class":326,"line":412},2,[367,414,416],{"emptyLinePlaceholder":415},true,"\n",[367,418,420,422,425,427],{"class":326,"line":419},3,[367,421,399],{"class":398},[367,423,424],{"class":402}," pandas ",[367,426,406],{"class":398},[367,428,429],{"class":402}," pd\n",[367,431,433,436,439,441],{"class":326,"line":432},4,[367,434,435],{"class":398},"from",[367,437,438],{"class":402}," openpyxl ",[367,440,399],{"class":398},[367,442,443],{"class":402}," Workbook\n",[367,445,447],{"class":326,"line":446},5,[367,448,416],{"emptyLinePlaceholder":415},[367,450,452,455,458],{"class":326,"line":451},6,[367,453,454],{"class":402},"wb ",[367,456,457],{"class":398},"=",[367,459,460],{"class":402}," Workbook()\n",[367,462,464,467,469],{"class":326,"line":463},7,[367,465,466],{"class":402},"ws ",[367,468,457],{"class":398},[367,470,471],{"class":402}," wb.active\n",[367,473,475,478,481,484,487,490,493,496,498,501],{"class":326,"line":474},8,[367,476,477],{"class":398},"for",[367,479,480],{"class":402}," row ",[367,482,483],{"class":398},"in",[367,485,486],{"class":402}," [[",[367,488,489],{"class":376},"\"Date\"",[367,491,492],{"class":402},", ",[367,494,495],{"class":376},"\"Description\"",[367,497,492],{"class":402},[367,499,500],{"class":376},"\"Amount\"",[367,502,503],{"class":402},"],\n",[367,505,507,510,514,516,518,520,522,525,528,530,533,536],{"class":326,"line":506},9,[367,508,509],{"class":402},"            [dt.datetime(",[367,511,513],{"class":512},"sP0c6","2026",[367,515,492],{"class":402},[367,517,46],{"class":512},[367,519,492],{"class":402},[367,521,118],{"class":512},[367,523,524],{"class":402},"), ",[367,526,527],{"class":376},"\"CARD PAYMENT TO\"",[367,529,492],{"class":402},[367,531,532],{"class":398},"-",[367,534,535],{"class":512},"42.10",[367,537,503],{"class":402},[367,539,541,544,547,549,552,554,556],{"class":326,"line":540},10,[367,542,543],{"class":402},"            [",[367,545,546],{"class":512},"None",[367,548,492],{"class":402},[367,550,551],{"class":376},"\"GREEN GROCER LTD\"",[367,553,492],{"class":402},[367,555,546],{"class":512},[367,557,503],{"class":402},[367,559,561,563,565,567,570,572,574],{"class":326,"line":560},11,[367,562,543],{"class":402},[367,564,546],{"class":512},[367,566,492],{"class":402},[367,568,569],{"class":376},"\"REF 88213\"",[367,571,492],{"class":402},[367,573,546],{"class":512},[367,575,503],{"class":402},[367,577,579,581,583,585,587,589,591,593,596,598,601],{"class":326,"line":578},12,[367,580,509],{"class":402},[367,582,513],{"class":512},[367,584,492],{"class":402},[367,586,46],{"class":512},[367,588,492],{"class":402},[367,590,46],{"class":512},[367,592,524],{"class":402},[367,594,595],{"class":376},"\"SALARY MARCH\"",[367,597,492],{"class":402},[367,599,600],{"class":512},"2500",[367,602,503],{"class":402},[367,604,606,608,610,612,614,616,618,620,623,625,627,630],{"class":326,"line":605},13,[367,607,509],{"class":402},[367,609,513],{"class":512},[367,611,492],{"class":402},[367,613,46],{"class":512},[367,615,492],{"class":402},[367,617,156],{"class":512},[367,619,524],{"class":402},[367,621,622],{"class":376},"\"TRANSFER TO\"",[367,624,492],{"class":402},[367,626,532],{"class":398},[367,628,629],{"class":512},"300",[367,631,503],{"class":402},[367,633,635,637,639,641,644,646,648],{"class":326,"line":634},14,[367,636,543],{"class":402},[367,638,546],{"class":512},[367,640,492],{"class":402},[367,642,643],{"class":376},"\"SAVINGS ACCOUNT\"",[367,645,492],{"class":402},[367,647,546],{"class":512},[367,649,650],{"class":402},"]]:\n",[367,652,654],{"class":326,"line":653},15,[367,655,656],{"class":402},"    ws.append(row)\n",[367,658,660,663,666],{"class":326,"line":659},16,[367,661,662],{"class":402},"wb.save(",[367,664,665],{"class":376},"\"statement.xlsx\"",[367,667,668],{"class":402},")\n",[367,670,672,675,677,680,682],{"class":326,"line":671},17,[367,673,674],{"class":402},"lines ",[367,676,457],{"class":398},[367,678,679],{"class":402}," pd.read_excel(",[367,681,665],{"class":376},[367,683,668],{"class":402},[367,685,687,690,693,696,699],{"class":326,"line":686},18,[367,688,689],{"class":512},"print",[367,691,692],{"class":402},"(",[367,694,695],{"class":512},"len",[367,697,698],{"class":402},"(lines))            ",[367,700,702],{"class":701},"s-wDw","# 6 lines for 3 transactions\n",[353,704,706],{"id":705},"step-1-find-the-rows-that-start-a-record","Step 1 — Find the rows that start a record",[10,708,709],{},"Every layout has some signal that a new record begins. In the statement it is a date: continuation lines never have one. Turn that signal into a boolean, then take its cumulative sum. The sum increases by one at each start row and stays the same on the continuation lines below it, so it numbers the records:",[358,711,713],{"className":389,"code":712,"language":391,"meta":363,"style":363},"starts = lines[\"Date\"].notna()\nrecord = starts.cumsum()\nprint(record.tolist())       # [1, 1, 1, 2, 3, 3]\n",[14,714,715,730,740],{"__ignoreMap":363},[367,716,717,720,722,725,727],{"class":326,"line":369},[367,718,719],{"class":402},"starts ",[367,721,457],{"class":398},[367,723,724],{"class":402}," lines[",[367,726,489],{"class":376},[367,728,729],{"class":402},"].notna()\n",[367,731,732,735,737],{"class":326,"line":412},[367,733,734],{"class":402},"record ",[367,736,457],{"class":398},[367,738,739],{"class":402}," starts.cumsum()\n",[367,741,742,744,747],{"class":326,"line":419},[367,743,689],{"class":512},[367,745,746],{"class":402},"(record.tolist())       ",[367,748,749],{"class":701},"# [1, 1, 1, 2, 3, 3]\n",[10,751,752],{},"Other exports use other signals: an invoice number, a value in the first column, a row that is not indented, or a line whose text matches a pattern such as a transaction code. Whatever it is, reduce it to one boolean Series; the rest of the method is the same. When the whole record sits in a single text column — a pasted log or a PDF converted to Excel — a regular expression usually provides the signal:",[358,754,756],{"className":389,"code":755,"language":391,"meta":363,"style":363},"text = pd.Series([\"02\u002F03 CARD PAYMENT TO\", \"GREEN GROCER LTD\", \"03\u002F03 SALARY MARCH\", \"04\u002F03 TRANSFER TO\"])\ntext_starts = text.str.match(r\"^\\d{2}\u002F\\d{2}\\s\")\nprint(text_starts.cumsum().tolist())     # [1, 1, 2, 3]\n",[14,757,758,788,825],{"__ignoreMap":363},[367,759,760,763,765,768,771,773,775,777,780,782,785],{"class":326,"line":369},[367,761,762],{"class":402},"text ",[367,764,457],{"class":398},[367,766,767],{"class":402}," pd.Series([",[367,769,770],{"class":376},"\"02\u002F03 CARD PAYMENT TO\"",[367,772,492],{"class":402},[367,774,551],{"class":376},[367,776,492],{"class":402},[367,778,779],{"class":376},"\"03\u002F03 SALARY MARCH\"",[367,781,492],{"class":402},[367,783,784],{"class":376},"\"04\u002F03 TRANSFER TO\"",[367,786,787],{"class":402},"])\n",[367,789,790,793,795,798,801,804,807,810,813,816,818,821,823],{"class":326,"line":412},[367,791,792],{"class":402},"text_starts ",[367,794,457],{"class":398},[367,796,797],{"class":402}," text.str.match(",[367,799,800],{"class":398},"r",[367,802,803],{"class":376},"\"",[367,805,806],{"class":512},"^\\d",[367,808,809],{"class":398},"{2}",[367,811,812],{"class":376},"\u002F",[367,814,815],{"class":512},"\\d",[367,817,809],{"class":398},[367,819,820],{"class":512},"\\s",[367,822,803],{"class":376},[367,824,668],{"class":402},[367,826,827,829,832],{"class":326,"line":419},[367,828,689],{"class":512},[367,830,831],{"class":402},"(text_starts.cumsum().tolist())     ",[367,833,834],{"class":701},"# [1, 1, 2, 3]\n",[10,836,837],{},"Anchor the pattern to the start of the line and make it specific enough that a continuation line cannot match by accident — a description that happens to begin with a number would otherwise start a record of its own.",[25,839,34,845,34,848,34,851,34,856,34,859,34,863,34,867,34,871,34,874,34,878,34,881,34,885,34,889,34,891,34,895,34,897,34,899,34,901,34,903,34,905,34,908,34,913,34,917,34,920,34,923,34,925,34,927,34,929,34,931,34,934,34,938,34,940,34,942,34,945,34,948,34,950,34,952,34,954,34,956,34,959,34,961,34,963,34,966,34,969,34,971,34,973,34,975,34,977,34,979,34,981,34,983,34,986,34,989,34,991,34,993,34,995,34,997,34,999,34,1001,34,1003,34,1005,34,1008,34,1011,34,1013,34,1015,34,1017,34,1019,34,1021,34,1023,34,1025,34,1027,34,1030,34,1032,34,1034,34,1036,34,1038,34,1040,34,1042,34,1044,34,1046],{"viewBox":840,"role":28,"ariaLabelledBy":841,"xmlns":32,"style":844},"178 0 403 278",[842,843],"mr-ids-t","mr-ids-d","width:100%;max-width:403px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[36,846,847],{"id":842},"The cumulative sum gives every line its record number",[40,849,850],{"id":843},"The start marker is true on the lines with a date and false on the continuation lines. Its running total is 1, 1, 1 for the card payment, 2 for the salary and 3, 3 for the transfer, so each continuation line carries the number of the record above it.",[44,852],{"x":853,"y":47,"width":854,"height":855,"fill":50},"178","403","278",[44,857],{"x":171,"y":858,"width":55,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"16.0",[44,860],{"x":861,"y":858,"width":862,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"227.0","63.5",[65,864,70],{"x":865,"y":866,"style":69},"258.7","33.0",[44,868],{"x":869,"y":858,"width":870,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"290.5","149.9",[65,872,78],{"x":873,"y":866,"style":69},"365.5",[44,875],{"x":876,"y":858,"width":877,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"440.4","59.8",[65,879,86],{"x":880,"y":866,"style":69},"470.3",[44,882],{"x":883,"y":858,"width":884,"height":56,"rx":47,"fill":57,"stroke":58,"style":59},"500.2","64.8",[65,886,888],{"x":887,"y":866,"style":69},"532.6","D",[44,890],{"x":171,"y":54,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},[65,892,95],{"x":893,"y":894,"style":69},"211.0","59.0",[44,896],{"x":861,"y":54,"width":862,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,898,101],{"x":865,"y":894,"style":100},[44,900],{"x":869,"y":54,"width":870,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,902,106],{"x":873,"y":894,"style":100},[44,904],{"x":876,"y":54,"width":877,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,906,907],{"x":880,"y":894,"style":100},"starts",[44,909],{"x":883,"y":54,"width":884,"height":90,"rx":47,"fill":910,"stroke":911,"style":912},"#d9f4f1","var(--teal,#0f9488)","stroke-width:1.5px",[65,914,916],{"x":887,"y":894,"style":915},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","record",[44,918],{"x":171,"y":919,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},"68.0",[65,921,118],{"x":893,"y":922,"style":69},"87.0",[44,924],{"x":861,"y":919,"width":862,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,926,124],{"x":865,"y":922,"style":123},[44,928],{"x":869,"y":919,"width":870,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,930,129],{"x":873,"y":922,"style":123},[44,932],{"x":876,"y":919,"width":877,"height":90,"rx":47,"fill":933,"stroke":331,"style":912},"#f0f4ff",[65,935,937],{"x":880,"y":922,"style":936},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","True",[44,939],{"x":883,"y":919,"width":884,"height":90,"rx":47,"fill":910,"stroke":911,"style":912},[65,941,95],{"x":887,"y":922,"style":915},[44,943],{"x":171,"y":944,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},"96.0",[65,946,46],{"x":893,"y":947,"style":69},"115.0",[44,949],{"x":861,"y":944,"width":862,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[44,951],{"x":869,"y":944,"width":870,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,953,147],{"x":873,"y":947,"style":123},[44,955],{"x":876,"y":944,"width":877,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,957,958],{"x":880,"y":947,"style":123},"False",[44,960],{"x":883,"y":944,"width":884,"height":90,"rx":47,"fill":910,"stroke":911,"style":912},[65,962,95],{"x":887,"y":947,"style":915},[44,964],{"x":171,"y":965,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},"124.0",[65,967,156],{"x":893,"y":968,"style":69},"143.0",[44,970],{"x":861,"y":965,"width":862,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[44,972],{"x":869,"y":965,"width":870,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,974,163],{"x":873,"y":968,"style":123},[44,976],{"x":876,"y":965,"width":877,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,978,958],{"x":880,"y":968,"style":123},[44,980],{"x":883,"y":965,"width":884,"height":90,"rx":47,"fill":910,"stroke":911,"style":912},[65,982,95],{"x":887,"y":968,"style":915},[44,984],{"x":171,"y":985,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},"152.0",[65,987,172],{"x":893,"y":988,"style":69},"171.0",[44,990],{"x":861,"y":985,"width":862,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,992,177],{"x":865,"y":988,"style":123},[44,994],{"x":869,"y":985,"width":870,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,996,182],{"x":873,"y":988,"style":123},[44,998],{"x":876,"y":985,"width":877,"height":90,"rx":47,"fill":933,"stroke":331,"style":912},[65,1000,937],{"x":880,"y":988,"style":936},[44,1002],{"x":883,"y":985,"width":884,"height":90,"rx":47,"fill":910,"stroke":911,"style":912},[65,1004,118],{"x":887,"y":988,"style":915},[44,1006],{"x":171,"y":1007,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},"180.0",[65,1009,194],{"x":893,"y":1010,"style":69},"199.0",[44,1012],{"x":861,"y":1007,"width":862,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,1014,199],{"x":865,"y":1010,"style":123},[44,1016],{"x":869,"y":1007,"width":870,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,1018,204],{"x":873,"y":1010,"style":123},[44,1020],{"x":876,"y":1007,"width":877,"height":90,"rx":47,"fill":933,"stroke":331,"style":912},[65,1022,937],{"x":880,"y":1010,"style":936},[44,1024],{"x":883,"y":1007,"width":884,"height":90,"rx":47,"fill":910,"stroke":911,"style":912},[65,1026,46],{"x":887,"y":1010,"style":915},[44,1028],{"x":171,"y":1029,"width":55,"height":90,"rx":47,"fill":57,"stroke":58,"style":59},"208.0",[65,1031,216],{"x":893,"y":861,"style":69},[44,1033],{"x":861,"y":1029,"width":862,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[44,1035],{"x":869,"y":1029,"width":870,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,1037,223],{"x":873,"y":861,"style":123},[44,1039],{"x":876,"y":1029,"width":877,"height":90,"rx":47,"fill":50,"stroke":58,"style":59},[65,1041,958],{"x":880,"y":861,"style":123},[44,1043],{"x":883,"y":1029,"width":884,"height":90,"rx":47,"fill":910,"stroke":911,"style":912},[65,1045,46],{"x":887,"y":861,"style":915},[65,1047,1051],{"x":1048,"y":1049,"style":1050},"380.0","258.0","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","start rows in blue; the teal column is starts.cumsum()",[353,1053,1055],{"id":1054},"step-2-aggregate-each-record","Step 2 — Aggregate each record",[10,1057,1058],{},"Group by the record number and give each column its own rule: join the text, keep the first date and amount. Named aggregation says this directly:",[358,1060,1062],{"className":389,"code":1061,"language":391,"meta":363,"style":363},"transactions = lines.groupby(record).agg(\n    date=(\"Date\", \"first\"),\n    description=(\"Description\", lambda s: \" \".join(s.dropna().astype(str).str.strip())),\n    amount=(\"Amount\", \"first\"),\n    lines=(\"Description\", \"size\"),\n).reset_index(drop=True)\nprint(transactions)\n",[14,1063,1064,1074,1094,1125,1142,1160,1174],{"__ignoreMap":363},[367,1065,1066,1069,1071],{"class":326,"line":369},[367,1067,1068],{"class":402},"transactions ",[367,1070,457],{"class":398},[367,1072,1073],{"class":402}," lines.groupby(record).agg(\n",[367,1075,1076,1080,1082,1084,1086,1088,1091],{"class":326,"line":412},[367,1077,1079],{"class":1078},"sa561","    date",[367,1081,457],{"class":398},[367,1083,692],{"class":402},[367,1085,489],{"class":376},[367,1087,492],{"class":402},[367,1089,1090],{"class":376},"\"first\"",[367,1092,1093],{"class":402},"),\n",[367,1095,1096,1099,1101,1103,1105,1107,1110,1113,1116,1119,1122],{"class":326,"line":419},[367,1097,1098],{"class":1078},"    description",[367,1100,457],{"class":398},[367,1102,692],{"class":402},[367,1104,495],{"class":376},[367,1106,492],{"class":402},[367,1108,1109],{"class":398},"lambda",[367,1111,1112],{"class":402}," s: ",[367,1114,1115],{"class":376},"\" \"",[367,1117,1118],{"class":402},".join(s.dropna().astype(",[367,1120,1121],{"class":512},"str",[367,1123,1124],{"class":402},").str.strip())),\n",[367,1126,1127,1130,1132,1134,1136,1138,1140],{"class":326,"line":432},[367,1128,1129],{"class":1078},"    amount",[367,1131,457],{"class":398},[367,1133,692],{"class":402},[367,1135,500],{"class":376},[367,1137,492],{"class":402},[367,1139,1090],{"class":376},[367,1141,1093],{"class":402},[367,1143,1144,1147,1149,1151,1153,1155,1158],{"class":326,"line":446},[367,1145,1146],{"class":1078},"    lines",[367,1148,457],{"class":398},[367,1150,692],{"class":402},[367,1152,495],{"class":376},[367,1154,492],{"class":402},[367,1156,1157],{"class":376},"\"size\"",[367,1159,1093],{"class":402},[367,1161,1162,1165,1168,1170,1172],{"class":326,"line":451},[367,1163,1164],{"class":402},").reset_index(",[367,1166,1167],{"class":1078},"drop",[367,1169,457],{"class":398},[367,1171,937],{"class":512},[367,1173,668],{"class":402},[367,1175,1176,1178],{"class":326,"line":463},[367,1177,689],{"class":512},[367,1179,1180],{"class":402},"(transactions)\n",[10,1182,1183,1186,1187,1190,1191,1194,1195,1199],{},[14,1184,1185],{},"first"," skips missing values, so it returns the date and amount from the start row even though the continuation lines are blank there. Dropping missing values before joining keeps stray ",[14,1188,1189],{},"nan"," text out of the description. The ",[14,1192,1193],{},"lines"," column records how many rows each transaction came from, which is useful when checking the result against the source. Aggregating different columns with different rules is covered in ",[19,1196,1198],{"href":1197},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fuse-different-aggregations-per-column-in-a-pivot\u002F","Use Different Aggregations per Column in a pandas Pivot",".",[353,1201,1203],{"id":1202},"step-3-check-that-nothing-was-misattached","Step 3 — Check that nothing was misattached",[10,1205,1206],{},"The method assumes the file follows the pattern. Three checks catch the cases where it does not:",[358,1208,1210],{"className":389,"code":1209,"language":391,"meta":363,"style":363},"problems = []\nif not starts.iloc[0]:\n    problems.append(\"the first line is a continuation with no record above it\")\nstray_amounts = lines.loc[~starts & lines[\"Amount\"].notna()]\nif not stray_amounts.empty:\n    problems.append(f\"continuation lines with amounts: {stray_amounts.index.tolist()}\")\nif abs(transactions[\"amount\"].sum() - lines[\"Amount\"].sum()) > 1e-9:\n    problems.append(\"total changed during regrouping\")\nprint(problems or \"ok\")      # ok\n",[14,1211,1212,1222,1238,1248,1273,1282,1306,1340,1349],{"__ignoreMap":363},[367,1213,1214,1217,1219],{"class":326,"line":369},[367,1215,1216],{"class":402},"problems ",[367,1218,457],{"class":398},[367,1220,1221],{"class":402}," []\n",[367,1223,1224,1227,1230,1233,1235],{"class":326,"line":412},[367,1225,1226],{"class":398},"if",[367,1228,1229],{"class":398}," not",[367,1231,1232],{"class":402}," starts.iloc[",[367,1234,47],{"class":512},[367,1236,1237],{"class":402},"]:\n",[367,1239,1240,1243,1246],{"class":326,"line":419},[367,1241,1242],{"class":402},"    problems.append(",[367,1244,1245],{"class":376},"\"the first line is a continuation with no record above it\"",[367,1247,668],{"class":402},[367,1249,1250,1253,1255,1258,1261,1263,1266,1268,1270],{"class":326,"line":432},[367,1251,1252],{"class":402},"stray_amounts ",[367,1254,457],{"class":398},[367,1256,1257],{"class":402}," lines.loc[",[367,1259,1260],{"class":398},"~",[367,1262,719],{"class":402},[367,1264,1265],{"class":398},"&",[367,1267,724],{"class":402},[367,1269,500],{"class":376},[367,1271,1272],{"class":402},"].notna()]\n",[367,1274,1275,1277,1279],{"class":326,"line":446},[367,1276,1226],{"class":398},[367,1278,1229],{"class":398},[367,1280,1281],{"class":402}," stray_amounts.empty:\n",[367,1283,1284,1286,1289,1292,1296,1299,1302,1304],{"class":326,"line":451},[367,1285,1242],{"class":402},[367,1287,1288],{"class":398},"f",[367,1290,1291],{"class":376},"\"continuation lines with amounts: ",[367,1293,1295],{"class":1294},"sSjpA","{",[367,1297,1298],{"class":402},"stray_amounts.index.tolist()",[367,1300,1301],{"class":1294},"}",[367,1303,803],{"class":376},[367,1305,668],{"class":402},[367,1307,1308,1310,1313,1316,1319,1322,1324,1326,1328,1331,1334,1337],{"class":326,"line":463},[367,1309,1226],{"class":398},[367,1311,1312],{"class":512}," abs",[367,1314,1315],{"class":402},"(transactions[",[367,1317,1318],{"class":376},"\"amount\"",[367,1320,1321],{"class":402},"].sum() ",[367,1323,532],{"class":398},[367,1325,724],{"class":402},[367,1327,500],{"class":376},[367,1329,1330],{"class":402},"].sum()) ",[367,1332,1333],{"class":398},">",[367,1335,1336],{"class":512}," 1e-9",[367,1338,1339],{"class":402},":\n",[367,1341,1342,1344,1347],{"class":326,"line":474},[367,1343,1242],{"class":402},[367,1345,1346],{"class":376},"\"total changed during regrouping\"",[367,1348,668],{"class":402},[367,1350,1351,1353,1356,1359,1362,1365],{"class":326,"line":506},[367,1352,689],{"class":512},[367,1354,1355],{"class":402},"(problems ",[367,1357,1358],{"class":398},"or",[367,1360,1361],{"class":376}," \"ok\"",[367,1363,1364],{"class":402},")      ",[367,1366,1367],{"class":701},"# ok\n",[10,1369,1370,1371,1373],{},"An orphan line at the top usually means the file was cut from a longer statement, and its text belongs to a record on a previous page. A continuation line with an amount means the signal is wrong — perhaps a fee printed on its own line without a date — and ",[14,1372,1185],{}," would silently drop that amount. The total check catches both. Page breaks that repeat the column headers in the middle of the data are a related problem; remove repeated header rows before numbering records.",[353,1375,1377],{"id":1376},"step-4-blocks-separated-by-blank-lines","Step 4 — Blocks separated by blank lines",[10,1379,1380],{},"Some exports put one field per row and separate records with a blank line — contact cards, or a form exported as \"field, value\" pairs. Here the record signal is the blank row:",[358,1382,1384],{"className":389,"code":1383,"language":391,"meta":363,"style":363},"wb = Workbook()\nws = wb.active\nfor row in [[\"Name\", \"Ann Lee\"], [\"Email\", \"ann@example.com\"], [\"Phone\", \"0123 456\"], [None, None],\n            [\"Name\", \"Bob Ray\"], [\"Email\", \"bob@example.com\"], [None, None],\n            [\"Name\", \"Cai Wu\"], [\"Phone\", \"0456 789\"], [\"Email\", \"cai@example.com\"]]:\n    ws.append(row)\nwb.save(\"contacts.xlsx\")\n\nkv = pd.read_excel(\"contacts.xlsx\", header=None, names=[\"field\", \"value\"])\nblank = kv.isna().all(axis=1)\nkv = kv.assign(record=blank.cumsum())[~blank]\nprint(kv[\"record\"].tolist())     # [0, 0, 0, 1, 1, 2, 2, 2]\n",[14,1385,1386,1394,1402,1451,1481,1512,1516,1525,1529,1569,1588,1609],{"__ignoreMap":363},[367,1387,1388,1390,1392],{"class":326,"line":369},[367,1389,454],{"class":402},[367,1391,457],{"class":398},[367,1393,460],{"class":402},[367,1395,1396,1398,1400],{"class":326,"line":412},[367,1397,466],{"class":402},[367,1399,457],{"class":398},[367,1401,471],{"class":402},[367,1403,1404,1406,1408,1410,1412,1415,1417,1420,1423,1426,1428,1431,1433,1436,1438,1441,1443,1445,1447,1449],{"class":326,"line":419},[367,1405,477],{"class":398},[367,1407,480],{"class":402},[367,1409,483],{"class":398},[367,1411,486],{"class":402},[367,1413,1414],{"class":376},"\"Name\"",[367,1416,492],{"class":402},[367,1418,1419],{"class":376},"\"Ann Lee\"",[367,1421,1422],{"class":402},"], [",[367,1424,1425],{"class":376},"\"Email\"",[367,1427,492],{"class":402},[367,1429,1430],{"class":376},"\"ann@example.com\"",[367,1432,1422],{"class":402},[367,1434,1435],{"class":376},"\"Phone\"",[367,1437,492],{"class":402},[367,1439,1440],{"class":376},"\"0123 456\"",[367,1442,1422],{"class":402},[367,1444,546],{"class":512},[367,1446,492],{"class":402},[367,1448,546],{"class":512},[367,1450,503],{"class":402},[367,1452,1453,1455,1457,1459,1462,1464,1466,1468,1471,1473,1475,1477,1479],{"class":326,"line":432},[367,1454,543],{"class":402},[367,1456,1414],{"class":376},[367,1458,492],{"class":402},[367,1460,1461],{"class":376},"\"Bob Ray\"",[367,1463,1422],{"class":402},[367,1465,1425],{"class":376},[367,1467,492],{"class":402},[367,1469,1470],{"class":376},"\"bob@example.com\"",[367,1472,1422],{"class":402},[367,1474,546],{"class":512},[367,1476,492],{"class":402},[367,1478,546],{"class":512},[367,1480,503],{"class":402},[367,1482,1483,1485,1487,1489,1492,1494,1496,1498,1501,1503,1505,1507,1510],{"class":326,"line":446},[367,1484,543],{"class":402},[367,1486,1414],{"class":376},[367,1488,492],{"class":402},[367,1490,1491],{"class":376},"\"Cai Wu\"",[367,1493,1422],{"class":402},[367,1495,1435],{"class":376},[367,1497,492],{"class":402},[367,1499,1500],{"class":376},"\"0456 789\"",[367,1502,1422],{"class":402},[367,1504,1425],{"class":376},[367,1506,492],{"class":402},[367,1508,1509],{"class":376},"\"cai@example.com\"",[367,1511,650],{"class":402},[367,1513,1514],{"class":326,"line":451},[367,1515,656],{"class":402},[367,1517,1518,1520,1523],{"class":326,"line":463},[367,1519,662],{"class":402},[367,1521,1522],{"class":376},"\"contacts.xlsx\"",[367,1524,668],{"class":402},[367,1526,1527],{"class":326,"line":474},[367,1528,416],{"emptyLinePlaceholder":415},[367,1530,1531,1534,1536,1538,1540,1542,1545,1547,1549,1551,1554,1556,1559,1562,1564,1567],{"class":326,"line":506},[367,1532,1533],{"class":402},"kv ",[367,1535,457],{"class":398},[367,1537,679],{"class":402},[367,1539,1522],{"class":376},[367,1541,492],{"class":402},[367,1543,1544],{"class":1078},"header",[367,1546,457],{"class":398},[367,1548,546],{"class":512},[367,1550,492],{"class":402},[367,1552,1553],{"class":1078},"names",[367,1555,457],{"class":398},[367,1557,1558],{"class":402},"[",[367,1560,1561],{"class":376},"\"field\"",[367,1563,492],{"class":402},[367,1565,1566],{"class":376},"\"value\"",[367,1568,787],{"class":402},[367,1570,1571,1574,1576,1579,1582,1584,1586],{"class":326,"line":540},[367,1572,1573],{"class":402},"blank ",[367,1575,457],{"class":398},[367,1577,1578],{"class":402}," kv.isna().all(",[367,1580,1581],{"class":1078},"axis",[367,1583,457],{"class":398},[367,1585,95],{"class":512},[367,1587,668],{"class":402},[367,1589,1590,1592,1594,1597,1599,1601,1604,1606],{"class":326,"line":560},[367,1591,1533],{"class":402},[367,1593,457],{"class":398},[367,1595,1596],{"class":402}," kv.assign(",[367,1598,916],{"class":1078},[367,1600,457],{"class":398},[367,1602,1603],{"class":402},"blank.cumsum())[",[367,1605,1260],{"class":398},[367,1607,1608],{"class":402},"blank]\n",[367,1610,1611,1613,1616,1619,1622],{"class":326,"line":578},[367,1612,689],{"class":512},[367,1614,1615],{"class":402},"(kv[",[367,1617,1618],{"class":376},"\"record\"",[367,1620,1621],{"class":402},"].tolist())     ",[367,1623,1624],{"class":701},"# [0, 0, 0, 1, 1, 2, 2, 2]\n",[10,1626,1627,1628,1631],{},"Reading with ",[14,1629,1630],{},"header=None"," matters: otherwise pandas would take \"Name, Ann Lee\" as the column headers. Fields do not have to appear in the same order in every block — Cai Wu has Phone before Email — because the next step matches them by name, not position.",[353,1633,1635],{"id":1634},"step-5-pivot-stacked-fields-into-columns","Step 5 — Pivot stacked fields into columns",[10,1637,1638,1639,1642],{},"With a record number on every line, pivot the field names into columns. Check first that no field repeats within a record, because ",[14,1640,1641],{},"pivot"," raises an error on duplicates and a silent \"keep one\" would lose data:",[358,1644,1646],{"className":389,"code":1645,"language":391,"meta":363,"style":363},"repeats = kv[kv.duplicated([\"record\", \"field\"], keep=False)]\nif not repeats.empty:\n    raise ValueError(f\"repeated fields:\\n{repeats}\")\ncontacts = kv.pivot(index=\"record\", columns=\"field\", values=\"value\").reset_index(drop=True)\ncontacts = contacts.reindex(columns=[\"Name\", \"Email\", \"Phone\"])\ncontacts.columns.name = None\nprint(contacts)\n",[14,1647,1648,1677,1686,1713,1758,1785,1795],{"__ignoreMap":363},[367,1649,1650,1653,1655,1658,1660,1662,1664,1667,1670,1672,1674],{"class":326,"line":369},[367,1651,1652],{"class":402},"repeats ",[367,1654,457],{"class":398},[367,1656,1657],{"class":402}," kv[kv.duplicated([",[367,1659,1618],{"class":376},[367,1661,492],{"class":402},[367,1663,1561],{"class":376},[367,1665,1666],{"class":402},"], ",[367,1668,1669],{"class":1078},"keep",[367,1671,457],{"class":398},[367,1673,958],{"class":512},[367,1675,1676],{"class":402},")]\n",[367,1678,1679,1681,1683],{"class":326,"line":412},[367,1680,1226],{"class":398},[367,1682,1229],{"class":398},[367,1684,1685],{"class":402}," repeats.empty:\n",[367,1687,1688,1691,1694,1696,1698,1701,1704,1707,1709,1711],{"class":326,"line":419},[367,1689,1690],{"class":398},"    raise",[367,1692,1693],{"class":512}," ValueError",[367,1695,692],{"class":402},[367,1697,1288],{"class":398},[367,1699,1700],{"class":376},"\"repeated fields:",[367,1702,1703],{"class":1294},"\\n{",[367,1705,1706],{"class":402},"repeats",[367,1708,1301],{"class":1294},[367,1710,803],{"class":376},[367,1712,668],{"class":402},[367,1714,1715,1718,1720,1723,1726,1728,1730,1732,1735,1737,1739,1741,1744,1746,1748,1750,1752,1754,1756],{"class":326,"line":432},[367,1716,1717],{"class":402},"contacts ",[367,1719,457],{"class":398},[367,1721,1722],{"class":402}," kv.pivot(",[367,1724,1725],{"class":1078},"index",[367,1727,457],{"class":398},[367,1729,1618],{"class":376},[367,1731,492],{"class":402},[367,1733,1734],{"class":1078},"columns",[367,1736,457],{"class":398},[367,1738,1561],{"class":376},[367,1740,492],{"class":402},[367,1742,1743],{"class":1078},"values",[367,1745,457],{"class":398},[367,1747,1566],{"class":376},[367,1749,1164],{"class":402},[367,1751,1167],{"class":1078},[367,1753,457],{"class":398},[367,1755,937],{"class":512},[367,1757,668],{"class":402},[367,1759,1760,1762,1764,1767,1769,1771,1773,1775,1777,1779,1781,1783],{"class":326,"line":446},[367,1761,1717],{"class":402},[367,1763,457],{"class":398},[367,1765,1766],{"class":402}," contacts.reindex(",[367,1768,1734],{"class":1078},[367,1770,457],{"class":398},[367,1772,1558],{"class":402},[367,1774,1414],{"class":376},[367,1776,492],{"class":402},[367,1778,1425],{"class":376},[367,1780,492],{"class":402},[367,1782,1435],{"class":376},[367,1784,787],{"class":402},[367,1786,1787,1790,1792],{"class":326,"line":451},[367,1788,1789],{"class":402},"contacts.columns.name ",[367,1791,457],{"class":398},[367,1793,1794],{"class":512}," None\n",[367,1796,1797,1799],{"class":326,"line":463},[367,1798,689],{"class":512},[367,1800,1801],{"class":402},"(contacts)\n",[10,1803,1804,1805,1808,1809,1812],{},"Bob Ray has no phone, so his cell is empty rather than borrowing a value from a neighbour. ",[14,1806,1807],{},"reindex"," fixes the column order and guarantees every expected column exists even if no record in this file had it. If a field can legitimately repeat — several phone numbers — join the values with ",[14,1810,1811],{},"pivot_table(aggfunc=\"; \".join)"," instead of raising.",[25,1814,34,1820,34,1823,34,1826,34,1830,34,1835,34,1846,34,1852,34,1856,34,1861,34,1865,34,1870,34,1873,34,1876,34,1882,34,1887,34,1891,34,1895,34,1899,34,1902,34,1905,34,1912,34,1916,34,1919,34,1922,34,1926,34,1929,34,1932,34,1938,34,1942,34,1944,34,1947,34,1951,34,1954,34,1959,34,1966,34,1971,34,1974],{"viewBox":1815,"role":28,"ariaLabelledBy":1816,"xmlns":32,"style":1819},"4 0 752 160",[1817,1818],"mr-steps-t","mr-steps-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[36,1821,1822],{"id":1817},"The same four steps for any multi-row layout",[40,1824,1825],{"id":1818},"Decide which rows start a record or which rows separate records, turn that into a boolean, number the records with a cumulative sum, aggregate or pivot each record into one row, then check for orphan lines, stray values and changed totals.",[44,1827],{"x":156,"y":47,"width":1828,"height":1829,"fill":50},"752","160",[44,1831],{"x":53,"y":858,"width":1832,"height":1833,"rx":1834,"fill":933,"stroke":331,"style":332},"115.2","128.0","12",[1836,1837,1839],"g",{"transform":1838},"translate(65.6,29.0) scale(1.000)",[1840,1841],"path",{"d":1842,"fill":1843,"stroke":1844,"style":1845},"M11 4a7 7 0 1 0 .01 0z M16 16l5 5","none","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[65,1847,1851],{"x":1848,"y":1849,"style":1850},"77.6","75.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Find the",[65,1853,1855],{"x":1848,"y":1854,"style":1850},"93.0","signal",[65,1857,1860],{"x":1848,"y":1858,"style":1859},"112.5","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","start row or",[65,1862,1864],{"x":1848,"y":1863,"style":1859},"130.0","blank",[326,1866],{"x1":1867,"y1":1868,"x2":1869,"y2":1868,"stroke":331,"style":332},"140.2","80.0","162.2",[334,1871],{"points":1872,"fill":337},"166.2,80.0 157.2,85.0 157.2,75.0",[44,1874],{"x":1875,"y":858,"width":1832,"height":1833,"rx":1834,"fill":933,"stroke":331,"style":332},"171.2",[1836,1877,1879],{"transform":1878},"translate(216.8,37.8) scale(1.000)",[1840,1880],{"d":1881,"fill":1843,"stroke":1844,"style":1845},"M5 12.5l4.5 4.5L19 7",[65,1883,1886],{"x":1884,"y":1885,"style":1850},"228.8","84.2","Boolean",[65,1888,1890],{"x":1884,"y":1889,"style":1850},"101.8","marker",[65,1892,1894],{"x":1884,"y":1893,"style":1859},"121.2","one per line",[326,1896],{"x1":1897,"y1":1868,"x2":1898,"y2":1868,"stroke":331,"style":332},"291.4","313.4",[334,1900],{"points":1901,"fill":337},"317.4,80.0 308.4,85.0 308.4,75.0",[44,1903],{"x":1904,"y":858,"width":1832,"height":1833,"rx":1834,"fill":910,"stroke":911,"style":332},"322.4",[1836,1906,1908],{"transform":1907},"translate(368.0,37.8) scale(1.000)",[1840,1909],{"d":1910,"fill":1843,"stroke":1911,"style":1845},"M5 9h14 M4 15h14 M10 4l-2 16 M16 4l-2 16","var(--teal-ink,#0b6157)",[65,1913,1915],{"x":1048,"y":1885,"style":1914},"font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Number",[65,1917,1918],{"x":1048,"y":1889,"style":1914},"records",[65,1920,1921],{"x":1048,"y":1893,"style":1859},"cumsum()",[326,1923],{"x1":1924,"y1":1868,"x2":1925,"y2":1868,"stroke":331,"style":332},"442.6","464.6",[334,1927],{"points":1928,"fill":337},"468.6,80.0 459.6,85.0 459.6,75.0",[44,1930],{"x":1931,"y":858,"width":1832,"height":1833,"rx":1834,"fill":933,"stroke":331,"style":332},"473.6",[1836,1933,1935],{"transform":1934},"translate(519.2,37.8) scale(1.000)",[1840,1936],{"d":1937,"fill":1843,"stroke":1844,"style":1845},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14",[65,1939,1941],{"x":1940,"y":1885,"style":1850},"531.2","Aggregate or",[65,1943,1641],{"x":1940,"y":1889,"style":1850},[65,1945,1946],{"x":1940,"y":1893,"style":1859},"one row each",[326,1948],{"x1":1949,"y1":1868,"x2":1950,"y2":1868,"stroke":331,"style":332},"593.8","615.8",[334,1952],{"points":1953,"fill":337},"619.8,80.0 610.8,85.0 610.8,75.0",[44,1955],{"x":1956,"y":858,"width":1832,"height":1833,"rx":1834,"fill":1957,"stroke":1958,"style":332},"624.8","#fdefd8","var(--gold,#b4740a)",[1836,1960,1962],{"transform":1961},"translate(670.4,37.8) scale(1.000)",[1840,1963],{"d":1964,"fill":1843,"stroke":1965,"style":1845},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","var(--gold-ink,#7a4e06)",[65,1967,1970],{"x":1968,"y":1885,"style":1969},"682.4","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Check the",[65,1972,1973],{"x":1968,"y":1889,"style":1969},"result",[65,1975,1976],{"x":1968,"y":1893,"style":1859},"orphans, totals",[353,1978,1980],{"id":1979},"step-6-write-the-rebuilt-records","Step 6 — Write the rebuilt records",[10,1982,1983],{},"Both results are ordinary one-row-per-record tables and can be written straight to Excel, with the source line count kept for auditing:",[358,1985,1987],{"className":389,"code":1986,"language":391,"meta":363,"style":363},"with pd.ExcelWriter(\"rebuilt.xlsx\") as writer:\n    transactions.to_excel(writer, sheet_name=\"Transactions\", index=False)\n    contacts.to_excel(writer, sheet_name=\"Contacts\", index=False)\nprint(pd.read_excel(\"rebuilt.xlsx\", sheet_name=\"Transactions\")[\"description\"].iloc[0])\n# CARD PAYMENT TO GREEN GROCER LTD REF 88213\n",[14,1988,1989,2008,2031,2053,2083],{"__ignoreMap":363},[367,1990,1991,1994,1997,2000,2003,2005],{"class":326,"line":369},[367,1992,1993],{"class":398},"with",[367,1995,1996],{"class":402}," pd.ExcelWriter(",[367,1998,1999],{"class":376},"\"rebuilt.xlsx\"",[367,2001,2002],{"class":402},") ",[367,2004,406],{"class":398},[367,2006,2007],{"class":402}," writer:\n",[367,2009,2010,2013,2016,2018,2021,2023,2025,2027,2029],{"class":326,"line":412},[367,2011,2012],{"class":402},"    transactions.to_excel(writer, ",[367,2014,2015],{"class":1078},"sheet_name",[367,2017,457],{"class":398},[367,2019,2020],{"class":376},"\"Transactions\"",[367,2022,492],{"class":402},[367,2024,1725],{"class":1078},[367,2026,457],{"class":398},[367,2028,958],{"class":512},[367,2030,668],{"class":402},[367,2032,2033,2036,2038,2040,2043,2045,2047,2049,2051],{"class":326,"line":419},[367,2034,2035],{"class":402},"    contacts.to_excel(writer, ",[367,2037,2015],{"class":1078},[367,2039,457],{"class":398},[367,2041,2042],{"class":376},"\"Contacts\"",[367,2044,492],{"class":402},[367,2046,1725],{"class":1078},[367,2048,457],{"class":398},[367,2050,958],{"class":512},[367,2052,668],{"class":402},[367,2054,2055,2057,2060,2062,2064,2066,2068,2070,2073,2076,2079,2081],{"class":326,"line":432},[367,2056,689],{"class":512},[367,2058,2059],{"class":402},"(pd.read_excel(",[367,2061,1999],{"class":376},[367,2063,492],{"class":402},[367,2065,2015],{"class":1078},[367,2067,457],{"class":398},[367,2069,2020],{"class":376},[367,2071,2072],{"class":402},")[",[367,2074,2075],{"class":376},"\"description\"",[367,2077,2078],{"class":402},"].iloc[",[367,2080,47],{"class":512},[367,2082,787],{"class":402},[367,2084,2085],{"class":326,"line":446},[367,2086,2087],{"class":701},"# CARD PAYMENT TO GREEN GROCER LTD REF 88213\n",[10,2089,2090,2091,1199],{},"Long joined descriptions are easier to read with wrapped text and a wider column; see ",[19,2092,2094],{"href":2093},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fwrap-text-and-align-cells-with-openpyxl\u002F","Wrap Text and Align Cells with openpyxl",[353,2096,2098],{"id":2097},"common-pitfalls","Common pitfalls",[2100,2101,2102,2110,2116,2122],"ul",{},[2103,2104,2105,2109],"li",{},[2106,2107,2108],"strong",{},"Forward-filling instead of grouping."," Filling the date down gives every continuation line a date and an amount of its own, so the amount is counted three times. Group, do not fill.",[2103,2111,2112,2115],{},[2106,2113,2114],{},"Sorting before numbering."," The method depends on the original line order. Number the records first, then sort the result.",[2103,2117,2118,2121],{},[2106,2119,2120],{},"Trusting a single file."," A layout that holds for this month's export may not hold for the next. Keep the checks from Step 3 in the pipeline permanently, so a changed layout fails the run instead of producing plausible nonsense.",[2103,2123,2124,2127,2128,2131],{},[2106,2125,2126],{},"Joining with no separator."," ",[14,2129,2130],{},"\"\".join"," glues \"TO\" and \"GREEN\" into \"TOGREEN\". Join with a space, or with a line break if the text will be shown wrapped.",[353,2133,2135],{"id":2134},"conclusion","Conclusion",[10,2137,2138],{},"Multi-row records all yield to the same idea: turn \"this line starts a record\" or \"this line separates records\" into a boolean, number the records with its cumulative sum, then aggregate each record with per-column rules or pivot its stacked fields into columns. Check for orphan lines, values on continuation rows, repeated fields and changed totals before trusting the result.",[353,2140,2142],{"id":2141},"frequently-asked-questions","Frequently asked questions",[10,2144,2145,2148],{},[2106,2146,2147],{},"How do I merge continuation rows into the row above in pandas?","\nMark the rows that start a record — for example, rows with a date — and take the cumulative sum of that marker. Every continuation row gets the same number as its record, so groupby on that number with \" \".join for text and \"first\" for other columns rebuilds one row per record.",[10,2150,2151,2154],{},[2106,2152,2153],{},"What if a record is identified by a blank line instead of a start row?","\nTake the cumulative sum of the blank-row marker instead. Each blank row increments the number, and the rows up to the next blank belong to one record. Drop the blank rows before grouping.",[10,2156,2157,2160],{},[2106,2158,2159],{},"How do I turn one-field-per-row blocks into columns?","\nGive each block a record number, then pivot with the field names as columns and the values as cell contents. Check for repeated fields first, because pivot raises an error on duplicates.",[10,2162,2163,2166],{},[2106,2164,2165],{},"How do I know the parse went right?","\nCheck that no continuation line appears before the first record, that continuation lines carry no amounts, and that totals match the source — the sum of amounts should be unchanged by the regrouping.",[353,2168,2170],{"id":2169},"related","Related",[2100,2172,2173,2179,2186,2193],{},[2103,2174,2175,2176,2178],{},"Up one level: ",[19,2177,22],{"href":21}," — pulling structure out of irregular sheets.",[2103,2180,2181,2185],{},[19,2182,2184],{"href":2183},"\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Fforward-fill-blank-cells-in-grouped-excel-reports\u002F","Forward-Fill Blank Cells in Grouped Excel Reports with pandas"," — when blanks mean \"same as above\" instead.",[2103,2187,2188,2192],{},[19,2189,2191],{"href":2190},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fread-multiple-tables-from-one-excel-sheet\u002F","Read Multiple Tables from One Excel Sheet"," — splitting a sheet into blocks.",[2103,2194,2195,2199],{},[19,2196,2198],{"href":2197},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fextract-report-header-fields-from-an-excel-sheet\u002F","Extract Report Header Fields from an Excel Sheet with Python"," — label-value pairs above a table.",[2201,2202,2203],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":363,"searchDepth":412,"depth":412,"links":2205},[2206,2207,2208,2209,2210,2211,2212,2213,2214,2215,2216],{"id":355,"depth":412,"text":356},{"id":705,"depth":412,"text":706},{"id":1054,"depth":412,"text":1055},{"id":1202,"depth":412,"text":1203},{"id":1376,"depth":412,"text":1377},{"id":1634,"depth":412,"text":1635},{"id":1979,"depth":412,"text":1980},{"id":2097,"depth":412,"text":2098},{"id":2134,"depth":412,"text":2135},{"id":2141,"depth":412,"text":2142},{"id":2169,"depth":412,"text":2170},"2026-09-23","Bank statements wrap long descriptions onto extra rows, and contact exports stack one field per row. Number each record from the row that starts it, join the continuation lines, pivot stacked fields into columns, and catch orphan lines and repeated fields instead of silently mixing records.","md",[2221,2223,2225,2227],{"q":2147,"a":2222},"Mark the rows that start a record — for example, rows with a date — and take the cumulative sum of that marker. Every continuation row gets the same number as its record, so groupby on that number with \" \".join for text and \"first\" for other columns rebuilds one row per record.",{"q":2153,"a":2224},"Take the cumulative sum of the blank-row marker instead. Each blank row increments the number, and the rows up to the next blank belong to one record. Drop the blank rows before grouping.",{"q":2159,"a":2226},"Give each block a record number, then pivot with the field names as columns and the values as cell contents. Check for repeated fields first, because pivot raises an error on duplicates.",{"q":2165,"a":2228},"Check that no continuation line appears before the first record, that continuation lines carry no amounts, and that totals match the source — the sum of amounts should be unchanged by the regrouping.","parse records that span multiple rows in excel with pandas",null,{"breadcrumb":2232},[2233,2235,2238],{"name":2234,"item":812},"Home",{"name":2236,"item":2237},"Advanced Data Transformation and Cleaning for Excel Automation","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":22,"item":21},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-records-that-span-multiple-rows-in-excel",{"title":2241,"description":2242},"Parse Multi-Row Records in Excel with pandas (continuation lines)","Combine Excel records that span several rows with pandas — continuation lines in bank statements, a cumsum record id, groupby aggregation per column, stacked label-value blocks pivoted to columns, and checks for orphan and conflicting lines.","parse-records-that-span-multiple-rows-in-excel","advanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-records-that-span-multiple-rows-in-excel\u002Findex",[2246,2247,2248],"messy-sheets","data-cleaning","pandas","how-to","NViDpzifns0mfLCmgHZz1Z3Jv0CaSRRHSV5YGzH1gZ4",[2252,2256],{"title":2253,"path":2254,"stem":2255,"children":-1},"Parse Numbers with Units in Excel Cells","\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-numbers-with-units-in-excel-cells","advanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-numbers-with-units-in-excel-cells\u002Findex",{"title":2257,"path":2258,"stem":2259,"children":-1},"Read a Calendar-Style Sheet into a Table","\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fread-a-calendar-style-sheet-into-a-table","advanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fread-a-calendar-style-sheet-into-a-table\u002Findex",[2261,2263],{"path":2262,"title":22},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets",{"path":2264,"title":2236},"\u002Fadvanced-data-transformation-and-cleaning",[],[2267,2270,2273,2276,2279,2282],{"path":2268,"title":2269,"kind":2249},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Ffreeze-header-row-in-excel-with-openpyxl","Freeze the Header Row in Excel with openpyxl",{"path":2271,"title":2272,"kind":2249},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fhandle-merged-cells-when-reading-excel-with-pandas","Handle Merged Cells When Reading Excel with pandas",{"path":2274,"title":2275,"kind":2249},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Funmerge-cells-and-fill-values-with-openpyxl","Unmerge Cells and Fill the Values with openpyxl",{"path":2277,"title":2278,"kind":2249},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fcombine-sheets-with-different-layouts-into-one-table","Combine Sheets with Different Layouts into One Table",{"path":2280,"title":2281,"kind":2249},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fextract-hyperlink-urls-from-excel-cells","Extract Hyperlink URLs from Excel Cells with openpyxl",{"path":2283,"title":2198,"kind":2249},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fextract-report-header-fields-from-an-excel-sheet",1790972090931]