Subtract dynamic value from every value in list - python

Apologies if my explanation below is poor, but I am fairly new to python scripting.
I'm trying to find a way to product a list of number that subtracting a set_value from an item in a list, then take the new_value and subtract it from the next item in the list, and so on.
For example:
subtraction_list = []
set_value = 100
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
set_value - list[0] = 99
new_set_value = 99
new_set_value - list[1] = 98
...
subtraction_list = [99, 97, 94, 90, 85, 79, 72, 64, 55, 45]
Regardless, any help would be greatly appreciated!

You can do this in a functional way by leveraging itertools.accumulate to make an iterator the yields the cumulative sum of your list.
It works like this:
from itertools import accumulate
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
m = accumulate([0] + l) # in 3.8 we can use accumulate(l, initial = 0)
list(m)
# [0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
You can zip this with your value and use it as part of the subtraction allowing you to avoid explicitly managing the state of the variable:
from itertools import accumulate
set_value = 100
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
m = accumulate([0]+l)
subtraction_list = [set_value - diff - n for diff, n in zip(m, l)]
# [99, 97, 94, 90, 85, 79, 72, 64, 55, 45]
For something completely different – if you are using python 3.8, you can use the walrus := operator here:
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
set_value = 100
subtraction_list = [set_value := set_value - n for n in l]
[One note: don't use list as a variable name. list is a built-in type in python that you can overwrite, which leads to hard-to-find bugs.]

First, you shouldn't use the variable name list, because it's the name of a built-in function, so you can try this with a simple loop:
set_value = 100
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
final_list=[]
for i in lst:
final_list.append(set_value-i)
set_value=set_value-i
final_list
>>>[99, 97, 94, 90, 85, 79, 72, 64, 55, 45]

Simlply loop through the list
subtraction_list = []
set_value = 100
list_of = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in range(len(list_of)):
set_value = set_value - list_of[i]
subtraction_list.append(set_value)

Related

How to find the index of the last odd number in a list, without reversing the list?

Have this so far, and essentially want to get there is something wrong with the position of last_odd as the compiler says the pop index is out of range?
def remove_last_odd(numbers):
has_odd = False
last_odd = 0
for num in range(len(numbers)):
if numbers[num] % 2 == 1:
has_odd = True
last_odd = numbers[num]
if has_odd:
numbers.pop(last_odd)
numbers = [1, 7, 2, 34, 8, 7, 2, 5, 14, 22, 93, 48, 76, 15, 6]
Remove the item at the given position in the list, and return it. If no index is specified, a.pop() removes and returns the last item in the list.
So basically, the solution to your problem would be to replace last_odd = numbers[num] with last_odd = num.
As #DeepSpace said, list.pop will
Remove the item at the given position in the list, and return it. If no index is specified, a.pop() removes and returns the last item in the list.
So basically, the solution to your problem would be to replace last_odd = numbers[num] with last_odd = num.
list.pop() receive index as argument and remove the value. So, last_odd should be assigned to num instead of numbers[num]
Your function doesn't have return value yet. It should return numbers list.
def remove_last_odd(numbers):
for num in range(len(numbers)):
if numbers[num] % 2 == 1:
last_odd = num
numbers.pop(last_odd)
return numbers
numbers = [1, 7, 2, 34, 8, 7, 2, 5, 14, 22, 93, 48, 76, 15, 6]
print(remove_last_odd(numbers))
# [1, 7, 2, 34, 8, 7, 2, 5, 14, 22, 93, 48, 76, 6]
Or iterating numbers value and using remove() method instead of pop():
def remove_last_odd(numbers):
for num in numbers:
if num % 2: last_odd = num
numbers.remove(last_odd)
return numbers
numbers = [1, 7, 2, 34, 8, 7, 2, 5, 14, 22, 93, 48, 76, 15, 6]
print(remove_last_odd(numbers))
# [1, 7, 2, 34, 8, 7, 2, 5, 14, 22, 93, 48, 76, 6]
No need to reverse the list but you can search it in reverse.
def remove_last_odd(numbers):
for i in range(len(numbers)-1, -1, -1):
if numbers[i] & 1:
numbers.pop(i)
break
numbers = [1, 7, 2, 34, 8, 7, 2, 5, 14, 22, 93, 48, 76, 15, 6]
remove_last_odd(numbers)
print(numbers)
Output:
[1, 7, 2, 34, 8, 7, 2, 5, 14, 22, 93, 48, 76, 6]
Option:
If you insist on a 'forward' search then:
def remove_last_odd(numbers):
ri = -1
for i, v in enumerate(numbers):
if v & 1:
ri = i
if ri >= 0:
numbers.pop(ri)
To get the index of the last odd element you can use the reversed() iterator which will not reverse the original list object.
A quick way to get the index is:
>>> numbers = [1, 7, 2, 34, 8, 7, 2, 5, 14, 22, 93, 48, 76, 15, 6]
>>> -next(filter(lambda v: v[1]%2==1, enumerate(reversed(numbers))))[0]
-1
Even for very large lists with a lot of even numbers at the end the result will be delivered quite quick (compared with the proposed code):
>>> from timeit import timeit
>>> def find_last_odd(numbers):
for num in range(len(numbers)):
if numbers[num] % 2 == 1:
last_odd = num
return last_odd
>>> numbers2=numbers+([2]*10000) # create a list with 10000 even numbers at the end!
>>> timeit(lambda: find_last_odd(numbers2),number=100)
0.5675344999999936
>>> timeit.timeit(lambda: -next(filter(lambda v: v[1]%2==1, enumerate(reversed(numbers2)))).__getitem__(0),number=100)
0.10892959999998197

Python: Difference between two positions in one list depending on rule set for another list

Consider two lists of identical length:
t is a list of irregular-intervals of times in seconds, arranged chronologically
pt is a list of sequences of numbers 1,2,3 wsuch that a 1 is followed by a consecutive string of 2's, then followed by a 3.
1 = start of event, 2 = continuation of event, 3 = end of event
This means that for a single event, the sequence begins with a single 1, is followed by a consecutive string of 2s (how many times it repeats will vary), and finally ends in a single 3.
There is more than 1 event contained in this vector
For example, the input could look like:
# |--Event #1-| |---Event #2----| |Event #3 |
pt = [1, 2, 2, 3, 1, 2, 2, 2, 3, 1, 2, 3 ]
t = [1, 10, 13, 14, 17, 20, 21, 25, 37, 32, 33, 38]
Is there a 1-liner that doesn't involve multiple nested loops that we could use that would calculate the difference in time values in t for each event sequence in pt?
For example, the desired output for the above inputs would be a list of length 3 (because there are 3 events) where the output is
Output: [13, 20, 6]
### Explanation:
# 13 = 14-1 = t[position where pt shows first 3] - t[position where pt shows first 1]
# 20 = 37-17 = t[position where pt shows second 3] - t[position where pt shows second 1]
# 6 = 38-32 = t[position where pt shows third 3] - t[position where pt shows third 1]
using pure python:
pt = [1, 2, 2, 3, 1, 2, 2, 2, 3, 1, 2, 3 ]
t = [1, 10, 13, 14, 17, 20, 21, 25, 37, 32, 33, 38]
l = [y for x, y in zip(pt,t) if x in [1,3]]
print([l[i:i+2][1] - l[i:i+2][0] for i in range(0, len(l), 2)])
[13, 20, 6]
using more_itertools.chunked():
from more_itertools import chunked
print([y-x for x,y in chunked([y for x, y in zip(pt,t) if x in [1,3]], 2)])
[13, 20, 6]
explanation
If you look close, we see this list comprehension occurring multiple times. This is the center of the solution!
[y for x, y in zip(pt,t) if x in [1,3]]
So, what's going on?
Using the zip function, we make a list of the paired elements, and if the x element (1st list element pair) is either 1 or 3 we add it to the list.
This gives us a list of the differences we need to find.
#|---| |----| |----|
[1, 14, 17, 37, 32, 38]
Now comes the second part, getting the differences from these. We essentially need to make pairs from this, the method I'm going to use here is chunking. The pure python way to partition a list into chunks is as follows:
#given a list l
chunklen = 2
[l[i:i+chunklen] for i in range(0, len(l), chunklen)]
using this we could partition the [1, 14, 17, 37, 32, 38] list to:
[[1, 14], [17, 37], [32, 38]]
but it's far simpler to just get the differences immediately!
l[i:i+chunklen][1]-l[i:i+chunklen][0]
#given l[i:i+chunklen] as [1, 14] this would return 14-1 i.e. 13
This seems to work
pt = [1, 2, 2, 3, 1, 2, 2, 2, 3, 1, 2, 3 ]
t = [1, 10, 13, 14, 17, 20, 21, 25, 37, 32, 33, 38]
st = 0
lst = []
for x in zip(pt,t):
if x[0] == 1: st = x[1]
if x[0] == 3:
d = x[1] - st
lst.append(d)
print(lst)
Output
[13, 20, 6]
Code:
pt = [1, 2, 2, 3, 1, 2, 2, 2, 3, 1, 2, 3 ]
t = [1, 10, 13, 14, 17, 20, 21, 25, 37, 32, 33, 38]
events_t =[]
arr = []
for pt_ele, t_ele in zip(pt, t):
arr.append(t_ele)
if pt_ele == 3:
events_t.append(arr)
arr = []
print(events_t)
res = [i[-1] - i[0] for i in events_t]
print(res)
Output:
[[1, 10, 13, 14], [17, 20, 21, 25, 37], [32, 33, 38]]
[13, 20, 6]

Python3: Reverse a list using only while, .pop() and .insert()

I am trying to do the following exercise:
Challenge: reverse a list using while .pop() insert() pop() the first item in the list and add to the beginning of a new string that will be reversed
some_numbers =[1,2,3,4,5,6,7,8,9,11,22,33,44,55,66,77]
Here's the code I have written:
some_numbers =[1,2,3,4,5,6,7,8,9,11,22,33,44,55,66,77]
new=[]
while some_numbers:
x=some_numbers.pop() #Should delete the last item on the list
new.insert(-1,x) #Should insert it in -1 position on the list
print(new)
However, this is the result I am getting:
[66, 55, 44, 33, 22, 11, 9, 8, 7, 6, 5, 4, 3, 2, 1, 77]
Does somebody know how can I make the "77" at the beginning on the list? I tried populating new=[" "] and it works but obviously the space appears as part of the list in the result and I just want the numbers there. Thanks!!
Try inserting at len(reverse):
some_numbers =[1,2,3,4,5,6,7,8,9,11,22,33,44,55,66,77]
reverse = []
while some_numbers:
reverse.insert(len(reverse), some_numbers.pop())
print(reverse)
Output
[77, 66, 55, 44, 33, 22, 11, 9, 8, 7, 6, 5, 4, 3, 2, 1]
From the documentation:
Insert an item at a given position. The first argument is the index of
the element before which to insert, so a.insert(0, x) inserts at the
front of the list, and a.insert(len(a), x) is equivalent to
a.append(x).
Notice that:
The first argument is the index of the element before which to
insert
When you do insert(-1, x), your code inserts before the last element, for example if you print reverse at each stage, you get:
[77]
[66, 77]
[66, 55, 77]
[66, 55, 44, 77]
[66, 55, 44, 33, 77]
[66, 55, 44, 33, 22, 77]
...
You can solove this challenge using with a single variable(i):
some_numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77]
new = []
i = 0
while some_numbers:
x = some_numbers.pop() # Should delete the last item on the list
new.insert(i, x) # Should insert it in -1 position on the list
i += 1
print(new)
Always pop the first item of some_numbers and insert it at the beginning of the new list, which is what the question stated was to be done:
>>> some_numbers = [1,2,3,4,5,6,7,8,9,11,22,33,44,55,66,77]
>>> new = []
>>> while some_numbers:
... new.insert(0, some_numbers.pop(0))
...
>>> new
[77, 66, 55, 44, 33, 22, 11, 9, 8, 7, 6, 5, 4, 3, 2, 1]
>>>
Why the OP's Answer Does Not Work
new.insert(-1, 3) inserts 3 before the last element of new. When new is empty, 3 is just inserted and we have [3] as the result. But subsequent insertions are inserted before the last element, which is 3. Thus, the first element inserted will always be the last element of the final list. In the example below, that will be the number 3:
>>> l = []
>>> l.insert(-1, 3)
>>> l
[3]
>>> l.insert(-1, 2)
>>> l
[2, 3]
>>> l.insert(-1, 1)
>>> l
[2, 1, 3]
>>>
So instead of reversing 1, 2, 3 to get 3, 2, 1, we end up with 2, 1, 3. 3 should have been at the beginning of the final result but it is instead at the end because it was the first item inserted.
Reverse the following list using python without using a list-reversed function. what about this?
List_new=[22 , 33 ,44 ,55 ,66 ,77 ,88]

