Subsetting python list into positive/negative movements/trends - python

Sorry for creating this question but I have been stuck on this question for a while.
Basically I'm trying to take a list:
numbers=[1, 2, -1, -2, 4, 5]
And subset this list into a list of list that display positive/negative movements (or trends)
The end result is to have:
subset_list = [[1, 2], [-1, -2], [4, 5]]
Basically I have been using nested while functions to append a positive movement to the subset, and when the condition is not met, the subset is appended to subset_list and then evaluates if there is a negative movement.
I keep getting an IndexError, and so far subset_list only contains [[1, 2]]
Here is my code:
numbers = [1,2,-1,-2,4,5]
subset = []
subset_list = []
subset.append(numbers[0])
i = 1
while i < (len(numbers)):
if numbers[i] <= numbers[i+1]:
subset.append(numbers[i])
i+= 1
while subset[-1] <= numbers[i] or numbers[i] <= numbers[i+1]:
subset.append(numbers[i])
i += 1
subset_list.append(subset)
subset = []
i += 1
if numbers[i] > numbers[i+1]:
subset.append(numbers[i])
i+= 1
while subset[-1] <= numbers[i] or numbers[i] <= numbers[i+1]:
subset.append(numbers[i])
i+= 1
subset_list.append(subset)
subset = []
i += 1
Thanks!
-Jake

Here is a way to re-write this:
numbers=[1,2,-1,-2,4,5]
direction = True # positive or negative
prevdirection = True
res = [[numbers[0]]]
for previtem, item in zip(numbers[:-1], numbers[1:]):
direction = True if item - previtem > 0 else False
if direction != prevdirection:
res.append([])
prevdirection = direction
res[-1].append(item)
print(res)

In python, one tends not to use the actual indexes in a list very often. Try a for-loop instead, plus a check to see whether the trend changed or not (this treats zero as a distinct trend from positive or negative - you can pretty simply change same_direction to group it one way or the other):
def same_direction(num1, num2):
# both numbers are positive, both are negative, or both are zero
return ((num1 > 0 and num2 > 0) or
(num1 < 0 and num2 < 0) or
(num1 == num2))
numbers = [1, 2, -1, -2, 4, 5]
result = [[]] #list with one sublist ready
last_number = 0
for num in numbers:
if same_direction(num, last_direction):
# No need for a new sublist, put new number in last sublist
result[-1].append(num)
else:
# trend changed, new sublist and put the number in it
result.append([num])

If change in trends always go through the sign change, you can "group" items based on a sign using itertools.groupby():
>>> from itertools import groupby
>>>
>>> [list(v) for _, v in groupby(numbers, lambda x: x < 0)]
[[1, 2], [-1, -2], [4, 5]]
We are using _ as a variable name for a "throw-away" variable since we don't need the grouping key in this case.

This is what I came up with. It is close to what you have but a little easier to read. I avoid having to increment the index counter i as much which is probably where you went wrong.
n= [1,2,-1,-2,4,5]
out=[]
i=1
tmp=[n[0]]
while i < len(n):
if n[i] >= 0 and tmp[-1] >= 0:
tmp.append(n[i])
elif n[i] < 0 and tmp[-1] < 0:
tmp.append(n[i])
else:
out.append(tmp)
tmp = [n[i]]
i = i + 1
if len(tmp) > 0: # typo fix was > 1
out.append(tmp)
print(out)

Here is my solution:
numbers = [1,2,-1,-2,4,5, 3, 2]
subset = []
subset_list = []
subset.append(numbers[0])
forward = 1
for i in range(0, len(numbers) - 1):
if ( forward == 1 ):
if numbers[i] <= numbers[i+1]:
subset.append(numbers[i+1])
else:
subset_list.append(subset)
subset = []
subset.append(numbers[i+1])
forward = 0
else:
if numbers[i] >= numbers[i+1]:
subset.append(numbers[i+1])
else:
subset_list.append(subset)
subset = []
subset.append(numbers[i+1])
forward = 1
subset_list.append(subset)
print(*subset)
print(*subset_list)
Unfortunately, I only have python 3 on my system so my answer is in python 3.

