[{"data":1,"prerenderedAt":2436},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fconvert-excel-to-csv-with-python":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fconvert-excel-to-csv-with-python":2428},{"id":4,"title":5,"body":6,"dateModified":2399,"datePublished":2399,"description":2400,"extension":2401,"faq":2402,"meta":2411,"navigation":191,"path":2421,"seo":2422,"slug":2424,"stem":2425,"type":2426,"__hash__":2427},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fconvert-excel-to-csv-with-python\u002Findex.md","Convert Excel to CSV with Python",{"type":7,"value":8,"toc":2382},"minimark",[9,35,126,131,159,163,234,245,249,252,568,595,599,602,945,952,956,959,1062,1117,1122,1126,1133,1428,1448,1513,1517,1520,1763,1774,1778,1781,2038,2047,2051,2054,2106,2109,2113,2244,2248,2255,2259,2262,2266,2279,2283,2297,2308,2317,2329,2333,2336,2345,2348,2378],[10,11,12,13,17,18,21,22,17,25,28,29,34],"p",{},"CSV is the format every tool can read and no tool can misinterpret — provided the conversion is done carefully. Done carelessly it turns ",[14,15,16],"code",{},"00412"," into ",[14,19,20],{},"412",", ",[14,23,24],{},"15\u002F01\u002F2026",[14,26,27],{},"2026-01-15 00:00:00",", and an accented name into mojibake. This guide, part of ",[30,31,33],"a",{"href":32},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002F","Working with Large Excel Files in Python",", covers the conversion and each of those traps.",[36,37,45,46,45,50,45,54,45,61,45,71,45,78,45,83,45,87,45,91,45,95,45,100,45,105,45,110,45,113,45,116,45,119,45,122],"svg",{"viewBox":38,"role":39,"ariaLabelledBy":40,"xmlns":43,"style":44},"0 0 740 232","img",[41,42],"csv-loss-t","csv-loss-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:740px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[47,48,49],"title",{"id":41},"What survives the conversion from xlsx to CSV, and what does not",[51,52,53],"desc",{"id":42},"Values, one sheet's rows and the header survive. Formulas become their cached results, formatting and column widths are lost, multiple sheets need multiple files, and types are only preserved if you control the read.",[55,56],"rect",{"x":57,"y":57,"width":58,"height":59,"fill":60},"0","740","232","#ffffff",[55,62],{"x":63,"y":64,"width":65,"height":66,"rx":67,"fill":68,"stroke":69,"style":70},"20","26","330","180","14","#d9f4f1","var(--teal,#0f9488)","stroke-width:2px",[72,73,77],"text",{"x":74,"y":75,"style":76},"185","54","font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","survives",[72,79,82],{"x":74,"y":80,"style":81},"84","font-size:11.5px;fill:var(--text,#172033);text-anchor:middle","cell values",[72,84,86],{"x":74,"y":85,"style":81},"108","the header row",[72,88,90],{"x":74,"y":89,"style":81},"132","row order",[72,92,94],{"x":74,"y":93,"style":81},"156","text, if read as text",[72,96,99],{"x":74,"y":97,"style":98},"186","font-size:11.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","streamable, universal",[55,101],{"x":102,"y":64,"width":65,"height":66,"rx":67,"fill":103,"stroke":104,"style":70},"390","#fce9e9","var(--danger,#dc2626)",[72,106,109],{"x":107,"y":75,"style":108},"555","font-size:13px;font-weight:700;fill:var(--danger,#dc2626);text-anchor:middle","lost or changed",[72,111,112],{"x":107,"y":80,"style":81},"formulas → cached values",[72,114,115],{"x":107,"y":85,"style":81},"fills, fonts, widths",[72,117,118],{"x":107,"y":89,"style":81},"extra sheets, charts, images",[72,120,121],{"x":107,"y":93,"style":81},"declared types",[72,123,125],{"x":107,"y":97,"style":124},"font-size:11.5px;font-weight:700;fill:var(--danger,#dc2626);text-anchor:middle","one sheet per file",[127,128,130],"h2",{"id":129},"prerequisites","Prerequisites",[132,133,138],"pre",{"className":134,"code":135,"language":136,"meta":137,"style":137},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[14,139,140],{"__ignoreMap":137},[141,142,145,149,153,156],"span",{"class":143,"line":144},"line",1,[141,146,148],{"class":147},"sMTad","pip",[141,150,152],{"class":151},"srMev"," install",[141,154,155],{"class":151}," pandas",[141,157,158],{"class":151}," openpyxl\n",[127,160,162],{"id":161},"the-one-liner-and-why-it-is-not-enough","The one-liner, and why it is not enough",[132,164,168],{"className":165,"code":166,"language":167,"meta":137,"style":137},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\npd.read_excel(\"orders.xlsx\", sheet_name=\"Orders\").to_csv(\"orders.csv\", index=False)\n","python",[14,169,170,186,193],{"__ignoreMap":137},[141,171,172,176,180,183],{"class":143,"line":144},[141,173,175],{"class":174},"s-kum","import",[141,177,179],{"class":178},"skGVy"," pandas ",[141,181,182],{"class":174},"as",[141,184,185],{"class":178}," pd\n",[141,187,189],{"class":143,"line":188},2,[141,190,192],{"emptyLinePlaceholder":191},true,"\n",[141,194,196,199,202,204,208,211,214,217,220,222,225,227,231],{"class":143,"line":195},3,[141,197,198],{"class":178},"pd.read_excel(",[141,200,201],{"class":151},"\"orders.xlsx\"",[141,203,21],{"class":178},[141,205,207],{"class":206},"sa561","sheet_name",[141,209,210],{"class":174},"=",[141,212,213],{"class":151},"\"Orders\"",[141,215,216],{"class":178},").to_csv(",[141,218,219],{"class":151},"\"orders.csv\"",[141,221,21],{"class":178},[141,223,224],{"class":206},"index",[141,226,210],{"class":174},[141,228,230],{"class":229},"sP0c6","False",[141,232,233],{"class":178},")\n",[10,235,236,237,240,241,244],{},"That is a valid conversion and it is fine for a sheet of plain numbers. On real data it silently changes three things: identifier columns lose leading zeros, dates gain a midnight timestamp, and any column containing one stray text value arrives as ",[14,238,239],{},"object"," and is written with ",[14,242,243],{},"nan"," where blanks were.",[127,246,248],{"id":247},"a-conversion-that-preserves-the-data","A conversion that preserves the data",[10,250,251],{},"Read raw, decide each column's treatment explicitly, then write:",[132,253,255],{"className":165,"code":254,"language":167,"meta":137,"style":137},"import pandas as pd\n\nTEXT_COLUMNS = [\"Code\", \"SKU\"]          # identifiers must stay text\nDATE_COLUMNS = [\"Order_Date\"]\n\ndf = pd.read_excel(\"orders.xlsx\", sheet_name=\"Orders\", dtype=object)\n\nfor column in TEXT_COLUMNS:\n    if column in df.columns:\n        df[column] = df[column].astype(\"string\")\n\nfor column in DATE_COLUMNS:\n    if column in df.columns:\n        df[column] = pd.to_datetime(df[column], errors=\"coerce\").dt.strftime(\"%Y-%m-%d\")\n\ndf.to_csv(\n    \"orders.csv\",\n    index=False,\n    encoding=\"utf-8\",\n    na_rep=\"\",                 # blanks stay blank, not \"nan\"\n    lineterminator=\"\\n\",\n)\nprint(open(\"orders.csv\").readline().strip())\n",[14,256,257,267,271,297,313,318,350,355,373,386,402,407,421,432,465,470,476,485,497,510,527,544,549],{"__ignoreMap":137},[141,258,259,261,263,265],{"class":143,"line":144},[141,260,175],{"class":174},[141,262,179],{"class":178},[141,264,182],{"class":174},[141,266,185],{"class":178},[141,268,269],{"class":143,"line":188},[141,270,192],{"emptyLinePlaceholder":191},[141,272,273,276,279,282,285,287,290,293],{"class":143,"line":195},[141,274,275],{"class":229},"TEXT_COLUMNS",[141,277,278],{"class":174}," =",[141,280,281],{"class":178}," [",[141,283,284],{"class":151},"\"Code\"",[141,286,21],{"class":178},[141,288,289],{"class":151},"\"SKU\"",[141,291,292],{"class":178},"]          ",[141,294,296],{"class":295},"s-wDw","# identifiers must stay text\n",[141,298,300,303,305,307,310],{"class":143,"line":299},4,[141,301,302],{"class":229},"DATE_COLUMNS",[141,304,278],{"class":174},[141,306,281],{"class":178},[141,308,309],{"class":151},"\"Order_Date\"",[141,311,312],{"class":178},"]\n",[141,314,316],{"class":143,"line":315},5,[141,317,192],{"emptyLinePlaceholder":191},[141,319,321,324,326,329,331,333,335,337,339,341,344,346,348],{"class":143,"line":320},6,[141,322,323],{"class":178},"df ",[141,325,210],{"class":174},[141,327,328],{"class":178}," pd.read_excel(",[141,330,201],{"class":151},[141,332,21],{"class":178},[141,334,207],{"class":206},[141,336,210],{"class":174},[141,338,213],{"class":151},[141,340,21],{"class":178},[141,342,343],{"class":206},"dtype",[141,345,210],{"class":174},[141,347,239],{"class":229},[141,349,233],{"class":178},[141,351,353],{"class":143,"line":352},7,[141,354,192],{"emptyLinePlaceholder":191},[141,356,358,361,364,367,370],{"class":143,"line":357},8,[141,359,360],{"class":174},"for",[141,362,363],{"class":178}," column ",[141,365,366],{"class":174},"in",[141,368,369],{"class":229}," TEXT_COLUMNS",[141,371,372],{"class":178},":\n",[141,374,376,379,381,383],{"class":143,"line":375},9,[141,377,378],{"class":174},"    if",[141,380,363],{"class":178},[141,382,366],{"class":174},[141,384,385],{"class":178}," df.columns:\n",[141,387,389,392,394,397,400],{"class":143,"line":388},10,[141,390,391],{"class":178},"        df[column] ",[141,393,210],{"class":174},[141,395,396],{"class":178}," df[column].astype(",[141,398,399],{"class":151},"\"string\"",[141,401,233],{"class":178},[141,403,405],{"class":143,"line":404},11,[141,406,192],{"emptyLinePlaceholder":191},[141,408,410,412,414,416,419],{"class":143,"line":409},12,[141,411,360],{"class":174},[141,413,363],{"class":178},[141,415,366],{"class":174},[141,417,418],{"class":229}," DATE_COLUMNS",[141,420,372],{"class":178},[141,422,424,426,428,430],{"class":143,"line":423},13,[141,425,378],{"class":174},[141,427,363],{"class":178},[141,429,366],{"class":174},[141,431,385],{"class":178},[141,433,435,437,439,442,445,447,450,453,456,460,463],{"class":143,"line":434},14,[141,436,391],{"class":178},[141,438,210],{"class":174},[141,440,441],{"class":178}," pd.to_datetime(df[column], ",[141,443,444],{"class":206},"errors",[141,446,210],{"class":174},[141,448,449],{"class":151},"\"coerce\"",[141,451,452],{"class":178},").dt.strftime(",[141,454,455],{"class":151},"\"%Y-%m-",[141,457,459],{"class":458},"sSjpA","%d",[141,461,462],{"class":151},"\"",[141,464,233],{"class":178},[141,466,468],{"class":143,"line":467},15,[141,469,192],{"emptyLinePlaceholder":191},[141,471,473],{"class":143,"line":472},16,[141,474,475],{"class":178},"df.to_csv(\n",[141,477,479,482],{"class":143,"line":478},17,[141,480,481],{"class":151},"    \"orders.csv\"",[141,483,484],{"class":178},",\n",[141,486,488,491,493,495],{"class":143,"line":487},18,[141,489,490],{"class":206},"    index",[141,492,210],{"class":174},[141,494,230],{"class":229},[141,496,484],{"class":178},[141,498,500,503,505,508],{"class":143,"line":499},19,[141,501,502],{"class":206},"    encoding",[141,504,210],{"class":174},[141,506,507],{"class":151},"\"utf-8\"",[141,509,484],{"class":178},[141,511,513,516,518,521,524],{"class":143,"line":512},20,[141,514,515],{"class":206},"    na_rep",[141,517,210],{"class":174},[141,519,520],{"class":151},"\"\"",[141,522,523],{"class":178},",                 ",[141,525,526],{"class":295},"# blanks stay blank, not \"nan\"\n",[141,528,530,533,535,537,540,542],{"class":143,"line":529},21,[141,531,532],{"class":206},"    lineterminator",[141,534,210],{"class":174},[141,536,462],{"class":151},[141,538,539],{"class":458},"\\n",[141,541,462],{"class":151},[141,543,484],{"class":178},[141,545,547],{"class":143,"line":546},22,[141,548,233],{"class":178},[141,550,552,555,558,561,563,565],{"class":143,"line":551},23,[141,553,554],{"class":229},"print",[141,556,557],{"class":178},"(",[141,559,560],{"class":229},"open",[141,562,557],{"class":178},[141,564,219],{"class":151},[141,566,567],{"class":178},").readline().strip())\n",[10,569,570,573,574,577,578,582,583,586,587,590,591,594],{},[14,571,572],{},"dtype=object"," stops the inference that damages identifiers, and ",[14,575,576],{},"strftime"," writes a date rather than a timestamp — CSV has no date type, so the string you write ",[579,580,581],"em",{},"is"," the format. ",[14,584,585],{},"na_rep=\"\""," matters more than it looks: the default writes an empty field anyway for ",[14,588,589],{},"NaN",", but an object column containing the literal string ",[14,592,593],{},"\"nan\""," will otherwise travel through unchanged, and downstream readers treat the two differently.",[127,596,598],{"id":597},"every-sheet-to-its-own-file","Every sheet to its own file",[10,600,601],{},"A workbook is many tables; CSV holds one. Name the outputs after the sheets and sanitise the filenames:",[132,603,605],{"className":165,"code":604,"language":167,"meta":137,"style":137},"import re\nfrom pathlib import Path\n\nimport pandas as pd\n\ndef sheets_to_csv(path, outdir=\"csv\", encoding=\"utf-8\"):\n    Path(outdir).mkdir(parents=True, exist_ok=True)\n    book = pd.ExcelFile(path)\n    written = []\n    for sheet in book.sheet_names:\n        frame = book.parse(sheet, dtype=object)\n        if frame.empty:\n            print(f\"skipping empty sheet {sheet!r}\")\n            continue\n        safe = re.sub(r\"[^A-Za-z0-9._-]+\", \"_\", sheet).strip(\"_\") or \"sheet\"\n        target = Path(outdir) \u002F f\"{safe}.csv\"\n        frame.to_csv(target, index=False, encoding=encoding, na_rep=\"\")\n        written.append(target)\n    return written\n\nfor target in sheets_to_csv(\"workbook.xlsx\"):\n    print(\"wrote\", target)\n",[14,606,607,614,627,631,641,645,672,696,706,716,729,747,755,784,789,840,868,898,903,911,915,932],{"__ignoreMap":137},[141,608,609,611],{"class":143,"line":144},[141,610,175],{"class":174},[141,612,613],{"class":178}," re\n",[141,615,616,619,622,624],{"class":143,"line":188},[141,617,618],{"class":174},"from",[141,620,621],{"class":178}," pathlib ",[141,623,175],{"class":174},[141,625,626],{"class":178}," Path\n",[141,628,629],{"class":143,"line":195},[141,630,192],{"emptyLinePlaceholder":191},[141,632,633,635,637,639],{"class":143,"line":299},[141,634,175],{"class":174},[141,636,179],{"class":178},[141,638,182],{"class":174},[141,640,185],{"class":178},[141,642,643],{"class":143,"line":315},[141,644,192],{"emptyLinePlaceholder":191},[141,646,647,650,654,657,659,662,665,667,669],{"class":143,"line":320},[141,648,649],{"class":174},"def",[141,651,653],{"class":652},"s_Opv"," sheets_to_csv",[141,655,656],{"class":178},"(path, outdir",[141,658,210],{"class":174},[141,660,661],{"class":151},"\"csv\"",[141,663,664],{"class":178},", encoding",[141,666,210],{"class":174},[141,668,507],{"class":151},[141,670,671],{"class":178},"):\n",[141,673,674,677,680,682,685,687,690,692,694],{"class":143,"line":352},[141,675,676],{"class":178},"    Path(outdir).mkdir(",[141,678,679],{"class":206},"parents",[141,681,210],{"class":174},[141,683,684],{"class":229},"True",[141,686,21],{"class":178},[141,688,689],{"class":206},"exist_ok",[141,691,210],{"class":174},[141,693,684],{"class":229},[141,695,233],{"class":178},[141,697,698,701,703],{"class":143,"line":357},[141,699,700],{"class":178},"    book ",[141,702,210],{"class":174},[141,704,705],{"class":178}," pd.ExcelFile(path)\n",[141,707,708,711,713],{"class":143,"line":375},[141,709,710],{"class":178},"    written ",[141,712,210],{"class":174},[141,714,715],{"class":178}," []\n",[141,717,718,721,724,726],{"class":143,"line":388},[141,719,720],{"class":174},"    for",[141,722,723],{"class":178}," sheet ",[141,725,366],{"class":174},[141,727,728],{"class":178}," book.sheet_names:\n",[141,730,731,734,736,739,741,743,745],{"class":143,"line":404},[141,732,733],{"class":178},"        frame ",[141,735,210],{"class":174},[141,737,738],{"class":178}," book.parse(sheet, ",[141,740,343],{"class":206},[141,742,210],{"class":174},[141,744,239],{"class":229},[141,746,233],{"class":178},[141,748,749,752],{"class":143,"line":409},[141,750,751],{"class":174},"        if",[141,753,754],{"class":178}," frame.empty:\n",[141,756,757,760,762,765,768,771,774,777,780,782],{"class":143,"line":423},[141,758,759],{"class":229},"            print",[141,761,557],{"class":178},[141,763,764],{"class":174},"f",[141,766,767],{"class":151},"\"skipping empty sheet ",[141,769,770],{"class":458},"{",[141,772,773],{"class":178},"sheet",[141,775,776],{"class":174},"!r",[141,778,779],{"class":458},"}",[141,781,462],{"class":151},[141,783,233],{"class":178},[141,785,786],{"class":143,"line":434},[141,787,788],{"class":174},"            continue\n",[141,790,791,794,796,799,802,804,807,810,813,816,819,821,823,826,829,831,834,837],{"class":143,"line":467},[141,792,793],{"class":178},"        safe ",[141,795,210],{"class":174},[141,797,798],{"class":178}," re.sub(",[141,800,801],{"class":174},"r",[141,803,462],{"class":151},[141,805,806],{"class":229},"[",[141,808,809],{"class":174},"^",[141,811,812],{"class":458},"A-Za-z0-9._-",[141,814,815],{"class":229},"]",[141,817,818],{"class":174},"+",[141,820,462],{"class":151},[141,822,21],{"class":178},[141,824,825],{"class":151},"\"_\"",[141,827,828],{"class":178},", sheet).strip(",[141,830,825],{"class":151},[141,832,833],{"class":178},") ",[141,835,836],{"class":174},"or",[141,838,839],{"class":151}," \"sheet\"\n",[141,841,842,845,847,850,853,856,858,860,863,865],{"class":143,"line":472},[141,843,844],{"class":178},"        target ",[141,846,210],{"class":174},[141,848,849],{"class":178}," Path(outdir) ",[141,851,852],{"class":174},"\u002F",[141,854,855],{"class":174}," f",[141,857,462],{"class":151},[141,859,770],{"class":458},[141,861,862],{"class":178},"safe",[141,864,779],{"class":458},[141,866,867],{"class":151},".csv\"\n",[141,869,870,873,875,877,879,881,884,886,889,892,894,896],{"class":143,"line":478},[141,871,872],{"class":178},"        frame.to_csv(target, ",[141,874,224],{"class":206},[141,876,210],{"class":174},[141,878,230],{"class":229},[141,880,21],{"class":178},[141,882,883],{"class":206},"encoding",[141,885,210],{"class":174},[141,887,888],{"class":178},"encoding, ",[141,890,891],{"class":206},"na_rep",[141,893,210],{"class":174},[141,895,520],{"class":151},[141,897,233],{"class":178},[141,899,900],{"class":143,"line":487},[141,901,902],{"class":178},"        written.append(target)\n",[141,904,905,908],{"class":143,"line":499},[141,906,907],{"class":174},"    return",[141,909,910],{"class":178}," written\n",[141,912,913],{"class":143,"line":512},[141,914,192],{"emptyLinePlaceholder":191},[141,916,917,919,922,924,927,930],{"class":143,"line":529},[141,918,360],{"class":174},[141,920,921],{"class":178}," target ",[141,923,366],{"class":174},[141,925,926],{"class":178}," sheets_to_csv(",[141,928,929],{"class":151},"\"workbook.xlsx\"",[141,931,671],{"class":178},[141,933,934,937,939,942],{"class":143,"line":546},[141,935,936],{"class":229},"    print",[141,938,557],{"class":178},[141,940,941],{"class":151},"\"wrote\"",[141,943,944],{"class":178},", target)\n",[10,946,947,948,951],{},"Sanitising is not optional on Windows, where a sheet named ",[14,949,950],{},"Q1\u002FQ2"," produces a path that cannot be created. Skipping empty sheets avoids a directory full of files containing nothing but a header — usually the leftovers of a template.",[127,953,955],{"id":954},"encodings-separators-and-excel-round-trips","Encodings, separators and Excel round trips",[10,957,958],{},"CSV has no metadata, so the reader has to guess these, and a mismatch is the classic cause of a file that \"looks corrupted\":",[960,961,962,978],"table",{},[963,964,965],"thead",{},[966,967,968,972,975],"tr",{},[969,970,971],"th",{},"Setting",[969,973,974],{},"Machine-to-machine",[969,976,977],{},"Will be opened in Excel",[979,980,981,998,1017,1031,1045],"tbody",{},[966,982,983,987,992],{},[984,985,986],"td",{},"Encoding",[984,988,989],{},[14,990,991],{},"utf-8",[984,993,994,997],{},[14,995,996],{},"utf-8-sig"," (adds a byte-order mark)",[966,999,1000,1003,1008],{},[984,1001,1002],{},"Separator",[984,1004,1005],{},[14,1006,1007],{},",",[984,1009,1010,1012,1013,1016],{},[14,1011,1007],{}," — or ",[14,1014,1015],{},";"," in comma-decimal locales",[966,1018,1019,1022,1026],{},[984,1020,1021],{},"Line ending",[984,1023,1024],{},[14,1025,539],{},[984,1027,1028],{},[14,1029,1030],{},"\\r\\n",[966,1032,1033,1036,1042],{},[984,1034,1035],{},"Dates",[984,1037,1038,1039],{},"ISO ",[14,1040,1041],{},"YYYY-MM-DD",[984,1043,1044],{},"ISO still, and let Excel parse it",[966,1046,1047,1050,1055],{},[984,1048,1049],{},"Decimals",[984,1051,1052],{},[14,1053,1054],{},".",[984,1056,1057,1059,1060],{},[14,1058,1054],{},", unless the locale demands ",[14,1061,1007],{},[132,1063,1065],{"className":165,"code":1064,"language":167,"meta":137,"style":137},"df.to_csv(\"for_excel.csv\", index=False, encoding=\"utf-8-sig\", sep=\";\", lineterminator=\"\\r\\n\")\n",[14,1066,1067],{"__ignoreMap":137},[141,1068,1069,1072,1075,1077,1079,1081,1083,1085,1087,1089,1092,1094,1097,1099,1102,1104,1107,1109,1111,1113,1115],{"class":143,"line":144},[141,1070,1071],{"class":178},"df.to_csv(",[141,1073,1074],{"class":151},"\"for_excel.csv\"",[141,1076,21],{"class":178},[141,1078,224],{"class":206},[141,1080,210],{"class":174},[141,1082,230],{"class":229},[141,1084,21],{"class":178},[141,1086,883],{"class":206},[141,1088,210],{"class":174},[141,1090,1091],{"class":151},"\"utf-8-sig\"",[141,1093,21],{"class":178},[141,1095,1096],{"class":206},"sep",[141,1098,210],{"class":174},[141,1100,1101],{"class":151},"\";\"",[141,1103,21],{"class":178},[141,1105,1106],{"class":206},"lineterminator",[141,1108,210],{"class":174},[141,1110,462],{"class":151},[141,1112,1030],{"class":458},[141,1114,462],{"class":151},[141,1116,233],{"class":178},[10,1118,1119,1121],{},[14,1120,996],{}," is the single most useful trick here: without the byte-order mark, Excel on Windows opens a UTF-8 file as if it were the legacy code page and every accented character breaks. The cost is three extra bytes that most other readers ignore.",[127,1123,1125],{"id":1124},"streaming-a-file-too-large-to-load","Streaming a file too large to load",[10,1127,1128,1129,1132],{},"When the sheet does not fit in memory, convert without a DataFrame. openpyxl streams rows in, the ",[14,1130,1131],{},"csv"," module streams them out, and peak memory is one row:",[132,1134,1136],{"className":165,"code":1135,"language":167,"meta":137,"style":137},"import csv\n\nfrom openpyxl import load_workbook\n\ndef stream_to_csv(xlsx_path, sheet, csv_path, encoding=\"utf-8\"):\n    wb = load_workbook(xlsx_path, read_only=True, data_only=True)\n    rows_written = 0\n    try:\n        ws = wb[sheet]\n        with open(csv_path, \"w\", newline=\"\", encoding=encoding) as handle:\n            writer = csv.writer(handle)\n            for row in ws.iter_rows(values_only=True):\n                if all(v is None for v in row):\n                    continue\n                writer.writerow([\"\" if v is None else v for v in row])\n                rows_written += 1\n    finally:\n        wb.close()\n    return rows_written\n\nprint(stream_to_csv(\"large.xlsx\", \"Orders\", \"large.csv\"), \"row(s) written\")\n",[14,1137,1138,1145,1149,1161,1165,1181,1209,1219,1226,1236,1273,1283,1305,1332,1337,1367,1378,1385,1390,1397,1401],{"__ignoreMap":137},[141,1139,1140,1142],{"class":143,"line":144},[141,1141,175],{"class":174},[141,1143,1144],{"class":178}," csv\n",[141,1146,1147],{"class":143,"line":188},[141,1148,192],{"emptyLinePlaceholder":191},[141,1150,1151,1153,1156,1158],{"class":143,"line":195},[141,1152,618],{"class":174},[141,1154,1155],{"class":178}," openpyxl ",[141,1157,175],{"class":174},[141,1159,1160],{"class":178}," load_workbook\n",[141,1162,1163],{"class":143,"line":299},[141,1164,192],{"emptyLinePlaceholder":191},[141,1166,1167,1169,1172,1175,1177,1179],{"class":143,"line":315},[141,1168,649],{"class":174},[141,1170,1171],{"class":652}," stream_to_csv",[141,1173,1174],{"class":178},"(xlsx_path, sheet, csv_path, encoding",[141,1176,210],{"class":174},[141,1178,507],{"class":151},[141,1180,671],{"class":178},[141,1182,1183,1186,1188,1191,1194,1196,1198,1200,1203,1205,1207],{"class":143,"line":320},[141,1184,1185],{"class":178},"    wb ",[141,1187,210],{"class":174},[141,1189,1190],{"class":178}," load_workbook(xlsx_path, ",[141,1192,1193],{"class":206},"read_only",[141,1195,210],{"class":174},[141,1197,684],{"class":229},[141,1199,21],{"class":178},[141,1201,1202],{"class":206},"data_only",[141,1204,210],{"class":174},[141,1206,684],{"class":229},[141,1208,233],{"class":178},[141,1210,1211,1214,1216],{"class":143,"line":352},[141,1212,1213],{"class":178},"    rows_written ",[141,1215,210],{"class":174},[141,1217,1218],{"class":229}," 0\n",[141,1220,1221,1224],{"class":143,"line":357},[141,1222,1223],{"class":174},"    try",[141,1225,372],{"class":178},[141,1227,1228,1231,1233],{"class":143,"line":375},[141,1229,1230],{"class":178},"        ws ",[141,1232,210],{"class":174},[141,1234,1235],{"class":178}," wb[sheet]\n",[141,1237,1238,1241,1244,1247,1250,1252,1255,1257,1259,1261,1263,1265,1268,1270],{"class":143,"line":388},[141,1239,1240],{"class":174},"        with",[141,1242,1243],{"class":229}," open",[141,1245,1246],{"class":178},"(csv_path, ",[141,1248,1249],{"class":151},"\"w\"",[141,1251,21],{"class":178},[141,1253,1254],{"class":206},"newline",[141,1256,210],{"class":174},[141,1258,520],{"class":151},[141,1260,21],{"class":178},[141,1262,883],{"class":206},[141,1264,210],{"class":174},[141,1266,1267],{"class":178},"encoding) ",[141,1269,182],{"class":174},[141,1271,1272],{"class":178}," handle:\n",[141,1274,1275,1278,1280],{"class":143,"line":404},[141,1276,1277],{"class":178},"            writer ",[141,1279,210],{"class":174},[141,1281,1282],{"class":178}," csv.writer(handle)\n",[141,1284,1285,1288,1291,1293,1296,1299,1301,1303],{"class":143,"line":409},[141,1286,1287],{"class":174},"            for",[141,1289,1290],{"class":178}," row ",[141,1292,366],{"class":174},[141,1294,1295],{"class":178}," ws.iter_rows(",[141,1297,1298],{"class":206},"values_only",[141,1300,210],{"class":174},[141,1302,684],{"class":229},[141,1304,671],{"class":178},[141,1306,1307,1310,1313,1316,1318,1321,1324,1327,1329],{"class":143,"line":423},[141,1308,1309],{"class":174},"                if",[141,1311,1312],{"class":229}," all",[141,1314,1315],{"class":178},"(v ",[141,1317,581],{"class":174},[141,1319,1320],{"class":229}," None",[141,1322,1323],{"class":174}," for",[141,1325,1326],{"class":178}," v ",[141,1328,366],{"class":174},[141,1330,1331],{"class":178}," row):\n",[141,1333,1334],{"class":143,"line":434},[141,1335,1336],{"class":174},"                    continue\n",[141,1338,1339,1342,1344,1347,1349,1351,1353,1356,1358,1360,1362,1364],{"class":143,"line":467},[141,1340,1341],{"class":178},"                writer.writerow([",[141,1343,520],{"class":151},[141,1345,1346],{"class":174}," if",[141,1348,1326],{"class":178},[141,1350,581],{"class":174},[141,1352,1320],{"class":229},[141,1354,1355],{"class":174}," else",[141,1357,1326],{"class":178},[141,1359,360],{"class":174},[141,1361,1326],{"class":178},[141,1363,366],{"class":174},[141,1365,1366],{"class":178}," row])\n",[141,1368,1369,1372,1375],{"class":143,"line":472},[141,1370,1371],{"class":178},"                rows_written ",[141,1373,1374],{"class":174},"+=",[141,1376,1377],{"class":229}," 1\n",[141,1379,1380,1383],{"class":143,"line":478},[141,1381,1382],{"class":174},"    finally",[141,1384,372],{"class":178},[141,1386,1387],{"class":143,"line":487},[141,1388,1389],{"class":178},"        wb.close()\n",[141,1391,1392,1394],{"class":143,"line":499},[141,1393,907],{"class":174},[141,1395,1396],{"class":178}," rows_written\n",[141,1398,1399],{"class":143,"line":512},[141,1400,192],{"emptyLinePlaceholder":191},[141,1402,1403,1405,1408,1411,1413,1415,1417,1420,1423,1426],{"class":143,"line":529},[141,1404,554],{"class":229},[141,1406,1407],{"class":178},"(stream_to_csv(",[141,1409,1410],{"class":151},"\"large.xlsx\"",[141,1412,21],{"class":178},[141,1414,213],{"class":151},[141,1416,21],{"class":178},[141,1418,1419],{"class":151},"\"large.csv\"",[141,1421,1422],{"class":178},"), ",[141,1424,1425],{"class":151},"\"row(s) written\"",[141,1427,233],{"class":178},[10,1429,1430,1433,1434,1436,1437,1439,1440,1443,1444,1447],{},[14,1431,1432],{},"newline=\"\""," on the ",[14,1435,560],{}," call is required, not stylistic: without it the ",[14,1438,1131],{}," module's line endings are translated a second time by the text layer, and every row in the output is followed by a blank one. Dates come through as ",[14,1441,1442],{},"datetime"," objects and are written in ISO form by ",[14,1445,1446],{},"str()",", which is the behaviour you want.",[36,1449,45,1454,45,1457,45,1460,45,1463,45,1469,45,1475,45,1479,45,1484,45,1489,45,1494,45,1497,45,1501,45,1504,45,1507,45,1510],{"viewBox":1450,"role":39,"ariaLabelledBy":1451,"xmlns":43,"style":44},"0 0 740 214",[1452,1453],"csv-route-t","csv-route-d",[47,1455,1456],{"id":1452},"Two conversion routes and when to use each",[51,1458,1459],{"id":1453},"The pandas route loads the sheet into a DataFrame, which allows type control and transformation but needs the memory. The streaming route passes rows from openpyxl straight to the csv writer, which handles any size but offers no transformation.",[55,1461],{"x":57,"y":57,"width":58,"height":1462,"fill":60},"214",[55,1464],{"x":1465,"y":1466,"width":65,"height":93,"rx":67,"fill":1467,"stroke":1468,"style":70},"24","30","#f0f4ff","var(--brand,#5b5cf0)",[72,1470,1474],{"x":1471,"y":1472,"style":1473},"189","58","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","via pandas",[72,1476,1478],{"x":1471,"y":1477,"style":81},"86","read_excel → to_csv",[72,1480,1483],{"x":1471,"y":1481,"style":1482},"110","font-size:11.5px;fill:var(--teal,#0f9488);text-anchor:middle","type control, filtering, renaming",[72,1485,1488],{"x":1471,"y":1486,"style":1487},"134","font-size:11.5px;fill:var(--danger,#dc2626);text-anchor:middle","needs the sheet in memory",[72,1490,1493],{"x":1471,"y":1491,"style":1492},"164","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","use for normal files",[55,1495],{"x":1496,"y":1466,"width":65,"height":93,"rx":67,"fill":68,"stroke":69,"style":70},"386",[72,1498,1500],{"x":1499,"y":1472,"style":76},"551","streaming",[72,1502,1503],{"x":1499,"y":1477,"style":81},"iter_rows → csv.writer",[72,1505,1506],{"x":1499,"y":1481,"style":1482},"any file size, flat memory",[72,1508,1509],{"x":1499,"y":1486,"style":1487},"no dtype handling for you",[72,1511,1512],{"x":1499,"y":1491,"style":1492},"use for huge files",[127,1514,1516],{"id":1515},"converting-only-part-of-a-workbook","Converting only part of a workbook",[10,1518,1519],{},"Most conversions do not need the whole sheet. Narrowing at the read is cheaper than filtering afterwards, and it keeps the output honest about what it contains:",[132,1521,1523],{"className":165,"code":1522,"language":167,"meta":137,"style":137},"import pandas as pd\n\ndf = pd.read_excel(\n    \"orders.xlsx\",\n    sheet_name=\"Orders\",\n    usecols=[\"Order_Date\", \"Region\", \"Quantity\", \"Unit_Price\"],\n    dtype=object,\n)\ndf[\"Order_Date\"] = pd.to_datetime(df[\"Order_Date\"], errors=\"coerce\")\nrecent = df[df[\"Order_Date\"] >= pd.Timestamp(\"2026-01-01\")].copy()\nrecent[\"Order_Date\"] = recent[\"Order_Date\"].dt.strftime(\"%Y-%m-%d\")\n\nrecent.to_csv(\"orders_2026.csv\", index=False, na_rep=\"\")\nprint(f\"{len(recent):,} of {len(df):,} row(s) exported\")\n",[14,1524,1525,1535,1539,1548,1555,1566,1595,1606,1610,1638,1664,1691,1695,1721],{"__ignoreMap":137},[141,1526,1527,1529,1531,1533],{"class":143,"line":144},[141,1528,175],{"class":174},[141,1530,179],{"class":178},[141,1532,182],{"class":174},[141,1534,185],{"class":178},[141,1536,1537],{"class":143,"line":188},[141,1538,192],{"emptyLinePlaceholder":191},[141,1540,1541,1543,1545],{"class":143,"line":195},[141,1542,323],{"class":178},[141,1544,210],{"class":174},[141,1546,1547],{"class":178}," pd.read_excel(\n",[141,1549,1550,1553],{"class":143,"line":299},[141,1551,1552],{"class":151},"    \"orders.xlsx\"",[141,1554,484],{"class":178},[141,1556,1557,1560,1562,1564],{"class":143,"line":315},[141,1558,1559],{"class":206},"    sheet_name",[141,1561,210],{"class":174},[141,1563,213],{"class":151},[141,1565,484],{"class":178},[141,1567,1568,1571,1573,1575,1577,1579,1582,1584,1587,1589,1592],{"class":143,"line":320},[141,1569,1570],{"class":206},"    usecols",[141,1572,210],{"class":174},[141,1574,806],{"class":178},[141,1576,309],{"class":151},[141,1578,21],{"class":178},[141,1580,1581],{"class":151},"\"Region\"",[141,1583,21],{"class":178},[141,1585,1586],{"class":151},"\"Quantity\"",[141,1588,21],{"class":178},[141,1590,1591],{"class":151},"\"Unit_Price\"",[141,1593,1594],{"class":178},"],\n",[141,1596,1597,1600,1602,1604],{"class":143,"line":352},[141,1598,1599],{"class":206},"    dtype",[141,1601,210],{"class":174},[141,1603,239],{"class":229},[141,1605,484],{"class":178},[141,1607,1608],{"class":143,"line":357},[141,1609,233],{"class":178},[141,1611,1612,1615,1617,1620,1622,1625,1627,1630,1632,1634,1636],{"class":143,"line":375},[141,1613,1614],{"class":178},"df[",[141,1616,309],{"class":151},[141,1618,1619],{"class":178},"] ",[141,1621,210],{"class":174},[141,1623,1624],{"class":178}," pd.to_datetime(df[",[141,1626,309],{"class":151},[141,1628,1629],{"class":178},"], ",[141,1631,444],{"class":206},[141,1633,210],{"class":174},[141,1635,449],{"class":151},[141,1637,233],{"class":178},[141,1639,1640,1643,1645,1648,1650,1652,1655,1658,1661],{"class":143,"line":388},[141,1641,1642],{"class":178},"recent ",[141,1644,210],{"class":174},[141,1646,1647],{"class":178}," df[df[",[141,1649,309],{"class":151},[141,1651,1619],{"class":178},[141,1653,1654],{"class":174},">=",[141,1656,1657],{"class":178}," pd.Timestamp(",[141,1659,1660],{"class":151},"\"2026-01-01\"",[141,1662,1663],{"class":178},")].copy()\n",[141,1665,1666,1669,1671,1673,1675,1678,1680,1683,1685,1687,1689],{"class":143,"line":404},[141,1667,1668],{"class":178},"recent[",[141,1670,309],{"class":151},[141,1672,1619],{"class":178},[141,1674,210],{"class":174},[141,1676,1677],{"class":178}," recent[",[141,1679,309],{"class":151},[141,1681,1682],{"class":178},"].dt.strftime(",[141,1684,455],{"class":151},[141,1686,459],{"class":458},[141,1688,462],{"class":151},[141,1690,233],{"class":178},[141,1692,1693],{"class":143,"line":409},[141,1694,192],{"emptyLinePlaceholder":191},[141,1696,1697,1700,1703,1705,1707,1709,1711,1713,1715,1717,1719],{"class":143,"line":423},[141,1698,1699],{"class":178},"recent.to_csv(",[141,1701,1702],{"class":151},"\"orders_2026.csv\"",[141,1704,21],{"class":178},[141,1706,224],{"class":206},[141,1708,210],{"class":174},[141,1710,230],{"class":229},[141,1712,21],{"class":178},[141,1714,891],{"class":206},[141,1716,210],{"class":174},[141,1718,520],{"class":151},[141,1720,233],{"class":178},[141,1722,1723,1725,1727,1729,1731,1733,1736,1739,1742,1744,1747,1749,1751,1754,1756,1758,1761],{"class":143,"line":434},[141,1724,554],{"class":229},[141,1726,557],{"class":178},[141,1728,764],{"class":174},[141,1730,462],{"class":151},[141,1732,770],{"class":458},[141,1734,1735],{"class":229},"len",[141,1737,1738],{"class":178},"(recent)",[141,1740,1741],{"class":174},":,",[141,1743,779],{"class":458},[141,1745,1746],{"class":151}," of ",[141,1748,770],{"class":458},[141,1750,1735],{"class":229},[141,1752,1753],{"class":178},"(df)",[141,1755,1741],{"class":174},[141,1757,779],{"class":458},[141,1759,1760],{"class":151}," row(s) exported\"",[141,1762,233],{"class":178},[10,1764,1765,1766,1769,1770,1773],{},"Name the output for what it holds — ",[14,1767,1768],{},"orders_2026.csv"," rather than ",[14,1771,1772],{},"orders.csv"," — because a filtered extract that looks like a full export is the kind of thing someone reconciles against a total six months later and cannot explain.",[127,1775,1777],{"id":1776},"verify-the-conversion","Verify the conversion",[10,1779,1780],{},"A conversion nobody checked is a conversion that silently dropped a column. Compare shapes and spot-check the fragile columns:",[132,1782,1784],{"className":165,"code":1783,"language":167,"meta":137,"style":137},"import pandas as pd\n\nsource = pd.read_excel(\"orders.xlsx\", sheet_name=\"Orders\", dtype=object)\nresult = pd.read_csv(\"orders.csv\", dtype=object, keep_default_na=False)\n\nassert list(source.columns) == list(result.columns), \"column mismatch\"\nassert len(source) == len(result), f\"row count {len(source)} -> {len(result)}\"\n\nfor column in [\"Code\", \"SKU\"]:\n    if column in source.columns:\n        before = source[column].astype(str).str.strip().tolist()\n        after = result[column].astype(str).str.strip().tolist()\n        assert before == after, f\"{column} changed during conversion\"\n\nprint(\"conversion verified\")\n",[14,1785,1786,1796,1800,1829,1860,1864,1886,1932,1936,1955,1966,1982,1996,2023,2027],{"__ignoreMap":137},[141,1787,1788,1790,1792,1794],{"class":143,"line":144},[141,1789,175],{"class":174},[141,1791,179],{"class":178},[141,1793,182],{"class":174},[141,1795,185],{"class":178},[141,1797,1798],{"class":143,"line":188},[141,1799,192],{"emptyLinePlaceholder":191},[141,1801,1802,1805,1807,1809,1811,1813,1815,1817,1819,1821,1823,1825,1827],{"class":143,"line":195},[141,1803,1804],{"class":178},"source ",[141,1806,210],{"class":174},[141,1808,328],{"class":178},[141,1810,201],{"class":151},[141,1812,21],{"class":178},[141,1814,207],{"class":206},[141,1816,210],{"class":174},[141,1818,213],{"class":151},[141,1820,21],{"class":178},[141,1822,343],{"class":206},[141,1824,210],{"class":174},[141,1826,239],{"class":229},[141,1828,233],{"class":178},[141,1830,1831,1834,1836,1839,1841,1843,1845,1847,1849,1851,1854,1856,1858],{"class":143,"line":299},[141,1832,1833],{"class":178},"result ",[141,1835,210],{"class":174},[141,1837,1838],{"class":178}," pd.read_csv(",[141,1840,219],{"class":151},[141,1842,21],{"class":178},[141,1844,343],{"class":206},[141,1846,210],{"class":174},[141,1848,239],{"class":229},[141,1850,21],{"class":178},[141,1852,1853],{"class":206},"keep_default_na",[141,1855,210],{"class":174},[141,1857,230],{"class":229},[141,1859,233],{"class":178},[141,1861,1862],{"class":143,"line":315},[141,1863,192],{"emptyLinePlaceholder":191},[141,1865,1866,1869,1872,1875,1878,1880,1883],{"class":143,"line":320},[141,1867,1868],{"class":174},"assert",[141,1870,1871],{"class":229}," list",[141,1873,1874],{"class":178},"(source.columns) ",[141,1876,1877],{"class":174},"==",[141,1879,1871],{"class":229},[141,1881,1882],{"class":178},"(result.columns), ",[141,1884,1885],{"class":151},"\"column mismatch\"\n",[141,1887,1888,1890,1893,1896,1898,1900,1903,1905,1908,1910,1912,1915,1917,1920,1922,1924,1927,1929],{"class":143,"line":352},[141,1889,1868],{"class":174},[141,1891,1892],{"class":229}," len",[141,1894,1895],{"class":178},"(source) ",[141,1897,1877],{"class":174},[141,1899,1892],{"class":229},[141,1901,1902],{"class":178},"(result), ",[141,1904,764],{"class":174},[141,1906,1907],{"class":151},"\"row count ",[141,1909,770],{"class":458},[141,1911,1735],{"class":229},[141,1913,1914],{"class":178},"(source)",[141,1916,779],{"class":458},[141,1918,1919],{"class":151}," -> ",[141,1921,770],{"class":458},[141,1923,1735],{"class":229},[141,1925,1926],{"class":178},"(result)",[141,1928,779],{"class":458},[141,1930,1931],{"class":151},"\"\n",[141,1933,1934],{"class":143,"line":357},[141,1935,192],{"emptyLinePlaceholder":191},[141,1937,1938,1940,1942,1944,1946,1948,1950,1952],{"class":143,"line":375},[141,1939,360],{"class":174},[141,1941,363],{"class":178},[141,1943,366],{"class":174},[141,1945,281],{"class":178},[141,1947,284],{"class":151},[141,1949,21],{"class":178},[141,1951,289],{"class":151},[141,1953,1954],{"class":178},"]:\n",[141,1956,1957,1959,1961,1963],{"class":143,"line":388},[141,1958,378],{"class":174},[141,1960,363],{"class":178},[141,1962,366],{"class":174},[141,1964,1965],{"class":178}," source.columns:\n",[141,1967,1968,1971,1973,1976,1979],{"class":143,"line":404},[141,1969,1970],{"class":178},"        before ",[141,1972,210],{"class":174},[141,1974,1975],{"class":178}," source[column].astype(",[141,1977,1978],{"class":229},"str",[141,1980,1981],{"class":178},").str.strip().tolist()\n",[141,1983,1984,1987,1989,1992,1994],{"class":143,"line":409},[141,1985,1986],{"class":178},"        after ",[141,1988,210],{"class":174},[141,1990,1991],{"class":178}," result[column].astype(",[141,1993,1978],{"class":229},[141,1995,1981],{"class":178},[141,1997,1998,2001,2004,2006,2009,2011,2013,2015,2018,2020],{"class":143,"line":423},[141,1999,2000],{"class":174},"        assert",[141,2002,2003],{"class":178}," before ",[141,2005,1877],{"class":174},[141,2007,2008],{"class":178}," after, ",[141,2010,764],{"class":174},[141,2012,462],{"class":151},[141,2014,770],{"class":458},[141,2016,2017],{"class":178},"column",[141,2019,779],{"class":458},[141,2021,2022],{"class":151}," changed during conversion\"\n",[141,2024,2025],{"class":143,"line":434},[141,2026,192],{"emptyLinePlaceholder":191},[141,2028,2029,2031,2033,2036],{"class":143,"line":467},[141,2030,554],{"class":229},[141,2032,557],{"class":178},[141,2034,2035],{"class":151},"\"conversion verified\"",[141,2037,233],{"class":178},[10,2039,2040,2043,2044,2046],{},[14,2041,2042],{},"keep_default_na=False"," on the read-back is what makes the comparison honest — otherwise pandas turns empty fields into ",[14,2045,589],{}," and the assertion fails for a reason that has nothing to do with the conversion.",[127,2048,2050],{"id":2049},"csv-or-something-better","CSV, or something better?",[10,2052,2053],{},"CSV is universal and lossy. Where both ends of the pipe are yours, a typed format is a straight upgrade:",[36,2055,45,2060,45,2063,45,2066,45,2069,45,2073,45,2076,45,2080,45,2083,45,2086,45,2089,45,2091,45,2094,45,2097,45,2100,45,2103],{"viewBox":2056,"role":39,"ariaLabelledBy":2057,"xmlns":43,"style":44},"0 0 740 216",[2058,2059],"csv-alt-t","csv-alt-d",[47,2061,2062],{"id":2058},"CSV compared with Parquet as a handoff format",[51,2064,2065],{"id":2059},"CSV is readable by anything and by people, but stores no types and compresses poorly. Parquet keeps dtypes, compresses well and allows reading single columns, but needs a library to read and cannot be opened by hand.",[55,2067],{"x":57,"y":57,"width":58,"height":2068,"fill":60},"216",[55,2070],{"x":1465,"y":1466,"width":65,"height":93,"rx":67,"fill":2071,"stroke":2072},"#ebebfd","var(--line,#cdd5e6)",[72,2074,2075],{"x":1471,"y":1472,"style":1473},"CSV",[72,2077,2079],{"x":1471,"y":2078,"style":1482},"88","every tool reads it",[72,2081,2082],{"x":1471,"y":1481,"style":1482},"a human can open it",[72,2084,2085],{"x":1471,"y":89,"style":1487},"no types, no compression",[72,2087,2088],{"x":1471,"y":1491,"style":1492},"use for handoffs you do not control",[55,2090],{"x":1496,"y":1466,"width":65,"height":93,"rx":67,"fill":68,"stroke":69,"style":70},[72,2092,2093],{"x":1499,"y":1472,"style":76},"Parquet",[72,2095,2096],{"x":1499,"y":2078,"style":1482},"dtypes survive the round trip",[72,2098,2099],{"x":1499,"y":1481,"style":1482},"columnar — read three of thirty",[72,2101,2102],{"x":1499,"y":89,"style":1487},"needs a library to read",[72,2104,2105],{"x":1499,"y":1491,"style":1492},"use inside your own pipeline",[10,2107,2108],{},"The practical rule: CSV crosses organisational boundaries, Parquet stays inside them. A daily job that reads the same export ten times should convert once to Parquet and read that, keeping a CSV only for whoever outside the team asked for one.",[127,2110,2112],{"id":2111},"common-pitfalls-and-gotchas","Common pitfalls and gotchas",[960,2114,2115,2128],{},[963,2116,2117],{},[966,2118,2119,2122,2125],{},[969,2120,2121],{},"Symptom",[969,2123,2124],{},"Cause",[969,2126,2127],{},"Fix",[979,2129,2130,2144,2161,2179,2192,2212,2230],{},[966,2131,2132,2135,2138],{},[984,2133,2134],{},"Leading zeros gone",[984,2136,2137],{},"Column inferred as numeric",[984,2139,2140,2141,2143],{},"Read with ",[14,2142,572],{},", cast identifiers to string",[966,2145,2146,2152,2155],{},[984,2147,2148,2149],{},"Dates show ",[14,2150,2151],{},"00:00:00",[984,2153,2154],{},"Written as timestamps",[984,2156,2157,2158],{},"Format with ",[14,2159,2160],{},"strftime(\"%Y-%m-%d\")",[966,2162,2163,2168,2173],{},[984,2164,2165,2167],{},[14,2166,243],{}," text in blank cells",[984,2169,2170,2171],{},"Object column carrying literal ",[14,2172,593],{},[984,2174,2175,2176,2178],{},"Set ",[14,2177,585],{}," and clean the source values",[966,2180,2181,2184,2187],{},[984,2182,2183],{},"Accents broken in Excel",[984,2185,2186],{},"UTF-8 without a byte-order mark",[984,2188,2189,2190],{},"Write ",[14,2191,996],{},[966,2193,2194,2197,2204],{},[984,2195,2196],{},"Blank line between every row",[984,2198,2199,2201,2202],{},[14,2200,560],{}," without ",[14,2203,1432],{},[984,2205,2206,2207,2209,2210],{},"Pass ",[14,2208,1432],{}," to ",[14,2211,560],{},[966,2213,2214,2217,2220],{},[984,2215,2216],{},"Whole column empty",[984,2218,2219],{},"Formulas with no cached results",[984,2221,2222,2223,2226,2227],{},"Read ",[14,2224,2225],{},"data_only=True"," and check for ",[14,2228,2229],{},"None",[966,2231,2232,2235,2241],{},[984,2233,2234],{},"Numbers become text in Excel",[984,2236,2237,2238,2240],{},"Locale expects ",[14,2239,1015],{}," and a decimal comma",[984,2242,2243],{},"Match the reader's locale, or hand over Parquet instead",[127,2245,2247],{"id":2246},"performance-and-scale-notes","Performance and scale notes",[10,2249,2250,2251,2254],{},"The pandas route costs a full parse plus a frame; the streaming route costs a parse and nothing else, and comfortably handles files that cannot be loaded. Either way, CSV output is roughly a third to a half the size of the equivalent ",[14,2252,2253],{},".xlsx"," and reads an order of magnitude faster afterwards, which is the real reason to convert: a pipeline that reads the same export daily should convert once and read CSV — or better, Parquet, which keeps the types CSV throws away — rather than parsing XML every run.",[127,2256,2258],{"id":2257},"convert-once-read-many-times","Convert once, read many times",[10,2260,2261],{},"A pipeline that reads the same workbook several times pays the parsing cost each time. Converting once to CSV or Parquet and pointing every later step at the converted file turns a repeated expense into a single one — and on a large export that change alone often halves the total runtime.",[127,2263,2265],{"id":2264},"conclusion","Conclusion",[10,2267,2268,2269,2271,2272,2274,2275,2278],{},"Converting to CSV is a one-liner that quietly damages identifiers, dates and blanks, and a five-line function that does not. Read with ",[14,2270,572],{},", cast identifiers to text, format dates as ISO strings, set ",[14,2273,585],{},", and choose the encoding for whoever opens the file. For workbooks too large to load, stream rows from openpyxl straight into ",[14,2276,2277],{},"csv.writer",", and verify the result by comparing shapes and the fragile columns.",[127,2280,2282],{"id":2281},"frequently-asked-questions","Frequently asked questions",[10,2284,2285,2289,2290,2293,2294,2296],{},[2286,2287,2288],"strong",{},"Why did my product codes lose their leading zeros?","\npandas inferred the column as numeric on the way in. Read it as a string with ",[14,2291,2292],{},"dtype=str",", or read the whole sheet with ",[14,2295,572],{},", and the text is written out intact.",[10,2298,2299,2302,2304,2305,2307],{},[2286,2300,2301],{},"Which encoding should I write?",[14,2303,991],{}," for anything machine-read. Use ",[14,2306,996],{}," when the file will be opened in Excel on Windows, because the byte-order mark stops Excel misreading accented characters.",[10,2309,2310,2313,2314,2316],{},[2286,2311,2312],{},"How do I convert a file too large to load?","\nStream it — iterate rows with openpyxl in read-only mode and write them with the ",[14,2315,1131],{}," module, so neither the sheet nor the output is ever fully in memory.",[10,2318,2319,2322,2323,2325,2326,2328],{},[2286,2320,2321],{},"What happens to formulas?","\nCSV holds values only. Read with ",[14,2324,2225],{}," to write the cached results, and check for ",[14,2327,2229],{}," values first or the column will be empty.",[127,2330,2332],{"id":2331},"related","Related",[10,2334,2335],{},"Up to the parent guide:",[2337,2338,2339],"ul",{},[2340,2341,2342,2344],"li",{},[30,2343,33],{"href":32}," — why a format change is often the real fix.",[10,2346,2347],{},"Related guides:",[2337,2349,2350,2357,2364,2371],{},[2340,2351,2352,2356],{},[30,2353,2355],{"href":2354},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fspeed-up-openpyxl-with-read-only-mode\u002F","Speed Up openpyxl with read_only Mode"," — the streaming reader behind the large-file converter.",[2340,2358,2359,2363],{},[30,2360,2362],{"href":2361},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fcheck-excel-data-types-with-pandas\u002F","Check Excel Data Types with pandas"," — deciding each column's treatment before writing.",[2340,2365,2366,2370],{},[30,2367,2369],{"href":2368},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fconvert-excel-file-to-pdf-with-python\u002F","Convert an Excel File to PDF with Python"," — the other direction, for human readers.",[2340,2372,2373,2377],{},[30,2374,2376],{"href":2375},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fhow-to-read-excel-with-pandas-step-by-step\u002F","How to Read Excel with pandas, Step by Step"," — the read side in detail.",[2379,2380,2381],"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 .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}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 .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":137,"searchDepth":188,"depth":188,"links":2383},[2384,2385,2386,2387,2388,2389,2390,2391,2392,2393,2394,2395,2396,2397,2398],{"id":129,"depth":188,"text":130},{"id":161,"depth":188,"text":162},{"id":247,"depth":188,"text":248},{"id":597,"depth":188,"text":598},{"id":954,"depth":188,"text":955},{"id":1124,"depth":188,"text":1125},{"id":1515,"depth":188,"text":1516},{"id":1776,"depth":188,"text":1777},{"id":2049,"depth":188,"text":2050},{"id":2111,"depth":188,"text":2112},{"id":2246,"depth":188,"text":2247},{"id":2257,"depth":188,"text":2258},{"id":2264,"depth":188,"text":2265},{"id":2281,"depth":188,"text":2282},{"id":2331,"depth":188,"text":2332},"2026-08-01","Turn .xlsx sheets into CSV without mangling dates, leading zeros or European decimals — one sheet, every sheet, or a streaming conversion for files too large to load.","md",[2403,2405,2407,2409],{"q":2288,"a":2404},"pandas inferred the column as numeric on the way in. Read it as a string with dtype=str, or read the whole sheet with dtype=object, and the text is written out intact.",{"q":2301,"a":2406},"utf-8 for anything machine-read. Use utf-8-sig when the file will be opened in Excel on Windows, because the byte-order mark stops Excel misreading accented characters.",{"q":2312,"a":2408},"Stream it — iterate rows with openpyxl in read-only mode and write them with the csv module, so neither the sheet nor the output is ever fully in memory.",{"q":2321,"a":2410},"CSV holds values only. Read with data_only=True to write the cached results, and check for None values first or the column will be empty.",{"breadcrumb":2412},[2413,2415,2418,2419],{"name":2414,"item":852},"Home",{"name":2416,"item":2417},"Advanced Data Transformation and Cleaning","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":33,"item":32},{"name":5,"item":2420},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fconvert-excel-to-csv-with-python\u002F","\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fconvert-excel-to-csv-with-python",{"title":5,"description":2423},"Convert Excel to CSV in Python: pandas one-liners, every sheet to its own file, dtype and date handling that survives the round trip, encodings, and a streaming converter.","convert-excel-to-csv-with-python","advanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fconvert-excel-to-csv-with-python\u002Findex","how-to","PeG9rYSMCC0AFcFgAq4NWZQgl0pTK9PXrBdrIsLUghA",[2429,2432],{"title":33,"path":2430,"stem":2431,"children":-1},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python","advanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Findex",{"title":2433,"path":2434,"stem":2435,"children":-1},"Process Multiple Excel Files in Parallel with Python","\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fprocess-multiple-excel-files-in-parallel-with-python","advanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fprocess-multiple-excel-files-in-parallel-with-python\u002Findex",1785584462728]