Search code examples
pythonmatlaboverlapdate-range

Find overlapping time ranges and store the result in a logical matrix


In MATLAB R2020b I use the following code to check if time ranges overlap each other.

% start = start time of time range, array of matlab datenum's    
% finish = end time of time range, array of matlab datenum's  
overlap = bsxfun(@lt, start(:), finish(:).');
overlap = overlap & overlap.';
no_overlap = isdiag(double(overlap));

The result is a diagonal matrix in cases there is no overlap and a matrix filled with 1 (true) and 0 (false) if there is overlap.

Matrix example with no overlapping time ranges:

1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1

Matrix example with overlapping time ranges:

1 0 0 0 0
0 1 1 1 1
0 1 1 0 0
0 1 0 1 0
0 1 0 0 1

I tried to do the same thing in python to get overlapping time ranges, but I found only solutions that compare time range 1 with time range 2. But I need to compare all time ranges with each other such as time range 1 with for example time range 5.

I used the information of this post Determine Whether Two Date Ranges Overlap and came up with the following code, which is an adjusted code from Find Non-overlapping intervals among a given set of intervals:

# arr = list of start finsh in format [[start1,finish1],[start2,finish2],....]
# N = length of arr
    
def findTimeOverlap(arr, N):     
        # If there are no set of interval
        if N < 1:
            return
         
        # To store the set of overlaps
        P = []
         
        # Sort the given interval according Starting time
        arr.sort(key = lambda a:a[0])
         
        # Iterate over all the interval
        for i in range(1, N):
            # Previous interval start
            prevStart = arr[i - 1][0] # A
             
            # Previous interval end
            prevEnd = arr[i - 1][1] # B
             
            # Current interval start
            currStart = arr[i][0] # C
    
            # Current interval end
            currEnd = arr[i][1] # D
             
            # If Previous Interval is less than current Interval then we store that answer
            if max([prevStart,currStart]) < min([prevEnd,currEnd]):
                P.append([prevStart, prevEnd, currStart, currEnd])
            
        # intervals to dataframe    
        df = pd.DataFrame(P, columns=["start_A","finish_A","start_B","finish_B"])
        if not df.empty:
            df["start_A"] = pd.to_datetime(df["start_A"], unit='ns')
            df["finish_A"] = pd.to_datetime(df["finish_A"], unit='ns')
            df["start_B"] = pd.to_datetime(df["start_B"], unit='ns')
            df["finish_B"] = pd.to_datetime(df["finish_B"], unit='ns')
        return df

Only this give a comparison based on time range 1 with time range 2 and not all the time ranges compared against each other.

How can I cross-compare all my time ranges and obtain a logical matrix like in the MATLAB example?


Solution

  • Based on Adriaan's comment about Matlab, I was able to create a python solution.

    def findTimeOverlap(start,finish):
        S = np.transpose(np.zeros([len(start), len(finish)]) + start)
        F = np.zeros([len(start), len(finish)]) + finish
        T = S < F
        T = (T == np.transpose(T)).astype(int)
        Overlapping = np.count_nonzero(T - np.diag(np.diagonal(T))) != 0
        return Overlapping, T
    

    In the case of time ranges with overlapping ranges:

    start = np.array([1,2,3,4,5])
    finish = np.array([2,6,4,5,6]) 
    Overlapping, T = findTimeOverlap(start,finish)
    
    Overlapping = True
    
    T = array([[1, 0, 0, 0, 0],
               [0, 1, 1, 1, 1],
               [0, 1, 1, 0, 0],
               [0, 1, 0, 1, 0],
               [0, 1, 0, 0, 1]])
    

    In the case of time ranges with no overlapping ranges:

    start = np.array([1,2,3,4,5])
    finish = np.array([2,3,4,5,6]) 
    Overlapping, T = findTimeOverlap(start,finish)
    
    Overlapping = False
    
    T = array([[1, 0, 0, 0, 0],
               [0, 1, 0, 0, 0],
               [0, 0, 1, 0, 0],
               [0, 0, 0, 1, 0],
               [0, 0, 0, 0, 1]])