[{"data":1,"prerenderedAt":1716},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffill-missing-dates-in-a-time-series-with-pandas":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffill-missing-dates-in-a-time-series-with-pandas":1683,"ancestors:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffill-missing-dates-in-a-time-series-with-pandas":1692,"children:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffill-missing-dates-in-a-time-series-with-pandas":1697,"related:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffill-missing-dates-in-a-time-series-with-pandas":1698},{"id":4,"title":5,"body":6,"dateModified":1648,"datePublished":1648,"description":1649,"extension":1650,"faq":1651,"keyword":1660,"kind":1661,"meta":1662,"navigation":235,"path":1671,"seo":1672,"slug":1675,"stem":1676,"topics":1677,"type":1681,"__hash__":1682},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffill-missing-dates-in-a-time-series-with-pandas\u002Findex.md","Fill Missing Dates in a Time Series from Excel with pandas",{"type":7,"value":8,"toc":1632},"minimark",[9,28,155,160,188,200,204,207,376,383,387,398,487,497,501,507,564,567,682,686,693,858,876,880,883,1036,1043,1047,1054,1100,1112,1116,1119,1256,1264,1378,1382,1385,1474,1482,1486,1540,1544,1554,1558,1567,1571,1577,1583,1589,1595,1599,1628],[10,11,12,13,17,18,21,22,27],"p",{},"Operational exports usually record events, not days. A store that took no orders on Wednesday simply has no Wednesday row. That seems harmless until the data is charted — the line jumps from Tuesday to Thursday — or averaged, when a \"seven-day average\" is taken over five days and comes out too high. Before anything that assumes regular periods, the missing dates need to exist as rows, and each column needs the right kind of fill: a missing day of ",[14,15,16],"em",{},"orders"," is zero, a missing day of ",[14,19,20],{},"stock"," is yesterday's stock. This page, part of ",[23,24,26],"a",{"href":25},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002F","Working with Dates and Times in Excel Data",", fills gaps in a daily sheet, per store, and handles the duplicate dates that make the obvious method fail.",[29,30,38,39,38,43,38,47,38,55,38,63,38,69,38,73,38,77,38,80,38,83,38,86,38,90,38,94,38,100,38,104,38,108,38,112,38,116,38,123,38,129,38,132,38,134,38,136,38,138,38,143,38,150],"svg",{"viewBox":31,"role":32,"ariaLabelledBy":33,"xmlns":36,"style":37},"4 0 748 212","img",[34,35],"gap-chart-t","gap-chart-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:748px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[40,41,42],"title",{"id":34},"North store orders before and after filling the gap",[44,45,46],"desc",{"id":35},"A line chart of daily orders for the North store from 2 to 6 March. The raw data has no row for 4 March, so the line runs straight from 3 on the 3rd to 4 on the 5th. After filling, 4 March has 0 orders and the line drops to the axis for that day.",[48,49],"rect",{"x":50,"y":51,"width":52,"height":53,"fill":54},"4","0","748","212","#ffffff",[56,57],"line",{"x1":58,"y1":59,"x2":60,"y2":59,"stroke":61,"style":62},"39.5","174.0","736.0","var(--line,#cdd5e6)","stroke-width:1px",[64,65,51],"text",{"x":66,"y":67,"style":68},"23.7","178.0","font-size:11px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle",[56,70],{"x1":58,"y1":71,"x2":60,"y2":71,"stroke":61,"style":72},"124.0","stroke-width:1px;stroke-dasharray:5 4",[64,74,76],{"x":66,"y":75,"style":68},"128.0","2",[56,78],{"x1":58,"y1":79,"x2":60,"y2":79,"stroke":61,"style":72},"74.0",[64,81,50],{"x":66,"y":82,"style":68},"78.0",[56,84],{"x1":58,"y1":85,"x2":60,"y2":85,"stroke":61,"style":72},"24.0",[64,87,89],{"x":66,"y":88,"style":68},"28.0","6",[56,91],{"x1":58,"y1":59,"x2":60,"y2":59,"stroke":92,"style":93},"var(--muted,#5b6780)","stroke-width:1.5px",[64,95,99],{"x":96,"y":97,"style":98},"109.1","192.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Mar 2",[64,101,103],{"x":102,"y":97,"style":98},"248.4","Mar 3",[64,105,107],{"x":106,"y":97,"style":98},"387.7","Mar 4",[64,109,111],{"x":110,"y":97,"style":98},"527.0","Mar 5",[64,113,115],{"x":114,"y":97,"style":98},"666.3","Mar 6",[117,118],"path",{"d":119,"fill":120,"stroke":121,"style":122},"M109.1,49.0 L248.4,99.0 L387.7,174.0 L527.0,74.0 L666.3,124.0","none","var(--teal,#0f9488)","stroke-width:2.5px;stroke-linejoin:round;stroke-linecap:round",[124,125],"circle",{"cx":96,"cy":126,"r":127,"fill":54,"stroke":121,"style":128},"49.0","4.0","stroke-width:2px",[124,130],{"cx":102,"cy":131,"r":127,"fill":54,"stroke":121,"style":128},"99.0",[124,133],{"cx":106,"cy":59,"r":127,"fill":54,"stroke":121,"style":128},[124,135],{"cx":110,"cy":79,"r":127,"fill":54,"stroke":121,"style":128},[124,137],{"cx":114,"cy":71,"r":127,"fill":54,"stroke":121,"style":128},[56,139],{"x1":106,"y1":140,"x2":106,"y2":59,"stroke":141,"style":142},"20.0","var(--accent,#f43f8f)","stroke-width:1.5px;stroke-dasharray:5 4",[48,144],{"x":145,"y":146,"width":147,"height":140,"rx":148,"fill":149,"stroke":141,"style":62},"323.6","8.0","128.3","10","#fee8f2",[64,151,154],{"x":106,"y":152,"style":153},"22.0","font-size:11px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","no row in the export",[156,157,159],"h2",{"id":158},"prerequisites","Prerequisites",[161,162,167],"pre",{"className":163,"code":164,"language":165,"meta":166,"style":166},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[168,169,170],"code",{"__ignoreMap":166},[171,172,174,178,182,185],"span",{"class":56,"line":173},1,[171,175,177],{"class":176},"sMTad","pip",[171,179,181],{"class":180},"srMev"," install",[171,183,184],{"class":180}," pandas",[171,186,187],{"class":180}," openpyxl\n",[10,189,190,193,194,196,197,199],{},[168,191,192],{},"daily.xlsx"," has seven rows for two stores over 2–6 March 2026: North is missing 4 March; South only has 2 and 6 March, and 6 March appears twice because two orders were exported separately. ",[168,195,16],{}," is a count per day; ",[168,198,20],{}," is the closing stock level.",[156,201,203],{"id":202},"step-1-aggregate-duplicate-dates-first","Step 1 — Aggregate duplicate dates first",[10,205,206],{},"Reindexing needs each date to appear once per store. Check for duplicates, and combine them with the right aggregation per column — orders are summed, stock is a level, so take the last value of the day:",[161,208,212],{"className":209,"code":210,"language":211,"meta":166,"style":166},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\ndaily = pd.read_excel(\"daily.xlsx\")\nprint(daily.duplicated([\"store\", \"date\"]).sum(), \"duplicate store-days\")\n\ndaily = (daily.groupby([\"store\", \"date\"], as_index=False)\n              .agg(orders=(\"orders\", \"sum\"), stock=(\"stock\", \"last\")))\nprint(daily.to_string(index=False))\n","python",[168,213,214,230,237,255,282,287,317,358],{"__ignoreMap":166},[171,215,216,220,224,227],{"class":56,"line":173},[171,217,219],{"class":218},"s-kum","import",[171,221,223],{"class":222},"skGVy"," pandas ",[171,225,226],{"class":218},"as",[171,228,229],{"class":222}," pd\n",[171,231,233],{"class":56,"line":232},2,[171,234,236],{"emptyLinePlaceholder":235},true,"\n",[171,238,240,243,246,249,252],{"class":56,"line":239},3,[171,241,242],{"class":222},"daily ",[171,244,245],{"class":218},"=",[171,247,248],{"class":222}," pd.read_excel(",[171,250,251],{"class":180},"\"daily.xlsx\"",[171,253,254],{"class":222},")\n",[171,256,258,262,265,268,271,274,277,280],{"class":56,"line":257},4,[171,259,261],{"class":260},"sP0c6","print",[171,263,264],{"class":222},"(daily.duplicated([",[171,266,267],{"class":180},"\"store\"",[171,269,270],{"class":222},", ",[171,272,273],{"class":180},"\"date\"",[171,275,276],{"class":222},"]).sum(), ",[171,278,279],{"class":180},"\"duplicate store-days\"",[171,281,254],{"class":222},[171,283,285],{"class":56,"line":284},5,[171,286,236],{"emptyLinePlaceholder":235},[171,288,290,292,294,297,299,301,303,306,310,312,315],{"class":56,"line":289},6,[171,291,242],{"class":222},[171,293,245],{"class":218},[171,295,296],{"class":222}," (daily.groupby([",[171,298,267],{"class":180},[171,300,270],{"class":222},[171,302,273],{"class":180},[171,304,305],{"class":222},"], ",[171,307,309],{"class":308},"sa561","as_index",[171,311,245],{"class":218},[171,313,314],{"class":260},"False",[171,316,254],{"class":222},[171,318,320,323,325,327,330,333,335,338,341,343,345,347,350,352,355],{"class":56,"line":319},7,[171,321,322],{"class":222},"              .agg(",[171,324,16],{"class":308},[171,326,245],{"class":218},[171,328,329],{"class":222},"(",[171,331,332],{"class":180},"\"orders\"",[171,334,270],{"class":222},[171,336,337],{"class":180},"\"sum\"",[171,339,340],{"class":222},"), ",[171,342,20],{"class":308},[171,344,245],{"class":218},[171,346,329],{"class":222},[171,348,349],{"class":180},"\"stock\"",[171,351,270],{"class":222},[171,353,354],{"class":180},"\"last\"",[171,356,357],{"class":222},")))\n",[171,359,361,363,366,369,371,373],{"class":56,"line":360},8,[171,362,261],{"class":260},[171,364,365],{"class":222},"(daily.to_string(",[171,367,368],{"class":308},"index",[171,370,245],{"class":218},[171,372,314],{"class":260},[171,374,375],{"class":222},"))\n",[10,377,378,379,382],{},"Skipping this step is the source of the error ",[168,380,381],{},"cannot reindex on an axis with duplicate labels",". Choosing the aggregation deliberately matters as much as removing the duplicates: summing a stock level would double it.",[156,384,386],{"id":385},"step-2-reindex-one-series-onto-a-full-date-range","Step 2 — Reindex one series onto a full date range",[10,388,389,390,393,394,397],{},"For a single store, set the date as the index and reindex onto ",[168,391,392],{},"pd.date_range"," from the first to the last date. Every date in the range gets a row; dates that were missing get ",[168,395,396],{},"NaN",":",[161,399,401],{"className":209,"code":400,"language":211,"meta":166,"style":166},"north = daily[daily[\"store\"] == \"North\"].set_index(\"date\")[[\"orders\", \"stock\"]]\nfull = pd.date_range(north.index.min(), north.index.max(), freq=\"D\")\nnorth = north.reindex(full)\nnorth.index.name = \"date\"\nprint(north)\n",[168,402,403,441,461,470,480],{"__ignoreMap":166},[171,404,405,408,410,413,415,418,421,424,427,429,432,434,436,438],{"class":56,"line":173},[171,406,407],{"class":222},"north ",[171,409,245],{"class":218},[171,411,412],{"class":222}," daily[daily[",[171,414,267],{"class":180},[171,416,417],{"class":222},"] ",[171,419,420],{"class":218},"==",[171,422,423],{"class":180}," \"North\"",[171,425,426],{"class":222},"].set_index(",[171,428,273],{"class":180},[171,430,431],{"class":222},")[[",[171,433,332],{"class":180},[171,435,270],{"class":222},[171,437,349],{"class":180},[171,439,440],{"class":222},"]]\n",[171,442,443,446,448,451,454,456,459],{"class":56,"line":232},[171,444,445],{"class":222},"full ",[171,447,245],{"class":218},[171,449,450],{"class":222}," pd.date_range(north.index.min(), north.index.max(), ",[171,452,453],{"class":308},"freq",[171,455,245],{"class":218},[171,457,458],{"class":180},"\"D\"",[171,460,254],{"class":222},[171,462,463,465,467],{"class":56,"line":239},[171,464,407],{"class":222},[171,466,245],{"class":218},[171,468,469],{"class":222}," north.reindex(full)\n",[171,471,472,475,477],{"class":56,"line":257},[171,473,474],{"class":222},"north.index.name ",[171,476,245],{"class":218},[171,478,479],{"class":180}," \"date\"\n",[171,481,482,484],{"class":56,"line":284},[171,483,261],{"class":260},[171,485,486],{"class":222},"(north)\n",[10,488,489,490,492,493,496],{},"4 March now exists, with ",[168,491,396],{}," in both columns. The range could equally be a fixed reporting period — ",[168,494,495],{},"pd.date_range(\"2026-03-01\", \"2026-03-31\")"," — which also adds rows before the first and after the last recorded day, so every report has the same number of rows whatever happened that month.",[156,498,500],{"id":499},"step-3-fill-each-column-according-to-what-it-measures","Step 3 — Fill each column according to what it measures",[10,502,503,504,506],{},"The ",[168,505,396],{},"s mean different things in different columns. No orders recorded means zero orders; no stock recorded means the stock did not change. Fill accordingly:",[161,508,510],{"className":209,"code":509,"language":211,"meta":166,"style":166},"north[\"orders\"] = north[\"orders\"].fillna(0).astype(int)\nnorth[\"stock\"] = north[\"stock\"].ffill()\nprint(north)\n",[168,511,512,541,558],{"__ignoreMap":166},[171,513,514,517,519,521,523,526,528,531,533,536,539],{"class":56,"line":173},[171,515,516],{"class":222},"north[",[171,518,332],{"class":180},[171,520,417],{"class":222},[171,522,245],{"class":218},[171,524,525],{"class":222}," north[",[171,527,332],{"class":180},[171,529,530],{"class":222},"].fillna(",[171,532,51],{"class":260},[171,534,535],{"class":222},").astype(",[171,537,538],{"class":260},"int",[171,540,254],{"class":222},[171,542,543,545,547,549,551,553,555],{"class":56,"line":232},[171,544,516],{"class":222},[171,546,349],{"class":180},[171,548,417],{"class":222},[171,550,245],{"class":218},[171,552,525],{"class":222},[171,554,349],{"class":180},[171,556,557],{"class":222},"].ffill()\n",[171,559,560,562],{"class":56,"line":239},[171,561,261],{"class":260},[171,563,486],{"class":222},[10,565,566],{},"Orders on 4 March are 0 and stock is carried forward from 3 March. Getting this wrong in either direction produces plausible-looking errors: forward-filling orders invents sales on days the store was closed, and zero-filling stock makes inventory vanish for a day and reappear.",[29,568,38,574,38,577,38,580,38,584,38,589,38,596,38,602,38,605,38,609,38,614,38,619,38,621,38,624,38,626,38,629,38,632,38,636,38,638,38,642,38,644,38,647,38,649,38,653,38,655,38,659,38,661,38,664,38,667,38,671,38,673,38,677,38,679],{"viewBox":569,"role":32,"ariaLabelledBy":570,"xmlns":36,"style":573},"4 0 752 208",[571,572],"gap-fill-t","gap-fill-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[40,575,576],{"id":571},"How to fill a missing day, by type of column",[44,578,579],{"id":572},"A reference table. Flows such as orders, sales and visits are filled with zero, because no record means nothing happened. Levels such as stock, balances and headcount are forward-filled with the last known value. Rates and prices are forward-filled or left missing, depending on whether the last value still applies. Measurements such as temperature are interpolated or left missing.",[48,581],{"x":50,"y":51,"width":582,"height":583,"fill":54},"752","208",[48,585],{"x":140,"y":586,"width":587,"height":588,"rx":51,"fill":54,"stroke":61,"style":62},"16.0","247.5","40.0",[48,590],{"x":591,"y":586,"width":592,"height":588,"rx":51,"fill":593,"stroke":594,"style":595},"267.5","236.3","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.2px",[64,597,601],{"x":598,"y":599,"style":600},"385.6","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Fill with",[48,603],{"x":604,"y":586,"width":592,"height":588,"rx":51,"fill":593,"stroke":594,"style":595},"503.7",[64,606,608],{"x":607,"y":599,"style":600},"621.9","Why",[48,610],{"x":140,"y":611,"width":587,"height":612,"rx":51,"fill":613,"stroke":61,"style":62},"56.0","34.0","#e7ebef",[64,615,618],{"x":616,"y":82,"style":617},"32.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","Flows: orders, sales, visits",[48,620],{"x":591,"y":611,"width":592,"height":612,"rx":51,"fill":54,"stroke":61,"style":62},[64,622,51],{"x":598,"y":623,"style":98},"77.0",[48,625],{"x":604,"y":611,"width":592,"height":612,"rx":51,"fill":54,"stroke":61,"style":62},[64,627,628],{"x":607,"y":623,"style":98},"no record means none happened",[48,630],{"x":140,"y":631,"width":587,"height":612,"rx":51,"fill":613,"stroke":61,"style":62},"90.0",[64,633,635],{"x":616,"y":634,"style":617},"112.0","Levels: stock, balance, headcount",[48,637],{"x":591,"y":631,"width":592,"height":612,"rx":51,"fill":54,"stroke":61,"style":62},[64,639,641],{"x":598,"y":640,"style":98},"111.0","last value (ffill)",[48,643],{"x":604,"y":631,"width":592,"height":612,"rx":51,"fill":54,"stroke":61,"style":62},[64,645,646],{"x":607,"y":640,"style":98},"unchanged until a new record",[48,648],{"x":140,"y":71,"width":587,"height":612,"rx":51,"fill":613,"stroke":61,"style":62},[64,650,652],{"x":616,"y":651,"style":617},"146.0","Rates and prices",[48,654],{"x":591,"y":71,"width":592,"height":612,"rx":51,"fill":54,"stroke":61,"style":62},[64,656,658],{"x":598,"y":657,"style":98},"145.0","ffill or leave missing",[48,660],{"x":604,"y":71,"width":592,"height":612,"rx":51,"fill":54,"stroke":61,"style":62},[64,662,663],{"x":607,"y":657,"style":98},"if the last value still applies",[48,665],{"x":140,"y":666,"width":587,"height":612,"rx":51,"fill":613,"stroke":61,"style":62},"158.0",[64,668,670],{"x":616,"y":669,"style":617},"180.0","Measurements",[48,672],{"x":591,"y":666,"width":592,"height":612,"rx":51,"fill":54,"stroke":61,"style":62},[64,674,676],{"x":598,"y":675,"style":98},"179.0","interpolate or leave missing",[48,678],{"x":604,"y":666,"width":592,"height":612,"rx":51,"fill":54,"stroke":61,"style":62},[64,680,681],{"x":607,"y":675,"style":98},"the true value is unknown",[156,683,685],{"id":684},"step-4-fill-gaps-for-every-store-at-once","Step 4 — Fill gaps for every store at once",[10,687,688,689,692],{},"With several stores, build the complete grid of every store and every date, then reindex the store-date index onto it. ",[168,690,691],{},"MultiIndex.from_product"," creates the grid:",[161,694,696],{"className":209,"code":695,"language":211,"meta":166,"style":166},"dates = pd.date_range(daily[\"date\"].min(), daily[\"date\"].max(), freq=\"D\")\ngrid = pd.MultiIndex.from_product([daily[\"store\"].unique(), dates], names=[\"store\", \"date\"])\n\nfilled = daily.set_index([\"store\", \"date\"]).reindex(grid)\nfilled[\"orders\"] = filled[\"orders\"].fillna(0).astype(int)\nfilled[\"stock\"] = filled.groupby(level=\"store\")[\"stock\"].ffill()\nfilled = filled.reset_index()\nprint(filled.to_string(index=False))\n",[168,697,698,726,758,762,781,807,834,843],{"__ignoreMap":166},[171,699,700,703,705,708,710,713,715,718,720,722,724],{"class":56,"line":173},[171,701,702],{"class":222},"dates ",[171,704,245],{"class":218},[171,706,707],{"class":222}," pd.date_range(daily[",[171,709,273],{"class":180},[171,711,712],{"class":222},"].min(), daily[",[171,714,273],{"class":180},[171,716,717],{"class":222},"].max(), ",[171,719,453],{"class":308},[171,721,245],{"class":218},[171,723,458],{"class":180},[171,725,254],{"class":222},[171,727,728,731,733,736,738,741,744,746,749,751,753,755],{"class":56,"line":232},[171,729,730],{"class":222},"grid ",[171,732,245],{"class":218},[171,734,735],{"class":222}," pd.MultiIndex.from_product([daily[",[171,737,267],{"class":180},[171,739,740],{"class":222},"].unique(), dates], ",[171,742,743],{"class":308},"names",[171,745,245],{"class":218},[171,747,748],{"class":222},"[",[171,750,267],{"class":180},[171,752,270],{"class":222},[171,754,273],{"class":180},[171,756,757],{"class":222},"])\n",[171,759,760],{"class":56,"line":239},[171,761,236],{"emptyLinePlaceholder":235},[171,763,764,767,769,772,774,776,778],{"class":56,"line":257},[171,765,766],{"class":222},"filled ",[171,768,245],{"class":218},[171,770,771],{"class":222}," daily.set_index([",[171,773,267],{"class":180},[171,775,270],{"class":222},[171,777,273],{"class":180},[171,779,780],{"class":222},"]).reindex(grid)\n",[171,782,783,786,788,790,792,795,797,799,801,803,805],{"class":56,"line":284},[171,784,785],{"class":222},"filled[",[171,787,332],{"class":180},[171,789,417],{"class":222},[171,791,245],{"class":218},[171,793,794],{"class":222}," filled[",[171,796,332],{"class":180},[171,798,530],{"class":222},[171,800,51],{"class":260},[171,802,535],{"class":222},[171,804,538],{"class":260},[171,806,254],{"class":222},[171,808,809,811,813,815,817,820,823,825,827,830,832],{"class":56,"line":289},[171,810,785],{"class":222},[171,812,349],{"class":180},[171,814,417],{"class":222},[171,816,245],{"class":218},[171,818,819],{"class":222}," filled.groupby(",[171,821,822],{"class":308},"level",[171,824,245],{"class":218},[171,826,267],{"class":180},[171,828,829],{"class":222},")[",[171,831,349],{"class":180},[171,833,557],{"class":222},[171,835,836,838,840],{"class":56,"line":319},[171,837,766],{"class":222},[171,839,245],{"class":218},[171,841,842],{"class":222}," filled.reset_index()\n",[171,844,845,847,850,852,854,856],{"class":56,"line":360},[171,846,261],{"class":260},[171,848,849],{"class":222},"(filled.to_string(",[171,851,368],{"class":308},[171,853,245],{"class":218},[171,855,314],{"class":260},[171,857,375],{"class":222},[10,859,860,861,864,865,868,869,872,873,875],{},"South now has a row for every day from 2 to 6 March, with zero orders on the quiet days. Forward-filling ",[14,862,863],{},"within each store"," matters: a plain ",[168,866,867],{},"ffill()"," over the whole frame would carry North's last stock value into South's first missing day. For stock before a store's first record, ",[168,870,871],{},"ffill"," leaves ",[168,874,396],{},", which is honest — the level is unknown.",[156,877,879],{"id":878},"keep-track-of-which-rows-were-added","Keep track of which rows were added",[10,881,882],{},"A filled zero and a recorded zero look identical, but they mean different things: one is \"the export had no row\", the other is \"the system recorded zero\". When the difference matters — a store that was closed versus one whose feed failed — record it before filling:",[161,884,886],{"className":209,"code":885,"language":211,"meta":166,"style":166},"marked = daily.set_index([\"store\", \"date\"]).reindex(grid)\nmarked[\"recorded\"] = marked[\"orders\"].notna()\nmarked[\"orders\"] = marked[\"orders\"].fillna(0).astype(int)\nmarked[\"stock\"] = marked.groupby(level=\"store\")[\"stock\"].ffill()\nmarked = marked.reset_index()\nprint(marked.groupby(\"store\")[\"recorded\"].agg([\"sum\", \"size\"]).rename(columns={\"sum\": \"recorded\", \"size\": \"days\"}))\n",[168,887,888,905,925,949,974,983],{"__ignoreMap":166},[171,889,890,893,895,897,899,901,903],{"class":56,"line":173},[171,891,892],{"class":222},"marked ",[171,894,245],{"class":218},[171,896,771],{"class":222},[171,898,267],{"class":180},[171,900,270],{"class":222},[171,902,273],{"class":180},[171,904,780],{"class":222},[171,906,907,910,913,915,917,920,922],{"class":56,"line":232},[171,908,909],{"class":222},"marked[",[171,911,912],{"class":180},"\"recorded\"",[171,914,417],{"class":222},[171,916,245],{"class":218},[171,918,919],{"class":222}," marked[",[171,921,332],{"class":180},[171,923,924],{"class":222},"].notna()\n",[171,926,927,929,931,933,935,937,939,941,943,945,947],{"class":56,"line":239},[171,928,909],{"class":222},[171,930,332],{"class":180},[171,932,417],{"class":222},[171,934,245],{"class":218},[171,936,919],{"class":222},[171,938,332],{"class":180},[171,940,530],{"class":222},[171,942,51],{"class":260},[171,944,535],{"class":222},[171,946,538],{"class":260},[171,948,254],{"class":222},[171,950,951,953,955,957,959,962,964,966,968,970,972],{"class":56,"line":257},[171,952,909],{"class":222},[171,954,349],{"class":180},[171,956,417],{"class":222},[171,958,245],{"class":218},[171,960,961],{"class":222}," marked.groupby(",[171,963,822],{"class":308},[171,965,245],{"class":218},[171,967,267],{"class":180},[171,969,829],{"class":222},[171,971,349],{"class":180},[171,973,557],{"class":222},[171,975,976,978,980],{"class":56,"line":284},[171,977,892],{"class":222},[171,979,245],{"class":218},[171,981,982],{"class":222}," marked.reset_index()\n",[171,984,985,987,990,992,994,996,999,1001,1003,1006,1009,1012,1014,1017,1019,1022,1024,1026,1028,1030,1033],{"class":56,"line":289},[171,986,261],{"class":260},[171,988,989],{"class":222},"(marked.groupby(",[171,991,267],{"class":180},[171,993,829],{"class":222},[171,995,912],{"class":180},[171,997,998],{"class":222},"].agg([",[171,1000,337],{"class":180},[171,1002,270],{"class":222},[171,1004,1005],{"class":180},"\"size\"",[171,1007,1008],{"class":222},"]).rename(",[171,1010,1011],{"class":308},"columns",[171,1013,245],{"class":218},[171,1015,1016],{"class":222},"{",[171,1018,337],{"class":180},[171,1020,1021],{"class":222},": ",[171,1023,912],{"class":180},[171,1025,270],{"class":222},[171,1027,1005],{"class":180},[171,1029,1021],{"class":222},[171,1031,1032],{"class":180},"\"days\"",[171,1034,1035],{"class":222},"}))\n",[10,1037,1038,1039,1042],{},"North recorded four of its five days and South two. In the workbook, the ",[168,1040,1041],{},"recorded"," column can drive a light grey fill on added rows, so readers see at a glance which figures came from the source and which were inferred. A store with a long run of unrecorded days is worth a question before it goes into a trend line: a missing feed filled with zeros looks exactly like a sudden collapse in sales.",[156,1044,1046],{"id":1045},"step-5-use-business-days-when-weekends-do-not-count","Step 5 — Use business days when weekends do not count",[10,1048,1049,1050,1053],{},"For data that only exists on working days — trades, office tickets — a calendar-day range adds weekend rows full of zeros that drag averages down. Use ",[168,1051,1052],{},"freq=\"B\""," for Monday to Friday:",[161,1055,1057],{"className":209,"code":1056,"language":211,"meta":166,"style":166},"weekdays = pd.date_range(\"2026-03-02\", \"2026-03-13\", freq=\"B\")\nprint(len(weekdays), weekdays.day_name().unique().tolist())\n",[168,1058,1059,1088],{"__ignoreMap":166},[171,1060,1061,1064,1066,1069,1072,1074,1077,1079,1081,1083,1086],{"class":56,"line":173},[171,1062,1063],{"class":222},"weekdays ",[171,1065,245],{"class":218},[171,1067,1068],{"class":222}," pd.date_range(",[171,1070,1071],{"class":180},"\"2026-03-02\"",[171,1073,270],{"class":222},[171,1075,1076],{"class":180},"\"2026-03-13\"",[171,1078,270],{"class":222},[171,1080,453],{"class":308},[171,1082,245],{"class":218},[171,1084,1085],{"class":180},"\"B\"",[171,1087,254],{"class":222},[171,1089,1090,1092,1094,1097],{"class":56,"line":232},[171,1091,261],{"class":260},[171,1093,329],{"class":222},[171,1095,1096],{"class":260},"len",[171,1098,1099],{"class":222},"(weekdays), weekdays.day_name().unique().tolist())\n",[10,1101,1102,1103,1107,1108,1111],{},"Ten dates, Monday to Friday only. Public holidays need a custom calendar; ",[23,1104,1106],{"href":1105},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fcalculate-business-days-between-excel-dates-with-pandas\u002F","Calculate Business Days Between Excel Dates with pandas"," shows how to build one with ",[168,1109,1110],{},"CustomBusinessDay"," and a holiday list.",[156,1113,1115],{"id":1114},"step-6-averages-and-totals-after-filling","Step 6 — Averages and totals after filling",[10,1117,1118],{},"Filling first changes the answers, which is the point. Compare the average orders per day for South before and after:",[161,1120,1122],{"className":209,"code":1121,"language":211,"meta":166,"style":166},"south_raw = daily.loc[daily[\"store\"] == \"South\", \"orders\"].mean()\nsouth_filled = filled.loc[filled[\"store\"] == \"South\", \"orders\"].mean()\nprint(f\"raw: {south_raw:.2f} orders\u002Fday, filled: {south_filled:.2f} orders\u002Fday\")\n\nweekly = filled.set_index(\"date\").groupby(\"store\")[\"orders\"].resample(\"W-SUN\").sum()\nprint(weekly)\n",[168,1123,1124,1150,1174,1215,1219,1249],{"__ignoreMap":166},[171,1125,1126,1129,1131,1134,1136,1138,1140,1143,1145,1147],{"class":56,"line":173},[171,1127,1128],{"class":222},"south_raw ",[171,1130,245],{"class":218},[171,1132,1133],{"class":222}," daily.loc[daily[",[171,1135,267],{"class":180},[171,1137,417],{"class":222},[171,1139,420],{"class":218},[171,1141,1142],{"class":180}," \"South\"",[171,1144,270],{"class":222},[171,1146,332],{"class":180},[171,1148,1149],{"class":222},"].mean()\n",[171,1151,1152,1155,1157,1160,1162,1164,1166,1168,1170,1172],{"class":56,"line":232},[171,1153,1154],{"class":222},"south_filled ",[171,1156,245],{"class":218},[171,1158,1159],{"class":222}," filled.loc[filled[",[171,1161,267],{"class":180},[171,1163,417],{"class":222},[171,1165,420],{"class":218},[171,1167,1142],{"class":180},[171,1169,270],{"class":222},[171,1171,332],{"class":180},[171,1173,1149],{"class":222},[171,1175,1176,1178,1180,1183,1186,1189,1192,1195,1198,1201,1203,1206,1208,1210,1213],{"class":56,"line":239},[171,1177,261],{"class":260},[171,1179,329],{"class":222},[171,1181,1182],{"class":218},"f",[171,1184,1185],{"class":180},"\"raw: ",[171,1187,1016],{"class":1188},"sSjpA",[171,1190,1191],{"class":222},"south_raw",[171,1193,1194],{"class":218},":.2f",[171,1196,1197],{"class":1188},"}",[171,1199,1200],{"class":180}," orders\u002Fday, filled: ",[171,1202,1016],{"class":1188},[171,1204,1205],{"class":222},"south_filled",[171,1207,1194],{"class":218},[171,1209,1197],{"class":1188},[171,1211,1212],{"class":180}," orders\u002Fday\"",[171,1214,254],{"class":222},[171,1216,1217],{"class":56,"line":257},[171,1218,236],{"emptyLinePlaceholder":235},[171,1220,1221,1224,1226,1229,1231,1234,1236,1238,1240,1243,1246],{"class":56,"line":284},[171,1222,1223],{"class":222},"weekly ",[171,1225,245],{"class":218},[171,1227,1228],{"class":222}," filled.set_index(",[171,1230,273],{"class":180},[171,1232,1233],{"class":222},").groupby(",[171,1235,267],{"class":180},[171,1237,829],{"class":222},[171,1239,332],{"class":180},[171,1241,1242],{"class":222},"].resample(",[171,1244,1245],{"class":180},"\"W-SUN\"",[171,1247,1248],{"class":222},").sum()\n",[171,1250,1251,1253],{"class":56,"line":289},[171,1252,261],{"class":260},[171,1254,1255],{"class":222},"(weekly)\n",[10,1257,1258,1259,1263],{},"South's raw average is 5 orders a day because it counts only the two days that had orders; over the five-day period it is 2. Weekly totals do not change — zeros add nothing — which is a useful check that filling did not alter the underlying data. ",[23,1260,1262],{"href":1261},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fgroup-excel-rows-by-month-and-quarter-with-pandas\u002F","Group Excel Rows by Month and Quarter with pandas"," covers resampling and calendar grouping.",[29,1265,38,1270,38,1273,38,1276,38,1279,38,1284,38,1293,38,1298,38,1303,38,1308,38,1315,38,1319,38,1324,38,1328,38,1334,38,1338,38,1345,38,1350,38,1354,38,1358,38,1363,38,1366,38,1371,38,1373],{"viewBox":1266,"role":32,"ariaLabelledBy":1267,"xmlns":36,"style":573},"4 0 752 207",[1268,1269],"gap-avg-t","gap-avg-d",[40,1271,1272],{"id":1268},"The same South data averaged with and without the empty days",[44,1274,1275],{"id":1269},"Two panels. Averaging only the recorded rows gives 5 orders per day, because the two quiet days and the gap are ignored. Averaging after filling missing days with zero gives 2 orders per day over the five-day period. Weekly totals are 10 in both cases.",[48,1277],{"x":50,"y":51,"width":582,"height":1278,"fill":54},"207",[48,1280],{"x":140,"y":586,"width":1281,"height":1282,"rx":1283,"fill":149,"stroke":141,"style":128},"351.0","175.0","14",[1285,1286,1288],"g",{"transform":1287},"translate(182.5,29.0) scale(1.083)",[117,1289],{"d":1290,"fill":120,"stroke":1291,"style":1292},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","var(--accent-ink,#be185d)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[64,1294,1297],{"x":1295,"y":82,"style":1296},"195.5","font-size:14px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","Recorded rows only",[56,1299],{"x1":1300,"y1":1301,"x2":1302,"y2":1301,"stroke":141,"style":62},"36.0","91.0","355.0",[64,1304,1307],{"x":1305,"y":640,"style":1306},"60.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","2 rows averaged",[1285,1309,1311],{"transform":1310},"translate(38.5,99.5) scale(0.625)",[117,1312],{"d":1313,"fill":120,"stroke":141,"style":1314},"M6 6l12 12 M18 6L6 18","stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[64,1316,1318],{"x":1305,"y":1317,"style":1306},"137.0","5 orders per day",[1285,1320,1322],{"transform":1321},"translate(38.5,125.5) scale(0.625)",[117,1323],{"d":1313,"fill":120,"stroke":141,"style":1314},[64,1325,1327],{"x":1305,"y":1326,"style":1306},"163.0","weekly total 10",[1285,1329,1331],{"transform":1330},"translate(38.5,151.5) scale(0.625)",[117,1332],{"d":1290,"fill":120,"stroke":1333,"style":1314},"var(--gold,#b4740a)",[48,1335],{"x":1336,"y":586,"width":1281,"height":1282,"rx":1283,"fill":1337,"stroke":121,"style":128},"389.0","#d9f4f1",[1285,1339,1341],{"transform":1340},"translate(551.5,29.0) scale(1.083)",[117,1342],{"d":1343,"fill":120,"stroke":1344,"style":1292},"M5 12.5l4.5 4.5L19 7","var(--teal-ink,#0b6157)",[64,1346,1349],{"x":1347,"y":82,"style":1348},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","After filling missing days",[56,1351],{"x1":1352,"y1":1301,"x2":1353,"y2":1301,"stroke":121,"style":62},"405.0","724.0",[64,1355,1357],{"x":1356,"y":640,"style":1306},"429.0","5 days averaged",[1285,1359,1361],{"transform":1360},"translate(407.5,99.5) scale(0.625)",[117,1362],{"d":1343,"fill":120,"stroke":121,"style":1314},[64,1364,1365],{"x":1356,"y":1317,"style":1306},"2 orders per day",[1285,1367,1369],{"transform":1368},"translate(407.5,125.5) scale(0.625)",[117,1370],{"d":1343,"fill":120,"stroke":121,"style":1314},[64,1372,1327],{"x":1356,"y":1326,"style":1306},[1285,1374,1376],{"transform":1375},"translate(407.5,151.5) scale(0.625)",[117,1377],{"d":1343,"fill":120,"stroke":121,"style":1314},[156,1379,1381],{"id":1380},"write-the-complete-series-to-excel","Write the complete series to Excel",[10,1383,1384],{},"A filled series is the right input for a chart in the workbook, because Excel's line and column charts plot one point per row:",[161,1386,1388],{"className":209,"code":1387,"language":211,"meta":166,"style":166},"with pd.ExcelWriter(\"daily_filled.xlsx\") as writer:\n    filled.to_excel(writer, sheet_name=\"Daily\", index=False)\n    weekly.reset_index().to_excel(writer, sheet_name=\"Weekly\", index=False)\nprint(pd.read_excel(\"daily_filled.xlsx\", sheet_name=\"Daily\").shape)\n",[168,1389,1390,1409,1432,1454],{"__ignoreMap":166},[171,1391,1392,1395,1398,1401,1404,1406],{"class":56,"line":173},[171,1393,1394],{"class":218},"with",[171,1396,1397],{"class":222}," pd.ExcelWriter(",[171,1399,1400],{"class":180},"\"daily_filled.xlsx\"",[171,1402,1403],{"class":222},") ",[171,1405,226],{"class":218},[171,1407,1408],{"class":222}," writer:\n",[171,1410,1411,1414,1417,1419,1422,1424,1426,1428,1430],{"class":56,"line":232},[171,1412,1413],{"class":222},"    filled.to_excel(writer, ",[171,1415,1416],{"class":308},"sheet_name",[171,1418,245],{"class":218},[171,1420,1421],{"class":180},"\"Daily\"",[171,1423,270],{"class":222},[171,1425,368],{"class":308},[171,1427,245],{"class":218},[171,1429,314],{"class":260},[171,1431,254],{"class":222},[171,1433,1434,1437,1439,1441,1444,1446,1448,1450,1452],{"class":56,"line":239},[171,1435,1436],{"class":222},"    weekly.reset_index().to_excel(writer, ",[171,1438,1416],{"class":308},[171,1440,245],{"class":218},[171,1442,1443],{"class":180},"\"Weekly\"",[171,1445,270],{"class":222},[171,1447,368],{"class":308},[171,1449,245],{"class":218},[171,1451,314],{"class":260},[171,1453,254],{"class":222},[171,1455,1456,1458,1461,1463,1465,1467,1469,1471],{"class":56,"line":257},[171,1457,261],{"class":260},[171,1459,1460],{"class":222},"(pd.read_excel(",[171,1462,1400],{"class":180},[171,1464,270],{"class":222},[171,1466,1416],{"class":308},[171,1468,245],{"class":218},[171,1470,1421],{"class":180},[171,1472,1473],{"class":222},").shape)\n",[10,1475,1476,1477,1481],{},"Ten rows — two stores times five days — where the export had seven, and one of those seven was a duplicate. ",[23,1478,1480],{"href":1479},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-line-chart-to-excel-report-with-python\u002F","Add a Line Chart to an Excel Report with Python"," adds the chart itself.",[156,1483,1485],{"id":1484},"common-pitfalls","Common pitfalls",[1487,1488,1489,1497,1503,1512,1521,1531],"ul",{},[1490,1491,1492,1496],"li",{},[1493,1494,1495],"strong",{},"Reindexing with duplicate dates."," Aggregate to one row per key and date first.",[1490,1498,1499,1502],{},[1493,1500,1501],{},"Zero-filling levels."," Stock and balances should carry forward, not drop to zero.",[1490,1504,1505,1508,1509,1511],{},[1493,1506,1507],{},"Forward-filling across groups."," Group by store before ",[168,1510,871],{},".",[1490,1513,1514,1517,1518,1520],{},[1493,1515,1516],{},"Calendar days for working-day data."," Use ",[168,1519,1052],{}," or a custom business calendar.",[1490,1522,1523,1526,1527,1530],{},[1493,1524,1525],{},"A range that stops at the last record."," If a store's final days are quiet, ",[168,1528,1529],{},"date_range(min, max)"," ends too early; use the reporting period's fixed start and end instead.",[1490,1532,1533,1536,1537,1539],{},[1493,1534,1535],{},"Losing the difference between filled and recorded zeros."," Keep a ",[168,1538,1041],{}," flag when a missing feed and a quiet day need different responses.",[156,1541,1543],{"id":1542},"performance-notes","Performance notes",[10,1545,1546,1549,1550,1553],{},[168,1547,1548],{},"reindex"," on a ",[168,1551,1552],{},"MultiIndex"," grid is fast, but the grid has one row per group per date: a thousand stores over five years is 1.8 million rows, which is fine in memory but large for a single sheet, and slow to chart in Excel. For very sparse data, fill only the period and groups a report actually needs.",[156,1555,1557],{"id":1556},"conclusion","Conclusion",[10,1559,1560,1561,1563,1564,1566],{},"Aggregate duplicate dates with the right function per column, reindex onto a full ",[168,1562,392],{}," — or a ",[168,1565,691],{}," grid for several groups — and fill each column according to what it measures: zero for flows, forward-fill within each group for levels. Use business-day frequencies where weekends do not count, and compute averages and charts only after the missing days exist.",[156,1568,1570],{"id":1569},"frequently-asked-questions","Frequently asked questions",[10,1572,1573,1576],{},[1493,1574,1575],{},"Why should I add rows for days with no data?","\nBecause averages, rolling windows and charts assume one row per period. Without the empty days, a seven-day average is taken over fewer than seven days, and a line chart joins Tuesday straight to Thursday as if Wednesday did not exist.",[10,1578,1579,1582],{},[1493,1580,1581],{},"Should missing days be filled with zero?","\nOnly for flows — orders, sales, visits — where no row really means nothing happened. For levels such as stock or balances, a missing day means the value did not change, so carry the last known value forward instead.",[10,1584,1585,1588],{},[1493,1586,1587],{},"Why does reindex raise \"cannot reindex on an axis with duplicate labels\"?","\nSome dates appear more than once. Aggregate them first — sum the orders per date, take the last stock value — so each date is unique before reindexing.",[10,1590,1591,1594],{},[1493,1592,1593],{},"How do I fill gaps for each store separately?","\nBuild the full grid of every store and every date with pd.MultiIndex.from_product, and reindex the grouped data onto it. Every store then has a row for every day, whether or not it traded.",[156,1596,1598],{"id":1597},"related","Related",[1487,1600,1601,1607,1614,1621],{},[1490,1602,1603,1604,1606],{},"Up one level: ",[23,1605,26],{"href":25}," — dates from import to report.",[1490,1608,1609,1613],{},[23,1610,1612],{"href":1611},"\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Finterpolate-missing-numeric-values-in-excel-data\u002F","Interpolate Missing Numeric Values in Excel Data"," — estimating measurements between known points.",[1490,1615,1616,1620],{},[23,1617,1619],{"href":1618},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcalculate-moving-averages-with-pandas-rolling\u002F","Calculate Moving Averages with pandas rolling"," — windows that need regular periods.",[1490,1622,1623,1627],{},[23,1624,1626],{"href":1625},"\u002Fadvanced-data-transformation-and-cleaning\u002Fhandling-missing-data-in-excel-reports\u002Fforward-fill-blank-cells-in-grouped-excel-reports\u002F","Forward-Fill Blank Cells in Grouped Excel Reports with pandas"," — forward-filling labels in reports.",[1629,1630,1631],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":166,"searchDepth":232,"depth":232,"links":1633},[1634,1635,1636,1637,1638,1639,1640,1641,1642,1643,1644,1645,1646,1647],{"id":158,"depth":232,"text":159},{"id":202,"depth":232,"text":203},{"id":385,"depth":232,"text":386},{"id":499,"depth":232,"text":500},{"id":684,"depth":232,"text":685},{"id":878,"depth":232,"text":879},{"id":1045,"depth":232,"text":1046},{"id":1114,"depth":232,"text":1115},{"id":1380,"depth":232,"text":1381},{"id":1484,"depth":232,"text":1485},{"id":1542,"depth":232,"text":1543},{"id":1556,"depth":232,"text":1557},{"id":1569,"depth":232,"text":1570},{"id":1597,"depth":232,"text":1598},"2026-09-25","Daily exports only have rows for days when something happened, so charts skip the quiet days and averages come out too high. Add the missing dates with reindex and date_range, fill counts with zero and balances with the last known value, do it per store, and aggregate duplicate dates first.","md",[1652,1654,1656,1658],{"q":1575,"a":1653},"Because averages, rolling windows and charts assume one row per period. Without the empty days, a seven-day average is taken over fewer than seven days, and a line chart joins Tuesday straight to Thursday as if Wednesday did not exist.",{"q":1581,"a":1655},"Only for flows — orders, sales, visits — where no row really means nothing happened. For levels such as stock or balances, a missing day means the value did not change, so carry the last known value forward instead.",{"q":1587,"a":1657},"Some dates appear more than once. Aggregate them first — sum the orders per date, take the last stock value — so each date is unique before reindexing.",{"q":1593,"a":1659},"Build the full grid of every store and every date with pd.MultiIndex.from_product, and reindex the grouped data onto it. Every store then has a row for every day, whether or not it traded.","fill missing dates in a time series from excel",null,{"breadcrumb":1663},[1664,1667,1670],{"name":1665,"item":1666},"Home","\u002F",{"name":1668,"item":1669},"Advanced Data Transformation and Cleaning for Excel Automation","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":26,"item":25},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffill-missing-dates-in-a-time-series-with-pandas",{"title":1673,"description":1674},"Fill Missing Dates in pandas (reindex, date_range, asfreq)","Insert missing days into an Excel time series with pandas: reindex on date_range, fill_value=0 for counts versus ffill for balances, per-group gaps, business days only, duplicate dates and resampling afterwards.","fill-missing-dates-in-a-time-series-with-pandas","advanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffill-missing-dates-in-a-time-series-with-pandas\u002Findex",[1678,1679,1680],"dates-and-times","missing-data","pandas","how-to","_P3P4-z32ZZCAODANvm6AeISgZqETbDi1hFJTbXnDPA",[1684,1688],{"title":1685,"path":1686,"stem":1687,"children":-1},"Expand Date Ranges into One Row per Day with pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fexpand-date-ranges-into-one-row-per-day-with-pandas","advanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fexpand-date-ranges-into-one-row-per-day-with-pandas\u002Findex",{"title":1689,"path":1690,"stem":1691,"children":-1},"Find Overlapping Date Ranges with pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffind-overlapping-date-ranges-with-pandas","advanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffind-overlapping-date-ranges-with-pandas\u002Findex",[1693,1695],{"path":1694,"title":1668},"\u002Fadvanced-data-transformation-and-cleaning",{"path":1696,"title":26},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data",[],[1699,1702,1705,1708,1710,1713],{"path":1700,"title":1701,"kind":1681},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-overdue-dates-with-openpyxl","Highlight Overdue Dates in Excel with openpyxl",{"path":1703,"title":1704,"kind":1681},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-weekends-and-holidays-in-a-date-column","Highlight Weekends and Holidays in a Date Column",{"path":1706,"title":1707,"kind":1681},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fkeep-the-latest-record-per-key-with-pandas","Keep the Latest Record per Key When Deduplicating with pandas",{"path":1709,"title":1619,"kind":1681},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcalculate-moving-averages-with-pandas-rolling",{"path":1711,"title":1712,"kind":1681},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Feomonth-and-edate-equivalents-in-pandas","EOMONTH and EDATE Equivalents in pandas",{"path":1714,"title":1715,"kind":1681},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fyearfrac-and-days360-equivalents-in-python","YEARFRAC and DAYS360 Equivalents in Python",1790972106188]