Open In App

Python – Group each increasing and decreasing run in list

Last Updated : 12 Apr, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Given a list, the task is to write a Python program to group each increasing and decreasing run.  This is known as a monotonous grouping. A list is monotonic if it is either monotone increasing or monotone decreasing. A list A is monotone decreasing if for all i <= j, A[i] >= A[j].

Example:

Input : test_list = [5, 6, 2, 9, 7, 1, 10, 4, 2, 1, 11, 12, 2]
Output : [[5, 6], [2], [9], [7, 1], [10], [4, 2, 1], [11, 12], [2]]
Explanation : 6 > 5 and then 2 is smaller than 6, hence becomes decreasing and new group is started. 2 and 9 being peak or transit elements, belong to individual groups.

Input : test_list = [5, 6, 2, 9, 7, 1, 10, 4, 2, 1]
Output : [[5, 6], [2], [9], [7, 1], [10], [4, 2, 1]]
Explanation : 6 > 5 and then 2 is smaller than 6, hence becomes decreasing and new group is started. 2 and 9 being peak or transit elements, belong to individual groups.

Example : Using loop  + zip()

In this, every two lists each starting from 0th and 1st index are zipped, and then each element from both are compared to check run and change flag accordingly. The initial flag value is evaluated on basis of which of the first 2 elements is greater, post that flag is toggled to have appropriate run grouping.

Step-by-step approach:

  • Create an empty list called “res” to store the result.
  • Create an empty list called “temp” to store temporary sublists.
  • Create a boolean variable called “is_up” and initialize it to True.
  • Check if the first element of the list is greater than the second element. If it is, then set “is_up” to False.
  • Use a for loop and the zip() function to iterate through the list and group the elements into monotonous sublists.
  • Append the current element to the “temp” list.
  • Check if the next element is greater than the current element and “is_up” is False, or if the next element is less than the current element and “is_up” is True. If either of these conditions is True, then append the “temp” list to the “res” list and create a new empty “temp” list.
  • Toggle the value of “is_up” to switch between increasing and decreasing sequences.
  • Append the last element to the “temp” list.
  • Append the final “temp” list to the “res” list.
  • Print the result.

Below is the implementation of the above approach:

Python3




# Python3 code to demonstrate working of
# Monotonous grouping in List
# Using loop + zip()
 
# initializing list
test_list = [5, 6, 2, 9, 7, 1, 10, 4, 2, 1, 11, 12, 2]
              
# printing original list
print("The original list is : " + str(test_list))
 
res = []
temp = []
is_up = True
if test_list[0] > test_list[1]:
    is_up = False
for curr, nex in zip(test_list, test_list[1:]):
    temp.append(curr)
     
    # checking for increasing or decreasing to change list
    if (nex > curr and not is_up) or (nex < curr and is_up):
        res.append(temp)
        temp = []
         
        # toggling
        is_up = not is_up
 
temp.append(nex)
res.append(temp)
 
# printing result
print("Monotonous grouping : " + str(res))


Output

The original list is : [5, 6, 2, 9, 7, 1, 10, 4, 2, 1, 11, 12, 2]
Monotonous grouping : [[5, 6], [2], [9], [7, 1], [10], [4, 2, 1], [11, 12], [2]]

Time Complexity: O(n)
Auxiliary Space: O(n)

Method 2: Using Simple for loop only.

The approach of the above program is to group the elements of a list into monotonically increasing or decreasing sublists. The program accomplishes this by iterating through the input list and maintaining a temporary sublist. When a change in direction is detected, the temporary sublist is added to the result list and a new temporary sublist is started with the current element. The direction of the new temporary sublist is set based on the direction of the previous sublist. Finally, the last temporary sublist is added to the result list. The resulting list contains all the monotonically increasing or decreasing sublists in the original list.

Below is the implementation:

Python3




# initializing list
test_list = [5, 6, 2, 9, 7, 1, 10, 4, 2, 1, 11, 12, 2]
 
# printing original list
print("The original list is : " + str(test_list))
 
res = []  # list to store the resulting sublists
# list to store the current sublist, initialized with the first element of the input list
temp = [test_list[0]]
is_up = True  # flag to indicate whether the current sublist is increasing or decreasing
 
# loop through the input list starting at the second element
for i in range(1, len(test_list)):
    # check if the current element is less than or greater than the previous element
    if test_list[i] > test_list[i-1] and not is_up:
        # if the current element is greater than the previous element and the current sublist was previously decreasing,
        # add the current sublist to the result list, start a new sublist with the current element, and set the flag to indicate increasing
        res.append(temp)
        temp = [test_list[i]]
        is_up = True
    elif test_list[i] < test_list[i-1] and is_up:
        # if the current element is less than the previous element and the current sublist was previously increasing,
        # add the current sublist to the result list, start a new sublist with the current element, and set the flag to indicate decreasing
        res.append(temp)
        temp = [test_list[i]]
        is_up = False
    else:
        # if the direction of the current sublist has not changed, add the current element to the current sublist
        temp.append(test_list[i])
 
