[{"data":1,"prerenderedAt":2112},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-boolean-columns-from-excel-with-pandas":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-boolean-columns-from-excel-with-pandas":2077,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-boolean-columns-from-excel-with-pandas":2086,"children:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-boolean-columns-from-excel-with-pandas":2091,"related:\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-boolean-columns-from-excel-with-pandas":2092},{"id":4,"title":5,"body":6,"dateModified":2042,"datePublished":2042,"description":2043,"extension":2044,"faq":2045,"keyword":2054,"kind":2055,"meta":2056,"navigation":380,"path":2065,"seo":2066,"slug":2069,"stem":2070,"topics":2071,"type":2075,"__hash__":2076},"docs\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-boolean-columns-from-excel-with-pandas\u002Findex.md","Read Yes\u002FNo and TRUE\u002FFALSE Columns as Booleans with pandas",{"type":7,"value":8,"toc":2028},"minimark",[9,26,35,306,311,339,345,349,352,418,449,453,464,554,562,623,626,801,805,820,1275,1294,1298,1304,1370,1373,1558,1562,1578,1656,1664,1668,1676,1752,1788,1833,1842,1849,1853,1920,1924,1942,1946,1959,1963,1969,1979,1985,1991,1995,2024],[10,11,12,13,17,18,21,22,25],"p",{},"Flags are everywhere in business spreadsheets — active customers, approved invoices, paid orders — and people record them however they like. One sheet uses Excel's own TRUE\u002FFALSE, the next types \"Yes\" and \"No\", another uses Y\u002FN codes, 1\u002F0, or an \"x\" in the column when the answer is yes and nothing when it is no. ",[14,15,16],"code",{},"pandas.read_excel"," turns only the first of those into booleans by itself. Its ",[14,19,20],{},"true_values"," and ",[14,23,24],{},"false_values"," options handle more, but with rules strict enough that they often do nothing and say nothing.",[10,27,28,29,34],{},"This page, part of ",[30,31,33],"a",{"href":32},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002F","Reading Excel Files with pandas",", shows what each style of flag becomes, when the built-in options work, and how to convert every flag column into a boolean you can filter on and trust.",[36,37,45,46,45,50,45,54,45,62,45,69,45,78,45,82,45,88,45,92,45,96,45,100,45,104,45,108,45,112,45,116,45,120,45,124,45,128,45,132,45,136,45,140,45,145,45,147,45,151,45,156,45,160,45,164,45,168,45,172,45,176,45,178,45,181,45,185,45,189,45,191,45,194,45,197,45,201,45,203,45,206,45,208,45,211,45,213,45,216,45,218,45,221,45,223,45,225,45,227,45,230,45,232,45,235,45,238,45,242,45,244,45,246,45,248,45,251,45,253,45,256,45,258,45,261,45,263,45,265,45,267,45,270,45,272,45,275,45,279,45,281,45,283,45,285,45,287,45,289,45,291,45,293,45,295,45,297,45,299,45,302,45,304],"svg",{"viewBox":38,"role":39,"ariaLabelledBy":40,"xmlns":43,"style":44},"145 0 470 200","img",[41,42],"rbc-styles-t","rbc-styles-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:470px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[47,48,49],"title",{"id":41},"Six ways a spreadsheet records a yes or no",[51,52,53],"desc",{"id":42},"A worksheet with an id column and six flag columns: active holds native TRUE and FALSE cells; approved holds Yes and No text; paid holds Y and N with a blank; flag holds 1 and 0; opt_in holds yes, No with a trailing space and YES; done holds an x or nothing.",[55,56],"rect",{"x":57,"y":58,"width":59,"height":60,"fill":61},"145","0","470","200","#ffffff",[63,64,68],"text",{"x":65,"y":66,"style":67},"380.0","31.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","flags.xlsx",[55,70],{"x":71,"y":72,"width":73,"height":74,"rx":58,"fill":75,"stroke":76,"style":77},"161.1","48.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[55,79],{"x":80,"y":72,"width":81,"height":74,"rx":58,"fill":75,"stroke":76,"style":77},"193.1","44.0",[63,83,87],{"x":84,"y":85,"style":86},"215.1","65.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[55,89],{"x":90,"y":72,"width":91,"height":74,"rx":58,"fill":75,"stroke":76,"style":77},"237.1","63.8",[63,93,95],{"x":94,"y":85,"style":86},"269.0","B",[55,97],{"x":98,"y":72,"width":99,"height":74,"rx":58,"fill":75,"stroke":76,"style":77},"300.9","83.1",[63,101,103],{"x":102,"y":85,"style":86},"342.5","C",[55,105],{"x":106,"y":72,"width":107,"height":74,"rx":58,"fill":75,"stroke":76,"style":77},"384.0","50.6",[63,109,111],{"x":110,"y":85,"style":86},"409.3","D",[55,113],{"x":114,"y":72,"width":115,"height":74,"rx":58,"fill":75,"stroke":76,"style":77},"434.6","47.7",[63,117,119],{"x":118,"y":85,"style":86},"458.5","E",[55,121],{"x":122,"y":72,"width":123,"height":74,"rx":58,"fill":75,"stroke":76,"style":77},"482.3","61.5",[63,125,127],{"x":126,"y":85,"style":86},"513.1","F",[55,129],{"x":130,"y":72,"width":131,"height":74,"rx":58,"fill":75,"stroke":76,"style":77},"543.9","55.0",[63,133,135],{"x":134,"y":85,"style":86},"571.4","G",[55,137],{"x":71,"y":138,"width":73,"height":139,"rx":58,"fill":75,"stroke":76,"style":77},"72.0","28.0",[63,141,144],{"x":142,"y":143,"style":86},"177.1","91.0","1",[55,146],{"x":80,"y":138,"width":81,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,148,150],{"x":84,"y":143,"style":149},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","id",[55,152],{"x":90,"y":138,"width":91,"height":139,"rx":58,"fill":153,"stroke":154,"style":155},"#d9f4f1","var(--teal,#0f9488)","stroke-width:1.5px",[63,157,159],{"x":94,"y":143,"style":158},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","active",[55,161],{"x":98,"y":138,"width":99,"height":139,"rx":58,"fill":162,"stroke":163,"style":155},"#f0f4ff","var(--brand,#5b5cf0)",[63,165,167],{"x":102,"y":143,"style":166},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","approved",[55,169],{"x":106,"y":138,"width":107,"height":139,"rx":58,"fill":170,"stroke":171,"style":155},"#fdefd8","var(--gold,#b4740a)",[63,173,175],{"x":110,"y":143,"style":174},"font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","paid",[55,177],{"x":114,"y":138,"width":115,"height":139,"rx":58,"fill":75,"stroke":76,"style":155},[63,179,180],{"x":118,"y":143,"style":149},"flag",[55,182],{"x":122,"y":138,"width":123,"height":139,"rx":58,"fill":183,"stroke":184,"style":155},"#fee8f2","var(--accent,#f43f8f)",[63,186,188],{"x":126,"y":143,"style":187},"font-size:12px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","opt_in",[55,190],{"x":130,"y":138,"width":131,"height":139,"rx":58,"fill":170,"stroke":171,"style":155},[63,192,193],{"x":134,"y":143,"style":174},"done",[55,195],{"x":71,"y":196,"width":73,"height":139,"rx":58,"fill":75,"stroke":76,"style":77},"100.0",[63,198,200],{"x":142,"y":199,"style":86},"119.0","2",[55,202],{"x":80,"y":196,"width":81,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,204,144],{"x":84,"y":199,"style":205},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[55,207],{"x":90,"y":196,"width":91,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,209,210],{"x":94,"y":199,"style":205},"TRUE",[55,212],{"x":98,"y":196,"width":99,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,214,215],{"x":102,"y":199,"style":205},"Yes",[55,217],{"x":106,"y":196,"width":107,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,219,220],{"x":110,"y":199,"style":205},"Y",[55,222],{"x":114,"y":196,"width":115,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,224,144],{"x":118,"y":199,"style":205},[55,226],{"x":122,"y":196,"width":123,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,228,229],{"x":126,"y":199,"style":205},"yes",[55,231],{"x":130,"y":196,"width":131,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,233,234],{"x":134,"y":199,"style":205},"x",[55,236],{"x":71,"y":237,"width":73,"height":139,"rx":58,"fill":75,"stroke":76,"style":77},"128.0",[63,239,241],{"x":142,"y":240,"style":86},"147.0","3",[55,243],{"x":80,"y":237,"width":81,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,245,200],{"x":84,"y":240,"style":205},[55,247],{"x":90,"y":237,"width":91,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,249,250],{"x":94,"y":240,"style":205},"FALSE",[55,252],{"x":98,"y":237,"width":99,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,254,255],{"x":102,"y":240,"style":205},"No",[55,257],{"x":106,"y":237,"width":107,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,259,260],{"x":110,"y":240,"style":205},"N",[55,262],{"x":114,"y":237,"width":115,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,264,58],{"x":118,"y":240,"style":205},[55,266],{"x":122,"y":237,"width":123,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,268,269],{"x":126,"y":240,"style":205},"No ",[55,271],{"x":130,"y":237,"width":131,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[55,273],{"x":71,"y":274,"width":73,"height":139,"rx":58,"fill":75,"stroke":76,"style":77},"156.0",[63,276,278],{"x":142,"y":277,"style":86},"175.0","4",[55,280],{"x":80,"y":274,"width":81,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,282,241],{"x":84,"y":277,"style":205},[55,284],{"x":90,"y":274,"width":91,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,286,210],{"x":94,"y":277,"style":205},[55,288],{"x":98,"y":274,"width":99,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,290,215],{"x":102,"y":277,"style":205},[55,292],{"x":106,"y":274,"width":107,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[55,294],{"x":114,"y":274,"width":115,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,296,144],{"x":118,"y":277,"style":205},[55,298],{"x":122,"y":274,"width":123,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,300,301],{"x":126,"y":277,"style":205},"YES",[55,303],{"x":130,"y":274,"width":131,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,305,234],{"x":134,"y":277,"style":205},[307,308,310],"h2",{"id":309},"prerequisites","Prerequisites",[312,313,318],"pre",{"className":314,"code":315,"language":316,"meta":317,"style":317},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[14,319,320],{"__ignoreMap":317},[321,322,325,329,333,336],"span",{"class":323,"line":324},"line",1,[321,326,328],{"class":327},"sMTad","pip",[321,330,332],{"class":331},"srMev"," install",[321,334,335],{"class":331}," pandas",[321,337,338],{"class":331}," openpyxl\n",[10,340,341,342,344],{},"The examples read ",[14,343,68],{},", which has one column for each style shown above.",[307,346,348],{"id":347},"step-1-see-what-read_excel-does-by-default","Step 1 — See what read_excel does by default",[10,350,351],{},"Read the sheet with no options and look at the types:",[312,353,357],{"className":354,"code":355,"language":356,"meta":317,"style":317},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\nraw = pd.read_excel(\"flags.xlsx\")\nprint(raw.dtypes.to_dict())\nprint(raw)\n","python",[14,358,359,375,382,400,410],{"__ignoreMap":317},[321,360,361,365,369,372],{"class":323,"line":324},[321,362,364],{"class":363},"s-kum","import",[321,366,368],{"class":367},"skGVy"," pandas ",[321,370,371],{"class":363},"as",[321,373,374],{"class":367}," pd\n",[321,376,378],{"class":323,"line":377},2,[321,379,381],{"emptyLinePlaceholder":380},true,"\n",[321,383,385,388,391,394,397],{"class":323,"line":384},3,[321,386,387],{"class":367},"raw ",[321,389,390],{"class":363},"=",[321,392,393],{"class":367}," pd.read_excel(",[321,395,396],{"class":331},"\"flags.xlsx\"",[321,398,399],{"class":367},")\n",[321,401,403,407],{"class":323,"line":402},4,[321,404,406],{"class":405},"sP0c6","print",[321,408,409],{"class":367},"(raw.dtypes.to_dict())\n",[321,411,413,415],{"class":323,"line":412},5,[321,414,406],{"class":405},[321,416,417],{"class":367},"(raw)\n",[10,419,420,421,423,424,427,428,21,431,434,435,437,438,441,442,444,445,448],{},"Only ",[14,422,159],{}," arrives as ",[14,425,426],{},"bool",", because Excel stored it as real boolean cells and openpyxl hands pandas Python ",[14,429,430],{},"True",[14,432,433],{},"False",". ",[14,436,180],{}," is ",[14,439,440],{},"int64",", which is right for a number and wrong for a flag. Every text style — Yes\u002FNo, Y\u002FN, the mixed-case ",[14,443,188],{},", the tick marks — is a string column. Filtering on those with ",[14,446,447],{},"df[df[\"approved\"]]"," raises an error or, worse, treats every non-empty string as true.",[307,450,452],{"id":451},"step-2-use-true_values-and-false_values-for-clean-columns","Step 2 — Use true_values and false_values for clean columns",[10,454,455,458,459,21,461,463],{},[14,456,457],{},"read_excel"," accepts lists of values to treat as ",[14,460,430],{},[14,462,433],{},". For a column spelled consistently and without blanks, this is all you need:",[312,465,467],{"className":354,"code":466,"language":356,"meta":317,"style":317},"typed = pd.read_excel(\"flags.xlsx\", true_values=[\"Yes\", \"Y\", \"yes\"], false_values=[\"No\", \"N\"])\nprint(typed.dtypes.to_dict())\nprint(typed[[\"approved\", \"paid\", \"opt_in\"]])\n",[14,468,469,524,531],{"__ignoreMap":317},[321,470,471,474,476,478,480,483,486,488,491,494,496,499,501,504,507,509,511,513,516,518,521],{"class":323,"line":324},[321,472,473],{"class":367},"typed ",[321,475,390],{"class":363},[321,477,393],{"class":367},[321,479,396],{"class":331},[321,481,482],{"class":367},", ",[321,484,20],{"class":485},"sa561",[321,487,390],{"class":363},[321,489,490],{"class":367},"[",[321,492,493],{"class":331},"\"Yes\"",[321,495,482],{"class":367},[321,497,498],{"class":331},"\"Y\"",[321,500,482],{"class":367},[321,502,503],{"class":331},"\"yes\"",[321,505,506],{"class":367},"], ",[321,508,24],{"class":485},[321,510,390],{"class":363},[321,512,490],{"class":367},[321,514,515],{"class":331},"\"No\"",[321,517,482],{"class":367},[321,519,520],{"class":331},"\"N\"",[321,522,523],{"class":367},"])\n",[321,525,526,528],{"class":323,"line":377},[321,527,406],{"class":405},[321,529,530],{"class":367},"(typed.dtypes.to_dict())\n",[321,532,533,535,538,541,543,546,548,551],{"class":323,"line":384},[321,534,406],{"class":405},[321,536,537],{"class":367},"(typed[[",[321,539,540],{"class":331},"\"approved\"",[321,542,482],{"class":367},[321,544,545],{"class":331},"\"paid\"",[321,547,482],{"class":367},[321,549,550],{"class":331},"\"opt_in\"",[321,552,553],{"class":367},"]])\n",[10,555,556,558,559,561],{},[14,557,167],{}," becomes a proper ",[14,560,426],{}," column. The other two show the rules:",[563,564,565,588,612],"ul",{},[566,567,568,572,573,577,578,580,581,21,584,587],"li",{},[569,570,571],"strong",{},"All or nothing."," A column is converted only when ",[574,575,576],"em",{},"every"," non-empty value is in one of the two lists. ",[14,579,188],{}," holds ",[14,582,583],{},"\"YES\"",[14,585,586],{},"\"No \""," with a trailing space; neither matches exactly, so the column is left as text — with no warning.",[566,589,590,593,594,596,597,599,600,603,604,482,606,21,608,611],{},[569,591,592],{},"Blanks break the type."," ",[14,595,175],{}," has an empty cell. Its values are converted, but a NumPy ",[14,598,426],{}," column cannot hold a missing value, so pandas falls back to an ",[14,601,602],{},"object"," column of ",[14,605,430],{},[14,607,433],{},[14,609,610],{},"NaN",", which behaves badly in filters.",[566,613,614,617,618,21,620,622],{},[569,615,616],{},"Exact matching."," The comparison is case-sensitive and whitespace-sensitive; ",[14,619,503],{},[14,621,493],{}," must both be listed.",[10,624,625],{},"The options apply to every column in the sheet, too, so a \"Y\" in a free-text notes column would be matched as well — another reason to prefer converting named columns explicitly.",[36,627,45,633,45,636,45,639,45,643,45,649,45,654,45,660,45,663,45,667,45,670,45,674,45,678,45,683,45,685,45,689,45,691,45,693,45,695,45,698,45,701,45,705,45,707,45,710,45,712,45,714,45,716,45,718,45,721,45,725,45,727,45,730,45,732,45,735,45,737,45,740,45,743,45,747,45,749,45,752,45,754,45,756,45,758,45,760,45,763,45,767,45,769,45,772,45,774,45,776,45,778,45,780,45,783,45,787,45,789,45,792,45,794,45,796,45,798],{"viewBox":628,"role":39,"ariaLabelledBy":629,"xmlns":43,"style":632},"4 0 752 276",[630,631],"rbc-outcomes-t","rbc-outcomes-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[47,634,635],{"id":630},"What each flag style becomes",[51,637,638],{"id":631},"A grid of flag styles against three reading methods. Native TRUE\u002FFALSE cells become bool with every method. Yes\u002FNo text without blanks becomes bool with true_values or the mapping. Y\u002FN with a blank becomes an object column with true_values and a nullable boolean with the mapping. Mixed case and trailing spaces stay text with true_values and become boolean with the mapping. 1\u002F0 stays an integer unless converted explicitly. Tick marks need a separate rule.",[55,640],{"x":278,"y":58,"width":641,"height":642,"fill":61},"752","276",[55,644],{"x":645,"y":646,"width":647,"height":648,"rx":58,"fill":61,"stroke":76,"style":77},"20.0","16.0","151.5","40.0",[55,650],{"x":651,"y":646,"width":652,"height":648,"rx":58,"fill":162,"stroke":163,"style":653},"171.5","189.5","stroke-width:1.2px",[63,655,659],{"x":656,"y":657,"style":658},"266.3","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","No options",[55,661],{"x":662,"y":646,"width":652,"height":648,"rx":58,"fill":162,"stroke":163,"style":653},"361.0",[63,664,666],{"x":665,"y":657,"style":658},"455.8","true_values \u002F false_values",[55,668],{"x":669,"y":646,"width":652,"height":648,"rx":58,"fill":162,"stroke":163,"style":653},"550.5",[63,671,673],{"x":672,"y":657,"style":658},"645.3","Strict mapping",[55,675],{"x":645,"y":676,"width":647,"height":677,"rx":58,"fill":75,"stroke":76,"style":77},"56.0","34.0",[63,679,682],{"x":73,"y":680,"style":681},"78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","TRUE \u002F FALSE cells",[55,684],{"x":651,"y":676,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,686,426],{"x":656,"y":687,"style":688},"77.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[55,690],{"x":662,"y":676,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,692,426],{"x":665,"y":687,"style":688},[55,694],{"x":669,"y":676,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,696,697],{"x":672,"y":687,"style":688},"boolean",[55,699],{"x":645,"y":700,"width":647,"height":677,"rx":58,"fill":75,"stroke":76,"style":77},"90.0",[63,702,704],{"x":73,"y":703,"style":681},"112.0","Yes \u002F No, no blanks",[55,706],{"x":651,"y":700,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,708,63],{"x":656,"y":709,"style":688},"111.0",[55,711],{"x":662,"y":700,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,713,426],{"x":665,"y":709,"style":688},[55,715],{"x":669,"y":700,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,717,697],{"x":672,"y":709,"style":688},[55,719],{"x":645,"y":720,"width":647,"height":677,"rx":58,"fill":75,"stroke":76,"style":77},"124.0",[63,722,724],{"x":73,"y":723,"style":681},"146.0","Y \u002F N with a blank",[55,726],{"x":651,"y":720,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,728,63],{"x":656,"y":729,"style":688},"145.0",[55,731],{"x":662,"y":720,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,733,734],{"x":665,"y":729,"style":688},"object (True\u002FFalse\u002FNaN)",[55,736],{"x":669,"y":720,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,738,739],{"x":672,"y":729,"style":688},"boolean with \u003CNA>",[55,741],{"x":645,"y":742,"width":647,"height":677,"rx":58,"fill":75,"stroke":76,"style":77},"158.0",[63,744,746],{"x":73,"y":745,"style":681},"180.0","yes \u002F \"No \" \u002F YES",[55,748],{"x":651,"y":742,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,750,63],{"x":656,"y":751,"style":688},"179.0",[55,753],{"x":662,"y":742,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,755,63],{"x":665,"y":751,"style":688},[55,757],{"x":669,"y":742,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,759,697],{"x":672,"y":751,"style":688},[55,761],{"x":645,"y":762,"width":647,"height":677,"rx":58,"fill":75,"stroke":76,"style":77},"192.0",[63,764,766],{"x":73,"y":765,"style":681},"214.0","1 \u002F 0",[55,768],{"x":651,"y":762,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,770,440],{"x":656,"y":771,"style":688},"213.0",[55,773],{"x":662,"y":762,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,775,440],{"x":665,"y":771,"style":688},[55,777],{"x":669,"y":762,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,779,697],{"x":672,"y":771,"style":688},[55,781],{"x":645,"y":782,"width":647,"height":677,"rx":58,"fill":75,"stroke":76,"style":77},"226.0",[63,784,786],{"x":73,"y":785,"style":681},"248.0","x or blank",[55,788],{"x":651,"y":782,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,790,63],{"x":656,"y":791,"style":688},"247.0",[55,793],{"x":662,"y":782,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,795,63],{"x":665,"y":791,"style":688},[55,797],{"x":669,"y":782,"width":652,"height":677,"rx":58,"fill":61,"stroke":76,"style":77},[63,799,800],{"x":672,"y":791,"style":688},"separate rule",[307,802,804],{"id":803},"step-3-convert-flag-columns-with-a-strict-mapping","Step 3 — Convert flag columns with a strict mapping",[10,806,807,808,811,812,482,814,21,816,819],{},"For sheets that people type into, normalise the text and map it yourself. Two decisions make the conversion trustworthy: use pandas' nullable ",[14,809,810],{},"\"boolean\""," dtype, which holds ",[14,813,430],{},[14,815,433],{},[14,817,818],{},"\u003CNA>",", and raise an error on any value you did not expect, so a new spelling is reported instead of silently becoming missing:",[312,821,823],{"className":354,"code":822,"language":356,"meta":317,"style":317},"TRUE_WORDS = {\"yes\", \"y\", \"true\", \"t\", \"1\", \"x\", \"✓\"}\nFALSE_WORDS = {\"no\", \"n\", \"false\", \"f\", \"0\"}\n\ndef to_flag(series, blank_means=pd.NA):\n    def convert(value):\n        if pd.isna(value) or str(value).strip() == \"\":\n            return blank_means\n        if isinstance(value, bool):\n            return value\n        word = str(value).strip().lower()\n        if isinstance(value, (int, float)) and value in (0, 1):\n            word = str(int(value))\n        if word in TRUE_WORDS:\n            return True\n        if word in FALSE_WORDS:\n            return False\n        raise ValueError(f\"{series.name}: unexpected flag value {value!r}\")\n    return series.map(convert).astype(\"boolean\")\n\ndf = pd.read_excel(\"flags.xlsx\")\nfor col in [\"active\", \"approved\", \"paid\", \"flag\", \"opt_in\"]:\n    df[col] = to_flag(df[col])\nprint(df.dtypes.to_dict())\nprint(df[[\"approved\", \"paid\", \"opt_in\", \"flag\"]])\n",[14,824,825,871,905,909,932,943,970,979,994,1002,1015,1056,1074,1089,1097,1111,1119,1163,1176,1181,1195,1232,1243,1251],{"__ignoreMap":317},[321,826,827,830,833,836,838,840,843,845,848,850,853,855,858,860,863,865,868],{"class":323,"line":324},[321,828,829],{"class":405},"TRUE_WORDS",[321,831,832],{"class":363}," =",[321,834,835],{"class":367}," {",[321,837,503],{"class":331},[321,839,482],{"class":367},[321,841,842],{"class":331},"\"y\"",[321,844,482],{"class":367},[321,846,847],{"class":331},"\"true\"",[321,849,482],{"class":367},[321,851,852],{"class":331},"\"t\"",[321,854,482],{"class":367},[321,856,857],{"class":331},"\"1\"",[321,859,482],{"class":367},[321,861,862],{"class":331},"\"x\"",[321,864,482],{"class":367},[321,866,867],{"class":331},"\"✓\"",[321,869,870],{"class":367},"}\n",[321,872,873,876,878,880,883,885,888,890,893,895,898,900,903],{"class":323,"line":377},[321,874,875],{"class":405},"FALSE_WORDS",[321,877,832],{"class":363},[321,879,835],{"class":367},[321,881,882],{"class":331},"\"no\"",[321,884,482],{"class":367},[321,886,887],{"class":331},"\"n\"",[321,889,482],{"class":367},[321,891,892],{"class":331},"\"false\"",[321,894,482],{"class":367},[321,896,897],{"class":331},"\"f\"",[321,899,482],{"class":367},[321,901,902],{"class":331},"\"0\"",[321,904,870],{"class":367},[321,906,907],{"class":323,"line":384},[321,908,381],{"emptyLinePlaceholder":380},[321,910,911,914,918,921,923,926,929],{"class":323,"line":402},[321,912,913],{"class":363},"def",[321,915,917],{"class":916},"s_Opv"," to_flag",[321,919,920],{"class":367},"(series, blank_means",[321,922,390],{"class":363},[321,924,925],{"class":367},"pd.",[321,927,928],{"class":405},"NA",[321,930,931],{"class":367},"):\n",[321,933,934,937,940],{"class":323,"line":412},[321,935,936],{"class":363},"    def",[321,938,939],{"class":916}," convert",[321,941,942],{"class":367},"(value):\n",[321,944,946,949,952,955,958,961,964,967],{"class":323,"line":945},6,[321,947,948],{"class":363},"        if",[321,950,951],{"class":367}," pd.isna(value) ",[321,953,954],{"class":363},"or",[321,956,957],{"class":405}," str",[321,959,960],{"class":367},"(value).strip() ",[321,962,963],{"class":363},"==",[321,965,966],{"class":331}," \"\"",[321,968,969],{"class":367},":\n",[321,971,973,976],{"class":323,"line":972},7,[321,974,975],{"class":363},"            return",[321,977,978],{"class":367}," blank_means\n",[321,980,982,984,987,990,992],{"class":323,"line":981},8,[321,983,948],{"class":363},[321,985,986],{"class":405}," isinstance",[321,988,989],{"class":367},"(value, ",[321,991,426],{"class":405},[321,993,931],{"class":367},[321,995,997,999],{"class":323,"line":996},9,[321,998,975],{"class":363},[321,1000,1001],{"class":367}," value\n",[321,1003,1005,1008,1010,1012],{"class":323,"line":1004},10,[321,1006,1007],{"class":367},"        word ",[321,1009,390],{"class":363},[321,1011,957],{"class":405},[321,1013,1014],{"class":367},"(value).strip().lower()\n",[321,1016,1018,1020,1022,1025,1028,1030,1033,1036,1039,1042,1045,1048,1050,1052,1054],{"class":323,"line":1017},11,[321,1019,948],{"class":363},[321,1021,986],{"class":405},[321,1023,1024],{"class":367},"(value, (",[321,1026,1027],{"class":405},"int",[321,1029,482],{"class":367},[321,1031,1032],{"class":405},"float",[321,1034,1035],{"class":367},")) ",[321,1037,1038],{"class":363},"and",[321,1040,1041],{"class":367}," value ",[321,1043,1044],{"class":363},"in",[321,1046,1047],{"class":367}," (",[321,1049,58],{"class":405},[321,1051,482],{"class":367},[321,1053,144],{"class":405},[321,1055,931],{"class":367},[321,1057,1059,1062,1064,1066,1069,1071],{"class":323,"line":1058},12,[321,1060,1061],{"class":367},"            word ",[321,1063,390],{"class":363},[321,1065,957],{"class":405},[321,1067,1068],{"class":367},"(",[321,1070,1027],{"class":405},[321,1072,1073],{"class":367},"(value))\n",[321,1075,1077,1079,1082,1084,1087],{"class":323,"line":1076},13,[321,1078,948],{"class":363},[321,1080,1081],{"class":367}," word ",[321,1083,1044],{"class":363},[321,1085,1086],{"class":405}," TRUE_WORDS",[321,1088,969],{"class":367},[321,1090,1092,1094],{"class":323,"line":1091},14,[321,1093,975],{"class":363},[321,1095,1096],{"class":405}," True\n",[321,1098,1100,1102,1104,1106,1109],{"class":323,"line":1099},15,[321,1101,948],{"class":363},[321,1103,1081],{"class":367},[321,1105,1044],{"class":363},[321,1107,1108],{"class":405}," FALSE_WORDS",[321,1110,969],{"class":367},[321,1112,1114,1116],{"class":323,"line":1113},16,[321,1115,975],{"class":363},[321,1117,1118],{"class":405}," False\n",[321,1120,1122,1125,1128,1130,1133,1136,1140,1143,1146,1149,1151,1154,1157,1159,1161],{"class":323,"line":1121},17,[321,1123,1124],{"class":363},"        raise",[321,1126,1127],{"class":405}," ValueError",[321,1129,1068],{"class":367},[321,1131,1132],{"class":363},"f",[321,1134,1135],{"class":331},"\"",[321,1137,1139],{"class":1138},"sSjpA","{",[321,1141,1142],{"class":367},"series.name",[321,1144,1145],{"class":1138},"}",[321,1147,1148],{"class":331},": unexpected flag value ",[321,1150,1139],{"class":1138},[321,1152,1153],{"class":367},"value",[321,1155,1156],{"class":363},"!r",[321,1158,1145],{"class":1138},[321,1160,1135],{"class":331},[321,1162,399],{"class":367},[321,1164,1166,1169,1172,1174],{"class":323,"line":1165},18,[321,1167,1168],{"class":363},"    return",[321,1170,1171],{"class":367}," series.map(convert).astype(",[321,1173,810],{"class":331},[321,1175,399],{"class":367},[321,1177,1179],{"class":323,"line":1178},19,[321,1180,381],{"emptyLinePlaceholder":380},[321,1182,1184,1187,1189,1191,1193],{"class":323,"line":1183},20,[321,1185,1186],{"class":367},"df ",[321,1188,390],{"class":363},[321,1190,393],{"class":367},[321,1192,396],{"class":331},[321,1194,399],{"class":367},[321,1196,1198,1201,1204,1206,1209,1212,1214,1216,1218,1220,1222,1225,1227,1229],{"class":323,"line":1197},21,[321,1199,1200],{"class":363},"for",[321,1202,1203],{"class":367}," col ",[321,1205,1044],{"class":363},[321,1207,1208],{"class":367}," [",[321,1210,1211],{"class":331},"\"active\"",[321,1213,482],{"class":367},[321,1215,540],{"class":331},[321,1217,482],{"class":367},[321,1219,545],{"class":331},[321,1221,482],{"class":367},[321,1223,1224],{"class":331},"\"flag\"",[321,1226,482],{"class":367},[321,1228,550],{"class":331},[321,1230,1231],{"class":367},"]:\n",[321,1233,1235,1238,1240],{"class":323,"line":1234},22,[321,1236,1237],{"class":367},"    df[col] ",[321,1239,390],{"class":363},[321,1241,1242],{"class":367}," to_flag(df[col])\n",[321,1244,1246,1248],{"class":323,"line":1245},23,[321,1247,406],{"class":405},[321,1249,1250],{"class":367},"(df.dtypes.to_dict())\n",[321,1252,1254,1256,1259,1261,1263,1265,1267,1269,1271,1273],{"class":323,"line":1253},24,[321,1255,406],{"class":405},[321,1257,1258],{"class":367},"(df[[",[321,1260,540],{"class":331},[321,1262,482],{"class":367},[321,1264,545],{"class":331},[321,1266,482],{"class":367},[321,1268,550],{"class":331},[321,1270,482],{"class":367},[321,1272,1224],{"class":331},[321,1274,553],{"class":367},[10,1276,1277,1278,434,1280,1282,1283,1285,1286,1289,1290,1293],{},"Every flag column is now ",[14,1279,697],{},[14,1281,175],{}," keeps its blank as ",[14,1284,818],{},", which is honest: nobody recorded whether that order was paid. Filtering with ",[14,1287,1288],{},"df[df[\"paid\"]]"," would fail on the missing value, so fill it according to the business rule before filtering — ",[14,1291,1292],{},"df[\"paid\"].fillna(False)"," when a blank means \"not yet\", for example.",[307,1295,1297],{"id":1296},"step-4-treat-a-tick-or-a-blank-as-yes-or-no","Step 4 — Treat a tick or a blank as yes or no",[10,1299,1300,1301,1303],{},"The ",[14,1302,193],{}," column uses the paper-form convention: an \"x\" when true, nothing when false. There a blank is not unknown — it is an answer. Pass the meaning of a blank explicitly:",[312,1305,1307],{"className":354,"code":1306,"language":356,"meta":317,"style":317},"df[\"done\"] = to_flag(df[\"done\"], blank_means=False)\nprint(df[[\"id\", \"done\"]])\nprint(df[\"done\"].dtype, df[\"done\"].sum())\n",[14,1308,1309,1338,1353],{"__ignoreMap":317},[321,1310,1311,1314,1317,1320,1322,1325,1327,1329,1332,1334,1336],{"class":323,"line":324},[321,1312,1313],{"class":367},"df[",[321,1315,1316],{"class":331},"\"done\"",[321,1318,1319],{"class":367},"] ",[321,1321,390],{"class":363},[321,1323,1324],{"class":367}," to_flag(df[",[321,1326,1316],{"class":331},[321,1328,506],{"class":367},[321,1330,1331],{"class":485},"blank_means",[321,1333,390],{"class":363},[321,1335,433],{"class":405},[321,1337,399],{"class":367},[321,1339,1340,1342,1344,1347,1349,1351],{"class":323,"line":377},[321,1341,406],{"class":405},[321,1343,1258],{"class":367},[321,1345,1346],{"class":331},"\"id\"",[321,1348,482],{"class":367},[321,1350,1316],{"class":331},[321,1352,553],{"class":367},[321,1354,1355,1357,1360,1362,1365,1367],{"class":323,"line":384},[321,1356,406],{"class":405},[321,1358,1359],{"class":367},"(df[",[321,1361,1316],{"class":331},[321,1363,1364],{"class":367},"].dtype, df[",[321,1366,1316],{"class":331},[321,1368,1369],{"class":367},"].sum())\n",[10,1371,1372],{},"Making the rule an argument rather than a default keeps the two conventions apart in your code. A blank in \"paid\" and a blank in \"done\" mean different things, and the column-by-column calls say so.",[36,1374,45,1380,45,1383,45,1386,45,1391,45,1394,45,1397,45,1401,45,1404,45,1407,45,1410,45,1414,45,1416,45,1418,45,1420,45,1422,45,1425,45,1427,45,1429,45,1431,45,1433,45,1435,45,1438,45,1441,45,1443,45,1445,45,1447,45,1449,45,1452,45,1455,45,1457,45,1459,45,1461,45,1464,45,1467,45,1470,45,1474,45,1477,45,1479,45,1482,45,1484,45,1486,45,1488,45,1490,45,1492,45,1494,45,1496,45,1498,45,1500,45,1502,45,1504,45,1506,45,1508,45,1510,45,1512,45,1514,45,1516,45,1518,45,1520,45,1522,45,1524,45,1526,45,1532,45,1536,45,1541,45,1546,45,1553],{"viewBox":1375,"role":39,"ariaLabelledBy":1376,"xmlns":43,"style":1379},"141 0 477 192",[1377,1378],"rbc-map-t","rbc-map-d","width:100%;max-width:477px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[47,1381,1382],{"id":1377},"Normalising messy flags to a nullable boolean",[51,1384,1385],{"id":1378},"Before: an opt_in column holding yes, No with a trailing space, and YES, plus a paid column holding Y, N and a blank. After to_flag: opt_in holds True, False, True and paid holds True, False and NA, both with the nullable boolean dtype.",[55,1387],{"x":1388,"y":58,"width":1389,"height":1390,"fill":61},"141","477","192",[55,1392],{"x":1393,"y":648,"width":73,"height":74,"rx":58,"fill":75,"stroke":76,"style":77},"157.7",[55,1395],{"x":1396,"y":648,"width":123,"height":74,"rx":58,"fill":75,"stroke":76,"style":77},"189.7",[63,1398,87],{"x":1399,"y":1400,"style":86},"220.5","57.0",[55,1402],{"x":1403,"y":648,"width":107,"height":74,"rx":58,"fill":75,"stroke":76,"style":77},"251.2",[63,1405,95],{"x":1406,"y":1400,"style":86},"276.5",[55,1408],{"x":1393,"y":1409,"width":73,"height":139,"rx":58,"fill":75,"stroke":76,"style":77},"64.0",[63,1411,144],{"x":1412,"y":1413,"style":86},"173.7","83.0",[55,1415],{"x":1396,"y":1409,"width":123,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,1417,188],{"x":1399,"y":1413,"style":149},[55,1419],{"x":1403,"y":1409,"width":107,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,1421,175],{"x":1406,"y":1413,"style":149},[55,1423],{"x":1393,"y":1424,"width":73,"height":139,"rx":58,"fill":75,"stroke":76,"style":77},"92.0",[63,1426,200],{"x":1412,"y":709,"style":86},[55,1428],{"x":1396,"y":1424,"width":123,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,1430,229],{"x":1399,"y":709,"style":205},[55,1432],{"x":1403,"y":1424,"width":107,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,1434,220],{"x":1406,"y":709,"style":205},[55,1436],{"x":1393,"y":1437,"width":73,"height":139,"rx":58,"fill":75,"stroke":76,"style":77},"120.0",[63,1439,241],{"x":1412,"y":1440,"style":86},"139.0",[55,1442],{"x":1396,"y":1437,"width":123,"height":139,"rx":58,"fill":183,"stroke":184,"style":155},[63,1444,269],{"x":1399,"y":1440,"style":187},[55,1446],{"x":1403,"y":1437,"width":107,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,1448,260],{"x":1406,"y":1440,"style":205},[55,1450],{"x":1393,"y":1451,"width":73,"height":139,"rx":58,"fill":75,"stroke":76,"style":77},"148.0",[63,1453,278],{"x":1412,"y":1454,"style":86},"167.0",[55,1456],{"x":1396,"y":1451,"width":123,"height":139,"rx":58,"fill":183,"stroke":184,"style":155},[63,1458,301],{"x":1399,"y":1454,"style":187},[55,1460],{"x":1403,"y":1451,"width":107,"height":139,"rx":58,"fill":170,"stroke":171,"style":155},[55,1462],{"x":1463,"y":648,"width":73,"height":74,"rx":58,"fill":75,"stroke":76,"style":77},"448.6",[55,1465],{"x":1466,"y":648,"width":123,"height":74,"rx":58,"fill":75,"stroke":76,"style":77},"480.6",[63,1468,87],{"x":1469,"y":1400,"style":86},"511.4",[55,1471],{"x":1472,"y":648,"width":1473,"height":74,"rx":58,"fill":75,"stroke":76,"style":77},"542.2","60.1",[63,1475,95],{"x":1476,"y":1400,"style":86},"572.2",[55,1478],{"x":1463,"y":1409,"width":73,"height":139,"rx":58,"fill":75,"stroke":76,"style":77},[63,1480,144],{"x":1481,"y":1413,"style":86},"464.6",[55,1483],{"x":1466,"y":1409,"width":123,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,1485,188],{"x":1469,"y":1413,"style":149},[55,1487],{"x":1472,"y":1409,"width":1473,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,1489,175],{"x":1476,"y":1413,"style":149},[55,1491],{"x":1463,"y":1424,"width":73,"height":139,"rx":58,"fill":75,"stroke":76,"style":77},[63,1493,200],{"x":1481,"y":709,"style":86},[55,1495],{"x":1466,"y":1424,"width":123,"height":139,"rx":58,"fill":153,"stroke":154,"style":155},[63,1497,430],{"x":1469,"y":709,"style":158},[55,1499],{"x":1472,"y":1424,"width":1473,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,1501,430],{"x":1476,"y":709,"style":205},[55,1503],{"x":1463,"y":1437,"width":73,"height":139,"rx":58,"fill":75,"stroke":76,"style":77},[63,1505,241],{"x":1481,"y":1440,"style":86},[55,1507],{"x":1466,"y":1437,"width":123,"height":139,"rx":58,"fill":153,"stroke":154,"style":155},[63,1509,433],{"x":1469,"y":1440,"style":158},[55,1511],{"x":1472,"y":1437,"width":1473,"height":139,"rx":58,"fill":61,"stroke":76,"style":77},[63,1513,433],{"x":1476,"y":1440,"style":205},[55,1515],{"x":1463,"y":1451,"width":73,"height":139,"rx":58,"fill":75,"stroke":76,"style":77},[63,1517,278],{"x":1481,"y":1454,"style":86},[55,1519],{"x":1466,"y":1451,"width":123,"height":139,"rx":58,"fill":153,"stroke":154,"style":155},[63,1521,430],{"x":1469,"y":1454,"style":158},[55,1523],{"x":1472,"y":1451,"width":1473,"height":139,"rx":58,"fill":170,"stroke":171,"style":155},[63,1525,818],{"x":1476,"y":1454,"style":174},[63,1527,1531],{"x":1528,"y":1529,"style":1530},"229.8","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","text read from Excel",[63,1533,1535],{"x":1534,"y":1529,"style":1530},"525.5","dtype \"boolean\"",[323,1537],{"x1":1538,"y1":1539,"x2":114,"y2":1539,"stroke":163,"style":1540},"311.9","108.0","stroke-width:2px",[1542,1543],"polygon",{"points":1544,"fill":1545},"438.6,108.0 429.6,113.0 429.6,103.0","#5b5cf0",[55,1547],{"x":1548,"y":1549,"width":1550,"height":1551,"rx":1552,"fill":61,"stroke":76,"style":653},"332.5","75.0","85.4","22.0","11",[63,1554,1557],{"x":1555,"y":700,"style":1556},"375.2","font-size:11.5px;font-weight:400;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","to_flag()",[307,1559,1561],{"id":1560},"step-5-fail-loudly-on-a-value-nobody-expected","Step 5 — Fail loudly on a value nobody expected",[10,1563,1300,1564,1567,1568,1571,1572,1574,1575,1577],{},[14,1565,1566],{},"ValueError"," in ",[14,1569,1570],{},"to_flag"," is the most useful part of it. A sheet that gains a third answer — \"Pending\", \"N\u002FA\", \"?\" — should stop the job rather than become ",[14,1573,818],{}," or ",[14,1576,433],{}," without anyone knowing:",[312,1579,1581],{"className":354,"code":1580,"language":356,"meta":317,"style":317},"survey = pd.DataFrame({\"consent\": [\"Yes\", \"No\", \"Pending\"]})\ntry:\n    to_flag(survey[\"consent\"])\nexcept ValueError as exc:\n    print(exc)\n# consent: unexpected flag value 'Pending'\n",[14,1582,1583,1613,1620,1629,1642,1650],{"__ignoreMap":317},[321,1584,1585,1588,1590,1593,1596,1599,1601,1603,1605,1607,1610],{"class":323,"line":324},[321,1586,1587],{"class":367},"survey ",[321,1589,390],{"class":363},[321,1591,1592],{"class":367}," pd.DataFrame({",[321,1594,1595],{"class":331},"\"consent\"",[321,1597,1598],{"class":367},": [",[321,1600,493],{"class":331},[321,1602,482],{"class":367},[321,1604,515],{"class":331},[321,1606,482],{"class":367},[321,1608,1609],{"class":331},"\"Pending\"",[321,1611,1612],{"class":367},"]})\n",[321,1614,1615,1618],{"class":323,"line":377},[321,1616,1617],{"class":363},"try",[321,1619,969],{"class":367},[321,1621,1622,1625,1627],{"class":323,"line":384},[321,1623,1624],{"class":367},"    to_flag(survey[",[321,1626,1595],{"class":331},[321,1628,523],{"class":367},[321,1630,1631,1634,1636,1639],{"class":323,"line":402},[321,1632,1633],{"class":363},"except",[321,1635,1127],{"class":405},[321,1637,1638],{"class":363}," as",[321,1640,1641],{"class":367}," exc:\n",[321,1643,1644,1647],{"class":323,"line":412},[321,1645,1646],{"class":405},"    print",[321,1648,1649],{"class":367},"(exc)\n",[321,1651,1652],{"class":323,"line":945},[321,1653,1655],{"class":1654},"s-wDw","# consent: unexpected flag value 'Pending'\n",[10,1657,1658,1659,1663],{},"When a third state is legitimate, the column is not a boolean. Keep it as a category and decide explicitly how each state is reported. ",[30,1660,1662],{"href":1661},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fvalidate-excel-columns-before-import-with-pandas\u002F","Validate Excel Columns Before Import with pandas"," shows how to collect errors like this across a whole sheet and report them together.",[307,1665,1667],{"id":1666},"step-6-write-booleans-back-and-re-read-them-carefully","Step 6 — Write booleans back, and re-read them carefully",[10,1669,1670,1671,1574,1673,1675],{},"Writing a ",[14,1672,426],{},[14,1674,697],{}," column produces real Excel TRUE\u002FFALSE cells, and missing values become empty cells, so the file itself is exactly what a reader expects. Reading it back is where the asymmetry shows:",[312,1677,1679],{"className":354,"code":1678,"language":356,"meta":317,"style":317},"df[[\"id\", \"approved\", \"paid\", \"done\"]].to_excel(\"flags_clean.xlsx\", index=False)\nback = pd.read_excel(\"flags_clean.xlsx\")\nprint(back.dtypes.to_dict())\nprint(back[[\"approved\", \"paid\"]])\n",[14,1680,1681,1717,1730,1737],{"__ignoreMap":317},[321,1682,1683,1686,1688,1690,1692,1694,1696,1698,1700,1703,1706,1708,1711,1713,1715],{"class":323,"line":324},[321,1684,1685],{"class":367},"df[[",[321,1687,1346],{"class":331},[321,1689,482],{"class":367},[321,1691,540],{"class":331},[321,1693,482],{"class":367},[321,1695,545],{"class":331},[321,1697,482],{"class":367},[321,1699,1316],{"class":331},[321,1701,1702],{"class":367},"]].to_excel(",[321,1704,1705],{"class":331},"\"flags_clean.xlsx\"",[321,1707,482],{"class":367},[321,1709,1710],{"class":485},"index",[321,1712,390],{"class":363},[321,1714,433],{"class":405},[321,1716,399],{"class":367},[321,1718,1719,1722,1724,1726,1728],{"class":323,"line":377},[321,1720,1721],{"class":367},"back ",[321,1723,390],{"class":363},[321,1725,393],{"class":367},[321,1727,1705],{"class":331},[321,1729,399],{"class":367},[321,1731,1732,1734],{"class":323,"line":384},[321,1733,406],{"class":405},[321,1735,1736],{"class":367},"(back.dtypes.to_dict())\n",[321,1738,1739,1741,1744,1746,1748,1750],{"class":323,"line":402},[321,1740,406],{"class":405},[321,1742,1743],{"class":367},"(back[[",[321,1745,540],{"class":331},[321,1747,482],{"class":367},[321,1749,545],{"class":331},[321,1751,553],{"class":367},[10,1753,1754,21,1756,1758,1759,434,1761,1763,1764,1767,1768,482,1771,21,1774,1776,1777,1779,1780,1782,1783,21,1785,1787],{},[14,1755,167],{},[14,1757,193],{}," return as ",[14,1760,426],{},[14,1762,175],{},", which has an empty cell, returns as ",[14,1765,1766],{},"float64"," holding ",[14,1769,1770],{},"1.0",[14,1772,1773],{},"0.0",[14,1775,610],{},": pandas cannot put a missing value in a NumPy ",[14,1778,426],{}," column, so it falls back to floats. Run such columns through the same conversion on the way in — ",[14,1781,1570],{}," accepts ",[14,1784,1770],{},[14,1786,1773],{}," — and the round trip is complete:",[312,1789,1791],{"className":354,"code":1790,"language":356,"meta":317,"style":317},"back[\"paid\"] = to_flag(back[\"paid\"])\nprint(back[\"paid\"].tolist(), back[\"paid\"].dtype)\n# [True, False, \u003CNA>] boolean\n",[14,1792,1793,1811,1828],{"__ignoreMap":317},[321,1794,1795,1798,1800,1802,1804,1807,1809],{"class":323,"line":324},[321,1796,1797],{"class":367},"back[",[321,1799,545],{"class":331},[321,1801,1319],{"class":367},[321,1803,390],{"class":363},[321,1805,1806],{"class":367}," to_flag(back[",[321,1808,545],{"class":331},[321,1810,523],{"class":367},[321,1812,1813,1815,1818,1820,1823,1825],{"class":323,"line":377},[321,1814,406],{"class":405},[321,1816,1817],{"class":367},"(back[",[321,1819,545],{"class":331},[321,1821,1822],{"class":367},"].tolist(), back[",[321,1824,545],{"class":331},[321,1826,1827],{"class":367},"].dtype)\n",[321,1829,1830],{"class":323,"line":384},[321,1831,1832],{"class":1654},"# [True, False, \u003CNA>] boolean\n",[10,1834,1835,1836,1838,1839,1841],{},"This is the general rule for flag columns: convert them explicitly every time a DataFrame is built from a file, rather than trusting whatever type inference produced. A job that reads the same workbook every day can list its flag columns once and apply ",[14,1837,1570],{}," to each straight after ",[14,1840,457],{},".",[10,1843,1844,1845,1848],{},"If the report's readers expect \"Yes\" and \"No\" rather than TRUE and FALSE, map the column to text as the last step before writing — ",[14,1846,1847],{},"df[\"paid\"].map({True: \"Yes\", False: \"No\"})"," — and keep the boolean in your code. Converting to text early brings back every problem this page solves.",[307,1850,1852],{"id":1851},"common-pitfalls","Common pitfalls",[563,1854,1855,1868,1889,1895,1912],{},[566,1856,1857,593,1860,1863,1864,1867],{},[569,1858,1859],{},"Filtering on a text column.",[14,1861,1862],{},"df[df[\"approved\"] == True]"," on a column of \"Yes\"\u002F\"No\" strings returns nothing; ",[14,1865,1866],{},"df[df[\"approved\"].astype(bool)]"," returns every row, because non-empty strings are truthy.",[566,1869,1870,1877,1878,482,1880,21,1883,1885,1886,1888],{},[569,1871,1872,1873,1876],{},"Using ",[14,1874,1875],{},"astype(bool)"," to convert."," It turns ",[14,1879,515],{},[14,1881,1882],{},"\"False\"",[14,1884,902],{}," into ",[14,1887,430],{},". Map the words instead.",[566,1890,1891,1894],{},[569,1892,1893],{},"Letting blanks become False by default."," Decide per column whether a blank means \"no\" or \"unknown\".",[566,1896,1897,1900,1901,1903,1904,1903,1906,1908,1909,1911],{},[569,1898,1899],{},"Summing a float flag column."," A flag column that came back as ",[14,1902,1770],{},"\u002F",[14,1905,1773],{},[14,1907,610],{}," still sums correctly, which hides the problem until someone filters on it or compares it with ",[14,1910,430],{},". Check dtypes after every read.",[566,1913,1914,593,1917,1919],{},[569,1915,1916],{},"Listing values once.",[14,1918,20],{}," is exact; if you keep it, include every case variant — or normalise first and skip it.",[307,1921,1923],{"id":1922},"performance-notes","Performance notes",[10,1925,1926,1929,1930,1933,1934,1937,1938,1941],{},[14,1927,1928],{},"Series.map"," with a Python function processes each value in turn, which is fast enough for hundreds of thousands of rows. For millions, normalise with vectorised string methods — ",[14,1931,1932],{},"s.str.strip().str.lower()"," — then ",[14,1935,1936],{},"map"," with a dictionary, which pandas executes much faster than a function call per value, and check for unmapped values with ",[14,1939,1940],{},"isna()"," afterwards.",[307,1943,1945],{"id":1944},"conclusion","Conclusion",[10,1947,1948,1950,1951,21,1953,1955,1956,1958],{},[14,1949,457],{}," returns real booleans only for native TRUE\u002FFALSE cells. ",[14,1952,20],{},[14,1954,24],{}," help with consistently spelled, blank-free text columns and silently skip everything else. For flags people type, normalise case and whitespace, map to pandas' nullable ",[14,1957,810],{}," dtype, state per column what a blank means, and raise on unexpected values so new spellings are noticed on the day they appear.",[307,1960,1962],{"id":1961},"frequently-asked-questions","Frequently asked questions",[10,1964,1965,1968],{},[569,1966,1967],{},"Why did true_values not convert my column?","\npandas converts a column only when every non-empty value is in true_values or false_values, compared exactly. One blank, one \"YES\" when you listed \"Yes\", or a trailing space leaves the whole column as text.",[10,1970,1971,1974,1975],{},[569,1972,1973],{},"What is the difference between bool and boolean in pandas?","\nbool is NumPy's type and cannot hold a missing value. boolean (with a lower-case b, written \"boolean\") is pandas' nullable type: True, False or ",[1976,1977,1978],"na",{},". Flag columns with blanks need the nullable one.",[10,1980,1981,1984],{},[569,1982,1983],{},"How are Excel TRUE and FALSE cells read?","\nAs real booleans. A column of native TRUE\u002FFALSE cells arrives with dtype bool without any options, because openpyxl returns Python True and False for them.",[10,1986,1987,1990],{},[569,1988,1989],{},"Should 1 and 0 become booleans?","\nOnly if the column really is a flag. pandas leaves numeric 1\u002F0 columns as integers, which is correct for counts. Convert a flag column explicitly with astype(\"boolean\") after checking it holds only 0 and 1.",[307,1992,1994],{"id":1993},"related","Related",[563,1996,1997,2003,2010,2017],{},[566,1998,1999,2000,2002],{},"Up one level: ",[30,2001,33],{"href":32}," — every option that shapes what read_excel returns.",[566,2004,2005,2009],{},[30,2006,2008],{"href":2007},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fuse-converters-when-reading-excel-with-pandas\u002F","Use Converters When Reading Excel with pandas"," — applying a function to a column while reading.",[566,2011,2012,2016],{},[30,2013,2015],{"href":2014},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fexcel-if-formulas-as-pandas-conditional-columns\u002F","Excel IF Formulas as pandas Conditional Columns"," — building flags from other columns.",[566,2018,2019,2023],{},[30,2020,2022],{"href":2021},"\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Freplace-placeholder-text-with-nan-in-pandas\u002F","Replace Placeholder Text with NaN in pandas"," — when \"N\u002FA\" and \"-\" mean missing.",[2025,2026,2027],"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_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}",{"title":317,"searchDepth":377,"depth":377,"links":2029},[2030,2031,2032,2033,2034,2035,2036,2037,2038,2039,2040,2041],{"id":309,"depth":377,"text":310},{"id":347,"depth":377,"text":348},{"id":451,"depth":377,"text":452},{"id":803,"depth":377,"text":804},{"id":1296,"depth":377,"text":1297},{"id":1560,"depth":377,"text":1561},{"id":1666,"depth":377,"text":1667},{"id":1851,"depth":377,"text":1852},{"id":1922,"depth":377,"text":1923},{"id":1944,"depth":377,"text":1945},{"id":1961,"depth":377,"text":1962},{"id":1993,"depth":377,"text":1994},"2026-09-25","Excel sheets store flags as TRUE\u002FFALSE cells, \"Yes\"\u002F\"No\" text, Y\u002FN codes, 1\u002F0 or a tick mark. See what read_excel does with each, why true_values quietly skips columns with blanks or odd spellings, and how to convert every flag column into a reliable nullable boolean.","md",[2046,2048,2050,2052],{"q":1967,"a":2047},"pandas converts a column only when every non-empty value is in true_values or false_values, compared exactly. One blank, one \"YES\" when you listed \"Yes\", or a trailing space leaves the whole column as text.",{"q":1973,"a":2049},"bool is NumPy's type and cannot hold a missing value. boolean (with a lower-case b, written \"boolean\") is pandas' nullable type: True, False or \u003CNA>. Flag columns with blanks need the nullable one.",{"q":1983,"a":2051},"As real booleans. A column of native TRUE\u002FFALSE cells arrives with dtype bool without any options, because openpyxl returns Python True and False for them.",{"q":1989,"a":2053},"Only if the column really is a flag. pandas leaves numeric 1\u002F0 columns as integers, which is correct for counts. Convert a flag column explicitly with astype(\"boolean\") after checking it holds only 0 and 1.","read yes no and true false columns as booleans with pandas",null,{"breadcrumb":2057},[2058,2060,2063],{"name":2059,"item":1903},"Home",{"name":2061,"item":2062},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":2064,"item":32},"Reading Excel Files with Pandas","\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-boolean-columns-from-excel-with-pandas",{"title":2067,"description":2068},"Read Yes\u002FNo Columns as Booleans in pandas read_excel","Convert Excel Yes\u002FNo, Y\u002FN, TRUE\u002FFALSE, 1\u002F0 and tick-mark columns to booleans with pandas: true_values and false_values, their limits with blanks and case, and a strict mapping to the nullable boolean dtype.","read-boolean-columns-from-excel-with-pandas","getting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-boolean-columns-from-excel-with-pandas\u002Findex",[2072,2073,2074],"reading-excel","data-cleaning","pandas","how-to","9SK5gQens-AhJeFDNGNekyv_POS_jfXBztVc1bmcK8k",[2078,2082],{"title":2079,"path":2080,"stem":2081,"children":-1},"Read an Excel File That Is Open in Excel","\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-an-excel-file-that-is-open-in-excel","getting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-an-excel-file-that-is-open-in-excel\u002Findex",{"title":2083,"path":2084,"stem":2085,"children":-1},"Read Excel Files Inside a ZIP Archive with Python","\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-excel-files-inside-a-zip-archive-with-python","getting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-excel-files-inside-a-zip-archive-with-python\u002Findex",[2087,2089],{"path":2088,"title":2061},"\u002Fgetting-started-with-python-excel-automation",{"path":2090,"title":2064},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas",[],[2093,2096,2099,2102,2105,2108],{"path":2094,"title":2095,"kind":2075},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-an-excel-file-from-a-url-or-bytes-in-python","Read an Excel File from a URL or Bytes in Python",{"path":2097,"title":2098,"kind":2075},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Ffind-the-last-row-with-data-in-openpyxl","Find the Last Row with Data in openpyxl",{"path":2100,"title":2101,"kind":2075},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fiterate-over-rows-and-columns-with-openpyxl","Iterate over Rows and Columns with openpyxl",{"path":2103,"title":2104,"kind":2075},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fread-cell-value-from-excel-with-openpyxl","Read a Cell Value From Excel With openpyxl",{"path":2106,"title":2107,"kind":2075},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fstandardise-excel-column-names-with-pandas","Standardise Excel Column Names with pandas",{"path":2109,"title":2110,"kind":2111},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets","Extracting Data from Messy Excel Sheets","guide",1790972065191]