1

Is there any efficient way to generate a list of N random timeframes which do not intersect each other given the initial lower and upper bounds as well as the time intervals that these time periods should have. For example in the following case I want 10 timestamps between 09:00-17:00:

Initial start time: {datetime} YYYY-MM-DD 09:00:00
Initial end time: {datetime} YYYY-MM-DD 17:00:00
Timestamp intervals (in minutes): [32 24 4 20 40 8 27 18 3 4] 

where the first time period 32 minutes long, the next 24 and so on.

The way I am doing it at the moment is by using more or less the following code snippet:

def random_time(start, end, timeframe=None):
     sec_diff = int((end - start).total_seconds())
     secs_to_add = random.randint(0, sec_diff)
     return start + timedelta(seconds=secs_to_add)

def in_datetimes_range(self, x, starts, ends):
     return np.any((starts <= x) & (x <= ends))

n = 10   
dadate = datetime.now()
year = self.dadate.year
month = self.dadate.month
day = self.dadate.day

start = datetime(year, month, day, 9, 0, 0)
end = datetime(year, month, day, 17, 0, 0)
timeframe = [32 24 4 20 40 8 27 18 3 4]

startTimes = []
endTimes = []

for i in range(0, n):
   while True:
      startTime = random_time(start, end)
      endTime = startTime + timedelta(minutes=int(timeframe[i]))

      if startTimes:
         startTimesAsNpArray = np.array(startTimes)
         endTimesAsNpArray = np.array(endTimes)
         #check if new time period falls inside existing timeframes or if existing timeframes fall within new time period
         inner_bound = np.logical_or(in_datetimes_range(startTime, startTimesAsNpArray, endTimesAsNpArray), in_datetimes_range(endTime, startTimesAsNpArray, endTimesAsNpArray))
         outer_bound = np.logical_or(in_datetimes_range(startTimesAsNpArray, startTime, endTime), in_datetimes_range(endTimesAsNpArray, startTime, endTime))

      if not inner_bound and not outer_bound:
         startTimes.append(startTime)
         endTimes.append(endTime)
         break

but this is really inefficient and I was looking for something more reliable if possible.

2 Answers 2

1

Here is a way to do it: the idea is that if we remove the total duration of the periods from the time available, generate start times in the period that is left, and then postpone them with the cumulated periods before them, we are sure that the intervals won't overlap.

from datetime import datetime, timedelta
import random


def generate_periods(start, end, durations):
    durations = [timedelta(minutes=m) for m in durations]

    total_duration = sum(durations, timedelta())
    nb_periods = len(durations)
    open_duration = (end - start) - total_duration

    delays = sorted(timedelta(seconds=s) 
                    for s in random.sample(range(0, int(open_duration.total_seconds())), nb_periods))
    periods = []
    periods_before = timedelta()
    for delay, duration in zip(delays, durations):
        periods.append((start + delay + periods_before,
                        start + delay + periods_before + duration))
        periods_before += duration

    return periods

Sample run:

durations = [32, 24, 4, 20, 40, 8, 27, 18, 3, 4] 
start_time = datetime(2019, 9, 2, 9, 0, 0)
end_time = datetime(2019, 9, 2, 17, 0, 0)
generate_periods(start_time, end_time, durations)

# [(datetime.datetime(2019, 9, 2, 9, 16, 1),
#   datetime.datetime(2019, 9, 2, 9, 48, 1)),
#  (datetime.datetime(2019, 9, 2, 9, 58, 57),
#   datetime.datetime(2019, 9, 2, 10, 22, 57)),
#  (datetime.datetime(2019, 9, 2, 10, 56, 41),
#   datetime.datetime(2019, 9, 2, 11, 0, 41)),
#  (datetime.datetime(2019, 9, 2, 11, 2, 37),
#   datetime.datetime(2019, 9, 2, 11, 22, 37)),
#  (datetime.datetime(2019, 9, 2, 11, 48, 17),
#   datetime.datetime(2019, 9, 2, 12, 28, 17)),
#  (datetime.datetime(2019, 9, 2, 13, 4, 28),
#   datetime.datetime(2019, 9, 2, 13, 12, 28)),
#  (datetime.datetime(2019, 9, 2, 15, 13, 3),
#   datetime.datetime(2019, 9, 2, 15, 40, 3)),
#  (datetime.datetime(2019, 9, 2, 16, 6, 44),
#   datetime.datetime(2019, 9, 2, 16, 24, 44)),
#  (datetime.datetime(2019, 9, 2, 16, 37, 42),
#   datetime.datetime(2019, 9, 2, 16, 40, 42)),
#  (datetime.datetime(2019, 9, 2, 16, 42, 50),
#   datetime.datetime(2019, 9, 2, 16, 46, 50))]
Sign up to request clarification or add additional context in comments.

1 Comment

thanks for the feedback. It seems to be doing the job I want. To be honest I was thinking a solution in a more randomized way but your approach of removing the total duration of the periods from the time available, and then generate the start times in the period that is left makes more sense.
0

Like this?

import pandas as pd 
from datetime import datetime

date = datetime.now()

start = datetime(date.year, date.month, date.day, 9, 0, 0)
end = datetime(date.year, date.month, date.day, 17, 0, 0)

interval = 32
periods = (end-start).seconds/60/interval

times = pd.date_range(start.strftime("%m/%d/%Y, %H:%M:%S"), periods=periods, freq=str(interval)+'min')

or like this

# =============================================================================
# or if you want the results as a dataframe
# =============================================================================

def xyz(interval):
    date = datetime.now()
    start = datetime(date.year, date.month, date.day, 9, 0, 0)
    end = datetime(date.year, date.month, date.day, 17, 0, 0)
    periods = (end-start).seconds/60/interval
    return pd.date_range(start.strftime("%m/%d/%Y, %H:%M:%S"), periods=periods, freq=str(interval)+'min')

timeframes = [32,24,4,20,40,8,27,18,3,4]

df_output=pd.DataFrame(index=timeframes, data=[xyz(x) for x in timeframes])

2 Comments

The essential part of the questions was to generate interval 'which do not intersect each other ' , I don't see how you address that here?
@Thierry Lathuille, oh you are right. I am very sorry. Should I delete my answer?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.