How to find the factors of a number given the prime factorization?

If I was given the prime factorization of a number in the form [2, 2, 3, 5, 5] how would I be able to find all of the factors in the form [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 25, 30, 50, 60, 75, 100, 150, 300]
I've attempted to do this through iterated loops, but as far as I was able to figure it out, it isn't clicking on a way to get the numbers as a result more than two numbers multiplying together
def find_factors(pfacts):
pfacts = [1] + pfacts
temp = []
for i in pfacts:
for j in pfacts[i+1:]:
if i * j not in temp:
temp.append(i * j)
return [1] + temp
I know this isn't the right way to do it because it only finds a small number of the factors
[1, 2, 3, 5, 6, 10, 15]
One way is to use itertools.product with numpy.prod and numpy.power:
import numpy as np
from itertools import product
f = [2, 2, 3, 5, 5]
uniq_f = np.unique(f)
counts = np.array(list(product(*(range(f.count(i) + 1) for i in uniq_f))))
sorted(np.prod(np.power(uniq_f, counts), 1))
Output:
[1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 25, 30, 50, 60, 75, 100, 150, 300]
You could use itertools.combinations (which will give duplicates) and set to filter the duplicates out:
from itertools import combinations
from functools import reduce
from operator import mul
factors = [2, 2, 3, 5, 5]
set(reduce(mul, combination) for i in range (1, len(factors) + 1) for combination in combinations(factors, i))
Output:
{2, 3, 4, 5, 6, 10, 12, 15, 20, 25, 30, 50, 60, 75, 100, 150, 300}
Multiply all the combinations and add them to a set.
import itertools
def multiplyList(myList):
# Multiply elements one by one
result = 1
for x in myList:
result = result * x
return result
factors=set()
stuff = [2, 2, 3, 5, 5]
for L in range(0, len(stuff)+1):
for subset in itertools.combinations(stuff, L):
factors.add(multiplyList(subset))
factors=list(sorted(factors))
print(factors)
This works as below:
[1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 25, 30, 50, 60, 75, 100, 150, 300]
I am a touch newer to python so I have trouble understanding some of the things already posted. Here is my answer, it is longer but may be easier for a beginner to follow.
import numpy as np
import itertools
factors = [2, 2, 3, 5, 5]
al = []
for i in range(len(factors)):
for combo in itertools.combinations(factors,i):
al.append(np.prod(combo))
print(np.unique(al))
output:
[ 1. 2. 3. 4. 5. 6. 10. 12. 15. 20. 25. 30. 50. 60.
75. 100. 150.]

