[{"data":1,"prerenderedAt":1868},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-a-data-table-below-an-excel-chart":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-a-data-table-below-an-excel-chart":1835,"ancestors:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-a-data-table-below-an-excel-chart":1844,"children:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-a-data-table-below-an-excel-chart":1849,"related:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-a-data-table-below-an-excel-chart":1850},{"id":4,"title":5,"body":6,"dateModified":1802,"datePublished":1802,"description":1803,"extension":1804,"faq":1805,"keyword":1814,"kind":1815,"meta":1816,"navigation":119,"path":1825,"seo":1826,"slug":1829,"stem":1830,"topics":1831,"type":1833,"__hash__":1834},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-a-data-table-below-an-excel-chart\u002Findex.md","Add a Data Table Below an Excel Chart",{"type":7,"value":8,"toc":1790},"minimark",[9,24,29,58,61,65,598,632,647,758,762,874,881,1044,1048,1498,1527,1601,1605,1618,1622,1634,1646,1650,1685,1689,1701,1705,1720,1741,1747,1753,1757,1786],[10,11,12,13,17,18,23],"p",{},"A chart shows the shape of the numbers; a table shows the numbers. Excel can do both in one object: a ",[14,15,16],"em",{},"chart data table"," is a small grid drawn under the category axis of a column, bar, line or area chart, with one row per series and one column per category, lined up with the bars above it. Readers who want the exact figure for South in the third quarter read it off the grid instead of guessing from the bar height, and the chart can drop its legend, because the data table's rows carry the series names and colour keys. This page adds a data table to a chart with openpyxl and with XlsxWriter, sets its borders and keys, makes it show the numbers in the right format, and renders both versions to check them. It belongs to ",[19,20,22],"a",{"href":21},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002F","Creating Charts in Excel with openpyxl",".",[25,26,28],"h2",{"id":27},"prerequisites","Prerequisites",[30,31,36],"pre",{"className":32,"code":33,"language":34,"meta":35,"style":35},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl xlsxwriter\n","bash","",[37,38,39],"code",{"__ignoreMap":35},[40,41,44,48,52,55],"span",{"class":42,"line":43},"line",1,[40,45,47],{"class":46},"sMTad","pip",[40,49,51],{"class":50},"srMev"," install",[40,53,54],{"class":50}," openpyxl",[40,56,57],{"class":50}," xlsxwriter\n",[10,59,60],{},"Data tables are a feature of charts with a category axis: column, bar, line and area charts and their combinations. Pie, doughnut, scatter and bubble charts cannot have one. LibreOffice is used at the end of each step to render the chart for checking.",[25,62,64],{"id":63},"step-1-a-data-table-with-openpyxl","Step 1 — A data table with openpyxl",[30,66,70],{"className":67,"code":68,"language":69,"meta":35,"style":35},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\nfrom openpyxl.chart import BarChart, Reference\nfrom openpyxl.chart.plotarea import DataTable\n\nwb = Workbook()\nws = wb.active\nws.title = \"Sales\"\nws.append([\"Quarter\", \"North\", \"South\"])\nfor row in [(\"Q1\", 120_500, 98_200), (\"Q2\", 135_250, 101_900), (\"Q3\", 128_100, 111_400), (\"Q4\", 150_750, 120_300)]:\n    ws.append(list(row))\nfor row in ws.iter_rows(min_row=2, min_col=2, max_col=3):\n    for cell in row:\n        cell.number_format = \"£#,##0\"\n\nchart = BarChart()\nchart.type = \"col\"\nchart.title = \"Sales by quarter\"\nchart.add_data(Reference(ws, min_col=2, max_col=3, min_row=1, max_row=5), titles_from_data=True)\nchart.set_categories(Reference(ws, min_col=1, min_row=2, max_row=5))\nchart.x_axis.delete = chart.y_axis.delete = False\nchart.y_axis.numFmt = \"£#,##0\"\nchart.plot_area.dTable = DataTable(showHorzBorder=True, showVertBorder=True, showOutline=True, showKeys=True)\nchart.legend = None                                   # the table's keys replace the legend\nchart.width, chart.height = 17, 10\nws.add_chart(chart, \"A8\")\nwb.save(\"sales_with_table.xlsx\")\nprint(\"saved\")\n","python",[37,71,72,88,101,114,121,133,144,155,178,256,268,311,325,336,341,352,363,374,427,458,474,484,531,546,562,573,584],{"__ignoreMap":35},[40,73,74,78,82,85],{"class":42,"line":43},[40,75,77],{"class":76},"s-kum","from",[40,79,81],{"class":80},"skGVy"," openpyxl ",[40,83,84],{"class":76},"import",[40,86,87],{"class":80}," Workbook\n",[40,89,91,93,96,98],{"class":42,"line":90},2,[40,92,77],{"class":76},[40,94,95],{"class":80}," openpyxl.chart ",[40,97,84],{"class":76},[40,99,100],{"class":80}," BarChart, Reference\n",[40,102,104,106,109,111],{"class":42,"line":103},3,[40,105,77],{"class":76},[40,107,108],{"class":80}," openpyxl.chart.plotarea ",[40,110,84],{"class":76},[40,112,113],{"class":80}," DataTable\n",[40,115,117],{"class":42,"line":116},4,[40,118,120],{"emptyLinePlaceholder":119},true,"\n",[40,122,124,127,130],{"class":42,"line":123},5,[40,125,126],{"class":80},"wb ",[40,128,129],{"class":76},"=",[40,131,132],{"class":80}," Workbook()\n",[40,134,136,139,141],{"class":42,"line":135},6,[40,137,138],{"class":80},"ws ",[40,140,129],{"class":76},[40,142,143],{"class":80}," wb.active\n",[40,145,147,150,152],{"class":42,"line":146},7,[40,148,149],{"class":80},"ws.title ",[40,151,129],{"class":76},[40,153,154],{"class":50}," \"Sales\"\n",[40,156,158,161,164,167,170,172,175],{"class":42,"line":157},8,[40,159,160],{"class":80},"ws.append([",[40,162,163],{"class":50},"\"Quarter\"",[40,165,166],{"class":80},", ",[40,168,169],{"class":50},"\"North\"",[40,171,166],{"class":80},[40,173,174],{"class":50},"\"South\"",[40,176,177],{"class":80},"])\n",[40,179,181,184,187,190,193,196,198,202,204,207,210,213,215,218,220,223,225,228,230,233,235,238,240,243,245,248,250,253],{"class":42,"line":180},9,[40,182,183],{"class":76},"for",[40,185,186],{"class":80}," row ",[40,188,189],{"class":76},"in",[40,191,192],{"class":80}," [(",[40,194,195],{"class":50},"\"Q1\"",[40,197,166],{"class":80},[40,199,201],{"class":200},"sP0c6","120_500",[40,203,166],{"class":80},[40,205,206],{"class":200},"98_200",[40,208,209],{"class":80},"), (",[40,211,212],{"class":50},"\"Q2\"",[40,214,166],{"class":80},[40,216,217],{"class":200},"135_250",[40,219,166],{"class":80},[40,221,222],{"class":200},"101_900",[40,224,209],{"class":80},[40,226,227],{"class":50},"\"Q3\"",[40,229,166],{"class":80},[40,231,232],{"class":200},"128_100",[40,234,166],{"class":80},[40,236,237],{"class":200},"111_400",[40,239,209],{"class":80},[40,241,242],{"class":50},"\"Q4\"",[40,244,166],{"class":80},[40,246,247],{"class":200},"150_750",[40,249,166],{"class":80},[40,251,252],{"class":200},"120_300",[40,254,255],{"class":80},")]:\n",[40,257,259,262,265],{"class":42,"line":258},10,[40,260,261],{"class":80},"    ws.append(",[40,263,264],{"class":200},"list",[40,266,267],{"class":80},"(row))\n",[40,269,271,273,275,277,280,284,286,289,291,294,296,298,300,303,305,308],{"class":42,"line":270},11,[40,272,183],{"class":76},[40,274,186],{"class":80},[40,276,189],{"class":76},[40,278,279],{"class":80}," ws.iter_rows(",[40,281,283],{"class":282},"sa561","min_row",[40,285,129],{"class":76},[40,287,288],{"class":200},"2",[40,290,166],{"class":80},[40,292,293],{"class":282},"min_col",[40,295,129],{"class":76},[40,297,288],{"class":200},[40,299,166],{"class":80},[40,301,302],{"class":282},"max_col",[40,304,129],{"class":76},[40,306,307],{"class":200},"3",[40,309,310],{"class":80},"):\n",[40,312,314,317,320,322],{"class":42,"line":313},12,[40,315,316],{"class":76},"    for",[40,318,319],{"class":80}," cell ",[40,321,189],{"class":76},[40,323,324],{"class":80}," row:\n",[40,326,328,331,333],{"class":42,"line":327},13,[40,329,330],{"class":80},"        cell.number_format ",[40,332,129],{"class":76},[40,334,335],{"class":50}," \"£#,##0\"\n",[40,337,339],{"class":42,"line":338},14,[40,340,120],{"emptyLinePlaceholder":119},[40,342,344,347,349],{"class":42,"line":343},15,[40,345,346],{"class":80},"chart ",[40,348,129],{"class":76},[40,350,351],{"class":80}," BarChart()\n",[40,353,355,358,360],{"class":42,"line":354},16,[40,356,357],{"class":80},"chart.type ",[40,359,129],{"class":76},[40,361,362],{"class":50}," \"col\"\n",[40,364,366,369,371],{"class":42,"line":365},17,[40,367,368],{"class":80},"chart.title ",[40,370,129],{"class":76},[40,372,373],{"class":50}," \"Sales by quarter\"\n",[40,375,377,380,382,384,386,388,390,392,394,396,398,400,403,405,408,410,413,416,419,421,424],{"class":42,"line":376},18,[40,378,379],{"class":80},"chart.add_data(Reference(ws, ",[40,381,293],{"class":282},[40,383,129],{"class":76},[40,385,288],{"class":200},[40,387,166],{"class":80},[40,389,302],{"class":282},[40,391,129],{"class":76},[40,393,307],{"class":200},[40,395,166],{"class":80},[40,397,283],{"class":282},[40,399,129],{"class":76},[40,401,402],{"class":200},"1",[40,404,166],{"class":80},[40,406,407],{"class":282},"max_row",[40,409,129],{"class":76},[40,411,412],{"class":200},"5",[40,414,415],{"class":80},"), ",[40,417,418],{"class":282},"titles_from_data",[40,420,129],{"class":76},[40,422,423],{"class":200},"True",[40,425,426],{"class":80},")\n",[40,428,430,433,435,437,439,441,443,445,447,449,451,453,455],{"class":42,"line":429},19,[40,431,432],{"class":80},"chart.set_categories(Reference(ws, ",[40,434,293],{"class":282},[40,436,129],{"class":76},[40,438,402],{"class":200},[40,440,166],{"class":80},[40,442,283],{"class":282},[40,444,129],{"class":76},[40,446,288],{"class":200},[40,448,166],{"class":80},[40,450,407],{"class":282},[40,452,129],{"class":76},[40,454,412],{"class":200},[40,456,457],{"class":80},"))\n",[40,459,461,464,466,469,471],{"class":42,"line":460},20,[40,462,463],{"class":80},"chart.x_axis.delete ",[40,465,129],{"class":76},[40,467,468],{"class":80}," chart.y_axis.delete ",[40,470,129],{"class":76},[40,472,473],{"class":200}," False\n",[40,475,477,480,482],{"class":42,"line":476},21,[40,478,479],{"class":80},"chart.y_axis.numFmt ",[40,481,129],{"class":76},[40,483,335],{"class":50},[40,485,487,490,492,495,498,500,502,504,507,509,511,513,516,518,520,522,525,527,529],{"class":42,"line":486},22,[40,488,489],{"class":80},"chart.plot_area.dTable ",[40,491,129],{"class":76},[40,493,494],{"class":80}," DataTable(",[40,496,497],{"class":282},"showHorzBorder",[40,499,129],{"class":76},[40,501,423],{"class":200},[40,503,166],{"class":80},[40,505,506],{"class":282},"showVertBorder",[40,508,129],{"class":76},[40,510,423],{"class":200},[40,512,166],{"class":80},[40,514,515],{"class":282},"showOutline",[40,517,129],{"class":76},[40,519,423],{"class":200},[40,521,166],{"class":80},[40,523,524],{"class":282},"showKeys",[40,526,129],{"class":76},[40,528,423],{"class":200},[40,530,426],{"class":80},[40,532,534,537,539,542],{"class":42,"line":533},23,[40,535,536],{"class":80},"chart.legend ",[40,538,129],{"class":76},[40,540,541],{"class":200}," None",[40,543,545],{"class":544},"s-wDw","                                   # the table's keys replace the legend\n",[40,547,549,552,554,557,559],{"class":42,"line":548},24,[40,550,551],{"class":80},"chart.width, chart.height ",[40,553,129],{"class":76},[40,555,556],{"class":200}," 17",[40,558,166],{"class":80},[40,560,561],{"class":200},"10\n",[40,563,565,568,571],{"class":42,"line":564},25,[40,566,567],{"class":80},"ws.add_chart(chart, ",[40,569,570],{"class":50},"\"A8\"",[40,572,426],{"class":80},[40,574,576,579,582],{"class":42,"line":575},26,[40,577,578],{"class":80},"wb.save(",[40,580,581],{"class":50},"\"sales_with_table.xlsx\"",[40,583,426],{"class":80},[40,585,587,590,593,596],{"class":42,"line":586},27,[40,588,589],{"class":200},"print",[40,591,592],{"class":80},"(",[40,594,595],{"class":50},"\"saved\"",[40,597,426],{"class":80},[10,599,600,601,604,605,608,609,612,613,615,616,618,619,621,622,624,625,608,628,631],{},"In openpyxl the data table belongs to the chart's ",[14,602,603],{},"plot area",": assigning a ",[37,606,607],{},"DataTable"," to ",[37,610,611],{},"chart.plot_area.dTable"," switches it on. Its four flags control the look. ",[37,614,497],{}," and ",[37,617,506],{}," draw the lines between rows and between columns, ",[37,620,515],{}," draws the border around the whole table, and ",[37,623,524],{}," puts a small colour swatch next to each series name — the same swatch a legend would show. With keys on, the legend is redundant, so it is removed by setting ",[37,626,627],{},"chart.legend",[37,629,630],{},"None",", which also gives the plot area the width the legend used to take.",[10,633,634,635,638,639,642,643,646],{},"The values in the table come straight from the cells the series refer to, ",[14,636,637],{},"with their number formats",". Formatting the source cells as ",[37,640,641],{},"£#,##0"," is therefore what makes the table show £135,250 instead of 135250. The value axis does not inherit the cell format automatically, so it gets the same format through ",[37,644,645],{},"numFmt",", and both now read in pounds.",[648,649,657,658,657,662,657,666,657,674,657,684,657,691,657,696,657,701,657,706,657,710,657,715,657,719,657,723,657,726,657,729,657,733,657,737,657,741,657,744,657,749,657,754],"svg",{"viewBox":650,"role":651,"ariaLabelledBy":652,"xmlns":655,"style":656},"84 0 592 376","img",[653,654],"dt-parts-t","dt-parts-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:592px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[659,660,661],"title",{"id":653},"What a chart data table adds",[663,664,665],"desc",{"id":654},"The plot area holds the columns. Directly below the category axis, the data table shows one row per series and one column per category, with a colour key beside each series name, so the legend can be removed.",[667,668],"rect",{"x":669,"y":670,"width":671,"height":672,"fill":673},"84","0","592","376","#ffffff",[667,675],{"x":676,"y":677,"width":678,"height":679,"rx":680,"fill":681,"stroke":682,"style":683},"100.0","16.0","560.0","59.0","12","#e7ebef","var(--line,#cdd5e6)","stroke-width:2px",[685,686,690],"text",{"x":687,"y":688,"style":689},"380.0","41.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Title",[685,692,695],{"x":687,"y":693,"style":694},"61.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","chart title",[42,697],{"x1":687,"y1":698,"x2":687,"y2":699,"stroke":700,"style":683},"80.0","102.0","var(--brand,#5b5cf0)",[702,703],"polygon",{"points":704,"fill":705},"380.0,106.0 375.0,97.0 385.0,97.0","#5b5cf0",[667,707],{"x":676,"y":708,"width":678,"height":679,"rx":680,"fill":709,"stroke":700,"style":683},"111.0","#f0f4ff",[685,711,714],{"x":687,"y":712,"style":713},"136.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Plot area",[685,716,718],{"x":687,"y":717,"style":694},"156.0","columns per quarter",[42,720],{"x1":687,"y1":721,"x2":687,"y2":722,"stroke":700,"style":683},"175.0","197.0",[702,724],{"points":725,"fill":705},"380.0,201.0 375.0,192.0 385.0,192.0",[667,727],{"x":676,"y":728,"width":678,"height":679,"rx":680,"fill":681,"stroke":682,"style":683},"206.0",[685,730,732],{"x":687,"y":731,"style":689},"231.5","Category axis",[685,734,736],{"x":687,"y":735,"style":694},"251.0","Q1 … Q4",[42,738],{"x1":687,"y1":739,"x2":687,"y2":740,"stroke":700,"style":683},"270.0","292.0",[702,742],{"points":743,"fill":705},"380.0,296.0 375.0,287.0 385.0,287.0",[667,745],{"x":676,"y":746,"width":678,"height":679,"rx":680,"fill":747,"stroke":748,"style":683},"301.0","#d9f4f1","var(--teal,#0f9488)",[685,750,753],{"x":687,"y":751,"style":752},"326.5","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Data table",[685,755,757],{"x":687,"y":756,"style":694},"346.0","one row per series, keys",[25,759,761],{"id":760},"step-2-render-and-check","Step 2 — Render and check",[30,763,765],{"className":32,"code":764,"language":34,"meta":35,"style":35},"soffice --headless --convert-to pdf sales_with_table.xlsx > \u002Fdev\u002Fnull 2>&1\npdftoppm -r 70 -f 1 -l 1 -png sales_with_table.pdf preview && ls preview*\npython - \u003C\u003C'EOF'\nimport zipfile, re\nxml = zipfile.ZipFile(\"sales_with_table.xlsx\").read(\"xl\u002Fcharts\u002Fchart1.xml\").decode()\nprint(re.search(r\"\u003CdTable>.*?\u003C\u002FdTable>\", xml).group())\nprint(\"legend present:\", \"\u003Clegend>\" in xml)\nEOF\n",[37,766,767,793,835,849,854,859,864,869],{"__ignoreMap":35},[40,768,769,772,775,778,781,784,787,790],{"class":42,"line":43},[40,770,771],{"class":46},"soffice",[40,773,774],{"class":200}," --headless",[40,776,777],{"class":200}," --convert-to",[40,779,780],{"class":50}," pdf",[40,782,783],{"class":50}," sales_with_table.xlsx",[40,785,786],{"class":76}," >",[40,788,789],{"class":50}," \u002Fdev\u002Fnull",[40,791,792],{"class":76}," 2>&1\n",[40,794,795,798,801,804,807,810,813,815,818,821,824,827,830,832],{"class":42,"line":90},[40,796,797],{"class":46},"pdftoppm",[40,799,800],{"class":200}," -r",[40,802,803],{"class":200}," 70",[40,805,806],{"class":200}," -f",[40,808,809],{"class":200}," 1",[40,811,812],{"class":200}," -l",[40,814,809],{"class":200},[40,816,817],{"class":200}," -png",[40,819,820],{"class":50}," sales_with_table.pdf",[40,822,823],{"class":50}," preview",[40,825,826],{"class":80}," && ",[40,828,829],{"class":46},"ls",[40,831,823],{"class":50},[40,833,834],{"class":200},"*\n",[40,836,837,839,842,845],{"class":42,"line":103},[40,838,69],{"class":46},[40,840,841],{"class":50}," -",[40,843,844],{"class":76}," \u003C\u003C",[40,846,848],{"class":847},"sXKb-","'EOF'\n",[40,850,851],{"class":42,"line":116},[40,852,853],{"class":50},"import zipfile, re\n",[40,855,856],{"class":42,"line":123},[40,857,858],{"class":50},"xml = zipfile.ZipFile(\"sales_with_table.xlsx\").read(\"xl\u002Fcharts\u002Fchart1.xml\").decode()\n",[40,860,861],{"class":42,"line":135},[40,862,863],{"class":50},"print(re.search(r\"\u003CdTable>.*?\u003C\u002FdTable>\", xml).group())\n",[40,865,866],{"class":42,"line":146},[40,867,868],{"class":50},"print(\"legend present:\", \"\u003Clegend>\" in xml)\n",[40,870,871],{"class":42,"line":157},[40,872,873],{"class":847},"EOF\n",[10,875,876,877,880],{},"Rendering the workbook shows the grid under the axis: a row for North and a row for South, each with its colour key, and the four quarters' values in pounds aligned under their columns. The chart XML confirms what was written — a ",[37,878,879],{},"dTable"," element with the four flags set and no legend element. Reading the XML is a quick, dependable test for a chart option that has no other visible trace in openpyxl once the file is saved.",[648,882,657,888,657,891,657,894,657,899,657,905,657,909,657,915,657,919,657,923,657,927,657,931,657,935,657,939,657,943,657,947,657,951,657,954,657,957,657,959,657,963,657,965,657,968,657,970,657,973,657,975,657,978,657,981,657,984,657,986,657,989,657,991,657,995,657,997,657,1000,657,1002,657,1005,657,1007,657,1010,657,1013,657,1016,657,1020,657,1024,657,1026,657,1029,657,1031,657,1034,657,1036,657,1039,657,1041],{"viewBox":883,"role":651,"ariaLabelledBy":884,"xmlns":655,"style":887},"148 0 464 140",[885,886],"dt-render-t","dt-render-d","width:100%;max-width:464px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[659,889,890],{"id":885},"The data table as rendered",[663,892,893],{"id":886},"Under the four quarters the table shows North at 120,500, 135,250, 128,100 and 150,750 pounds and South at 98,200, 101,900, 111,400 and 120,300 pounds, each row with its colour key.",[667,895],{"x":896,"y":670,"width":897,"height":898,"fill":673},"148","464","140",[667,900],{"x":901,"y":677,"width":902,"height":903,"rx":670,"fill":681,"stroke":682,"style":904},"164.4","32.0","24.0","stroke-width:1px",[667,906],{"x":907,"y":677,"width":908,"height":903,"rx":670,"fill":681,"stroke":682,"style":904},"196.4","70.8",[685,910,914],{"x":911,"y":912,"style":913},"231.8","33.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[667,916],{"x":917,"y":677,"width":918,"height":903,"rx":670,"fill":681,"stroke":682,"style":904},"267.2","82.9",[685,920,922],{"x":921,"y":912,"style":913},"308.7","B",[667,924],{"x":925,"y":677,"width":926,"height":903,"rx":670,"fill":681,"stroke":682,"style":904},"350.2","81.9",[685,928,930],{"x":929,"y":912,"style":913},"391.1","C",[667,932],{"x":933,"y":677,"width":934,"height":903,"rx":670,"fill":681,"stroke":682,"style":904},"432.1","80.2",[685,936,938],{"x":937,"y":912,"style":913},"472.2","D",[667,940],{"x":941,"y":677,"width":942,"height":903,"rx":670,"fill":681,"stroke":682,"style":904},"512.4","83.2",[685,944,946],{"x":945,"y":912,"style":913},"554.0","E",[667,948],{"x":901,"y":949,"width":902,"height":950,"rx":670,"fill":681,"stroke":682,"style":904},"40.0","28.0",[685,952,402],{"x":953,"y":679,"style":913},"180.4",[667,955],{"x":907,"y":949,"width":908,"height":950,"rx":670,"fill":709,"stroke":700,"style":956},"stroke-width:1.5px",[667,958],{"x":917,"y":949,"width":918,"height":950,"rx":670,"fill":709,"stroke":700,"style":956},[685,960,962],{"x":921,"y":679,"style":961},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Q1",[667,964],{"x":925,"y":949,"width":926,"height":950,"rx":670,"fill":709,"stroke":700,"style":956},[685,966,967],{"x":929,"y":679,"style":961},"Q2",[667,969],{"x":933,"y":949,"width":934,"height":950,"rx":670,"fill":709,"stroke":700,"style":956},[685,971,972],{"x":937,"y":679,"style":961},"Q3",[667,974],{"x":941,"y":949,"width":942,"height":950,"rx":670,"fill":709,"stroke":700,"style":956},[685,976,977],{"x":945,"y":679,"style":961},"Q4",[667,979],{"x":901,"y":980,"width":902,"height":950,"rx":670,"fill":681,"stroke":682,"style":904},"68.0",[685,982,288],{"x":953,"y":983,"style":913},"87.0",[667,985],{"x":907,"y":980,"width":908,"height":950,"rx":670,"fill":709,"stroke":700,"style":956},[685,987,988],{"x":911,"y":983,"style":961},"■ North",[667,990],{"x":917,"y":980,"width":918,"height":950,"rx":670,"fill":673,"stroke":682,"style":904},[685,992,994],{"x":921,"y":983,"style":993},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","£120,500",[667,996],{"x":925,"y":980,"width":926,"height":950,"rx":670,"fill":673,"stroke":682,"style":904},[685,998,999],{"x":929,"y":983,"style":993},"£135,250",[667,1001],{"x":933,"y":980,"width":934,"height":950,"rx":670,"fill":673,"stroke":682,"style":904},[685,1003,1004],{"x":937,"y":983,"style":993},"£128,100",[667,1006],{"x":941,"y":980,"width":942,"height":950,"rx":670,"fill":673,"stroke":682,"style":904},[685,1008,1009],{"x":945,"y":983,"style":993},"£150,750",[667,1011],{"x":901,"y":1012,"width":902,"height":950,"rx":670,"fill":681,"stroke":682,"style":904},"96.0",[685,1014,307],{"x":953,"y":1015,"style":913},"115.0",[667,1017],{"x":907,"y":1012,"width":908,"height":950,"rx":670,"fill":1018,"stroke":1019,"style":956},"#fce9e9","var(--danger,#dc2626)",[685,1021,1023],{"x":911,"y":1015,"style":1022},"font-size:12px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","■ South",[667,1025],{"x":917,"y":1012,"width":918,"height":950,"rx":670,"fill":673,"stroke":682,"style":904},[685,1027,1028],{"x":921,"y":1015,"style":993},"£98,200",[667,1030],{"x":925,"y":1012,"width":926,"height":950,"rx":670,"fill":673,"stroke":682,"style":904},[685,1032,1033],{"x":929,"y":1015,"style":993},"£101,900",[667,1035],{"x":933,"y":1012,"width":934,"height":950,"rx":670,"fill":673,"stroke":682,"style":904},[685,1037,1038],{"x":937,"y":1015,"style":993},"£111,400",[667,1040],{"x":941,"y":1012,"width":942,"height":950,"rx":670,"fill":673,"stroke":682,"style":904},[685,1042,1043],{"x":945,"y":1015,"style":993},"£120,300",[25,1045,1047],{"id":1046},"step-3-the-same-with-xlsxwriter","Step 3 — The same with XlsxWriter",[30,1049,1051],{"className":67,"code":1050,"language":69,"meta":35,"style":35},"import xlsxwriter\n\nwb = xlsxwriter.Workbook(\"sales_with_table_xw.xlsx\")\nws = wb.add_worksheet(\"Sales\")\nmoney = wb.add_format({\"num_format\": \"£#,##0\"})\nws.write_row(\"A1\", [\"Quarter\", \"North\", \"South\"])\nfor i, (q, n, s) in enumerate([(\"Q1\", 120_500, 98_200), (\"Q2\", 135_250, 101_900), (\"Q3\", 128_100, 111_400), (\"Q4\", 150_750, 120_300)], start=1):\n    ws.write(i, 0, q)\n    ws.write(i, 1, n, money)\n    ws.write(i, 2, s, money)\n\nchart = wb.add_chart({\"type\": \"column\"})\nfor col in (1, 2):\n    chart.add_series({\"name\": [\"Sales\", 0, col], \"categories\": [\"Sales\", 1, 0, 4, 0], \"values\": [\"Sales\", 1, col, 4, col]})\nchart.set_title({\"name\": \"Sales by quarter\"})\nchart.set_table({\"show_keys\": True, \"horizontal\": True, \"vertical\": True, \"outline\": True,\n                 \"font\": {\"size\": 9}})\nchart.set_legend({\"none\": True})\nchart.set_y_axis({\"num_format\": \"£#,##0\"})\nchart.set_size({\"width\": 640, \"height\": 380})\nws.insert_chart(\"A8\", chart)\nwb.close()\nprint(\"saved\")\n",[37,1052,1053,1059,1063,1077,1091,1113,1136,1209,1219,1228,1237,1241,1260,1280,1346,1360,1402,1421,1435,1448,1473,1483,1488],{"__ignoreMap":35},[40,1054,1055,1057],{"class":42,"line":43},[40,1056,84],{"class":76},[40,1058,57],{"class":80},[40,1060,1061],{"class":42,"line":90},[40,1062,120],{"emptyLinePlaceholder":119},[40,1064,1065,1067,1069,1072,1075],{"class":42,"line":103},[40,1066,126],{"class":80},[40,1068,129],{"class":76},[40,1070,1071],{"class":80}," xlsxwriter.Workbook(",[40,1073,1074],{"class":50},"\"sales_with_table_xw.xlsx\"",[40,1076,426],{"class":80},[40,1078,1079,1081,1083,1086,1089],{"class":42,"line":116},[40,1080,138],{"class":80},[40,1082,129],{"class":76},[40,1084,1085],{"class":80}," wb.add_worksheet(",[40,1087,1088],{"class":50},"\"Sales\"",[40,1090,426],{"class":80},[40,1092,1093,1096,1098,1101,1104,1107,1110],{"class":42,"line":123},[40,1094,1095],{"class":80},"money ",[40,1097,129],{"class":76},[40,1099,1100],{"class":80}," wb.add_format({",[40,1102,1103],{"class":50},"\"num_format\"",[40,1105,1106],{"class":80},": ",[40,1108,1109],{"class":50},"\"£#,##0\"",[40,1111,1112],{"class":80},"})\n",[40,1114,1115,1118,1121,1124,1126,1128,1130,1132,1134],{"class":42,"line":135},[40,1116,1117],{"class":80},"ws.write_row(",[40,1119,1120],{"class":50},"\"A1\"",[40,1122,1123],{"class":80},", [",[40,1125,163],{"class":50},[40,1127,166],{"class":80},[40,1129,169],{"class":50},[40,1131,166],{"class":80},[40,1133,174],{"class":50},[40,1135,177],{"class":80},[40,1137,1138,1140,1143,1145,1148,1151,1153,1155,1157,1159,1161,1163,1165,1167,1169,1171,1173,1175,1177,1179,1181,1183,1185,1187,1189,1191,1193,1195,1197,1200,1203,1205,1207],{"class":42,"line":146},[40,1139,183],{"class":76},[40,1141,1142],{"class":80}," i, (q, n, s) ",[40,1144,189],{"class":76},[40,1146,1147],{"class":200}," enumerate",[40,1149,1150],{"class":80},"([(",[40,1152,195],{"class":50},[40,1154,166],{"class":80},[40,1156,201],{"class":200},[40,1158,166],{"class":80},[40,1160,206],{"class":200},[40,1162,209],{"class":80},[40,1164,212],{"class":50},[40,1166,166],{"class":80},[40,1168,217],{"class":200},[40,1170,166],{"class":80},[40,1172,222],{"class":200},[40,1174,209],{"class":80},[40,1176,227],{"class":50},[40,1178,166],{"class":80},[40,1180,232],{"class":200},[40,1182,166],{"class":80},[40,1184,237],{"class":200},[40,1186,209],{"class":80},[40,1188,242],{"class":50},[40,1190,166],{"class":80},[40,1192,247],{"class":200},[40,1194,166],{"class":80},[40,1196,252],{"class":200},[40,1198,1199],{"class":80},")], ",[40,1201,1202],{"class":282},"start",[40,1204,129],{"class":76},[40,1206,402],{"class":200},[40,1208,310],{"class":80},[40,1210,1211,1214,1216],{"class":42,"line":157},[40,1212,1213],{"class":80},"    ws.write(i, ",[40,1215,670],{"class":200},[40,1217,1218],{"class":80},", q)\n",[40,1220,1221,1223,1225],{"class":42,"line":180},[40,1222,1213],{"class":80},[40,1224,402],{"class":200},[40,1226,1227],{"class":80},", n, money)\n",[40,1229,1230,1232,1234],{"class":42,"line":258},[40,1231,1213],{"class":80},[40,1233,288],{"class":200},[40,1235,1236],{"class":80},", s, money)\n",[40,1238,1239],{"class":42,"line":270},[40,1240,120],{"emptyLinePlaceholder":119},[40,1242,1243,1245,1247,1250,1253,1255,1258],{"class":42,"line":313},[40,1244,346],{"class":80},[40,1246,129],{"class":76},[40,1248,1249],{"class":80}," wb.add_chart({",[40,1251,1252],{"class":50},"\"type\"",[40,1254,1106],{"class":80},[40,1256,1257],{"class":50},"\"column\"",[40,1259,1112],{"class":80},[40,1261,1262,1264,1267,1269,1272,1274,1276,1278],{"class":42,"line":327},[40,1263,183],{"class":76},[40,1265,1266],{"class":80}," col ",[40,1268,189],{"class":76},[40,1270,1271],{"class":80}," (",[40,1273,402],{"class":200},[40,1275,166],{"class":80},[40,1277,288],{"class":200},[40,1279,310],{"class":80},[40,1281,1282,1285,1288,1291,1293,1295,1297,1300,1303,1305,1307,1309,1311,1313,1315,1317,1320,1322,1324,1327,1330,1332,1334,1336,1338,1341,1343],{"class":42,"line":338},[40,1283,1284],{"class":80},"    chart.add_series({",[40,1286,1287],{"class":50},"\"name\"",[40,1289,1290],{"class":80},": [",[40,1292,1088],{"class":50},[40,1294,166],{"class":80},[40,1296,670],{"class":200},[40,1298,1299],{"class":80},", col], ",[40,1301,1302],{"class":50},"\"categories\"",[40,1304,1290],{"class":80},[40,1306,1088],{"class":50},[40,1308,166],{"class":80},[40,1310,402],{"class":200},[40,1312,166],{"class":80},[40,1314,670],{"class":200},[40,1316,166],{"class":80},[40,1318,1319],{"class":200},"4",[40,1321,166],{"class":80},[40,1323,670],{"class":200},[40,1325,1326],{"class":80},"], ",[40,1328,1329],{"class":50},"\"values\"",[40,1331,1290],{"class":80},[40,1333,1088],{"class":50},[40,1335,166],{"class":80},[40,1337,402],{"class":200},[40,1339,1340],{"class":80},", col, ",[40,1342,1319],{"class":200},[40,1344,1345],{"class":80},", col]})\n",[40,1347,1348,1351,1353,1355,1358],{"class":42,"line":343},[40,1349,1350],{"class":80},"chart.set_title({",[40,1352,1287],{"class":50},[40,1354,1106],{"class":80},[40,1356,1357],{"class":50},"\"Sales by quarter\"",[40,1359,1112],{"class":80},[40,1361,1362,1365,1368,1370,1372,1374,1377,1379,1381,1383,1386,1388,1390,1392,1395,1397,1399],{"class":42,"line":354},[40,1363,1364],{"class":80},"chart.set_table({",[40,1366,1367],{"class":50},"\"show_keys\"",[40,1369,1106],{"class":80},[40,1371,423],{"class":200},[40,1373,166],{"class":80},[40,1375,1376],{"class":50},"\"horizontal\"",[40,1378,1106],{"class":80},[40,1380,423],{"class":200},[40,1382,166],{"class":80},[40,1384,1385],{"class":50},"\"vertical\"",[40,1387,1106],{"class":80},[40,1389,423],{"class":200},[40,1391,166],{"class":80},[40,1393,1394],{"class":50},"\"outline\"",[40,1396,1106],{"class":80},[40,1398,423],{"class":200},[40,1400,1401],{"class":80},",\n",[40,1403,1404,1407,1410,1413,1415,1418],{"class":42,"line":365},[40,1405,1406],{"class":50},"                 \"font\"",[40,1408,1409],{"class":80},": {",[40,1411,1412],{"class":50},"\"size\"",[40,1414,1106],{"class":80},[40,1416,1417],{"class":200},"9",[40,1419,1420],{"class":80},"}})\n",[40,1422,1423,1426,1429,1431,1433],{"class":42,"line":376},[40,1424,1425],{"class":80},"chart.set_legend({",[40,1427,1428],{"class":50},"\"none\"",[40,1430,1106],{"class":80},[40,1432,423],{"class":200},[40,1434,1112],{"class":80},[40,1436,1437,1440,1442,1444,1446],{"class":42,"line":429},[40,1438,1439],{"class":80},"chart.set_y_axis({",[40,1441,1103],{"class":50},[40,1443,1106],{"class":80},[40,1445,1109],{"class":50},[40,1447,1112],{"class":80},[40,1449,1450,1453,1456,1458,1461,1463,1466,1468,1471],{"class":42,"line":460},[40,1451,1452],{"class":80},"chart.set_size({",[40,1454,1455],{"class":50},"\"width\"",[40,1457,1106],{"class":80},[40,1459,1460],{"class":200},"640",[40,1462,166],{"class":80},[40,1464,1465],{"class":50},"\"height\"",[40,1467,1106],{"class":80},[40,1469,1470],{"class":200},"380",[40,1472,1112],{"class":80},[40,1474,1475,1478,1480],{"class":42,"line":476},[40,1476,1477],{"class":80},"ws.insert_chart(",[40,1479,570],{"class":50},[40,1481,1482],{"class":80},", chart)\n",[40,1484,1485],{"class":42,"line":486},[40,1486,1487],{"class":80},"wb.close()\n",[40,1489,1490,1492,1494,1496],{"class":42,"line":533},[40,1491,589],{"class":200},[40,1493,592],{"class":80},[40,1495,595],{"class":50},[40,1497,426],{"class":80},[10,1499,1500,1501,1504,1505,166,1508,615,1511,1514,1515,1518,1519,1522,1523,1526],{},"XlsxWriter calls it ",[37,1502,1503],{},"set_table",", with options named after what they do: ",[37,1506,1507],{},"horizontal",[37,1509,1510],{},"vertical",[37,1512,1513],{},"outline"," for the three kinds of border, ",[37,1516,1517],{},"show_keys"," for the colour swatches, and a ",[37,1520,1521],{},"font"," dictionary for the table's text, which is useful because the default size can be large on a small chart. The legend is switched off with ",[37,1524,1525],{},"set_legend({\"none\": True})",". Everything else — series, categories, axis formats — is unchanged from a chart without a table. The XlsxWriter version renders the same way, with the pound values coming from the cell formats. In both libraries the table is set when the chart is created. Neither can add one to a chart that already exists in a workbook: XlsxWriter writes new files only, and openpyxl does not keep existing charts when it loads a file, so the chart has to be regenerated with the table included.",[648,1528,657,1534,657,1537,657,1540,657,1544,657,1550,657,1556,657,1561,657,1565,657,1569,657,1573,657,1577,657,1580,657,1585,657,1589,657,1592,657,1595,657,1598],{"viewBox":1529,"role":651,"ariaLabelledBy":1530,"xmlns":655,"style":1533},"4 0 752 199",[1531,1532],"dt-libs-t","dt-libs-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[659,1535,1536],{"id":1531},"The option in each library",[663,1538,1539],{"id":1532},"In openpyxl a DataTable object is assigned to the chart's plot area, with showHorzBorder, showVertBorder, showOutline and showKeys flags. In XlsxWriter the chart's set_table method takes horizontal, vertical, outline, show_keys and font options.",[667,1541],{"x":1319,"y":670,"width":1542,"height":1543,"fill":673},"752","199",[667,1545],{"x":1546,"y":677,"width":1547,"height":1548,"rx":1549,"fill":709,"stroke":700,"style":683},"20.0","351.0","167.0","14",[685,1551,1555],{"x":1552,"y":1553,"style":1554},"195.5","44.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","openpyxl",[42,1557],{"x1":1558,"y1":1559,"x2":1560,"y2":1559,"stroke":700,"style":904},"36.0","57.0","355.0",[685,1562,1564],{"x":1552,"y":1563,"style":993},"77.0","chart.plot_area.dTable = DataTable(...)",[685,1566,1568],{"x":1552,"y":1567,"style":993},"103.0","showHorzBorder, showVertBorder",[685,1570,1572],{"x":1552,"y":1571,"style":993},"129.0","showOutline, showKeys",[685,1574,1576],{"x":1552,"y":1575,"style":993},"155.0","chart.legend = None",[667,1578],{"x":1579,"y":677,"width":1547,"height":1548,"rx":1549,"fill":747,"stroke":748,"style":683},"389.0",[685,1581,1584],{"x":1582,"y":1553,"style":1583},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","XlsxWriter",[42,1586],{"x1":1587,"y1":1559,"x2":1588,"y2":1559,"stroke":748,"style":904},"405.0","724.0",[685,1590,1591],{"x":1582,"y":1563,"style":993},"chart.set_table({...})",[685,1593,1594],{"x":1582,"y":1567,"style":993},"horizontal, vertical, outline",[685,1596,1597],{"x":1582,"y":1571,"style":993},"show_keys, font",[685,1599,1600],{"x":1582,"y":1575,"style":993},"set_legend({'none': True})",[25,1602,1604],{"id":1603},"fitting-the-table-into-the-chart","Fitting the table into the chart",[10,1606,1607,1608,1610,1611,1614,1615,1617],{},"The data table takes its space from the plot area, so a chart that looked well proportioned without one can end up with squat columns once the table is added. Three adjustments help. Make the chart taller — two series add roughly two rows of text, so an extra centimetre or two of height in openpyxl, or forty to sixty pixels in XlsxWriter, keeps the columns readable. Shorten the category labels, because they head the table's columns as well as labelling the axis: ",[14,1609,962],{}," fits where ",[14,1612,1613],{},"First quarter 2026"," would wrap. And keep the table's font a size or two smaller than the axis labels, which XlsxWriter's ",[37,1616,1521],{}," option makes easy and which in openpyxl comes from the chart's text properties. For line charts the table works the same way, with each value sitting under the point it belongs to, which makes it particularly clear for monthly trends of one or two measures.",[25,1619,1621],{"id":1620},"when-a-data-table-is-the-right-choice","When a data table is the right choice",[10,1623,1624,1625,1628,1629,1633],{},"A data table works best on charts with a handful of series and up to about a dozen categories — quarterly figures for two or three regions, monthly figures for one or two measures. Every additional series adds a row and squeezes the plot area; every additional category makes the columns of the table narrower until the numbers no longer fit and Excel starts wrapping or shrinking them. For a single series, ",[14,1626,1627],{},"data labels"," on the bars are usually neater, because the number sits right on the bar it describes; see ",[19,1630,1632],{"href":1631},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Flabel-chart-points-from-cell-values-with-xlsxwriter\u002F","Label Chart Points from Cell Values with XlsxWriter",". For many series or long periods, a separate formatted table next to the chart is more readable than squeezing everything under the axis.",[10,1635,1636,1637,1640,1641,1645],{},"Data tables also suit reports that are printed or turned into PDFs. A reader of a printed page cannot hover over a bar to see its value, and a table under the chart answers the question without sending them to another sheet. Keep the number format short — thousands with a ",[14,1638,1639],{},"k",", or no decimals — so the values fit their columns; the techniques in ",[19,1642,1644],{"href":1643},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fformat-numbers-in-thousands-and-millions-in-excel\u002F","Format Numbers in Thousands and Millions in Excel"," apply directly, because the table uses the cells' formats.",[25,1647,1649],{"id":1648},"common-pitfalls","Common pitfalls",[1651,1652,1653,1661,1667,1673,1679],"ul",{},[1654,1655,1656,1660],"li",{},[1657,1658,1659],"strong",{},"Unformatted source cells."," The table shows the cells' number formats; format them first.",[1654,1662,1663,1666],{},[1657,1664,1665],{},"A legend as well as keys."," Remove the legend when the table shows keys.",[1654,1668,1669,1672],{},[1657,1670,1671],{},"Pie or scatter charts."," Data tables need a category axis.",[1654,1674,1675,1678],{},[1657,1676,1677],{},"Too many categories."," The columns get too narrow; use data labels or a separate table.",[1654,1680,1681,1684],{},[1657,1682,1683],{},"Axis format forgotten."," The value axis does not take the cell format automatically.",[25,1686,1688],{"id":1687},"conclusion","Conclusion",[10,1690,1691,1692,608,1695,1697,1698,1700],{},"To add a data table below an Excel chart, assign ",[37,1693,1694],{},"DataTable(showHorzBorder=True, showVertBorder=True, showOutline=True, showKeys=True)",[37,1696,611],{}," in openpyxl, or call ",[37,1699,1591],{}," in XlsxWriter. Remove the legend when the table shows keys, format the source cells so the table shows the right number format, set the axis format to match, and render the file once to check the layout.",[25,1702,1704],{"id":1703},"frequently-asked-questions","Frequently asked questions",[10,1706,1707,1710,1711,1713,1714,1717,1718,23],{},[1657,1708,1709],{},"How do I add a data table to a chart with openpyxl?","\nImport ",[37,1712,607],{}," from ",[37,1715,1716],{},"openpyxl.chart.plotarea"," and assign an instance to ",[37,1719,611],{},[10,1721,1722,1725,1726,1729,1730,166,1732,166,1734,166,1736,615,1738,1740],{},[1657,1723,1724],{},"How do I add a data table to a chart with XlsxWriter?","\nCall ",[37,1727,1728],{},"chart.set_table()",", optionally with ",[37,1731,1517],{},[37,1733,1507],{},[37,1735,1510],{},[37,1737,1513],{},[37,1739,1521],{}," options.",[10,1742,1743,1746],{},[1657,1744,1745],{},"How do I change the number format in a chart data table?","\nThe table uses the number format of the source cells, so format those cells.",[10,1748,1749,1752],{},[1657,1750,1751],{},"Which chart types support a data table?","\nCharts with a category axis — column, bar, line and area charts; not pie, doughnut, scatter or bubble charts.",[25,1754,1756],{"id":1755},"related","Related",[1651,1758,1759,1765,1772,1779],{},[1654,1760,1761,1762,1764],{},"Up one level: ",[19,1763,22],{"href":21}," — native Excel charts from Python.",[1654,1766,1767,1771],{},[19,1768,1770],{"href":1769},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-bar-chart-in-excel-with-openpyxl\u002F","Create a Bar Chart in Excel with openpyxl"," — the chart underneath.",[1654,1773,1774,1778],{},[19,1775,1777],{"href":1776},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fadd-a-chart-to-an-excel-file-with-xlsxwriter\u002F","Add a Chart to an Excel File with xlsxwriter"," — XlsxWriter charts.",[1654,1780,1781,1785],{},[19,1782,1784],{"href":1783},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fformat-chart-axes-with-openpyxl\u002F","Format Chart Axes with openpyxl"," — axis number formats.",[1787,1788,1789],"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 .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .sXKb-, html code.shiki .sXKb-{--shiki-default:#032F62;--shiki-dark:#F0F3F6}",{"title":35,"searchDepth":90,"depth":90,"links":1791},[1792,1793,1794,1795,1796,1797,1798,1799,1800,1801],{"id":27,"depth":90,"text":28},{"id":63,"depth":90,"text":64},{"id":760,"depth":90,"text":761},{"id":1046,"depth":90,"text":1047},{"id":1603,"depth":90,"text":1604},{"id":1620,"depth":90,"text":1621},{"id":1648,"depth":90,"text":1649},{"id":1687,"depth":90,"text":1688},{"id":1703,"depth":90,"text":1704},{"id":1755,"depth":90,"text":1756},"2026-10-02","Show the numbers behind a chart in a grid directly underneath it — Excel's chart data table — from Python: switch it on with openpyxl's DataTable or XlsxWriter's set_table, choose borders and legend keys, drop the separate legend, carry the cells' number formats into the table, and check the rendered result. Includes when a data table helps and when data labels or a separate table are better.","md",[1806,1808,1810,1812],{"q":1709,"a":1807},"Import DataTable from openpyxl.chart.plotarea and assign an instance to chart.plot_area.dTable.",{"q":1724,"a":1809},"Call chart.set_table(), optionally with show_keys, horizontal, vertical, outline and font options.",{"q":1745,"a":1811},"The table uses the number format of the source cells, so format those cells.",{"q":1751,"a":1813},"Charts with a category axis — column, bar, line and area charts; not pie, doughnut, scatter or bubble charts.","excel chart data table python",null,{"breadcrumb":1817},[1818,1821,1824],{"name":1819,"item":1820},"Home","\u002F",{"name":1822,"item":1823},"Formatting and Charting Excel Reports with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002F",{"name":22,"item":21},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-a-data-table-below-an-excel-chart",{"title":1827,"description":1828},"Add a Data Table Below an Excel Chart with Python","Excel chart data tables from Python: openpyxl DataTable and XlsxWriter set_table, borders and legend keys, number formats, removing the legend, rendering checks and when to use data labels instead.","add-a-data-table-below-an-excel-chart","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-a-data-table-below-an-excel-chart\u002Findex",[1832,1555],"charts-and-images","how-to","Ye20qRXsat1Y00C5ur4x6JTBo2A-fuSiGppyF4XS4uk",[1836,1840],{"title":1837,"path":1838,"stem":1839,"children":-1},"Add a Combo Chart with a Secondary Axis in openpyxl","\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-a-combo-chart-with-a-secondary-axis-in-openpyxl","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-a-combo-chart-with-a-secondary-axis-in-openpyxl\u002Findex",{"title":1841,"path":1842,"stem":1843,"children":-1},"Add a Trendline to an Excel Chart with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-a-trendline-to-an-excel-chart-with-python","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-a-trendline-to-an-excel-chart-with-python\u002Findex",[1845,1847],{"path":1846,"title":22},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl",{"path":1848,"title":1822},"\u002Fformatting-and-charting-excel-reports-with-python",[],[1851,1854,1857,1860,1862,1865],{"path":1852,"title":1853,"kind":1833},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fbuild-a-dashboard-sheet-with-charts-from-multiple-tabs","Build a Dashboard Sheet with Charts from Multiple Tabs",{"path":1855,"title":1856,"kind":1833},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fbuild-a-rolling-12-month-chart-on-an-excel-dashboard","Build a Rolling 12-Month Chart on an Excel Dashboard",{"path":1858,"title":1859,"kind":1833},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fkeep-charts-and-images-when-filling-an-excel-template","Keep Charts and Images When Filling an Excel Template",{"path":1861,"title":1777,"kind":1833},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fadd-a-chart-to-an-excel-file-with-xlsxwriter",{"path":1863,"title":1864,"kind":1833},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fadd-sparklines-to-an-excel-report-with-xlsxwriter","Add Sparklines to an Excel Report with xlsxwriter",{"path":1866,"title":1867,"kind":1833},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fbuild-a-waterfall-chart-in-excel-with-python","Build a Waterfall Chart in Excel with Python",1790972108838]