[{"data":1,"prerenderedAt":2278},["ShallowReactive",2],{"doc:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffind-overlapping-date-ranges-with-pandas":3,"surround:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffind-overlapping-date-ranges-with-pandas":2244,"ancestors:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffind-overlapping-date-ranges-with-pandas":2253,"children:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffind-overlapping-date-ranges-with-pandas":2258,"related:\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffind-overlapping-date-ranges-with-pandas":2259},{"id":4,"title":5,"body":6,"dateModified":2210,"datePublished":2210,"description":2211,"extension":2212,"faq":2213,"keyword":2222,"kind":2223,"meta":2224,"navigation":95,"path":2233,"seo":2234,"slug":2237,"stem":2238,"topics":2239,"type":2242,"__hash__":2243},"docs\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffind-overlapping-date-ranges-with-pandas\u002Findex.md","Find Overlapping Date Ranges with pandas",{"type":7,"value":8,"toc":2197},"minimark",[9,19,24,56,67,213,216,220,223,367,380,520,524,527,651,658,662,673,873,880,1132,1136,1139,1394,1607,1620,1624,1627,1763,1766,1770,1773,2061,2072,2076,2121,2125,2128,2132,2138,2148,2154,2160,2164,2193],[10,11,12,13,18],"p",{},"Any sheet with a start date and an end date per row can contain clashes: a meeting room booked twice for the same afternoon, an employee with two overlapping leave requests, a customer with two active contracts for the same product, a vehicle assigned to two jobs at once. Spotting them by eye works for ten rows and fails for ten thousand. The test for two ranges overlapping is a single comparison, and pandas can apply it per group — per room, per employee, per contract — either against the previous row or against every other row. The same ideas then merge overlapping ranges into continuous periods and find the gaps between them. This page is part of ",[14,15,17],"a",{"href":16},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002F","Working with Dates and Times in Excel Data",".",[20,21,23],"h2",{"id":22},"prerequisites","Prerequisites",[25,26,31],"pre",{"className":27,"code":28,"language":29,"meta":30,"style":30},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl xlsxwriter\n","bash","",[32,33,34],"code",{"__ignoreMap":30},[35,36,39,43,47,50,53],"span",{"class":37,"line":38},"line",1,[35,40,42],{"class":41},"sMTad","pip",[35,44,46],{"class":45},"srMev"," install",[35,48,49],{"class":45}," pandas",[35,51,52],{"class":45}," openpyxl",[35,54,55],{"class":45}," xlsxwriter\n",[10,57,58,61,62,66],{},[32,59,60],{},"leave_requests.xlsx"," lists eleven leave requests for four employees with an ID, start and end date, and status. End dates are ",[63,64,65],"em",{},"inclusive"," — a request from 6 to 10 July covers five days, the 10th included — which is how people write leave, bookings and contract periods. Hidden in the data: overlapping requests for two employees, one request that lies completely inside another, two requests that touch but do not overlap, and one request whose end is before its start.",[25,68,72],{"className":69,"code":70,"language":71,"meta":30,"style":30},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\nleave = pd.read_excel(\"leave_requests.xlsx\", parse_dates=[\"Start\", \"End\"])\nprint(leave.to_string(index=False))\nbad = leave[leave[\"End\"] \u003C leave[\"Start\"]]\nprint(\"end before start:\", bad[\"RequestID\"].tolist())\nleave = leave.drop(bad.index)\n","python",[32,73,74,90,97,135,156,183,203],{"__ignoreMap":30},[35,75,76,80,84,87],{"class":37,"line":38},[35,77,79],{"class":78},"s-kum","import",[35,81,83],{"class":82},"skGVy"," pandas ",[35,85,86],{"class":78},"as",[35,88,89],{"class":82}," pd\n",[35,91,93],{"class":37,"line":92},2,[35,94,96],{"emptyLinePlaceholder":95},true,"\n",[35,98,100,103,106,109,112,115,119,121,124,127,129,132],{"class":37,"line":99},3,[35,101,102],{"class":82},"leave ",[35,104,105],{"class":78},"=",[35,107,108],{"class":82}," pd.read_excel(",[35,110,111],{"class":45},"\"leave_requests.xlsx\"",[35,113,114],{"class":82},", ",[35,116,118],{"class":117},"sa561","parse_dates",[35,120,105],{"class":78},[35,122,123],{"class":82},"[",[35,125,126],{"class":45},"\"Start\"",[35,128,114],{"class":82},[35,130,131],{"class":45},"\"End\"",[35,133,134],{"class":82},"])\n",[35,136,138,142,145,148,150,153],{"class":37,"line":137},4,[35,139,141],{"class":140},"sP0c6","print",[35,143,144],{"class":82},"(leave.to_string(",[35,146,147],{"class":117},"index",[35,149,105],{"class":78},[35,151,152],{"class":140},"False",[35,154,155],{"class":82},"))\n",[35,157,159,162,164,167,169,172,175,178,180],{"class":37,"line":158},5,[35,160,161],{"class":82},"bad ",[35,163,105],{"class":78},[35,165,166],{"class":82}," leave[leave[",[35,168,131],{"class":45},[35,170,171],{"class":82},"] ",[35,173,174],{"class":78},"\u003C",[35,176,177],{"class":82}," leave[",[35,179,126],{"class":45},[35,181,182],{"class":82},"]]\n",[35,184,186,188,191,194,197,200],{"class":37,"line":185},6,[35,187,141],{"class":140},[35,189,190],{"class":82},"(",[35,192,193],{"class":45},"\"end before start:\"",[35,195,196],{"class":82},", bad[",[35,198,199],{"class":45},"\"RequestID\"",[35,201,202],{"class":82},"].tolist())\n",[35,204,206,208,210],{"class":37,"line":205},7,[35,207,102],{"class":82},[35,209,105],{"class":78},[35,211,212],{"class":82}," leave.drop(bad.index)\n",[10,214,215],{},"Check for reversed ranges first. A range that ends before it starts overlaps nothing and makes every later calculation misleading; report it and drop it from the comparison.",[20,217,219],{"id":218},"step-1-the-overlap-test","Step 1 — The overlap test",[10,221,222],{},"Two ranges A and B overlap when each starts before the other ends. With inclusive end dates that is:",[25,224,226],{"className":69,"code":225,"language":71,"meta":30,"style":30},"def overlaps(a_start, a_end, b_start, b_end):\n    return (a_start \u003C= b_end) & (b_start \u003C= a_end)\n\nd = pd.Timestamp\nprint(overlaps(d(\"2026-07-06\"), d(\"2026-07-10\"), d(\"2026-07-09\"), d(\"2026-07-15\")))  # partial overlap\nprint(overlaps(d(\"2026-07-01\"), d(\"2026-07-31\"), d(\"2026-07-13\"), d(\"2026-07-17\")))  # one inside the other\nprint(overlaps(d(\"2026-07-06\"), d(\"2026-07-08\"), d(\"2026-07-09\"), d(\"2026-07-10\")))  # back to back\n",[32,227,228,240,265,269,279,312,341],{"__ignoreMap":30},[35,229,230,233,237],{"class":37,"line":38},[35,231,232],{"class":78},"def",[35,234,236],{"class":235},"s_Opv"," overlaps",[35,238,239],{"class":82},"(a_start, a_end, b_start, b_end):\n",[35,241,242,245,248,251,254,257,260,262],{"class":37,"line":92},[35,243,244],{"class":78},"    return",[35,246,247],{"class":82}," (a_start ",[35,249,250],{"class":78},"\u003C=",[35,252,253],{"class":82}," b_end) ",[35,255,256],{"class":78},"&",[35,258,259],{"class":82}," (b_start ",[35,261,250],{"class":78},[35,263,264],{"class":82}," a_end)\n",[35,266,267],{"class":37,"line":99},[35,268,96],{"emptyLinePlaceholder":95},[35,270,271,274,276],{"class":37,"line":137},[35,272,273],{"class":82},"d ",[35,275,105],{"class":78},[35,277,278],{"class":82}," pd.Timestamp\n",[35,280,281,283,286,289,292,295,297,300,302,305,308],{"class":37,"line":158},[35,282,141],{"class":140},[35,284,285],{"class":82},"(overlaps(d(",[35,287,288],{"class":45},"\"2026-07-06\"",[35,290,291],{"class":82},"), d(",[35,293,294],{"class":45},"\"2026-07-10\"",[35,296,291],{"class":82},[35,298,299],{"class":45},"\"2026-07-09\"",[35,301,291],{"class":82},[35,303,304],{"class":45},"\"2026-07-15\"",[35,306,307],{"class":82},")))  ",[35,309,311],{"class":310},"s-wDw","# partial overlap\n",[35,313,314,316,318,321,323,326,328,331,333,336,338],{"class":37,"line":185},[35,315,141],{"class":140},[35,317,285],{"class":82},[35,319,320],{"class":45},"\"2026-07-01\"",[35,322,291],{"class":82},[35,324,325],{"class":45},"\"2026-07-31\"",[35,327,291],{"class":82},[35,329,330],{"class":45},"\"2026-07-13\"",[35,332,291],{"class":82},[35,334,335],{"class":45},"\"2026-07-17\"",[35,337,307],{"class":82},[35,339,340],{"class":310},"# one inside the other\n",[35,342,343,345,347,349,351,354,356,358,360,362,364],{"class":37,"line":205},[35,344,141],{"class":140},[35,346,285],{"class":82},[35,348,288],{"class":45},[35,350,291],{"class":82},[35,352,353],{"class":45},"\"2026-07-08\"",[35,355,291],{"class":82},[35,357,299],{"class":45},[35,359,291],{"class":82},[35,361,294],{"class":45},[35,363,307],{"class":82},[35,365,366],{"class":310},"# back to back\n",[10,368,369,370,373,374,376,377,379],{},"This two-comparison test covers every case — partial overlap on either side, one range inside the other, identical ranges — and nothing else. Back-to-back ranges (the 8th and the 9th) do not overlap with inclusive ends. If your data uses ",[63,371,372],{},"exclusive"," ends, as hotel check-out dates or time slots often do, use ",[32,375,174],{}," instead of ",[32,378,250],{},", otherwise a booking ending at 14:00 clashes with one starting at 14:00.",[381,382,390,391,390,395,390,399,390,407,390,414,390,424,390,435,390,442,390,447,390,451,390,455,390,459,390,464,390,469,390,473,390,475,390,477,390,482,390,489,390,494,390,497,390,499,390,501,390,504,390,509,390,513,390,516,390,518],"svg",{"viewBox":383,"role":384,"ariaLabelledBy":385,"xmlns":388,"style":389},"4 0 752 279","img",[386,387],"ov-cases-t","ov-cases-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[392,393,394],"title",{"id":386},"The cases the test has to get right",[396,397,398],"desc",{"id":387},"Four pairs of ranges along a timeline. A partial overlap and a range inside another both count as overlapping; two ranges that touch at consecutive days and two separate ranges do not.",[400,401],"rect",{"x":402,"y":403,"width":404,"height":405,"fill":406},"4","0","752","279","#ffffff",[37,408],{"x1":409,"y1":410,"x2":411,"y2":410,"stroke":412,"style":413},"94.0","139.5","666.0","var(--muted,#5b6780)","stroke-width:2.5px",[400,415],{"x":416,"y":417,"width":418,"height":419,"rx":420,"fill":421,"stroke":422,"style":423},"20.0","16.0","172.0","91.5","12","#fce9e9","var(--danger,#dc2626)","stroke-width:2px",[425,426,428],"g",{"transform":427},"translate(94.0,29.0) scale(1.000)",[429,430],"path",{"d":431,"fill":432,"stroke":433,"style":434},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","none","var(--accent-ink,#be185d)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[436,437,441],"text",{"x":438,"y":439,"style":440},"106.0","74.5","font-size:12.5px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","Partial overlap",[436,443,446],{"x":438,"y":444,"style":445},"93.0","font-size:11px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","6–10 Jul and 9–15 Jul",[37,448],{"x1":438,"y1":449,"x2":438,"y2":410,"stroke":412,"style":450},"107.5","stroke-width:1.5px",[452,453],"circle",{"cx":438,"cy":410,"r":454,"fill":422,"stroke":406,"style":423},"7.0",[400,456],{"x":457,"y":458,"width":418,"height":419,"rx":420,"fill":421,"stroke":422,"style":423},"202.7","171.5",[425,460,462],{"transform":461},"translate(276.7,184.5) scale(1.000)",[429,463],{"d":431,"fill":432,"stroke":433,"style":434},[436,465,468],{"x":466,"y":467,"style":440},"288.7","230.0","One inside another",[436,470,472],{"x":466,"y":471,"style":445},"248.5","1–31 Jul and 13–17 Jul",[37,474],{"x1":466,"y1":410,"x2":466,"y2":458,"stroke":412,"style":450},[452,476],{"cx":466,"cy":410,"r":454,"fill":422,"stroke":406,"style":423},[400,478],{"x":479,"y":417,"width":418,"height":419,"rx":420,"fill":480,"stroke":481,"style":423},"385.3","#d9f4f1","var(--teal,#0f9488)",[425,483,485],{"transform":484},"translate(459.3,29.0) scale(1.000)",[429,486],{"d":487,"fill":432,"stroke":488,"style":434},"M5 12.5l4.5 4.5L19 7","var(--teal-ink,#0b6157)",[436,490,493],{"x":491,"y":439,"style":492},"471.3","font-size:12.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Back to back",[436,495,496],{"x":491,"y":444,"style":445},"6–8 Jul then 9–10 Jul",[37,498],{"x1":491,"y1":449,"x2":491,"y2":410,"stroke":412,"style":450},[452,500],{"cx":491,"cy":410,"r":454,"fill":481,"stroke":406,"style":423},[400,502],{"x":503,"y":458,"width":418,"height":419,"rx":420,"fill":480,"stroke":481,"style":423},"568.0",[425,505,507],{"transform":506},"translate(642.0,184.5) scale(1.000)",[429,508],{"d":487,"fill":432,"stroke":488,"style":434},[436,510,512],{"x":511,"y":467,"style":492},"654.0","Separate",[436,514,515],{"x":511,"y":471,"style":445},"6–10 Jul and 3–7 Aug",[37,517],{"x1":511,"y1":410,"x2":511,"y2":458,"stroke":412,"style":450},[452,519],{"cx":511,"cy":410,"r":454,"fill":481,"stroke":406,"style":423},[20,521,523],{"id":522},"step-2-compare-each-range-with-the-ones-before-it","Step 2 — Compare each range with the ones before it",[10,525,526],{},"For a quick flag per row, sort by group and start date and compare each row with the latest end date seen so far in its group. Using the running maximum of the end — not just the previous row's end — matters when one long range contains several shorter ones:",[25,528,530],{"className":69,"code":529,"language":71,"meta":30,"style":30},"leave = leave.sort_values([\"Employee\", \"Start\", \"End\"]).reset_index(drop=True)\nprev_max_end = leave.groupby(\"Employee\")[\"End\"].transform(lambda s: s.cummax().shift())\nleave[\"Overlaps previous\"] = leave[\"Start\"] \u003C= prev_max_end\nprint(leave[[\"RequestID\", \"Employee\", \"Start\", \"End\", \"Overlaps previous\"]].to_string(index=False))\n",[32,531,532,566,592,615],{"__ignoreMap":30},[35,533,534,536,538,541,544,546,548,550,552,555,558,560,563],{"class":37,"line":38},[35,535,102],{"class":82},[35,537,105],{"class":78},[35,539,540],{"class":82}," leave.sort_values([",[35,542,543],{"class":45},"\"Employee\"",[35,545,114],{"class":82},[35,547,126],{"class":45},[35,549,114],{"class":82},[35,551,131],{"class":45},[35,553,554],{"class":82},"]).reset_index(",[35,556,557],{"class":117},"drop",[35,559,105],{"class":78},[35,561,562],{"class":140},"True",[35,564,565],{"class":82},")\n",[35,567,568,571,573,576,578,581,583,586,589],{"class":37,"line":92},[35,569,570],{"class":82},"prev_max_end ",[35,572,105],{"class":78},[35,574,575],{"class":82}," leave.groupby(",[35,577,543],{"class":45},[35,579,580],{"class":82},")[",[35,582,131],{"class":45},[35,584,585],{"class":82},"].transform(",[35,587,588],{"class":78},"lambda",[35,590,591],{"class":82}," s: s.cummax().shift())\n",[35,593,594,597,600,602,604,606,608,610,612],{"class":37,"line":99},[35,595,596],{"class":82},"leave[",[35,598,599],{"class":45},"\"Overlaps previous\"",[35,601,171],{"class":82},[35,603,105],{"class":78},[35,605,177],{"class":82},[35,607,126],{"class":45},[35,609,171],{"class":82},[35,611,250],{"class":78},[35,613,614],{"class":82}," prev_max_end\n",[35,616,617,619,622,624,626,628,630,632,634,636,638,640,643,645,647,649],{"class":37,"line":137},[35,618,141],{"class":140},[35,620,621],{"class":82},"(leave[[",[35,623,199],{"class":45},[35,625,114],{"class":82},[35,627,543],{"class":45},[35,629,114],{"class":82},[35,631,126],{"class":45},[35,633,114],{"class":82},[35,635,131],{"class":45},[35,637,114],{"class":82},[35,639,599],{"class":45},[35,641,642],{"class":82},"]].to_string(",[35,644,147],{"class":117},[35,646,105],{"class":78},[35,648,152],{"class":140},[35,650,155],{"class":82},[10,652,653,654,657],{},"Jonas's July requests show why the running maximum is needed: his month-long approved leave from 1 to 31 July contains both later requests. Comparing each row only with the row directly before it would flag the 13–17 July request (it starts before the 31st), but the 20–24 July request would be compared with the 17th and pass. ",[32,655,656],{},"cummax().shift()"," carries the 31st forward, so both are flagged. Adaeze's single-day request on 7 August is flagged too — it falls on the last day of her approved week — while Claire's back-to-back requests are not.",[20,659,661],{"id":660},"step-3-list-every-overlapping-pair","Step 3 — List every overlapping pair",[10,663,664,665,668,669,672],{},"A flag says ",[63,666,667],{},"that"," a row clashes; for resolving it, people need to know ",[63,670,671],{},"with what",". Merge each group with itself, keep pairs that overlap, and drop each pair's mirror image:",[25,674,676],{"className":69,"code":675,"language":71,"meta":30,"style":30},"pairs = leave.merge(leave, on=\"Employee\", suffixes=(\"\", \"_other\"))\npairs = pairs[(pairs[\"RequestID\"] \u003C pairs[\"RequestID_other\"])\n              & overlaps(pairs[\"Start\"], pairs[\"End\"], pairs[\"Start_other\"], pairs[\"End_other\"])]\npairs[\"Days\"] = ((pairs[[\"End\", \"End_other\"]].min(axis=1) - pairs[[\"Start\", \"Start_other\"]].max(axis=1)).dt.days + 1)\nprint(pairs[[\"Employee\", \"RequestID\", \"Status\", \"RequestID_other\", \"Status_other\", \"Days\"]].to_string(index=False))\n",[32,677,678,714,737,765,832],{"__ignoreMap":30},[35,679,680,683,685,688,691,693,695,697,700,702,704,707,709,712],{"class":37,"line":38},[35,681,682],{"class":82},"pairs ",[35,684,105],{"class":78},[35,686,687],{"class":82}," leave.merge(leave, ",[35,689,690],{"class":117},"on",[35,692,105],{"class":78},[35,694,543],{"class":45},[35,696,114],{"class":82},[35,698,699],{"class":117},"suffixes",[35,701,105],{"class":78},[35,703,190],{"class":82},[35,705,706],{"class":45},"\"\"",[35,708,114],{"class":82},[35,710,711],{"class":45},"\"_other\"",[35,713,155],{"class":82},[35,715,716,718,720,723,725,727,729,732,735],{"class":37,"line":92},[35,717,682],{"class":82},[35,719,105],{"class":78},[35,721,722],{"class":82}," pairs[(pairs[",[35,724,199],{"class":45},[35,726,171],{"class":82},[35,728,174],{"class":78},[35,730,731],{"class":82}," pairs[",[35,733,734],{"class":45},"\"RequestID_other\"",[35,736,134],{"class":82},[35,738,739,742,745,747,750,752,754,757,759,762],{"class":37,"line":99},[35,740,741],{"class":78},"              &",[35,743,744],{"class":82}," overlaps(pairs[",[35,746,126],{"class":45},[35,748,749],{"class":82},"], pairs[",[35,751,131],{"class":45},[35,753,749],{"class":82},[35,755,756],{"class":45},"\"Start_other\"",[35,758,749],{"class":82},[35,760,761],{"class":45},"\"End_other\"",[35,763,764],{"class":82},"])]\n",[35,766,767,770,773,775,777,780,782,784,786,789,792,794,797,800,803,806,808,810,812,815,817,819,821,824,827,830],{"class":37,"line":137},[35,768,769],{"class":82},"pairs[",[35,771,772],{"class":45},"\"Days\"",[35,774,171],{"class":82},[35,776,105],{"class":78},[35,778,779],{"class":82}," ((pairs[[",[35,781,131],{"class":45},[35,783,114],{"class":82},[35,785,761],{"class":45},[35,787,788],{"class":82},"]].min(",[35,790,791],{"class":117},"axis",[35,793,105],{"class":78},[35,795,796],{"class":140},"1",[35,798,799],{"class":82},") ",[35,801,802],{"class":78},"-",[35,804,805],{"class":82}," pairs[[",[35,807,126],{"class":45},[35,809,114],{"class":82},[35,811,756],{"class":45},[35,813,814],{"class":82},"]].max(",[35,816,791],{"class":117},[35,818,105],{"class":78},[35,820,796],{"class":140},[35,822,823],{"class":82},")).dt.days ",[35,825,826],{"class":78},"+",[35,828,829],{"class":140}," 1",[35,831,565],{"class":82},[35,833,834,836,839,841,843,845,847,850,852,854,856,859,861,863,865,867,869,871],{"class":37,"line":158},[35,835,141],{"class":140},[35,837,838],{"class":82},"(pairs[[",[35,840,543],{"class":45},[35,842,114],{"class":82},[35,844,199],{"class":45},[35,846,114],{"class":82},[35,848,849],{"class":45},"\"Status\"",[35,851,114],{"class":82},[35,853,734],{"class":45},[35,855,114],{"class":82},[35,857,858],{"class":45},"\"Status_other\"",[35,860,114],{"class":82},[35,862,772],{"class":45},[35,864,642],{"class":82},[35,866,147],{"class":117},[35,868,105],{"class":78},[35,870,152],{"class":140},[35,872,155],{"class":82},[10,874,875,876,879],{},"The condition ",[32,877,878],{},"RequestID \u003C RequestID_other"," keeps each pair once and removes each request's comparison with itself. The overlap length is the later start to the earlier end, plus one day because both ends are inclusive. A self-merge compares every pair in a group, so its size grows with the square of the group size — fine for dozens of ranges per employee or room, too big for a group of a hundred thousand rows, where the sorted approach of Step 2 is the one to use.",[381,881,390,887,390,890,390,893,390,898,390,906,390,910,390,916,390,920,390,924,390,928,390,932,390,936,390,940,390,943,390,947,390,951,390,955,390,959,390,963,390,967,390,971,390,973,390,976,390,978,390,981,390,983,390,986,390,988,390,990,390,992,390,995,390,998,390,1002,390,1004,390,1007,390,1009,390,1012,390,1014,390,1017,390,1019,390,1022,390,1024,390,1027,390,1031,390,1034,390,1037,390,1041,390,1043,390,1045,390,1047,390,1050,390,1052,390,1054,390,1056,390,1059,390,1061,390,1063,390,1065,390,1067,390,1070,390,1073,390,1075,390,1078,390,1080,390,1083,390,1085,390,1087,390,1089,390,1092,390,1094,390,1096,390,1098,390,1101,390,1104,390,1107,390,1109,390,1111,390,1113,390,1115,390,1117,390,1119,390,1121,390,1124,390,1126,390,1128,390,1130],{"viewBox":882,"role":384,"ariaLabelledBy":883,"xmlns":388,"style":886},"123 0 514 196",[884,885],"ov-pairs-t","ov-pairs-d","width:100%;max-width:514px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[392,888,889],{"id":884},"The overlapping pairs",[396,891,892],{"id":885},"A table of overlapping request pairs. Adaeze's L-101 and L-102 overlap by two days, L-103 and L-104 by one day; Jonas's L-105 overlaps L-106 and L-107 by five days each.",[400,894],{"x":895,"y":403,"width":896,"height":897,"fill":406},"123","514","196",[400,899],{"x":900,"y":417,"width":901,"height":902,"rx":403,"fill":903,"stroke":904,"style":905},"139.1","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[400,907],{"x":908,"y":417,"width":909,"height":902,"rx":403,"fill":903,"stroke":904,"style":905},"171.1","79.8",[436,911,915],{"x":912,"y":913,"style":914},"211.0","33.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[400,917],{"x":918,"y":417,"width":919,"height":902,"rx":403,"fill":903,"stroke":904,"style":905},"250.9","70.5",[436,921,923],{"x":922,"y":913,"style":914},"286.1","B",[400,925],{"x":926,"y":417,"width":927,"height":902,"rx":403,"fill":903,"stroke":904,"style":905},"321.4","80.1",[436,929,931],{"x":930,"y":913,"style":914},"361.4","C",[400,933],{"x":934,"y":417,"width":935,"height":902,"rx":403,"fill":903,"stroke":904,"style":905},"401.4","96.5",[436,937,939],{"x":938,"y":913,"style":914},"449.7","D",[400,941],{"x":942,"y":417,"width":919,"height":902,"rx":403,"fill":903,"stroke":904,"style":905},"498.0",[436,944,946],{"x":945,"y":913,"style":914},"533.2","E",[400,948],{"x":949,"y":417,"width":950,"height":902,"rx":403,"fill":903,"stroke":904,"style":905},"568.4","52.5",[436,952,954],{"x":953,"y":913,"style":914},"594.7","F",[400,956],{"x":900,"y":957,"width":901,"height":958,"rx":403,"fill":903,"stroke":904,"style":905},"40.0","28.0",[436,960,796],{"x":961,"y":962,"style":914},"155.1","59.0",[400,964],{"x":908,"y":957,"width":909,"height":958,"rx":403,"fill":965,"stroke":966,"style":450},"#f0f4ff","var(--brand,#5b5cf0)",[436,968,970],{"x":912,"y":962,"style":969},"font-size:11px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Employee",[400,972],{"x":918,"y":957,"width":919,"height":958,"rx":403,"fill":965,"stroke":966,"style":450},[436,974,975],{"x":922,"y":962,"style":969},"Request",[400,977],{"x":926,"y":957,"width":927,"height":958,"rx":403,"fill":965,"stroke":966,"style":450},[436,979,980],{"x":930,"y":962,"style":969},"Status",[400,982],{"x":934,"y":957,"width":935,"height":958,"rx":403,"fill":965,"stroke":966,"style":450},[436,984,985],{"x":938,"y":962,"style":969},"Clashes with",[400,987],{"x":942,"y":957,"width":919,"height":958,"rx":403,"fill":965,"stroke":966,"style":450},[436,989,980],{"x":945,"y":962,"style":969},[400,991],{"x":949,"y":957,"width":950,"height":958,"rx":403,"fill":965,"stroke":966,"style":450},[436,993,994],{"x":953,"y":962,"style":969},"Days",[400,996],{"x":900,"y":997,"width":901,"height":958,"rx":403,"fill":903,"stroke":904,"style":905},"68.0",[436,999,1001],{"x":961,"y":1000,"style":914},"87.0","2",[400,1003],{"x":908,"y":997,"width":909,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1005,1006],{"x":912,"y":1000,"style":445},"Adaeze",[400,1008],{"x":918,"y":997,"width":919,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1010,1011],{"x":922,"y":1000,"style":445},"L-101",[400,1013],{"x":926,"y":997,"width":927,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1015,1016],{"x":930,"y":1000,"style":445},"Approved",[400,1018],{"x":934,"y":997,"width":935,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1020,1021],{"x":938,"y":1000,"style":445},"L-102",[400,1023],{"x":942,"y":997,"width":919,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1025,1026],{"x":945,"y":1000,"style":445},"Pending",[400,1028],{"x":949,"y":997,"width":950,"height":958,"rx":403,"fill":1029,"stroke":1030,"style":450},"#fdefd8","var(--gold,#b4740a)",[436,1032,1001],{"x":953,"y":1000,"style":1033},"font-size:11px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle",[400,1035],{"x":900,"y":1036,"width":901,"height":958,"rx":403,"fill":903,"stroke":904,"style":905},"96.0",[436,1038,1040],{"x":961,"y":1039,"style":914},"115.0","3",[400,1042],{"x":908,"y":1036,"width":909,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1044,1006],{"x":912,"y":1039,"style":445},[400,1046],{"x":918,"y":1036,"width":919,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1048,1049],{"x":922,"y":1039,"style":445},"L-103",[400,1051],{"x":926,"y":1036,"width":927,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1053,1016],{"x":930,"y":1039,"style":445},[400,1055],{"x":934,"y":1036,"width":935,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1057,1058],{"x":938,"y":1039,"style":445},"L-104",[400,1060],{"x":942,"y":1036,"width":919,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1062,1026],{"x":945,"y":1039,"style":445},[400,1064],{"x":949,"y":1036,"width":950,"height":958,"rx":403,"fill":1029,"stroke":1030,"style":450},[436,1066,796],{"x":953,"y":1039,"style":1033},[400,1068],{"x":900,"y":1069,"width":901,"height":958,"rx":403,"fill":903,"stroke":904,"style":905},"124.0",[436,1071,402],{"x":961,"y":1072,"style":914},"143.0",[400,1074],{"x":908,"y":1069,"width":909,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1076,1077],{"x":912,"y":1072,"style":445},"Jonas",[400,1079],{"x":918,"y":1069,"width":919,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1081,1082],{"x":922,"y":1072,"style":445},"L-105",[400,1084],{"x":926,"y":1069,"width":927,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1086,1016],{"x":930,"y":1072,"style":445},[400,1088],{"x":934,"y":1069,"width":935,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1090,1091],{"x":938,"y":1072,"style":445},"L-106",[400,1093],{"x":942,"y":1069,"width":919,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1095,1026],{"x":945,"y":1072,"style":445},[400,1097],{"x":949,"y":1069,"width":950,"height":958,"rx":403,"fill":1029,"stroke":1030,"style":450},[436,1099,1100],{"x":953,"y":1072,"style":1033},"5",[400,1102],{"x":900,"y":1103,"width":901,"height":958,"rx":403,"fill":903,"stroke":904,"style":905},"152.0",[436,1105,1100],{"x":961,"y":1106,"style":914},"171.0",[400,1108],{"x":908,"y":1103,"width":909,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1110,1077],{"x":912,"y":1106,"style":445},[400,1112],{"x":918,"y":1103,"width":919,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1114,1082],{"x":922,"y":1106,"style":445},[400,1116],{"x":926,"y":1103,"width":927,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1118,1016],{"x":930,"y":1106,"style":445},[400,1120],{"x":934,"y":1103,"width":935,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1122,1123],{"x":938,"y":1106,"style":445},"L-107",[400,1125],{"x":942,"y":1103,"width":919,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1127,1026],{"x":945,"y":1106,"style":445},[400,1129],{"x":949,"y":1103,"width":950,"height":958,"rx":403,"fill":1029,"stroke":1030,"style":450},[436,1131,1100],{"x":953,"y":1106,"style":1033},[20,1133,1135],{"id":1134},"step-4-merge-overlaps-into-continuous-periods","Step 4 — Merge overlaps into continuous periods",[10,1137,1138],{},"The opposite question comes up just as often: how many days was each employee away, counting overlapping requests once? Merge overlapping (and, optionally, touching) ranges into continuous periods. A new period starts wherever a range begins after everything before it has ended:",[25,1140,1142],{"className":69,"code":1141,"language":71,"meta":30,"style":30},"def merge_ranges(g, touch=True):\n    g = g.sort_values(\"Start\")\n    gap = pd.Timedelta(days=1 if touch else 0)\n    new_period = g[\"Start\"] > g[\"End\"].cummax().shift() + gap\n    period = new_period.cumsum()\n    return g.groupby(period).agg(Start=(\"Start\", \"min\"), End=(\"End\", \"max\"),\n                                 Requests=(\"RequestID\", \", \".join))\n\nperiods = leave.groupby(\"Employee\").apply(merge_ranges, include_groups=False).reset_index(level=1, drop=True)\nperiods[\"Days\"] = (periods[\"End\"] - periods[\"Start\"]).dt.days + 1\nprint(periods.to_string())\n",[32,1143,1144,1161,1175,1206,1235,1245,1286,1305,1310,1352,1386],{"__ignoreMap":30},[35,1145,1146,1148,1151,1154,1156,1158],{"class":37,"line":38},[35,1147,232],{"class":78},[35,1149,1150],{"class":235}," merge_ranges",[35,1152,1153],{"class":82},"(g, touch",[35,1155,105],{"class":78},[35,1157,562],{"class":140},[35,1159,1160],{"class":82},"):\n",[35,1162,1163,1166,1168,1171,1173],{"class":37,"line":92},[35,1164,1165],{"class":82},"    g ",[35,1167,105],{"class":78},[35,1169,1170],{"class":82}," g.sort_values(",[35,1172,126],{"class":45},[35,1174,565],{"class":82},[35,1176,1177,1180,1182,1185,1188,1190,1192,1195,1198,1201,1204],{"class":37,"line":99},[35,1178,1179],{"class":82},"    gap ",[35,1181,105],{"class":78},[35,1183,1184],{"class":82}," pd.Timedelta(",[35,1186,1187],{"class":117},"days",[35,1189,105],{"class":78},[35,1191,796],{"class":140},[35,1193,1194],{"class":78}," if",[35,1196,1197],{"class":82}," touch ",[35,1199,1200],{"class":78},"else",[35,1202,1203],{"class":140}," 0",[35,1205,565],{"class":82},[35,1207,1208,1211,1213,1216,1218,1220,1223,1225,1227,1230,1232],{"class":37,"line":137},[35,1209,1210],{"class":82},"    new_period ",[35,1212,105],{"class":78},[35,1214,1215],{"class":82}," g[",[35,1217,126],{"class":45},[35,1219,171],{"class":82},[35,1221,1222],{"class":78},">",[35,1224,1215],{"class":82},[35,1226,131],{"class":45},[35,1228,1229],{"class":82},"].cummax().shift() ",[35,1231,826],{"class":78},[35,1233,1234],{"class":82}," gap\n",[35,1236,1237,1240,1242],{"class":37,"line":158},[35,1238,1239],{"class":82},"    period ",[35,1241,105],{"class":78},[35,1243,1244],{"class":82}," new_period.cumsum()\n",[35,1246,1247,1249,1252,1255,1257,1259,1261,1263,1266,1269,1272,1274,1276,1278,1280,1283],{"class":37,"line":185},[35,1248,244],{"class":78},[35,1250,1251],{"class":82}," g.groupby(period).agg(",[35,1253,1254],{"class":117},"Start",[35,1256,105],{"class":78},[35,1258,190],{"class":82},[35,1260,126],{"class":45},[35,1262,114],{"class":82},[35,1264,1265],{"class":45},"\"min\"",[35,1267,1268],{"class":82},"), ",[35,1270,1271],{"class":117},"End",[35,1273,105],{"class":78},[35,1275,190],{"class":82},[35,1277,131],{"class":45},[35,1279,114],{"class":82},[35,1281,1282],{"class":45},"\"max\"",[35,1284,1285],{"class":82},"),\n",[35,1287,1288,1291,1293,1295,1297,1299,1302],{"class":37,"line":205},[35,1289,1290],{"class":117},"                                 Requests",[35,1292,105],{"class":78},[35,1294,190],{"class":82},[35,1296,199],{"class":45},[35,1298,114],{"class":82},[35,1300,1301],{"class":45},"\", \"",[35,1303,1304],{"class":82},".join))\n",[35,1306,1308],{"class":37,"line":1307},8,[35,1309,96],{"emptyLinePlaceholder":95},[35,1311,1313,1316,1318,1320,1322,1325,1328,1330,1332,1335,1338,1340,1342,1344,1346,1348,1350],{"class":37,"line":1312},9,[35,1314,1315],{"class":82},"periods ",[35,1317,105],{"class":78},[35,1319,575],{"class":82},[35,1321,543],{"class":45},[35,1323,1324],{"class":82},").apply(merge_ranges, ",[35,1326,1327],{"class":117},"include_groups",[35,1329,105],{"class":78},[35,1331,152],{"class":140},[35,1333,1334],{"class":82},").reset_index(",[35,1336,1337],{"class":117},"level",[35,1339,105],{"class":78},[35,1341,796],{"class":140},[35,1343,114],{"class":82},[35,1345,557],{"class":117},[35,1347,105],{"class":78},[35,1349,562],{"class":140},[35,1351,565],{"class":82},[35,1353,1355,1358,1360,1362,1364,1367,1369,1371,1373,1376,1378,1381,1383],{"class":37,"line":1354},10,[35,1356,1357],{"class":82},"periods[",[35,1359,772],{"class":45},[35,1361,171],{"class":82},[35,1363,105],{"class":78},[35,1365,1366],{"class":82}," (periods[",[35,1368,131],{"class":45},[35,1370,171],{"class":82},[35,1372,802],{"class":78},[35,1374,1375],{"class":82}," periods[",[35,1377,126],{"class":45},[35,1379,1380],{"class":82},"]).dt.days ",[35,1382,826],{"class":78},[35,1384,1385],{"class":140}," 1\n",[35,1387,1389,1391],{"class":37,"line":1388},11,[35,1390,141],{"class":140},[35,1392,1393],{"class":82},"(periods.to_string())\n",[381,1395,390,1401,390,1404,390,1407,390,1412,390,1415,390,1418,390,1422,390,1426,390,1429,390,1432,390,1435,390,1438,390,1442,390,1444,390,1447,390,1449,390,1451,390,1453,390,1455,390,1458,390,1461,390,1463,390,1465,390,1467,390,1470,390,1472,390,1475,390,1478,390,1481,390,1483,390,1485,390,1487,390,1490,390,1492,390,1495,390,1498,390,1501,390,1503,390,1505,390,1507,390,1510,390,1512,390,1515,390,1518,390,1522,390,1525,390,1529,390,1532,390,1535,390,1538,390,1540,390,1543,390,1545,390,1547,390,1549,390,1551,390,1553,390,1555,390,1557,390,1559,390,1561,390,1564,390,1566,390,1568,390,1570,390,1573,390,1579,390,1583,390,1588,390,1593,390,1601],{"viewBox":1396,"role":384,"ariaLabelledBy":1397,"xmlns":388,"style":1400},"69 0 621 192",[1398,1399],"ov-merge-t","ov-merge-d","width:100%;max-width:621px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[392,1402,1403],{"id":1398},"Three overlapping requests become one period",[396,1405,1406],{"id":1399},"On the left Jonas's three requests, one covering all of July and two shorter ones inside it. On the right a single merged period from 1 to 31 July lasting 31 days.",[400,1408],{"x":1409,"y":403,"width":1410,"height":1411,"fill":406},"69","621","192",[400,1413],{"x":1414,"y":957,"width":901,"height":902,"rx":403,"fill":903,"stroke":904,"style":905},"85.7",[400,1416],{"x":1417,"y":957,"width":919,"height":902,"rx":403,"fill":903,"stroke":904,"style":905},"117.7",[436,1419,915],{"x":1420,"y":1421,"style":914},"153.0","57.0",[400,1423],{"x":1424,"y":957,"width":1425,"height":902,"rx":403,"fill":903,"stroke":904,"style":905},"188.3","58.9",[436,1427,923],{"x":1428,"y":1421,"style":914},"217.7",[400,1430],{"x":1431,"y":957,"width":1425,"height":902,"rx":403,"fill":903,"stroke":904,"style":905},"247.1",[436,1433,931],{"x":1434,"y":1421,"style":914},"276.6",[400,1436],{"x":1414,"y":1437,"width":901,"height":958,"rx":403,"fill":903,"stroke":904,"style":905},"64.0",[436,1439,796],{"x":1440,"y":1441,"style":914},"101.7","83.0",[400,1443],{"x":1417,"y":1437,"width":919,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1445,975],{"x":1420,"y":1441,"style":1446},"font-size:11px;font-weight:700;fill:var(--text,#172033);text-anchor:middle",[400,1448],{"x":1424,"y":1437,"width":1425,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1450,1254],{"x":1428,"y":1441,"style":1446},[400,1452],{"x":1431,"y":1437,"width":1425,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1454,1271],{"x":1434,"y":1441,"style":1446},[400,1456],{"x":1414,"y":1457,"width":901,"height":958,"rx":403,"fill":903,"stroke":904,"style":905},"92.0",[436,1459,1001],{"x":1440,"y":1460,"style":914},"111.0",[400,1462],{"x":1417,"y":1457,"width":919,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1464,1082],{"x":1420,"y":1460,"style":445},[400,1466],{"x":1424,"y":1457,"width":1425,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1468,1469],{"x":1428,"y":1460,"style":445},"01 Jul",[400,1471],{"x":1431,"y":1457,"width":1425,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1473,1474],{"x":1434,"y":1460,"style":445},"31 Jul",[400,1476],{"x":1414,"y":1477,"width":901,"height":958,"rx":403,"fill":903,"stroke":904,"style":905},"120.0",[436,1479,1040],{"x":1440,"y":1480,"style":914},"139.0",[400,1482],{"x":1417,"y":1477,"width":919,"height":958,"rx":403,"fill":1029,"stroke":1030,"style":450},[436,1484,1091],{"x":1420,"y":1480,"style":1033},[400,1486],{"x":1424,"y":1477,"width":1425,"height":958,"rx":403,"fill":1029,"stroke":1030,"style":450},[436,1488,1489],{"x":1428,"y":1480,"style":1033},"13 Jul",[400,1491],{"x":1431,"y":1477,"width":1425,"height":958,"rx":403,"fill":1029,"stroke":1030,"style":450},[436,1493,1494],{"x":1434,"y":1480,"style":1033},"17 Jul",[400,1496],{"x":1414,"y":1497,"width":901,"height":958,"rx":403,"fill":903,"stroke":904,"style":905},"148.0",[436,1499,402],{"x":1440,"y":1500,"style":914},"167.0",[400,1502],{"x":1417,"y":1497,"width":919,"height":958,"rx":403,"fill":1029,"stroke":1030,"style":450},[436,1504,1123],{"x":1420,"y":1500,"style":1033},[400,1506],{"x":1424,"y":1497,"width":1425,"height":958,"rx":403,"fill":1029,"stroke":1030,"style":450},[436,1508,1509],{"x":1428,"y":1500,"style":1033},"20 Jul",[400,1511],{"x":1431,"y":1497,"width":1425,"height":958,"rx":403,"fill":1029,"stroke":1030,"style":450},[436,1513,1514],{"x":1434,"y":1500,"style":1033},"24 Jul",[400,1516],{"x":1517,"y":957,"width":901,"height":902,"rx":403,"fill":903,"stroke":904,"style":905},"477.0",[400,1519],{"x":1520,"y":957,"width":1521,"height":902,"rx":403,"fill":903,"stroke":904,"style":905},"509.0","56.6",[436,1523,915],{"x":1524,"y":1421,"style":914},"537.3",[400,1526],{"x":1527,"y":957,"width":1528,"height":902,"rx":403,"fill":903,"stroke":904,"style":905},"565.5","56.2",[436,1530,923],{"x":1531,"y":1421,"style":914},"593.7",[400,1533],{"x":1534,"y":957,"width":950,"height":902,"rx":403,"fill":903,"stroke":904,"style":905},"621.8",[436,1536,931],{"x":1537,"y":1421,"style":914},"648.0",[400,1539],{"x":1517,"y":1437,"width":901,"height":958,"rx":403,"fill":903,"stroke":904,"style":905},[436,1541,796],{"x":1542,"y":1441,"style":914},"493.0",[400,1544],{"x":1520,"y":1437,"width":1521,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1546,1254],{"x":1524,"y":1441,"style":1446},[400,1548],{"x":1527,"y":1437,"width":1528,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1550,1271],{"x":1531,"y":1441,"style":1446},[400,1552],{"x":1534,"y":1437,"width":950,"height":958,"rx":403,"fill":406,"stroke":904,"style":905},[436,1554,994],{"x":1537,"y":1441,"style":1446},[400,1556],{"x":1517,"y":1457,"width":901,"height":958,"rx":403,"fill":903,"stroke":904,"style":905},[436,1558,1001],{"x":1542,"y":1460,"style":914},[400,1560],{"x":1520,"y":1457,"width":1521,"height":958,"rx":403,"fill":480,"stroke":481,"style":450},[436,1562,1469],{"x":1524,"y":1460,"style":1563},"font-size:11px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[400,1565],{"x":1527,"y":1457,"width":1528,"height":958,"rx":403,"fill":480,"stroke":481,"style":450},[436,1567,1474],{"x":1531,"y":1460,"style":1563},[400,1569],{"x":1534,"y":1457,"width":950,"height":958,"rx":403,"fill":480,"stroke":481,"style":450},[436,1571,1572],{"x":1537,"y":1460,"style":1563},"31",[436,1574,1578],{"x":1575,"y":1576,"style":1577},"195.9","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","before",[436,1580,1582],{"x":1581,"y":1576,"style":1577},"575.6","after",[37,1584],{"x1":1585,"y1":1586,"x2":1587,"y2":1586,"stroke":966,"style":423},"316.0","80.0","463.0",[1589,1590],"polygon",{"points":1591,"fill":1592},"467.0,80.0 458.0,85.0 458.0,75.0","#5b5cf0",[400,1594],{"x":1595,"y":1596,"width":1597,"height":1598,"rx":1599,"fill":406,"stroke":904,"style":1600},"339.1","47.0","104.8","22.0","11","stroke-width:1.2px",[436,1602,1606],{"x":1603,"y":1604,"style":1605},"391.5","62.0","font-size:11.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","merge_ranges",[10,1608,1609,1612,1613,1616,1617,1619],{},[32,1610,1611],{},"new_period"," is True on each row that starts after the running maximum end of the rows before it; its cumulative sum numbers the periods 0, 1, 2, …, and a group-by on that number collapses each period to its first start and last end. With ",[32,1614,1615],{},"touch=True",", ranges on consecutive days — Claire's 6–8 and 9–10 July — join into one period, which is what \"continuous absence\" usually means; set it to ",[32,1618,152],{}," to keep them apart. The same technique turns a list of equipment rentals into utilisation periods or contract lines into continuous customer tenure.",[20,1621,1623],{"id":1622},"step-5-gaps-between-ranges","Step 5 — Gaps between ranges",[10,1625,1626],{},"With merged periods, the gaps are the days between one period's end and the next one's start:",[25,1628,1630],{"className":69,"code":1629,"language":71,"meta":30,"style":30},"periods = periods.reset_index()\nperiods[\"Next start\"] = periods.groupby(\"Employee\")[\"Start\"].shift(-1)\nperiods[\"Gap days\"] = (periods[\"Next start\"] - periods[\"End\"]).dt.days - 1\ngaps = periods.dropna(subset=[\"Next start\"]).astype({\"Gap days\": int})\nprint(gaps[[\"Employee\", \"End\", \"Next start\", \"Gap days\"]].to_string(index=False))\n",[32,1631,1632,1641,1670,1699,1732],{"__ignoreMap":30},[35,1633,1634,1636,1638],{"class":37,"line":38},[35,1635,1315],{"class":82},[35,1637,105],{"class":78},[35,1639,1640],{"class":82}," periods.reset_index()\n",[35,1642,1643,1645,1648,1650,1652,1655,1657,1659,1661,1664,1666,1668],{"class":37,"line":92},[35,1644,1357],{"class":82},[35,1646,1647],{"class":45},"\"Next start\"",[35,1649,171],{"class":82},[35,1651,105],{"class":78},[35,1653,1654],{"class":82}," periods.groupby(",[35,1656,543],{"class":45},[35,1658,580],{"class":82},[35,1660,126],{"class":45},[35,1662,1663],{"class":82},"].shift(",[35,1665,802],{"class":78},[35,1667,796],{"class":140},[35,1669,565],{"class":82},[35,1671,1672,1674,1677,1679,1681,1683,1685,1687,1689,1691,1693,1695,1697],{"class":37,"line":99},[35,1673,1357],{"class":82},[35,1675,1676],{"class":45},"\"Gap days\"",[35,1678,171],{"class":82},[35,1680,105],{"class":78},[35,1682,1366],{"class":82},[35,1684,1647],{"class":45},[35,1686,171],{"class":82},[35,1688,802],{"class":78},[35,1690,1375],{"class":82},[35,1692,131],{"class":45},[35,1694,1380],{"class":82},[35,1696,802],{"class":78},[35,1698,1385],{"class":140},[35,1700,1701,1704,1706,1709,1712,1714,1716,1718,1721,1723,1726,1729],{"class":37,"line":137},[35,1702,1703],{"class":82},"gaps ",[35,1705,105],{"class":78},[35,1707,1708],{"class":82}," periods.dropna(",[35,1710,1711],{"class":117},"subset",[35,1713,105],{"class":78},[35,1715,123],{"class":82},[35,1717,1647],{"class":45},[35,1719,1720],{"class":82},"]).astype({",[35,1722,1676],{"class":45},[35,1724,1725],{"class":82},": ",[35,1727,1728],{"class":140},"int",[35,1730,1731],{"class":82},"})\n",[35,1733,1734,1736,1739,1741,1743,1745,1747,1749,1751,1753,1755,1757,1759,1761],{"class":37,"line":158},[35,1735,141],{"class":140},[35,1737,1738],{"class":82},"(gaps[[",[35,1740,543],{"class":45},[35,1742,114],{"class":82},[35,1744,131],{"class":45},[35,1746,114],{"class":82},[35,1748,1647],{"class":45},[35,1750,114],{"class":82},[35,1752,1676],{"class":45},[35,1754,642],{"class":82},[35,1756,147],{"class":117},[35,1758,105],{"class":78},[35,1760,152],{"class":140},[35,1762,155],{"class":82},[10,1764,1765],{},"Gaps answer questions such as \"which contracts lapsed before renewal\" or \"how long was the room free\". Subtracting one converts the difference between two inclusive dates into the number of whole days in between.",[20,1767,1769],{"id":1768},"step-6-highlight-the-clashes-in-excel","Step 6 — Highlight the clashes in Excel",[10,1771,1772],{},"Write the requests back with the clashing rows highlighted and the pair list on its own sheet, so whoever approves leave can see the conflicts in context:",[25,1774,1776],{"className":69,"code":1775,"language":71,"meta":30,"style":30},"clash_ids = set(pairs[\"RequestID\"]) | set(pairs[\"RequestID_other\"])\nleave[\"Clash\"] = leave[\"RequestID\"].isin(clash_ids)\n\nwith pd.ExcelWriter(\"leave_checked.xlsx\", engine=\"xlsxwriter\", datetime_format=\"dd mmm yyyy\") as xw:\n    leave.drop(columns=\"Overlaps previous\").to_excel(xw, sheet_name=\"Requests\", index=False)\n    pairs[[\"Employee\", \"RequestID\", \"RequestID_other\", \"Days\"]].to_excel(xw, sheet_name=\"Clashes\", index=False)\n    wb, ws = xw.book, xw.sheets[\"Requests\"]\n    red = wb.add_format({\"bg_color\": \"#FCE9E9\", \"font_color\": \"#9C0006\"})\n    ws.conditional_format(1, 0, len(leave), 5, {\"type\": \"formula\", \"criteria\": \"=$F2=TRUE\", \"format\": red})\n    ws.set_column(\"A:E\", 13)\nprint(sorted(clash_ids))\n",[32,1777,1778,1807,1825,1829,1867,1900,1939,1954,1984,2034,2049],{"__ignoreMap":30},[35,1779,1780,1783,1785,1788,1791,1793,1796,1799,1801,1803,1805],{"class":37,"line":38},[35,1781,1782],{"class":82},"clash_ids ",[35,1784,105],{"class":78},[35,1786,1787],{"class":140}," set",[35,1789,1790],{"class":82},"(pairs[",[35,1792,199],{"class":45},[35,1794,1795],{"class":82},"]) ",[35,1797,1798],{"class":78},"|",[35,1800,1787],{"class":140},[35,1802,1790],{"class":82},[35,1804,734],{"class":45},[35,1806,134],{"class":82},[35,1808,1809,1811,1814,1816,1818,1820,1822],{"class":37,"line":92},[35,1810,596],{"class":82},[35,1812,1813],{"class":45},"\"Clash\"",[35,1815,171],{"class":82},[35,1817,105],{"class":78},[35,1819,177],{"class":82},[35,1821,199],{"class":45},[35,1823,1824],{"class":82},"].isin(clash_ids)\n",[35,1826,1827],{"class":37,"line":99},[35,1828,96],{"emptyLinePlaceholder":95},[35,1830,1831,1834,1837,1840,1842,1845,1847,1850,1852,1855,1857,1860,1862,1864],{"class":37,"line":137},[35,1832,1833],{"class":78},"with",[35,1835,1836],{"class":82}," pd.ExcelWriter(",[35,1838,1839],{"class":45},"\"leave_checked.xlsx\"",[35,1841,114],{"class":82},[35,1843,1844],{"class":117},"engine",[35,1846,105],{"class":78},[35,1848,1849],{"class":45},"\"xlsxwriter\"",[35,1851,114],{"class":82},[35,1853,1854],{"class":117},"datetime_format",[35,1856,105],{"class":78},[35,1858,1859],{"class":45},"\"dd mmm yyyy\"",[35,1861,799],{"class":82},[35,1863,86],{"class":78},[35,1865,1866],{"class":82}," xw:\n",[35,1868,1869,1872,1875,1877,1879,1882,1885,1887,1890,1892,1894,1896,1898],{"class":37,"line":158},[35,1870,1871],{"class":82},"    leave.drop(",[35,1873,1874],{"class":117},"columns",[35,1876,105],{"class":78},[35,1878,599],{"class":45},[35,1880,1881],{"class":82},").to_excel(xw, ",[35,1883,1884],{"class":117},"sheet_name",[35,1886,105],{"class":78},[35,1888,1889],{"class":45},"\"Requests\"",[35,1891,114],{"class":82},[35,1893,147],{"class":117},[35,1895,105],{"class":78},[35,1897,152],{"class":140},[35,1899,565],{"class":82},[35,1901,1902,1905,1907,1909,1911,1913,1915,1917,1919,1922,1924,1926,1929,1931,1933,1935,1937],{"class":37,"line":185},[35,1903,1904],{"class":82},"    pairs[[",[35,1906,543],{"class":45},[35,1908,114],{"class":82},[35,1910,199],{"class":45},[35,1912,114],{"class":82},[35,1914,734],{"class":45},[35,1916,114],{"class":82},[35,1918,772],{"class":45},[35,1920,1921],{"class":82},"]].to_excel(xw, ",[35,1923,1884],{"class":117},[35,1925,105],{"class":78},[35,1927,1928],{"class":45},"\"Clashes\"",[35,1930,114],{"class":82},[35,1932,147],{"class":117},[35,1934,105],{"class":78},[35,1936,152],{"class":140},[35,1938,565],{"class":82},[35,1940,1941,1944,1946,1949,1951],{"class":37,"line":205},[35,1942,1943],{"class":82},"    wb, ws ",[35,1945,105],{"class":78},[35,1947,1948],{"class":82}," xw.book, xw.sheets[",[35,1950,1889],{"class":45},[35,1952,1953],{"class":82},"]\n",[35,1955,1956,1959,1961,1964,1967,1969,1972,1974,1977,1979,1982],{"class":37,"line":1307},[35,1957,1958],{"class":82},"    red ",[35,1960,105],{"class":78},[35,1962,1963],{"class":82}," wb.add_format({",[35,1965,1966],{"class":45},"\"bg_color\"",[35,1968,1725],{"class":82},[35,1970,1971],{"class":45},"\"#FCE9E9\"",[35,1973,114],{"class":82},[35,1975,1976],{"class":45},"\"font_color\"",[35,1978,1725],{"class":82},[35,1980,1981],{"class":45},"\"#9C0006\"",[35,1983,1731],{"class":82},[35,1985,1986,1989,1991,1993,1995,1997,2000,2003,2005,2008,2011,2013,2016,2018,2021,2023,2026,2028,2031],{"class":37,"line":1312},[35,1987,1988],{"class":82},"    ws.conditional_format(",[35,1990,796],{"class":140},[35,1992,114],{"class":82},[35,1994,403],{"class":140},[35,1996,114],{"class":82},[35,1998,1999],{"class":140},"len",[35,2001,2002],{"class":82},"(leave), ",[35,2004,1100],{"class":140},[35,2006,2007],{"class":82},", {",[35,2009,2010],{"class":45},"\"type\"",[35,2012,1725],{"class":82},[35,2014,2015],{"class":45},"\"formula\"",[35,2017,114],{"class":82},[35,2019,2020],{"class":45},"\"criteria\"",[35,2022,1725],{"class":82},[35,2024,2025],{"class":45},"\"=$F2=TRUE\"",[35,2027,114],{"class":82},[35,2029,2030],{"class":45},"\"format\"",[35,2032,2033],{"class":82},": red})\n",[35,2035,2036,2039,2042,2044,2047],{"class":37,"line":1354},[35,2037,2038],{"class":82},"    ws.set_column(",[35,2040,2041],{"class":45},"\"A:E\"",[35,2043,114],{"class":82},[35,2045,2046],{"class":140},"13",[35,2048,565],{"class":82},[35,2050,2051,2053,2055,2058],{"class":37,"line":1388},[35,2052,141],{"class":140},[35,2054,190],{"class":82},[35,2056,2057],{"class":140},"sorted",[35,2059,2060],{"class":82},"(clash_ids))\n",[10,2062,2063,2064,2067,2068,18],{},"The formula rule colours a whole row when its ",[32,2065,2066],{},"Clash"," column is TRUE, as in ",[14,2069,2071],{"href":2070},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-an-entire-row-based-on-a-cell-value-with-openpyxl\u002F","Highlight an Entire Row Based on a Cell Value with openpyxl",[20,2073,2075],{"id":2074},"common-pitfalls","Common pitfalls",[2077,2078,2079,2093,2099,2105,2111],"ul",{},[2080,2081,2082,2086,2087,2089,2090,2092],"li",{},[2083,2084,2085],"strong",{},"Inclusive versus exclusive ends."," Use ",[32,2088,250],{}," for inclusive end dates and ",[32,2091,174],{}," for exclusive ones; mixing them flags back-to-back ranges or misses one-day overlaps.",[2080,2094,2095,2098],{},[2083,2096,2097],{},"Comparing only with the previous row."," A long range can contain several later ones; compare with the running maximum end.",[2080,2100,2101,2104],{},[2083,2102,2103],{},"Unsorted data."," Every sequential method assumes rows sorted by group and start date.",[2080,2106,2107,2110],{},[2083,2108,2109],{},"Reversed ranges."," Rows with the end before the start silently break the logic; report them first.",[2080,2112,2113,2116,2117,2120],{},[2083,2114,2115],{},"Times on the dates."," A start of 9 July 00:00 and an end of 9 July 17:00 behave differently from whole days; normalise with ",[32,2118,2119],{},"dt.normalize()"," if only dates matter.",[20,2122,2124],{"id":2123},"conclusion","Conclusion",[10,2126,2127],{},"Two ranges overlap when each starts on or before the other's end. Sort by group and start, compare each start with the running maximum of earlier ends to flag clashes, self-merge within groups to list each overlapping pair with its length, and number continuous periods with a cumulative sum to merge overlaps and measure gaps. Check for reversed ranges first, decide whether ends are inclusive, and write the clashes back to Excel with the affected rows highlighted.",[20,2129,2131],{"id":2130},"frequently-asked-questions","Frequently asked questions",[10,2133,2134,2137],{},[2083,2135,2136],{},"How do I check if two date ranges overlap in pandas?","\nTwo ranges overlap when start_a \u003C= end_b and start_b \u003C= end_a, for inclusive end dates. Apply the comparison to whole columns — for example after a self-merge within each group — to test many pairs at once.",[10,2139,2140,2143,2144,2147],{},[2083,2141,2142],{},"How do I find overlapping bookings for the same room or employee?","\nSort by the group column and start date, then compare each start with the running maximum of earlier end dates in the group, computed with ",[32,2145,2146],{},"transform(lambda s: s.cummax().shift())"," on the grouped end column. Rows whose start is on or before that value overlap an earlier booking.",[10,2149,2150,2153],{},[2083,2151,2152],{},"How do I merge overlapping date ranges into one?","\nSort by start, mark rows whose start is after the running maximum end of the rows before, take the cumulative sum of that mark as a period number, and aggregate each period to its minimum start and maximum end.",[10,2155,2156,2159],{},[2083,2157,2158],{},"Do ranges that touch count as overlapping?","\nWith inclusive end dates, a range ending on the 8th and one starting on the 9th do not overlap. When merging into continuous periods you may still want to join them; allow a one-day gap in the comparison to do so.",[20,2161,2163],{"id":2162},"related","Related",[2077,2165,2166,2172,2179,2186],{},[2080,2167,2168,2169,2171],{},"Up one level: ",[14,2170,17],{"href":16}," — parsing, grouping and date arithmetic.",[2080,2173,2174,2178],{},[14,2175,2177],{"href":2176},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Fexpand-date-ranges-into-one-row-per-day-with-pandas\u002F","Expand Date Ranges into One Row per Day with pandas"," — the day-by-day alternative for counting.",[2080,2180,2181,2185],{},[14,2182,2184],{"href":2183},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fjoin-rows-to-effective-date-ranges-with-pandas\u002F","Join Rows to Effective Date Ranges with pandas"," — matching dates to ranges from another table.",[2080,2187,2188,2192],{},[14,2189,2191],{"href":2190},"\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"," — counting working days instead of calendar days.",[2194,2195,2196],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}",{"title":30,"searchDepth":92,"depth":92,"links":2198},[2199,2200,2201,2202,2203,2204,2205,2206,2207,2208,2209],{"id":22,"depth":92,"text":23},{"id":218,"depth":92,"text":219},{"id":522,"depth":92,"text":523},{"id":660,"depth":92,"text":661},{"id":1134,"depth":92,"text":1135},{"id":1622,"depth":92,"text":1623},{"id":1768,"depth":92,"text":1769},{"id":2074,"depth":92,"text":2075},{"id":2123,"depth":92,"text":2124},{"id":2130,"depth":92,"text":2131},{"id":2162,"depth":92,"text":2163},"2026-09-29","Catch double bookings, overlapping contracts and clashing leave requests in a sheet of start and end dates: the overlap test, checking each record against the previous one per group, listing every overlapping pair, merging overlaps into continuous periods, finding gaps, and highlighting the clashes in Excel.","md",[2214,2216,2218,2220],{"q":2136,"a":2215},"Two ranges overlap when start_a \u003C= end_b and start_b \u003C= end_a, for inclusive end dates. Apply the comparison to whole columns — for example after a self-merge within each group — to test many pairs at once.",{"q":2142,"a":2217},"Sort by the group column and start date, then compare each start with the running maximum of earlier end dates in the group, computed with transform(lambda s: s.cummax().shift()) on the grouped end column. Rows whose start is on or before that value overlap an earlier booking.",{"q":2152,"a":2219},"Sort by start, mark rows whose start is after the running maximum end of the rows before, take the cumulative sum of that mark as a period number, and aggregate each period to its minimum start and maximum end.",{"q":2158,"a":2221},"With inclusive end dates, a range ending on the 8th and one starting on the 9th do not overlap. When merging into continuous periods you may still want to join them; allow a one-day gap in the comparison to do so.","find overlapping date ranges with pandas",null,{"breadcrumb":2225},[2226,2229,2232],{"name":2227,"item":2228},"Home","\u002F",{"name":2230,"item":2231},"Advanced Data Transformation and Cleaning for Excel Automation","\u002Fadvanced-data-transformation-and-cleaning\u002F",{"name":17,"item":16},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffind-overlapping-date-ranges-with-pandas",{"title":2235,"description":2236},"Find Overlapping Date Ranges in Excel Data with pandas","Detect overlapping start\u002Fend date ranges per employee, room or contract with pandas: the a.start \u003C= b.end test, sort and shift with cummax, a self-merge for every pair, merging overlaps into periods, gaps between ranges and highlighted rows in Excel.","find-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",[2240,2241],"dates-and-times","pandas","how-to","npYWCJn5xJqDS1hJpnyPG9_9ZiNmNERF9OCVHBCYTrk",[2245,2249],{"title":2246,"path":2247,"stem":2248,"children":-1},"Fill Missing Dates in a Time Series from Excel with pandas","\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffill-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",{"title":2250,"path":2251,"stem":2252,"children":-1},"Fix Excel Serial Numbers Showing Instead of Dates","\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffix-excel-serial-numbers-showing-instead-of-dates","advanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffix-excel-serial-numbers-showing-instead-of-dates\u002Findex",[2254,2256],{"path":2255,"title":2230},"\u002Fadvanced-data-transformation-and-cleaning",{"path":2257,"title":17},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data",[],[2260,2263,2266,2269,2272,2275],{"path":2261,"title":2262,"kind":2242},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-overdue-dates-with-openpyxl","Highlight Overdue Dates in Excel with openpyxl",{"path":2264,"title":2265,"kind":2242},"\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":2267,"title":2268,"kind":2242},"\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":2270,"title":2271,"kind":2242},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcalculate-moving-averages-with-pandas-rolling","Calculate Moving Averages with pandas rolling",{"path":2273,"title":2274,"kind":2242},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Feomonth-and-edate-equivalents-in-pandas","EOMONTH and EDATE Equivalents in pandas",{"path":2276,"title":2277,"kind":2242},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Fyearfrac-and-days360-equivalents-in-python","YEARFRAC and DAYS360 Equivalents in Python",1790972106287]