[{"data":1,"prerenderedAt":2093},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fcontrol-how-xlwings-converts-dataframes-and-ranges":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fcontrol-how-xlwings-converts-dataframes-and-ranges":2061,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fcontrol-how-xlwings-converts-dataframes-and-ranges":2070,"children:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fcontrol-how-xlwings-converts-dataframes-and-ranges":2075,"related:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fcontrol-how-xlwings-converts-dataframes-and-ranges":2076},{"id":4,"title":5,"body":6,"dateModified":2027,"datePublished":2027,"description":2028,"extension":2029,"faq":2030,"keyword":2039,"kind":2040,"meta":2041,"navigation":132,"path":2051,"seo":2052,"slug":2054,"stem":2055,"topics":2056,"type":2059,"__hash__":2060},"docs\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fcontrol-how-xlwings-converts-dataframes-and-ranges\u002Findex.md","Control How xlwings Converts DataFrames and Ranges",{"type":7,"value":8,"toc":2015},"minimark",[9,33,38,66,80,84,91,256,270,401,405,428,576,605,752,756,773,1093,1122,1318,1322,1328,1787,1832,1836,1863,1867,1913,1917,1950,1954,1960,1966,1972,1978,1982,2011],[10,11,12,13,17,18,22,23,26,27,32],"p",{},"xlwings moves values between a live Excel workbook and Python, and the shape and type of what comes back depends on the range and on ",[14,15,16],"em",{},"converter options"," — the arguments to ",[19,20,21],"code",{},"range.options(...)",". Most surprises in xlwings code are conversion surprises: a single cell comes back as a float rather than a list, so a loop over \"the rows\" iterates over nothing; a one-row range comes back flat; whole numbers come back as ",[19,24,25],{},"12.0","; dates come back as datetimes with midnight times; and a DataFrame read without care loses its first column into the index. This page goes through the options one at a time — dimensions, numbers, dates, empty cells, orientation and expansion — then the DataFrame and dictionary converters, and finishes with a custom converter that applies a team's own conventions every time. The converter logic is exercised directly in Python, so its behaviour is shown without needing Excel. It belongs to ",[28,29,31],"a",{"href":30},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002F","Automating Excel with xlwings Basics",".",[34,35,37],"h2",{"id":36},"prerequisites","Prerequisites",[39,40,45],"pre",{"className":41,"code":42,"language":43,"meta":44,"style":44},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install xlwings pandas\n","bash","",[19,46,47],{"__ignoreMap":44},[48,49,52,56,60,63],"span",{"class":50,"line":51},"line",1,[48,53,55],{"class":54},"sMTad","pip",[48,57,59],{"class":58},"srMev"," install",[48,61,62],{"class":58}," xlwings",[48,64,65],{"class":58}," pandas\n",[10,67,68,69,72,73,72,76,79],{},"Reading and writing ranges needs desktop Excel on Windows or macOS. The examples that touch a workbook assume a sheet with a small table starting in A1 — ",[14,70,71],{},"Region",", ",[14,74,75],{},"Date",[14,77,78],{},"Qty"," — and are shown as they would run against Excel. The converter classes themselves are plain Python, so the blocks that call them directly run anywhere xlwings is installed, and their outputs below come from those runs.",[34,81,83],{"id":82},"step-1-shape-scalar-list-or-list-of-lists","Step 1 — Shape: scalar, list or list of lists",[10,85,86,87,90],{},"By default xlwings returns the ",[14,88,89],{},"simplest"," shape that fits the range:",[39,92,96],{"className":93,"code":94,"language":95,"meta":44,"style":44},"language-python shiki shiki-themes github-light github-dark-high-contrast","import xlwings as xw\nimport datetime as dt\n\nsheet = xw.books.active.sheets[\"Data\"]\nsheet[\"A2\"].value                          # 'North'                      a single cell -> scalar\nsheet[\"A2:C2\"].value                       # ['North', datetime(...), 12.0] one row -> flat list\nsheet[\"A2:A4\"].value                       # ['North', 'South', 'East']    one column -> flat list\nsheet[\"A1:C4\"].value                       # [[...], [...], ...]           rectangle -> list of lists\nsheet[\"A2:A4\"].options(ndim=2).value       # [['North'], ['South'], ['East']]\nsheet[\"A2\"].options(ndim=1).value          # ['North']\n","python",[19,97,98,114,127,134,152,168,182,195,208,234],{"__ignoreMap":44},[48,99,100,104,108,111],{"class":50,"line":51},[48,101,103],{"class":102},"s-kum","import",[48,105,107],{"class":106},"skGVy"," xlwings ",[48,109,110],{"class":102},"as",[48,112,113],{"class":106}," xw\n",[48,115,117,119,122,124],{"class":50,"line":116},2,[48,118,103],{"class":102},[48,120,121],{"class":106}," datetime ",[48,123,110],{"class":102},[48,125,126],{"class":106}," dt\n",[48,128,130],{"class":50,"line":129},3,[48,131,133],{"emptyLinePlaceholder":132},true,"\n",[48,135,137,140,143,146,149],{"class":50,"line":136},4,[48,138,139],{"class":106},"sheet ",[48,141,142],{"class":102},"=",[48,144,145],{"class":106}," xw.books.active.sheets[",[48,147,148],{"class":58},"\"Data\"",[48,150,151],{"class":106},"]\n",[48,153,155,158,161,164],{"class":50,"line":154},5,[48,156,157],{"class":106},"sheet[",[48,159,160],{"class":58},"\"A2\"",[48,162,163],{"class":106},"].value                          ",[48,165,167],{"class":166},"s-wDw","# 'North'                      a single cell -> scalar\n",[48,169,171,173,176,179],{"class":50,"line":170},6,[48,172,157],{"class":106},[48,174,175],{"class":58},"\"A2:C2\"",[48,177,178],{"class":106},"].value                       ",[48,180,181],{"class":166},"# ['North', datetime(...), 12.0] one row -> flat list\n",[48,183,185,187,190,192],{"class":50,"line":184},7,[48,186,157],{"class":106},[48,188,189],{"class":58},"\"A2:A4\"",[48,191,178],{"class":106},[48,193,194],{"class":166},"# ['North', 'South', 'East']    one column -> flat list\n",[48,196,198,200,203,205],{"class":50,"line":197},8,[48,199,157],{"class":106},[48,201,202],{"class":58},"\"A1:C4\"",[48,204,178],{"class":106},[48,206,207],{"class":166},"# [[...], [...], ...]           rectangle -> list of lists\n",[48,209,211,213,215,218,222,224,228,231],{"class":50,"line":210},9,[48,212,157],{"class":106},[48,214,189],{"class":58},[48,216,217],{"class":106},"].options(",[48,219,221],{"class":220},"sa561","ndim",[48,223,142],{"class":102},[48,225,227],{"class":226},"sP0c6","2",[48,229,230],{"class":106},").value       ",[48,232,233],{"class":166},"# [['North'], ['South'], ['East']]\n",[48,235,237,239,241,243,245,247,250,253],{"class":50,"line":236},10,[48,238,157],{"class":106},[48,240,160],{"class":58},[48,242,217],{"class":106},[48,244,221],{"class":220},[48,246,142],{"class":102},[48,248,249],{"class":226},"1",[48,251,252],{"class":106},").value          ",[48,254,255],{"class":166},"# ['North']\n",[10,257,258,259,262,263,266,267,269],{},"A single row and a single column both come back as flat lists, which means code cannot tell them apart and a one-row table looks the same as a one-column list. ",[19,260,261],{},"ndim=2"," always returns a list of rows, even for a single cell, and ",[19,264,265],{},"ndim=1"," always returns a flat list. Any code that processes \"rows\" of a range whose size depends on the data — a table that sometimes has one row — should read with ",[19,268,261],{},", or it will break on the day there is only one row.",[271,272,280,281,280,285,280,289,280,297,280,305,280,312,280,319,280,322,280,325,280,330,280,336,280,338,280,343,280,345,280,348,280,351,280,355,280,357,280,361,280,363,280,366,280,369,280,373,280,375,280,379,280,381,280,384,280,387,280,391,280,393,280,397,280,399],"svg",{"viewBox":273,"role":274,"ariaLabelledBy":275,"xmlns":278,"style":279},"4 0 752 208","img",[276,277],"xc-shape-t","xc-shape-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[282,283,284],"title",{"id":276},"What a range returns",[286,287,288],"desc",{"id":277},"By default a single cell returns a scalar, a single row or column a flat list, and a rectangle a list of rows. With ndim=2 every range returns a list of rows; with ndim=1 a single cell or row returns a flat list.",[290,291],"rect",{"x":292,"y":293,"width":294,"height":295,"fill":296},"4","0","752","208","#ffffff",[290,298],{"x":299,"y":300,"width":301,"height":302,"rx":293,"fill":296,"stroke":303,"style":304},"20.0","16.0","102.8","40.0","var(--line,#cdd5e6)","stroke-width:1px",[290,306],{"x":307,"y":300,"width":308,"height":302,"rx":293,"fill":309,"stroke":310,"style":311},"122.8","308.6","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.2px",[313,314,318],"text",{"x":315,"y":316,"style":317},"277.1","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","default",[290,320],{"x":321,"y":300,"width":308,"height":302,"rx":293,"fill":309,"stroke":310,"style":311},"431.4",[313,323,261],{"x":324,"y":316,"style":317},"585.7",[290,326],{"x":299,"y":327,"width":301,"height":328,"rx":293,"fill":329,"stroke":303,"style":304},"56.0","34.0","#e7ebef",[313,331,335],{"x":332,"y":333,"style":334},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","one cell",[290,337],{"x":307,"y":327,"width":308,"height":328,"rx":293,"fill":296,"stroke":303,"style":304},[313,339,342],{"x":315,"y":340,"style":341},"77.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","'North'",[290,344],{"x":321,"y":327,"width":308,"height":328,"rx":293,"fill":296,"stroke":303,"style":304},[313,346,347],{"x":324,"y":340,"style":341},"[['North']]",[290,349],{"x":299,"y":350,"width":301,"height":328,"rx":293,"fill":329,"stroke":303,"style":304},"90.0",[313,352,354],{"x":332,"y":353,"style":334},"112.0","one row",[290,356],{"x":307,"y":350,"width":308,"height":328,"rx":293,"fill":296,"stroke":303,"style":304},[313,358,360],{"x":315,"y":359,"style":341},"111.0","['North', …, 12.0]",[290,362],{"x":321,"y":350,"width":308,"height":328,"rx":293,"fill":296,"stroke":303,"style":304},[313,364,365],{"x":324,"y":359,"style":341},"[['North', …, 12.0]]",[290,367],{"x":299,"y":368,"width":301,"height":328,"rx":293,"fill":329,"stroke":303,"style":304},"124.0",[313,370,372],{"x":332,"y":371,"style":334},"146.0","one column",[290,374],{"x":307,"y":368,"width":308,"height":328,"rx":293,"fill":296,"stroke":303,"style":304},[313,376,378],{"x":315,"y":377,"style":341},"145.0","['North', 'South']",[290,380],{"x":321,"y":368,"width":308,"height":328,"rx":293,"fill":296,"stroke":303,"style":304},[313,382,383],{"x":324,"y":377,"style":341},"[['North'], ['South']]",[290,385],{"x":299,"y":386,"width":301,"height":328,"rx":293,"fill":329,"stroke":303,"style":304},"158.0",[313,388,390],{"x":332,"y":389,"style":334},"180.0","rectangle",[290,392],{"x":307,"y":386,"width":308,"height":328,"rx":293,"fill":296,"stroke":303,"style":304},[313,394,396],{"x":315,"y":395,"style":341},"179.0","list of rows",[290,398],{"x":321,"y":386,"width":308,"height":328,"rx":293,"fill":296,"stroke":303,"style":304},[313,400,396],{"x":324,"y":395,"style":341},[34,402,404],{"id":403},"step-2-numbers-dates-empty-cells-and-orientation","Step 2 — Numbers, dates, empty cells and orientation",[10,406,407,408,411,412,415,416,419,420,423,424,427],{},"Excel stores every number as a float and every date as a date-time. The ",[19,409,410],{},"numbers"," and ",[19,413,414],{},"dates"," options convert them on the way in; ",[19,417,418],{},"empty"," sets what blank cells become; ",[19,421,422],{},"transpose"," swaps rows and columns; ",[19,425,426],{},"expand"," sizes the range from the data:",[39,429,431],{"className":93,"code":430,"language":95,"meta":44,"style":44},"sheet[\"C2:C4\"].options(numbers=int).value                     # [12, 7, 30] instead of [12.0, 7.0, 30.0]\nsheet[\"B2:B4\"].options(dates=dt.date).value                   # [date(2026, 9, 1), ...] without times\nsheet[\"A1:C4\"].options(empty=\"\").value                        # blanks become '' instead of None\nsheet[\"A1:C4\"].options(transpose=True).value                  # columns as rows\nsheet[\"A1\"].options(expand=\"table\").value                     # the whole contiguous table from A1\nsheet[\"A1\"].options(transpose=True).value = [1, 2, 3]         # writes down a column instead of across\n",[19,432,433,455,474,495,516,537],{"__ignoreMap":44},[48,434,435,437,440,442,444,446,449,452],{"class":50,"line":51},[48,436,157],{"class":106},[48,438,439],{"class":58},"\"C2:C4\"",[48,441,217],{"class":106},[48,443,410],{"class":220},[48,445,142],{"class":102},[48,447,448],{"class":226},"int",[48,450,451],{"class":106},").value                     ",[48,453,454],{"class":166},"# [12, 7, 30] instead of [12.0, 7.0, 30.0]\n",[48,456,457,459,462,464,466,468,471],{"class":50,"line":116},[48,458,157],{"class":106},[48,460,461],{"class":58},"\"B2:B4\"",[48,463,217],{"class":106},[48,465,414],{"class":220},[48,467,142],{"class":102},[48,469,470],{"class":106},"dt.date).value                   ",[48,472,473],{"class":166},"# [date(2026, 9, 1), ...] without times\n",[48,475,476,478,480,482,484,486,489,492],{"class":50,"line":129},[48,477,157],{"class":106},[48,479,202],{"class":58},[48,481,217],{"class":106},[48,483,418],{"class":220},[48,485,142],{"class":102},[48,487,488],{"class":58},"\"\"",[48,490,491],{"class":106},").value                        ",[48,493,494],{"class":166},"# blanks become '' instead of None\n",[48,496,497,499,501,503,505,507,510,513],{"class":50,"line":136},[48,498,157],{"class":106},[48,500,202],{"class":58},[48,502,217],{"class":106},[48,504,422],{"class":220},[48,506,142],{"class":102},[48,508,509],{"class":226},"True",[48,511,512],{"class":106},").value                  ",[48,514,515],{"class":166},"# columns as rows\n",[48,517,518,520,523,525,527,529,532,534],{"class":50,"line":154},[48,519,157],{"class":106},[48,521,522],{"class":58},"\"A1\"",[48,524,217],{"class":106},[48,526,426],{"class":220},[48,528,142],{"class":102},[48,530,531],{"class":58},"\"table\"",[48,533,451],{"class":106},[48,535,536],{"class":166},"# the whole contiguous table from A1\n",[48,538,539,541,543,545,547,549,551,554,556,559,561,563,565,567,570,573],{"class":50,"line":170},[48,540,157],{"class":106},[48,542,522],{"class":58},[48,544,217],{"class":106},[48,546,422],{"class":220},[48,548,142],{"class":102},[48,550,509],{"class":226},[48,552,553],{"class":106},").value ",[48,555,142],{"class":102},[48,557,558],{"class":106}," [",[48,560,249],{"class":226},[48,562,72],{"class":106},[48,564,227],{"class":226},[48,566,72],{"class":106},[48,568,569],{"class":226},"3",[48,571,572],{"class":106},"]         ",[48,574,575],{"class":166},"# writes down a column instead of across\n",[10,577,578,581,582,585,586,589,590,592,593,596,597,600,601,604],{},[19,579,580],{},"numbers=int"," converts every number in the range, so use it only where all values are whole — a quantity column, not a price column, because 2.5 would become 2. ",[19,583,584],{},"dates=dt.date"," drops the time part, which avoids ",[19,587,588],{},"2026-09-01 00:00:00"," appearing in output. ",[19,591,418],{}," is useful when the result goes into a text-based format where ",[19,594,595],{},"None"," would print as the word \"None\". ",[19,598,599],{},"expand=\"table\""," grows the range down and right from the anchor cell until it meets empty rows and columns, so the code does not need to know the table's size — but a blank row inside the data stops the expansion there. On writing, a flat list goes across a row; ",[19,602,603],{},"transpose=True"," sends it down a column.",[271,606,280,611,280,614,280,617,280,620,280,627,280,638,280,643,280,647,280,651,280,654,280,659,280,666,280,671,280,676,280,682,280,686,280,690,280,692,280,695,280,699,280,702,280,707,280,714,280,718,280,721,280,723,280,726,280,730,280,733,280,738,280,745,280,749],{"viewBox":607,"role":274,"ariaLabelledBy":608,"xmlns":278,"style":279},"4 0 752 272",[609,610],"xc-which-t","xc-which-d",[282,612,613],{"id":609},"Which converter for which range",[286,615,616],{"id":610},"A settings block of names and values reads best as a dict. A table with a header row reads as a DataFrame with index=False. A list whose size varies reads with ndim=2 so one row still gives a list of rows. A project-wide convention belongs in a registered custom converter.",[290,618],{"x":292,"y":293,"width":294,"height":619,"fill":296},"272",[290,621],{"x":622,"y":300,"width":623,"height":624,"rx":625,"fill":309,"stroke":310,"style":626},"240.0","280.0","75.5","12","stroke-width:2px",[628,629,631],"g",{"transform":630},"translate(368.0,29.0) scale(1.000)",[632,633],"path",{"d":634,"fill":635,"stroke":636,"style":637},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14","none","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[313,639,642],{"x":640,"y":624,"style":641},"380.0","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","What is in the range?",[50,644],{"x1":640,"y1":645,"x2":640,"y2":646,"stroke":310,"style":626},"91.5","111.5",[50,648],{"x1":649,"y1":646,"x2":650,"y2":646,"stroke":310,"style":626},"134.7","625.3",[50,652],{"x1":649,"y1":646,"x2":649,"y2":653,"stroke":310,"style":626},"157.5",[655,656],"polygon",{"points":657,"fill":658},"134.7,161.5 129.7,152.5 139.7,152.5","#5b5cf0",[290,660],{"x":661,"y":662,"width":663,"height":664,"rx":665,"fill":296,"stroke":303,"style":311},"72.5","121.5","124.4","21.0","10",[313,667,670],{"x":649,"y":668,"style":669},"136.0","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","names and values",[290,672],{"x":299,"y":673,"width":674,"height":675,"rx":625,"fill":309,"stroke":310,"style":626},"163.5","229.3","93.0",[628,677,679],{"transform":678},"translate(122.7,176.5) scale(1.000)",[632,680],{"d":681,"fill":635,"stroke":636,"style":637},"M8 11a4 4 0 1 0 .01 0z M11 13l9-9 M17 7l2 2 M15 9l2 2",[313,683,685],{"x":649,"y":684,"style":641},"223.0","options(dict)",[313,687,689],{"x":649,"y":688,"style":341},"242.5","settings lookup",[50,691],{"x1":640,"y1":646,"x2":640,"y2":653,"stroke":310,"style":626},[655,693],{"points":694,"fill":658},"380.0,161.5 375.0,152.5 385.0,152.5",[290,696],{"x":697,"y":662,"width":698,"height":664,"rx":665,"fill":296,"stroke":303,"style":311},"319.9","120.2",[313,700,701],{"x":640,"y":668,"style":669},"table with header",[290,703],{"x":704,"y":673,"width":674,"height":675,"rx":625,"fill":705,"stroke":706,"style":626},"265.3","#d9f4f1","var(--teal,#0f9488)",[628,708,710],{"transform":709},"translate(368.0,176.5) scale(1.000)",[632,711],{"d":712,"fill":635,"stroke":713,"style":637},"M12 3c-4 0-4 1.6-4 3v2h4.2v1H6c-2 0-3 1.4-3 4s1 4 3 4h2v-2.6c0-1.6 1.3-2.9 2.9-2.9h4.2c1.3 0 2.4-1.1 2.4-2.4V6c0-1.6-1.8-3-5.5-3z M10 5.5h.01 M12 21c4 0 4-1.6 4-3v-2h-4.2v-1H18c2 0 3-1.4 3-4s-1-4-3-4h-2v2.6c0 1.6-1.3 2.9-2.9 2.9H8.9c-1.3 0-2.4 1.1-2.4 2.4V18c0 1.6 1.8 3 5.5 3z M14 18.5h.01","var(--teal-ink,#0b6157)",[313,715,717],{"x":640,"y":684,"style":716},"font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","pd.DataFrame, index=False",[313,719,720],{"x":640,"y":688,"style":341},"analysis in pandas",[50,722],{"x1":650,"y1":646,"x2":650,"y2":653,"stroke":310,"style":626},[655,724],{"points":725,"fill":658},"625.3,161.5 620.3,152.5 630.3,152.5",[290,727],{"x":728,"y":662,"width":729,"height":664,"rx":665,"fill":296,"stroke":303,"style":311},"560.0","130.6",[313,731,732],{"x":650,"y":668,"style":669},"house conventions",[290,734],{"x":735,"y":673,"width":674,"height":675,"rx":625,"fill":736,"stroke":737,"style":626},"510.7","#fdefd8","var(--gold,#b4740a)",[628,739,741],{"transform":740},"translate(613.3,176.5) scale(1.000)",[632,742],{"d":743,"fill":635,"stroke":744,"style":637},"M12 9a3 3 0 1 0 .01 0z M12 2.5v3 M12 18.5v3 M2.5 12h3 M18.5 12h3 M5.3 5.3l2.1 2.1 M16.6 16.6l2.1 2.1 M5.3 18.7l2.1-2.1 M16.6 7.4l2.1-2.1","var(--gold-ink,#7a4e06)",[313,746,748],{"x":650,"y":684,"style":747},"font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","custom converter",[313,750,751],{"x":650,"y":688,"style":341},"options('clean')",[34,753,755],{"id":754},"step-3-dataframes-header-and-index","Step 3 — DataFrames: header and index",[10,757,758,759,762,763,411,766,769,770,772],{},"The ",[19,760,761],{},"pd.DataFrame"," converter turns a list of rows into a DataFrame and back. What it does with the first row and the first column is controlled by ",[19,764,765],{},"header",[19,767,768],{},"index",", and the default for ",[19,771,768],{}," catches most people out. Here is the converter applied to the values xlwings reads from the A1:C3 block:",[39,774,776],{"className":93,"code":775,"language":95,"meta":44,"style":44},"import datetime as dt\nimport pandas as pd\nfrom xlwings.conversion import PandasDataFrameConverter, DictConverter\nfrom xlwings.conversion.framework import Options\n\nblock = [[\"Region\", \"Date\", \"Qty\"], [\"North\", dt.datetime(2026, 9, 1), 12.0], [\"South\", dt.datetime(2026, 9, 2), None]]\nfor opts in [{}, {\"index\": False}, {\"index\": False, \"header\": 2}]:\n    df = PandasDataFrameConverter.read_value(block, Options(opts))\n    print(f\"{str(opts):\u003C28} index={df.index.tolist()}  columns={list(df.columns)}\")\n\nsummary = pd.DataFrame({\"Region\": [\"North\", \"South\"], \"Qty\": [12, 7]})\nprint(\"write, default:    \", PandasDataFrameConverter.write_value(summary, Options({})))\nprint(\"write, index=False:\", PandasDataFrameConverter.write_value(summary, Options({\"index\": False})))\n",[19,777,778,788,800,813,825,829,902,946,956,1014,1018,1057,1071],{"__ignoreMap":44},[48,779,780,782,784,786],{"class":50,"line":51},[48,781,103],{"class":102},[48,783,121],{"class":106},[48,785,110],{"class":102},[48,787,126],{"class":106},[48,789,790,792,795,797],{"class":50,"line":116},[48,791,103],{"class":102},[48,793,794],{"class":106}," pandas ",[48,796,110],{"class":102},[48,798,799],{"class":106}," pd\n",[48,801,802,805,808,810],{"class":50,"line":129},[48,803,804],{"class":102},"from",[48,806,807],{"class":106}," xlwings.conversion ",[48,809,103],{"class":102},[48,811,812],{"class":106}," PandasDataFrameConverter, DictConverter\n",[48,814,815,817,820,822],{"class":50,"line":136},[48,816,804],{"class":102},[48,818,819],{"class":106}," xlwings.conversion.framework ",[48,821,103],{"class":102},[48,823,824],{"class":106}," Options\n",[48,826,827],{"class":50,"line":154},[48,828,133],{"emptyLinePlaceholder":132},[48,830,831,834,836,839,842,844,847,849,852,855,858,861,864,866,869,871,873,876,878,880,883,885,887,889,891,893,895,897,899],{"class":50,"line":170},[48,832,833],{"class":106},"block ",[48,835,142],{"class":102},[48,837,838],{"class":106}," [[",[48,840,841],{"class":58},"\"Region\"",[48,843,72],{"class":106},[48,845,846],{"class":58},"\"Date\"",[48,848,72],{"class":106},[48,850,851],{"class":58},"\"Qty\"",[48,853,854],{"class":106},"], [",[48,856,857],{"class":58},"\"North\"",[48,859,860],{"class":106},", dt.datetime(",[48,862,863],{"class":226},"2026",[48,865,72],{"class":106},[48,867,868],{"class":226},"9",[48,870,72],{"class":106},[48,872,249],{"class":226},[48,874,875],{"class":106},"), ",[48,877,25],{"class":226},[48,879,854],{"class":106},[48,881,882],{"class":58},"\"South\"",[48,884,860],{"class":106},[48,886,863],{"class":226},[48,888,72],{"class":106},[48,890,868],{"class":226},[48,892,72],{"class":106},[48,894,227],{"class":226},[48,896,875],{"class":106},[48,898,595],{"class":226},[48,900,901],{"class":106},"]]\n",[48,903,904,907,910,913,916,919,922,925,928,930,932,934,936,939,941,943],{"class":50,"line":184},[48,905,906],{"class":102},"for",[48,908,909],{"class":106}," opts ",[48,911,912],{"class":102},"in",[48,914,915],{"class":106}," [{}, {",[48,917,918],{"class":58},"\"index\"",[48,920,921],{"class":106},": ",[48,923,924],{"class":226},"False",[48,926,927],{"class":106},"}, {",[48,929,918],{"class":58},[48,931,921],{"class":106},[48,933,924],{"class":226},[48,935,72],{"class":106},[48,937,938],{"class":58},"\"header\"",[48,940,921],{"class":106},[48,942,227],{"class":226},[48,944,945],{"class":106},"}]:\n",[48,947,948,951,953],{"class":50,"line":197},[48,949,950],{"class":106},"    df ",[48,952,142],{"class":102},[48,954,955],{"class":106}," PandasDataFrameConverter.read_value(block, Options(opts))\n",[48,957,958,961,964,967,970,974,977,980,983,986,989,991,994,996,999,1001,1004,1007,1009,1011],{"class":50,"line":210},[48,959,960],{"class":226},"    print",[48,962,963],{"class":106},"(",[48,965,966],{"class":102},"f",[48,968,969],{"class":58},"\"",[48,971,973],{"class":972},"sSjpA","{",[48,975,976],{"class":226},"str",[48,978,979],{"class":106},"(opts)",[48,981,982],{"class":102},":\u003C28",[48,984,985],{"class":972},"}",[48,987,988],{"class":58}," index=",[48,990,973],{"class":972},[48,992,993],{"class":106},"df.index.tolist()",[48,995,985],{"class":972},[48,997,998],{"class":58},"  columns=",[48,1000,973],{"class":972},[48,1002,1003],{"class":226},"list",[48,1005,1006],{"class":106},"(df.columns)",[48,1008,985],{"class":972},[48,1010,969],{"class":58},[48,1012,1013],{"class":106},")\n",[48,1015,1016],{"class":50,"line":236},[48,1017,133],{"emptyLinePlaceholder":132},[48,1019,1021,1024,1026,1029,1031,1034,1036,1038,1040,1043,1045,1047,1049,1051,1054],{"class":50,"line":1020},11,[48,1022,1023],{"class":106},"summary ",[48,1025,142],{"class":102},[48,1027,1028],{"class":106}," pd.DataFrame({",[48,1030,841],{"class":58},[48,1032,1033],{"class":106},": [",[48,1035,857],{"class":58},[48,1037,72],{"class":106},[48,1039,882],{"class":58},[48,1041,1042],{"class":106},"], ",[48,1044,851],{"class":58},[48,1046,1033],{"class":106},[48,1048,625],{"class":226},[48,1050,72],{"class":106},[48,1052,1053],{"class":226},"7",[48,1055,1056],{"class":106},"]})\n",[48,1058,1060,1063,1065,1068],{"class":50,"line":1059},12,[48,1061,1062],{"class":226},"print",[48,1064,963],{"class":106},[48,1066,1067],{"class":58},"\"write, default:    \"",[48,1069,1070],{"class":106},", PandasDataFrameConverter.write_value(summary, Options({})))\n",[48,1072,1074,1076,1078,1081,1084,1086,1088,1090],{"class":50,"line":1073},13,[48,1075,1062],{"class":226},[48,1077,963],{"class":106},[48,1079,1080],{"class":58},"\"write, index=False:\"",[48,1082,1083],{"class":106},", PandasDataFrameConverter.write_value(summary, Options({",[48,1085,918],{"class":58},[48,1087,921],{"class":106},[48,1089,924],{"class":226},[48,1091,1092],{"class":106},"})))\n",[10,1094,1095,1096,1099,1100,1102,1103,1106,1107,1110,1111,1113,1114,1117,1118,1121],{},"With no options, the first row becomes the column names ",[14,1097,1098],{},"and the first column becomes the index"," — ",[14,1101,71],{}," disappears from the columns into the index. That default suits sheets written by xlwings itself with an index, and almost nothing else. ",[19,1104,1105],{},"index=False"," keeps it as an ordinary column, which is nearly always what a table read from a sheet needs. ",[19,1108,1109],{},"header=2"," treats two rows as a two-level header, producing tuples as column names. Writing is symmetrical: by default the index is written as an extra first column — with an empty header cell and the numbers 0 and 1 — and ",[19,1112,1105],{}," leaves it out. In code these are ",[19,1115,1116],{},"sheet[\"A1\"].options(pd.DataFrame, expand=\"table\", index=False).value"," to read and ",[19,1119,1120],{},"sheet[\"E1\"].options(index=False).value = summary"," to write.",[271,1123,280,1128,280,1131,280,1134,280,1137,280,1140,280,1144,280,1150,280,1154,280,1158,280,1162,280,1166,280,1170,280,1174,280,1176,280,1179,280,1181,280,1183,280,1185,280,1187,280,1190,280,1192,280,1194,280,1198,280,1200,280,1203,280,1205,280,1207,280,1210,280,1213,280,1215,280,1218,280,1220,280,1223,280,1225,280,1228,280,1232,280,1235,280,1239,280,1242,280,1244,280,1247,280,1249,280,1252,280,1254,280,1257,280,1259,280,1261,280,1264,280,1268,280,1270,280,1273,280,1275,280,1277,280,1279,280,1283,280,1285,280,1288,280,1293,280,1297,280,1302,280,1305,280,1312],{"viewBox":1124,"role":274,"ariaLabelledBy":1125,"xmlns":278,"style":279},"4 0 752 164",[1126,1127],"xc-df-t","xc-df-d",[282,1129,1130],{"id":1126},"The same block read with and without index=False",[286,1132,1133],{"id":1127},"Read with default options, the Region column becomes the DataFrame index and the columns are Date and Qty. Read with index=False, Region stays a column and the index is 0 and 1.",[290,1135],{"x":292,"y":293,"width":294,"height":1136,"fill":296},"164",[290,1138],{"x":299,"y":302,"width":332,"height":1139,"rx":293,"fill":329,"stroke":303,"style":304},"24.0",[290,1141],{"x":1142,"y":302,"width":1143,"height":1139,"rx":293,"fill":329,"stroke":303,"style":304},"52.0","56.4",[313,1145,1149],{"x":1146,"y":1147,"style":1148},"80.2","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[290,1151],{"x":1152,"y":302,"width":1153,"height":1139,"rx":293,"fill":329,"stroke":303,"style":304},"108.4","56.7",[313,1155,1157],{"x":1156,"y":1147,"style":1148},"136.8","B",[290,1159],{"x":1160,"y":302,"width":1161,"height":1139,"rx":293,"fill":329,"stroke":303,"style":304},"165.1","38.9",[313,1163,1165],{"x":1164,"y":1147,"style":1148},"184.6","C",[290,1167],{"x":299,"y":1168,"width":332,"height":1169,"rx":293,"fill":329,"stroke":303,"style":304},"64.0","28.0",[313,1171,249],{"x":1172,"y":1173,"style":1148},"36.0","83.0",[290,1175],{"x":1142,"y":1168,"width":1143,"height":1169,"rx":293,"fill":296,"stroke":303,"style":304},[313,1177,71],{"x":1146,"y":1173,"style":1178},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle",[290,1180],{"x":1152,"y":1168,"width":1153,"height":1169,"rx":293,"fill":296,"stroke":303,"style":304},[313,1182,75],{"x":1156,"y":1173,"style":1178},[290,1184],{"x":1160,"y":1168,"width":1161,"height":1169,"rx":293,"fill":296,"stroke":303,"style":304},[313,1186,78],{"x":1164,"y":1173,"style":1178},[290,1188],{"x":299,"y":1189,"width":332,"height":1169,"rx":293,"fill":329,"stroke":303,"style":304},"92.0",[313,1191,227],{"x":1172,"y":359,"style":1148},[290,1193],{"x":1142,"y":1189,"width":1143,"height":1169,"rx":293,"fill":296,"stroke":303,"style":304},[313,1195,1197],{"x":1146,"y":359,"style":1196},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","North",[290,1199],{"x":1152,"y":1189,"width":1153,"height":1169,"rx":293,"fill":296,"stroke":303,"style":304},[313,1201,1202],{"x":1156,"y":359,"style":1196},"01 Sep",[290,1204],{"x":1160,"y":1189,"width":1161,"height":1169,"rx":293,"fill":296,"stroke":303,"style":304},[313,1206,625],{"x":1164,"y":359,"style":1196},[290,1208],{"x":299,"y":1209,"width":332,"height":1169,"rx":293,"fill":329,"stroke":303,"style":304},"120.0",[313,1211,569],{"x":1172,"y":1212,"style":1148},"139.0",[290,1214],{"x":1142,"y":1209,"width":1143,"height":1169,"rx":293,"fill":296,"stroke":303,"style":304},[313,1216,1217],{"x":1146,"y":1212,"style":1196},"South",[290,1219],{"x":1152,"y":1209,"width":1153,"height":1169,"rx":293,"fill":296,"stroke":303,"style":304},[313,1221,1222],{"x":1156,"y":1212,"style":1196},"02 Sep",[290,1224],{"x":1160,"y":1209,"width":1161,"height":1169,"rx":293,"fill":296,"stroke":303,"style":304},[290,1226],{"x":1227,"y":302,"width":332,"height":1139,"rx":293,"fill":329,"stroke":303,"style":304},"487.1",[290,1229],{"x":1230,"y":302,"width":1231,"height":1139,"rx":293,"fill":329,"stroke":303,"style":304},"519.1","108.7",[313,1233,1149],{"x":1234,"y":1147,"style":1148},"573.4",[290,1236],{"x":1237,"y":302,"width":1238,"height":1139,"rx":293,"fill":329,"stroke":303,"style":304},"627.8","112.2",[313,1240,1157],{"x":1241,"y":1147,"style":1148},"683.9",[290,1243],{"x":1227,"y":1168,"width":332,"height":1169,"rx":293,"fill":329,"stroke":303,"style":304},[313,1245,249],{"x":1246,"y":1173,"style":1148},"503.1",[290,1248],{"x":1230,"y":1168,"width":1231,"height":1169,"rx":293,"fill":296,"stroke":303,"style":304},[313,1250,1251],{"x":1234,"y":1173,"style":1178},"default: index",[290,1253],{"x":1237,"y":1168,"width":1238,"height":1169,"rx":293,"fill":296,"stroke":303,"style":304},[313,1255,1256],{"x":1241,"y":1173,"style":1178},"columns",[290,1258],{"x":1227,"y":1189,"width":332,"height":1169,"rx":293,"fill":329,"stroke":303,"style":304},[313,1260,227],{"x":1246,"y":359,"style":1148},[290,1262],{"x":1230,"y":1189,"width":1231,"height":1169,"rx":293,"fill":736,"stroke":737,"style":1263},"stroke-width:1.5px",[313,1265,1267],{"x":1234,"y":359,"style":1266},"font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","North, South",[290,1269],{"x":1237,"y":1189,"width":1238,"height":1169,"rx":293,"fill":736,"stroke":737,"style":1263},[313,1271,1272],{"x":1241,"y":359,"style":1266},"Date, Qty",[290,1274],{"x":1227,"y":1209,"width":332,"height":1169,"rx":293,"fill":329,"stroke":303,"style":304},[313,1276,569],{"x":1246,"y":1212,"style":1148},[290,1278],{"x":1230,"y":1209,"width":1231,"height":1169,"rx":293,"fill":705,"stroke":706,"style":1263},[313,1280,1282],{"x":1234,"y":1212,"style":1281},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","index=False: 0, 1",[290,1284],{"x":1237,"y":1209,"width":1238,"height":1169,"rx":293,"fill":705,"stroke":706,"style":1263},[313,1286,1287],{"x":1241,"y":1212,"style":1281},"Region, Date, Qty",[313,1289,1292],{"x":353,"y":1290,"style":1291},"29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","before",[313,1294,1296],{"x":1295,"y":1290,"style":1291},"613.5","after",[50,1298],{"x1":1299,"y1":1300,"x2":1301,"y2":1300,"stroke":310,"style":626},"214.0","94.0","473.1",[655,1303],{"points":1304,"fill":658},"477.1,94.0 468.1,99.0 468.1,89.0",[290,1306],{"x":1307,"y":1308,"width":1309,"height":1310,"rx":1311,"fill":296,"stroke":303,"style":311},"248.3","61.0","194.5","22.0","11",[313,1313,1317],{"x":1314,"y":1315,"style":1316},"345.5","76.0","font-size:11.5px;font-weight:400;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","options(pd.DataFrame, …)",[34,1319,1321],{"id":1320},"step-4-dictionaries-and-a-custom-converter","Step 4 — Dictionaries and a custom converter",[10,1323,1324,1325,1327],{},"A two-column range of names and values — a settings block — converts straight to a dictionary with ",[19,1326,685],{},". For conventions you apply every time, write a converter class and register it under a name:",[39,1329,1331],{"className":93,"code":1330,"language":95,"meta":44,"style":44},"import xlwings as xw\n\nprint(DictConverter.read_value([[\"rate\", 0.2], [\"fx\", 1.17], [\"region\", \"North\"]], Options({})))\n\nclass CleanTable(xw.conversion.Converter):\n    \"\"\"Header row to snake_case, whole numbers as int, blank rows dropped; writes without the index.\"\"\"\n\n    @staticmethod\n    def read_value(value, options):\n        header, *rows = value\n        cols = [str(h).strip().lower().replace(\" \", \"_\") for h in header]\n        rows = [r for r in rows if any(v not in (None, \"\") for v in r)]\n        df = pd.DataFrame(rows, columns=cols)\n        for c in df.columns:\n            if pd.api.types.is_float_dtype(df[c]) and df[c].dropna().apply(float.is_integer).all():\n                df[c] = df[c].astype(\"Int64\")\n        return df\n\n    @staticmethod\n    def write_value(df, options):\n        return [list(df.columns)] + df.astype(object).where(df.notna(), None).values.tolist()\n\nCleanTable.register(\"clean\")\nraw = [[\"Order ID\", \"Qty\", \"Unit Price\"], [\"SO-1\", 12.0, 2.5], [None, None, None], [\"SO-2\", 7.0, None]]\ndf = CleanTable.read_value(raw, Options({}))\nprint(df.dtypes.to_dict())\nprint(CleanTable.write_value(df, Options({})))\n",[19,1332,1333,1343,1347,1384,1388,1415,1420,1424,1433,1444,1460,1495,1551,1568,1582,1603,1619,1628,1633,1640,1651,1680,1685,1696,1760,1771,1779],{"__ignoreMap":44},[48,1334,1335,1337,1339,1341],{"class":50,"line":51},[48,1336,103],{"class":102},[48,1338,107],{"class":106},[48,1340,110],{"class":102},[48,1342,113],{"class":106},[48,1344,1345],{"class":50,"line":116},[48,1346,133],{"emptyLinePlaceholder":132},[48,1348,1349,1351,1354,1357,1359,1362,1364,1367,1369,1372,1374,1377,1379,1381],{"class":50,"line":129},[48,1350,1062],{"class":226},[48,1352,1353],{"class":106},"(DictConverter.read_value([[",[48,1355,1356],{"class":58},"\"rate\"",[48,1358,72],{"class":106},[48,1360,1361],{"class":226},"0.2",[48,1363,854],{"class":106},[48,1365,1366],{"class":58},"\"fx\"",[48,1368,72],{"class":106},[48,1370,1371],{"class":226},"1.17",[48,1373,854],{"class":106},[48,1375,1376],{"class":58},"\"region\"",[48,1378,72],{"class":106},[48,1380,857],{"class":58},[48,1382,1383],{"class":106},"]], Options({})))\n",[48,1385,1386],{"class":50,"line":136},[48,1387,133],{"emptyLinePlaceholder":132},[48,1389,1390,1393,1396,1398,1402,1404,1407,1409,1412],{"class":50,"line":154},[48,1391,1392],{"class":102},"class",[48,1394,1395],{"class":54}," CleanTable",[48,1397,963],{"class":106},[48,1399,1401],{"class":1400},"sHg5J","xw",[48,1403,32],{"class":106},[48,1405,1406],{"class":1400},"conversion",[48,1408,32],{"class":106},[48,1410,1411],{"class":1400},"Converter",[48,1413,1414],{"class":106},"):\n",[48,1416,1417],{"class":50,"line":170},[48,1418,1419],{"class":58},"    \"\"\"Header row to snake_case, whole numbers as int, blank rows dropped; writes without the index.\"\"\"\n",[48,1421,1422],{"class":50,"line":184},[48,1423,133],{"emptyLinePlaceholder":132},[48,1425,1426,1430],{"class":50,"line":197},[48,1427,1429],{"class":1428},"s_Opv","    @",[48,1431,1432],{"class":226},"staticmethod\n",[48,1434,1435,1438,1441],{"class":50,"line":210},[48,1436,1437],{"class":102},"    def",[48,1439,1440],{"class":1428}," read_value",[48,1442,1443],{"class":106},"(value, options):\n",[48,1445,1446,1449,1452,1455,1457],{"class":50,"line":236},[48,1447,1448],{"class":106},"        header, ",[48,1450,1451],{"class":102},"*",[48,1453,1454],{"class":106},"rows ",[48,1456,142],{"class":102},[48,1458,1459],{"class":106}," value\n",[48,1461,1462,1465,1467,1469,1471,1474,1477,1479,1482,1485,1487,1490,1492],{"class":50,"line":1020},[48,1463,1464],{"class":106},"        cols ",[48,1466,142],{"class":102},[48,1468,558],{"class":106},[48,1470,976],{"class":226},[48,1472,1473],{"class":106},"(h).strip().lower().replace(",[48,1475,1476],{"class":58},"\" \"",[48,1478,72],{"class":106},[48,1480,1481],{"class":58},"\"_\"",[48,1483,1484],{"class":106},") ",[48,1486,906],{"class":102},[48,1488,1489],{"class":106}," h ",[48,1491,912],{"class":102},[48,1493,1494],{"class":106}," header]\n",[48,1496,1497,1500,1502,1505,1507,1510,1512,1515,1518,1521,1524,1527,1530,1533,1535,1537,1539,1541,1543,1546,1548],{"class":50,"line":1059},[48,1498,1499],{"class":106},"        rows ",[48,1501,142],{"class":102},[48,1503,1504],{"class":106}," [r ",[48,1506,906],{"class":102},[48,1508,1509],{"class":106}," r ",[48,1511,912],{"class":102},[48,1513,1514],{"class":106}," rows ",[48,1516,1517],{"class":102},"if",[48,1519,1520],{"class":226}," any",[48,1522,1523],{"class":106},"(v ",[48,1525,1526],{"class":102},"not",[48,1528,1529],{"class":102}," in",[48,1531,1532],{"class":106}," (",[48,1534,595],{"class":226},[48,1536,72],{"class":106},[48,1538,488],{"class":58},[48,1540,1484],{"class":106},[48,1542,906],{"class":102},[48,1544,1545],{"class":106}," v ",[48,1547,912],{"class":102},[48,1549,1550],{"class":106}," r)]\n",[48,1552,1553,1556,1558,1561,1563,1565],{"class":50,"line":1073},[48,1554,1555],{"class":106},"        df ",[48,1557,142],{"class":102},[48,1559,1560],{"class":106}," pd.DataFrame(rows, ",[48,1562,1256],{"class":220},[48,1564,142],{"class":102},[48,1566,1567],{"class":106},"cols)\n",[48,1569,1571,1574,1577,1579],{"class":50,"line":1570},14,[48,1572,1573],{"class":102},"        for",[48,1575,1576],{"class":106}," c ",[48,1578,912],{"class":102},[48,1580,1581],{"class":106}," df.columns:\n",[48,1583,1585,1588,1591,1594,1597,1600],{"class":50,"line":1584},15,[48,1586,1587],{"class":102},"            if",[48,1589,1590],{"class":106}," pd.api.types.is_float_dtype(df[c]) ",[48,1592,1593],{"class":102},"and",[48,1595,1596],{"class":106}," df[c].dropna().apply(",[48,1598,1599],{"class":226},"float",[48,1601,1602],{"class":106},".is_integer).all():\n",[48,1604,1606,1609,1611,1614,1617],{"class":50,"line":1605},16,[48,1607,1608],{"class":106},"                df[c] ",[48,1610,142],{"class":102},[48,1612,1613],{"class":106}," df[c].astype(",[48,1615,1616],{"class":58},"\"Int64\"",[48,1618,1013],{"class":106},[48,1620,1622,1625],{"class":50,"line":1621},17,[48,1623,1624],{"class":102},"        return",[48,1626,1627],{"class":106}," df\n",[48,1629,1631],{"class":50,"line":1630},18,[48,1632,133],{"emptyLinePlaceholder":132},[48,1634,1636,1638],{"class":50,"line":1635},19,[48,1637,1429],{"class":1428},[48,1639,1432],{"class":226},[48,1641,1643,1645,1648],{"class":50,"line":1642},20,[48,1644,1437],{"class":102},[48,1646,1647],{"class":1428}," write_value",[48,1649,1650],{"class":106},"(df, options):\n",[48,1652,1654,1656,1658,1660,1663,1666,1669,1672,1675,1677],{"class":50,"line":1653},21,[48,1655,1624],{"class":102},[48,1657,558],{"class":106},[48,1659,1003],{"class":226},[48,1661,1662],{"class":106},"(df.columns)] ",[48,1664,1665],{"class":102},"+",[48,1667,1668],{"class":106}," df.astype(",[48,1670,1671],{"class":226},"object",[48,1673,1674],{"class":106},").where(df.notna(), ",[48,1676,595],{"class":226},[48,1678,1679],{"class":106},").values.tolist()\n",[48,1681,1683],{"class":50,"line":1682},22,[48,1684,133],{"emptyLinePlaceholder":132},[48,1686,1688,1691,1694],{"class":50,"line":1687},23,[48,1689,1690],{"class":106},"CleanTable.register(",[48,1692,1693],{"class":58},"\"clean\"",[48,1695,1013],{"class":106},[48,1697,1699,1702,1704,1706,1709,1711,1713,1715,1718,1720,1723,1725,1727,1729,1732,1734,1736,1738,1740,1742,1744,1746,1749,1751,1754,1756,1758],{"class":50,"line":1698},24,[48,1700,1701],{"class":106},"raw ",[48,1703,142],{"class":102},[48,1705,838],{"class":106},[48,1707,1708],{"class":58},"\"Order ID\"",[48,1710,72],{"class":106},[48,1712,851],{"class":58},[48,1714,72],{"class":106},[48,1716,1717],{"class":58},"\"Unit Price\"",[48,1719,854],{"class":106},[48,1721,1722],{"class":58},"\"SO-1\"",[48,1724,72],{"class":106},[48,1726,25],{"class":226},[48,1728,72],{"class":106},[48,1730,1731],{"class":226},"2.5",[48,1733,854],{"class":106},[48,1735,595],{"class":226},[48,1737,72],{"class":106},[48,1739,595],{"class":226},[48,1741,72],{"class":106},[48,1743,595],{"class":226},[48,1745,854],{"class":106},[48,1747,1748],{"class":58},"\"SO-2\"",[48,1750,72],{"class":106},[48,1752,1753],{"class":226},"7.0",[48,1755,72],{"class":106},[48,1757,595],{"class":226},[48,1759,901],{"class":106},[48,1761,1763,1766,1768],{"class":50,"line":1762},25,[48,1764,1765],{"class":106},"df ",[48,1767,142],{"class":102},[48,1769,1770],{"class":106}," CleanTable.read_value(raw, Options({}))\n",[48,1772,1774,1776],{"class":50,"line":1773},26,[48,1775,1062],{"class":226},[48,1777,1778],{"class":106},"(df.dtypes.to_dict())\n",[48,1780,1782,1784],{"class":50,"line":1781},27,[48,1783,1062],{"class":226},[48,1785,1786],{"class":106},"(CleanTable.write_value(df, Options({})))\n",[10,1788,1789,1790,1793,1794,72,1797,411,1800,1803,1804,1806,1807,1810,1811,1813,1814,1816,1817,1820,1821,411,1824,1827,1828,32],{},"The settings block becomes ",[19,1791,1792],{},"{'rate': 0.2, 'fx': 1.17, 'region': 'North'}",", ready to look values up by name. The custom converter tidies headers into ",[19,1795,1796],{},"order_id",[19,1798,1799],{},"qty",[19,1801,1802],{},"unit_price",", drops the blank row, and turns the ",[14,1805,78],{}," column — all whole numbers — into a nullable integer column while leaving ",[14,1808,1809],{},"Unit Price"," as floats; writing produces a header row and plain Python values with blanks as ",[19,1812,595],{},". Registered as ",[19,1815,1693],{},", it is used like a built-in: ",[19,1818,1819],{},"sheet[\"A1\"].options(\"clean\", expand=\"table\").value",". Putting conventions in a converter means every script in a project reads tables the same way, and, as here, its logic can be tested without Excel by calling ",[19,1822,1823],{},"read_value",[19,1825,1826],{},"write_value"," directly. Bulk reading and writing for speed is covered in ",[28,1829,1831],{"href":1830},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fspeed-up-xlwings-with-bulk-reads-and-writes\u002F","Speed Up xlwings with Bulk Reads and Writes",[34,1833,1835],{"id":1834},"options-on-write","Options on write",[10,1837,1838,1839,411,1842,1845,1846,1848,1849,1852,1853,1855,1856,1858,1859,1862],{},"Most options also work when writing. ",[19,1840,1841],{},"options(index=False)",[19,1843,1844],{},"options(header=False)"," control what part of a DataFrame is written; ",[19,1847,603],{}," writes rows as columns; and assigning a list of lists to a single anchor cell — ",[19,1850,1851],{},"sheet[\"A1\"].value = rows"," — fills the block starting there, so the target range never needs to be sized by hand. Writing ",[19,1854,595],{}," clears a cell, which is the counterpart of ",[19,1857,418],{}," on reading. Options can be combined, and chaining ",[19,1860,1861],{},"options()"," once per range keeps the intent visible in the code.",[34,1864,1866],{"id":1865},"common-pitfalls","Common pitfalls",[1868,1869,1870,1880,1888,1896,1904],"ul",{},[1871,1872,1873,1877,1878,32],"li",{},[1874,1875,1876],"strong",{},"Assuming a list of rows."," One row or one cell comes back flat or scalar; use ",[19,1879,261],{},[1871,1881,1882,1885,1886,32],{},[1874,1883,1884],{},"The first column vanishing."," The DataFrame converter uses it as the index; pass ",[19,1887,1105],{},[1871,1889,1890,1893,1894,32],{},[1874,1891,1892],{},"An extra index column in the sheet."," Write with ",[19,1895,1841],{},[1871,1897,1898,1903],{},[1874,1899,1900,1902],{},[19,1901,580],{}," on decimals."," It truncates every number in the range.",[1871,1905,1906,1909,1910,1912],{},[1874,1907,1908],{},"Blank rows inside a table."," ",[19,1911,599],{}," stops at the first one.",[34,1914,1916],{"id":1915},"conclusion","Conclusion",[10,1918,1919,1920,921,1923,1925,1926,411,1928,1930,1931,1933,1934,1936,1937,1939,1940,1943,1944,1946,1947,1949],{},"Control xlwings conversions with ",[19,1921,1922],{},"range.options()",[19,1924,261],{}," for a predictable list of rows, ",[19,1927,580],{},[19,1929,584],{}," for cleaner types, ",[19,1932,418],{}," for blanks, ",[19,1935,422],{}," for orientation and ",[19,1938,599],{}," to size the range from the data. Read tables with ",[19,1941,1942],{},"options(pd.DataFrame, index=False)"," and write them with ",[19,1945,1841],{},", use ",[19,1948,685],{}," for settings blocks, and put your own conventions in a registered converter whose logic you can test without Excel.",[34,1951,1953],{"id":1952},"frequently-asked-questions","Frequently asked questions",[10,1955,1956,1959],{},[1874,1957,1958],{},"Why does xlwings return a single value instead of a list?","\nA single cell returns a scalar and a single row or column a flat list; use options(ndim=2) to always get a list of rows.",[10,1961,1962,1965],{},[1874,1963,1964],{},"How do I read a range into a pandas DataFrame with xlwings without losing the first column?","\nUse range.options(pd.DataFrame, index=False, expand=\"table\").value; without index=False the first column becomes the index.",[10,1967,1968,1971],{},[1874,1969,1970],{},"How do I stop xlwings writing the DataFrame index?","\nWrite with range.options(index=False).value = df.",[10,1973,1974,1977],{},[1874,1975,1976],{},"How do I make xlwings return integers instead of floats?","\nUse options(numbers=int), but only for ranges where every number is whole, because decimals are truncated.",[34,1979,1981],{"id":1980},"related","Related",[1868,1983,1984,1990,1997,2004],{},[1871,1985,1986,1987,1989],{},"Up one level: ",[28,1988,31],{"href":30}," — live Excel from Python.",[1871,1991,1992,1996],{},[28,1993,1995],{"href":1994},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fread-and-write-a-live-excel-workbook-with-xlwings\u002F","Read and Write a Live Excel Workbook with xlwings"," — the basics.",[1871,1998,1999,2003],{},[28,2000,2002],{"href":2001},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fwork-with-named-ranges-and-tables-in-xlwings\u002F","Work with Named Ranges and Tables in xlwings"," — addressing data by name.",[1871,2005,2006,2010],{},[28,2007,2009],{"href":2008},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fhow-pandas-data-types-are-written-to-excel-cells\u002F","How pandas Data Types Are Written to Excel Cells"," — the file-based equivalent.",[2012,2013,2014],"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 .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 .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .sHg5J, html code.shiki .sHg5J{--shiki-default:#6F42C1;--shiki-dark:#91CBFF}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":44,"searchDepth":116,"depth":116,"links":2016},[2017,2018,2019,2020,2021,2022,2023,2024,2025,2026],{"id":36,"depth":116,"text":37},{"id":82,"depth":116,"text":83},{"id":403,"depth":116,"text":404},{"id":754,"depth":116,"text":755},{"id":1320,"depth":116,"text":1321},{"id":1834,"depth":116,"text":1835},{"id":1865,"depth":116,"text":1866},{"id":1915,"depth":116,"text":1916},{"id":1952,"depth":116,"text":1953},{"id":1980,"depth":116,"text":1981},"2026-10-02","Get the Python values you expect from xlwings: why a range comes back as a scalar, a list or a list of lists, and how ndim, numbers, dates, empty, transpose and expand change that; how the DataFrame converter treats headers and the index on read and write; dictionaries from two-column ranges; and writing a custom converter for your own conventions, with its logic tested without Excel.","md",[2031,2033,2035,2037],{"q":1958,"a":2032},"A single cell returns a scalar and a single row or column a flat list; use options(ndim=2) to always get a list of rows.",{"q":1964,"a":2034},"Use range.options(pd.DataFrame, index=False, expand=\"table\").value; without index=False the first column becomes the index.",{"q":1970,"a":2036},"Write with range.options(index=False).value = df.",{"q":1976,"a":2038},"Use options(numbers=int), but only for ranges where every number is whole, because decimals are truncated.","control how xlwings converts dataframes and ranges",null,{"breadcrumb":2042},[2043,2046,2049],{"name":2044,"item":2045},"Home","\u002F",{"name":2047,"item":2048},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":2050,"item":30},"Automating Excel With xlwings: The Basics","\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fcontrol-how-xlwings-converts-dataframes-and-ranges",{"title":5,"description":2053},"xlwings range.options explained: ndim, numbers, dates, empty, transpose, expand, pd.DataFrame with header and index, dict conversion, and custom Converter classes registered and tested in Python.","control-how-xlwings-converts-dataframes-and-ranges","getting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fcontrol-how-xlwings-converts-dataframes-and-ranges\u002Findex",[2057,2058],"xlwings","pandas","how-to","8Jf0sum341wp6p-2iO2n98rvK4-aMzm6BigCZJbCuig",[2062,2066],{"title":2063,"path":2064,"stem":2065,"children":-1},"Call Python from Excel with an xlwings UDF","\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fcall-python-from-excel-with-xlwings-udf","getting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fcall-python-from-excel-with-xlwings-udf\u002Findex",{"title":2067,"path":2068,"stem":2069,"children":-1},"Format Cells in a Live Workbook with xlwings","\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fformat-cells-in-a-live-workbook-with-xlwings","getting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fformat-cells-in-a-live-workbook-with-xlwings\u002Findex",[2071,2073],{"path":2072,"title":2050},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics",{"path":2074,"title":2047},"\u002Fgetting-started-with-python-excel-automation",[],[2077,2080,2083,2087,2088,2091],{"path":2078,"title":2079,"kind":2059},"\u002Fautomating-reporting-workflows\u002Ftesting-and-packaging-excel-automation-scripts\u002Ftest-win32com-and-xlwings-code-without-excel","Test win32com and xlwings Code Without Excel",{"path":2081,"title":2082,"kind":2059},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fautomate-excel-on-macos-with-python","Automate Excel on macOS with Python",{"path":2084,"title":2085,"kind":2086},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fxlwings-vs-pywin32-for-excel-automation","xlwings vs pywin32 for Excel Automation","compare",{"path":2068,"title":2067,"kind":2059},{"path":2089,"title":2090,"kind":2059},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Finsert-a-matplotlib-plot-into-a-live-workbook-with-xlwings","Insert a matplotlib Plot into a Live Workbook with xlwings",{"path":2092,"title":1995,"kind":2059},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fread-and-write-a-live-excel-workbook-with-xlwings",1790972056938]