[{"data":1,"prerenderedAt":2111},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fwrite-custom-number-format-codes-in-excel-with-python":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fwrite-custom-number-format-codes-in-excel-with-python":2103},{"id":4,"title":5,"body":6,"dateModified":2075,"datePublished":2075,"description":2076,"extension":2077,"faq":2078,"meta":2087,"navigation":208,"path":2096,"seo":2097,"slug":2099,"stem":2100,"type":2101,"__hash__":2102},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fwrite-custom-number-format-codes-in-excel-with-python\u002Findex.md","Write Custom Number Format Codes in Excel with Python",{"type":7,"value":8,"toc":2059},"minimark",[9,19,136,141,173,177,180,357,372,383,387,480,501,743,754,758,827,838,846,850,853,956,963,967,974,1199,1205,1209,1212,1420,1431,1442,1446,1456,1459,1484,1491,1495,1597,1601,1604,1797,1805,1809,1891,1894,1901,1973,1977,1986,1990,1997,2003,2009,2019,2023,2055],[10,11,12,13,18],"p",{},"A number format decides what a reader sees without touching what the cell contains, which is what\nmakes it the right tool for currency, percentages, thousands and the small conditional touches that\nmake a report legible. The syntax is a compact language with four sections and a handful of\nplaceholders, and once it is legible the whole surface opens up. This guide is part of\n",[14,15,17],"a",{"href":16},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002F","Applying Number and Date Formats in Excel",".",[20,21,29,30,29,34,29,38,29,45,29,54,29,61,29,65,29,69,29,74,29,79,29,83,29,86,29,91,29,96,29,99,29,102,29,105,29,109,29,112,29,115,29,120,29,125,29,127,29,130],"svg",{"viewBox":22,"role":23,"ariaLabelledBy":24,"xmlns":27,"style":28},"0 0 760 256","img",[25,26],"nf-sections-t","nf-sections-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:760px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[31,32,33],"title",{"id":25},"The four sections of a number format code",[35,36,37],"desc",{"id":26},"Semicolons split a format into parts applied to positive values, negative values, zero and text in that order, so a single code can show a red parenthesised negative and a dash for zero.",[39,40],"rect",{"x":41,"y":41,"width":42,"height":43,"fill":44},"0","760","256","#ffffff",[39,46],{"x":47,"y":48,"width":49,"height":50,"rx":51,"fill":52,"stroke":53},"18","30","724","34","8","#e7ebef","var(--line,#cdd5e6)",[55,56,60],"text",{"x":57,"y":58,"style":59},"126.6","52","font-size:12px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","Section",[55,62,64],{"x":63,"y":58,"style":59},"361.9","Applies to",[55,66,68],{"x":67,"y":58,"style":59},"615.3000000000001","Example part",[39,70],{"x":47,"y":71,"width":49,"height":50,"rx":51,"fill":72,"stroke":73},"67","#d9f4f1","var(--teal,#0f9488)",[55,75,78],{"x":57,"y":76,"style":77},"88.0","font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","first",[55,80,82],{"x":63,"y":76,"style":81},"font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","positive numbers",[55,84,85],{"x":67,"y":76,"style":81},"#,##0.00",[39,87],{"x":47,"y":88,"width":49,"height":50,"rx":51,"fill":89,"stroke":90},"107","#fee8f2","var(--accent,#d81b73)",[55,92,95],{"x":57,"y":93,"style":94},"128.0","font-size:12px;font-weight:700;fill:var(--accent,#d81b73);text-anchor:middle","second",[55,97,98],{"x":63,"y":93,"style":81},"negative numbers",[55,100,101],{"x":67,"y":93,"style":81},"[Red]-#,##0.00",[39,103],{"x":47,"y":104,"width":49,"height":50,"rx":51,"fill":52,"stroke":53},"147",[55,106,108],{"x":57,"y":107,"style":59},"168.0","third",[55,110,111],{"x":63,"y":107,"style":81},"zero",[55,113,114],{"x":67,"y":107,"style":81},"\"–\"",[39,116],{"x":47,"y":117,"width":49,"height":50,"rx":51,"fill":118,"stroke":119},"187","#fdefd8","var(--gold,#b4740a)",[55,121,124],{"x":57,"y":122,"style":123},"208.0","font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","fourth",[55,126,55],{"x":63,"y":122,"style":81},[55,128,129],{"x":67,"y":122,"style":81},"@",[55,131,135],{"x":132,"y":133,"style":134},"380.0","244","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","fewer sections widen the earlier ones; an empty one hides the value",[137,138,140],"h2",{"id":139},"prerequisites","Prerequisites",[142,143,148],"pre",{"className":144,"code":145,"language":146,"meta":147,"style":147},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl xlsxwriter pandas\n","bash","",[149,150,151],"code",{"__ignoreMap":147},[152,153,156,160,164,167,170],"span",{"class":154,"line":155},"line",1,[152,157,159],{"class":158},"sMTad","pip",[152,161,163],{"class":162},"srMev"," install",[152,165,166],{"class":162}," openpyxl",[152,168,169],{"class":162}," xlsxwriter",[152,171,172],{"class":162}," pandas\n",[137,174,176],{"id":175},"the-four-sections","The four sections",[10,178,179],{},"A format code is up to four parts separated by semicolons: what to show for positive values,\nnegative values, zero, and text. Supplying fewer is allowed and changes the meaning.",[142,181,185],{"className":182,"code":183,"language":184,"meta":147,"style":147},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\n\nbook = Workbook()\nsheet = book.active\n\nsheet[\"A1\"] = 1234.5\nsheet[\"A1\"].number_format = '#,##0.00;[Red]-#,##0.00;\"–\";@'\n\nsheet[\"A2\"] = -1234.5\nsheet[\"A2\"].number_format = '#,##0.00;[Red]-#,##0.00;\"–\";@'\n\nsheet[\"A3\"] = 0\nsheet[\"A3\"].number_format = '#,##0.00;[Red]-#,##0.00;\"–\";@'\n\nbook.save(\"formats.xlsx\")\n","python",[149,186,187,203,210,222,233,238,256,271,276,294,307,312,327,340,345],{"__ignoreMap":147},[152,188,189,193,197,200],{"class":154,"line":155},[152,190,192],{"class":191},"s-kum","from",[152,194,196],{"class":195},"skGVy"," openpyxl ",[152,198,199],{"class":191},"import",[152,201,202],{"class":195}," Workbook\n",[152,204,206],{"class":154,"line":205},2,[152,207,209],{"emptyLinePlaceholder":208},true,"\n",[152,211,213,216,219],{"class":154,"line":212},3,[152,214,215],{"class":195},"book ",[152,217,218],{"class":191},"=",[152,220,221],{"class":195}," Workbook()\n",[152,223,225,228,230],{"class":154,"line":224},4,[152,226,227],{"class":195},"sheet ",[152,229,218],{"class":191},[152,231,232],{"class":195}," book.active\n",[152,234,236],{"class":154,"line":235},5,[152,237,209],{"emptyLinePlaceholder":208},[152,239,241,244,247,250,252],{"class":154,"line":240},6,[152,242,243],{"class":195},"sheet[",[152,245,246],{"class":162},"\"A1\"",[152,248,249],{"class":195},"] ",[152,251,218],{"class":191},[152,253,255],{"class":254},"sP0c6"," 1234.5\n",[152,257,259,261,263,266,268],{"class":154,"line":258},7,[152,260,243],{"class":195},[152,262,246],{"class":162},[152,264,265],{"class":195},"].number_format ",[152,267,218],{"class":191},[152,269,270],{"class":162}," '#,##0.00;[Red]-#,##0.00;\"–\";@'\n",[152,272,274],{"class":154,"line":273},8,[152,275,209],{"emptyLinePlaceholder":208},[152,277,279,281,284,286,288,291],{"class":154,"line":278},9,[152,280,243],{"class":195},[152,282,283],{"class":162},"\"A2\"",[152,285,249],{"class":195},[152,287,218],{"class":191},[152,289,290],{"class":191}," -",[152,292,293],{"class":254},"1234.5\n",[152,295,297,299,301,303,305],{"class":154,"line":296},10,[152,298,243],{"class":195},[152,300,283],{"class":162},[152,302,265],{"class":195},[152,304,218],{"class":191},[152,306,270],{"class":162},[152,308,310],{"class":154,"line":309},11,[152,311,209],{"emptyLinePlaceholder":208},[152,313,315,317,320,322,324],{"class":154,"line":314},12,[152,316,243],{"class":195},[152,318,319],{"class":162},"\"A3\"",[152,321,249],{"class":195},[152,323,218],{"class":191},[152,325,326],{"class":254}," 0\n",[152,328,330,332,334,336,338],{"class":154,"line":329},13,[152,331,243],{"class":195},[152,333,319],{"class":162},[152,335,265],{"class":195},[152,337,218],{"class":191},[152,339,270],{"class":162},[152,341,343],{"class":154,"line":342},14,[152,344,209],{"emptyLinePlaceholder":208},[152,346,348,351,354],{"class":154,"line":347},15,[152,349,350],{"class":195},"book.save(",[152,352,353],{"class":162},"\"formats.xlsx\"",[152,355,356],{"class":195},")\n",[10,358,359,360,363,364,367,368,371],{},"That single code shows ",[149,361,362],{},"1,234.50",", a red ",[149,365,366],{},"-1,234.50",", and a dash for zero. Rendering zeros as a dash\nrather than ",[149,369,370],{},"0.00"," is one of the highest-value formatting choices in a report — a table full of\nzeros reads as noise, and a table of dashes reads as absence.",[10,373,374,375,377,378,382],{},"The ",[149,376,129],{}," in the fourth section means \"show the text as it is\". Leaving that section off means text is\ndisplayed normally anyway; leaving it ",[379,380,381],"em",{},"empty"," — a trailing semicolon with nothing after it — hides\ntext entirely, which is occasionally useful and more often an accident.",[137,384,386],{"id":385},"the-placeholders-that-matter","The placeholders that matter",[20,388,29,393,29,396,29,399,29,402,29,412,29,417,29,423,29,427,29,430,29,434,29,437,29,442,29,446,29,449,29,452,29,455,29,458,29,463,29,467,29,470,29,473,29,476],{"viewBox":389,"role":23,"ariaLabelledBy":390,"xmlns":27,"style":28},"0 0 760 209",[391,392],"nf-place-t","nf-place-d",[31,394,395],{"id":391},"What each placeholder does",[35,397,398],{"id":392},"A zero always shows a digit and pads, a hash shows one only when present, a question mark reserves alignment space, and a trailing comma scales the displayed value by a thousand.",[39,400],{"x":41,"y":41,"width":42,"height":401,"fill":44},"209",[39,403],{"x":404,"y":405,"width":406,"height":407,"rx":408,"fill":409,"stroke":410,"style":411},"20.0","26","226.7","141","14","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[55,413,416],{"x":414,"y":58,"style":415},"133.35","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","0 and #",[154,418],{"x1":419,"y1":420,"x2":421,"y2":420,"stroke":410,"style":422},"36.0","62","230.7","stroke-width:1px",[55,424,426],{"x":414,"y":425,"style":81},"84","0 pads with zeros",[55,428,429],{"x":414,"y":88,"style":81},"# shows only if present",[55,431,433],{"x":414,"y":432,"style":81},"130","0000 gives 0042",[39,435],{"x":436,"y":405,"width":406,"height":407,"rx":408,"fill":72,"stroke":73,"style":411},"266.7",[55,438,441],{"x":439,"y":58,"style":440},"380.04999999999995","font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","? and .",[154,443],{"x1":444,"y1":420,"x2":445,"y2":420,"stroke":73,"style":422},"282.7","477.4",[55,447,448],{"x":439,"y":425,"style":81},"? reserves space",[55,450,451],{"x":439,"y":88,"style":81},"aligns decimal points",[55,453,454],{"x":439,"y":432,"style":81},"??0.00 lines up",[39,456],{"x":457,"y":405,"width":406,"height":407,"rx":408,"fill":118,"stroke":119,"style":411},"513.4",[55,459,462],{"x":460,"y":58,"style":461},"626.75","font-size:13px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle",", and \"text\"",[154,464],{"x1":465,"y1":420,"x2":466,"y2":420,"stroke":119,"style":422},"529.4","724.0999999999999",[55,468,469],{"x":460,"y":425,"style":81},"trailing , scales by 1000",[55,471,472],{"x":460,"y":88,"style":81},"quoted text is literal",[55,474,475],{"x":460,"y":432,"style":81},"value is unchanged",[55,477,479],{"x":132,"y":478,"style":134},"195","the stored number never changes — only what is drawn",[10,481,482,483,485,486,489,490,493,494,496,497,500],{},"Four symbols do most of the work. ",[149,484,41],{}," is a digit that is always shown, padding with zeros. ",[149,487,488],{},"#"," is a\ndigit shown only if present. ",[149,491,492],{},"?"," reserves space without showing a digit, which aligns decimals in a\ncolumn. And ",[149,495,18],{}," and ",[149,498,499],{},","," are the decimal and thousands separators, with a comma after the last digit\nplaceholder scaling by a thousand.",[142,502,504],{"className":182,"code":503,"language":184,"meta":147,"style":147},"examples = {\n    \"0\":            1234.5,     # 1235      — no decimals, rounded\n    \"0.00\":         1234.5,     # 1234.50\n    \"#,##0\":        1234.5,     # 1,235\n    \"#,##0.00\":     1234.5,     # 1,234.50\n    \"#,##0,\":       1234500,    # 1,235     — displayed in thousands\n    '#,##0,, \"M\"':  1234500,    # 1 M       — displayed in millions\n    \"0000\":         42,         # 0042      — zero-padded identifier\n    \"0.0%\":         0.0725,     # 7.3%      — multiplies by 100\n    \"??0.00\":       7.5,        #    7.50   — aligned on the decimal point\n}\n\nfor index, (code, value) in enumerate(examples.items(), start=1):\n    cell = sheet.cell(row=index, column=3, value=value)\n    cell.number_format = code\n",[149,505,506,516,534,549,564,579,596,611,627,642,658,663,667,696,733],{"__ignoreMap":147},[152,507,508,511,513],{"class":154,"line":155},[152,509,510],{"class":195},"examples ",[152,512,218],{"class":191},[152,514,515],{"class":195}," {\n",[152,517,518,521,524,527,530],{"class":154,"line":205},[152,519,520],{"class":162},"    \"0\"",[152,522,523],{"class":195},":            ",[152,525,526],{"class":254},"1234.5",[152,528,529],{"class":195},",     ",[152,531,533],{"class":532},"s-wDw","# 1235      — no decimals, rounded\n",[152,535,536,539,542,544,546],{"class":154,"line":212},[152,537,538],{"class":162},"    \"0.00\"",[152,540,541],{"class":195},":         ",[152,543,526],{"class":254},[152,545,529],{"class":195},[152,547,548],{"class":532},"# 1234.50\n",[152,550,551,554,557,559,561],{"class":154,"line":224},[152,552,553],{"class":162},"    \"#,##0\"",[152,555,556],{"class":195},":        ",[152,558,526],{"class":254},[152,560,529],{"class":195},[152,562,563],{"class":532},"# 1,235\n",[152,565,566,569,572,574,576],{"class":154,"line":235},[152,567,568],{"class":162},"    \"#,##0.00\"",[152,570,571],{"class":195},":     ",[152,573,526],{"class":254},[152,575,529],{"class":195},[152,577,578],{"class":532},"# 1,234.50\n",[152,580,581,584,587,590,593],{"class":154,"line":240},[152,582,583],{"class":162},"    \"#,##0,\"",[152,585,586],{"class":195},":       ",[152,588,589],{"class":254},"1234500",[152,591,592],{"class":195},",    ",[152,594,595],{"class":532},"# 1,235     — displayed in thousands\n",[152,597,598,601,604,606,608],{"class":154,"line":258},[152,599,600],{"class":162},"    '#,##0,, \"M\"'",[152,602,603],{"class":195},":  ",[152,605,589],{"class":254},[152,607,592],{"class":195},[152,609,610],{"class":532},"# 1 M       — displayed in millions\n",[152,612,613,616,618,621,624],{"class":154,"line":273},[152,614,615],{"class":162},"    \"0000\"",[152,617,541],{"class":195},[152,619,620],{"class":254},"42",[152,622,623],{"class":195},",         ",[152,625,626],{"class":532},"# 0042      — zero-padded identifier\n",[152,628,629,632,634,637,639],{"class":154,"line":278},[152,630,631],{"class":162},"    \"0.0%\"",[152,633,541],{"class":195},[152,635,636],{"class":254},"0.0725",[152,638,529],{"class":195},[152,640,641],{"class":532},"# 7.3%      — multiplies by 100\n",[152,643,644,647,649,652,655],{"class":154,"line":296},[152,645,646],{"class":162},"    \"??0.00\"",[152,648,586],{"class":195},[152,650,651],{"class":254},"7.5",[152,653,654],{"class":195},",        ",[152,656,657],{"class":532},"#    7.50   — aligned on the decimal point\n",[152,659,660],{"class":154,"line":309},[152,661,662],{"class":195},"}\n",[152,664,665],{"class":154,"line":314},[152,666,209],{"emptyLinePlaceholder":208},[152,668,669,672,675,678,681,684,688,690,693],{"class":154,"line":329},[152,670,671],{"class":191},"for",[152,673,674],{"class":195}," index, (code, value) ",[152,676,677],{"class":191},"in",[152,679,680],{"class":254}," enumerate",[152,682,683],{"class":195},"(examples.items(), ",[152,685,687],{"class":686},"sa561","start",[152,689,218],{"class":191},[152,691,692],{"class":254},"1",[152,694,695],{"class":195},"):\n",[152,697,698,701,703,706,709,711,714,717,719,722,725,728,730],{"class":154,"line":342},[152,699,700],{"class":195},"    cell ",[152,702,218],{"class":191},[152,704,705],{"class":195}," sheet.cell(",[152,707,708],{"class":686},"row",[152,710,218],{"class":191},[152,712,713],{"class":195},"index, ",[152,715,716],{"class":686},"column",[152,718,218],{"class":191},[152,720,721],{"class":254},"3",[152,723,724],{"class":195},", ",[152,726,727],{"class":686},"value",[152,729,218],{"class":191},[152,731,732],{"class":195},"value)\n",[152,734,735,738,740],{"class":154,"line":347},[152,736,737],{"class":195},"    cell.number_format ",[152,739,218],{"class":191},[152,741,742],{"class":195}," code\n",[10,744,745,746,749,750,753],{},"The scaling comma is the one worth remembering: ",[149,747,748],{},"#,##0,"," shows 1,234,500 as ",[149,751,752],{},"1,235"," while the cell\nstill contains 1,234,500, so totals and charts remain correct. Dividing the value in Python instead\nwould break both.",[137,755,757],{"id":756},"currency-and-where-the-symbol-goes","Currency, and where the symbol goes",[142,759,761],{"className":182,"code":760,"language":184,"meta":147,"style":147},"codes = {\n    \"GBP\":      '£#,##0.00',\n    \"EUR\":      '€#,##0.00',\n    \"USD red\":  '$#,##0.00;[Red]($#,##0.00)',\n    \"accounting\": '_-£* #,##0.00_-;-£* #,##0.00_-;_-£* \"–\"_-;_-@_-',\n}\n",[149,762,763,772,786,798,810,823],{"__ignoreMap":147},[152,764,765,768,770],{"class":154,"line":155},[152,766,767],{"class":195},"codes ",[152,769,218],{"class":191},[152,771,515],{"class":195},[152,773,774,777,780,783],{"class":154,"line":205},[152,775,776],{"class":162},"    \"GBP\"",[152,778,779],{"class":195},":      ",[152,781,782],{"class":162},"'£#,##0.00'",[152,784,785],{"class":195},",\n",[152,787,788,791,793,796],{"class":154,"line":212},[152,789,790],{"class":162},"    \"EUR\"",[152,792,779],{"class":195},[152,794,795],{"class":162},"'€#,##0.00'",[152,797,785],{"class":195},[152,799,800,803,805,808],{"class":154,"line":224},[152,801,802],{"class":162},"    \"USD red\"",[152,804,603],{"class":195},[152,806,807],{"class":162},"'$#,##0.00;[Red]($#,##0.00)'",[152,809,785],{"class":195},[152,811,812,815,818,821],{"class":154,"line":235},[152,813,814],{"class":162},"    \"accounting\"",[152,816,817],{"class":195},": ",[152,819,820],{"class":162},"'_-£* #,##0.00_-;-£* #,##0.00_-;_-£* \"–\"_-;_-@_-'",[152,822,785],{"class":195},[152,824,825],{"class":154,"line":240},[152,826,662],{"class":195},[10,828,829,830,833,834,837],{},"The accounting format is the unreadable one and it is worth decoding once: ",[149,831,832],{},"_-"," inserts a space the\nwidth of a minus sign, ",[149,835,836],{},"*"," repeats the following character to fill the cell, and the combination is\nwhat pushes the currency symbol to the left edge while the digits stay right-aligned. Nobody writes\nthat from memory — copy it, and change only the symbol.",[10,839,840,841,845],{},"The parenthesised negative in the third entry is the convention most finance readers expect, and it\nis a formatting choice rather than a data one, which is exactly the distinction this whole guide\nturns on. ",[14,842,844],{"href":843},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fformat-excel-cells-as-currency-with-python\u002F","Format Excel Cells as Currency with Python","\ncovers the currency case in more depth.",[137,847,849],{"id":848},"text-units-and-conditions-inside-a-format","Text, units and conditions inside a format",[10,851,852],{},"Anything in double quotes is shown literally, which is how units get attached without turning the\nnumber into a string.",[142,854,856],{"className":182,"code":855,"language":184,"meta":147,"style":147},"sheet[\"E1\"] = 12.5\nsheet[\"E1\"].number_format = '0.0\" kg\"'            # 12.5 kg, still a number\n\nsheet[\"E2\"] = 0.0725\nsheet[\"E2\"].number_format = '0.0%\" YoY\"'          # 7.3% YoY\n\nsheet[\"E3\"] = 4200\nsheet[\"E3\"].number_format = '[>=1000]#,##0,\"k\";[\u003C1000]#,##0'   # 4k\n",[149,857,858,872,888,892,906,922,926,940],{"__ignoreMap":147},[152,859,860,862,865,867,869],{"class":154,"line":155},[152,861,243],{"class":195},[152,863,864],{"class":162},"\"E1\"",[152,866,249],{"class":195},[152,868,218],{"class":191},[152,870,871],{"class":254}," 12.5\n",[152,873,874,876,878,880,882,885],{"class":154,"line":205},[152,875,243],{"class":195},[152,877,864],{"class":162},[152,879,265],{"class":195},[152,881,218],{"class":191},[152,883,884],{"class":162}," '0.0\" kg\"'",[152,886,887],{"class":532},"            # 12.5 kg, still a number\n",[152,889,890],{"class":154,"line":212},[152,891,209],{"emptyLinePlaceholder":208},[152,893,894,896,899,901,903],{"class":154,"line":224},[152,895,243],{"class":195},[152,897,898],{"class":162},"\"E2\"",[152,900,249],{"class":195},[152,902,218],{"class":191},[152,904,905],{"class":254}," 0.0725\n",[152,907,908,910,912,914,916,919],{"class":154,"line":235},[152,909,243],{"class":195},[152,911,898],{"class":162},[152,913,265],{"class":195},[152,915,218],{"class":191},[152,917,918],{"class":162}," '0.0%\" YoY\"'",[152,920,921],{"class":532},"          # 7.3% YoY\n",[152,923,924],{"class":154,"line":240},[152,925,209],{"emptyLinePlaceholder":208},[152,927,928,930,933,935,937],{"class":154,"line":258},[152,929,243],{"class":195},[152,931,932],{"class":162},"\"E3\"",[152,934,249],{"class":195},[152,936,218],{"class":191},[152,938,939],{"class":254}," 4200\n",[152,941,942,944,946,948,950,953],{"class":154,"line":273},[152,943,243],{"class":195},[152,945,932],{"class":162},[152,947,265],{"class":195},[152,949,218],{"class":191},[152,951,952],{"class":162}," '[>=1000]#,##0,\"k\";[\u003C1000]#,##0'",[152,954,955],{"class":532},"   # 4k\n",[10,957,958,959,18],{},"Square-bracket conditions let a single format branch on the value, which covers the common case of\nshowing large numbers in thousands and small ones in full. Excel allows two conditions plus a\nfallback — beyond that, conditional formatting is the right tool, as in\n",[14,960,962],{"href":961},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-cells-above-a-threshold-with-openpyxl\u002F","Highlight Cells Above a Threshold with openpyxl",[137,964,966],{"id":965},"applying-formats-efficiently","Applying formats efficiently",[10,968,969,970,973],{},"Setting ",[149,971,972],{},"number_format"," per cell is fine for a few hundred rows and wasteful for fifty thousand.\nxlsxwriter attaches a format to a whole column in one call, which is both faster to write and smaller\nin the finished file.",[142,975,977],{"className":182,"code":976,"language":184,"meta":147,"style":147},"import pandas as pd\n\nframe = pd.DataFrame({\"Region\": [\"North\", \"South\"], \"Revenue\": [1234500.0, 987650.0],\n                      \"Share\": [0.5556, 0.4444]})\n\nwith pd.ExcelWriter(\"report.xlsx\", engine=\"xlsxwriter\") as writer:\n    frame.to_excel(writer, sheet_name=\"Summary\", index=False)\n    book, sheet = writer.book, writer.sheets[\"Summary\"]\n    thousands = book.add_format({\"num_format\": '#,##0,\"k\"'})\n    percent = book.add_format({\"num_format\": \"0.0%\"})\n    sheet.set_column(\"B:B\", 14, thousands)\n    sheet.set_column(\"C:C\", 10, percent)\n",[149,978,979,992,996,1039,1057,1061,1090,1115,1130,1151,1169,1184],{"__ignoreMap":147},[152,980,981,983,986,989],{"class":154,"line":155},[152,982,199],{"class":191},[152,984,985],{"class":195}," pandas ",[152,987,988],{"class":191},"as",[152,990,991],{"class":195}," pd\n",[152,993,994],{"class":154,"line":205},[152,995,209],{"emptyLinePlaceholder":208},[152,997,998,1001,1003,1006,1009,1012,1015,1017,1020,1023,1026,1028,1031,1033,1036],{"class":154,"line":212},[152,999,1000],{"class":195},"frame ",[152,1002,218],{"class":191},[152,1004,1005],{"class":195}," pd.DataFrame({",[152,1007,1008],{"class":162},"\"Region\"",[152,1010,1011],{"class":195},": [",[152,1013,1014],{"class":162},"\"North\"",[152,1016,724],{"class":195},[152,1018,1019],{"class":162},"\"South\"",[152,1021,1022],{"class":195},"], ",[152,1024,1025],{"class":162},"\"Revenue\"",[152,1027,1011],{"class":195},[152,1029,1030],{"class":254},"1234500.0",[152,1032,724],{"class":195},[152,1034,1035],{"class":254},"987650.0",[152,1037,1038],{"class":195},"],\n",[152,1040,1041,1044,1046,1049,1051,1054],{"class":154,"line":224},[152,1042,1043],{"class":162},"                      \"Share\"",[152,1045,1011],{"class":195},[152,1047,1048],{"class":254},"0.5556",[152,1050,724],{"class":195},[152,1052,1053],{"class":254},"0.4444",[152,1055,1056],{"class":195},"]})\n",[152,1058,1059],{"class":154,"line":235},[152,1060,209],{"emptyLinePlaceholder":208},[152,1062,1063,1066,1069,1072,1074,1077,1079,1082,1085,1087],{"class":154,"line":240},[152,1064,1065],{"class":191},"with",[152,1067,1068],{"class":195}," pd.ExcelWriter(",[152,1070,1071],{"class":162},"\"report.xlsx\"",[152,1073,724],{"class":195},[152,1075,1076],{"class":686},"engine",[152,1078,218],{"class":191},[152,1080,1081],{"class":162},"\"xlsxwriter\"",[152,1083,1084],{"class":195},") ",[152,1086,988],{"class":191},[152,1088,1089],{"class":195}," writer:\n",[152,1091,1092,1095,1098,1100,1103,1105,1108,1110,1113],{"class":154,"line":258},[152,1093,1094],{"class":195},"    frame.to_excel(writer, ",[152,1096,1097],{"class":686},"sheet_name",[152,1099,218],{"class":191},[152,1101,1102],{"class":162},"\"Summary\"",[152,1104,724],{"class":195},[152,1106,1107],{"class":686},"index",[152,1109,218],{"class":191},[152,1111,1112],{"class":254},"False",[152,1114,356],{"class":195},[152,1116,1117,1120,1122,1125,1127],{"class":154,"line":273},[152,1118,1119],{"class":195},"    book, sheet ",[152,1121,218],{"class":191},[152,1123,1124],{"class":195}," writer.book, writer.sheets[",[152,1126,1102],{"class":162},[152,1128,1129],{"class":195},"]\n",[152,1131,1132,1135,1137,1140,1143,1145,1148],{"class":154,"line":278},[152,1133,1134],{"class":195},"    thousands ",[152,1136,218],{"class":191},[152,1138,1139],{"class":195}," book.add_format({",[152,1141,1142],{"class":162},"\"num_format\"",[152,1144,817],{"class":195},[152,1146,1147],{"class":162},"'#,##0,\"k\"'",[152,1149,1150],{"class":195},"})\n",[152,1152,1153,1156,1158,1160,1162,1164,1167],{"class":154,"line":296},[152,1154,1155],{"class":195},"    percent ",[152,1157,218],{"class":191},[152,1159,1139],{"class":195},[152,1161,1142],{"class":162},[152,1163,817],{"class":195},[152,1165,1166],{"class":162},"\"0.0%\"",[152,1168,1150],{"class":195},[152,1170,1171,1174,1177,1179,1181],{"class":154,"line":309},[152,1172,1173],{"class":195},"    sheet.set_column(",[152,1175,1176],{"class":162},"\"B:B\"",[152,1178,724],{"class":195},[152,1180,408],{"class":254},[152,1182,1183],{"class":195},", thousands)\n",[152,1185,1186,1188,1191,1193,1196],{"class":154,"line":314},[152,1187,1173],{"class":195},[152,1189,1190],{"class":162},"\"C:C\"",[152,1192,724],{"class":195},[152,1194,1195],{"class":254},"10",[152,1197,1198],{"class":195},", percent)\n",[10,1200,1201,1204],{},[149,1202,1203],{},"set_column"," applies to every cell in the column that is not individually formatted, which means one\ncall covers however many rows arrive next month. In openpyxl the equivalent is a loop, and the\nloop is what makes formatting a large sheet slow.",[137,1206,1208],{"id":1207},"building-a-small-format-library","Building a small format library",[10,1210,1211],{},"Once a report uses more than three or four codes, keeping them as literals scattered through the\nscript guarantees they drift apart. A dictionary of named formats, created once per workbook, keeps\nthe appearance consistent and makes a change to the house style a single edit.",[142,1213,1215],{"className":182,"code":1214,"language":184,"meta":147,"style":147},"def build_formats(book) -> dict:\n    return {\n        \"money\":     book.add_format({\"num_format\": '#,##0.00'}),\n        \"thousands\": book.add_format({\"num_format\": '#,##0,\"k\"'}),\n        \"percent\":   book.add_format({\"num_format\": '0.0%'}),\n        \"signed\":    book.add_format({\"num_format\": '▲ 0.0%;▼ 0.0%;–'}),\n        \"date\":      book.add_format({\"num_format\": 'yyyy-mm-dd'}),\n        \"header\":    book.add_format({\"bold\": True, \"bg_color\": \"#F0F4FF\", \"border\": 1}),\n    }\n\nformats = build_formats(book)\nsheet.set_column(\"B:B\", 14, formats[\"money\"])\nsheet.set_column(\"D:D\", 10, formats[\"percent\"])\n",[149,1216,1217,1235,1242,1260,1276,1293,1310,1327,1363,1368,1372,1382,1402],{"__ignoreMap":147},[152,1218,1219,1222,1226,1229,1232],{"class":154,"line":155},[152,1220,1221],{"class":191},"def",[152,1223,1225],{"class":1224},"s_Opv"," build_formats",[152,1227,1228],{"class":195},"(book) -> ",[152,1230,1231],{"class":254},"dict",[152,1233,1234],{"class":195},":\n",[152,1236,1237,1240],{"class":154,"line":205},[152,1238,1239],{"class":191},"    return",[152,1241,515],{"class":195},[152,1243,1244,1247,1250,1252,1254,1257],{"class":154,"line":212},[152,1245,1246],{"class":162},"        \"money\"",[152,1248,1249],{"class":195},":     book.add_format({",[152,1251,1142],{"class":162},[152,1253,817],{"class":195},[152,1255,1256],{"class":162},"'#,##0.00'",[152,1258,1259],{"class":195},"}),\n",[152,1261,1262,1265,1268,1270,1272,1274],{"class":154,"line":224},[152,1263,1264],{"class":162},"        \"thousands\"",[152,1266,1267],{"class":195},": book.add_format({",[152,1269,1142],{"class":162},[152,1271,817],{"class":195},[152,1273,1147],{"class":162},[152,1275,1259],{"class":195},[152,1277,1278,1281,1284,1286,1288,1291],{"class":154,"line":235},[152,1279,1280],{"class":162},"        \"percent\"",[152,1282,1283],{"class":195},":   book.add_format({",[152,1285,1142],{"class":162},[152,1287,817],{"class":195},[152,1289,1290],{"class":162},"'0.0%'",[152,1292,1259],{"class":195},[152,1294,1295,1298,1301,1303,1305,1308],{"class":154,"line":240},[152,1296,1297],{"class":162},"        \"signed\"",[152,1299,1300],{"class":195},":    book.add_format({",[152,1302,1142],{"class":162},[152,1304,817],{"class":195},[152,1306,1307],{"class":162},"'▲ 0.0%;▼ 0.0%;–'",[152,1309,1259],{"class":195},[152,1311,1312,1315,1318,1320,1322,1325],{"class":154,"line":258},[152,1313,1314],{"class":162},"        \"date\"",[152,1316,1317],{"class":195},":      book.add_format({",[152,1319,1142],{"class":162},[152,1321,817],{"class":195},[152,1323,1324],{"class":162},"'yyyy-mm-dd'",[152,1326,1259],{"class":195},[152,1328,1329,1332,1334,1337,1339,1342,1344,1347,1349,1352,1354,1357,1359,1361],{"class":154,"line":273},[152,1330,1331],{"class":162},"        \"header\"",[152,1333,1300],{"class":195},[152,1335,1336],{"class":162},"\"bold\"",[152,1338,817],{"class":195},[152,1340,1341],{"class":254},"True",[152,1343,724],{"class":195},[152,1345,1346],{"class":162},"\"bg_color\"",[152,1348,817],{"class":195},[152,1350,1351],{"class":162},"\"#F0F4FF\"",[152,1353,724],{"class":195},[152,1355,1356],{"class":162},"\"border\"",[152,1358,817],{"class":195},[152,1360,692],{"class":254},[152,1362,1259],{"class":195},[152,1364,1365],{"class":154,"line":278},[152,1366,1367],{"class":195},"    }\n",[152,1369,1370],{"class":154,"line":296},[152,1371,209],{"emptyLinePlaceholder":208},[152,1373,1374,1377,1379],{"class":154,"line":309},[152,1375,1376],{"class":195},"formats ",[152,1378,218],{"class":191},[152,1380,1381],{"class":195}," build_formats(book)\n",[152,1383,1384,1387,1389,1391,1393,1396,1399],{"class":154,"line":314},[152,1385,1386],{"class":195},"sheet.set_column(",[152,1388,1176],{"class":162},[152,1390,724],{"class":195},[152,1392,408],{"class":254},[152,1394,1395],{"class":195},", formats[",[152,1397,1398],{"class":162},"\"money\"",[152,1400,1401],{"class":195},"])\n",[152,1403,1404,1406,1409,1411,1413,1415,1418],{"class":154,"line":329},[152,1405,1386],{"class":195},[152,1407,1408],{"class":162},"\"D:D\"",[152,1410,724],{"class":195},[152,1412,1195],{"class":254},[152,1414,1395],{"class":195},[152,1416,1417],{"class":162},"\"percent\"",[152,1419,1401],{"class":195},[10,1421,1422,1423,1426,1427,1430],{},"Naming them by meaning rather than by code — ",[149,1424,1425],{},"money",", not ",[149,1428,1429],{},"hash_comma_hash_hash_zero"," — is what makes\nthe calling code readable a year later. It also makes the set auditable: a quick look at the\ndictionary answers \"how many ways does this report show a number\", which is a question worth asking\nof any workbook that has grown organically.",[10,1432,1433,1434,1437,1438,18],{},"The openpyxl equivalent uses ",[149,1435,1436],{},"NamedStyle",", which is registered on the workbook once and then assigned\nby name — the approach described in\n",[14,1439,1441],{"href":1440},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Fapply-a-reusable-style-theme-across-an-excel-report\u002F","Apply a Reusable Style Theme Across an Excel Report",[137,1443,1445],{"id":1444},"locale-and-the-workbook-that-looks-wrong-elsewhere","Locale, and the workbook that looks wrong elsewhere",[10,1447,1448,1449,1452,1453,1455],{},"Format codes are stored in a locale-neutral form and rendered according to the reader's settings,\nwhich mostly works and occasionally does not. The separators are the safe part: a comma in the code\nmeans \"thousands separator\" and renders as a full stop in locales that use one. The currency symbol\nis the unsafe part, because a literal ",[149,1450,1451],{},"£"," is a literal ",[149,1454,1451],{}," wherever the file is opened.",[10,1457,1458],{},"Where a report crosses locales, the honest options are to state the currency in the column header and\nuse a plain numeric format, or to embed the locale identifier in the code:",[142,1460,1462],{"className":182,"code":1461,"language":184,"meta":147,"style":147},"uk_pounds = '[$£-en-GB]#,##0.00'\neuros_de  = '[$€-de-DE]#,##0.00'\n",[149,1463,1464,1474],{"__ignoreMap":147},[152,1465,1466,1469,1471],{"class":154,"line":155},[152,1467,1468],{"class":195},"uk_pounds ",[152,1470,218],{"class":191},[152,1472,1473],{"class":162}," '[$£-en-GB]#,##0.00'\n",[152,1475,1476,1479,1481],{"class":154,"line":205},[152,1477,1478],{"class":195},"euros_de  ",[152,1480,218],{"class":191},[152,1482,1483],{"class":162}," '[$€-de-DE]#,##0.00'\n",[10,1485,1486,1487,18],{},"The bracketed form tells Excel both the symbol and the locale it belongs to, which keeps the number\ngrouping consistent with the currency rather than with the reader's machine. It is rarely needed for\nan internal report and worth knowing about the first time one is sent abroad, alongside the date\nquestion covered in\n",[14,1488,1490],{"href":1489},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fformat-dates-in-excel-cells-with-python\u002F","Format Dates in Excel Cells with Python",[137,1492,1494],{"id":1493},"common-pitfalls","Common pitfalls",[1496,1497,1498,1514],"table",{},[1499,1500,1501],"thead",{},[1502,1503,1504,1508,1511],"tr",{},[1505,1506,1507],"th",{},"Symptom",[1505,1509,1510],{},"Cause",[1505,1512,1513],{},"Fix",[1515,1516,1517,1529,1544,1557,1571,1582],"tbody",{},[1502,1518,1519,1523,1526],{},[1520,1521,1522],"td",{},"The percentage shows as 725%",[1520,1524,1525],{},"The value was already multiplied by 100",[1520,1527,1528],{},"Store the proportion; the format multiplies",[1502,1530,1531,1534,1537],{},[1520,1532,1533],{},"The format is ignored",[1520,1535,1536],{},"The cell contains text, not a number",[1520,1538,1539,1540,1543],{},"Convert with ",[149,1541,1542],{},"pd.to_numeric"," before writing",[1502,1545,1546,1549,1554],{},[1520,1547,1548],{},"Column formatting does not apply",[1520,1550,1551,1552],{},"Cells written individually after ",[149,1553,1203],{},[1520,1555,1556],{},"Pass the format to the write call as well",[1502,1558,1559,1562,1565],{},[1520,1560,1561],{},"Text disappears from a cell",[1520,1563,1564],{},"An empty fourth section in the code",[1520,1566,1567,1568,1570],{},"Use ",[149,1569,129],{}," or omit the section entirely",[1502,1572,1573,1576,1579],{},[1520,1574,1575],{},"The number changes when reopened",[1520,1577,1578],{},"The value was divided in Python, not scaled by format",[1520,1580,1581],{},"Use a trailing comma in the code instead",[1502,1583,1584,1587,1590],{},[1520,1585,1586],{},"Dates show as five-digit numbers",[1520,1588,1589],{},"No date format applied to a serial",[1520,1591,1592,1593],{},"See ",[14,1594,1596],{"href":1595},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffix-excel-serial-numbers-showing-instead-of-dates\u002F","fixing Excel serial numbers",[137,1598,1600],{"id":1599},"reading-the-formats-already-in-a-workbook","Reading the formats already in a workbook",[10,1602,1603],{},"When matching an existing report's appearance, the fastest route is to read the codes it already\nuses rather than guessing at them.",[142,1605,1607],{"className":182,"code":1606,"language":184,"meta":147,"style":147},"from collections import Counter\nfrom openpyxl import load_workbook\n\nbook = load_workbook(\"existing-report.xlsx\")\ncodes = Counter()\nfor sheet in book.worksheets:\n    for row in sheet.iter_rows():\n        for cell in row:\n            if cell.value is not None and cell.number_format != \"General\":\n                codes[cell.number_format] += 1\n\nfor code, count in codes.most_common(10):\n    print(f\"{count:6d}  {code}\")\n",[149,1608,1609,1621,1632,1636,1650,1659,1671,1684,1697,1728,1739,1743,1759],{"__ignoreMap":147},[152,1610,1611,1613,1616,1618],{"class":154,"line":155},[152,1612,192],{"class":191},[152,1614,1615],{"class":195}," collections ",[152,1617,199],{"class":191},[152,1619,1620],{"class":195}," Counter\n",[152,1622,1623,1625,1627,1629],{"class":154,"line":205},[152,1624,192],{"class":191},[152,1626,196],{"class":195},[152,1628,199],{"class":191},[152,1630,1631],{"class":195}," load_workbook\n",[152,1633,1634],{"class":154,"line":212},[152,1635,209],{"emptyLinePlaceholder":208},[152,1637,1638,1640,1642,1645,1648],{"class":154,"line":224},[152,1639,215],{"class":195},[152,1641,218],{"class":191},[152,1643,1644],{"class":195}," load_workbook(",[152,1646,1647],{"class":162},"\"existing-report.xlsx\"",[152,1649,356],{"class":195},[152,1651,1652,1654,1656],{"class":154,"line":235},[152,1653,767],{"class":195},[152,1655,218],{"class":191},[152,1657,1658],{"class":195}," Counter()\n",[152,1660,1661,1663,1666,1668],{"class":154,"line":240},[152,1662,671],{"class":191},[152,1664,1665],{"class":195}," sheet ",[152,1667,677],{"class":191},[152,1669,1670],{"class":195}," book.worksheets:\n",[152,1672,1673,1676,1679,1681],{"class":154,"line":258},[152,1674,1675],{"class":191},"    for",[152,1677,1678],{"class":195}," row ",[152,1680,677],{"class":191},[152,1682,1683],{"class":195}," sheet.iter_rows():\n",[152,1685,1686,1689,1692,1694],{"class":154,"line":273},[152,1687,1688],{"class":191},"        for",[152,1690,1691],{"class":195}," cell ",[152,1693,677],{"class":191},[152,1695,1696],{"class":195}," row:\n",[152,1698,1699,1702,1705,1708,1711,1714,1717,1720,1723,1726],{"class":154,"line":278},[152,1700,1701],{"class":191},"            if",[152,1703,1704],{"class":195}," cell.value ",[152,1706,1707],{"class":191},"is",[152,1709,1710],{"class":191}," not",[152,1712,1713],{"class":254}," None",[152,1715,1716],{"class":191}," and",[152,1718,1719],{"class":195}," cell.number_format ",[152,1721,1722],{"class":191},"!=",[152,1724,1725],{"class":162}," \"General\"",[152,1727,1234],{"class":195},[152,1729,1730,1733,1736],{"class":154,"line":296},[152,1731,1732],{"class":195},"                codes[cell.number_format] ",[152,1734,1735],{"class":191},"+=",[152,1737,1738],{"class":254}," 1\n",[152,1740,1741],{"class":154,"line":309},[152,1742,209],{"emptyLinePlaceholder":208},[152,1744,1745,1747,1750,1752,1755,1757],{"class":154,"line":314},[152,1746,671],{"class":191},[152,1748,1749],{"class":195}," code, count ",[152,1751,677],{"class":191},[152,1753,1754],{"class":195}," codes.most_common(",[152,1756,1195],{"class":254},[152,1758,695],{"class":195},[152,1760,1761,1764,1767,1770,1773,1777,1780,1783,1786,1789,1791,1793,1795],{"class":154,"line":329},[152,1762,1763],{"class":254},"    print",[152,1765,1766],{"class":195},"(",[152,1768,1769],{"class":191},"f",[152,1771,1772],{"class":162},"\"",[152,1774,1776],{"class":1775},"sSjpA","{",[152,1778,1779],{"class":195},"count",[152,1781,1782],{"class":191},":6d",[152,1784,1785],{"class":1775},"}",[152,1787,1788],{"class":1775},"  {",[152,1790,149],{"class":195},[152,1792,1785],{"class":1775},[152,1794,1772],{"class":162},[152,1796,356],{"class":195},[10,1798,1799,1800,1804],{},"That inventory is usually shorter than expected — a real report tends to use three or four codes —\nand copying them exactly is what makes a generated workbook indistinguishable from the hand-built one\nit replaces. It is the same technique as the formula inventory in\n",[14,1801,1803],{"href":1802},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002F","Excel Formula Equivalents in pandas",",\napplied to appearance instead of logic.",[137,1806,1808],{"id":1807},"performance-and-scale","Performance and scale",[20,1810,29,1816,29,1819,29,1822,29,1826,29,1832,29,1838,29,1845,29,1851,29,1855,29,1857,29,1861,29,1866,29,1870,29,1873,29,1877,29,1882,29,1886],{"viewBox":1811,"role":23,"ariaLabelledBy":1812,"xmlns":27,"style":1815},"0 0 720 196",[1813,1814],"nf-cost-t","nf-cost-d","width:100%;max-width:720px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[31,1817,1818],{"id":1813},"Where formatting cost accumulates on a 50,000-row sheet",[35,1820,1821],{"id":1814},"Creating a format object per row fills the workbook's style table, formatting cells individually is better, and applying one format to the whole column is cheapest by a wide margin.",[39,1823],{"x":41,"y":41,"width":1824,"height":1825,"fill":44},"720","196",[55,1827,1831],{"x":1828,"y":1829,"style":1830},"20","56","font-size:12px;font-weight:600;fill:var(--text,#172033);text-anchor:start","a format per row",[39,1833],{"x":1834,"y":1835,"width":1836,"height":405,"rx":1837,"fill":52,"stroke":53},"200","40","344.0","6",[39,1839],{"x":1840,"y":1841,"width":1842,"height":1843,"rx":1844,"fill":89,"stroke":90},"201","41","342.0","24","5",[55,1846,1850],{"x":1847,"y":1848,"style":1849},"556.0","58","font-size:12px;font-weight:700;fill:var(--accent,#d81b73);text-anchor:start","50,000 style entries",[55,1852,1854],{"x":1828,"y":1853,"style":1830},"100","one format, per cell",[39,1856],{"x":1834,"y":425,"width":1836,"height":405,"rx":1837,"fill":52,"stroke":53},[39,1858],{"x":1840,"y":1859,"width":1860,"height":1843,"rx":1844,"fill":118,"stroke":119},"85","100.0",[55,1862,1865],{"x":1847,"y":1863,"style":1864},"102","font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:start","one entry, many refs",[55,1867,1869],{"x":1828,"y":1868,"style":1830},"144","one format, per column",[39,1871],{"x":1834,"y":1872,"width":1836,"height":405,"rx":1837,"fill":52,"stroke":53},"128",[39,1874],{"x":1840,"y":1875,"width":1876,"height":1843,"rx":1844,"fill":72,"stroke":73},"129","28",[55,1878,1881],{"x":1847,"y":1879,"style":1880},"146","font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:start","one call",[55,1883,1885],{"x":1828,"y":1828,"style":1884},"font-size:11.5px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:start","relative cost",[55,1887,1890],{"x":1888,"y":1889,"style":134},"360.0","186","reuse the format object; format the column, not the cells",[10,1892,1893],{},"Each distinct format in a workbook is an entry in its style table, and each formatted cell references\none. That means the cost is in the number of distinct formats and the number of individually\nformatted cells, not in the complexity of the codes themselves.",[10,1895,1896,1897,1900],{},"Two habits keep a large workbook fast. Create each format object once and reuse it, rather than\nconstructing one per row — in xlsxwriter a fresh ",[149,1898,1899],{},"add_format"," per row produces thousands of style\nentries. And prefer column-level formatting to per-cell formatting wherever the whole column shares a\nformat, which is almost always.",[142,1902,1904],{"className":182,"code":1903,"language":184,"meta":147,"style":147},"money = book.add_format({\"num_format\": \"#,##0.00\"})     # once\nfor index in range(1, 50001):\n    sheet.write_number(index, 1, values[index - 1], money)   # reused\n",[149,1905,1906,1928,1951],{"__ignoreMap":147},[152,1907,1908,1911,1913,1915,1917,1919,1922,1925],{"class":154,"line":155},[152,1909,1910],{"class":195},"money ",[152,1912,218],{"class":191},[152,1914,1139],{"class":195},[152,1916,1142],{"class":162},[152,1918,817],{"class":195},[152,1920,1921],{"class":162},"\"#,##0.00\"",[152,1923,1924],{"class":195},"})     ",[152,1926,1927],{"class":532},"# once\n",[152,1929,1930,1932,1935,1937,1940,1942,1944,1946,1949],{"class":154,"line":205},[152,1931,671],{"class":191},[152,1933,1934],{"class":195}," index ",[152,1936,677],{"class":191},[152,1938,1939],{"class":254}," range",[152,1941,1766],{"class":195},[152,1943,692],{"class":254},[152,1945,724],{"class":195},[152,1947,1948],{"class":254},"50001",[152,1950,695],{"class":195},[152,1952,1953,1956,1958,1961,1964,1967,1970],{"class":154,"line":212},[152,1954,1955],{"class":195},"    sheet.write_number(index, ",[152,1957,692],{"class":254},[152,1959,1960],{"class":195},", values[index ",[152,1962,1963],{"class":191},"-",[152,1965,1966],{"class":254}," 1",[152,1968,1969],{"class":195},"], money)   ",[152,1971,1972],{"class":532},"# reused\n",[137,1974,1976],{"id":1975},"conclusion","Conclusion",[10,1978,1979,1980,1982,1983,1985],{},"A format code has four sections — positive, negative, zero, text — and a handful of placeholders:\n",[149,1981,41],{}," always shows a digit, ",[149,1984,488],{}," shows one only if present, a trailing comma scales by a thousand, and\nquoted text appears literally. Use them to show zeros as dashes, negatives in red parentheses and\nlarge numbers in thousands, all without changing the stored value. Apply them to columns rather than\ncells, and reuse format objects rather than creating one per row.",[137,1987,1989],{"id":1988},"frequently-asked-questions","Frequently asked questions",[10,1991,1992,1996],{},[1993,1994,1995],"strong",{},"How many sections can a format code have?","\nUp to four, separated by semicolons: positive, negative, zero and text, in that order. With two sections the second covers negatives and zeros; with one, it covers everything. A missing section is not the same as an empty one — an empty section hides the value.",[10,1998,1999,2002],{},[1993,2000,2001],{},"How do I show a number in thousands?","\nPut a comma after the last digit placeholder: '#,##0,' divides the displayed value by a thousand, and two commas by a million. The stored value is unchanged, which is what makes it safe — the cell still sums correctly.",[10,2004,2005,2008],{},[1993,2006,2007],{},"Why does my format work in openpyxl but not xlsxwriter?","\nThe code is the same in both; the difference is where you set it. openpyxl assigns to cell.number_format per cell, xlsxwriter attaches a Format object to a column or a write call. A column format in xlsxwriter does not apply to cells written individually afterwards.",[10,2010,2011,2014,2015,2018],{},[1993,2012,2013],{},"Can a format change the cell colour?","\nYes, with a colour name in square brackets at the start of a section — ",[152,2016,2017],{},"Red","-#,##0. It is limited to eight named colours and applies to the whole section, which is why conditional formatting is the better tool for anything more nuanced.",[137,2020,2022],{"id":2021},"related","Related",[2024,2025,2026,2033,2038,2045,2050],"ul",{},[2027,2028,2029,2030,2032],"li",{},"Up one level: ",[14,2031,17],{"href":16}," — the wider formatting topic.",[2027,2034,2035,2037],{},[14,2036,844],{"href":843}," — currency symbols, alignment and the accounting layout.",[2027,2039,2040,2044],{},[14,2041,2043],{"href":2042},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fformat-numbers-as-percentages-in-excel-with-python\u002F","Format Numbers as Percentages in Excel with Python"," — the proportion-versus-percentage trap in detail.",[2027,2046,2047,2049],{},[14,2048,1490],{"href":1489}," — the date half of the same syntax.",[2027,2051,2052,2054],{},[14,2053,1441],{"href":1440}," — keeping formats consistent across sheets.",[2056,2057,2058],"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 .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":147,"searchDepth":205,"depth":205,"links":2060},[2061,2062,2063,2064,2065,2066,2067,2068,2069,2070,2071,2072,2073,2074],{"id":139,"depth":205,"text":140},{"id":175,"depth":205,"text":176},{"id":385,"depth":205,"text":386},{"id":756,"depth":205,"text":757},{"id":848,"depth":205,"text":849},{"id":965,"depth":205,"text":966},{"id":1207,"depth":205,"text":1208},{"id":1444,"depth":205,"text":1445},{"id":1493,"depth":205,"text":1494},{"id":1599,"depth":205,"text":1600},{"id":1807,"depth":205,"text":1808},{"id":1975,"depth":205,"text":1976},{"id":1988,"depth":205,"text":1989},{"id":2021,"depth":205,"text":2022},"2026-09-04","The four-section format language explained: dashes for zero, red parenthesised negatives, thousands scaling with a trailing comma, literal units — all without changing the stored value.","md",[2079,2081,2083,2085],{"q":1995,"a":2080},"Up to four, separated by semicolons: positive, negative, zero and text, in that order. With two sections the second covers negatives and zeros; with one, it covers everything. A missing section is not the same as an empty one — an empty section hides the value.",{"q":2001,"a":2082},"Put a comma after the last digit placeholder: '#,##0,' divides the displayed value by a thousand, and two commas by a million. The stored value is unchanged, which is what makes it safe — the cell still sums correctly.",{"q":2007,"a":2084},"The code is the same in both; the difference is where you set it. openpyxl assigns to cell.number_format per cell, xlsxwriter attaches a Format object to a column or a write call. A column format in xlsxwriter does not apply to cells written individually afterwards.",{"q":2013,"a":2086},"Yes, with a colour name in square brackets at the start of a section — [Red]-#,##0. It is limited to eight named colours and applies to the whole section, which is why conditional formatting is the better tool for anything more nuanced.",{"breadcrumb":2088},[2089,2092,2095],{"name":2090,"item":2091},"Home","\u002F",{"name":2093,"item":2094},"Formatting and Charting Excel Reports with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002F",{"name":17,"item":16},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fwrite-custom-number-format-codes-in-excel-with-python",{"title":5,"description":2098},"Master Excel number format codes from Python — positive, negative, zero and text sections, 0 and # placeholders, thousands scaling, bracketed conditions and column-level application.","write-custom-number-format-codes-in-excel-with-python","formatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fwrite-custom-number-format-codes-in-excel-with-python\u002Findex","how-to","GyaOed-yuBdbnV7gnHm0A2TS4Tcp9mv_aa4IVQL5ohQ",[2104,2107],{"title":2043,"path":2105,"stem":2106,"children":-1},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fformat-numbers-as-percentages-in-excel-with-python","formatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fformat-numbers-as-percentages-in-excel-with-python\u002Findex",{"title":2108,"path":2109,"stem":2110,"children":-1},"Building Excel Reports with xlsxwriter","\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter","formatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Findex",1788710159324]