Related

How to sum even numbers from a list and then remove them?

I have tried to add sum of even numbers of a list and I am successful but in my second part I am not able to delete even numbers from the list. For example my input is [1,2,4,6,5] and When I tried this given code below, the output for sum of even numbers was 8 and new list was [1,4,5]. I want output as sum of even numbers as 12 and new list is [1,5].
n=list(map(int, input("elements of array:-").strip().split()))
even_sum = 0
for num in n:
if num%2==0:
even_sum += num
n.remove(num)
else:
odd_sum += num
print(even_sum)
print(n)
Here is a slightly more pythonic way to achieve what you want:
L = [1,2,4,6,5]
Lsum = sum([int(elem % 2 == 0) * elem for elem in L])
Lnew = [elem for elem in L if elem % 2 == 1]
You shouldn't iterate over the list and modify it.
n = [1,2,4,6,5]
odd_list = []
even_sum = 0
for num in n:
if num%2==0:
even_sum += num
else:
odd_sum += num
odd_list.append(num)
Heres a more nicer way achieving that only. It is called list comprehension
n = [1, 2, 4, 6, 5]
even_list = [i for i in n if i%2==0]
even_sum = sum(even_list)
// The OP wants an odd list as a result, revised version:
n = [1, 2, 4, 6, 5]
odd_list = [i for i in n if i%2==1]
even_sum = sum(n) - sum(odd_list)
This is happening because during iteration you are skipping some of the elements by removing others from the same sequence...
You can iterate backwards through the list instead. This eliminates the need for copying elements to a new list.
Like this:
n=list(map(int, input("elements of array:-").strip().split()))
i = len(n) -1
while i >= 0:
if n[i] % 2 == 0:
even_sum += n[i]
del n[i]
else:
odd_sum += n[i]
i -= 1

How to group a list of number together based on threshold using python while loop?

How to group a list of numbers together based on a threshold using python while loop?
For example, I have a list of numbers which are [1,2,3,2,5,6,2] and the threshold is 8. My expected output should be [[1,2,3,2], [5], [6,2]]. Since 5+6 is not equal to my threshold of 8 only 5 will be return. The function should continue till it finishes the list of numbers.
def group_numbers(num_list, threshold):
i = 0
total = 0
temp_list = []
result_list = []
while i < len(num_list) and total + num_list[i] <= threshold:
total += num_list[i]
temp_list.append(num_list[i])
print(i)
print(temp_list)
i += 1
else:
result_list.append(temp_list)
total = 0
temp_list = []
return result_list
my current code will only return [[1, 2, 3, 2]] and it will not continue. Not sure what is the problem here
There are probably better ways to do it, but that's what i have:
def group_list(lst, thresh):
output =[]
foo = []
for i in lst:
if (sum(foo) + i) > thresh:
output.append(foo)
foo = [i]
else:
foo.append(i)
output.append(foo)
return output

Loop gets stuck on the third index

