[{"data":1,"prerenderedAt":1820},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-query-for-cleaning-excel-data":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-query-for-cleaning-excel-data":1786,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-query-for-cleaning-excel-data":1794,"children:\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-query-for-cleaning-excel-data":1799,"related:\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-query-for-cleaning-excel-data":1800},{"id":4,"title":5,"body":6,"dateModified":1752,"datePublished":1752,"description":1753,"extension":1754,"faq":1755,"keyword":1764,"kind":1765,"meta":1766,"navigation":250,"path":1775,"seo":1776,"slug":1779,"stem":1780,"topics":1781,"type":1784,"__hash__":1785},"docs\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-query-for-cleaning-excel-data\u002Findex.md","Python vs Power Query for Cleaning Excel Data",{"type":7,"value":8,"toc":1738},"minimark",[9,19,177,182,185,207,217,220,596,599,944,948,955,963,967,975,979,1186,1194,1198,1213,1341,1349,1353,1356,1482,1485,1489,1501,1505,1636,1639,1643,1666,1670,1673,1677,1683,1689,1695,1701,1705,1734],[10,11,12,13,18],"p",{},"Power Query and pandas are the two most capable ways to clean spreadsheet data, and they are more alike than they look. Both record a sequence of transformations — promote headers, trim text, change types, unpivot, merge — and replay it every time new data arrives. The difference is who writes and maintains the steps, and where they run: Power Query in Excel's editor, clicked together and refreshed inside a workbook; pandas in code, run by a script wherever Python runs. This guide, part of ",[14,15,17],"a",{"href":16},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002F","Choosing a Python Excel Library",", compares them honestly and shows the same job in both.",[20,21,29,30,29,34,29,38,29,46,29,56,29,67,29,74,29,81,29,87,29,95,29,99,29,104,29,108,29,113,29,117,29,123,29,128,29,135,29,140,29,144,29,148,29,153,29,156,29,161,29,164,29,169,29,172],"svg",{"viewBox":22,"role":23,"ariaLabelledBy":24,"xmlns":27,"style":28},"4 0 752 233","img",[25,26],"pqpy-overview-t","pqpy-overview-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[31,32,33],"title",{"id":25},"Power Query and pandas side by side",[35,36,37],"desc",{"id":26},"Power Query is built into Excel, is operated through a graphical editor that records M steps, and refreshes inside the workbook for users who do not program. pandas is a Python library, written as code, run by scripts and schedulers, and suited to logic Power Query does not offer.",[39,40],"rect",{"x":41,"y":42,"width":43,"height":44,"fill":45},"4","0","752","233","#ffffff",[39,47],{"x":48,"y":49,"width":50,"height":51,"rx":52,"fill":53,"stroke":54,"style":55},"20.0","16.0","351.0","201.0","14","#fdefd8","var(--gold,#b4740a)","stroke-width:2px",[57,58,60],"g",{"transform":59},"translate(182.5,29.0) scale(1.083)",[61,62],"path",{"d":63,"fill":64,"stroke":65,"style":66},"M3 5h18l-7 8v6l-4 2v-8z","none","var(--gold-ink,#7a4e06)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[68,69,73],"text",{"x":70,"y":71,"style":72},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Power Query",[75,76],"line",{"x1":77,"y1":78,"x2":79,"y2":78,"stroke":54,"style":80},"36.0","91.0","355.0","stroke-width:1px",[68,82,86],{"x":83,"y":84,"style":85},"60.0","111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","built into Excel",[57,88,90],{"transform":89},"translate(38.5,99.5) scale(0.625)",[61,91],{"d":92,"fill":64,"stroke":93,"style":94},"M5 12.5l4.5 4.5L19 7","var(--teal,#0f9488)","stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[68,96,98],{"x":83,"y":97,"style":85},"137.0","point-and-click, records M",[57,100,102],{"transform":101},"translate(38.5,125.5) scale(0.625)",[61,103],{"d":92,"fill":64,"stroke":93,"style":94},[68,105,107],{"x":83,"y":106,"style":85},"163.0","refreshes inside the workbook",[57,109,111],{"transform":110},"translate(38.5,151.5) scale(0.625)",[61,112],{"d":92,"fill":64,"stroke":93,"style":94},[68,114,116],{"x":83,"y":115,"style":85},"189.0","needs Excel to run",[57,118,120],{"transform":119},"translate(38.5,177.5) scale(0.625)",[61,121],{"d":122,"fill":64,"stroke":54,"style":94},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4",[39,124],{"x":125,"y":49,"width":50,"height":51,"rx":52,"fill":126,"stroke":127,"style":55},"389.0","#f0f4ff","var(--brand,#5b5cf0)",[57,129,131],{"transform":130},"translate(551.5,29.0) scale(1.083)",[61,132],{"d":133,"fill":64,"stroke":134,"style":66},"M12 3c-4 0-4 1.6-4 3v2h4.2v1H6c-2 0-3 1.4-3 4s1 4 3 4h2v-2.6c0-1.6 1.3-2.9 2.9-2.9h4.2c1.3 0 2.4-1.1 2.4-2.4V6c0-1.6-1.8-3-5.5-3z M10 5.5h.01 M12 21c4 0 4-1.6 4-3v-2h-4.2v-1H18c2 0 3-1.4 3-4s-1-4-3-4h-2v2.6c0 1.6-1.3 2.9-2.9 2.9H8.9c-1.3 0-2.4 1.1-2.4 2.4V18c0 1.6 1.8 3 5.5 3z M14 18.5h.01","var(--brand-strong,#4338ca)",[68,136,139],{"x":137,"y":71,"style":138},"564.5","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Python + pandas",[75,141],{"x1":142,"y1":78,"x2":143,"y2":78,"stroke":127,"style":80},"405.0","724.0",[68,145,147],{"x":146,"y":84,"style":85},"429.0","code, testable, versioned",[57,149,151],{"transform":150},"translate(407.5,99.5) scale(0.625)",[61,152],{"d":92,"fill":64,"stroke":93,"style":94},[68,154,155],{"x":146,"y":97,"style":85},"runs unattended anywhere",[57,157,159],{"transform":158},"translate(407.5,125.5) scale(0.625)",[61,160],{"d":92,"fill":64,"stroke":93,"style":94},[68,162,163],{"x":146,"y":106,"style":85},"fuzzy, regex, ML, APIs",[57,165,167],{"transform":166},"translate(407.5,151.5) scale(0.625)",[61,168],{"d":92,"fill":64,"stroke":93,"style":94},[68,170,171],{"x":146,"y":115,"style":85},"needs someone who codes",[57,173,175],{"transform":174},"translate(407.5,177.5) scale(0.625)",[61,176],{"d":122,"fill":64,"stroke":54,"style":94},[178,179,181],"h2",{"id":180},"the-same-job-in-both","The same job in both",[10,183,184],{},"A regional sales export arrives as a wide table: one row per rep, one column per month, with untidy names and a blank row or two. The goal is a tidy long table — one row per rep and month — with clean names and numeric amounts.",[10,186,187,188,192,193,196,197,192,200,192,203,206],{},"In Power Query, the steps are clicks in the editor: ",[189,190,191],"em",{},"Use First Row as Headers",", ",[189,194,195],{},"Trim"," on the rep column, ",[189,198,199],{},"Remove Blank Rows",[189,201,202],{},"Unpivot Other Columns",[189,204,205],{},"Change Type",". The editor writes the M code for you:",[208,209,214],"pre",{"className":210,"code":212,"language":68,"meta":213},[211],"language-text","let\n    Source = Excel.CurrentWorkbook(){[Name=\"RawSales\"]}[Content],\n    Trimmed = Table.TransformColumns(Source, {{\"Rep\", Text.Trim, type text}}),\n    NoBlanks = Table.SelectRows(Trimmed, each [Rep] \u003C> null and [Rep] \u003C> \"\"),\n    Long = Table.UnpivotOtherColumns(NoBlanks, {\"Region\", \"Rep\"}, \"Month\", \"Amount\"),\n    Typed = Table.TransformColumnTypes(Long, {{\"Amount\", type number}})\nin\n    Typed\n","",[215,216,212],"code",{"__ignoreMap":213},[10,218,219],{},"The same job in pandas, with sample data so it runs as written:",[208,221,225],{"className":222,"code":223,"language":224,"meta":213,"style":213},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\nraw = pd.DataFrame({\n    \"Region\": [\"North\", \"North\", None, \"South\"],\n    \"Rep\": [\"  Ana \", \"Ben\", None, \"Cy  \"],\n    \"Jan\": [1200, 950, None, 700],\n    \"Feb\": [1300, 990, None, 720],\n    \"Mar\": [1250, 1010, None, 760],\n})\nraw.to_excel(\"raw_sales.xlsx\", index=False)\n\ndf = pd.read_excel(\"raw_sales.xlsx\")\ndf[\"Rep\"] = df[\"Rep\"].str.strip()\ndf = df.dropna(subset=[\"Rep\"])\ntidy = df.melt(id_vars=[\"Region\", \"Rep\"], var_name=\"Month\", value_name=\"Amount\")\ntidy[\"Amount\"] = pd.to_numeric(tidy[\"Amount\"])\nprint(tidy.head(4))\nprint(len(tidy))   # 9\n","python",[215,226,227,245,252,264,295,322,349,376,403,409,432,437,452,474,497,545,564,578],{"__ignoreMap":213},[228,229,231,235,239,242],"span",{"class":75,"line":230},1,[228,232,234],{"class":233},"s-kum","import",[228,236,238],{"class":237},"skGVy"," pandas ",[228,240,241],{"class":233},"as",[228,243,244],{"class":237}," pd\n",[228,246,248],{"class":75,"line":247},2,[228,249,251],{"emptyLinePlaceholder":250},true,"\n",[228,253,255,258,261],{"class":75,"line":254},3,[228,256,257],{"class":237},"raw ",[228,259,260],{"class":233},"=",[228,262,263],{"class":237}," pd.DataFrame({\n",[228,265,267,271,274,277,279,281,283,287,289,292],{"class":75,"line":266},4,[228,268,270],{"class":269},"srMev","    \"Region\"",[228,272,273],{"class":237},": [",[228,275,276],{"class":269},"\"North\"",[228,278,192],{"class":237},[228,280,276],{"class":269},[228,282,192],{"class":237},[228,284,286],{"class":285},"sP0c6","None",[228,288,192],{"class":237},[228,290,291],{"class":269},"\"South\"",[228,293,294],{"class":237},"],\n",[228,296,298,301,303,306,308,311,313,315,317,320],{"class":75,"line":297},5,[228,299,300],{"class":269},"    \"Rep\"",[228,302,273],{"class":237},[228,304,305],{"class":269},"\"  Ana \"",[228,307,192],{"class":237},[228,309,310],{"class":269},"\"Ben\"",[228,312,192],{"class":237},[228,314,286],{"class":285},[228,316,192],{"class":237},[228,318,319],{"class":269},"\"Cy  \"",[228,321,294],{"class":237},[228,323,325,328,330,333,335,338,340,342,344,347],{"class":75,"line":324},6,[228,326,327],{"class":269},"    \"Jan\"",[228,329,273],{"class":237},[228,331,332],{"class":285},"1200",[228,334,192],{"class":237},[228,336,337],{"class":285},"950",[228,339,192],{"class":237},[228,341,286],{"class":285},[228,343,192],{"class":237},[228,345,346],{"class":285},"700",[228,348,294],{"class":237},[228,350,352,355,357,360,362,365,367,369,371,374],{"class":75,"line":351},7,[228,353,354],{"class":269},"    \"Feb\"",[228,356,273],{"class":237},[228,358,359],{"class":285},"1300",[228,361,192],{"class":237},[228,363,364],{"class":285},"990",[228,366,192],{"class":237},[228,368,286],{"class":285},[228,370,192],{"class":237},[228,372,373],{"class":285},"720",[228,375,294],{"class":237},[228,377,379,382,384,387,389,392,394,396,398,401],{"class":75,"line":378},8,[228,380,381],{"class":269},"    \"Mar\"",[228,383,273],{"class":237},[228,385,386],{"class":285},"1250",[228,388,192],{"class":237},[228,390,391],{"class":285},"1010",[228,393,192],{"class":237},[228,395,286],{"class":285},[228,397,192],{"class":237},[228,399,400],{"class":285},"760",[228,402,294],{"class":237},[228,404,406],{"class":75,"line":405},9,[228,407,408],{"class":237},"})\n",[228,410,412,415,418,420,424,426,429],{"class":75,"line":411},10,[228,413,414],{"class":237},"raw.to_excel(",[228,416,417],{"class":269},"\"raw_sales.xlsx\"",[228,419,192],{"class":237},[228,421,423],{"class":422},"sa561","index",[228,425,260],{"class":233},[228,427,428],{"class":285},"False",[228,430,431],{"class":237},")\n",[228,433,435],{"class":75,"line":434},11,[228,436,251],{"emptyLinePlaceholder":250},[228,438,440,443,445,448,450],{"class":75,"line":439},12,[228,441,442],{"class":237},"df ",[228,444,260],{"class":233},[228,446,447],{"class":237}," pd.read_excel(",[228,449,417],{"class":269},[228,451,431],{"class":237},[228,453,455,458,461,464,466,469,471],{"class":75,"line":454},13,[228,456,457],{"class":237},"df[",[228,459,460],{"class":269},"\"Rep\"",[228,462,463],{"class":237},"] ",[228,465,260],{"class":233},[228,467,468],{"class":237}," df[",[228,470,460],{"class":269},[228,472,473],{"class":237},"].str.strip()\n",[228,475,477,479,481,484,487,489,492,494],{"class":75,"line":476},14,[228,478,442],{"class":237},[228,480,260],{"class":233},[228,482,483],{"class":237}," df.dropna(",[228,485,486],{"class":422},"subset",[228,488,260],{"class":233},[228,490,491],{"class":237},"[",[228,493,460],{"class":269},[228,495,496],{"class":237},"])\n",[228,498,500,503,505,508,511,513,515,518,520,522,525,528,530,533,535,538,540,543],{"class":75,"line":499},15,[228,501,502],{"class":237},"tidy ",[228,504,260],{"class":233},[228,506,507],{"class":237}," df.melt(",[228,509,510],{"class":422},"id_vars",[228,512,260],{"class":233},[228,514,491],{"class":237},[228,516,517],{"class":269},"\"Region\"",[228,519,192],{"class":237},[228,521,460],{"class":269},[228,523,524],{"class":237},"], ",[228,526,527],{"class":422},"var_name",[228,529,260],{"class":233},[228,531,532],{"class":269},"\"Month\"",[228,534,192],{"class":237},[228,536,537],{"class":422},"value_name",[228,539,260],{"class":233},[228,541,542],{"class":269},"\"Amount\"",[228,544,431],{"class":237},[228,546,548,551,553,555,557,560,562],{"class":75,"line":547},16,[228,549,550],{"class":237},"tidy[",[228,552,542],{"class":269},[228,554,463],{"class":237},[228,556,260],{"class":233},[228,558,559],{"class":237}," pd.to_numeric(tidy[",[228,561,542],{"class":269},[228,563,496],{"class":237},[228,565,567,570,573,575],{"class":75,"line":566},17,[228,568,569],{"class":285},"print",[228,571,572],{"class":237},"(tidy.head(",[228,574,41],{"class":285},[228,576,577],{"class":237},"))\n",[228,579,581,583,586,589,592],{"class":75,"line":580},18,[228,582,569],{"class":285},[228,584,585],{"class":237},"(",[228,587,588],{"class":285},"len",[228,590,591],{"class":237},"(tidy))   ",[228,593,595],{"class":594},"s-wDw","# 9\n",[10,597,598],{},"Five steps each, one-to-one. For a transformation like this there is no winner on capability; the choice rests on the questions below.",[20,600,29,606,29,609,29,612,29,617,29,625,29,629,29,635,29,639,29,643,29,647,29,651,29,655,29,659,29,663,29,668,29,670,29,674,29,676,29,679,29,681,29,684,29,686,29,689,29,692,29,695,29,700,29,704,29,706,29,709,29,711,29,713,29,715,29,717,29,720,29,724,29,726,29,729,29,731,29,733,29,735,29,737,29,739,29,741,29,744,29,747,29,749,29,751,29,753,29,755,29,758,29,762,29,764,29,767,29,769,29,771,29,773,29,775,29,777,29,779,29,782,29,786,29,789,29,793,29,796,29,800,29,803,29,805,29,808,29,810,29,812,29,814,29,817,29,819,29,822,29,824,29,826,29,828,29,831,29,834,29,837,29,839,29,841,29,843,29,845,29,847,29,849,29,851,29,853,29,855,29,857,29,859,29,861,29,863,29,866,29,868,29,870,29,872,29,874,29,876,29,878,29,880,29,882,29,884,29,886,29,888,29,890,29,893,29,897,29,899,29,902,29,904,29,906,29,908,29,910,29,916,29,920,29,925,29,930,29,938],{"viewBox":601,"role":23,"ariaLabelledBy":602,"xmlns":27,"style":605},"33 0 694 248",[603,604],"pqpy-unpivot-t","pqpy-unpivot-d","width:100%;max-width:694px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[31,607,608],{"id":603},"The wide export and the tidy result both tools produce",[35,610,611],{"id":604},"The wide table has one column per month with reps as rows and untidy names. After trimming, removing the blank row and unpivoting, the long table has one row per rep and month with a numeric amount column.",[39,613],{"x":614,"y":42,"width":615,"height":616,"fill":45},"33","694","248",[39,618],{"x":619,"y":620,"width":621,"height":622,"rx":42,"fill":623,"stroke":624,"style":80},"49.2","40.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)",[39,626],{"x":627,"y":620,"width":628,"height":622,"rx":42,"fill":623,"stroke":624,"style":80},"81.2","57.5",[68,630,634],{"x":631,"y":632,"style":633},"109.9","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[39,636],{"x":637,"y":620,"width":638,"height":622,"rx":42,"fill":623,"stroke":624,"style":80},"138.7","54.4",[68,640,642],{"x":641,"y":632,"style":633},"165.9","B",[39,644],{"x":645,"y":620,"width":646,"height":622,"rx":42,"fill":623,"stroke":624,"style":80},"193.1","54.6",[68,648,650],{"x":649,"y":632,"style":633},"220.3","C",[39,652],{"x":653,"y":620,"width":654,"height":622,"rx":42,"fill":623,"stroke":624,"style":80},"247.6","53.7",[68,656,658],{"x":657,"y":632,"style":633},"274.5","D",[39,660],{"x":619,"y":661,"width":621,"height":662,"rx":42,"fill":623,"stroke":624,"style":80},"64.0","28.0",[68,664,667],{"x":665,"y":666,"style":633},"65.2","83.0","1",[39,669],{"x":627,"y":661,"width":628,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,671,673],{"x":631,"y":666,"style":672},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Rep",[39,675],{"x":637,"y":661,"width":638,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,677,678],{"x":641,"y":666,"style":672},"Jan",[39,680],{"x":645,"y":661,"width":646,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,682,683],{"x":649,"y":666,"style":672},"Feb",[39,685],{"x":653,"y":661,"width":654,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,687,688],{"x":657,"y":666,"style":672},"Mar",[39,690],{"x":619,"y":691,"width":621,"height":662,"rx":42,"fill":623,"stroke":624,"style":80},"92.0",[68,693,694],{"x":665,"y":84,"style":633},"2",[39,696],{"x":627,"y":691,"width":628,"height":662,"rx":42,"fill":697,"stroke":698,"style":699},"#fee8f2","var(--accent,#f43f8f)","stroke-width:1.5px",[68,701,703],{"x":631,"y":84,"style":702},"font-size:12px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","  Ana ",[39,705],{"x":637,"y":691,"width":638,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,707,332],{"x":641,"y":84,"style":708},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[39,710],{"x":645,"y":691,"width":646,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,712,359],{"x":649,"y":84,"style":708},[39,714],{"x":653,"y":691,"width":654,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,716,386],{"x":657,"y":84,"style":708},[39,718],{"x":619,"y":719,"width":621,"height":662,"rx":42,"fill":623,"stroke":624,"style":80},"120.0",[68,721,723],{"x":665,"y":722,"style":633},"139.0","3",[39,725],{"x":627,"y":719,"width":628,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,727,728],{"x":631,"y":722,"style":708},"Ben",[39,730],{"x":637,"y":719,"width":638,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,732,337],{"x":641,"y":722,"style":708},[39,734],{"x":645,"y":719,"width":646,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,736,364],{"x":649,"y":722,"style":708},[39,738],{"x":653,"y":719,"width":654,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,740,391],{"x":657,"y":722,"style":708},[39,742],{"x":619,"y":743,"width":621,"height":662,"rx":42,"fill":623,"stroke":624,"style":80},"148.0",[68,745,41],{"x":665,"y":746,"style":633},"167.0",[39,748],{"x":627,"y":743,"width":628,"height":662,"rx":42,"fill":697,"stroke":698,"style":699},[39,750],{"x":637,"y":743,"width":638,"height":662,"rx":42,"fill":697,"stroke":698,"style":699},[39,752],{"x":645,"y":743,"width":646,"height":662,"rx":42,"fill":697,"stroke":698,"style":699},[39,754],{"x":653,"y":743,"width":654,"height":662,"rx":42,"fill":697,"stroke":698,"style":699},[39,756],{"x":619,"y":757,"width":621,"height":662,"rx":42,"fill":623,"stroke":624,"style":80},"176.0",[68,759,761],{"x":665,"y":760,"style":633},"195.0","5",[39,763],{"x":627,"y":757,"width":628,"height":662,"rx":42,"fill":697,"stroke":698,"style":699},[68,765,766],{"x":631,"y":760,"style":702},"Cy  ",[39,768],{"x":637,"y":757,"width":638,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,770,346],{"x":641,"y":760,"style":708},[39,772],{"x":645,"y":757,"width":646,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,774,373],{"x":649,"y":760,"style":708},[39,776],{"x":653,"y":757,"width":654,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,778,400],{"x":657,"y":760,"style":708},[39,780],{"x":781,"y":620,"width":621,"height":622,"rx":42,"fill":623,"stroke":624,"style":80},"493.5",[39,783],{"x":784,"y":620,"width":785,"height":622,"rx":42,"fill":623,"stroke":624,"style":80},"525.5","48.6",[68,787,634],{"x":788,"y":632,"style":633},"549.8",[39,790],{"x":791,"y":620,"width":792,"height":622,"rx":42,"fill":623,"stroke":624,"style":80},"574.1","63.8",[68,794,642],{"x":795,"y":632,"style":633},"606.0",[39,797],{"x":798,"y":620,"width":799,"height":622,"rx":42,"fill":623,"stroke":624,"style":80},"637.9","73.0",[68,801,650],{"x":802,"y":632,"style":633},"674.4",[39,804],{"x":781,"y":661,"width":621,"height":662,"rx":42,"fill":623,"stroke":624,"style":80},[68,806,667],{"x":807,"y":666,"style":633},"509.5",[39,809],{"x":784,"y":661,"width":785,"height":662,"rx":42,"fill":623,"stroke":624,"style":699},[68,811,673],{"x":788,"y":666,"style":672},[39,813],{"x":791,"y":661,"width":792,"height":662,"rx":42,"fill":623,"stroke":624,"style":699},[68,815,816],{"x":795,"y":666,"style":672},"Month",[39,818],{"x":798,"y":661,"width":799,"height":662,"rx":42,"fill":623,"stroke":624,"style":699},[68,820,821],{"x":802,"y":666,"style":672},"Amount",[39,823],{"x":781,"y":691,"width":621,"height":662,"rx":42,"fill":623,"stroke":624,"style":80},[68,825,694],{"x":807,"y":84,"style":633},[39,827],{"x":784,"y":691,"width":785,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,829,830],{"x":788,"y":84,"style":708},"Ana",[39,832],{"x":791,"y":691,"width":792,"height":662,"rx":42,"fill":833,"stroke":93,"style":699},"#d9f4f1",[68,835,678],{"x":795,"y":84,"style":836},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[39,838],{"x":798,"y":691,"width":799,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,840,332],{"x":802,"y":84,"style":708},[39,842],{"x":781,"y":719,"width":621,"height":662,"rx":42,"fill":623,"stroke":624,"style":80},[68,844,723],{"x":807,"y":722,"style":633},[39,846],{"x":784,"y":719,"width":785,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,848,728],{"x":788,"y":722,"style":708},[39,850],{"x":791,"y":719,"width":792,"height":662,"rx":42,"fill":833,"stroke":93,"style":699},[68,852,678],{"x":795,"y":722,"style":836},[39,854],{"x":798,"y":719,"width":799,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,856,337],{"x":802,"y":722,"style":708},[39,858],{"x":781,"y":743,"width":621,"height":662,"rx":42,"fill":623,"stroke":624,"style":80},[68,860,41],{"x":807,"y":746,"style":633},[39,862],{"x":784,"y":743,"width":785,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,864,865],{"x":788,"y":746,"style":708},"Cy",[39,867],{"x":791,"y":743,"width":792,"height":662,"rx":42,"fill":833,"stroke":93,"style":699},[68,869,678],{"x":795,"y":746,"style":836},[39,871],{"x":798,"y":743,"width":799,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,873,346],{"x":802,"y":746,"style":708},[39,875],{"x":781,"y":757,"width":621,"height":662,"rx":42,"fill":623,"stroke":624,"style":80},[68,877,761],{"x":807,"y":760,"style":633},[39,879],{"x":784,"y":757,"width":785,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,881,830],{"x":788,"y":760,"style":708},[39,883],{"x":791,"y":757,"width":792,"height":662,"rx":42,"fill":833,"stroke":93,"style":699},[68,885,683],{"x":795,"y":760,"style":836},[39,887],{"x":798,"y":757,"width":799,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,889,359],{"x":802,"y":760,"style":708},[39,891],{"x":781,"y":892,"width":621,"height":662,"rx":42,"fill":623,"stroke":624,"style":80},"204.0",[68,894,896],{"x":807,"y":895,"style":633},"223.0","6",[39,898],{"x":784,"y":892,"width":785,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,900,901],{"x":788,"y":895,"style":708},"…",[39,903],{"x":791,"y":892,"width":792,"height":662,"rx":42,"fill":833,"stroke":93,"style":699},[68,905,901],{"x":795,"y":895,"style":836},[39,907],{"x":798,"y":892,"width":799,"height":662,"rx":42,"fill":45,"stroke":624,"style":80},[68,909,901],{"x":802,"y":895,"style":708},[68,911,915],{"x":912,"y":913,"style":914},"175.2","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","before",[68,917,919],{"x":918,"y":913,"style":914},"602.2","after",[75,921],{"x1":922,"y1":923,"x2":924,"y2":923,"stroke":127,"style":55},"311.3","122.0","479.5",[926,927],"polygon",{"points":928,"fill":929},"483.5,122.0 474.5,127.0 474.5,117.0","#5b5cf0",[39,931],{"x":932,"y":933,"width":934,"height":935,"rx":936,"fill":45,"stroke":624,"style":937},"336.5","89.0","121.8","22.0","11","stroke-width:1.2px",[68,939,943],{"x":940,"y":941,"style":942},"397.4","104.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","unpivot \u002F melt",[178,945,947],{"id":946},"who-maintains-it","Who maintains it",[10,949,950,951,954],{},"This is usually the deciding question. Power Query's steps are visible in the editor's ",[189,952,953],{},"Applied Steps"," list, and an analyst who has never programmed can inspect, reorder and fix them. When the people who own the report are Excel users, Power Query keeps them in control — nobody waits for a developer when a column is renamed.",[10,956,957,958,962],{},"pandas code is maintained by people who read Python. In exchange it gets code review, automated tests on sample files, and version control that shows exactly what changed and why — practices covered in ",[14,959,961],{"href":960},"\u002Fautomating-reporting-workflows\u002Ftesting-and-packaging-excel-automation-scripts\u002Ftest-excel-output-with-pytest\u002F","Test Excel Output with pytest",". For a transformation that feeds other systems or finance-critical numbers, that auditability matters more than a friendly editor.",[178,964,966],{"id":965},"where-it-runs","Where it runs",[10,968,969,970,974],{},"Power Query in desktop Excel runs when someone refreshes the workbook. That suits reports people open daily; it does not suit \"produce this file at 06:00 and email it\". Python runs wherever a scheduler can start it — a server, a container, GitHub Actions — with no Excel installed, as described in ",[14,971,973],{"href":972},"\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002F","Scheduling Python Excel Scripts with Cron",". Power Query can be scheduled in the Power BI service or through other Microsoft cloud tooling, but that is a different product setup from a workbook on a desk.",[178,976,978],{"id":977},"what-logic-each-handles-well","What logic each handles well",[20,980,29,985,29,988,29,991,29,994,29,997,29,1001,29,1006,29,1009,29,1013,29,1017,29,1021,29,1023,29,1029,29,1031,29,1036,29,1039,29,1043,29,1045,29,1050,29,1052,29,1057,29,1060,29,1064,29,1066,29,1071,29,1073,29,1078,29,1081,29,1085,29,1087,29,1092,29,1094,29,1100,29,1103,29,1107,29,1109,29,1114,29,1116,29,1121,29,1124,29,1128,29,1130,29,1137,29,1139,29,1144,29,1147,29,1151,29,1153,29,1158,29,1160,29,1165,29,1168,29,1172,29,1174,29,1179,29,1181],{"viewBox":981,"role":23,"ariaLabelledBy":982,"xmlns":27,"style":28},"4 0 752 344",[983,984],"pqpy-logic-t","pqpy-logic-d",[31,986,987],{"id":983},"Cleaning tasks and how well each tool handles them",[35,989,990],{"id":984},"Both tools handle trimming, type changes, unpivoting, merges and grouping well. Power Query folds filters to databases. Python is stronger at fuzzy matching, complex regular expressions, custom validation rules, calling APIs and statistical or machine learning steps.",[39,992],{"x":41,"y":42,"width":43,"height":993,"fill":45},"344",[39,995],{"x":48,"y":49,"width":996,"height":620,"rx":42,"fill":45,"stroke":624,"style":80},"233.7",[39,998],{"x":999,"y":49,"width":1000,"height":620,"rx":42,"fill":126,"stroke":127,"style":937},"253.7","243.1",[68,1002,73],{"x":1003,"y":1004,"style":1005},"375.3","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle",[39,1007],{"x":1008,"y":49,"width":1000,"height":620,"rx":42,"fill":126,"stroke":127,"style":937},"496.9",[68,1010,1012],{"x":1011,"y":1004,"style":1005},"618.4","pandas",[39,1014],{"x":48,"y":1015,"width":996,"height":1016,"rx":42,"fill":623,"stroke":624,"style":80},"56.0","34.0",[68,1018,1020],{"x":621,"y":71,"style":1019},"font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","Trim, split, change type",[39,1022],{"x":999,"y":1015,"width":1000,"height":1016,"rx":42,"fill":45,"stroke":624,"style":80},[57,1024,1026],{"transform":1025},"translate(366.3,64.0) scale(0.750)",[61,1027],{"d":92,"fill":64,"stroke":93,"style":1028},"stroke-width:2.40px;stroke-linecap:round;stroke-linejoin:round",[39,1030],{"x":1008,"y":1015,"width":1000,"height":1016,"rx":42,"fill":45,"stroke":624,"style":80},[57,1032,1034],{"transform":1033},"translate(609.4,64.0) scale(0.750)",[61,1035],{"d":92,"fill":64,"stroke":93,"style":1028},[39,1037],{"x":48,"y":1038,"width":996,"height":1016,"rx":42,"fill":623,"stroke":624,"style":80},"90.0",[68,1040,1042],{"x":621,"y":1041,"style":1019},"112.0","Unpivot, pivot, group",[39,1044],{"x":999,"y":1038,"width":1000,"height":1016,"rx":42,"fill":45,"stroke":624,"style":80},[57,1046,1048],{"transform":1047},"translate(366.3,98.0) scale(0.750)",[61,1049],{"d":92,"fill":64,"stroke":93,"style":1028},[39,1051],{"x":1008,"y":1038,"width":1000,"height":1016,"rx":42,"fill":45,"stroke":624,"style":80},[57,1053,1055],{"transform":1054},"translate(609.4,98.0) scale(0.750)",[61,1056],{"d":92,"fill":64,"stroke":93,"style":1028},[39,1058],{"x":48,"y":1059,"width":996,"height":1016,"rx":42,"fill":623,"stroke":624,"style":80},"124.0",[68,1061,1063],{"x":621,"y":1062,"style":1019},"146.0","Merge tables on keys",[39,1065],{"x":999,"y":1059,"width":1000,"height":1016,"rx":42,"fill":45,"stroke":624,"style":80},[57,1067,1069],{"transform":1068},"translate(366.3,132.0) scale(0.750)",[61,1070],{"d":92,"fill":64,"stroke":93,"style":1028},[39,1072],{"x":1008,"y":1059,"width":1000,"height":1016,"rx":42,"fill":45,"stroke":624,"style":80},[57,1074,1076],{"transform":1075},"translate(609.4,132.0) scale(0.750)",[61,1077],{"d":92,"fill":64,"stroke":93,"style":1028},[39,1079],{"x":48,"y":1080,"width":996,"height":1016,"rx":42,"fill":623,"stroke":624,"style":80},"158.0",[68,1082,1084],{"x":621,"y":1083,"style":1019},"180.0","Push filters to a database",[39,1086],{"x":999,"y":1080,"width":1000,"height":1016,"rx":42,"fill":45,"stroke":624,"style":80},[57,1088,1090],{"transform":1089},"translate(366.3,166.0) scale(0.750)",[61,1091],{"d":92,"fill":64,"stroke":93,"style":1028},[39,1093],{"x":1008,"y":1080,"width":1000,"height":1016,"rx":42,"fill":45,"stroke":624,"style":80},[57,1095,1097],{"transform":1096},"translate(609.9,166.5) scale(0.708)",[61,1098],{"d":122,"fill":64,"stroke":54,"style":1099},"stroke-width:2.54px;stroke-linecap:round;stroke-linejoin:round",[39,1101],{"x":48,"y":1102,"width":996,"height":1016,"rx":42,"fill":623,"stroke":624,"style":80},"192.0",[68,1104,1106],{"x":621,"y":1105,"style":1019},"214.0","Fuzzy matching names",[39,1108],{"x":999,"y":1102,"width":1000,"height":1016,"rx":42,"fill":45,"stroke":624,"style":80},[57,1110,1112],{"transform":1111},"translate(366.8,200.5) scale(0.708)",[61,1113],{"d":122,"fill":64,"stroke":54,"style":1099},[39,1115],{"x":1008,"y":1102,"width":1000,"height":1016,"rx":42,"fill":45,"stroke":624,"style":80},[57,1117,1119],{"transform":1118},"translate(609.4,200.0) scale(0.750)",[61,1120],{"d":92,"fill":64,"stroke":93,"style":1028},[39,1122],{"x":48,"y":1123,"width":996,"height":1016,"rx":42,"fill":623,"stroke":624,"style":80},"226.0",[68,1125,1127],{"x":621,"y":1126,"style":1019},"248.0","Complex regular expressions",[39,1129],{"x":999,"y":1123,"width":1000,"height":1016,"rx":42,"fill":45,"stroke":624,"style":80},[57,1131,1133],{"transform":1132},"translate(367.3,235.0) scale(0.667)",[61,1134],{"d":1135,"fill":64,"stroke":698,"style":1136},"M6 6l12 12 M18 6L6 18","stroke-width:2.70px;stroke-linecap:round;stroke-linejoin:round",[39,1138],{"x":1008,"y":1123,"width":1000,"height":1016,"rx":42,"fill":45,"stroke":624,"style":80},[57,1140,1142],{"transform":1141},"translate(609.4,234.0) scale(0.750)",[61,1143],{"d":92,"fill":64,"stroke":93,"style":1028},[39,1145],{"x":48,"y":1146,"width":996,"height":1016,"rx":42,"fill":623,"stroke":624,"style":80},"260.0",[68,1148,1150],{"x":621,"y":1149,"style":1019},"282.0","Custom validation, error reports",[39,1152],{"x":999,"y":1146,"width":1000,"height":1016,"rx":42,"fill":45,"stroke":624,"style":80},[57,1154,1156],{"transform":1155},"translate(366.8,268.5) scale(0.708)",[61,1157],{"d":122,"fill":64,"stroke":54,"style":1099},[39,1159],{"x":1008,"y":1146,"width":1000,"height":1016,"rx":42,"fill":45,"stroke":624,"style":80},[57,1161,1163],{"transform":1162},"translate(609.4,268.0) scale(0.750)",[61,1164],{"d":92,"fill":64,"stroke":93,"style":1028},[39,1166],{"x":48,"y":1167,"width":996,"height":1016,"rx":42,"fill":623,"stroke":624,"style":80},"294.0",[68,1169,1171],{"x":621,"y":1170,"style":1019},"316.0","Call APIs, send email, write files",[39,1173],{"x":999,"y":1167,"width":1000,"height":1016,"rx":42,"fill":45,"stroke":624,"style":80},[57,1175,1177],{"transform":1176},"translate(367.3,303.0) scale(0.667)",[61,1178],{"d":1135,"fill":64,"stroke":698,"style":1136},[39,1180],{"x":1008,"y":1167,"width":1000,"height":1016,"rx":42,"fill":45,"stroke":624,"style":80},[57,1182,1184],{"transform":1183},"translate(609.4,302.0) scale(0.750)",[61,1185],{"d":92,"fill":64,"stroke":93,"style":1028},[10,1187,1188,1189,1193],{},"Power Query's fuzzy merge exists but offers little control over the scoring; Python libraries like RapidFuzz let you choose the scorer, the threshold and the blocking strategy — see ",[14,1190,1192],{"href":1191},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Ffuzzy-match-names-between-two-excel-files-with-python\u002F","Fuzzy Match Names Between Two Excel Files with Python",". M has no regular expressions in its standard library, so pattern-based extraction is awkward. And anything that leaves the workbook — writing several files, emailing a summary, posting to an API — is outside Power Query's job.",[178,1195,1197],{"id":1196},"handling-bad-rows","Handling bad rows",[10,1199,1200,1201,1204,1205,1208,1209,1212],{},"Real exports contain values that do not fit: text in a number column, impossible dates, codes nobody recognises. The two tools treat them differently. Power Query marks a failed conversion as an ",[189,1202,1203],{},"Error"," in that cell; the editor can show only the erroring rows (",[189,1206,1207],{},"Keep Errors",") or replace them with a value (",[189,1210,1211],{},"Replace Errors","), and an unhandled error can stop the load. pandas gives you a choice per conversion — raise immediately, or coerce to missing and report — and makes it easy to collect every problem into one table:",[208,1214,1216],{"className":222,"code":1215,"language":224,"meta":213,"style":213},"messy = pd.DataFrame({\"Rep\": [\"Ana\", \"Ben\", \"Cy\"], \"Amount\": [\"1200\", \"n\u002Fa\", \"1,050\"]})\namounts = pd.to_numeric(messy[\"Amount\"].str.replace(\",\", \"\", regex=False), errors=\"coerce\")\nissues = messy[amounts.isna()].assign(problem=\"Amount is not a number\")\nprint(issues)          # Ben's \"n\u002Fa\" is reported, \"1,050\" is cleaned to 1050\n",[215,1217,1218,1266,1311,1331],{"__ignoreMap":213},[228,1219,1220,1223,1225,1228,1230,1232,1235,1237,1239,1241,1244,1246,1248,1250,1253,1255,1258,1260,1263],{"class":75,"line":230},[228,1221,1222],{"class":237},"messy ",[228,1224,260],{"class":233},[228,1226,1227],{"class":237}," pd.DataFrame({",[228,1229,460],{"class":269},[228,1231,273],{"class":237},[228,1233,1234],{"class":269},"\"Ana\"",[228,1236,192],{"class":237},[228,1238,310],{"class":269},[228,1240,192],{"class":237},[228,1242,1243],{"class":269},"\"Cy\"",[228,1245,524],{"class":237},[228,1247,542],{"class":269},[228,1249,273],{"class":237},[228,1251,1252],{"class":269},"\"1200\"",[228,1254,192],{"class":237},[228,1256,1257],{"class":269},"\"n\u002Fa\"",[228,1259,192],{"class":237},[228,1261,1262],{"class":269},"\"1,050\"",[228,1264,1265],{"class":237},"]})\n",[228,1267,1268,1271,1273,1276,1278,1281,1284,1286,1289,1291,1294,1296,1298,1301,1304,1306,1309],{"class":75,"line":247},[228,1269,1270],{"class":237},"amounts ",[228,1272,260],{"class":233},[228,1274,1275],{"class":237}," pd.to_numeric(messy[",[228,1277,542],{"class":269},[228,1279,1280],{"class":237},"].str.replace(",[228,1282,1283],{"class":269},"\",\"",[228,1285,192],{"class":237},[228,1287,1288],{"class":269},"\"\"",[228,1290,192],{"class":237},[228,1292,1293],{"class":422},"regex",[228,1295,260],{"class":233},[228,1297,428],{"class":285},[228,1299,1300],{"class":237},"), ",[228,1302,1303],{"class":422},"errors",[228,1305,260],{"class":233},[228,1307,1308],{"class":269},"\"coerce\"",[228,1310,431],{"class":237},[228,1312,1313,1316,1318,1321,1324,1326,1329],{"class":75,"line":254},[228,1314,1315],{"class":237},"issues ",[228,1317,260],{"class":233},[228,1319,1320],{"class":237}," messy[amounts.isna()].assign(",[228,1322,1323],{"class":422},"problem",[228,1325,260],{"class":233},[228,1327,1328],{"class":269},"\"Amount is not a number\"",[228,1330,431],{"class":237},[228,1332,1333,1335,1338],{"class":75,"line":266},[228,1334,569],{"class":285},[228,1336,1337],{"class":237},"(issues)          ",[228,1339,1340],{"class":594},"# Ben's \"n\u002Fa\" is reported, \"1,050\" is cleaned to 1050\n",[10,1342,1343,1344,1348],{},"An issues table like this can be written to its own sheet next to the cleaned data, so the people who produce the export can fix the source. Building that kind of report is the subject of ",[14,1345,1347],{"href":1346},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002F","Validating Excel Data with Python",".",[178,1350,1352],{"id":1351},"translating-steps-between-the-two","Translating steps between the two",[10,1354,1355],{},"When a workbook's queries outgrow Power Query, the migration is mostly mechanical, because most editor steps have a direct pandas counterpart:",[1357,1358,1359,1372],"table",{},[1360,1361,1362],"thead",{},[1363,1364,1365,1369],"tr",{},[1366,1367,1368],"th",{},"Power Query step",[1366,1370,1371],{},"pandas equivalent",[1373,1374,1375,1385,1399,1414,1423,1432,1442,1452,1462,1472],"tbody",{},[1363,1376,1377,1380],{},[1378,1379,191],"td",{},[1378,1381,1382],{},[215,1383,1384],{},"pd.read_excel(..., header=0)",[1363,1386,1387,1390],{},[1378,1388,1389],{},"Trim \u002F Clean",[1378,1391,1392,1395,1396],{},[215,1393,1394],{},".str.strip()",", remove control characters with ",[215,1397,1398],{},".str.replace()",[1363,1400,1401,1403],{},[1378,1402,205],{},[1378,1404,1405,192,1408,192,1411],{},[215,1406,1407],{},"pd.to_numeric",[215,1409,1410],{},"pd.to_datetime",[215,1412,1413],{},".astype()",[1363,1415,1416,1418],{},[1378,1417,199],{},[1378,1419,1420],{},[215,1421,1422],{},".dropna(how=\"all\")",[1363,1424,1425,1427],{},[1378,1426,202],{},[1378,1428,1429],{},[215,1430,1431],{},".melt(id_vars=[...])",[1363,1433,1434,1437],{},[1378,1435,1436],{},"Pivot Column",[1378,1438,1439],{},[215,1440,1441],{},".pivot_table(...)",[1363,1443,1444,1447],{},[1378,1445,1446],{},"Merge Queries",[1378,1448,1449],{},[215,1450,1451],{},".merge(..., how=\"left\")",[1363,1453,1454,1457],{},[1378,1455,1456],{},"Append Queries",[1378,1458,1459],{},[215,1460,1461],{},"pd.concat([...])",[1363,1463,1464,1467],{},[1378,1465,1466],{},"Group By",[1378,1468,1469],{},[215,1470,1471],{},".groupby(...).agg(...)",[1363,1473,1474,1477],{},[1378,1475,1476],{},"Replace Values",[1378,1478,1479],{},[215,1480,1481],{},".replace({...})",[10,1483,1484],{},"Translate one query at a time and compare its output with the Power Query result on the same input before retiring the query. Differences almost always come from type inference — Power Query and pandas guess types differently — so set types explicitly on both sides while comparing.",[178,1486,1488],{"id":1487},"scale-and-speed","Scale and speed",[10,1490,1491,1492,1495,1496,1500],{},"For database sources, Power Query's ",[189,1493,1494],{},"query folding"," translates steps into SQL and lets the server do the filtering, which is hard to beat. For file sources of typical size — thousands to a few hundred thousand rows — both are fast enough that it does not matter. Beyond that, pandas (or Polars) running on a server is more predictable than a refresh inside a desktop workbook, and it frees the person who would otherwise wait for Excel. For very large Excel inputs, the reading techniques in ",[14,1497,1499],{"href":1498},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002F","Working with Large Excel Files in Python"," apply.",[178,1502,1504],{"id":1503},"using-both-together","Using both together",[20,1506,29,1511,29,1514,29,1517,29,1520,29,1526,29,1533,29,1538,29,1542,29,1546,29,1549,29,1552,29,1559,29,1564,29,1569,29,1574,29,1577,29,1582,29,1584,29,1587,29,1591,29,1594,29,1597,29,1603,29,1606,29,1609,29,1611,29,1614,29,1618,29,1621,29,1624,29,1630,29,1633],{"viewBox":1507,"role":23,"ariaLabelledBy":1508,"xmlns":27,"style":28},"4 0 752 272",[1509,1510],"pqpy-choose-t","pqpy-choose-d",[31,1512,1513],{"id":1509},"Choosing Power Query, pandas or both for a cleaning job",[35,1515,1516],{"id":1510},"If Excel users maintain the steps inside a workbook they refresh, choose Power Query. If the cleaning runs unattended, feeds other systems or needs logic Power Query lacks, choose pandas. If a scheduled job should prepare data that users then shape in Excel, let Python write a clean file and Power Query import it.",[39,1518],{"x":41,"y":42,"width":43,"height":1519,"fill":45},"272",[39,1521],{"x":1522,"y":49,"width":1523,"height":1524,"rx":1525,"fill":126,"stroke":127,"style":55},"218.0","324.0","75.5","12",[57,1527,1529],{"transform":1528},"translate(368.0,29.0) scale(1.000)",[61,1530],{"d":1531,"fill":64,"stroke":134,"style":1532},"M9 11a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z M2.5 20c0-3.6 2.9-6 6.5-6s6.5 2.4 6.5 6 M16 4.5a3.3 3.3 0 0 1 0 6.3 M18 14.3c2.2.7 3.5 2.8 3.5 5.7","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[68,1534,1537],{"x":1535,"y":1524,"style":1536},"380.0","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Who runs and maintains the cleaning?",[75,1539],{"x1":1535,"y1":1540,"x2":1535,"y2":1541,"stroke":127,"style":55},"91.5","111.5",[75,1543],{"x1":1544,"y1":1541,"x2":1545,"y2":1541,"stroke":127,"style":55},"134.7","625.3",[75,1547],{"x1":1544,"y1":1541,"x2":1544,"y2":1548,"stroke":127,"style":55},"157.5",[926,1550],{"points":1551,"fill":929},"134.7,161.5 129.7,152.5 139.7,152.5",[39,1553],{"x":1554,"y":1555,"width":1556,"height":1557,"rx":1558,"fill":45,"stroke":624,"style":937},"49.0","121.5","171.3","21.0","10",[68,1560,1563],{"x":1544,"y":1561,"style":1562},"136.0","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","Excel users in a workbook",[39,1565],{"x":48,"y":1566,"width":1567,"height":1568,"rx":1525,"fill":53,"stroke":54,"style":55},"163.5","229.3","93.0",[57,1570,1572],{"transform":1571},"translate(122.7,176.5) scale(1.000)",[61,1573],{"d":63,"fill":64,"stroke":65,"style":1532},[68,1575,73],{"x":1544,"y":895,"style":1576},"font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle",[68,1578,1581],{"x":1544,"y":1579,"style":1580},"242.5","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","refresh on open",[75,1583],{"x1":1535,"y1":1541,"x2":1535,"y2":1548,"stroke":127,"style":55},[926,1585],{"points":1586,"fill":929},"380.0,161.5 375.0,152.5 385.0,152.5",[39,1588],{"x":1589,"y":1555,"width":1590,"height":1557,"rx":1558,"fill":45,"stroke":624,"style":937},"324.6","110.8",[68,1592,1593],{"x":1535,"y":1561,"style":1562},"a scheduled job",[39,1595],{"x":1596,"y":1566,"width":1567,"height":1568,"rx":1525,"fill":833,"stroke":93,"style":55},"265.3",[57,1598,1600],{"transform":1599},"translate(368.0,176.5) scale(1.000)",[61,1601],{"d":133,"fill":64,"stroke":1602,"style":1532},"var(--teal-ink,#0b6157)",[68,1604,1012],{"x":1535,"y":895,"style":1605},"font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[68,1607,1608],{"x":1535,"y":1579,"style":1580},"tested and versioned",[75,1610],{"x1":1545,"y1":1541,"x2":1545,"y2":1548,"stroke":127,"style":55},[926,1612],{"points":1613,"fill":929},"625.3,161.5 620.3,152.5 630.3,152.5",[39,1615],{"x":1616,"y":1555,"width":1617,"height":1557,"rx":1558,"fill":45,"stroke":624,"style":937},"602.9","45.0",[68,1619,1620],{"x":1545,"y":1561,"style":1562},"both",[39,1622],{"x":1623,"y":1566,"width":1567,"height":1568,"rx":1525,"fill":126,"stroke":127,"style":55},"510.7",[57,1625,1627],{"transform":1626},"translate(613.3,176.5) scale(1.000)",[61,1628],{"d":1629,"fill":64,"stroke":134,"style":1532},"M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1 M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1",[68,1631,1632],{"x":1545,"y":895,"style":1536},"Python → file → Power Query",[68,1634,1635],{"x":1545,"y":1579,"style":1580},"clean feed, local shaping",[10,1637,1638],{},"The hybrid is often best: a Python job does the hard, unattended part — collecting files, fuzzy-matching customers, validating — and writes one clean file to a shared folder; the analysts' workbooks import it with Power Query and do their own light shaping. Each tool does what it is good at, and the boundary between them is a plain file anyone can open.",[178,1640,1642],{"id":1641},"common-pitfalls","Common pitfalls",[1644,1645,1646,1654,1660],"ul",{},[1647,1648,1649,1653],"li",{},[1650,1651,1652],"strong",{},"Rebuilding Power Query in pandas for no reason."," If a workbook's queries work and the people who own them can maintain them, migrating gains little.",[1647,1655,1656,1659],{},[1650,1657,1658],{},"Growing Power Query beyond its home."," Dozens of chained queries with custom M functions, maintained by one person, become harder to own than equivalent Python. Move logic to Python when the M becomes the bottleneck.",[1647,1661,1662,1665],{},[1650,1663,1664],{},"Different results from the two."," Type inference differs: Power Query may guess a date where pandas keeps text. When migrating, compare outputs row by row on real files before switching.",[178,1667,1669],{"id":1668},"conclusion","Conclusion",[10,1671,1672],{},"For the core of data cleaning — trimming, typing, unpivoting, merging — Power Query and pandas are equally capable. Choose Power Query when Excel users own the steps and refresh them in the workbook; choose pandas when the cleaning runs unattended, needs fuzzy matching, regular expressions or custom validation, or must be tested and versioned. When a job needs both, let Python produce a clean file and Power Query consume it.",[178,1674,1676],{"id":1675},"frequently-asked-questions","Frequently asked questions",[10,1678,1679,1682],{},[1650,1680,1681],{},"Is Power Query or pandas better for cleaning Excel data?","\nPower Query is better when the cleaning lives inside a workbook that non-programmers refresh and maintain. pandas is better when the cleaning runs unattended, feeds other systems, needs logic Power Query lacks — fuzzy matching, complex regular expressions, custom validation — or must be tested and version-controlled.",[10,1684,1685,1688],{},[1650,1686,1687],{},"Can Power Query run on a schedule without Excel?","\nNot by itself in desktop Excel; a query refreshes when someone opens the workbook or clicks Refresh. Scheduled refresh is available in Power BI and through other Microsoft services. A Python script runs on any scheduler without Excel.",[10,1690,1691,1694],{},[1650,1692,1693],{},"Is Power Query faster than pandas?","\nFor database sources Power Query can push filters down to the server, which is very efficient. For files, both are fast at typical sizes; pandas is usually faster on complex in-memory work and scales further with tools like Polars.",[10,1696,1697,1700],{},[1650,1698,1699],{},"Can I use both?","\nYes, and it is common. Python can produce a clean file that Power Query picks up, or Power Query can import data that Python in Excel then analyses with xl().",[178,1702,1704],{"id":1703},"related","Related",[1644,1706,1707,1713,1720,1727],{},[1647,1708,1709,1710,1712],{},"Up one level: ",[14,1711,17],{"href":16}," — picking tools for Excel work.",[1647,1714,1715,1719],{},[14,1716,1718],{"href":1717},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002F","Cleaning Excel Data With Pandas"," — the pandas side in depth.",[1647,1721,1722,1726],{},[14,1723,1725],{"href":1724},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-python-in-excel\u002Fget-external-data-into-python-in-excel\u002F","Get External Data into Python in Excel with Power Query"," — the two working together inside Excel.",[1647,1728,1729,1733],{},[14,1730,1732],{"href":1731},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-vba-for-excel-automation\u002F","Python vs VBA for Excel Automation"," — the other in-Excel alternative.",[1735,1736,1737],"style",{},"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 .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html .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);}",{"title":213,"searchDepth":247,"depth":247,"links":1739},[1740,1741,1742,1743,1744,1745,1746,1747,1748,1749,1750,1751],{"id":180,"depth":247,"text":181},{"id":946,"depth":247,"text":947},{"id":965,"depth":247,"text":966},{"id":977,"depth":247,"text":978},{"id":1196,"depth":247,"text":1197},{"id":1351,"depth":247,"text":1352},{"id":1487,"depth":247,"text":1488},{"id":1503,"depth":247,"text":1504},{"id":1641,"depth":247,"text":1642},{"id":1668,"depth":247,"text":1669},{"id":1675,"depth":247,"text":1676},{"id":1703,"depth":247,"text":1704},"2026-09-23","Power Query and pandas both clean and reshape Excel data with repeatable steps. Compare them on who maintains the work, where it runs, what logic each handles well and how they scale — with the same unpivot-and-clean job in M and in Python.","md",[1756,1758,1760,1762],{"q":1681,"a":1757},"Power Query is better when the cleaning lives inside a workbook that non-programmers refresh and maintain. pandas is better when the cleaning runs unattended, feeds other systems, needs logic Power Query lacks — fuzzy matching, complex regular expressions, custom validation — or must be tested and version-controlled.",{"q":1687,"a":1759},"Not by itself in desktop Excel; a query refreshes when someone opens the workbook or clicks Refresh. Scheduled refresh is available in Power BI and through other Microsoft services. A Python script runs on any scheduler without Excel.",{"q":1693,"a":1761},"For database sources Power Query can push filters down to the server, which is very efficient. For files, both are fast at typical sizes; pandas is usually faster on complex in-memory work and scales further with tools like Polars.",{"q":1699,"a":1763},"Yes, and it is common. Python can produce a clean file that Power Query picks up, or Power Query can import data that Python in Excel then analyses with xl().","python vs power query for cleaning excel data",null,{"breadcrumb":1767},[1768,1771,1774],{"name":1769,"item":1770},"Home","\u002F",{"name":1772,"item":1773},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":17,"item":16},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-query-for-cleaning-excel-data",{"title":1777,"description":1778},"Python (pandas) vs Power Query for Cleaning Excel Data","When to clean Excel data with Power Query and when with Python and pandas — maintainability, refresh versus scheduled jobs, fuzzy matching and regex, scale — with the same transformation written in both.","python-vs-power-query-for-cleaning-excel-data","getting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-query-for-cleaning-excel-data\u002Findex",[1782,1783],"choosing-libraries","data-cleaning","how-to","RRS-BfoBTBR5dEKVZgyysDV4mHRWABcFqiAKxl9fmPU",[1787,1791],{"title":1788,"path":1789,"stem":1790,"children":-1},"Python vs Power Automate for Report Workflows","\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-automate-for-report-workflows","getting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-automate-for-report-workflows\u002Findex",{"title":1732,"path":1792,"stem":1793,"children":-1},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-vba-for-excel-automation","getting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-vba-for-excel-automation\u002Findex",[1795,1797],{"path":1796,"title":17},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library",{"path":1798,"title":1772},"\u002Fgetting-started-with-python-excel-automation",[],[1801,1805,1808,1811,1814,1817],{"path":1802,"title":1803,"kind":1804},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fxlwings-vs-pywin32-for-excel-automation","xlwings vs pywin32 for Excel Automation","compare",{"path":1806,"title":1807,"kind":1804},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fopenpyxl-vs-xlsxwriter-vs-pandas-excelwriter","openpyxl vs xlsxwriter vs pandas.ExcelWriter",{"path":1809,"title":1810,"kind":1784},"\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Fchoose-a-scheduler-for-python-excel-reports","Choose a Scheduler for Python Excel Reports",{"path":1812,"title":1813,"kind":1804},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fgoogle-sheets-vs-excel-files-for-python-automation","Google Sheets vs Excel Files for Python Automation",{"path":1815,"title":1816,"kind":1784},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Flightweight-python-excel-libraries-compared","Lightweight Python Excel Libraries Compared",{"path":1818,"title":1819,"kind":1804},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fopenpyxl-vs-pandas-for-excel-automation","openpyxl vs pandas for Excel Automation",1790972058916]