[{"data":1,"prerenderedAt":2214},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-numbers-with-units-in-excel-cells":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-numbers-with-units-in-excel-cells":2180,"ancestors:\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-numbers-with-units-in-excel-cells":2189,"children:\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-numbers-with-units-in-excel-cells":2194,"related:\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-numbers-with-units-in-excel-cells":2195},{"id":4,"title":5,"body":6,"dateModified":2146,"datePublished":2146,"description":2147,"extension":2148,"faq":2149,"keyword":2158,"kind":2159,"meta":2160,"navigation":109,"path":2169,"seo":2170,"slug":2173,"stem":2174,"topics":2175,"type":2178,"__hash__":2179},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-numbers-with-units-in-excel-cells\u002Findex.md","Parse Numbers with Units in Excel Cells",{"type":7,"value":8,"toc":2134},"minimark",[9,24,29,60,66,70,73,424,431,435,438,755,762,975,979,982,1278,1281,1285,1288,1500,1512,1623,1627,1633,1887,1898,2020,2024,2059,2063,2069,2073,2079,2085,2091,2097,2101,2130],[10,11,12,13,17,18,23],"p",{},"Spreadsheets filled in by people store quantities the way people write them: \"12 kg\", \"1,200 g\", \"2.5t\", \"0.5 tonnes\", \"500 ml\". A product sheet assembled from several suppliers might mix all of these in one column, alongside plain numbers and the occasional \"approx. 3 kg\". Excel treats every one of them as text, so nothing can be summed, sorted or compared. Converting them needs two decisions per cell — what the number is, and what unit it is in — and then a conversion to one unit for the whole column. This page builds that with a regular expression and a unit table in pandas, handles abbreviations like \"k\" and \"M\", decimal commas and missing units, shows when the ",[14,15,16],"code",{},"pint"," library is the better tool, and reports every cell it could not parse instead of silently dropping it. It belongs to ",[19,20,22],"a",{"href":21},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002F","Extracting Data from Messy Excel Sheets",".",[25,26,28],"h2",{"id":27},"prerequisites","Prerequisites",[30,31,36],"pre",{"className":32,"code":33,"language":34,"meta":35,"style":35},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl pint\n","bash","",[14,37,38],{"__ignoreMap":35},[39,40,43,47,51,54,57],"span",{"class":41,"line":42},"line",1,[39,44,46],{"class":45},"sMTad","pip",[39,48,50],{"class":49},"srMev"," install",[39,52,53],{"class":49}," pandas",[39,55,56],{"class":49}," openpyxl",[39,58,59],{"class":49}," pint\n",[10,61,62,65],{},[14,63,64],{},"products.xlsx"," lists twelve products from several suppliers. The Weight column mixes kilograms, grams, tonnes and pounds, with and without spaces, in different case, with a decimal comma in one cell, a plain number in another, an \"approx.\" prefix and one blank. Volume mixes litres and millilitres. Annual units uses \"k\" and \"M\" suffixes and thousands separators.",[25,67,69],{"id":68},"step-1-split-each-cell-into-value-and-unit","Step 1 — Split each cell into value and unit",[10,71,72],{},"A single regular expression captures an optional leading word, the number (with either decimal separator) and the unit after it:",[30,74,78],{"className":75,"code":76,"language":77,"meta":35,"style":35},"language-python shiki shiki-themes github-light github-dark-high-contrast","import re\nimport pandas as pd\n\ndf = pd.read_excel(\"products.xlsx\", dtype=str).fillna(\"\")\nPATTERN = re.compile(r\"^\\s*(?:approx\\.?|about|~)?\\s*([-+]?\\d[\\d.,\\s]*)\\s*([a-zA-Z]*)\\s*$\")\n\ndef split_value(text):\n    m = PATTERN.match(str(text))\n    if not m:\n        return None, None\n    number, unit = m.groups()\n    return number.strip(), unit.lower() or None\n\nparts = df[\"Weight\"].apply(lambda v: pd.Series(split_value(v), index=[\"number\", \"unit\"]))\nprint(pd.concat([df[\"Weight\"], parts], axis=1).to_string(index=False))\n","python",[14,79,80,90,104,111,148,253,258,271,290,302,316,327,342,347,389],{"__ignoreMap":35},[39,81,82,86],{"class":41,"line":42},[39,83,85],{"class":84},"s-kum","import",[39,87,89],{"class":88},"skGVy"," re\n",[39,91,93,95,98,101],{"class":41,"line":92},2,[39,94,85],{"class":84},[39,96,97],{"class":88}," pandas ",[39,99,100],{"class":84},"as",[39,102,103],{"class":88}," pd\n",[39,105,107],{"class":41,"line":106},3,[39,108,110],{"emptyLinePlaceholder":109},true,"\n",[39,112,114,117,120,123,126,129,133,135,139,142,145],{"class":41,"line":113},4,[39,115,116],{"class":88},"df ",[39,118,119],{"class":84},"=",[39,121,122],{"class":88}," pd.read_excel(",[39,124,125],{"class":49},"\"products.xlsx\"",[39,127,128],{"class":88},", ",[39,130,132],{"class":131},"sa561","dtype",[39,134,119],{"class":84},[39,136,138],{"class":137},"sP0c6","str",[39,140,141],{"class":88},").fillna(",[39,143,144],{"class":49},"\"\"",[39,146,147],{"class":88},")\n",[39,149,151,154,157,160,163,166,169,172,175,178,182,185,188,191,194,197,200,203,205,208,212,215,217,220,223,226,228,231,233,235,238,240,242,244,246,249,251],{"class":41,"line":150},5,[39,152,153],{"class":137},"PATTERN",[39,155,156],{"class":84}," =",[39,158,159],{"class":88}," re.compile(",[39,161,162],{"class":84},"r",[39,164,165],{"class":49},"\"",[39,167,168],{"class":137},"^\\s",[39,170,171],{"class":84},"*",[39,173,174],{"class":137},"(?:",[39,176,177],{"class":49},"approx",[39,179,181],{"class":180},"s_b0D","\\.",[39,183,184],{"class":84},"?|",[39,186,187],{"class":49},"about",[39,189,190],{"class":84},"|",[39,192,193],{"class":49},"~",[39,195,196],{"class":137},")",[39,198,199],{"class":84},"?",[39,201,202],{"class":137},"\\s",[39,204,171],{"class":84},[39,206,207],{"class":137},"([",[39,209,211],{"class":210},"sSjpA","-+",[39,213,214],{"class":137},"]",[39,216,199],{"class":84},[39,218,219],{"class":137},"\\d[\\d",[39,221,222],{"class":210},".,",[39,224,225],{"class":137},"\\s]",[39,227,171],{"class":84},[39,229,230],{"class":137},")\\s",[39,232,171],{"class":84},[39,234,207],{"class":137},[39,236,237],{"class":210},"a-zA-Z",[39,239,214],{"class":137},[39,241,171],{"class":84},[39,243,230],{"class":137},[39,245,171],{"class":84},[39,247,248],{"class":137},"$",[39,250,165],{"class":49},[39,252,147],{"class":88},[39,254,256],{"class":41,"line":255},6,[39,257,110],{"emptyLinePlaceholder":109},[39,259,261,264,268],{"class":41,"line":260},7,[39,262,263],{"class":84},"def",[39,265,267],{"class":266},"s_Opv"," split_value",[39,269,270],{"class":88},"(text):\n",[39,272,274,277,279,282,285,287],{"class":41,"line":273},8,[39,275,276],{"class":88},"    m ",[39,278,119],{"class":84},[39,280,281],{"class":137}," PATTERN",[39,283,284],{"class":88},".match(",[39,286,138],{"class":137},[39,288,289],{"class":88},"(text))\n",[39,291,293,296,299],{"class":41,"line":292},9,[39,294,295],{"class":84},"    if",[39,297,298],{"class":84}," not",[39,300,301],{"class":88}," m:\n",[39,303,305,308,311,313],{"class":41,"line":304},10,[39,306,307],{"class":84},"        return",[39,309,310],{"class":137}," None",[39,312,128],{"class":88},[39,314,315],{"class":137},"None\n",[39,317,319,322,324],{"class":41,"line":318},11,[39,320,321],{"class":88},"    number, unit ",[39,323,119],{"class":84},[39,325,326],{"class":88}," m.groups()\n",[39,328,330,333,336,339],{"class":41,"line":329},12,[39,331,332],{"class":84},"    return",[39,334,335],{"class":88}," number.strip(), unit.lower() ",[39,337,338],{"class":84},"or",[39,340,341],{"class":137}," None\n",[39,343,345],{"class":41,"line":344},13,[39,346,110],{"emptyLinePlaceholder":109},[39,348,350,353,355,358,361,364,367,370,373,375,378,381,383,386],{"class":41,"line":349},14,[39,351,352],{"class":88},"parts ",[39,354,119],{"class":84},[39,356,357],{"class":88}," df[",[39,359,360],{"class":49},"\"Weight\"",[39,362,363],{"class":88},"].apply(",[39,365,366],{"class":84},"lambda",[39,368,369],{"class":88}," v: pd.Series(split_value(v), ",[39,371,372],{"class":131},"index",[39,374,119],{"class":84},[39,376,377],{"class":88},"[",[39,379,380],{"class":49},"\"number\"",[39,382,128],{"class":88},[39,384,385],{"class":49},"\"unit\"",[39,387,388],{"class":88},"]))\n",[39,390,392,395,398,400,403,406,408,411,414,416,418,421],{"class":41,"line":391},15,[39,393,394],{"class":137},"print",[39,396,397],{"class":88},"(pd.concat([df[",[39,399,360],{"class":49},[39,401,402],{"class":88},"], parts], ",[39,404,405],{"class":131},"axis",[39,407,119],{"class":84},[39,409,410],{"class":137},"1",[39,412,413],{"class":88},").to_string(",[39,415,372],{"class":131},[39,417,119],{"class":84},[39,419,420],{"class":137},"False",[39,422,423],{"class":88},"))\n",[10,425,426,427,430],{},"Reading with ",[14,428,429],{},"dtype=str"," keeps every cell as written, including the plain number 15, which arrives as the text \"15\". The pattern allows an optional \"approx.\", \"about\" or \"~\" prefix, then a number that may contain digits, dots, commas and spaces, then letters for the unit. Units are lower-cased so \"KG\" and \"kg\" are the same. The blank cell produces no match, which is the correct result: there is no value. Keeping the raw number text at this stage, before converting it, means the decimal-separator question can be answered separately in the next step.",[25,432,434],{"id":433},"step-2-convert-the-number-text-decimal-commas-included","Step 2 — Convert the number text, decimal commas included",[10,436,437],{},"\"1,200\" is twelve hundred; \"0,75\" is three quarters. A simple rule distinguishes them: a comma followed by exactly three digits at the end is a thousands separator, otherwise a lone comma is a decimal separator:",[30,439,441],{"className":75,"code":440,"language":77,"meta":35,"style":35},"def to_number(text):\n    if text is None:\n        return None\n    t = text.replace(\" \", \"\")\n    if \",\" in t and \".\" in t:                       # 1,234.5 or 1.234,5\n        t = t.replace(\",\", \"\") if t.rfind(\".\") > t.rfind(\",\") else t.replace(\".\", \"\").replace(\",\", \".\")\n    elif \",\" in t:\n        t = t.replace(\",\", \"\") if re.fullmatch(r\"\\d{1,3}(,\\d{3})+\", t) else t.replace(\",\", \".\")\n    return float(t)\n\nfor sample in [\"1,200\", \"0,75\", \"1.234,5\", \"1,234.5\", \"25\", \"2.5\"]:\n    print(f\"{sample:>8} → {to_number(sample)}\")\n",[14,442,443,452,467,473,492,520,582,594,658,668,672,717],{"__ignoreMap":35},[39,444,445,447,450],{"class":41,"line":42},[39,446,263],{"class":84},[39,448,449],{"class":266}," to_number",[39,451,270],{"class":88},[39,453,454,456,459,462,464],{"class":41,"line":92},[39,455,295],{"class":84},[39,457,458],{"class":88}," text ",[39,460,461],{"class":84},"is",[39,463,310],{"class":137},[39,465,466],{"class":88},":\n",[39,468,469,471],{"class":41,"line":106},[39,470,307],{"class":84},[39,472,341],{"class":137},[39,474,475,478,480,483,486,488,490],{"class":41,"line":113},[39,476,477],{"class":88},"    t ",[39,479,119],{"class":84},[39,481,482],{"class":88}," text.replace(",[39,484,485],{"class":49},"\" \"",[39,487,128],{"class":88},[39,489,144],{"class":49},[39,491,147],{"class":88},[39,493,494,496,499,502,505,508,511,513,516],{"class":41,"line":150},[39,495,295],{"class":84},[39,497,498],{"class":49}," \",\"",[39,500,501],{"class":84}," in",[39,503,504],{"class":88}," t ",[39,506,507],{"class":84},"and",[39,509,510],{"class":49}," \".\"",[39,512,501],{"class":84},[39,514,515],{"class":88}," t:                       ",[39,517,519],{"class":518},"s-wDw","# 1,234.5 or 1.234,5\n",[39,521,522,525,527,530,533,535,537,540,543,546,549,551,554,556,558,560,563,565,567,569,571,574,576,578,580],{"class":41,"line":255},[39,523,524],{"class":88},"        t ",[39,526,119],{"class":84},[39,528,529],{"class":88}," t.replace(",[39,531,532],{"class":49},"\",\"",[39,534,128],{"class":88},[39,536,144],{"class":49},[39,538,539],{"class":88},") ",[39,541,542],{"class":84},"if",[39,544,545],{"class":88}," t.rfind(",[39,547,548],{"class":49},"\".\"",[39,550,539],{"class":88},[39,552,553],{"class":84},">",[39,555,545],{"class":88},[39,557,532],{"class":49},[39,559,539],{"class":88},[39,561,562],{"class":84},"else",[39,564,529],{"class":88},[39,566,548],{"class":49},[39,568,128],{"class":88},[39,570,144],{"class":49},[39,572,573],{"class":88},").replace(",[39,575,532],{"class":49},[39,577,128],{"class":88},[39,579,548],{"class":49},[39,581,147],{"class":88},[39,583,584,587,589,591],{"class":41,"line":260},[39,585,586],{"class":84},"    elif",[39,588,498],{"class":49},[39,590,501],{"class":84},[39,592,593],{"class":88}," t:\n",[39,595,596,598,600,602,604,606,608,610,612,615,617,619,622,625,628,631,633,636,638,641,643,646,648,650,652,654,656],{"class":41,"line":273},[39,597,524],{"class":88},[39,599,119],{"class":84},[39,601,529],{"class":88},[39,603,532],{"class":49},[39,605,128],{"class":88},[39,607,144],{"class":49},[39,609,539],{"class":88},[39,611,542],{"class":84},[39,613,614],{"class":88}," re.fullmatch(",[39,616,162],{"class":84},[39,618,165],{"class":49},[39,620,621],{"class":137},"\\d",[39,623,624],{"class":84},"{1,3}",[39,626,627],{"class":137},"(",[39,629,630],{"class":49},",",[39,632,621],{"class":137},[39,634,635],{"class":84},"{3}",[39,637,196],{"class":137},[39,639,640],{"class":84},"+",[39,642,165],{"class":49},[39,644,645],{"class":88},", t) ",[39,647,562],{"class":84},[39,649,529],{"class":88},[39,651,532],{"class":49},[39,653,128],{"class":88},[39,655,548],{"class":49},[39,657,147],{"class":88},[39,659,660,662,665],{"class":41,"line":292},[39,661,332],{"class":84},[39,663,664],{"class":137}," float",[39,666,667],{"class":88},"(t)\n",[39,669,670],{"class":41,"line":304},[39,671,110],{"emptyLinePlaceholder":109},[39,673,674,677,680,683,686,689,691,694,696,699,701,704,706,709,711,714],{"class":41,"line":318},[39,675,676],{"class":84},"for",[39,678,679],{"class":88}," sample ",[39,681,682],{"class":84},"in",[39,684,685],{"class":88}," [",[39,687,688],{"class":49},"\"1,200\"",[39,690,128],{"class":88},[39,692,693],{"class":49},"\"0,75\"",[39,695,128],{"class":88},[39,697,698],{"class":49},"\"1.234,5\"",[39,700,128],{"class":88},[39,702,703],{"class":49},"\"1,234.5\"",[39,705,128],{"class":88},[39,707,708],{"class":49},"\"25\"",[39,710,128],{"class":88},[39,712,713],{"class":49},"\"2.5\"",[39,715,716],{"class":88},"]:\n",[39,718,719,722,724,727,729,732,735,738,741,744,746,749,751,753],{"class":41,"line":329},[39,720,721],{"class":137},"    print",[39,723,627],{"class":88},[39,725,726],{"class":84},"f",[39,728,165],{"class":49},[39,730,731],{"class":210},"{",[39,733,734],{"class":88},"sample",[39,736,737],{"class":84},":>8",[39,739,740],{"class":210},"}",[39,742,743],{"class":49}," → ",[39,745,731],{"class":210},[39,747,748],{"class":88},"to_number(sample)",[39,750,740],{"class":210},[39,752,165],{"class":49},[39,754,147],{"class":88},[10,756,757,758,23],{},"When both separators appear, whichever comes last is the decimal point. A lone comma followed by groups of exactly three digits is a thousands separator; anything else — \"0,75\", \"2,5\" — is a decimal comma. The rule is ambiguous for a value like \"1,500\", which could be either; in a column that otherwise uses decimal points, thousands is the right reading, but if your data is mostly European it may not be. When in doubt, read the column's other values or ask the source which convention they use. CSV files with decimal commas are covered in ",[19,759,761],{"href":760},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-csv-exports-with-semicolons-and-decimal-commas\u002F","Read CSV Exports with Semicolons and Decimal Commas",[763,764,772,773,772,777,772,781,772,789,772,798,772,802,772,809,772,813,772,817,772,819,772,823,772,826,772,830,772,832,772,836,772,839,772,843,772,845,772,848,772,851,772,855,772,857,772,860,772,863,772,867,772,869,772,872,772,876,772,881,772,886,772,890,772,893,772,897,772,901,772,905,772,908,772,912,772,914,772,917,772,920,772,924,772,926,772,929,772,932,772,936,772,938,772,940,772,946,772,950,772,957,772,962,772,970],"svg",{"viewBox":765,"role":766,"ariaLabelledBy":767,"xmlns":770,"style":771},"127 0 505 220","img",[768,769],"un-split-t","un-split-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:505px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[774,775,776],"title",{"id":768},"From free text to a value and a unit",[778,779,780],"desc",{"id":769},"Cells such as 1,200 g, 2.5t, 0,75 kg and approx. 18 kg are split by a regular expression into a number and a unit, and the number text is converted with a rule for thousands and decimal commas.",[782,783],"rect",{"x":784,"y":785,"width":786,"height":787,"fill":788},"127","0","505","220","#ffffff",[782,790],{"x":791,"y":792,"width":793,"height":794,"rx":785,"fill":795,"stroke":796,"style":797},"143.9","40.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[782,799],{"x":800,"y":792,"width":801,"height":794,"rx":785,"fill":795,"stroke":796,"style":797},"175.9","96.1",[803,804,808],"text",{"x":805,"y":806,"style":807},"224.0","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[782,810],{"x":791,"y":811,"width":793,"height":812,"rx":785,"fill":795,"stroke":796,"style":797},"64.0","28.0",[803,814,410],{"x":815,"y":816,"style":807},"159.9","83.0",[782,818],{"x":800,"y":811,"width":801,"height":812,"rx":785,"fill":788,"stroke":796,"style":797},[803,820,822],{"x":805,"y":816,"style":821},"font-size:10.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Weight",[782,824],{"x":791,"y":825,"width":793,"height":812,"rx":785,"fill":795,"stroke":796,"style":797},"92.0",[803,827,829],{"x":815,"y":828,"style":807},"111.0","2",[782,831],{"x":800,"y":825,"width":801,"height":812,"rx":785,"fill":788,"stroke":796,"style":797},[803,833,835],{"x":805,"y":828,"style":834},"font-size:10.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","1,200 g",[782,837],{"x":791,"y":838,"width":793,"height":812,"rx":785,"fill":795,"stroke":796,"style":797},"120.0",[803,840,842],{"x":815,"y":841,"style":807},"139.0","3",[782,844],{"x":800,"y":838,"width":801,"height":812,"rx":785,"fill":788,"stroke":796,"style":797},[803,846,847],{"x":805,"y":841,"style":834},"2.5t",[782,849],{"x":791,"y":850,"width":793,"height":812,"rx":785,"fill":795,"stroke":796,"style":797},"148.0",[803,852,854],{"x":815,"y":853,"style":807},"167.0","4",[782,856],{"x":800,"y":850,"width":801,"height":812,"rx":785,"fill":788,"stroke":796,"style":797},[803,858,859],{"x":805,"y":853,"style":834},"0,75 kg",[782,861],{"x":791,"y":862,"width":793,"height":812,"rx":785,"fill":795,"stroke":796,"style":797},"176.0",[803,864,866],{"x":815,"y":865,"style":807},"195.0","5",[782,868],{"x":800,"y":862,"width":801,"height":812,"rx":785,"fill":788,"stroke":796,"style":797},[803,870,871],{"x":805,"y":865,"style":834},"approx. 18 kg",[782,873],{"x":874,"y":792,"width":875,"height":812,"rx":785,"fill":788,"stroke":796,"style":797},"509.7","61.7",[803,877,880],{"x":878,"y":879,"style":821},"540.5","59.0","value",[782,882],{"x":883,"y":792,"width":884,"height":812,"rx":785,"fill":795,"stroke":796,"style":885},"571.3","44.8","stroke-width:1.5px",[803,887,889],{"x":888,"y":879,"style":821},"593.7","unit",[782,891],{"x":874,"y":892,"width":875,"height":812,"rx":785,"fill":788,"stroke":796,"style":797},"68.0",[803,894,896],{"x":878,"y":895,"style":834},"87.0","1200.0",[782,898],{"x":883,"y":892,"width":884,"height":812,"rx":785,"fill":899,"stroke":900,"style":885},"#d9f4f1","var(--teal,#0f9488)",[803,902,904],{"x":888,"y":895,"style":903},"font-size:10.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","g",[782,906],{"x":874,"y":907,"width":875,"height":812,"rx":785,"fill":788,"stroke":796,"style":797},"96.0",[803,909,911],{"x":878,"y":910,"style":834},"115.0","2.5",[782,913],{"x":883,"y":907,"width":884,"height":812,"rx":785,"fill":899,"stroke":900,"style":885},[803,915,916],{"x":888,"y":910,"style":903},"t",[782,918],{"x":874,"y":919,"width":875,"height":812,"rx":785,"fill":788,"stroke":796,"style":797},"124.0",[803,921,923],{"x":878,"y":922,"style":834},"143.0","0.75",[782,925],{"x":883,"y":919,"width":884,"height":812,"rx":785,"fill":899,"stroke":900,"style":885},[803,927,928],{"x":888,"y":922,"style":903},"kg",[782,930],{"x":874,"y":931,"width":875,"height":812,"rx":785,"fill":788,"stroke":796,"style":797},"152.0",[803,933,935],{"x":878,"y":934,"style":834},"171.0","18.0",[782,937],{"x":883,"y":931,"width":884,"height":812,"rx":785,"fill":899,"stroke":900,"style":885},[803,939,928],{"x":888,"y":934,"style":903},[803,941,945],{"x":942,"y":943,"style":944},"208.0","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","cell text",[803,947,949],{"x":948,"y":943,"style":944},"562.9","value, unit",[41,951],{"x1":952,"y1":953,"x2":954,"y2":953,"stroke":955,"style":956},"282.0","110.0","495.7","var(--brand,#5b5cf0)","stroke-width:2px",[958,959],"polygon",{"points":960,"fill":961},"499.7,110.0 490.7,115.0 490.7,105.0","#5b5cf0",[782,963],{"x":964,"y":965,"width":966,"height":967,"rx":968,"fill":788,"stroke":796,"style":969},"311.8","77.0","158.1","22.0","11","stroke-width:1.2px",[803,971,974],{"x":972,"y":825,"style":973},"390.9","font-size:11.5px;font-weight:400;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","regex + to_number()",[25,976,978],{"id":977},"step-3-convert-to-one-unit-with-a-lookup-table","Step 3 — Convert to one unit with a lookup table",[10,980,981],{},"With value and unit separated, conversion is a multiplication by a factor from a table. Spelled-out forms and plurals map to the same factor; a missing unit gets the column's default:",[30,983,985],{"className":75,"code":984,"language":77,"meta":35,"style":35},"TO_KG = {\"g\": 0.001, \"kg\": 1.0, \"t\": 1000.0, \"tonne\": 1000.0, \"tonnes\": 1000.0, \"lb\": 0.45359237, \"lbs\": 0.45359237}\n\ndef weight_kg(text, default_unit=\"kg\"):\n    number, unit = split_value(text)\n    if number is None:\n        return None, \"unparsed\" if str(text).strip() else \"blank\"\n    unit = unit or default_unit\n    if unit not in TO_KG:\n        return None, f\"unknown unit '{unit}'\"\n    return round(to_number(number) * TO_KG[unit], 4), \"ok\"\n\ndf[[\"Weight kg\", \"Weight status\"]] = df[\"Weight\"].apply(lambda v: pd.Series(weight_kg(v)))\nprint(df[[\"SKU\", \"Weight\", \"Weight kg\", \"Weight status\"]].to_string(index=False))\n",[14,986,987,1066,1070,1087,1096,1109,1134,1149,1165,1187,1212,1216,1245],{"__ignoreMap":35},[39,988,989,992,994,997,1000,1003,1006,1008,1011,1013,1016,1018,1021,1023,1026,1028,1031,1033,1035,1037,1040,1042,1044,1046,1049,1051,1054,1056,1059,1061,1063],{"class":41,"line":42},[39,990,991],{"class":137},"TO_KG",[39,993,156],{"class":84},[39,995,996],{"class":88}," {",[39,998,999],{"class":49},"\"g\"",[39,1001,1002],{"class":88},": ",[39,1004,1005],{"class":137},"0.001",[39,1007,128],{"class":88},[39,1009,1010],{"class":49},"\"kg\"",[39,1012,1002],{"class":88},[39,1014,1015],{"class":137},"1.0",[39,1017,128],{"class":88},[39,1019,1020],{"class":49},"\"t\"",[39,1022,1002],{"class":88},[39,1024,1025],{"class":137},"1000.0",[39,1027,128],{"class":88},[39,1029,1030],{"class":49},"\"tonne\"",[39,1032,1002],{"class":88},[39,1034,1025],{"class":137},[39,1036,128],{"class":88},[39,1038,1039],{"class":49},"\"tonnes\"",[39,1041,1002],{"class":88},[39,1043,1025],{"class":137},[39,1045,128],{"class":88},[39,1047,1048],{"class":49},"\"lb\"",[39,1050,1002],{"class":88},[39,1052,1053],{"class":137},"0.45359237",[39,1055,128],{"class":88},[39,1057,1058],{"class":49},"\"lbs\"",[39,1060,1002],{"class":88},[39,1062,1053],{"class":137},[39,1064,1065],{"class":88},"}\n",[39,1067,1068],{"class":41,"line":92},[39,1069,110],{"emptyLinePlaceholder":109},[39,1071,1072,1074,1077,1080,1082,1084],{"class":41,"line":106},[39,1073,263],{"class":84},[39,1075,1076],{"class":266}," weight_kg",[39,1078,1079],{"class":88},"(text, default_unit",[39,1081,119],{"class":84},[39,1083,1010],{"class":49},[39,1085,1086],{"class":88},"):\n",[39,1088,1089,1091,1093],{"class":41,"line":113},[39,1090,321],{"class":88},[39,1092,119],{"class":84},[39,1094,1095],{"class":88}," split_value(text)\n",[39,1097,1098,1100,1103,1105,1107],{"class":41,"line":150},[39,1099,295],{"class":84},[39,1101,1102],{"class":88}," number ",[39,1104,461],{"class":84},[39,1106,310],{"class":137},[39,1108,466],{"class":88},[39,1110,1111,1113,1115,1117,1120,1123,1126,1129,1131],{"class":41,"line":255},[39,1112,307],{"class":84},[39,1114,310],{"class":137},[39,1116,128],{"class":88},[39,1118,1119],{"class":49},"\"unparsed\"",[39,1121,1122],{"class":84}," if",[39,1124,1125],{"class":137}," str",[39,1127,1128],{"class":88},"(text).strip() ",[39,1130,562],{"class":84},[39,1132,1133],{"class":49}," \"blank\"\n",[39,1135,1136,1139,1141,1144,1146],{"class":41,"line":260},[39,1137,1138],{"class":88},"    unit ",[39,1140,119],{"class":84},[39,1142,1143],{"class":88}," unit ",[39,1145,338],{"class":84},[39,1147,1148],{"class":88}," default_unit\n",[39,1150,1151,1153,1155,1158,1160,1163],{"class":41,"line":273},[39,1152,295],{"class":84},[39,1154,1143],{"class":88},[39,1156,1157],{"class":84},"not",[39,1159,501],{"class":84},[39,1161,1162],{"class":137}," TO_KG",[39,1164,466],{"class":88},[39,1166,1167,1169,1171,1173,1175,1178,1180,1182,1184],{"class":41,"line":292},[39,1168,307],{"class":84},[39,1170,310],{"class":137},[39,1172,128],{"class":88},[39,1174,726],{"class":84},[39,1176,1177],{"class":49},"\"unknown unit '",[39,1179,731],{"class":210},[39,1181,889],{"class":88},[39,1183,740],{"class":210},[39,1185,1186],{"class":49},"'\"\n",[39,1188,1189,1191,1194,1197,1199,1201,1204,1206,1209],{"class":41,"line":304},[39,1190,332],{"class":84},[39,1192,1193],{"class":137}," round",[39,1195,1196],{"class":88},"(to_number(number) ",[39,1198,171],{"class":84},[39,1200,1162],{"class":137},[39,1202,1203],{"class":88},"[unit], ",[39,1205,854],{"class":137},[39,1207,1208],{"class":88},"), ",[39,1210,1211],{"class":49},"\"ok\"\n",[39,1213,1214],{"class":41,"line":318},[39,1215,110],{"emptyLinePlaceholder":109},[39,1217,1218,1221,1224,1226,1229,1232,1234,1236,1238,1240,1242],{"class":41,"line":329},[39,1219,1220],{"class":88},"df[[",[39,1222,1223],{"class":49},"\"Weight kg\"",[39,1225,128],{"class":88},[39,1227,1228],{"class":49},"\"Weight status\"",[39,1230,1231],{"class":88},"]] ",[39,1233,119],{"class":84},[39,1235,357],{"class":88},[39,1237,360],{"class":49},[39,1239,363],{"class":88},[39,1241,366],{"class":84},[39,1243,1244],{"class":88}," v: pd.Series(weight_kg(v)))\n",[39,1246,1247,1249,1252,1255,1257,1259,1261,1263,1265,1267,1270,1272,1274,1276],{"class":41,"line":344},[39,1248,394],{"class":137},[39,1250,1251],{"class":88},"(df[[",[39,1253,1254],{"class":49},"\"SKU\"",[39,1256,128],{"class":88},[39,1258,360],{"class":49},[39,1260,128],{"class":88},[39,1262,1223],{"class":49},[39,1264,128],{"class":88},[39,1266,1228],{"class":49},[39,1268,1269],{"class":88},"]].to_string(",[39,1271,372],{"class":131},[39,1273,119],{"class":84},[39,1275,420],{"class":137},[39,1277,423],{"class":88},[10,1279,1280],{},"Every weight now has a value in kilograms: 1,200 g becomes 1.2, 2.5 t becomes 2,500, 350 lbs about 158.8, and the bare \"15\" is read as 15 kg by default. The status column says what happened to each cell — \"ok\", \"blank\", or a specific problem — so nothing disappears silently. Writing the factors out as a table, with the exact pound-to-kilogram factor, makes the conversion reviewable by someone who does not read code. The default unit for bare numbers is a real decision: if the column header said \"Weight (kg)\" it is safe, if it said \"Weight\" it deserves a flag rather than an assumption.",[25,1282,1284],{"id":1283},"step-4-suffixes-like-k-and-m","Step 4 — Suffixes like k and M",[10,1286,1287],{},"The annual-units column uses a different convention: magnitude suffixes rather than physical units. The same split works with a different table:",[30,1289,1291],{"className":75,"code":1290,"language":77,"meta":35,"style":35},"SCALE = {None: 1, \"k\": 1_000, \"m\": 1_000_000, \"bn\": 1_000_000_000}\n\ndef scaled(text):\n    number, suffix = split_value(text)\n    if number is None or suffix not in SCALE:\n        return None\n    return int(round(to_number(number) * SCALE[suffix]))\n\ndf[\"Annual units n\"] = df[\"Annual units\"].apply(scaled)\nprint(df[[\"SKU\", \"Annual units\", \"Annual units n\"]].to_string(index=False))\nprint(\"total annual units:\", f\"{df['Annual units n'].sum():,}\")\n",[14,1292,1293,1341,1345,1354,1363,1388,1394,1415,1419,1440,1466],{"__ignoreMap":35},[39,1294,1295,1298,1300,1302,1305,1307,1309,1311,1314,1316,1319,1321,1324,1326,1329,1331,1334,1336,1339],{"class":41,"line":42},[39,1296,1297],{"class":137},"SCALE",[39,1299,156],{"class":84},[39,1301,996],{"class":88},[39,1303,1304],{"class":137},"None",[39,1306,1002],{"class":88},[39,1308,410],{"class":137},[39,1310,128],{"class":88},[39,1312,1313],{"class":49},"\"k\"",[39,1315,1002],{"class":88},[39,1317,1318],{"class":137},"1_000",[39,1320,128],{"class":88},[39,1322,1323],{"class":49},"\"m\"",[39,1325,1002],{"class":88},[39,1327,1328],{"class":137},"1_000_000",[39,1330,128],{"class":88},[39,1332,1333],{"class":49},"\"bn\"",[39,1335,1002],{"class":88},[39,1337,1338],{"class":137},"1_000_000_000",[39,1340,1065],{"class":88},[39,1342,1343],{"class":41,"line":92},[39,1344,110],{"emptyLinePlaceholder":109},[39,1346,1347,1349,1352],{"class":41,"line":106},[39,1348,263],{"class":84},[39,1350,1351],{"class":266}," scaled",[39,1353,270],{"class":88},[39,1355,1356,1359,1361],{"class":41,"line":113},[39,1357,1358],{"class":88},"    number, suffix ",[39,1360,119],{"class":84},[39,1362,1095],{"class":88},[39,1364,1365,1367,1369,1371,1373,1376,1379,1381,1383,1386],{"class":41,"line":150},[39,1366,295],{"class":84},[39,1368,1102],{"class":88},[39,1370,461],{"class":84},[39,1372,310],{"class":137},[39,1374,1375],{"class":84}," or",[39,1377,1378],{"class":88}," suffix ",[39,1380,1157],{"class":84},[39,1382,501],{"class":84},[39,1384,1385],{"class":137}," SCALE",[39,1387,466],{"class":88},[39,1389,1390,1392],{"class":41,"line":255},[39,1391,307],{"class":84},[39,1393,341],{"class":137},[39,1395,1396,1398,1401,1403,1406,1408,1410,1412],{"class":41,"line":260},[39,1397,332],{"class":84},[39,1399,1400],{"class":137}," int",[39,1402,627],{"class":88},[39,1404,1405],{"class":137},"round",[39,1407,1196],{"class":88},[39,1409,171],{"class":84},[39,1411,1385],{"class":137},[39,1413,1414],{"class":88},"[suffix]))\n",[39,1416,1417],{"class":41,"line":273},[39,1418,110],{"emptyLinePlaceholder":109},[39,1420,1421,1424,1427,1430,1432,1434,1437],{"class":41,"line":292},[39,1422,1423],{"class":88},"df[",[39,1425,1426],{"class":49},"\"Annual units n\"",[39,1428,1429],{"class":88},"] ",[39,1431,119],{"class":84},[39,1433,357],{"class":88},[39,1435,1436],{"class":49},"\"Annual units\"",[39,1438,1439],{"class":88},"].apply(scaled)\n",[39,1441,1442,1444,1446,1448,1450,1452,1454,1456,1458,1460,1462,1464],{"class":41,"line":304},[39,1443,394],{"class":137},[39,1445,1251],{"class":88},[39,1447,1254],{"class":49},[39,1449,128],{"class":88},[39,1451,1436],{"class":49},[39,1453,128],{"class":88},[39,1455,1426],{"class":49},[39,1457,1269],{"class":88},[39,1459,372],{"class":131},[39,1461,119],{"class":84},[39,1463,420],{"class":137},[39,1465,423],{"class":88},[39,1467,1468,1470,1472,1475,1477,1479,1481,1483,1485,1488,1491,1494,1496,1498],{"class":41,"line":318},[39,1469,394],{"class":137},[39,1471,627],{"class":88},[39,1473,1474],{"class":49},"\"total annual units:\"",[39,1476,128],{"class":88},[39,1478,726],{"class":84},[39,1480,165],{"class":49},[39,1482,731],{"class":210},[39,1484,1423],{"class":88},[39,1486,1487],{"class":49},"'Annual units n'",[39,1489,1490],{"class":88},"].sum()",[39,1492,1493],{"class":84},":,",[39,1495,740],{"class":210},[39,1497,165],{"class":49},[39,1499,147],{"class":88},[10,1501,1502,1503,1507,1508,1511],{},"\"3.5k\" becomes 3,500, \"1.2M\" 1,200,000, and \"4,500\" — a thousands separator, no suffix — 4,500. Note that \"m\" means ",[1504,1505,1506],"em",{},"million"," here but ",[1504,1509,1510],{},"metre"," in a length column: a unit table belongs to a column, not to the whole sheet, which is why each column gets its own. Rounding to an integer suits counts; keep floats for anything that can be fractional.",[763,1513,772,1519,772,1522,772,1525,772,1529,772,1534,772,1539,772,1545,772,1548,772,1552,772,1556,772,1560,772,1562,772,1566,772,1568,772,1571,772,1574,772,1578,772,1580,772,1583,772,1585,772,1588,772,1590,772,1594,772,1596,772,1600,772,1602,772,1605,772,1608,772,1612,772,1614,772,1618,772,1620],{"viewBox":1514,"role":766,"ariaLabelledBy":1515,"xmlns":770,"style":1518},"4 0 752 208",[1516,1517],"un-tables-t","un-tables-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[774,1520,1521],{"id":1516},"One conversion table per column",[778,1523,1524],{"id":1517},"The weight column converts g, kg, t and lbs to kilograms. The volume column converts ml and l to litres. The annual units column uses magnitude suffixes, where k means a thousand and M a million. The same letter can mean different things in different columns, such as m for million or metre.",[782,1526],{"x":854,"y":785,"width":1527,"height":1528,"fill":788},"752","208",[782,1530],{"x":1531,"y":1532,"width":1533,"height":792,"rx":785,"fill":788,"stroke":796,"style":797},"20.0","16.0","130.3",[782,1535],{"x":1536,"y":1532,"width":1537,"height":792,"rx":785,"fill":1538,"stroke":955,"style":969},"150.3","294.8","#f0f4ff",[803,1540,1544],{"x":1541,"y":1542,"style":1543},"297.8","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","units seen",[782,1546],{"x":1547,"y":1532,"width":1537,"height":792,"rx":785,"fill":1538,"stroke":955,"style":969},"445.2",[803,1549,1551],{"x":1550,"y":1542,"style":1543},"592.6","converted to",[782,1553],{"x":1531,"y":1554,"width":1533,"height":1555,"rx":785,"fill":795,"stroke":796,"style":797},"56.0","34.0",[803,1557,822],{"x":793,"y":1558,"style":1559},"78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start",[782,1561],{"x":1536,"y":1554,"width":1537,"height":1555,"rx":785,"fill":788,"stroke":796,"style":797},[803,1563,1565],{"x":1541,"y":965,"style":1564},"font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","g, kg, t, tonnes, lbs",[782,1567],{"x":1547,"y":1554,"width":1537,"height":1555,"rx":785,"fill":788,"stroke":796,"style":797},[803,1569,1570],{"x":1550,"y":965,"style":1564},"kilograms",[782,1572],{"x":1531,"y":1573,"width":1533,"height":1555,"rx":785,"fill":795,"stroke":796,"style":797},"90.0",[803,1575,1577],{"x":793,"y":1576,"style":1559},"112.0","Volume",[782,1579],{"x":1536,"y":1573,"width":1537,"height":1555,"rx":785,"fill":788,"stroke":796,"style":797},[803,1581,1582],{"x":1541,"y":828,"style":1564},"ml, l",[782,1584],{"x":1547,"y":1573,"width":1537,"height":1555,"rx":785,"fill":788,"stroke":796,"style":797},[803,1586,1587],{"x":1550,"y":828,"style":1564},"litres",[782,1589],{"x":1531,"y":919,"width":1533,"height":1555,"rx":785,"fill":795,"stroke":796,"style":797},[803,1591,1593],{"x":793,"y":1592,"style":1559},"146.0","Annual units",[782,1595],{"x":1536,"y":919,"width":1537,"height":1555,"rx":785,"fill":788,"stroke":796,"style":797},[803,1597,1599],{"x":1541,"y":1598,"style":1564},"145.0","k, M, plain",[782,1601],{"x":1547,"y":919,"width":1537,"height":1555,"rx":785,"fill":788,"stroke":796,"style":797},[803,1603,1604],{"x":1550,"y":1598,"style":1564},"count",[782,1606],{"x":1531,"y":1607,"width":1533,"height":1555,"rx":785,"fill":795,"stroke":796,"style":797},"158.0",[803,1609,1611],{"x":793,"y":1610,"style":1559},"180.0","a length column",[782,1613],{"x":1536,"y":1607,"width":1537,"height":1555,"rx":785,"fill":788,"stroke":796,"style":797},[803,1615,1617],{"x":1541,"y":1616,"style":1564},"179.0","mm, cm, m",[782,1619],{"x":1547,"y":1607,"width":1537,"height":1555,"rx":785,"fill":788,"stroke":796,"style":797},[803,1621,1622],{"x":1550,"y":1616,"style":1564},"metres — m ≠ million",[25,1624,1626],{"id":1625},"step-5-let-pint-handle-the-units-and-report-the-failures","Step 5 — Let pint handle the units, and report the failures",[10,1628,1629,1630,1632],{},"For columns with many different units, or units with prefixes and compound forms, the ",[14,1631,16],{}," library knows thousands of units and does the conversion itself. Use the same split, then hand the unit to pint:",[30,1634,1636],{"className":75,"code":1635,"language":77,"meta":35,"style":35},"import pint\n\nureg = pint.UnitRegistry()\n\ndef litres(text):\n    number, unit = split_value(text)\n    if number is None:\n        return None\n    try:\n        return round(ureg.Quantity(to_number(number), unit or \"l\").to(\"liter\").magnitude, 4)\n    except pint.errors.UndefinedUnitError:\n        return None\n\ndf[\"Volume l\"] = df[\"Volume\"].apply(litres)\nprint(df.loc[df[\"Volume\"] != \"\", [\"SKU\", \"Volume\", \"Volume l\"]].to_string(index=False))\n\nproblems = df.loc[df[\"Weight status\"].isin([\"unparsed\"]) | df[\"Weight status\"].str.startswith(\"unknown\"), [\"SKU\", \"Weight\", \"Weight status\"]]\nprint(\"weights needing attention:\", problems.to_dict(\"records\") or \"none\")\n",[14,1637,1638,1644,1648,1658,1662,1671,1679,1691,1697,1704,1731,1739,1745,1749,1768,1808,1813,1862],{"__ignoreMap":35},[39,1639,1640,1642],{"class":41,"line":42},[39,1641,85],{"class":84},[39,1643,59],{"class":88},[39,1645,1646],{"class":41,"line":92},[39,1647,110],{"emptyLinePlaceholder":109},[39,1649,1650,1653,1655],{"class":41,"line":106},[39,1651,1652],{"class":88},"ureg ",[39,1654,119],{"class":84},[39,1656,1657],{"class":88}," pint.UnitRegistry()\n",[39,1659,1660],{"class":41,"line":113},[39,1661,110],{"emptyLinePlaceholder":109},[39,1663,1664,1666,1669],{"class":41,"line":150},[39,1665,263],{"class":84},[39,1667,1668],{"class":266}," litres",[39,1670,270],{"class":88},[39,1672,1673,1675,1677],{"class":41,"line":255},[39,1674,321],{"class":88},[39,1676,119],{"class":84},[39,1678,1095],{"class":88},[39,1680,1681,1683,1685,1687,1689],{"class":41,"line":260},[39,1682,295],{"class":84},[39,1684,1102],{"class":88},[39,1686,461],{"class":84},[39,1688,310],{"class":137},[39,1690,466],{"class":88},[39,1692,1693,1695],{"class":41,"line":273},[39,1694,307],{"class":84},[39,1696,341],{"class":137},[39,1698,1699,1702],{"class":41,"line":292},[39,1700,1701],{"class":84},"    try",[39,1703,466],{"class":88},[39,1705,1706,1708,1710,1713,1715,1718,1721,1724,1727,1729],{"class":41,"line":304},[39,1707,307],{"class":84},[39,1709,1193],{"class":137},[39,1711,1712],{"class":88},"(ureg.Quantity(to_number(number), unit ",[39,1714,338],{"class":84},[39,1716,1717],{"class":49}," \"l\"",[39,1719,1720],{"class":88},").to(",[39,1722,1723],{"class":49},"\"liter\"",[39,1725,1726],{"class":88},").magnitude, ",[39,1728,854],{"class":137},[39,1730,147],{"class":88},[39,1732,1733,1736],{"class":41,"line":318},[39,1734,1735],{"class":84},"    except",[39,1737,1738],{"class":88}," pint.errors.UndefinedUnitError:\n",[39,1740,1741,1743],{"class":41,"line":329},[39,1742,307],{"class":84},[39,1744,341],{"class":137},[39,1746,1747],{"class":41,"line":344},[39,1748,110],{"emptyLinePlaceholder":109},[39,1750,1751,1753,1756,1758,1760,1762,1765],{"class":41,"line":349},[39,1752,1423],{"class":88},[39,1754,1755],{"class":49},"\"Volume l\"",[39,1757,1429],{"class":88},[39,1759,119],{"class":84},[39,1761,357],{"class":88},[39,1763,1764],{"class":49},"\"Volume\"",[39,1766,1767],{"class":88},"].apply(litres)\n",[39,1769,1770,1772,1775,1777,1779,1782,1785,1788,1790,1792,1794,1796,1798,1800,1802,1804,1806],{"class":41,"line":391},[39,1771,394],{"class":137},[39,1773,1774],{"class":88},"(df.loc[df[",[39,1776,1764],{"class":49},[39,1778,1429],{"class":88},[39,1780,1781],{"class":84},"!=",[39,1783,1784],{"class":49}," \"\"",[39,1786,1787],{"class":88},", [",[39,1789,1254],{"class":49},[39,1791,128],{"class":88},[39,1793,1764],{"class":49},[39,1795,128],{"class":88},[39,1797,1755],{"class":49},[39,1799,1269],{"class":88},[39,1801,372],{"class":131},[39,1803,119],{"class":84},[39,1805,420],{"class":137},[39,1807,423],{"class":88},[39,1809,1811],{"class":41,"line":1810},16,[39,1812,110],{"emptyLinePlaceholder":109},[39,1814,1816,1819,1821,1824,1826,1829,1831,1834,1836,1838,1840,1843,1846,1849,1851,1853,1855,1857,1859],{"class":41,"line":1815},17,[39,1817,1818],{"class":88},"problems ",[39,1820,119],{"class":84},[39,1822,1823],{"class":88}," df.loc[df[",[39,1825,1228],{"class":49},[39,1827,1828],{"class":88},"].isin([",[39,1830,1119],{"class":49},[39,1832,1833],{"class":88},"]) ",[39,1835,190],{"class":84},[39,1837,357],{"class":88},[39,1839,1228],{"class":49},[39,1841,1842],{"class":88},"].str.startswith(",[39,1844,1845],{"class":49},"\"unknown\"",[39,1847,1848],{"class":88},"), [",[39,1850,1254],{"class":49},[39,1852,128],{"class":88},[39,1854,360],{"class":49},[39,1856,128],{"class":88},[39,1858,1228],{"class":49},[39,1860,1861],{"class":88},"]]\n",[39,1863,1865,1867,1869,1872,1875,1878,1880,1882,1885],{"class":41,"line":1864},18,[39,1866,394],{"class":137},[39,1868,627],{"class":88},[39,1870,1871],{"class":49},"\"weights needing attention:\"",[39,1873,1874],{"class":88},", problems.to_dict(",[39,1876,1877],{"class":49},"\"records\"",[39,1879,539],{"class":88},[39,1881,338],{"class":84},[39,1883,1884],{"class":49}," \"none\"",[39,1886,147],{"class":88},[10,1888,1889,1890,1893,1894,1897],{},"pint converts \"250 ml\", \"2.5 L\" and \"310ml\" to litres without a hand-written table — it knows that ",[14,1891,1892],{},"ml"," is millilitre and ",[14,1895,1896],{},"L"," is litre. It is the better choice when units are varied or scientific; a small explicit table is clearer when the column only ever has three or four units, and it lets you decide what ambiguous abbreviations mean. Either way, end with a list of everything that did not convert. Here every weight parsed — including the \"approx.\" one — so the list is empty; a cell like \"two bags\" would appear in it with status \"unparsed\" and go back to the supplier rather than vanish.",[763,1899,772,1904,772,1907,772,1910,772,1913,772,1918,772,1928,772,1934,772,1937,772,1942,772,1945,772,1948,772,1955,772,1960,772,1963,772,1967,772,1970,772,1973,772,1980,772,1985,772,1988,772,1992,772,1995,772,2000,772,2007,772,2013,772,2017],{"viewBox":1900,"role":766,"ariaLabelledBy":1901,"xmlns":770,"style":1518},"4 0 752 142",[1902,1903],"un-flow-t","un-flow-d",[774,1905,1906],{"id":1902},"From a text cell to a number in one unit",[778,1908,1909],{"id":1903},"Each cell is read as text, split into number and unit by a regular expression, the number text is converted with the separator rule, the unit is looked up in the column's table or in pint to get a factor, and the status of every cell is recorded so failures are reported.",[782,1911],{"x":854,"y":785,"width":1527,"height":1912,"fill":788},"142",[782,1914],{"x":1531,"y":1532,"width":1915,"height":1916,"rx":1917,"fill":795,"stroke":796,"style":956},"153.0","110.5","12",[904,1919,1921],{"transform":1920},"translate(84.5,37.8) scale(1.000)",[1922,1923],"path",{"d":1924,"fill":1925,"stroke":1926,"style":1927},"M4 4h16v16H4z M4 9h16 M4 14h16 M10 4v16","none","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[803,1929,1933],{"x":1930,"y":1931,"style":1932},"96.5","84.2","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Read as text",[803,1935,429],{"x":1930,"y":1936,"style":1564},"103.8",[41,1938],{"x1":1939,"y1":1940,"x2":1941,"y2":1940,"stroke":955,"style":956},"178.0","71.2","200.0",[958,1943],{"points":1944,"fill":961},"204.0,71.2 195.0,76.2 195.0,66.2",[782,1946],{"x":1947,"y":1532,"width":1915,"height":1916,"rx":1917,"fill":1538,"stroke":955,"style":956},"209.0",[904,1949,1951],{"transform":1950},"translate(273.5,37.8) scale(1.000)",[1922,1952],{"d":1953,"fill":1925,"stroke":1954,"style":1927},"M11 4a7 7 0 1 0 .01 0z M16 16l5 5","var(--brand-strong,#4338ca)",[803,1956,1959],{"x":1957,"y":1931,"style":1958},"285.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Split",[803,1961,1962],{"x":1957,"y":1936,"style":1564},"number + unit regex",[41,1964],{"x1":1965,"y1":1940,"x2":1966,"y2":1940,"stroke":955,"style":956},"367.0","389.0",[958,1968],{"points":1969,"fill":961},"393.0,71.2 384.0,76.2 384.0,66.2",[782,1971],{"x":1972,"y":1532,"width":1915,"height":1916,"rx":1917,"fill":899,"stroke":900,"style":956},"398.0",[904,1974,1976],{"transform":1975},"translate(462.5,37.8) scale(1.000)",[1922,1977],{"d":1978,"fill":1925,"stroke":1979,"style":1927},"M20 11a8 8 0 1 0-2.3 5.7 M20 4v7h-7","var(--teal-ink,#0b6157)",[803,1981,1984],{"x":1982,"y":1931,"style":1983},"474.5","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Convert",[803,1986,1987],{"x":1982,"y":1936,"style":1564},"separators, factor",[41,1989],{"x1":1990,"y1":1940,"x2":1991,"y2":1940,"stroke":955,"style":956},"556.0","578.0",[958,1993],{"points":1994,"fill":961},"582.0,71.2 573.0,76.2 573.0,66.2",[782,1996],{"x":1997,"y":1532,"width":1915,"height":1916,"rx":1917,"fill":1998,"stroke":1999,"style":956},"587.0","#fdefd8","var(--gold,#b4740a)",[904,2001,2003],{"transform":2002},"translate(651.5,29.0) scale(1.000)",[1922,2004],{"d":2005,"fill":1925,"stroke":2006,"style":1927},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","var(--gold-ink,#7a4e06)",[803,2008,2012],{"x":2009,"y":2010,"style":2011},"663.5","75.5","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Status",[803,2014,2016],{"x":2009,"y":2015,"style":1564},"95.0","ok \u002F blank \u002F unknown",[803,2018,889],{"x":2009,"y":2019,"style":1564},"112.5",[25,2021,2023],{"id":2022},"common-pitfalls","Common pitfalls",[2025,2026,2027,2035,2041,2047,2053],"ul",{},[2028,2029,2030,2034],"li",{},[2031,2032,2033],"strong",{},"One unit table for the whole sheet."," The same letters mean different things in different columns.",[2028,2036,2037,2040],{},[2031,2038,2039],{},"Guessing decimal commas."," Apply a clear rule and check ambiguous values like \"1,500\".",[2028,2042,2043,2046],{},[2031,2044,2045],{},"Bare numbers assumed to be in some unit."," Decide the default per column and document it.",[2028,2048,2049,2052],{},[2031,2050,2051],{},"Dropping cells that do not parse."," Record a status and report them.",[2028,2054,2055,2058],{},[2031,2056,2057],{},"Keeping the original text only."," Store the converted number in a new column and keep the original for audit.",[25,2060,2062],{"id":2061},"conclusion","Conclusion",[10,2064,2065,2066,2068],{},"To parse numbers with units from Excel cells, read the column as text, split each cell into a number and a unit with a regular expression, convert the number text with an explicit rule for thousands and decimal commas, and multiply by a factor from a per-column unit table — or let ",[14,2067,16],{}," do the conversion when units are varied. Treat magnitude suffixes such as k and M as a table of their own, give bare numbers a deliberate default unit, and record a status for every cell so that anything that failed to parse is reported rather than lost.",[25,2070,2072],{"id":2071},"frequently-asked-questions","Frequently asked questions",[10,2074,2075,2078],{},[2031,2076,2077],{},"How do I convert \"12 kg\" text to a number in pandas?","\nSplit the text into the number and the unit with a regular expression, convert the number with float after handling separators, and multiply by a conversion factor for the unit.",[10,2080,2081,2084],{},[2031,2082,2083],{},"How do I convert mixed units like g, kg and t to one unit?","\nUse a dictionary mapping each unit to its factor in the target unit, such as g to 0.001 and t to 1000 for kilograms, or use the pint library to convert.",[10,2086,2087,2090],{},[2031,2088,2089],{},"How do I convert values like 3.5k or 1.2M to numbers?","\nSplit off the suffix and multiply by its magnitude: k for thousand, M for million.",[10,2092,2093,2096],{},[2031,2094,2095],{},"How do I handle decimal commas such as 0,75 kg?","\nTreat a comma followed by groups of exactly three digits as a thousands separator and any other single comma as a decimal point, and check ambiguous values against the rest of the column.",[25,2098,2100],{"id":2099},"related","Related",[2025,2102,2103,2109,2116,2123],{},[2028,2104,2105,2106,2108],{},"Up one level: ",[19,2107,22],{"href":21}," — getting data out of awkward layouts.",[2028,2110,2111,2115],{},[19,2112,2114],{"href":2113},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fextract-numbers-and-codes-from-text-with-pandas\u002F","Extract Numbers and Codes from Text with pandas"," — numbers buried in longer text.",[2028,2117,2118,2122],{},[19,2119,2121],{"href":2120},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-excel-text-columns-to-numbers-with-pandas\u002F","Convert Excel Text Columns to Numbers with pandas"," — numbers stored as text.",[2028,2124,2125,2129],{},[19,2126,2128],{"href":2127},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-percentage-strings-to-numbers-with-pandas\u002F","Convert Percentage Strings to Numbers with pandas"," — another unit-like suffix.",[2131,2132,2133],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .s_b0D, html code.shiki .s_b0D{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#72F088;--shiki-dark-font-weight:bold}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}",{"title":35,"searchDepth":92,"depth":92,"links":2135},[2136,2137,2138,2139,2140,2141,2142,2143,2144,2145],{"id":27,"depth":92,"text":28},{"id":68,"depth":92,"text":69},{"id":433,"depth":92,"text":434},{"id":977,"depth":92,"text":978},{"id":1283,"depth":92,"text":1284},{"id":1625,"depth":92,"text":1626},{"id":2022,"depth":92,"text":2023},{"id":2061,"depth":92,"text":2062},{"id":2071,"depth":92,"text":2072},{"id":2099,"depth":92,"text":2100},"2026-10-02","Turn cells like \"12 kg\", \"1,200 g\", \"2.5t\", \"500 ml\", \"3.5k\" and \"1,5 kg\" into numbers in one unit with pandas: a regular expression that splits value from unit, a conversion table for the units you expect, suffixes such as k and M, decimal commas, the pint library for broader unit support, and a report of every value that could not be parsed.","md",[2150,2152,2154,2156],{"q":2077,"a":2151},"Split the text into the number and the unit with a regular expression, convert the number with float after handling separators, and multiply by a conversion factor for the unit.",{"q":2083,"a":2153},"Use a dictionary mapping each unit to its factor in the target unit, such as g to 0.001 and t to 1000 for kilograms, or use the pint library to convert.",{"q":2089,"a":2155},"Split off the suffix and multiply by its magnitude: k for thousand, M for million.",{"q":2095,"a":2157},"Treat a comma followed by groups of exactly three digits as a thousands separator and any other single comma as a decimal point, and check ambiguous values against the rest of the column.","parse numbers with units in excel cells",null,{"breadcrumb":2161},[2162,2165,2168],{"name":2163,"item":2164},"Home","\u002F",{"name":2166,"item":2167},"Advanced Data Transformation and Cleaning for Excel Automation","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":22,"item":21},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-numbers-with-units-in-excel-cells",{"title":2171,"description":2172},"Parse Numbers with Units from Excel Cells in Python","Split \"12 kg\", \"2.5t\", \"500ml\" or \"3.5k\" into value and unit with pandas, convert to one base unit with a lookup table or pint, handle k\u002FM suffixes and decimal commas, and list unparseable cells.","parse-numbers-with-units-in-excel-cells","advanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-numbers-with-units-in-excel-cells\u002Findex",[2176,2177],"messy-sheets","data-cleaning","how-to","DFqEaR4rLOTycJcnfJ57s_2Bafcx2HLf9jvIw8GfJ7Y",[2181,2185],{"title":2182,"path":2183,"stem":2184,"children":-1},"Find the Header Row in an Excel Sheet with a Shifting Layout","\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Ffind-the-header-row-in-an-excel-sheet-with-a-shifting-layout","advanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Ffind-the-header-row-in-an-excel-sheet-with-a-shifting-layout\u002Findex",{"title":2186,"path":2187,"stem":2188,"children":-1},"Parse Records That Span Multiple Rows in Excel with pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-records-that-span-multiple-rows-in-excel","advanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fparse-records-that-span-multiple-rows-in-excel\u002Findex",[2190,2192],{"path":2191,"title":22},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets",{"path":2193,"title":2166},"\u002Fadvanced-data-transformation-and-cleaning",[],[2196,2199,2202,2205,2208,2211],{"path":2197,"title":2198,"kind":2178},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Ffreeze-header-row-in-excel-with-openpyxl","Freeze the Header Row in Excel with openpyxl",{"path":2200,"title":2201,"kind":2178},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fhandle-merged-cells-when-reading-excel-with-pandas","Handle Merged Cells When Reading Excel with pandas",{"path":2203,"title":2204,"kind":2178},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Funmerge-cells-and-fill-values-with-openpyxl","Unmerge Cells and Fill the Values with openpyxl",{"path":2206,"title":2207,"kind":2178},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fcombine-sheets-with-different-layouts-into-one-table","Combine Sheets with Different Layouts into One Table",{"path":2209,"title":2210,"kind":2178},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fextract-hyperlink-urls-from-excel-cells","Extract Hyperlink URLs from Excel Cells with openpyxl",{"path":2212,"title":2213,"kind":2178},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fextract-report-header-fields-from-an-excel-sheet","Extract Report Header Fields from an Excel Sheet with Python",1790972090908]