Good day. I need to make a function that returns a sum of the two lowest positive integers in the list.
It didn't work out so I've made it print its each step and then I saw that it gets stuck on the third index for some reason. It says 'list index out of range' but I can't quite understand why is it if there are 5 positions in the list. Can't figute it out on my own.
def sum_two_smallest_numbers(numbers):
a = 2147483647
numers = numbers
b = a
for i in range(0, len(numbers)):
print(numbers[i])
if numbers[i] < a and numbers[i] > 0:
a = numbers[i]
numbers.pop(i)
for j in range(0, len(numers)):
if numers[j] < b and numers[j] > 0:
b = numers[j]
numers.pop(j)
return a + b
print(sum_two_smallest_numbers([19, 5, 42, 2, 77]))
This works.
a = [-1, 2, 34, 22, 4]
a = [i for i in a if i > 0]
first_min = min(a)
a.remove(first_min)
second_min = min(a)
print(first_min+second_min)
pop removes a value from the list so each time you use pop the length of your list gets shorter and right now you keep deleting numbers from your list using this algorithm.
def sum_two_smallest_numbers(numbers):
a = 2147483647
numers = numbers
b = a
ind = -1
for i in range(0, len(numbers)):
# print(numbers[i])
if numbers[i] < a and numbers[i] > 0:
a = numbers[i]
ind = i
numbers.pop(ind)
for j in range(0, len(numbers)-1):
if numers[j] < b and numers[j] > 0:
b = numers[j]
return a + b
print(sum_two_smallest_numbers([19, 5, 42, 2, 77]))
Note that this algorithm assumes there are at least 2 positive numbers in your list. Also, there are more efficient ways to do this with only one for loop.
First the rule is never modify a list while iterating on it. You are trying to circumvent it by using indices, but you compute the range before starting the loop and then remove elements from the list => at a time you get an index that is greater or equal than the size of the list.
In addition, you will remove more elements than the smallest one. Let us assume that we fixed the above error and see what happens
first iteration: 19 is less than 2147483647: a = 19 and 19 is removed from the list
next: 5 is less that 19: again a = 5 and 5 is removed which is bad because it is the second smallest element !
So you should at least change your algo to only remove the final smallest element:
def sum_two_smallest_numbers(numbers):
a = 2147483647
numers = numbers
b = a
for i in range(0, len(numbers)):
print(numbers[i])
if numbers[i] < a and numbers[i] > 0:
a = numbers[i]
numbers.remove(a)
for j in range(0, len(numers)):
if numers[j] < b and numers[j] > 0:
b = numers[j]
numers.remove(b)
return a + b
print(sum_two_smallest_numbers([19, 5, 42, 2, 77]))
But this is un-pythonic because Python lists are iterable, and not efficient because you iterate the array twice. This would be better:
def sum_two_smallest_numbers(numbers):
a = b = 2147483647 # a and b will be the 2 smallest elements
for i in numbers:
print(i)
if i < b: # if i >= b : nothing to do
if i < a: # new element is the current smallest
b = a
a = i
else: # new element is the current second smallest
b = i
# uncomment both following line if you want to remove the elements from the array
# as a side effect
# numbers.remove(a)
# numbers.remove(b)
return a + b

Find whether the largest element in the array is at least twice as much as every other number in the array?

