[{"data":1,"prerenderedAt":2378},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fgroupby-and-pivotby-function-equivalents-in-pandas":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fgroupby-and-pivotby-function-equivalents-in-pandas":2345,"ancestors:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fgroupby-and-pivotby-function-equivalents-in-pandas":2354,"children:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fgroupby-and-pivotby-function-equivalents-in-pandas":2359,"related:\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fgroupby-and-pivotby-function-equivalents-in-pandas":2360},{"id":4,"title":5,"body":6,"dateModified":2312,"datePublished":2312,"description":2313,"extension":2314,"faq":2315,"keyword":2324,"kind":2325,"meta":2326,"navigation":113,"path":2334,"seo":2335,"slug":2338,"stem":2339,"topics":2340,"type":2343,"__hash__":2344},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fgroupby-and-pivotby-function-equivalents-in-pandas\u002Findex.md","Excel GROUPBY and PIVOTBY Function Equivalents in pandas",{"type":7,"value":8,"toc":2297},"minimark",[9,28,33,61,71,75,85,247,258,600,604,619,719,738,742,755,995,1006,1010,1016,1103,1117,1309,1313,1335,1456,1482,1486,1503,1677,1683,1788,1792,1798,2084,2099,2103,2122,2126,2185,2189,2225,2229,2235,2241,2254,2260,2264,2293],[10,11,12,13,17,18,21,22,27],"p",{},"Excel 365 added two functions that replace a whole pivot table with a formula: ",[14,15,16],"code",{},"GROUPBY(row_fields, values, function)"," returns a summary table that spills down the sheet, and ",[14,19,20],{},"PIVOTBY(row_fields, col_fields, values, function)"," returns a crosstab. Analysts love them because the summary updates as the data changes and needs no refresh. When such a workbook becomes a scheduled Python report, every one of those formulas has a pandas counterpart — and pandas is often the only way to get the same numbers into a file other people can open, because the functions only calculate in recent Excel 365. This page maps the arguments that matter onto pandas code. It belongs to ",[23,24,26],"a",{"href":25},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002F","Excel Formula Equivalents in pandas",".",[29,30,32],"h2",{"id":31},"prerequisites","Prerequisites",[34,35,40],"pre",{"className":36,"code":37,"language":38,"meta":39,"style":39},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[14,41,42],{"__ignoreMap":39},[43,44,47,51,55,58],"span",{"class":45,"line":46},"line",1,[43,48,50],{"class":49},"sMTad","pip",[43,52,54],{"class":53},"srMev"," install",[43,56,57],{"class":53}," pandas",[43,59,60],{"class":53}," openpyxl\n",[10,62,63,66,67,70],{},[14,64,65],{},"sales.xlsx"," is a flat sales table — region, category, quarter, sales — the kind of range a ",[14,68,69],{},"GROUPBY"," formula points at.",[29,72,74],{"id":73},"step-1-groupby-with-its-default-total","Step 1 — GROUPBY with its default total",[10,76,77,80,81,84],{},[14,78,79],{},"=GROUPBY(A2:A13, D2:D13, SUM)"," groups by region, sums sales, sorts the regions ascending and adds a grand total row at the bottom (its ",[14,82,83],{},"total_depth"," defaults to grand totals). In pandas:",[34,86,90],{"className":87,"code":88,"language":89,"meta":39,"style":39},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\nsales = pd.read_excel(\"sales.xlsx\")\n\ndef groupby_with_total(df, by, value, func=\"sum\", total_label=\"Total\"):\n    out = df.groupby(by, as_index=False)[value].agg(func).sort_values(by)\n    total = pd.DataFrame({by: [total_label], value: [df[value].agg(func)]})\n    return pd.concat([out, total], ignore_index=True)\n\nprint(groupby_with_total(sales, \"Region\", \"Sales\"))\n","python",[14,91,92,108,115,133,138,167,191,202,221,226],{"__ignoreMap":39},[43,93,94,98,102,105],{"class":45,"line":46},[43,95,97],{"class":96},"s-kum","import",[43,99,101],{"class":100},"skGVy"," pandas ",[43,103,104],{"class":96},"as",[43,106,107],{"class":100}," pd\n",[43,109,111],{"class":45,"line":110},2,[43,112,114],{"emptyLinePlaceholder":113},true,"\n",[43,116,118,121,124,127,130],{"class":45,"line":117},3,[43,119,120],{"class":100},"sales ",[43,122,123],{"class":96},"=",[43,125,126],{"class":100}," pd.read_excel(",[43,128,129],{"class":53},"\"sales.xlsx\"",[43,131,132],{"class":100},")\n",[43,134,136],{"class":45,"line":135},4,[43,137,114],{"emptyLinePlaceholder":113},[43,139,141,144,148,151,153,156,159,161,164],{"class":45,"line":140},5,[43,142,143],{"class":96},"def",[43,145,147],{"class":146},"s_Opv"," groupby_with_total",[43,149,150],{"class":100},"(df, by, value, func",[43,152,123],{"class":96},[43,154,155],{"class":53},"\"sum\"",[43,157,158],{"class":100},", total_label",[43,160,123],{"class":96},[43,162,163],{"class":53},"\"Total\"",[43,165,166],{"class":100},"):\n",[43,168,170,173,175,178,182,184,188],{"class":45,"line":169},6,[43,171,172],{"class":100},"    out ",[43,174,123],{"class":96},[43,176,177],{"class":100}," df.groupby(by, ",[43,179,181],{"class":180},"sa561","as_index",[43,183,123],{"class":96},[43,185,187],{"class":186},"sP0c6","False",[43,189,190],{"class":100},")[value].agg(func).sort_values(by)\n",[43,192,194,197,199],{"class":45,"line":193},7,[43,195,196],{"class":100},"    total ",[43,198,123],{"class":96},[43,200,201],{"class":100}," pd.DataFrame({by: [total_label], value: [df[value].agg(func)]})\n",[43,203,205,208,211,214,216,219],{"class":45,"line":204},8,[43,206,207],{"class":96},"    return",[43,209,210],{"class":100}," pd.concat([out, total], ",[43,212,213],{"class":180},"ignore_index",[43,215,123],{"class":96},[43,217,218],{"class":186},"True",[43,220,132],{"class":100},[43,222,224],{"class":45,"line":223},9,[43,225,114],{"emptyLinePlaceholder":113},[43,227,229,232,235,238,241,244],{"class":45,"line":228},10,[43,230,231],{"class":186},"print",[43,233,234],{"class":100},"(groupby_with_total(sales, ",[43,236,237],{"class":53},"\"Region\"",[43,239,240],{"class":100},", ",[43,242,243],{"class":53},"\"Sales\"",[43,245,246],{"class":100},"))\n",[10,248,249,250,253,254,257],{},"The grand total is computed from the original rows, not by summing the group results. For ",[14,251,252],{},"SUM"," that makes no difference; for ",[14,255,256],{},"AVERAGE"," it does — the overall average is not the average of the group averages — and computing it from the rows is what Excel does too.",[259,260,268,269,268,273,268,277,268,285,268,294,268,298,268,305,268,309,268,313,268,317,268,321,268,325,268,329,268,333,268,338,268,343,268,347,268,349,268,352,268,354,268,357,268,359,268,362,268,365,268,369,268,371,268,375,268,377,268,380,268,382,268,385,268,387,268,390,268,393,268,397,268,399,268,401,268,403,268,406,268,408,268,410,268,412,268,415,268,418,268,422,268,424,268,427,268,429,268,431,268,433,268,435,268,437,268,440,268,443,268,447,268,449,268,452,268,454,268,456,268,458,268,460,268,462,268,465,268,468,268,471,268,474,268,477,268,480,268,482,268,485,268,487,268,489,268,491,268,493,268,495,268,497,268,499,268,501,268,503,268,506,268,508,268,510,268,512,268,514,268,516,268,519,268,521,268,523,268,525,268,527,268,529,268,532,268,534,268,536,268,538,268,541,268,543,268,546,268,549,268,553,268,557,268,561,268,563,268,566,268,571,268,575,268,581,268,586,268,594],"svg",{"viewBox":261,"role":262,"ariaLabelledBy":263,"xmlns":266,"style":267},"41 0 678 248","img",[264,265],"gb-basic-t","gb-basic-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:678px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[270,271,272],"title",{"id":264},"GROUPBY's summary rebuilt in pandas",[274,275,276],"desc",{"id":265},"The flat table of region, category, quarter and sales becomes a summary with one row per region in ascending order — East, North, South, West — and a Total row at the bottom.",[278,279],"rect",{"x":280,"y":281,"width":282,"height":283,"fill":284},"41","0","678","248","#ffffff",[278,286],{"x":287,"y":288,"width":289,"height":290,"rx":281,"fill":291,"stroke":292,"style":293},"57.2","40.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[278,295],{"x":296,"y":288,"width":297,"height":290,"rx":281,"fill":291,"stroke":292,"style":293},"89.2","61.4",[299,300,304],"text",{"x":301,"y":302,"style":303},"119.9","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[278,306],{"x":307,"y":288,"width":308,"height":290,"rx":281,"fill":291,"stroke":292,"style":293},"150.6","76.9",[299,310,312],{"x":311,"y":302,"style":303},"189.0","B",[278,314],{"x":315,"y":288,"width":316,"height":290,"rx":281,"fill":291,"stroke":292,"style":293},"227.5","65.4",[299,318,320],{"x":319,"y":302,"style":303},"260.2","C",[278,322],{"x":323,"y":288,"width":324,"height":290,"rx":281,"fill":291,"stroke":292,"style":293},"292.9","53.4",[299,326,328],{"x":327,"y":302,"style":303},"319.6","D",[278,330],{"x":287,"y":331,"width":289,"height":332,"rx":281,"fill":291,"stroke":292,"style":293},"64.0","28.0",[299,334,337],{"x":335,"y":336,"style":303},"73.2","83.0","1",[278,339],{"x":296,"y":331,"width":297,"height":332,"rx":281,"fill":340,"stroke":341,"style":342},"#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.5px",[299,344,346],{"x":301,"y":336,"style":345},"font-size:10.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Region",[278,348],{"x":307,"y":331,"width":308,"height":332,"rx":281,"fill":340,"stroke":341,"style":342},[299,350,351],{"x":311,"y":336,"style":345},"Category",[278,353],{"x":315,"y":331,"width":316,"height":332,"rx":281,"fill":340,"stroke":341,"style":342},[299,355,356],{"x":319,"y":336,"style":345},"Quarter",[278,358],{"x":323,"y":331,"width":324,"height":332,"rx":281,"fill":340,"stroke":341,"style":342},[299,360,361],{"x":327,"y":336,"style":345},"Sales",[278,363],{"x":287,"y":364,"width":289,"height":332,"rx":281,"fill":291,"stroke":292,"style":293},"92.0",[299,366,368],{"x":335,"y":367,"style":303},"111.0","2",[278,370],{"x":296,"y":364,"width":297,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,372,374],{"x":301,"y":367,"style":373},"font-size:10.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","North",[278,376],{"x":307,"y":364,"width":308,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,378,379],{"x":311,"y":367,"style":373},"Hardware",[278,381],{"x":315,"y":364,"width":316,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,383,384],{"x":319,"y":367,"style":373},"Q1",[278,386],{"x":323,"y":364,"width":324,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,388,389],{"x":327,"y":367,"style":373},"1200",[278,391],{"x":287,"y":392,"width":289,"height":332,"rx":281,"fill":291,"stroke":292,"style":293},"120.0",[299,394,396],{"x":335,"y":395,"style":303},"139.0","3",[278,398],{"x":296,"y":392,"width":297,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,400,374],{"x":301,"y":395,"style":373},[278,402],{"x":307,"y":392,"width":308,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,404,405],{"x":311,"y":395,"style":373},"Garden",[278,407],{"x":315,"y":392,"width":316,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,409,384],{"x":319,"y":395,"style":373},[278,411],{"x":323,"y":392,"width":324,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,413,414],{"x":327,"y":395,"style":373},"800",[278,416],{"x":287,"y":417,"width":289,"height":332,"rx":281,"fill":291,"stroke":292,"style":293},"148.0",[299,419,421],{"x":335,"y":420,"style":303},"167.0","4",[278,423],{"x":296,"y":417,"width":297,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,425,426],{"x":301,"y":420,"style":373},"South",[278,428],{"x":307,"y":417,"width":308,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,430,379],{"x":311,"y":420,"style":373},[278,432],{"x":315,"y":417,"width":316,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,434,384],{"x":319,"y":420,"style":373},[278,436],{"x":323,"y":417,"width":324,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,438,439],{"x":327,"y":420,"style":373},"950",[278,441],{"x":287,"y":442,"width":289,"height":332,"rx":281,"fill":291,"stroke":292,"style":293},"176.0",[299,444,446],{"x":335,"y":445,"style":303},"195.0","5",[278,448],{"x":296,"y":442,"width":297,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,450,451],{"x":301,"y":445,"style":373},"East",[278,453],{"x":307,"y":442,"width":308,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,455,379],{"x":311,"y":445,"style":373},[278,457],{"x":315,"y":442,"width":316,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,459,384],{"x":319,"y":445,"style":373},[278,461],{"x":323,"y":442,"width":324,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,463,464],{"x":327,"y":445,"style":373},"600",[278,466],{"x":467,"y":288,"width":289,"height":290,"rx":281,"fill":291,"stroke":292,"style":293},"556.1",[278,469],{"x":470,"y":288,"width":297,"height":290,"rx":281,"fill":291,"stroke":292,"style":293},"588.1",[299,472,304],{"x":473,"y":302,"style":303},"618.8",[278,475],{"x":476,"y":288,"width":324,"height":290,"rx":281,"fill":291,"stroke":292,"style":293},"649.5",[299,478,312],{"x":479,"y":302,"style":303},"676.1",[278,481],{"x":467,"y":331,"width":289,"height":332,"rx":281,"fill":291,"stroke":292,"style":293},[299,483,337],{"x":484,"y":336,"style":303},"572.1",[278,486],{"x":470,"y":331,"width":297,"height":332,"rx":281,"fill":340,"stroke":341,"style":342},[299,488,346],{"x":473,"y":336,"style":345},[278,490],{"x":476,"y":331,"width":324,"height":332,"rx":281,"fill":340,"stroke":341,"style":342},[299,492,361],{"x":479,"y":336,"style":345},[278,494],{"x":467,"y":364,"width":289,"height":332,"rx":281,"fill":291,"stroke":292,"style":293},[299,496,368],{"x":484,"y":367,"style":303},[278,498],{"x":470,"y":364,"width":297,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,500,451],{"x":473,"y":367,"style":373},[278,502],{"x":476,"y":364,"width":324,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,504,505],{"x":479,"y":367,"style":373},"900",[278,507],{"x":467,"y":392,"width":289,"height":332,"rx":281,"fill":291,"stroke":292,"style":293},[299,509,396],{"x":484,"y":395,"style":303},[278,511],{"x":470,"y":392,"width":297,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,513,374],{"x":473,"y":395,"style":373},[278,515],{"x":476,"y":392,"width":324,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,517,518],{"x":479,"y":395,"style":373},"4450",[278,520],{"x":467,"y":417,"width":289,"height":332,"rx":281,"fill":291,"stroke":292,"style":293},[299,522,421],{"x":484,"y":420,"style":303},[278,524],{"x":470,"y":417,"width":297,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,526,426],{"x":473,"y":420,"style":373},[278,528],{"x":476,"y":417,"width":324,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,530,531],{"x":479,"y":420,"style":373},"2700",[278,533],{"x":467,"y":442,"width":289,"height":332,"rx":281,"fill":291,"stroke":292,"style":293},[299,535,446],{"x":484,"y":445,"style":303},[278,537],{"x":470,"y":442,"width":297,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,539,540],{"x":473,"y":445,"style":373},"West",[278,542],{"x":476,"y":442,"width":324,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,544,545],{"x":479,"y":445,"style":373},"250",[278,547],{"x":467,"y":548,"width":289,"height":332,"rx":281,"fill":291,"stroke":292,"style":293},"204.0",[299,550,552],{"x":484,"y":551,"style":303},"223.0","6",[278,554],{"x":470,"y":548,"width":297,"height":332,"rx":281,"fill":555,"stroke":556,"style":342},"#d9f4f1","var(--teal,#0f9488)",[299,558,560],{"x":473,"y":551,"style":559},"font-size:10.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Total",[278,562],{"x":476,"y":548,"width":324,"height":332,"rx":281,"fill":555,"stroke":556,"style":342},[299,564,565],{"x":479,"y":551,"style":559},"8300",[299,567,65],{"x":568,"y":569,"style":570},"201.7","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle",[299,572,574],{"x":573,"y":569,"style":570},"629.4","summary",[45,576],{"x1":577,"y1":578,"x2":579,"y2":578,"stroke":341,"style":580},"356.3","122.0","542.1","stroke-width:2px",[582,583],"polygon",{"points":584,"fill":585},"546.1,122.0 537.1,127.0 537.1,117.0","#5b5cf0",[278,587],{"x":588,"y":589,"width":590,"height":591,"rx":592,"fill":284,"stroke":292,"style":593},"383.2","89.0","135.8","22.0","11","stroke-width:1.2px",[299,595,599],{"x":596,"y":597,"style":598},"451.2","104.0","font-size:11.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","groupby + total row",[29,601,603],{"id":602},"step-2-sort-by-value-and-filter-rows-first","Step 2 — Sort by value and filter rows first",[10,605,606,607,610,611,614,615,618],{},"Two optional arguments change the result most often. ",[14,608,609],{},"sort_order"," sorts by a result column — ",[14,612,613],{},"-2"," means descending by the second column, the values — and ",[14,616,617],{},"filter_array"," keeps only rows where a condition holds before grouping:",[34,620,622],{"className":87,"code":621,"language":89,"meta":39,"style":39},"q2 = sales[sales[\"Quarter\"] == \"Q2\"]                       # filter_array: D2:D13 = \"Q2\"\nranked = (q2.groupby(\"Region\", as_index=False)[\"Sales\"].sum()\n            .sort_values(\"Sales\", ascending=False))         # sort_order: -2\nprint(ranked.to_string(index=False))\n",[14,623,624,653,681,703],{"__ignoreMap":39},[43,625,626,629,631,634,637,640,643,646,649],{"class":45,"line":46},[43,627,628],{"class":100},"q2 ",[43,630,123],{"class":96},[43,632,633],{"class":100}," sales[sales[",[43,635,636],{"class":53},"\"Quarter\"",[43,638,639],{"class":100},"] ",[43,641,642],{"class":96},"==",[43,644,645],{"class":53}," \"Q2\"",[43,647,648],{"class":100},"]                       ",[43,650,652],{"class":651},"s-wDw","# filter_array: D2:D13 = \"Q2\"\n",[43,654,655,658,660,663,665,667,669,671,673,676,678],{"class":45,"line":110},[43,656,657],{"class":100},"ranked ",[43,659,123],{"class":96},[43,661,662],{"class":100}," (q2.groupby(",[43,664,237],{"class":53},[43,666,240],{"class":100},[43,668,181],{"class":180},[43,670,123],{"class":96},[43,672,187],{"class":186},[43,674,675],{"class":100},")[",[43,677,243],{"class":53},[43,679,680],{"class":100},"].sum()\n",[43,682,683,686,688,690,693,695,697,700],{"class":45,"line":117},[43,684,685],{"class":100},"            .sort_values(",[43,687,243],{"class":53},[43,689,240],{"class":100},[43,691,692],{"class":180},"ascending",[43,694,123],{"class":96},[43,696,187],{"class":186},[43,698,699],{"class":100},"))         ",[43,701,702],{"class":651},"# sort_order: -2\n",[43,704,705,707,710,713,715,717],{"class":45,"line":135},[43,706,231],{"class":186},[43,708,709],{"class":100},"(ranked.to_string(",[43,711,712],{"class":180},"index",[43,714,123],{"class":96},[43,716,187],{"class":186},[43,718,246],{"class":100},[10,720,721,722,726,727,729,730,733,734,27],{},"Filtering ",[723,724,725],"em",{},"before"," grouping is the point of ",[14,728,617],{},": excluded rows do not reach the total either. Note East's Q2 hardware row with sales of 0 — it is kept, because zero is a value; a filter on ",[14,731,732],{},"Sales > 0"," would remove it. The ranked order makes this the usual \"top regions\" table; for the top N per group, see ",[23,735,737],{"href":736},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fget-the-top-n-rows-per-group-in-pandas\u002F","Get the Top N Rows per Group in pandas",[29,739,741],{"id":740},"step-3-several-functions-and-two-levels","Step 3 — Several functions and two levels",[10,743,744,746,747,750,751,754],{},[14,745,69],{}," accepts several functions stacked side by side (",[14,748,749],{},"HSTACK(SUM, AVERAGE, COUNT)",") and several row fields, with ",[14,752,753],{},"total_depth=2"," adding a subtotal per first-level group. pandas does the same with named aggregations and a subtotal loop:",[34,756,758],{"className":87,"code":757,"language":89,"meta":39,"style":39},"stats = sales.groupby(\"Region\").agg(Sum=(\"Sales\", \"sum\"), Average=(\"Sales\", \"mean\"), Count=(\"Sales\", \"count\"))\nprint(stats.round(1))\n\ndef two_level(df, outer, inner, value):\n    parts = []\n    for key, part in df.groupby(outer, sort=True):\n        detail = part.groupby(inner, as_index=False)[value].sum()\n        detail.insert(0, outer, key)\n        parts.append(detail)\n        parts.append(pd.DataFrame({outer: [key], inner: [\"Subtotal\"], value: [part[value].sum()]}))\n    parts.append(pd.DataFrame({outer: [\"Total\"], inner: [\"\"], value: [df[value].sum()]}))\n    return pd.concat(parts, ignore_index=True)\n\nprint(two_level(sales, \"Region\", \"Category\", \"Sales\").to_string(index=False))\n",[14,759,760,824,835,839,849,859,882,901,911,916,927,944,960,965],{"__ignoreMap":39},[43,761,762,765,767,770,772,775,778,780,783,785,787,789,792,795,797,799,801,803,806,808,811,813,815,817,819,822],{"class":45,"line":46},[43,763,764],{"class":100},"stats ",[43,766,123],{"class":96},[43,768,769],{"class":100}," sales.groupby(",[43,771,237],{"class":53},[43,773,774],{"class":100},").agg(",[43,776,777],{"class":180},"Sum",[43,779,123],{"class":96},[43,781,782],{"class":100},"(",[43,784,243],{"class":53},[43,786,240],{"class":100},[43,788,155],{"class":53},[43,790,791],{"class":100},"), ",[43,793,794],{"class":180},"Average",[43,796,123],{"class":96},[43,798,782],{"class":100},[43,800,243],{"class":53},[43,802,240],{"class":100},[43,804,805],{"class":53},"\"mean\"",[43,807,791],{"class":100},[43,809,810],{"class":180},"Count",[43,812,123],{"class":96},[43,814,782],{"class":100},[43,816,243],{"class":53},[43,818,240],{"class":100},[43,820,821],{"class":53},"\"count\"",[43,823,246],{"class":100},[43,825,826,828,831,833],{"class":45,"line":110},[43,827,231],{"class":186},[43,829,830],{"class":100},"(stats.round(",[43,832,337],{"class":186},[43,834,246],{"class":100},[43,836,837],{"class":45,"line":117},[43,838,114],{"emptyLinePlaceholder":113},[43,840,841,843,846],{"class":45,"line":135},[43,842,143],{"class":96},[43,844,845],{"class":146}," two_level",[43,847,848],{"class":100},"(df, outer, inner, value):\n",[43,850,851,854,856],{"class":45,"line":140},[43,852,853],{"class":100},"    parts ",[43,855,123],{"class":96},[43,857,858],{"class":100}," []\n",[43,860,861,864,867,870,873,876,878,880],{"class":45,"line":169},[43,862,863],{"class":96},"    for",[43,865,866],{"class":100}," key, part ",[43,868,869],{"class":96},"in",[43,871,872],{"class":100}," df.groupby(outer, ",[43,874,875],{"class":180},"sort",[43,877,123],{"class":96},[43,879,218],{"class":186},[43,881,166],{"class":100},[43,883,884,887,889,892,894,896,898],{"class":45,"line":193},[43,885,886],{"class":100},"        detail ",[43,888,123],{"class":96},[43,890,891],{"class":100}," part.groupby(inner, ",[43,893,181],{"class":180},[43,895,123],{"class":96},[43,897,187],{"class":186},[43,899,900],{"class":100},")[value].sum()\n",[43,902,903,906,908],{"class":45,"line":204},[43,904,905],{"class":100},"        detail.insert(",[43,907,281],{"class":186},[43,909,910],{"class":100},", outer, key)\n",[43,912,913],{"class":45,"line":223},[43,914,915],{"class":100},"        parts.append(detail)\n",[43,917,918,921,924],{"class":45,"line":228},[43,919,920],{"class":100},"        parts.append(pd.DataFrame({outer: [key], inner: [",[43,922,923],{"class":53},"\"Subtotal\"",[43,925,926],{"class":100},"], value: [part[value].sum()]}))\n",[43,928,930,933,935,938,941],{"class":45,"line":929},11,[43,931,932],{"class":100},"    parts.append(pd.DataFrame({outer: [",[43,934,163],{"class":53},[43,936,937],{"class":100},"], inner: [",[43,939,940],{"class":53},"\"\"",[43,942,943],{"class":100},"], value: [df[value].sum()]}))\n",[43,945,947,949,952,954,956,958],{"class":45,"line":946},12,[43,948,207],{"class":96},[43,950,951],{"class":100}," pd.concat(parts, ",[43,953,213],{"class":180},[43,955,123],{"class":96},[43,957,218],{"class":186},[43,959,132],{"class":100},[43,961,963],{"class":45,"line":962},13,[43,964,114],{"emptyLinePlaceholder":113},[43,966,968,970,973,975,977,980,982,984,987,989,991,993],{"class":45,"line":967},14,[43,969,231],{"class":186},[43,971,972],{"class":100},"(two_level(sales, ",[43,974,237],{"class":53},[43,976,240],{"class":100},[43,978,979],{"class":53},"\"Category\"",[43,981,240],{"class":100},[43,983,243],{"class":53},[43,985,986],{"class":100},").to_string(",[43,988,712],{"class":180},[43,990,123],{"class":96},[43,992,187],{"class":186},[43,994,246],{"class":100},[10,996,997,998,1001,1002,27],{},"Named aggregations give the result columns readable names, which a summary sheet needs anyway. The two-level layout — detail rows, a subtotal per region, a grand total — is what ",[14,999,1000],{},"GROUPBY(A2:B13, D2:D13, SUM, , 2)"," produces; writing it out with bold subtotal rows is covered in ",[23,1003,1005],{"href":1004},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fadd-subtotals-and-grand-totals-to-a-pandas-pivot-table\u002F","Add Subtotals and Grand Totals to a pandas Pivot Table",[29,1007,1009],{"id":1008},"step-4-pivotby-is-pivot_table-with-margins","Step 4 — PIVOTBY is pivot_table with margins",[10,1011,1012,1015],{},[14,1013,1014],{},"=PIVOTBY(A2:A13, C2:C13, D2:D13, SUM)"," puts regions down the side, quarters across the top, and adds a total row and a total column by default:",[34,1017,1019],{"className":87,"code":1018,"language":89,"meta":39,"style":39},"cross = pd.pivot_table(sales, index=\"Region\", columns=\"Quarter\", values=\"Sales\",\n                       aggfunc=\"sum\", fill_value=0, margins=True, margins_name=\"Total\")\nprint(cross)\n",[14,1020,1021,1058,1096],{"__ignoreMap":39},[43,1022,1023,1026,1028,1031,1033,1035,1037,1039,1042,1044,1046,1048,1051,1053,1055],{"class":45,"line":46},[43,1024,1025],{"class":100},"cross ",[43,1027,123],{"class":96},[43,1029,1030],{"class":100}," pd.pivot_table(sales, ",[43,1032,712],{"class":180},[43,1034,123],{"class":96},[43,1036,237],{"class":53},[43,1038,240],{"class":100},[43,1040,1041],{"class":180},"columns",[43,1043,123],{"class":96},[43,1045,636],{"class":53},[43,1047,240],{"class":100},[43,1049,1050],{"class":180},"values",[43,1052,123],{"class":96},[43,1054,243],{"class":53},[43,1056,1057],{"class":100},",\n",[43,1059,1060,1063,1065,1067,1069,1072,1074,1076,1078,1081,1083,1085,1087,1090,1092,1094],{"class":45,"line":110},[43,1061,1062],{"class":180},"                       aggfunc",[43,1064,123],{"class":96},[43,1066,155],{"class":53},[43,1068,240],{"class":100},[43,1070,1071],{"class":180},"fill_value",[43,1073,123],{"class":96},[43,1075,281],{"class":186},[43,1077,240],{"class":100},[43,1079,1080],{"class":180},"margins",[43,1082,123],{"class":96},[43,1084,218],{"class":186},[43,1086,240],{"class":100},[43,1088,1089],{"class":180},"margins_name",[43,1091,123],{"class":96},[43,1093,163],{"class":53},[43,1095,132],{"class":100},[43,1097,1098,1100],{"class":45,"line":117},[43,1099,231],{"class":186},[43,1101,1102],{"class":100},"(cross)\n",[10,1104,1105,1108,1109,1112,1113,1116],{},[14,1106,1107],{},"margins=True"," adds the totals, computed from the underlying rows like Excel's. ",[14,1110,1111],{},"fill_value=0"," shows 0 where a combination has no rows (West had no Q1 sales); without it the cell is ",[14,1114,1115],{},"NaN",", the equivalent of the blank Excel leaves. Combinations that never occur at all are simply absent from both outputs.",[259,1118,268,1124,268,1127,268,1130,268,1135,268,1139,268,1143,268,1147,268,1151,268,1154,268,1158,268,1161,268,1164,268,1167,268,1169,268,1173,268,1175,268,1178,268,1180,268,1182,268,1184,268,1187,268,1189,268,1191,268,1194,268,1197,268,1199,268,1202,268,1204,268,1206,268,1208,268,1211,268,1213,268,1216,268,1219,268,1222,268,1224,268,1226,268,1228,268,1231,268,1233,268,1236,268,1238,268,1240,268,1243,268,1246,268,1248,268,1250,268,1252,268,1255,268,1257,268,1259,268,1261,268,1263,268,1266,268,1269,268,1271,268,1273,268,1275,268,1277,268,1279,268,1281,268,1283,268,1285,268,1288,268,1291,268,1293,268,1295,268,1297,268,1300,268,1302,268,1305,268,1307],{"viewBox":1119,"role":262,"ariaLabelledBy":1120,"xmlns":266,"style":1123},"234 0 291 224",[1121,1122],"gb-pivot-t","gb-pivot-d","width:100%;max-width:291px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[270,1125,1126],{"id":1121},"PIVOTBY's crosstab with row and column totals",[274,1128,1129],{"id":1122},"Regions down the side and quarters across the top with summed sales. East has 600 in Q1 and 300 in Q2; North 2000 and 2450; South 1350 and 1350; West 0 and 250. The Total row and column give 3950 for Q1, 4350 for Q2 and 8300 overall.",[278,1131],{"x":1132,"y":281,"width":1133,"height":1134,"fill":284},"234","291","224",[278,1136],{"x":1137,"y":1138,"width":289,"height":290,"rx":281,"fill":291,"stroke":292,"style":293},"250.7","16.0",[278,1140],{"x":1141,"y":1138,"width":1142,"height":290,"rx":281,"fill":291,"stroke":292,"style":293},"282.7","63.2",[299,1144,304],{"x":1145,"y":1146,"style":303},"314.3","33.0",[278,1148],{"x":1149,"y":1138,"width":1150,"height":290,"rx":281,"fill":291,"stroke":292,"style":293},"345.9","54.6",[299,1152,312],{"x":1153,"y":1146,"style":303},"373.2",[278,1155],{"x":1156,"y":1138,"width":1157,"height":290,"rx":281,"fill":291,"stroke":292,"style":293},"400.5","54.2",[299,1159,320],{"x":1160,"y":1146,"style":303},"427.6",[278,1162],{"x":1163,"y":1138,"width":1150,"height":290,"rx":281,"fill":291,"stroke":292,"style":293},"454.7",[299,1165,328],{"x":1166,"y":1146,"style":303},"482.0",[278,1168],{"x":1137,"y":288,"width":289,"height":332,"rx":281,"fill":291,"stroke":292,"style":293},[299,1170,337],{"x":1171,"y":1172,"style":303},"266.7","59.0",[278,1174],{"x":1141,"y":288,"width":1142,"height":332,"rx":281,"fill":340,"stroke":341,"style":342},[299,1176,346],{"x":1145,"y":1172,"style":1177},"font-size:11px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle",[278,1179],{"x":1149,"y":288,"width":1150,"height":332,"rx":281,"fill":340,"stroke":341,"style":342},[299,1181,384],{"x":1153,"y":1172,"style":1177},[278,1183],{"x":1156,"y":288,"width":1157,"height":332,"rx":281,"fill":340,"stroke":341,"style":342},[299,1185,1186],{"x":1160,"y":1172,"style":1177},"Q2",[278,1188],{"x":1163,"y":288,"width":1150,"height":332,"rx":281,"fill":340,"stroke":341,"style":342},[299,1190,560],{"x":1166,"y":1172,"style":1177},[278,1192],{"x":1137,"y":1193,"width":289,"height":332,"rx":281,"fill":291,"stroke":292,"style":293},"68.0",[299,1195,368],{"x":1171,"y":1196,"style":303},"87.0",[278,1198],{"x":1141,"y":1193,"width":1142,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,1200,451],{"x":1145,"y":1196,"style":1201},"font-size:11px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[278,1203],{"x":1149,"y":1193,"width":1150,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,1205,464],{"x":1153,"y":1196,"style":1201},[278,1207],{"x":1156,"y":1193,"width":1157,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,1209,1210],{"x":1160,"y":1196,"style":1201},"300",[278,1212],{"x":1163,"y":1193,"width":1150,"height":332,"rx":281,"fill":555,"stroke":556,"style":342},[299,1214,505],{"x":1166,"y":1196,"style":1215},"font-size:11px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[278,1217],{"x":1137,"y":1218,"width":289,"height":332,"rx":281,"fill":291,"stroke":292,"style":293},"96.0",[299,1220,396],{"x":1171,"y":1221,"style":303},"115.0",[278,1223],{"x":1141,"y":1218,"width":1142,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,1225,374],{"x":1145,"y":1221,"style":1201},[278,1227],{"x":1149,"y":1218,"width":1150,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,1229,1230],{"x":1153,"y":1221,"style":1201},"2000",[278,1232],{"x":1156,"y":1218,"width":1157,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,1234,1235],{"x":1160,"y":1221,"style":1201},"2450",[278,1237],{"x":1163,"y":1218,"width":1150,"height":332,"rx":281,"fill":555,"stroke":556,"style":342},[299,1239,518],{"x":1166,"y":1221,"style":1215},[278,1241],{"x":1137,"y":1242,"width":289,"height":332,"rx":281,"fill":291,"stroke":292,"style":293},"124.0",[299,1244,421],{"x":1171,"y":1245,"style":303},"143.0",[278,1247],{"x":1141,"y":1242,"width":1142,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,1249,426],{"x":1145,"y":1245,"style":1201},[278,1251],{"x":1149,"y":1242,"width":1150,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,1253,1254],{"x":1153,"y":1245,"style":1201},"1350",[278,1256],{"x":1156,"y":1242,"width":1157,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,1258,1254],{"x":1160,"y":1245,"style":1201},[278,1260],{"x":1163,"y":1242,"width":1150,"height":332,"rx":281,"fill":555,"stroke":556,"style":342},[299,1262,531],{"x":1166,"y":1245,"style":1215},[278,1264],{"x":1137,"y":1265,"width":289,"height":332,"rx":281,"fill":291,"stroke":292,"style":293},"152.0",[299,1267,446],{"x":1171,"y":1268,"style":303},"171.0",[278,1270],{"x":1141,"y":1265,"width":1142,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,1272,540],{"x":1145,"y":1268,"style":1201},[278,1274],{"x":1149,"y":1265,"width":1150,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,1276,281],{"x":1153,"y":1268,"style":1201},[278,1278],{"x":1156,"y":1265,"width":1157,"height":332,"rx":281,"fill":284,"stroke":292,"style":293},[299,1280,545],{"x":1160,"y":1268,"style":1201},[278,1282],{"x":1163,"y":1265,"width":1150,"height":332,"rx":281,"fill":555,"stroke":556,"style":342},[299,1284,545],{"x":1166,"y":1268,"style":1215},[278,1286],{"x":1137,"y":1287,"width":289,"height":332,"rx":281,"fill":291,"stroke":292,"style":293},"180.0",[299,1289,552],{"x":1171,"y":1290,"style":303},"199.0",[278,1292],{"x":1141,"y":1287,"width":1142,"height":332,"rx":281,"fill":555,"stroke":556,"style":342},[299,1294,560],{"x":1145,"y":1290,"style":1215},[278,1296],{"x":1149,"y":1287,"width":1150,"height":332,"rx":281,"fill":555,"stroke":556,"style":342},[299,1298,1299],{"x":1153,"y":1290,"style":1215},"3950",[278,1301],{"x":1156,"y":1287,"width":1157,"height":332,"rx":281,"fill":555,"stroke":556,"style":342},[299,1303,1304],{"x":1160,"y":1290,"style":1215},"4350",[278,1306],{"x":1163,"y":1287,"width":1150,"height":332,"rx":281,"fill":555,"stroke":556,"style":342},[299,1308,565],{"x":1166,"y":1290,"style":1215},[29,1310,1312],{"id":1311},"step-5-percentof-and-shares-of-the-total","Step 5 — PERCENTOF and shares of the total",[10,1314,1315,1316,1319,1320,1322,1323,1326,1327,1330,1331,1334],{},"Excel added ",[14,1317,1318],{},"PERCENTOF"," to use as the function in ",[14,1321,69],{}," and ",[14,1324,1325],{},"PIVOTBY",": each group's share of the total. In pandas, divide by the total — or use ",[14,1328,1329],{},"normalize"," in ",[14,1332,1333],{},"crosstab",":",[34,1336,1338],{"className":87,"code":1337,"language":89,"meta":39,"style":39},"share = sales.groupby(\"Region\")[\"Sales\"].sum() \u002F sales[\"Sales\"].sum()\nprint(share.map(\"{:.1%}\".format))\ncol_share = pd.crosstab(sales[\"Region\"], sales[\"Quarter\"], values=sales[\"Sales\"],\n                        aggfunc=\"sum\", normalize=\"columns\")\nprint(col_share.round(3))\n",[14,1339,1340,1368,1393,1425,1445],{"__ignoreMap":39},[43,1341,1342,1345,1347,1349,1351,1353,1355,1358,1361,1364,1366],{"class":45,"line":46},[43,1343,1344],{"class":100},"share ",[43,1346,123],{"class":96},[43,1348,769],{"class":100},[43,1350,237],{"class":53},[43,1352,675],{"class":100},[43,1354,243],{"class":53},[43,1356,1357],{"class":100},"].sum() ",[43,1359,1360],{"class":96},"\u002F",[43,1362,1363],{"class":100}," sales[",[43,1365,243],{"class":53},[43,1367,680],{"class":100},[43,1369,1370,1372,1375,1378,1382,1385,1388,1390],{"class":45,"line":110},[43,1371,231],{"class":186},[43,1373,1374],{"class":100},"(share.map(",[43,1376,1377],{"class":53},"\"",[43,1379,1381],{"class":1380},"sSjpA","{",[43,1383,1384],{"class":96},":.1%",[43,1386,1387],{"class":1380},"}",[43,1389,1377],{"class":53},[43,1391,1392],{"class":100},".format))\n",[43,1394,1395,1398,1400,1403,1405,1408,1410,1413,1415,1417,1420,1422],{"class":45,"line":117},[43,1396,1397],{"class":100},"col_share ",[43,1399,123],{"class":96},[43,1401,1402],{"class":100}," pd.crosstab(sales[",[43,1404,237],{"class":53},[43,1406,1407],{"class":100},"], sales[",[43,1409,636],{"class":53},[43,1411,1412],{"class":100},"], ",[43,1414,1050],{"class":180},[43,1416,123],{"class":96},[43,1418,1419],{"class":100},"sales[",[43,1421,243],{"class":53},[43,1423,1424],{"class":100},"],\n",[43,1426,1427,1430,1432,1434,1436,1438,1440,1443],{"class":45,"line":135},[43,1428,1429],{"class":180},"                        aggfunc",[43,1431,123],{"class":96},[43,1433,155],{"class":53},[43,1435,240],{"class":100},[43,1437,1329],{"class":180},[43,1439,123],{"class":96},[43,1441,1442],{"class":53},"\"columns\"",[43,1444,132],{"class":100},[43,1446,1447,1449,1452,1454],{"class":45,"line":140},[43,1448,231],{"class":186},[43,1450,1451],{"class":100},"(col_share.round(",[43,1453,396],{"class":186},[43,1455,246],{"class":100},[10,1457,1458,1461,1462,1465,1466,1469,1470,1473,1474,1477,1478,27],{},[14,1459,1460],{},"normalize=\"columns\""," gives each region's share ",[723,1463,1464],{},"within"," each quarter, the equivalent of PIVOTBY's ",[14,1467,1468],{},"relative_to"," set to columns; ",[14,1471,1472],{},"normalize=\"index\""," gives shares within each row, and ",[14,1475,1476],{},"normalize=\"all\""," shares of the grand total. More ways to compute shares are in ",[23,1479,1481],{"href":1480},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcalculate-percent-of-total-per-group-in-pandas\u002F","Calculate Percent of Total per Group in pandas",[29,1483,1485],{"id":1484},"step-6-write-the-summary-as-values","Step 6 — Write the summary as values",[10,1487,1488,1489,1492,1493,1495,1496,1499,1500,1502],{},"A workbook that ",[723,1490,1491],{},"contains"," ",[14,1494,69],{}," formulas only calculates in Excel 365: older Excel versions show ",[14,1497,1498],{},"#NAME?",", and LibreOffice did not evaluate them in testing (version 26.2 returned ",[14,1501,1498],{}," for both functions). Writing the pandas results as values works everywhere:",[34,1504,1506],{"className":87,"code":1505,"language":89,"meta":39,"style":39},"with pd.ExcelWriter(\"sales_summary.xlsx\", engine=\"xlsxwriter\") as xw:\n    groupby_with_total(sales, \"Region\", \"Sales\").to_excel(xw, sheet_name=\"By region\", index=False)\n    cross.to_excel(xw, sheet_name=\"Region x quarter\")\n    bold = xw.book.add_format({\"bold\": True, \"top\": 1})\n    last = len(sales[\"Region\"].unique()) + 1\n    xw.sheets[\"By region\"].set_row(last, None, bold)\nwith pd.ExcelFile(\"sales_summary.xlsx\") as xf:\n    print(xf.sheet_names)\n",[14,1507,1508,1537,1569,1583,1613,1637,1653,1669],{"__ignoreMap":39},[43,1509,1510,1513,1516,1519,1521,1524,1526,1529,1532,1534],{"class":45,"line":46},[43,1511,1512],{"class":96},"with",[43,1514,1515],{"class":100}," pd.ExcelWriter(",[43,1517,1518],{"class":53},"\"sales_summary.xlsx\"",[43,1520,240],{"class":100},[43,1522,1523],{"class":180},"engine",[43,1525,123],{"class":96},[43,1527,1528],{"class":53},"\"xlsxwriter\"",[43,1530,1531],{"class":100},") ",[43,1533,104],{"class":96},[43,1535,1536],{"class":100}," xw:\n",[43,1538,1539,1542,1544,1546,1548,1551,1554,1556,1559,1561,1563,1565,1567],{"class":45,"line":110},[43,1540,1541],{"class":100},"    groupby_with_total(sales, ",[43,1543,237],{"class":53},[43,1545,240],{"class":100},[43,1547,243],{"class":53},[43,1549,1550],{"class":100},").to_excel(xw, ",[43,1552,1553],{"class":180},"sheet_name",[43,1555,123],{"class":96},[43,1557,1558],{"class":53},"\"By region\"",[43,1560,240],{"class":100},[43,1562,712],{"class":180},[43,1564,123],{"class":96},[43,1566,187],{"class":186},[43,1568,132],{"class":100},[43,1570,1571,1574,1576,1578,1581],{"class":45,"line":117},[43,1572,1573],{"class":100},"    cross.to_excel(xw, ",[43,1575,1553],{"class":180},[43,1577,123],{"class":96},[43,1579,1580],{"class":53},"\"Region x quarter\"",[43,1582,132],{"class":100},[43,1584,1585,1588,1590,1593,1596,1599,1601,1603,1606,1608,1610],{"class":45,"line":135},[43,1586,1587],{"class":100},"    bold ",[43,1589,123],{"class":96},[43,1591,1592],{"class":100}," xw.book.add_format({",[43,1594,1595],{"class":53},"\"bold\"",[43,1597,1598],{"class":100},": ",[43,1600,218],{"class":186},[43,1602,240],{"class":100},[43,1604,1605],{"class":53},"\"top\"",[43,1607,1598],{"class":100},[43,1609,337],{"class":186},[43,1611,1612],{"class":100},"})\n",[43,1614,1615,1618,1620,1623,1626,1628,1631,1634],{"class":45,"line":140},[43,1616,1617],{"class":100},"    last ",[43,1619,123],{"class":96},[43,1621,1622],{"class":186}," len",[43,1624,1625],{"class":100},"(sales[",[43,1627,237],{"class":53},[43,1629,1630],{"class":100},"].unique()) ",[43,1632,1633],{"class":96},"+",[43,1635,1636],{"class":186}," 1\n",[43,1638,1639,1642,1644,1647,1650],{"class":45,"line":169},[43,1640,1641],{"class":100},"    xw.sheets[",[43,1643,1558],{"class":53},[43,1645,1646],{"class":100},"].set_row(last, ",[43,1648,1649],{"class":186},"None",[43,1651,1652],{"class":100},", bold)\n",[43,1654,1655,1657,1660,1662,1664,1666],{"class":45,"line":193},[43,1656,1512],{"class":96},[43,1658,1659],{"class":100}," pd.ExcelFile(",[43,1661,1518],{"class":53},[43,1663,1531],{"class":100},[43,1665,104],{"class":96},[43,1667,1668],{"class":100}," xf:\n",[43,1670,1671,1674],{"class":45,"line":204},[43,1672,1673],{"class":186},"    print",[43,1675,1676],{"class":100},"(xf.sheet_names)\n",[10,1678,1679,1680,1682],{},"The total row is bolded with a top border so it reads like the formula's output. If recipients do have Excel 365 and want a live formula, keep the data sheet in the file and let them add ",[14,1681,69],{}," themselves; the Python report remains the version of record.",[259,1684,268,1690,268,1693,268,1696,268,1700,268,1704,268,1708,268,1714,268,1718,268,1723,268,1725,268,1730,268,1733,268,1736,268,1738,268,1741,268,1743,268,1746,268,1748,268,1752,268,1755,268,1757,268,1759,268,1763,268,1766,268,1770,268,1772,268,1776,268,1779,268,1782,268,1784],{"viewBox":1685,"role":262,"ariaLabelledBy":1686,"xmlns":266,"style":1689},"4 0 752 276",[1687,1688],"gb-map-t","gb-map-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[270,1691,1692],{"id":1687},"GROUPBY and PIVOTBY arguments and their pandas equivalents",[274,1694,1695],{"id":1688},"row_fields maps to groupby keys, values and function to agg, total_depth to an appended total or subtotal rows, sort_order to sort_values, filter_array to a boolean filter before grouping, PIVOTBY col_fields to pivot_table columns with margins, and PERCENTOF to dividing by the total or normalize.",[278,1697],{"x":421,"y":281,"width":1698,"height":1699,"fill":284},"752","276",[278,1701],{"x":1702,"y":1138,"width":1703,"height":288,"rx":281,"fill":284,"stroke":292,"style":293},"20.0","205.8",[278,1705],{"x":1706,"y":1138,"width":1707,"height":288,"rx":281,"fill":340,"stroke":341,"style":593},"225.8","514.2",[299,1709,1713],{"x":1710,"y":1711,"style":1712},"482.9","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","pandas",[278,1715],{"x":1702,"y":1716,"width":1703,"height":1717,"rx":281,"fill":291,"stroke":292,"style":293},"56.0","34.0",[299,1719,1722],{"x":289,"y":1720,"style":1721},"78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","row_fields, values, function",[278,1724],{"x":1706,"y":1716,"width":1707,"height":1717,"rx":281,"fill":284,"stroke":292,"style":293},[299,1726,1729],{"x":1710,"y":1727,"style":1728},"77.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","groupby(keys)[value].agg(func)",[278,1731],{"x":1702,"y":1732,"width":1703,"height":1717,"rx":281,"fill":291,"stroke":292,"style":293},"90.0",[299,1734,83],{"x":289,"y":1735,"style":1721},"112.0",[278,1737],{"x":1706,"y":1732,"width":1707,"height":1717,"rx":281,"fill":284,"stroke":292,"style":293},[299,1739,1740],{"x":1710,"y":367,"style":1728},"concat a total \u002F subtotal rows",[278,1742],{"x":1702,"y":1242,"width":1703,"height":1717,"rx":281,"fill":291,"stroke":292,"style":293},[299,1744,609],{"x":289,"y":1745,"style":1721},"146.0",[278,1747],{"x":1706,"y":1242,"width":1707,"height":1717,"rx":281,"fill":284,"stroke":292,"style":293},[299,1749,1751],{"x":1710,"y":1750,"style":1728},"145.0","sort_values(column, ascending=…)",[278,1753],{"x":1702,"y":1754,"width":1703,"height":1717,"rx":281,"fill":291,"stroke":292,"style":293},"158.0",[299,1756,617],{"x":289,"y":1287,"style":1721},[278,1758],{"x":1706,"y":1754,"width":1707,"height":1717,"rx":281,"fill":284,"stroke":292,"style":293},[299,1760,1762],{"x":1710,"y":1761,"style":1728},"179.0","df[condition] before groupby",[278,1764],{"x":1702,"y":1765,"width":1703,"height":1717,"rx":281,"fill":291,"stroke":292,"style":293},"192.0",[299,1767,1769],{"x":289,"y":1768,"style":1721},"214.0","PIVOTBY col_fields + totals",[278,1771],{"x":1706,"y":1765,"width":1707,"height":1717,"rx":281,"fill":284,"stroke":292,"style":293},[299,1773,1775],{"x":1710,"y":1774,"style":1728},"213.0","pivot_table(…, margins=True)",[278,1777],{"x":1702,"y":1778,"width":1703,"height":1717,"rx":281,"fill":291,"stroke":292,"style":293},"226.0",[299,1780,1318],{"x":289,"y":1781,"style":1721},"248.0",[278,1783],{"x":1706,"y":1778,"width":1707,"height":1717,"rx":281,"fill":284,"stroke":292,"style":293},[299,1785,1787],{"x":1710,"y":1786,"style":1728},"247.0","divide by total, or normalize=",[29,1789,1791],{"id":1790},"step-7-one-helper-for-many-summaries","Step 7 — One helper for many summaries",[10,1793,1794,1795,1797],{},"A workbook converted from Excel 365 often has a dozen ",[14,1796,69],{}," formulas that differ only in the field, the function and the filter. Capture the pattern once, so each summary is one call and the defaults match Excel's — ascending sort, a grand total, optional filter:",[34,1799,1801],{"className":87,"code":1800,"language":89,"meta":39,"style":39},"def excel_groupby(df, by, value, func=\"sum\", sort_desc_by_value=False, where=None, total=True):\n    data = df if where is None else df[where(df)]\n    out = data.groupby(by, as_index=False)[value].agg(func)\n    out = out.sort_values(value if sort_desc_by_value else by, ascending=not sort_desc_by_value)\n    if total:\n        label = {by: \"Total\"} if isinstance(by, str) else {by[0]: \"Total\"}\n        out = pd.concat([out, pd.DataFrame([{**label, value: data[value].agg(func)}])], ignore_index=True)\n    return out\n\nprint(excel_groupby(sales, \"Category\", \"Sales\", \"mean\").round(1).to_string(index=False))\nprint(excel_groupby(sales, \"Region\", \"Sales\", sort_desc_by_value=True,\n                    where=lambda d: d[\"Category\"] == \"Garden\").to_string(index=False))\n",[14,1802,1803,1839,1867,1885,1913,1921,1964,1988,1995,1999,2031,2054],{"__ignoreMap":39},[43,1804,1805,1807,1810,1812,1814,1816,1819,1821,1823,1826,1828,1830,1833,1835,1837],{"class":45,"line":46},[43,1806,143],{"class":96},[43,1808,1809],{"class":146}," excel_groupby",[43,1811,150],{"class":100},[43,1813,123],{"class":96},[43,1815,155],{"class":53},[43,1817,1818],{"class":100},", sort_desc_by_value",[43,1820,123],{"class":96},[43,1822,187],{"class":186},[43,1824,1825],{"class":100},", where",[43,1827,123],{"class":96},[43,1829,1649],{"class":186},[43,1831,1832],{"class":100},", total",[43,1834,123],{"class":96},[43,1836,218],{"class":186},[43,1838,166],{"class":100},[43,1840,1841,1844,1846,1849,1852,1855,1858,1861,1864],{"class":45,"line":110},[43,1842,1843],{"class":100},"    data ",[43,1845,123],{"class":96},[43,1847,1848],{"class":100}," df ",[43,1850,1851],{"class":96},"if",[43,1853,1854],{"class":100}," where ",[43,1856,1857],{"class":96},"is",[43,1859,1860],{"class":186}," None",[43,1862,1863],{"class":96}," else",[43,1865,1866],{"class":100}," df[where(df)]\n",[43,1868,1869,1871,1873,1876,1878,1880,1882],{"class":45,"line":117},[43,1870,172],{"class":100},[43,1872,123],{"class":96},[43,1874,1875],{"class":100}," data.groupby(by, ",[43,1877,181],{"class":180},[43,1879,123],{"class":96},[43,1881,187],{"class":186},[43,1883,1884],{"class":100},")[value].agg(func)\n",[43,1886,1887,1889,1891,1894,1896,1899,1902,1905,1907,1910],{"class":45,"line":135},[43,1888,172],{"class":100},[43,1890,123],{"class":96},[43,1892,1893],{"class":100}," out.sort_values(value ",[43,1895,1851],{"class":96},[43,1897,1898],{"class":100}," sort_desc_by_value ",[43,1900,1901],{"class":96},"else",[43,1903,1904],{"class":100}," by, ",[43,1906,692],{"class":180},[43,1908,1909],{"class":96},"=not",[43,1911,1912],{"class":100}," sort_desc_by_value)\n",[43,1914,1915,1918],{"class":45,"line":140},[43,1916,1917],{"class":96},"    if",[43,1919,1920],{"class":100}," total:\n",[43,1922,1923,1926,1928,1931,1933,1936,1938,1941,1944,1947,1949,1951,1954,1956,1959,1961],{"class":45,"line":169},[43,1924,1925],{"class":100},"        label ",[43,1927,123],{"class":96},[43,1929,1930],{"class":100}," {by: ",[43,1932,163],{"class":53},[43,1934,1935],{"class":100},"} ",[43,1937,1851],{"class":96},[43,1939,1940],{"class":186}," isinstance",[43,1942,1943],{"class":100},"(by, ",[43,1945,1946],{"class":186},"str",[43,1948,1531],{"class":100},[43,1950,1901],{"class":96},[43,1952,1953],{"class":100}," {by[",[43,1955,281],{"class":186},[43,1957,1958],{"class":100},"]: ",[43,1960,163],{"class":53},[43,1962,1963],{"class":100},"}\n",[43,1965,1966,1969,1971,1974,1977,1980,1982,1984,1986],{"class":45,"line":193},[43,1967,1968],{"class":100},"        out ",[43,1970,123],{"class":96},[43,1972,1973],{"class":100}," pd.concat([out, pd.DataFrame([{",[43,1975,1976],{"class":96},"**",[43,1978,1979],{"class":100},"label, value: data[value].agg(func)}])], ",[43,1981,213],{"class":180},[43,1983,123],{"class":96},[43,1985,218],{"class":186},[43,1987,132],{"class":100},[43,1989,1990,1992],{"class":45,"line":204},[43,1991,207],{"class":96},[43,1993,1994],{"class":100}," out\n",[43,1996,1997],{"class":45,"line":223},[43,1998,114],{"emptyLinePlaceholder":113},[43,2000,2001,2003,2006,2008,2010,2012,2014,2016,2019,2021,2023,2025,2027,2029],{"class":45,"line":228},[43,2002,231],{"class":186},[43,2004,2005],{"class":100},"(excel_groupby(sales, ",[43,2007,979],{"class":53},[43,2009,240],{"class":100},[43,2011,243],{"class":53},[43,2013,240],{"class":100},[43,2015,805],{"class":53},[43,2017,2018],{"class":100},").round(",[43,2020,337],{"class":186},[43,2022,986],{"class":100},[43,2024,712],{"class":180},[43,2026,123],{"class":96},[43,2028,187],{"class":186},[43,2030,246],{"class":100},[43,2032,2033,2035,2037,2039,2041,2043,2045,2048,2050,2052],{"class":45,"line":929},[43,2034,231],{"class":186},[43,2036,2005],{"class":100},[43,2038,237],{"class":53},[43,2040,240],{"class":100},[43,2042,243],{"class":53},[43,2044,240],{"class":100},[43,2046,2047],{"class":180},"sort_desc_by_value",[43,2049,123],{"class":96},[43,2051,218],{"class":186},[43,2053,1057],{"class":100},[43,2055,2056,2059,2062,2065,2067,2069,2071,2074,2076,2078,2080,2082],{"class":45,"line":946},[43,2057,2058],{"class":180},"                    where",[43,2060,2061],{"class":96},"=lambda",[43,2063,2064],{"class":100}," d: d[",[43,2066,979],{"class":53},[43,2068,639],{"class":100},[43,2070,642],{"class":96},[43,2072,2073],{"class":53}," \"Garden\"",[43,2075,986],{"class":100},[43,2077,712],{"class":180},[43,2079,123],{"class":96},[43,2081,187],{"class":186},[43,2083,246],{"class":100},[10,2085,2086,2087,2090,2091,2094,2095,27],{},"The first call is ",[14,2088,2089],{},"GROUPBY(B2:B13, D2:D13, AVERAGE)",": average sales per category with an overall average of all rows underneath. The second is a filtered, value-sorted summary of garden sales by region, ",[14,2092,2093],{},"GROUPBY(A2:A13, D2:D13, SUM, , , -2, B2:B13=\"Garden\")",". Passing the filter as a function of the DataFrame keeps the helper reusable across tables. Each summary can then be written to its own sheet in one loop, which is the shape most monthly packs take — see ",[23,2096,2098],{"href":2097},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fwrite-multiple-dataframes-to-one-excel-file\u002F","Write Multiple DataFrames to One Excel File",[29,2100,2102],{"id":2101},"performance-notes","Performance notes",[10,2104,2105,1322,2108,2111,2112,2115,2116,2118,2119,2121],{},[14,2106,2107],{},"groupby",[14,2109,2110],{},"pivot_table"," handle millions of rows in well under a second for sums and counts; the Python-level loop in ",[14,2113,2114],{},"two_level"," runs once per outer group, which is fine for regions and products and slow only for tens of thousands of groups — for those, compute subtotals with a second ",[14,2117,2107],{}," on the outer key and merge. In the spreadsheet, by contrast, each ",[14,2120,69],{}," recalculates whenever its source range changes, so a workbook with many of them over a large table becomes sluggish; moving the summaries into the scheduled Python job is often what prompts the conversion in the first place.",[29,2123,2125],{"id":2124},"common-pitfalls","Common pitfalls",[2127,2128,2129,2143,2151,2160,2172],"ul",{},[2130,2131,2132,2136,2137,240,2139,2142],"li",{},[2133,2134,2135],"strong",{},"Averaging averages."," Compute totals for ",[14,2138,256],{},[14,2140,2141],{},"MEDIAN"," and the like from the rows, not from group results.",[2130,2144,2145,1492,2148,2150],{},[2133,2146,2147],{},"Filtering after grouping.",[14,2149,617],{}," removes rows before aggregation; filtering the summary changes nothing about the total.",[2130,2152,2153,2156,2157,2159],{},[2133,2154,2155],{},"Missing combinations."," Use ",[14,2158,1111],{}," only where \"no rows\" really means zero.",[2130,2161,2162,2165,2166,1322,2168,2171],{},[2133,2163,2164],{},"Mixed-type keys."," A region typed as ",[14,2167,374],{},[14,2169,2170],{},"North "," forms two groups in both tools; strip text first.",[2130,2173,2174,1492,2177,1322,2179,2181,2182,2184],{},[2133,2175,2176],{},"Shipping live formulas.",[14,2178,69],{},[14,2180,1325],{}," show ",[14,2183,1498],{}," outside Excel 365.",[29,2186,2188],{"id":2187},"conclusion","Conclusion",[10,2190,2191,2193,2194,2197,2198,1322,2200,2202,2203,2206,2207,2209,2210,2193,2212,2214,2215,2217,2218,2220,2221,2224],{},[14,2192,69],{}," is ",[14,2195,2196],{},"groupby().agg()"," plus a total row computed from the underlying data; its ",[14,2199,609],{},[14,2201,617],{}," arguments are ",[14,2204,2205],{},"sort_values"," and a boolean filter applied first; several functions are named aggregations; and ",[14,2208,753],{}," is a subtotal per outer group. ",[14,2211,1325],{},[14,2213,2110],{}," with ",[14,2216,1107],{},", and ",[14,2219,1318],{}," is a division by the total or ",[14,2222,2223],{},"crosstab(normalize=...)",". Write the results as values, because workbooks that depend on these functions only calculate in Excel 365.",[29,2226,2228],{"id":2227},"frequently-asked-questions","Frequently asked questions",[10,2230,2231,2234],{},[2133,2232,2233],{},"What is the pandas equivalent of Excel GROUPBY?","\nGroup by the row field, select the value column and aggregate it with the function, sort by the row field, with a total row appended that is calculated from all rows. Use named aggregations for several functions at once.",[10,2236,2237,2240],{},[2133,2238,2239],{},"What is the pandas equivalent of Excel PIVOTBY?","\npd.pivot_table(df, index=row_field, columns=col_field, values=value, aggfunc=function, margins=True). The margins add the row and column totals that PIVOTBY shows by default.",[10,2242,2243,2246,2247,2253],{},[2133,2244,2245],{},"How do I replicate GROUPBY's filter_array in pandas?","\nFilter the DataFrame with a boolean condition before grouping, for example df",[43,2248,2249,2250,2252],{},"df",[43,2251,636],{}," == \"Q2\"",".groupby(\"Region\"). Excluded rows then do not contribute to the groups or the total.",[10,2255,2256,2259],{},[2133,2257,2258],{},"Do GROUPBY and PIVOTBY work in older Excel or LibreOffice?","\nNo. They are Excel 365 functions; older Excel versions show #NAME?, and LibreOffice did not evaluate them in testing. Write the summary as values from pandas for files that others open.",[29,2261,2263],{"id":2262},"related","Related",[2127,2265,2266,2272,2279,2286],{},[2130,2267,2268,2269,2271],{},"Up one level: ",[23,2270,26],{"href":25}," — Excel functions and their pandas counterparts.",[2130,2273,2274,2278],{},[23,2275,2277],{"href":2276},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcreate-pivot-table-from-excel-with-pandas\u002F","Create a Pivot Table from Excel with pandas"," — pivot_table in depth.",[2130,2280,2281,2285],{},[23,2282,2284],{"href":2283},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fsumif-and-sumifs-equivalent-in-pandas\u002F","SUMIF and SUMIFS Equivalent in pandas"," — single conditional totals.",[2130,2287,2288,2292],{},[23,2289,2291],{"href":2290},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Funique-sort-and-filter-equivalents-in-pandas\u002F","UNIQUE, SORT and FILTER Equivalents in pandas"," — the other dynamic array functions.",[2294,2295,2296],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":39,"searchDepth":110,"depth":110,"links":2298},[2299,2300,2301,2302,2303,2304,2305,2306,2307,2308,2309,2310,2311],{"id":31,"depth":110,"text":32},{"id":73,"depth":110,"text":74},{"id":602,"depth":110,"text":603},{"id":740,"depth":110,"text":741},{"id":1008,"depth":110,"text":1009},{"id":1311,"depth":110,"text":1312},{"id":1484,"depth":110,"text":1485},{"id":1790,"depth":110,"text":1791},{"id":2101,"depth":110,"text":2102},{"id":2124,"depth":110,"text":2125},{"id":2187,"depth":110,"text":2188},{"id":2227,"depth":110,"text":2228},{"id":2262,"depth":110,"text":2263},"2026-10-02","Excel 365's GROUPBY and PIVOTBY functions summarise a table with one formula. Translate them to pandas: groupby with a grand total row, sorting by the value column, filtering rows before grouping, several aggregations at once, subtotals for two levels, pivot_table with row and column totals, and PERCENTOF shares — plus why a workbook that relies on them will not calculate outside Excel 365.","md",[2316,2318,2320,2322],{"q":2233,"a":2317},"Group by the row field, select the value column and aggregate it with the function, sort by the row field, with a total row appended that is calculated from all rows. Use named aggregations for several functions at once.",{"q":2239,"a":2319},"pd.pivot_table(df, index=row_field, columns=col_field, values=value, aggfunc=function, margins=True). The margins add the row and column totals that PIVOTBY shows by default.",{"q":2245,"a":2321},"Filter the DataFrame with a boolean condition before grouping, for example df[df[\"Quarter\"] == \"Q2\"].groupby(\"Region\"). Excluded rows then do not contribute to the groups or the total.",{"q":2258,"a":2323},"No. They are Excel 365 functions; older Excel versions show #NAME?, and LibreOffice did not evaluate them in testing. Write the summary as values from pandas for files that others open.","groupby and pivotby function equivalents in pandas",null,{"breadcrumb":2327},[2328,2330,2333],{"name":2329,"item":1360},"Home",{"name":2331,"item":2332},"Advanced Data Transformation and Cleaning for Excel Automation","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":26,"item":25},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fgroupby-and-pivotby-function-equivalents-in-pandas",{"title":2336,"description":2337},"Excel GROUPBY and PIVOTBY Equivalents in pandas","pandas versions of Excel GROUPBY and PIVOTBY: groupby + total row, sort_order and filter_array, multiple functions, subtotal depth, pivot_table margins for PIVOTBY, PERCENTOF with normalize, and compatibility limits of the Excel functions.","groupby-and-pivotby-function-equivalents-in-pandas","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fgroupby-and-pivotby-function-equivalents-in-pandas\u002Findex",[2341,2342],"formula-equivalents","pivots-and-summaries","how-to","aXiFBAVQaRcQ7u1amjOXvBi344LPaQwqtqZi2y9EuWA",[2346,2350],{"title":2347,"path":2348,"stem":2349,"children":-1},"Goal Seek in Python Instead of Excel","\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fgoal-seek-in-python-instead-of-excel","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fgoal-seek-in-python-instead-of-excel\u002Findex",{"title":2351,"path":2352,"stem":2353,"children":-1},"IFERROR, IFNA, ISNA and ISNUMBER Equivalents in pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fiferror-and-isna-equivalents-in-pandas","advanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fiferror-and-isna-equivalents-in-pandas\u002Findex",[2355,2357],{"path":2356,"title":26},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas",{"path":2358,"title":2331},"\u002Fadvanced-data-transformation-and-cleaning",[],[2361,2364,2367,2370,2373,2375],{"path":2362,"title":2363,"kind":2343},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fvlookup-equivalent-in-pandas-for-excel-files","The VLOOKUP Equivalent in pandas for Excel Files",{"path":2365,"title":2366,"kind":2343},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fextract-numbers-and-codes-from-text-with-pandas","Extract Numbers and Codes from Text with pandas",{"path":2368,"title":2369,"kind":2343},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fsplit-full-names-into-first-and-last-names-with-pandas","Split Full Names into First and Last Names with pandas",{"path":2371,"title":2372,"kind":2343},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fbin-values-into-ranges-with-pandas-cut","Bin Values into Ranges Like Excel FREQUENCY with pd.cut",{"path":2374,"title":1481,"kind":2343},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcalculate-percent-of-total-per-group-in-pandas",{"path":2376,"title":2377,"kind":2343},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcount-distinct-values-per-group-in-pandas","Count Distinct Values per Group in pandas",1790972088248]