[{"data":1,"prerenderedAt":2432},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-currency-strings-to-numbers-with-pandas":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-currency-strings-to-numbers-with-pandas":2404,"ancestors:\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-currency-strings-to-numbers-with-pandas":2412,"children:\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-currency-strings-to-numbers-with-pandas":2417,"related:\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-currency-strings-to-numbers-with-pandas":2418},{"id":4,"title":5,"body":6,"dateModified":2368,"datePublished":2368,"description":2369,"extension":2370,"faq":2371,"keyword":2380,"kind":2381,"meta":2382,"navigation":383,"path":2392,"seo":2393,"slug":2396,"stem":2397,"topics":2398,"type":2402,"__hash__":2403},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-currency-strings-to-numbers-with-pandas\u002Findex.md","Convert Currency Strings Like \"$1,234.56\" and \"(500)\" to Numbers with pandas",{"type":7,"value":8,"toc":2354},"minimark",[9,40,297,302,329,343,347,491,510,514,520,1322,1348,1521,1525,1528,1623,1637,1805,1809,1815,1897,1938,1942,1957,2000,2008,2012,2015,2196,2204,2208,2261,2265,2279,2283,2289,2293,2299,2305,2311,2317,2321,2350],[10,11,12,13,17,18,21,22,25,26,29,30,33,34,39],"p",{},"An amount column that arrives as text is one of the most common reasons a report cannot total. The export shows what the source system displayed — ",[14,15,16],"code",{},"$1,234.56",", negatives in accounting brackets like ",[14,19,20],{},"(1,200.00)",", the currency written in front or behind — and pandas reads each cell as a string. ",[14,23,24],{},"astype(float)"," fails on the first dollar sign; ",[14,27,28],{},"pd.to_numeric(errors=\"coerce\")"," \"succeeds\" by turning every one of them into ",[14,31,32],{},"NaN",", which is worse. This page, part of ",[35,36,38],"a",{"href":37},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002F","Cleaning Excel Data With Pandas",", builds a parser that handles the formats real exports use, keeps the currency as its own column, and reports anything it cannot read.",[41,42,50,51,50,55,50,59,50,67,50,76,50,80,50,87,50,91,50,96,50,98,50,102,50,105,50,109,50,111,50,114,50,117,50,121,50,123,50,125,50,128,50,132,50,134,50,137,50,140,50,144,50,146,50,149,50,152,50,156,50,158,50,161,50,164,50,168,50,171,50,175,50,179,50,181,50,184,50,186,50,189,50,191,50,194,50,196,50,198,50,200,50,203,50,205,50,208,50,210,50,212,50,217,50,221,50,223,50,225,50,227,50,229,50,232,50,234,50,237,50,239,50,241,50,243,50,245,50,247,50,249,50,251,50,253,50,255,50,258,50,260,50,266,50,270,50,278,50,283,50,291],"svg",{"viewBox":43,"role":44,"ariaLabelledBy":45,"xmlns":48,"style":49},"124 0 511 248","img",[46,47],"cur-parse-t","cur-parse-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:511px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[52,53,54],"title",{"id":46},"Currency text parsed into amounts and codes",[56,57,58],"desc",{"id":47},"Left: text values from an export such as $1,234.56, (1,200.00), -€50, 1.234,56 EUR and 750.00- with a trailing minus. Right: each parsed into a signed number and a separate currency code: 1234.56 USD, -1200 blank, -50 EUR, 1234.56 EUR and -750 blank.",[60,61],"rect",{"x":62,"y":63,"width":64,"height":65,"fill":66},"124","0","511","248","#ffffff",[60,68],{"x":69,"y":70,"width":71,"height":72,"rx":63,"fill":73,"stroke":74,"style":75},"140.7","40.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[60,77],{"x":78,"y":70,"width":79,"height":72,"rx":63,"fill":73,"stroke":74,"style":75},"172.7","106.6",[81,82,86],"text",{"x":83,"y":84,"style":85},"226.0","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[60,88],{"x":69,"y":89,"width":71,"height":90,"rx":63,"fill":73,"stroke":74,"style":75},"64.0","28.0",[81,92,95],{"x":93,"y":94,"style":85},"156.7","83.0","1",[60,97],{"x":78,"y":89,"width":79,"height":90,"rx":63,"fill":66,"stroke":74,"style":75},[81,99,101],{"x":83,"y":94,"style":100},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","amount",[60,103],{"x":69,"y":104,"width":71,"height":90,"rx":63,"fill":73,"stroke":74,"style":75},"92.0",[81,106,108],{"x":93,"y":107,"style":85},"111.0","2",[60,110],{"x":78,"y":104,"width":79,"height":90,"rx":63,"fill":66,"stroke":74,"style":75},[81,112,16],{"x":83,"y":107,"style":113},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[60,115],{"x":69,"y":116,"width":71,"height":90,"rx":63,"fill":73,"stroke":74,"style":75},"120.0",[81,118,120],{"x":93,"y":119,"style":85},"139.0","3",[60,122],{"x":78,"y":116,"width":79,"height":90,"rx":63,"fill":66,"stroke":74,"style":75},[81,124,20],{"x":83,"y":119,"style":113},[60,126],{"x":69,"y":127,"width":71,"height":90,"rx":63,"fill":73,"stroke":74,"style":75},"148.0",[81,129,131],{"x":93,"y":130,"style":85},"167.0","4",[60,133],{"x":78,"y":127,"width":79,"height":90,"rx":63,"fill":66,"stroke":74,"style":75},[81,135,136],{"x":83,"y":130,"style":113},"-€50",[60,138],{"x":69,"y":139,"width":71,"height":90,"rx":63,"fill":73,"stroke":74,"style":75},"176.0",[81,141,143],{"x":93,"y":142,"style":85},"195.0","5",[60,145],{"x":78,"y":139,"width":79,"height":90,"rx":63,"fill":66,"stroke":74,"style":75},[81,147,148],{"x":83,"y":142,"style":113},"1.234,56 EUR",[60,150],{"x":69,"y":151,"width":71,"height":90,"rx":63,"fill":73,"stroke":74,"style":75},"204.0",[81,153,155],{"x":93,"y":154,"style":85},"223.0","6",[60,157],{"x":78,"y":151,"width":79,"height":90,"rx":63,"fill":66,"stroke":74,"style":75},[81,159,160],{"x":83,"y":154,"style":113},"750.00-",[60,162],{"x":163,"y":70,"width":71,"height":72,"rx":63,"fill":73,"stroke":74,"style":75},"462.4",[60,165],{"x":166,"y":70,"width":167,"height":72,"rx":63,"fill":73,"stroke":74,"style":75},"494.4","74.2",[81,169,86],{"x":170,"y":84,"style":85},"531.6",[60,172],{"x":173,"y":70,"width":174,"height":72,"rx":63,"fill":73,"stroke":74,"style":75},"568.7","50.6",[81,176,178],{"x":177,"y":84,"style":85},"594.0","B",[60,180],{"x":163,"y":89,"width":71,"height":90,"rx":63,"fill":73,"stroke":74,"style":75},[81,182,95],{"x":183,"y":94,"style":85},"478.4",[60,185],{"x":166,"y":89,"width":167,"height":90,"rx":63,"fill":66,"stroke":74,"style":75},[81,187,188],{"x":170,"y":94,"style":100},"value",[60,190],{"x":173,"y":89,"width":174,"height":90,"rx":63,"fill":66,"stroke":74,"style":75},[81,192,193],{"x":177,"y":94,"style":100},"cur",[60,195],{"x":163,"y":104,"width":71,"height":90,"rx":63,"fill":73,"stroke":74,"style":75},[81,197,108],{"x":183,"y":107,"style":85},[60,199],{"x":166,"y":104,"width":167,"height":90,"rx":63,"fill":66,"stroke":74,"style":75},[81,201,202],{"x":170,"y":107,"style":113},"1234.56",[60,204],{"x":173,"y":104,"width":174,"height":90,"rx":63,"fill":66,"stroke":74,"style":75},[81,206,207],{"x":177,"y":107,"style":113},"USD",[60,209],{"x":163,"y":116,"width":71,"height":90,"rx":63,"fill":73,"stroke":74,"style":75},[81,211,120],{"x":183,"y":119,"style":85},[60,213],{"x":166,"y":116,"width":167,"height":90,"rx":63,"fill":214,"stroke":215,"style":216},"#d9f4f1","var(--teal,#0f9488)","stroke-width:1.5px",[81,218,220],{"x":170,"y":119,"style":219},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","-1200",[60,222],{"x":173,"y":116,"width":174,"height":90,"rx":63,"fill":66,"stroke":74,"style":75},[60,224],{"x":163,"y":127,"width":71,"height":90,"rx":63,"fill":73,"stroke":74,"style":75},[81,226,131],{"x":183,"y":130,"style":85},[60,228],{"x":166,"y":127,"width":167,"height":90,"rx":63,"fill":214,"stroke":215,"style":216},[81,230,231],{"x":170,"y":130,"style":219},"-50",[60,233],{"x":173,"y":127,"width":174,"height":90,"rx":63,"fill":66,"stroke":74,"style":75},[81,235,236],{"x":177,"y":130,"style":113},"EUR",[60,238],{"x":163,"y":139,"width":71,"height":90,"rx":63,"fill":73,"stroke":74,"style":75},[81,240,143],{"x":183,"y":142,"style":85},[60,242],{"x":166,"y":139,"width":167,"height":90,"rx":63,"fill":66,"stroke":74,"style":75},[81,244,202],{"x":170,"y":142,"style":113},[60,246],{"x":173,"y":139,"width":174,"height":90,"rx":63,"fill":66,"stroke":74,"style":75},[81,248,236],{"x":177,"y":142,"style":113},[60,250],{"x":163,"y":151,"width":71,"height":90,"rx":63,"fill":73,"stroke":74,"style":75},[81,252,155],{"x":183,"y":154,"style":85},[60,254],{"x":166,"y":151,"width":167,"height":90,"rx":63,"fill":214,"stroke":215,"style":216},[81,256,257],{"x":170,"y":154,"style":219},"-750",[60,259],{"x":173,"y":151,"width":174,"height":90,"rx":63,"fill":66,"stroke":74,"style":75},[81,261,265],{"x":262,"y":263,"style":264},"210.0","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","amount (text)",[81,267,269],{"x":268,"y":263,"style":264},"540.9","value and currency",[271,272],"line",{"x1":273,"y1":274,"x2":275,"y2":274,"stroke":276,"style":277},"289.3","136.0","448.4","var(--brand,#5b5cf0)","stroke-width:2px",[279,280],"polygon",{"points":281,"fill":282},"452.4,136.0 443.4,141.0 443.4,131.0","#5b5cf0",[60,284],{"x":285,"y":286,"width":287,"height":288,"rx":289,"fill":66,"stroke":74,"style":290},"313.6","103.0","114.5","22.0","11","stroke-width:1.2px",[81,292,296],{"x":293,"y":294,"style":295},"370.9","118.0","font-size:11.5px;font-weight:400;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","parse_money()",[298,299,301],"h2",{"id":300},"prerequisites","Prerequisites",[303,304,309],"pre",{"className":305,"code":306,"language":307,"meta":308,"style":308},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[14,310,311],{"__ignoreMap":308},[312,313,315,319,323,326],"span",{"class":271,"line":314},1,[312,316,318],{"class":317},"sMTad","pip",[312,320,322],{"class":321},"srMev"," install",[312,324,325],{"class":321}," pandas",[312,327,328],{"class":321}," openpyxl\n",[10,330,331,334,335,338,339,342],{},[14,332,333],{},"ledger.xlsx"," has eleven invoices whose amounts cover the usual export formats: a dollar amount with a thousands comma, an accounting negative in brackets, a euro amount with a leading minus, a European-formatted amount with a code, a trailing minus as some ERP systems write it, a ",[14,336,337],{},"CR"," suffix for a credit, a code prefix, a pound sign with a space, ",[14,340,341],{},"n\u002Fa",", an Indian-style grouped number, and one amount with a word typed in front of it.",[298,344,346],{"id":345},"step-1-see-why-the-obvious-conversions-fail","Step 1 — See why the obvious conversions fail",[303,348,352],{"className":349,"code":350,"language":351,"meta":308,"style":308},"language-python shiki shiki-themes github-light github-dark-high-contrast","import re\nimport pandas as pd\n\nledger = pd.read_excel(\"ledger.xlsx\")\ntry:\n    ledger[\"amount\"].astype(float)\nexcept ValueError as exc:\n    print(\"astype:\", exc)\n\nprint(pd.to_numeric(ledger[\"amount\"], errors=\"coerce\").tolist())\n","python",[14,353,354,364,378,385,403,412,430,445,460,465],{"__ignoreMap":308},[312,355,356,360],{"class":271,"line":314},[312,357,359],{"class":358},"s-kum","import",[312,361,363],{"class":362},"skGVy"," re\n",[312,365,367,369,372,375],{"class":271,"line":366},2,[312,368,359],{"class":358},[312,370,371],{"class":362}," pandas ",[312,373,374],{"class":358},"as",[312,376,377],{"class":362}," pd\n",[312,379,381],{"class":271,"line":380},3,[312,382,384],{"emptyLinePlaceholder":383},true,"\n",[312,386,388,391,394,397,400],{"class":271,"line":387},4,[312,389,390],{"class":362},"ledger ",[312,392,393],{"class":358},"=",[312,395,396],{"class":362}," pd.read_excel(",[312,398,399],{"class":321},"\"ledger.xlsx\"",[312,401,402],{"class":362},")\n",[312,404,406,409],{"class":271,"line":405},5,[312,407,408],{"class":358},"try",[312,410,411],{"class":362},":\n",[312,413,415,418,421,424,428],{"class":271,"line":414},6,[312,416,417],{"class":362},"    ledger[",[312,419,420],{"class":321},"\"amount\"",[312,422,423],{"class":362},"].astype(",[312,425,427],{"class":426},"sP0c6","float",[312,429,402],{"class":362},[312,431,433,436,439,442],{"class":271,"line":432},7,[312,434,435],{"class":358},"except",[312,437,438],{"class":426}," ValueError",[312,440,441],{"class":358}," as",[312,443,444],{"class":362}," exc:\n",[312,446,448,451,454,457],{"class":271,"line":447},8,[312,449,450],{"class":426},"    print",[312,452,453],{"class":362},"(",[312,455,456],{"class":321},"\"astype:\"",[312,458,459],{"class":362},", exc)\n",[312,461,463],{"class":271,"line":462},9,[312,464,384],{"emptyLinePlaceholder":383},[312,466,468,471,474,476,479,483,485,488],{"class":271,"line":467},10,[312,469,470],{"class":426},"print",[312,472,473],{"class":362},"(pd.to_numeric(ledger[",[312,475,420],{"class":321},[312,477,478],{"class":362},"], ",[312,480,482],{"class":481},"sa561","errors",[312,484,393],{"class":358},[312,486,487],{"class":321},"\"coerce\"",[312,489,490],{"class":362},").tolist())\n",[10,492,493,495,496,498,499,502,503,506,507,509],{},[14,494,24],{}," stops at ",[14,497,16],{},". ",[14,500,501],{},"to_numeric"," with ",[14,504,505],{},"errors=\"coerce\""," returns ",[14,508,32],{}," for every value — including the nine that are perfectly good amounts — and nothing warns you. A total over that column would be zero. Coercing is useful as a final step, after cleaning, to catch what is left; used first, it silently discards the data.",[298,511,513],{"id":512},"step-2-parse-each-value-into-a-sign-a-number-and-a-currency","Step 2 — Parse each value into a sign, a number and a currency",[10,515,516,517,519],{},"Handle the formats one feature at a time: pull out the currency (symbol or three-letter code), note whether the amount is negative (leading or trailing minus, brackets, a ",[14,518,337],{}," suffix), then decide which separator is the decimal point and strip the other:",[303,521,523],{"className":349,"code":522,"language":351,"meta":308,"style":308},"SYMBOLS = {\"$\": \"USD\", \"€\": \"EUR\", \"£\": \"GBP\", \"¥\": \"JPY\"}\n\ndef parse_money(text):\n    if pd.isna(text):\n        return None, None\n    s = str(text).strip()\n    currency = None\n    for sym, code in SYMBOLS.items():\n        if sym in s:\n            currency, s = code, s.replace(sym, \"\")\n    m = re.search(r\"\\b([A-Z]{3})\\b\", s)\n    if m and m.group(1) not in (\"CR\", \"DR\"):\n        currency, s = m.group(1), s.replace(m.group(1), \"\")\n    negative = False\n    if re.search(r\"\\bCR\\b\", s):\n        negative, s = True, re.sub(r\"\\bCR\\b\", \"\", s)\n    s = re.sub(r\"\\bDR\\b\", \"\", s).strip()\n    if s.startswith(\"(\") and s.endswith(\")\"):\n        negative, s = True, s[1:-1]\n    if s.endswith(\"-\"):\n        negative, s = True, s[:-1]\n    if s.startswith(\"-\"):\n        negative, s = True, s[1:]\n    s = s.replace(\" \", \"\")\n    if \",\" in s and \".\" in s:\n        decimal = \",\" if s.rfind(\",\") > s.rfind(\".\") else \".\"\n    elif \",\" in s and re.fullmatch(r\"\\d+,\\d{2}\", s):\n        decimal = \",\"\n    else:\n        decimal = \".\"\n    thousands = \".\" if decimal == \",\" else \",\"\n    s = s.replace(thousands, \"\").replace(decimal, \".\")\n    if not re.fullmatch(r\"\\d+(\\.\\d+)?\", s):\n        return None, currency\n    value = float(s)\n    return (-value if negative else value), currency\n\nfor raw in ledger[\"amount\"]:\n    print(f\"{raw!r:16} -> {parse_money(raw)}\")\n",[14,524,525,579,583,595,603,616,629,639,656,669,684,722,761,785,796,819,851,881,904,929,941,959,970,986,1005,1027,1064,1103,1113,1121,1130,1155,1174,1212,1222,1236,1260,1265,1284],{"__ignoreMap":308},[312,526,527,530,533,536,539,542,545,548,551,553,556,558,561,563,566,568,571,573,576],{"class":271,"line":314},[312,528,529],{"class":426},"SYMBOLS",[312,531,532],{"class":358}," =",[312,534,535],{"class":362}," {",[312,537,538],{"class":321},"\"$\"",[312,540,541],{"class":362},": ",[312,543,544],{"class":321},"\"USD\"",[312,546,547],{"class":362},", ",[312,549,550],{"class":321},"\"€\"",[312,552,541],{"class":362},[312,554,555],{"class":321},"\"EUR\"",[312,557,547],{"class":362},[312,559,560],{"class":321},"\"£\"",[312,562,541],{"class":362},[312,564,565],{"class":321},"\"GBP\"",[312,567,547],{"class":362},[312,569,570],{"class":321},"\"¥\"",[312,572,541],{"class":362},[312,574,575],{"class":321},"\"JPY\"",[312,577,578],{"class":362},"}\n",[312,580,581],{"class":271,"line":366},[312,582,384],{"emptyLinePlaceholder":383},[312,584,585,588,592],{"class":271,"line":380},[312,586,587],{"class":358},"def",[312,589,591],{"class":590},"s_Opv"," parse_money",[312,593,594],{"class":362},"(text):\n",[312,596,597,600],{"class":271,"line":387},[312,598,599],{"class":358},"    if",[312,601,602],{"class":362}," pd.isna(text):\n",[312,604,605,608,611,613],{"class":271,"line":405},[312,606,607],{"class":358},"        return",[312,609,610],{"class":426}," None",[312,612,547],{"class":362},[312,614,615],{"class":426},"None\n",[312,617,618,621,623,626],{"class":271,"line":414},[312,619,620],{"class":362},"    s ",[312,622,393],{"class":358},[312,624,625],{"class":426}," str",[312,627,628],{"class":362},"(text).strip()\n",[312,630,631,634,636],{"class":271,"line":432},[312,632,633],{"class":362},"    currency ",[312,635,393],{"class":358},[312,637,638],{"class":426}," None\n",[312,640,641,644,647,650,653],{"class":271,"line":447},[312,642,643],{"class":358},"    for",[312,645,646],{"class":362}," sym, code ",[312,648,649],{"class":358},"in",[312,651,652],{"class":426}," SYMBOLS",[312,654,655],{"class":362},".items():\n",[312,657,658,661,664,666],{"class":271,"line":462},[312,659,660],{"class":358},"        if",[312,662,663],{"class":362}," sym ",[312,665,649],{"class":358},[312,667,668],{"class":362}," s:\n",[312,670,671,674,676,679,682],{"class":271,"line":467},[312,672,673],{"class":362},"            currency, s ",[312,675,393],{"class":358},[312,677,678],{"class":362}," code, s.replace(sym, ",[312,680,681],{"class":321},"\"\"",[312,683,402],{"class":362},[312,685,687,690,692,695,698,701,704,708,711,714,717,719],{"class":271,"line":686},11,[312,688,689],{"class":362},"    m ",[312,691,393],{"class":358},[312,693,694],{"class":362}," re.search(",[312,696,697],{"class":358},"r",[312,699,700],{"class":321},"\"",[312,702,703],{"class":426},"\\b([",[312,705,707],{"class":706},"sSjpA","A-Z",[312,709,710],{"class":426},"]",[312,712,713],{"class":358},"{3}",[312,715,716],{"class":426},")\\b",[312,718,700],{"class":321},[312,720,721],{"class":362},", s)\n",[312,723,725,727,730,733,736,738,741,744,747,750,753,755,758],{"class":271,"line":724},12,[312,726,599],{"class":358},[312,728,729],{"class":362}," m ",[312,731,732],{"class":358},"and",[312,734,735],{"class":362}," m.group(",[312,737,95],{"class":426},[312,739,740],{"class":362},") ",[312,742,743],{"class":358},"not",[312,745,746],{"class":358}," in",[312,748,749],{"class":362}," (",[312,751,752],{"class":321},"\"CR\"",[312,754,547],{"class":362},[312,756,757],{"class":321},"\"DR\"",[312,759,760],{"class":362},"):\n",[312,762,764,767,769,771,773,776,778,781,783],{"class":271,"line":763},13,[312,765,766],{"class":362},"        currency, s ",[312,768,393],{"class":358},[312,770,735],{"class":362},[312,772,95],{"class":426},[312,774,775],{"class":362},"), s.replace(m.group(",[312,777,95],{"class":426},[312,779,780],{"class":362},"), ",[312,782,681],{"class":321},[312,784,402],{"class":362},[312,786,788,791,793],{"class":271,"line":787},14,[312,789,790],{"class":362},"    negative ",[312,792,393],{"class":358},[312,794,795],{"class":426}," False\n",[312,797,799,801,803,805,807,810,812,814,816],{"class":271,"line":798},15,[312,800,599],{"class":358},[312,802,694],{"class":362},[312,804,697],{"class":358},[312,806,700],{"class":321},[312,808,809],{"class":426},"\\b",[312,811,337],{"class":321},[312,813,809],{"class":426},[312,815,700],{"class":321},[312,817,818],{"class":362},", s):\n",[312,820,822,825,827,830,833,835,837,839,841,843,845,847,849],{"class":271,"line":821},16,[312,823,824],{"class":362},"        negative, s ",[312,826,393],{"class":358},[312,828,829],{"class":426}," True",[312,831,832],{"class":362},", re.sub(",[312,834,697],{"class":358},[312,836,700],{"class":321},[312,838,809],{"class":426},[312,840,337],{"class":321},[312,842,809],{"class":426},[312,844,700],{"class":321},[312,846,547],{"class":362},[312,848,681],{"class":321},[312,850,721],{"class":362},[312,852,854,856,858,861,863,865,867,870,872,874,876,878],{"class":271,"line":853},17,[312,855,620],{"class":362},[312,857,393],{"class":358},[312,859,860],{"class":362}," re.sub(",[312,862,697],{"class":358},[312,864,700],{"class":321},[312,866,809],{"class":426},[312,868,869],{"class":321},"DR",[312,871,809],{"class":426},[312,873,700],{"class":321},[312,875,547],{"class":362},[312,877,681],{"class":321},[312,879,880],{"class":362},", s).strip()\n",[312,882,884,886,889,892,894,896,899,902],{"class":271,"line":883},18,[312,885,599],{"class":358},[312,887,888],{"class":362}," s.startswith(",[312,890,891],{"class":321},"\"(\"",[312,893,740],{"class":362},[312,895,732],{"class":358},[312,897,898],{"class":362}," s.endswith(",[312,900,901],{"class":321},"\")\"",[312,903,760],{"class":362},[312,905,907,909,911,913,916,918,921,924,926],{"class":271,"line":906},19,[312,908,824],{"class":362},[312,910,393],{"class":358},[312,912,829],{"class":426},[312,914,915],{"class":362},", s[",[312,917,95],{"class":426},[312,919,920],{"class":362},":",[312,922,923],{"class":358},"-",[312,925,95],{"class":426},[312,927,928],{"class":362},"]\n",[312,930,932,934,936,939],{"class":271,"line":931},20,[312,933,599],{"class":358},[312,935,898],{"class":362},[312,937,938],{"class":321},"\"-\"",[312,940,760],{"class":362},[312,942,944,946,948,950,953,955,957],{"class":271,"line":943},21,[312,945,824],{"class":362},[312,947,393],{"class":358},[312,949,829],{"class":426},[312,951,952],{"class":362},", s[:",[312,954,923],{"class":358},[312,956,95],{"class":426},[312,958,928],{"class":362},[312,960,962,964,966,968],{"class":271,"line":961},22,[312,963,599],{"class":358},[312,965,888],{"class":362},[312,967,938],{"class":321},[312,969,760],{"class":362},[312,971,973,975,977,979,981,983],{"class":271,"line":972},23,[312,974,824],{"class":362},[312,976,393],{"class":358},[312,978,829],{"class":426},[312,980,915],{"class":362},[312,982,95],{"class":426},[312,984,985],{"class":362},":]\n",[312,987,989,991,993,996,999,1001,1003],{"class":271,"line":988},24,[312,990,620],{"class":362},[312,992,393],{"class":358},[312,994,995],{"class":362}," s.replace(",[312,997,998],{"class":321},"\" \"",[312,1000,547],{"class":362},[312,1002,681],{"class":321},[312,1004,402],{"class":362},[312,1006,1008,1010,1013,1015,1018,1020,1023,1025],{"class":271,"line":1007},25,[312,1009,599],{"class":358},[312,1011,1012],{"class":321}," \",\"",[312,1014,746],{"class":358},[312,1016,1017],{"class":362}," s ",[312,1019,732],{"class":358},[312,1021,1022],{"class":321}," \".\"",[312,1024,746],{"class":358},[312,1026,668],{"class":362},[312,1028,1030,1033,1035,1037,1040,1043,1046,1048,1051,1053,1056,1058,1061],{"class":271,"line":1029},26,[312,1031,1032],{"class":362},"        decimal ",[312,1034,393],{"class":358},[312,1036,1012],{"class":321},[312,1038,1039],{"class":358}," if",[312,1041,1042],{"class":362}," s.rfind(",[312,1044,1045],{"class":321},"\",\"",[312,1047,740],{"class":362},[312,1049,1050],{"class":358},">",[312,1052,1042],{"class":362},[312,1054,1055],{"class":321},"\".\"",[312,1057,740],{"class":362},[312,1059,1060],{"class":358},"else",[312,1062,1063],{"class":321}," \".\"\n",[312,1065,1067,1070,1072,1074,1076,1078,1081,1083,1085,1088,1091,1094,1096,1099,1101],{"class":271,"line":1066},27,[312,1068,1069],{"class":358},"    elif",[312,1071,1012],{"class":321},[312,1073,746],{"class":358},[312,1075,1017],{"class":362},[312,1077,732],{"class":358},[312,1079,1080],{"class":362}," re.fullmatch(",[312,1082,697],{"class":358},[312,1084,700],{"class":321},[312,1086,1087],{"class":426},"\\d",[312,1089,1090],{"class":358},"+",[312,1092,1093],{"class":321},",",[312,1095,1087],{"class":426},[312,1097,1098],{"class":358},"{2}",[312,1100,700],{"class":321},[312,1102,818],{"class":362},[312,1104,1106,1108,1110],{"class":271,"line":1105},28,[312,1107,1032],{"class":362},[312,1109,393],{"class":358},[312,1111,1112],{"class":321}," \",\"\n",[312,1114,1116,1119],{"class":271,"line":1115},29,[312,1117,1118],{"class":358},"    else",[312,1120,411],{"class":362},[312,1122,1124,1126,1128],{"class":271,"line":1123},30,[312,1125,1032],{"class":362},[312,1127,393],{"class":358},[312,1129,1063],{"class":321},[312,1131,1133,1136,1138,1140,1142,1145,1148,1150,1153],{"class":271,"line":1132},31,[312,1134,1135],{"class":362},"    thousands ",[312,1137,393],{"class":358},[312,1139,1022],{"class":321},[312,1141,1039],{"class":358},[312,1143,1144],{"class":362}," decimal ",[312,1146,1147],{"class":358},"==",[312,1149,1012],{"class":321},[312,1151,1152],{"class":358}," else",[312,1154,1112],{"class":321},[312,1156,1158,1160,1162,1165,1167,1170,1172],{"class":271,"line":1157},32,[312,1159,620],{"class":362},[312,1161,393],{"class":358},[312,1163,1164],{"class":362}," s.replace(thousands, ",[312,1166,681],{"class":321},[312,1168,1169],{"class":362},").replace(decimal, ",[312,1171,1055],{"class":321},[312,1173,402],{"class":362},[312,1175,1177,1179,1182,1184,1186,1188,1190,1192,1194,1198,1200,1202,1205,1208,1210],{"class":271,"line":1176},33,[312,1178,599],{"class":358},[312,1180,1181],{"class":358}," not",[312,1183,1080],{"class":362},[312,1185,697],{"class":358},[312,1187,700],{"class":321},[312,1189,1087],{"class":426},[312,1191,1090],{"class":358},[312,1193,453],{"class":426},[312,1195,1197],{"class":1196},"s_b0D","\\.",[312,1199,1087],{"class":426},[312,1201,1090],{"class":358},[312,1203,1204],{"class":426},")",[312,1206,1207],{"class":358},"?",[312,1209,700],{"class":321},[312,1211,818],{"class":362},[312,1213,1215,1217,1219],{"class":271,"line":1214},34,[312,1216,607],{"class":358},[312,1218,610],{"class":426},[312,1220,1221],{"class":362},", currency\n",[312,1223,1225,1228,1230,1233],{"class":271,"line":1224},35,[312,1226,1227],{"class":362},"    value ",[312,1229,393],{"class":358},[312,1231,1232],{"class":426}," float",[312,1234,1235],{"class":362},"(s)\n",[312,1237,1239,1242,1244,1246,1249,1252,1255,1257],{"class":271,"line":1238},36,[312,1240,1241],{"class":358},"    return",[312,1243,749],{"class":362},[312,1245,923],{"class":358},[312,1247,1248],{"class":362},"value ",[312,1250,1251],{"class":358},"if",[312,1253,1254],{"class":362}," negative ",[312,1256,1060],{"class":358},[312,1258,1259],{"class":362}," value), currency\n",[312,1261,1263],{"class":271,"line":1262},37,[312,1264,384],{"emptyLinePlaceholder":383},[312,1266,1268,1271,1274,1276,1279,1281],{"class":271,"line":1267},38,[312,1269,1270],{"class":358},"for",[312,1272,1273],{"class":362}," raw ",[312,1275,649],{"class":358},[312,1277,1278],{"class":362}," ledger[",[312,1280,420],{"class":321},[312,1282,1283],{"class":362},"]:\n",[312,1285,1287,1289,1291,1294,1296,1299,1302,1305,1308,1311,1313,1316,1318,1320],{"class":271,"line":1286},39,[312,1288,450],{"class":426},[312,1290,453],{"class":362},[312,1292,1293],{"class":358},"f",[312,1295,700],{"class":321},[312,1297,1298],{"class":706},"{",[312,1300,1301],{"class":362},"raw",[312,1303,1304],{"class":358},"!r:16",[312,1306,1307],{"class":706},"}",[312,1309,1310],{"class":321}," -> ",[312,1312,1298],{"class":706},[312,1314,1315],{"class":362},"parse_money(raw)",[312,1317,1307],{"class":706},[312,1319,700],{"class":321},[312,1321,402],{"class":362},[10,1323,1324,1325,1327,1328,1331,1332,1327,1334,1336,1337,1340,1341,506,1344,1347],{},"The order of the checks matters. Currency is removed first so that ",[14,1326,207],{}," and ",[14,1329,1330],{},"€"," do not confuse the sign and separator logic; ",[14,1333,337],{},[14,1335,869],{}," are excluded from the currency codes because they are credit and debit markers, not currencies; and the decimal separator is decided only after everything else has been stripped. The last ",[14,1338,1339],{},"fullmatch"," refuses anything that is not a clean number by then — ",[14,1342,1343],{},"about 300",[14,1345,1346],{},"None"," instead of raising.",[41,1349,50,1355,50,1358,50,1361,50,1365,50,1370,50,1375,50,1381,50,1384,50,1388,50,1391,50,1395,50,1399,50,1403,50,1405,50,1409,50,1411,50,1413,50,1415,50,1418,50,1421,50,1424,50,1426,50,1428,50,1430,50,1433,50,1435,50,1438,50,1441,50,1444,50,1446,50,1449,50,1451,50,1453,50,1455,50,1458,50,1461,50,1465,50,1467,50,1471,50,1473,50,1475,50,1477,50,1480,50,1483,50,1486,50,1488,50,1491,50,1493,50,1495,50,1497,50,1500,50,1502,50,1506,50,1508,50,1512,50,1514,50,1516,50,1518],{"viewBox":1350,"role":44,"ariaLabelledBy":1351,"xmlns":48,"style":1354},"4 0 752 276",[1352,1353],"cur-formats-t","cur-formats-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[52,1356,1357],{"id":1352},"Export formats and how the parser reads them",[56,1359,1360],{"id":1353},"A reference table. $1,234.56 is USD 1234.56 with a comma for thousands. (1,200.00) is minus 1200 because of the brackets. 750.00- is minus 750 because of the trailing minus. 100.00 CR is minus 100 because CR marks a credit. 1.234,56 EUR is EUR 1234.56 because the comma comes last. 12,34,567.00 is 1234567 with Indian digit grouping.",[60,1362],{"x":131,"y":63,"width":1363,"height":1364,"fill":66},"752","276",[60,1366],{"x":1367,"y":1368,"width":1369,"height":70,"rx":63,"fill":66,"stroke":74,"style":75},"20.0","16.0","114.4",[60,1371],{"x":1372,"y":1368,"width":1373,"height":70,"rx":63,"fill":1374,"stroke":276,"style":290},"134.4","201.9","#f0f4ff",[81,1376,1380],{"x":1377,"y":1378,"style":1379},"235.4","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Value",[60,1382],{"x":1383,"y":1368,"width":1373,"height":70,"rx":63,"fill":1374,"stroke":276,"style":290},"336.3",[81,1385,1387],{"x":1386,"y":1378,"style":1379},"437.2","Currency",[60,1389],{"x":1390,"y":1368,"width":1373,"height":70,"rx":63,"fill":1374,"stroke":276,"style":290},"538.1",[81,1392,1394],{"x":1393,"y":1378,"style":1379},"639.1","Rule applied",[60,1396],{"x":1367,"y":1397,"width":1369,"height":1398,"rx":63,"fill":73,"stroke":74,"style":75},"56.0","34.0",[81,1400,16],{"x":71,"y":1401,"style":1402},"78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start",[60,1404],{"x":1372,"y":1397,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1406,202],{"x":1377,"y":1407,"style":1408},"77.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[60,1410],{"x":1383,"y":1397,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1412,207],{"x":1386,"y":1407,"style":1408},[60,1414],{"x":1390,"y":1397,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1416,1417],{"x":1393,"y":1407,"style":1408},"symbol; comma = thousands",[60,1419],{"x":1367,"y":1420,"width":1369,"height":1398,"rx":63,"fill":73,"stroke":74,"style":75},"90.0",[81,1422,20],{"x":71,"y":1423,"style":1402},"112.0",[60,1425],{"x":1372,"y":1420,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1427,220],{"x":1377,"y":107,"style":1408},[60,1429],{"x":1383,"y":1420,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1431,1432],{"x":1386,"y":107,"style":1408},"—",[60,1434],{"x":1390,"y":1420,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1436,1437],{"x":1393,"y":107,"style":1408},"brackets = negative",[60,1439],{"x":1367,"y":1440,"width":1369,"height":1398,"rx":63,"fill":73,"stroke":74,"style":75},"124.0",[81,1442,160],{"x":71,"y":1443,"style":1402},"146.0",[60,1445],{"x":1372,"y":1440,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1447,257],{"x":1377,"y":1448,"style":1408},"145.0",[60,1450],{"x":1383,"y":1440,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1452,1432],{"x":1386,"y":1448,"style":1408},[60,1454],{"x":1390,"y":1440,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1456,1457],{"x":1393,"y":1448,"style":1408},"trailing minus",[60,1459],{"x":1367,"y":1460,"width":1369,"height":1398,"rx":63,"fill":73,"stroke":74,"style":75},"158.0",[81,1462,1464],{"x":71,"y":1463,"style":1402},"180.0","100.00 CR",[60,1466],{"x":1372,"y":1460,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1468,1470],{"x":1377,"y":1469,"style":1408},"179.0","-100",[60,1472],{"x":1383,"y":1460,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1474,1432],{"x":1386,"y":1469,"style":1408},[60,1476],{"x":1390,"y":1460,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1478,1479],{"x":1393,"y":1469,"style":1408},"CR = credit",[60,1481],{"x":1367,"y":1482,"width":1369,"height":1398,"rx":63,"fill":73,"stroke":74,"style":75},"192.0",[81,1484,148],{"x":71,"y":1485,"style":1402},"214.0",[60,1487],{"x":1372,"y":1482,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1489,202],{"x":1377,"y":1490,"style":1408},"213.0",[60,1492],{"x":1383,"y":1482,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1494,236],{"x":1386,"y":1490,"style":1408},[60,1496],{"x":1390,"y":1482,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1498,1499],{"x":1393,"y":1490,"style":1408},"comma last = decimal",[60,1501],{"x":1367,"y":83,"width":1369,"height":1398,"rx":63,"fill":73,"stroke":74,"style":75},[81,1503,1505],{"x":71,"y":1504,"style":1402},"248.0","12,34,567.00",[60,1507],{"x":1372,"y":83,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1509,1511],{"x":1377,"y":1510,"style":1408},"247.0","1234567",[60,1513],{"x":1383,"y":83,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1515,1432],{"x":1386,"y":1510,"style":1408},[60,1517],{"x":1390,"y":83,"width":1373,"height":1398,"rx":63,"fill":66,"stroke":74,"style":75},[81,1519,1520],{"x":1393,"y":1510,"style":1408},"any grouping removed",[298,1522,1524],{"id":1523},"step-3-apply-it-to-the-column-and-keep-the-currency","Step 3 — Apply it to the column and keep the currency",[10,1526,1527],{},"Apply the parser once and split its result into two columns:",[303,1529,1531],{"className":349,"code":1530,"language":351,"meta":308,"style":308},"parsed = ledger[\"amount\"].map(parse_money)\nledger[\"value\"] = parsed.str[0].astype(\"Float64\")\nledger[\"currency\"] = parsed.str[1]\nprint(ledger)\nprint(ledger.groupby(\"currency\", dropna=False)[\"value\"].sum())\n",[14,1532,1533,1547,1572,1589,1596],{"__ignoreMap":308},[312,1534,1535,1538,1540,1542,1544],{"class":271,"line":314},[312,1536,1537],{"class":362},"parsed ",[312,1539,393],{"class":358},[312,1541,1278],{"class":362},[312,1543,420],{"class":321},[312,1545,1546],{"class":362},"].map(parse_money)\n",[312,1548,1549,1552,1555,1558,1560,1563,1565,1567,1570],{"class":271,"line":366},[312,1550,1551],{"class":362},"ledger[",[312,1553,1554],{"class":321},"\"value\"",[312,1556,1557],{"class":362},"] ",[312,1559,393],{"class":358},[312,1561,1562],{"class":362}," parsed.str[",[312,1564,63],{"class":426},[312,1566,423],{"class":362},[312,1568,1569],{"class":321},"\"Float64\"",[312,1571,402],{"class":362},[312,1573,1574,1576,1579,1581,1583,1585,1587],{"class":271,"line":380},[312,1575,1551],{"class":362},[312,1577,1578],{"class":321},"\"currency\"",[312,1580,1557],{"class":362},[312,1582,393],{"class":358},[312,1584,1562],{"class":362},[312,1586,95],{"class":426},[312,1588,928],{"class":362},[312,1590,1591,1593],{"class":271,"line":387},[312,1592,470],{"class":426},[312,1594,1595],{"class":362},"(ledger)\n",[312,1597,1598,1600,1603,1605,1607,1610,1612,1615,1618,1620],{"class":271,"line":405},[312,1599,470],{"class":426},[312,1601,1602],{"class":362},"(ledger.groupby(",[312,1604,1578],{"class":321},[312,1606,547],{"class":362},[312,1608,1609],{"class":481},"dropna",[312,1611,393],{"class":358},[312,1613,1614],{"class":426},"False",[312,1616,1617],{"class":362},")[",[312,1619,1554],{"class":321},[312,1621,1622],{"class":362},"].sum())\n",[10,1624,1625,1626,1629,1630,1633,1634,1636],{},"A nullable ",[14,1627,1628],{},"Float64"," column keeps the missing and unparseable rows as ",[14,1631,1632],{},"\u003CNA>"," rather than turning it into a float ",[14,1635,32],{},", which makes missing values obvious in the output. Grouping by currency shows why the code column matters: adding dollars to euros gives a number with no meaning. Amounts with no currency marker are grouped under a missing currency; decide per source what they are — usually the company's reporting currency — and fill them explicitly.",[41,1638,50,1643,50,1646,50,1649,50,1652,50,1657,50,1668,50,1674,50,1678,50,1682,50,1685,50,1690,50,1693,50,1698,50,1705,50,1711,50,1715,50,1718,50,1722,50,1725,50,1728,50,1735,50,1740,50,1743,50,1746,50,1749,50,1753,50,1756,50,1759,50,1765,50,1769,50,1772,50,1775,50,1778,50,1782,50,1785,50,1788,50,1795,50,1800,50,1803],{"viewBox":1639,"role":44,"ariaLabelledBy":1640,"xmlns":48,"style":1354},"4 0 752 160",[1641,1642],"cur-steps-t","cur-steps-d",[52,1644,1645],{"id":1641},"The order of operations in parse_money",[56,1647,1648],{"id":1642},"Five steps in order. Remove the currency symbol or code and remember it. Detect negatives from brackets, a leading or trailing minus, or a CR suffix. Remove spaces. Decide the decimal separator from whichever of comma or dot comes last, and strip the other. Check the remainder is a plain number, and return None if it is not.",[60,1650],{"x":131,"y":63,"width":1363,"height":1651,"fill":66},"160",[60,1653],{"x":1367,"y":1368,"width":1654,"height":1655,"rx":1656,"fill":73,"stroke":74,"style":277},"115.2","128.0","12",[1658,1659,1661],"g",{"transform":1660},"translate(65.6,29.0) scale(1.000)",[1662,1663],"path",{"d":1664,"fill":1665,"stroke":1666,"style":1667},"M5 9h14 M4 15h14 M10 4l-2 16 M16 4l-2 16","none","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[81,1669,1673],{"x":1670,"y":1671,"style":1672},"77.6","75.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Take the",[81,1675,1677],{"x":1670,"y":1676,"style":1672},"93.0","currency",[81,1679,1681],{"x":1670,"y":1680,"style":1408},"112.5","$ € £ or USD,",[81,1683,236],{"x":1670,"y":1684,"style":1408},"130.0",[271,1686],{"x1":1687,"y1":1688,"x2":1689,"y2":1688,"stroke":276,"style":277},"140.2","80.0","162.2",[279,1691],{"points":1692,"fill":282},"166.2,80.0 157.2,85.0 157.2,75.0",[60,1694],{"x":1695,"y":1368,"width":1654,"height":1655,"rx":1656,"fill":1696,"stroke":1697,"style":277},"171.2","#fdefd8","var(--gold,#b4740a)",[1658,1699,1701],{"transform":1700},"translate(216.8,37.8) scale(1.000)",[1662,1702],{"d":1703,"fill":1665,"stroke":1704,"style":1667},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","var(--gold-ink,#7a4e06)",[81,1706,1710],{"x":1707,"y":1708,"style":1709},"228.8","84.2","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Find the sign",[81,1712,1714],{"x":1707,"y":1713,"style":1408},"103.8","( ), -, trailing -,",[81,1716,337],{"x":1707,"y":1717,"style":1408},"121.2",[271,1719],{"x1":1720,"y1":1688,"x2":1721,"y2":1688,"stroke":276,"style":277},"291.4","313.4",[279,1723],{"points":1724,"fill":282},"317.4,80.0 308.4,85.0 308.4,75.0",[60,1726],{"x":1727,"y":1368,"width":1654,"height":1655,"rx":1656,"fill":1374,"stroke":276,"style":277},"322.4",[1658,1729,1731],{"transform":1730},"translate(368.0,29.0) scale(1.000)",[1662,1732],{"d":1733,"fill":1665,"stroke":1734,"style":1667},"M3 5h18l-7 8v6l-4 2v-8z","var(--brand-strong,#4338ca)",[81,1736,1739],{"x":1737,"y":1671,"style":1738},"380.0","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Pick the",[81,1741,1742],{"x":1737,"y":1676,"style":1738},"decimal",[81,1744,1745],{"x":1737,"y":1680,"style":1408},"last of comma",[81,1747,1748],{"x":1737,"y":1684,"style":1408},"and dot",[271,1750],{"x1":1751,"y1":1688,"x2":1752,"y2":1688,"stroke":276,"style":277},"442.6","464.6",[279,1754],{"points":1755,"fill":282},"468.6,80.0 459.6,85.0 459.6,75.0",[60,1757],{"x":1758,"y":1368,"width":1654,"height":1655,"rx":1656,"fill":1374,"stroke":276,"style":277},"473.6",[1658,1760,1762],{"transform":1761},"translate(519.2,29.0) scale(1.000)",[1662,1763],{"d":1764,"fill":1665,"stroke":1734,"style":1667},"M20 11a8 8 0 1 0-2.3 5.7 M20 4v7h-7",[81,1766,1768],{"x":1767,"y":1671,"style":1738},"531.2","Strip",[81,1770,1771],{"x":1767,"y":1676,"style":1738},"separators",[81,1773,1774],{"x":1767,"y":1680,"style":1408},"thousands",[81,1776,1777],{"x":1767,"y":1684,"style":1408},"removed",[271,1779],{"x1":1780,"y1":1688,"x2":1781,"y2":1688,"stroke":276,"style":277},"593.8","615.8",[279,1783],{"points":1784,"fill":282},"619.8,80.0 610.8,85.0 610.8,75.0",[60,1786],{"x":1787,"y":1368,"width":1654,"height":1655,"rx":1656,"fill":214,"stroke":215,"style":277},"624.8",[1658,1789,1791],{"transform":1790},"translate(670.4,37.8) scale(1.000)",[1662,1792],{"d":1793,"fill":1665,"stroke":1794,"style":1667},"M5 12.5l4.5 4.5L19 7","var(--teal-ink,#0b6157)",[81,1796,1799],{"x":1797,"y":1708,"style":1798},"682.4","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Validate",[81,1801,1802],{"x":1797,"y":1713,"style":1408},"plain number, or",[81,1804,1346],{"x":1797,"y":1717,"style":1408},[298,1806,1808],{"id":1807},"step-4-report-what-did-not-parse","Step 4 — Report what did not parse",[10,1810,1811,1812,1814],{},"A parser that quietly returns ",[14,1813,1346],{}," for surprises is only safe if someone looks at the surprises. List every value that failed, with its row, before the data goes any further:",[303,1816,1818],{"className":349,"code":1817,"language":351,"meta":308,"style":308},"failed = ledger[ledger[\"value\"].isna() & ledger[\"amount\"].notna()]\nprint(f\"{len(failed)} amount(s) could not be parsed:\")\nprint(failed[[\"invoice\", \"amount\"]].to_string(index=False))\n",[14,1819,1820,1845,1870],{"__ignoreMap":308},[312,1821,1822,1825,1827,1830,1832,1835,1838,1840,1842],{"class":271,"line":314},[312,1823,1824],{"class":362},"failed ",[312,1826,393],{"class":358},[312,1828,1829],{"class":362}," ledger[ledger[",[312,1831,1554],{"class":321},[312,1833,1834],{"class":362},"].isna() ",[312,1836,1837],{"class":358},"&",[312,1839,1278],{"class":362},[312,1841,420],{"class":321},[312,1843,1844],{"class":362},"].notna()]\n",[312,1846,1847,1849,1851,1853,1855,1857,1860,1863,1865,1868],{"class":271,"line":366},[312,1848,470],{"class":426},[312,1850,453],{"class":362},[312,1852,1293],{"class":358},[312,1854,700],{"class":321},[312,1856,1298],{"class":706},[312,1858,1859],{"class":426},"len",[312,1861,1862],{"class":362},"(failed)",[312,1864,1307],{"class":706},[312,1866,1867],{"class":321}," amount(s) could not be parsed:\"",[312,1869,402],{"class":362},[312,1871,1872,1874,1877,1880,1882,1884,1887,1890,1892,1894],{"class":271,"line":380},[312,1873,470],{"class":426},[312,1875,1876],{"class":362},"(failed[[",[312,1878,1879],{"class":321},"\"invoice\"",[312,1881,547],{"class":362},[312,1883,420],{"class":321},[312,1885,1886],{"class":362},"]].to_string(",[312,1888,1889],{"class":481},"index",[312,1891,393],{"class":358},[312,1893,1614],{"class":426},[312,1895,1896],{"class":362},"))\n",[10,1898,1899,1900,1902,1903,1905,1906,1909,1910,547,1912,547,1915,547,1918,1921,1922,1924,1925,1928,1929,1932,1933,1937],{},"Only ",[14,1901,1343],{}," is listed. ",[14,1904,341],{}," never reached the parser: ",[14,1907,1908],{},"read_excel"," treats a handful of strings — ",[14,1911,341],{},[14,1913,1914],{},"NA",[14,1916,1917],{},"NULL",[14,1919,1920],{},"#N\u002FA"," and others — as missing by default, so the cell was already ",[14,1923,32],{}," when it was read. That is usually helpful, but it means a placeholder vanishes silently; pass ",[14,1926,1927],{},"keep_default_na=False"," and your own ",[14,1930,1931],{},"na_values"," when you need to see exactly what the sheet said. Anything that does appear in the failed list is a format the parser has not met — a new currency symbol, a value with a note typed after it — and deserves either a rule or a question to whoever owns the export. In a scheduled job, fail the run when the list contains anything other than the known placeholders; ",[35,1934,1936],{"href":1935},"\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 the pattern for whole sheets.",[298,1939,1941],{"id":1940},"step-5-know-the-ambiguous-case","Step 5 — Know the ambiguous case",[10,1943,1944,1945,1948,1949,1952,1953,1956],{},"One format cannot be decided from the text alone: a number with exactly one separator and three digits after it. ",[14,1946,1947],{},"1,234"," is one thousand two hundred and thirty-four in the United States and one point two three four in Germany; ",[14,1950,1951],{},"1.234"," is the reverse. The parser above treats a lone comma followed by exactly two digits as a decimal comma (",[14,1954,1955],{},"12,50","), and everything else with a lone separator as US-style. That is a reasonable default for amounts, which almost always carry cents, but it is a default:",[303,1958,1960],{"className":349,"code":1959,"language":351,"meta":308,"style":308},"for raw in [\"1,234\", \"1.234\", \"12,50\", \"12.50\"]:\n    print(raw, parse_money(raw))\n",[14,1961,1962,1993],{"__ignoreMap":308},[312,1963,1964,1966,1968,1970,1973,1976,1978,1981,1983,1986,1988,1991],{"class":271,"line":314},[312,1965,1270],{"class":358},[312,1967,1273],{"class":362},[312,1969,649],{"class":358},[312,1971,1972],{"class":362}," [",[312,1974,1975],{"class":321},"\"1,234\"",[312,1977,547],{"class":362},[312,1979,1980],{"class":321},"\"1.234\"",[312,1982,547],{"class":362},[312,1984,1985],{"class":321},"\"12,50\"",[312,1987,547],{"class":362},[312,1989,1990],{"class":321},"\"12.50\"",[312,1992,1283],{"class":362},[312,1994,1995,1997],{"class":271,"line":366},[312,1996,450],{"class":426},[312,1998,1999],{"class":362},"(raw, parse_money(raw))\n",[10,2001,2002,2003,2007],{},"When a source is known to be European, do not guess — read it with the conventions stated explicitly, as in ",[35,2004,2006],{"href":2005},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fread-csv-exports-with-semicolons-and-decimal-commas\u002F","Read European CSV Exports with Semicolons and Decimal Commas",", or pass the decimal separator into the parser as a parameter per source.",[298,2009,2011],{"id":2010},"step-6-write-real-numbers-back-to-excel","Step 6 — Write real numbers back to Excel",[10,2013,2014],{},"Once the values are numbers, write them as numbers and let a number format provide the symbol, so the column sums, sorts and filters properly in Excel:",[303,2016,2018],{"className":349,"code":2017,"language":351,"meta":308,"style":308},"from openpyxl import load_workbook\n\nout = ledger[[\"invoice\", \"value\", \"currency\", \"amount\"]].rename(columns={\"amount\": \"original_text\"})\nout.to_excel(\"ledger_clean.xlsx\", index=False)\nwb = load_workbook(\"ledger_clean.xlsx\")\nws = wb.active\nfor (cell,) in ws.iter_rows(min_row=2, min_col=2, max_col=2):\n    cell.number_format = \"#,##0.00;[Red](#,##0.00)\"\nwb.save(\"ledger_clean.xlsx\")\nprint(pd.read_excel(\"ledger_clean.xlsx\")[\"value\"].sum())\n",[14,2019,2020,2033,2037,2081,2099,2113,2123,2162,2172,2181],{"__ignoreMap":308},[312,2021,2022,2025,2028,2030],{"class":271,"line":314},[312,2023,2024],{"class":358},"from",[312,2026,2027],{"class":362}," openpyxl ",[312,2029,359],{"class":358},[312,2031,2032],{"class":362}," load_workbook\n",[312,2034,2035],{"class":271,"line":366},[312,2036,384],{"emptyLinePlaceholder":383},[312,2038,2039,2042,2044,2047,2049,2051,2053,2055,2057,2059,2061,2064,2067,2069,2071,2073,2075,2078],{"class":271,"line":380},[312,2040,2041],{"class":362},"out ",[312,2043,393],{"class":358},[312,2045,2046],{"class":362}," ledger[[",[312,2048,1879],{"class":321},[312,2050,547],{"class":362},[312,2052,1554],{"class":321},[312,2054,547],{"class":362},[312,2056,1578],{"class":321},[312,2058,547],{"class":362},[312,2060,420],{"class":321},[312,2062,2063],{"class":362},"]].rename(",[312,2065,2066],{"class":481},"columns",[312,2068,393],{"class":358},[312,2070,1298],{"class":362},[312,2072,420],{"class":321},[312,2074,541],{"class":362},[312,2076,2077],{"class":321},"\"original_text\"",[312,2079,2080],{"class":362},"})\n",[312,2082,2083,2086,2089,2091,2093,2095,2097],{"class":271,"line":387},[312,2084,2085],{"class":362},"out.to_excel(",[312,2087,2088],{"class":321},"\"ledger_clean.xlsx\"",[312,2090,547],{"class":362},[312,2092,1889],{"class":481},[312,2094,393],{"class":358},[312,2096,1614],{"class":426},[312,2098,402],{"class":362},[312,2100,2101,2104,2106,2109,2111],{"class":271,"line":405},[312,2102,2103],{"class":362},"wb ",[312,2105,393],{"class":358},[312,2107,2108],{"class":362}," load_workbook(",[312,2110,2088],{"class":321},[312,2112,402],{"class":362},[312,2114,2115,2118,2120],{"class":271,"line":414},[312,2116,2117],{"class":362},"ws ",[312,2119,393],{"class":358},[312,2121,2122],{"class":362}," wb.active\n",[312,2124,2125,2127,2130,2132,2135,2138,2140,2142,2144,2147,2149,2151,2153,2156,2158,2160],{"class":271,"line":432},[312,2126,1270],{"class":358},[312,2128,2129],{"class":362}," (cell,) ",[312,2131,649],{"class":358},[312,2133,2134],{"class":362}," ws.iter_rows(",[312,2136,2137],{"class":481},"min_row",[312,2139,393],{"class":358},[312,2141,108],{"class":426},[312,2143,547],{"class":362},[312,2145,2146],{"class":481},"min_col",[312,2148,393],{"class":358},[312,2150,108],{"class":426},[312,2152,547],{"class":362},[312,2154,2155],{"class":481},"max_col",[312,2157,393],{"class":358},[312,2159,108],{"class":426},[312,2161,760],{"class":362},[312,2163,2164,2167,2169],{"class":271,"line":447},[312,2165,2166],{"class":362},"    cell.number_format ",[312,2168,393],{"class":358},[312,2170,2171],{"class":321}," \"#,##0.00;[Red](#,##0.00)\"\n",[312,2173,2174,2177,2179],{"class":271,"line":462},[312,2175,2176],{"class":362},"wb.save(",[312,2178,2088],{"class":321},[312,2180,402],{"class":362},[312,2182,2183,2185,2188,2190,2192,2194],{"class":271,"line":467},[312,2184,470],{"class":426},[312,2186,2187],{"class":362},"(pd.read_excel(",[312,2189,2088],{"class":321},[312,2191,1617],{"class":362},[312,2193,1554],{"class":321},[312,2195,1622],{"class":362},[10,2197,2198,2199,2203],{},"Keeping the original text in its own column makes the cleaning auditable: anyone who doubts a value can see what the export said. The format shows negatives in red brackets, the same convention the source used, while the cell holds a real negative number. ",[35,2200,2202],{"href":2201},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fformat-excel-cells-as-currency-with-python\u002F","Format Excel Cells as Currency with Python"," covers currency formats in depth.",[298,2205,2207],{"id":2206},"common-pitfalls","Common pitfalls",[2209,2210,2211,2225,2234,2243,2249],"ul",{},[2212,2213,2214,2221,2222,2224],"li",{},[2215,2216,2217,2220],"strong",{},[14,2218,2219],{},"to_numeric(errors=\"coerce\")"," first."," It turns every formatted amount into ",[14,2223,32],{}," without a word.",[2212,2226,2227,2230,2231,2233],{},[2215,2228,2229],{},"Losing the sign."," Brackets, trailing minus signs and ",[14,2232,337],{}," suffixes all mean negative.",[2212,2235,2236,2242],{},[2215,2237,2238,2239,2241],{},"Treating ",[14,2240,337],{}," as a currency code."," It is a credit marker; exclude it from the code check.",[2212,2244,2245,2248],{},[2215,2246,2247],{},"Summing mixed currencies."," Keep a currency column and group by it.",[2212,2250,2251,2260],{},[2215,2252,2253,2254,2256,2257,2259],{},"Guessing on ",[14,2255,1947],{}," versus ",[14,2258,1951],{},"."," Fix the convention per source when amounts have no decimals.",[298,2262,2264],{"id":2263},"performance-notes","Performance notes",[10,2266,2267,2270,2271,2274,2275,2278],{},[14,2268,2269],{},"Series.map"," with a Python function processes about a few hundred thousand values per second, which is fine for most ledgers. For millions of rows, handle the dominant format with vectorised string methods — ",[14,2272,2273],{},"str.replace"," for symbols and separators, ",[14,2276,2277],{},"str.startswith(\"(\")"," for brackets — and send only the rows that do not match to the full parser.",[298,2280,2282],{"id":2281},"conclusion","Conclusion",[10,2284,2285,2286,2288],{},"Currency text must be cleaned before it can be converted: take the symbol or code into its own column, turn brackets, leading or trailing minus signs and ",[14,2287,337],{}," suffixes into a negative sign, choose the decimal separator from whichever of comma and dot comes last, and validate that what remains is a plain number. Report every value that fails, fix the convention per source for ambiguous whole numbers, and write real numbers back with a currency number format.",[298,2290,2292],{"id":2291},"frequently-asked-questions","Frequently asked questions",[10,2294,2295,2298],{},[2215,2296,2297],{},"Why does astype(float) fail on my amount column?","\nBecause the column holds text such as \"$1,234.56\". float() cannot parse currency symbols or thousands separators, and it has no idea that \"(500)\" means minus 500. Clean the text first, then convert.",[10,2300,2301,2304],{},[2215,2302,2303],{},"How do accounting brackets work?","\nAccounting formats show negative amounts in parentheses instead of with a minus sign, so \"(1,200.00)\" means -1200. When the text is exported, the brackets become part of the value and must be turned back into a sign.",[10,2306,2307,2310],{},[2215,2308,2309],{},"How can I tell a European amount from an American one?","\nIn \"1.234,56\" the comma comes last, so it is the decimal separator; in \"1,234.56\" the dot comes last. A parser can use whichever separator appears last as the decimal point, as long as amounts always have decimals. For whole numbers such as \"1.234\", fix the convention per source.",[10,2312,2313,2316],{},[2215,2314,2315],{},"Should I store the currency symbol?","\nStore the currency as a separate code column (USD, EUR, GBP) and the amount as a plain number. Mixed currencies must not be summed together, and a code column makes that visible and easy to group by.",[298,2318,2320],{"id":2319},"related","Related",[2209,2322,2323,2329,2336,2343],{},[2212,2324,2325,2326,2328],{},"Up one level: ",[35,2327,38],{"href":37}," — the full cleaning workflow.",[2212,2330,2331,2335],{},[35,2332,2334],{"href":2333},"\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"," — the general text-to-number problem.",[2212,2337,2338,2342],{},[35,2339,2341],{"href":2340},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-percentage-strings-to-numbers-with-pandas\u002F","Convert Percentage Strings Like \"12.5%\" to Numbers with pandas"," — the same idea for rates.",[2212,2344,2345,2349],{},[35,2346,2348],{"href":2347},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fshow-negative-numbers-in-red-with-parentheses\u002F","Show Negative Numbers in Red with Parentheses in Excel"," — the display side of accounting negatives.",[2351,2352,2353],"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_b0D, html code.shiki .s_b0D{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#72F088;--shiki-dark-font-weight:bold}",{"title":308,"searchDepth":366,"depth":366,"links":2355},[2356,2357,2358,2359,2360,2361,2362,2363,2364,2365,2366,2367],{"id":300,"depth":366,"text":301},{"id":345,"depth":366,"text":346},{"id":512,"depth":366,"text":513},{"id":1523,"depth":366,"text":1524},{"id":1807,"depth":366,"text":1808},{"id":1940,"depth":366,"text":1941},{"id":2010,"depth":366,"text":2011},{"id":2206,"depth":366,"text":2207},{"id":2263,"depth":366,"text":2264},{"id":2281,"depth":366,"text":2282},{"id":2291,"depth":366,"text":2292},{"id":2319,"depth":366,"text":2320},"2026-09-25","Amount columns exported as text — \"$1,234.56\", \"(1,200.00)\", \"-€50\", \"1.234,56 EUR\", \"750.00-\" or \"100.00 CR\" — cannot be summed. Parse every common currency format into a signed number and a separate currency code, report the values that do not parse, and write real numbers back to Excel.","md",[2372,2374,2376,2378],{"q":2297,"a":2373},"Because the column holds text such as \"$1,234.56\". float() cannot parse currency symbols or thousands separators, and it has no idea that \"(500)\" means minus 500. Clean the text first, then convert.",{"q":2303,"a":2375},"Accounting formats show negative amounts in parentheses instead of with a minus sign, so \"(1,200.00)\" means -1200. When the text is exported, the brackets become part of the value and must be turned back into a sign.",{"q":2309,"a":2377},"In \"1.234,56\" the comma comes last, so it is the decimal separator; in \"1,234.56\" the dot comes last. A parser can use whichever separator appears last as the decimal point, as long as amounts always have decimals. For whole numbers such as \"1.234\", fix the convention per source.",{"q":2315,"a":2379},"Store the currency as a separate code column (USD, EUR, GBP) and the amount as a plain number. Mixed currencies must not be summed together, and a code column makes that visible and easy to group by.","convert currency strings to numbers with pandas",null,{"breadcrumb":2383},[2384,2387,2390],{"name":2385,"item":2386},"Home","\u002F",{"name":2388,"item":2389},"Advanced Data Transformation and Cleaning for Excel Automation","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":2391,"item":37},"Cleaning Excel Data With Pandas: A Production Workflow","\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-currency-strings-to-numbers-with-pandas",{"title":2394,"description":2395},"Convert Currency Text to Numbers in pandas (Symbols, Brackets, Codes)","Parse currency strings in pandas: dollar and euro symbols, thousands separators, accounting brackets for negatives, trailing minus signs, CR\u002FDR suffixes, European decimal commas and ISO currency codes, with a report of unparseable values.","convert-currency-strings-to-numbers-with-pandas","advanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-currency-strings-to-numbers-with-pandas\u002Findex",[2399,2400,2401],"data-cleaning","pandas","number-formats","how-to","wANBncQEadmRgHJfk-zgwKuUjhzAxj6-i6W6ztGKSOk",[2405,2409],{"title":2406,"path":2407,"stem":2408,"children":-1},"Coalesce Several Excel Columns into One with pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fcoalesce-several-excel-columns-into-one-with-pandas","advanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fcoalesce-several-excel-columns-into-one-with-pandas\u002Findex",{"title":2334,"path":2410,"stem":2411,"children":-1},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-excel-text-columns-to-numbers-with-pandas","advanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-excel-text-columns-to-numbers-with-pandas\u002Findex",[2413,2415],{"path":2414,"title":2391},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas",{"path":2416,"title":2388},"\u002Fadvanced-data-transformation-and-cleaning",[],[2419,2422,2425,2428,2429,2430],{"path":2420,"title":2421,"kind":2402},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Ffind-duplicate-rows-in-excel-with-python","Find Duplicate Rows in Excel with Python",{"path":2423,"title":2424,"kind":2402},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-duplicate-values-in-excel-with-openpyxl","Highlight Duplicate Values in Excel with openpyxl",{"path":2426,"title":2427,"kind":2402},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fclean-mixed-currency-amounts-into-amount-and-currency-columns","Clean Mixed-Currency Amounts into Amount and Currency Columns",{"path":2407,"title":2406,"kind":2402},{"path":2410,"title":2334,"kind":2402},{"path":2431,"title":2341,"kind":2402},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fconvert-percentage-strings-to-numbers-with-pandas",1790972080553]