0

An example of a list showing date, start hour, finish hour and description:

A = [['24/02/2022', '11', '12', 'task1'],
     ['24/02/2022', '9', '10', 'task2'],
     ['24/02/2022', '14', '18', 'task1'],
     ['24/02/2022', '13', '14', 'task3']]

I would like to create a timespan for each list entry and check for time overlap between entries. List can contain a variable number of entries.

But I'm stuck on how to loop through A containing n number of lists to generate the datetime objects? I'm not sure how to extract the information and use something like datetime.combine(date, time) to create the objects.

Any help would be greatly appreciated.

BeRT2me
  • 12,699
  • 2
  • 13
  • 31
filipe
  • 1
  • 1

3 Answers3

0

One could put the data in a pd.DataFrame, then convert the date column to pd.Timestamp, and create df.Timestamp for start and finish by adding the respective hours and the date:

import pandas as pd
from itertools import combinations

A = [['24/02/2022', '11', '12', 'task1'],['24/02/2022', '9', '10', 'task2'],['24/02/2022', '14', '18', 'task1'],['24/02/2022', '13', '14', 'task3'] ]
df = pd.DataFrame(A, columns=['date', 'start', 'finish', 'description'])
df.date = pd.to_datetime(df.date)
df.start = df.apply(lambda x: x.date + pd.DateOffset(hours=float(x.start)), axis=1)
df.finish = df.apply(lambda x: x.date + pd.DateOffset(hours=float(x.finish)), axis=1)
df.drop(columns=['date'], inplace=True)

print(df)

prints:

            start              finish description
0 2022-02-24 11:00:00 2022-02-24 12:00:00       task1
1 2022-02-24 09:00:00 2022-02-24 10:00:00       task2
2 2022-02-24 14:00:00 2022-02-24 18:00:00       task1
3 2022-02-24 13:00:00 2022-02-24 14:00:00       task3

Then one could iterate over each pair (generated using itertools on the index) and check if either the start or finish timestamp of the latter (with index j) is in the range of the former (with index i), and, if so, add to a set overlaps the tuple (i, j, overlap), as follows:

a, b = list(zip(*combinations(df.index, 2)))
overlaps = set()
iterable = zip(df.loc[a, :].iterrows(), df.loc[b, :].iterrows())
for (i, r1), (j, r2) in iterable:
    overlap = None
    if r1.start < r2.start < r1.finish:
        if r2.finish < r1.finish:
            overlap = r2.finish - r2.start
        else:
            overlap = r1.finish - r2.start
    elif r1.start < r2.finish < r1.finish:
        if r1.start < r2.start:
            overlap = r2.finish - r2.start
        else:
            overlap = r2.finish - r1.start
    if overlap is not None:
        overlaps.add((i, j, overlap))

for i, j, overlap in overlaps:
    print(f'Entries {i} and {j} overlap by {overlap}.')
Michael Hodel
  • 2,845
  • 1
  • 5
  • 10
0

Literally, iterate through the list creating start and end datetime objects.
Here I simply print them but you can store them in another list, whatever, for later calculations.

import datetime

A = [['24/02/2022', '11', '12', 'task1'],['24/02/2022', '9', '10', 'task2'],['24/02/2022', '14', '18', 'task1'],['24/02/2022', '13', '14', 'task3'] ]

>>> A
[['24/02/2022', '11', '12', 'task1'], ['24/02/2022', '9', '10', 'task2'], ['24/02/2022', '14', '18', 'task1'], ['24/02/2022', '13', '14', 'task3']]
>>> for item in A:
...  start = datetime.datetime.combine(datetime.datetime.strptime(item[0],'%d/%m/%Y'), datetime.time(int(item[1]), 0)) 
...  end = datetime.datetime.combine(datetime.datetime.strptime(item[0],'%d/%m/%Y'), datetime.time(int(item[2]), 0)) 
...  print(start, end, item[3])
... 
2022-02-24 11:00:00 2022-02-24 12:00:00 task1
2022-02-24 09:00:00 2022-02-24 10:00:00 task2
2022-02-24 14:00:00 2022-02-24 18:00:00 task1
2022-02-24 13:00:00 2022-02-24 14:00:00 task3

See: Pythonic way to combine datetime.date and datetime.time objects for datetime.combine answer

Edit subject to your comment:

Why do the hard work when someone has already done that work for you?

There is a module called DateTimeRange
https://pypi.org/project/DateTimeRange/

Not tested exhaustively:

import datetime
from datetimerange import DateTimeRange