Python Reversing Certain Parts of Tuple

Suppose i have the following tuple as:
a = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
What i want to do is reversing certain parts of the tuple.
For example 4 elements stays same and then 4 elements will be reversed.
I want to get the following result:
a = (1,2,3,4,8,7,6,5,9,10,11,12,16,15,14,13,17,18,19,20)
How can i achieve that (as pythonian kind of way)?
Thanks...
Tuples are immutable, however, by converting a to a list, in-place assignment can be performed:
a = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
new_a = list(a)
for c, i in enumerate(range(0, len(a), 4)):
if c%2:
new_a[i:i+4] = new_a[i:i+4][::-1]
print(tuple(new_a))
Output:
(1, 2, 3, 4, 8, 7, 6, 5, 9, 10, 11, 12, 16, 15, 14, 13, 17, 18, 19, 20)
The direct, simple, readable answer (pythonic?):
a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
for n in range(0, len(a), 8):
a[n+4:n+8] = a[n+7:n+3:-1]
print(a)
Here's a generator-based solution. The benefits of doing it this way is that this solution doesn't require the input to be sliceable, so you can apply it to the output of functions like zip and map.
from itertools import zip_longest as zipl
from itertools import cycle, chain
_marker = object()
def cycle_map(iterable, func_iterable):
funcs = cycle(func_iterable)
for func, item in zip(funcs, iterable):
if func:
yield func(item)
else:
yield item
def reverse_filter(iterable, remove=_marker):
t = tuple(i for i in iterable if i is not remove)
return reversed(t)
def reverse_alternating(iterable, length=4):
chunks = zipl(*[iter(iterable)]*length, fillvalue=_marker)
funcs = (None, reverse_filter)
return chain.from_iterable(cycle_map(chunks, funcs))
a = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
list(reverse_alternating(a))
# [1, 2, 3, 4, 8, 7, 6, 5, 9, 10, 11, 12, 16, 15, 14, 13, 17, 18, 19, 20]
Using list slicing.
Ex:
from itertools import chain
a = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
res = [a[v:v+4] if i%2 == 0 else list(reversed(a[v:v+4])) for i, v in enumerate(range(0, len(a), 4))]
print(tuple(chain.from_iterable(res))) #Flatten list
Output:
(1, 2, 3, 4, 8, 7, 6, 5, 9, 10, 11, 12, 16, 15, 14, 13, 17, 18, 19, 20)
Just another way to approach the problem using list comprehension (not the fastest probably). Wanted to share the logic, assuming that len(a) is divisible by 4:
a = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
lst = [a[j:j+4] if i%2 == 0 else a[j:j+4][::-1] for i, j in
enumerate(range(0,len(a), 4))]
result = tuple([j for i in lst for j in i])
print (result)
Output
(1, 2, 3, 4, 8, 7, 6, 5, 9, 10, 11, 12, 16, 15, 14, 13, 17, 18, 19, 20)

Categories

Resources