I'm trying to run a program that finds the index of the number that is at least two times larger than all other number in the array.
Here's my code
def dominantIndex(self, nums):
max_num = max(nums)
max_i =nums.index(max_num)
if len(nums) == 1:
return nums.index(max_num)
for num in nums:
if max_num >= 2*num:
return num.index(max_num)
return -1
However , it doesn't work perfectly for all inputs. Could someone please fix it and check for inputs like :
[1,0]
[0,3,4,8]
[0,3,5,2]
This checks for many possible input problems.
Then it sorts the list to get the answer you are looking for. I decided to sort, for simplicity, but you could use other methods as well. I added comments so everything is clear, especially about the input tests, as asked.
def dominantIndex(nums):
# If the array is empty or None or not a list, return -1
if not nums or type(nums) != list:
return -1
# If the array is of length 1, return the only index, 0
elif len(nums) == 1:
return 0
sorted_numbers = sorted(nums)
# If the highest number is twice the second largest, return it's index
if sorted_numbers[-2] * 2 <= sorted_numbers[-1]:
return nums.index(sorted_numbers[-1])
else:
return -1
There actually is a library function nlargest in the heapq module.
>>> L1 = [1, 0]
>>> L2 = [0, 3, 4, 8]
>>> L3 = [0, 4, 5, 2]
>>>
>>> from heapq import nlargest
>>> def dom_ind(nums):
... a, b = nlargest(2, range(len(nums)), key=nums.__getitem__)
... return a if nums[a] >= 2 * nums[b] else -1
...
>>>
>>> dom_ind(L1)
0
>>> dom_ind(L2)
3
>>> dom_ind(L3)
-1
Use below code
def check(nums):
if len(nums) == 1:
return 0
max_num = max(nums)
ind = nums.index(max_num)
updated_array = map(lambda x: x if x != max_num else -1, nums)
if max_num >= 2*max(updated_array):
return ind
return -1
Output:
check([1,0])
0
>>> check([0,3,4,8])
3
>>> check([0,3,5,2])
-1
You can get the second highest as well;:
def dominantIndex(nums):
max_num = max(nums)
secondHighest = max( n for n in nums if n != max_num)
if max_num > 2 * secondHighest:
return nums.index(max_num)
return -1
print(dominantIndex( [1,2,39,7]))
print(dominantIndex( [1,2,3,5]))
Output:
2
-1
I'd iterate over the list and find the two largest elements. Then, if the largest element is at least twice as large as the second largest, you can return its index.
def dominantIndex(nums):
# Assumption - nums has a length of at least 2.
# Bootsrap the rist two indexes
if nums[0] > nums[1]:
max_ind = 0
next_ind = 1
else:
max_ind = 1
next_ind = 0
# Go over the rest
for i in range(2, len(nums)):
if nums[i] > nums[max_ind]:
next_ind = max_ind
max_ind = i
elif nums[i] > nums[next_ind]:
next_ind = i
if nums[max_ind] >= nums[next_ind] * 2:
retrun max_ind
return -1
I think none of the other suggested solutions works if the largest number appears multiple times in nums. This does:
def dominantIndex(self, nums):
s = sorted(nums)
if s[-1] >= 2 * s[-2]:
return nums.index(s[-1])
else:
return -1
In addition, you might want to check if nums has more than one element.

How do I loop through two lists only iterating one at a time?

Im trying to implement merge sort in python and I'd like to know what is the pythonic way of looping through two lists iterating only one of the lists each loop.
This is what I have now (using indexes)
def merge(array1, array2):
final = []
i = 0
j = 0
while i < len(array1) or j < len(array2):
if array1[i] <= array2[j]:
final.append(array1[i])
i += 1
elif array2[j] < array1[i]:
final.append(array2[j])
j += 1
# Finished one of the arrays
if i == len(array1):
final.extend(array2[j:])
break
elif j == len(array2):
final.extend(array1[i:])
break
return final
Thanks.
I'm not sure not sure your problem here is using indices, but there's a lot of unneeded code.
Firstly, we can change your initialisers to:
final, i, j = [], 0, 0
Next, if we change your while condition we can remove the breaks:
while i < len(array1) and j < len(array2):
Your elif isn't meaningful as it will always be true, we can thus make your if:
if array1[i] <= array2[j]:
final.append(array1[i])
i += 1
else:
final.append(array2[j])
j += 1
And now because we're automatically breaking out of the loop we don't need the breaks and can move the extends outside:
def merge(array1, array2):
final, i, j = [], 0, 0
while i < len(array1) and j < len(array2):
if array1[i] <= array2[j]:
final.append(array1[i])
i += 1
else:
final.append(array2[j])
j += 1
final.extend(array1[i:])
final.extend(array2[j:])
return final
This gives you smaller, more readable code without actually changing the way you're doing anything, that way you'll still understand it.
Note that we can perform:
final.extend(array1[i:])
final.extend(array2[j:])
outside the loop because one array will have contents, for example [7,9], while the other will be empty([]):
>>> final = [3,6]
>>> array1 = [3,6]
>>> array2 = [7,9]
>>> i = 2
>>> j = 0
>>> array1[i:]
[]
>>> array2[j:]
[7, 9]
>>> final.extend(array1[i:])
>>> final
[3, 6]
>>> final.extend(array2[j:])
>>> final
[3, 6, 7, 9]

Categories