A = [
     ['24/02/2022', '11', '12', 'task0'], ['24/02/2022', '9', '10', 'task1'], ['24/02/2022', '14', '18', 'task2'],
     ['24/02/2022', '13', '14', 'task3'], ['24/02/2022', '13', '15', 'task4']
    ]

# periods will be [start, end, task_id, overlaps, overlaps_with]
periods = []

for item in A:
    start = datetime.datetime.combine(datetime.datetime.strptime(item[0],'%d/%m/%Y'), datetime.time(int(item[1]), 0))
    end = datetime.datetime.combine(datetime.datetime.strptime(item[0],'%d/%m/%Y'), datetime.time(int(item[2]), 0))
    periods.append([start, end, item[3], False, ''])

for period in periods:
    if period[3] == True: # test for already True overlap
        continue
    range = DateTimeRange(period[0], period[1])
    for test_loop in periods:
        if test_loop[2] == period[2]: # test for testing against self using task string
            continue
        test = DateTimeRange(test_loop[0], test_loop[1])
        if test.is_intersection(range):
            print (test_loop[2], "overlaps", period[2])
            period[3] = True
            period[4] += test_loop[2]+', '

for period in periods:
    print(period)

Results in:

task3 overlaps task2
task4 overlaps task2
task2 overlaps task3
task4 overlaps task3
task2 overlaps task4
task3 overlaps task4
[datetime.datetime(2022, 2, 24, 11, 0), datetime.datetime(2022, 2, 24, 12, 0), 'task0', False, '']
[datetime.datetime(2022, 2, 24, 9, 0), datetime.datetime(2022, 2, 24, 10, 0), 'task1', False, '']
[datetime.datetime(2022, 2, 24, 14, 0), datetime.datetime(2022, 2, 24, 18, 0), 'task2', True, 'task3, task4, ']
[datetime.datetime(2022, 2, 24, 13, 0), datetime.datetime(2022, 2, 24, 14, 0), 'task3', True, 'task2, task4, ']
[datetime.datetime(2022, 2, 24, 13, 0), datetime.datetime(2022, 2, 24, 15, 0), 'task4', True, 'task2, task3, ']
Rolf of Saxony
  • 21,661
  • 5
  • 39
  • 60
  • thankyou, with the first option, how could I Iterate through all possible combinations to check for any overlap? I have got what you have working, storing the start / end datetime objects in a list, but can't figure out how to check each pair against each other for overlap – filipe Apr 24 '22 at 02:21
  • @filipe See my latest edit – Rolf of Saxony Apr 24 '22 at 15:23
0
import datetime as dt
from datetime import datetime
A = [['24/02/2022', '11', '12', 'task1'], ['24/02/2022', '9', '10', 'task2'],
     ['24/02/2022', '14', '18', 'task1'], ['24/02/2022', '13', '14', 'task3']]

A = [[datetime.combine(datetime.strptime(date, '%d/%m/%Y'), dt.time(int(start, 0))),
      datetime.combine(datetime.strptime(date, '%d/%m/%Y'), dt.time(int(end, 0))),
      task] for date, start, end, task in [*A]]
for item in A:
    print(*item)

Output:

2022-02-24 11:00:00 2022-02-24 12:00:00 task1
2022-02-24 09:00:00 2022-02-24 10:00:00 task2
2022-02-24 14:00:00 2022-02-24 18:00:00 task1
2022-02-24 13:00:00 2022-02-24 14:00:00 task3

Alternative Pandas method:

import pandas as pd

A = [['24/02/2022', '11', '12', 'task1'], ['24/02/2022', '9', '10', 'task2'],
     ['24/02/2022', '14', '18', 'task1'], ['24/02/2022', '13', '14', 'task3']]

df = pd.DataFrame(A, columns=['date', 'start', 'finish', 'description'])
df.start = pd.to_datetime(df.date + ' ' + df.start + ":00:00")
df.finish = pd.to_datetime(df.date + ' ' + df.finish + ":00:00")
df = df[['start', 'finish', 'description']]
print(df)

Output:

                start              finish description
0 2022-02-24 11:00:00 2022-02-24 12:00:00       task1
1 2022-02-24 09:00:00 2022-02-24 10:00:00       task2
2 2022-02-24 14:00:00 2022-02-24 18:00:00       task1
3 2022-02-24 13:00:00 2022-02-24 14:00:00       task3
BeRT2me
  • 12,699
  • 2
  • 13
  • 31
  • thankyou, with the first option, how could I Iterate through all possible combinations to check for any overlap? – filipe Apr 24 '22 at 02:19