# add the final sublist to the result list
res.append(temp)
 
# print the result
print("Monotonous grouping : " + str(res))


Output

The original list is : [5, 6, 2, 9, 7, 1, 10, 4, 2, 1, 11, 12, 2]
Monotonous grouping : [[5, 6], [2], [9], [7, 1], [10], [4, 2, 1], [11, 12], [2]]

Time Complexity: O(n), where n is the length of the input list. 
Auxiliary Space: O(n), where n is the length of the input list. 

Method#3: Using Recursive method.

Algorithm:

  1. If the length of the list is less than 2, return a list containing the list itself.
  2. Initialize an empty list res, a temporary list temp with the first element of the list, and a boolean flag is_up to indicate
  3. whether the current monotonic sequence is increasing or decreasing.
  4. Iterate over the list starting from the second element, and compare it with the previous element to check if the monotonic sequence has changed.
  5. If the monotonic sequence has changed, add the current temp list to the result list res, and recursively call the function on the remaining part of the list starting from the current index. Concatenate the result of the recursive call to the res list and return it.
  6. If the entire list is monotonic, return a list containing the temp list.

Python3




def monotonous_grouping_recursive(lst):
    if len(lst) < 2:
        return [lst]
     
    res = []
    temp = [lst[0]]
    is_up = lst[1] > lst[0]
     
    for i in range(1, len(lst)):
        curr = lst[i]
        prev = lst[i-1]
         
        # checking for increasing or decreasing to change list
        if (curr > prev and not is_up) or (curr < prev and is_up):
            res += [temp] + monotonous_grouping_recursive(lst[i:])
            return res
         
        temp.append(curr)
     
    # if the entire list is monotonic, return the list
    return [temp]
     
test_list = [5, 6, 2, 9, 7, 1, 10, 4, 2, 1, 11, 12, 2]
print("The original list is : " + str(test_list))
print("Monotonous grouping : " + str(monotonous_grouping_recursive(test_list)))


Output

The original list is : [5, 6, 2, 9, 7, 1, 10, 4, 2, 1, 11, 12, 2]
Monotonous grouping : [[5, 6], [2, 9], [7, 1], [10, 4, 2, 1], [11, 12], [2]]

Time complexity: O(nlogn) in the worst case where n is the length of the input list. This is because the function is called recursively on roughly half of the input list each time, and the length of each sub-list passed to the recursive call decreases by at least 1. The zip() function used in the iterative version has a time complexity of O(n), so the iterative version has a better worst-case time complexity of O(n).

Space complexity: O(n) in the worst case where n is the length of the input list, due to the recursion stack and the res list.

Method#4: Using numpy:

  1. Convert the input list to a NumPy array.
  2. Compute the differences between adjacent elements in the array using NumPy’s diff function.
  3. Compute the sign of each difference using NumPy’s sign function.
  4. Identify the indices where the sign changes from positive to negative or vice versa using NumPy’s where function.
  5. Split the input array into sub-arrays at the identified indices using NumPy’s split function.
  6. Convert each sub-array back to a list using a list comprehension.
  7. Return the list of sub-lists.

Python3




import numpy as np
 
def monotonous_grouping_numpy(lst):
    lst = np.array(lst)
    diff = np.diff(lst)
    split_indices = np.where(np.diff(np.sign(diff)) != 0)[0] + 1
    split_lists = np.split(lst, split_indices)
    return [list(l) for l in split_lists]
 
test_list = [5, 6, 2, 9, 7, 1, 10, 4, 2, 1, 11, 12, 2]
print("The original list is : " + str(test_list))
print("Monotonous grouping : " + str(monotonous_grouping_numpy(test_list)))
#This code is contributed by Rayudu.


Output:

The original list is : [5, 6, 2, 9, 7, 1, 10, 4, 2, 1, 11, 12, 2]
Monotonous grouping : [[5], [6], [2], [9, 7], [1], [10, 4, 2], [1, 11], [12, 2]]

Time complexity:

Converting the input list to a NumPy array takes O(n) time, where n is the length of the input list.
Computing the differences and signs of adjacent elements takes O(n) time as well.
Identifying the split indices using the where function takes O(n) time.
Splitting the array into sub-arrays using the split function takes O(k) time, where k is the number of split indices.
Converting each sub-array back to a list using a list comprehension takes O(kn) time.
Overall, the time complexity is O(kn).
Auxiliary Space:

Converting the input list to a NumPy array and computing the differences and signs of adjacent elements each require O(n) space.
Identifying the split indices using the where function requires O(k) space.
Splitting the array into sub-arrays using the split function requires O(n) space.
Converting each sub-array back to a list using a list comprehension requires O(kn) space.
Overall, the space complexity is O(kn).



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads