'How to calculate a list based on multiple results from items in a list?
I have a list A=[a,b,c,d]. I need to calculate a new list B based on operations between each item in A.
B= [a, b-(a), c-(a+(b-a)), d-(a+(b-a)+(c-(a+(b-a)))) ]
Is there a Pythonic way of doing this? List A is not always a 4 item list, so the solution needs to be generalizable to lists of arbitrary length. Thanks in advance.
Solution 1:[1]
Observe that the expression for your list can be simplified to:
B = [a, b-a, c-b, d-c]
With this in mind, we can use a list comprehension:
[y - x for x, y in zip([0] + data, data)]
For example,
data = [1, 2, 7, 6]
result = [y - x for x, y in zip([0] + data, data)]
print(result)
outputs:
[1, 1, 5, -1]
Solution 2:[2]
All your terms cancel out (c-(a+(b-a)) simplifies to c - b, d-(a+(b-a)+(c-(a+(b-a)))) simplifies to d - c), so the real algorithm here is that each term is equal to the matching term minus the prior term. This simplifies things dramatically:
B = [A[0]] # Initial term has no prior to subtract from it
B += [x - y for x, y in zip(A[1:], A)] # All other terms computed by subtracting term n - 1 from term n
If you want to one-line this (ignoring imports) you can stick a virtual 0 in to get the results for the first element without explicitly special-casing it:
from itertools import chain # At top of file
B = [x - y for x, y in zip(A, chain([0], A))]
If you love using map and friends for microoptimizations, you could replace the latter with:
from operator import sub # At top of file
B = [*map(sub, A, chain([0], A))]
and push all the work to the C layer (no per-element bytecode execution).
Solution 3:[3]
Two solutions that don't assume that things "cancel out" (because that is wrong already for standard types like float and Counter, as shown below).
If I understand the pattern correctly, the first B-value shall be the first A-value and then each next B-value shall always be the next A-value minus the sum of all previous B-values. One way to do that:
B = []
for a in A:
if not B:
b = sumB = a
else:
b = a - sumB
sumB = sumB + b
B.append(b)
Fun one using itertools.accumulate and operator.sub:
B = A[:1]
B += map(sub, A[1:], accumulate(B))
Tests:
A = [31, 41, 59, 26]
reference [31, 10, 18, -33]
subtract_neighbors correct [31, 10, 18, -33]
loop correct [31, 10, 18, -33]
fun correct [31, 10, 18, -33]
A = [1, 1, 1e-20, 1e-20]
reference [1, 0, -1.0, 1e-20]
subtract_neighbors wrong [1, 0, -1.0, 0.0]
loop correct [1, 0, -1.0, 1e-20]
fun correct [1, 0, -1.0, 1e-20]
A = [Counter(), Counter({None: 1}), Counter(), Counter({None: 1})]
reference [Counter(), Counter({None: 1}), Counter(), Counter()]
subtract_neighbors wrong [Counter(), Counter({None: 1}), Counter(), Counter({None: 1})]
loop correct [Counter(), Counter({None: 1}), Counter(), Counter()]
fun correct [Counter(), Counter({None: 1}), Counter(), Counter()]
Code doing the above checks (Try it online!):
def reference(A):
a, b, c, d = A
return [a, b-(a), c-(a+(b-a)), d-(a+(b-a)+(c-(a+(b-a)))) ]
def subtract_neighbors(A):
a, b, c, d = A
return [a, b-a, c-b, d-c]
def loop(A):
B = []
for a in A:
if not B:
b = sumB = a
else:
b = a - sumB
sumB = sumB + b
B.append(b)
return B
def fun(A):
B = A[:1]
B += map(sub, A[1:], accumulate(B))
return B
from collections import Counter
from itertools import accumulate
from operator import sub
funcs = [
reference,
subtract_neighbors,
loop,
fun,
]
tests = [
[31, 41, 59,26],
[1, 1, 1e-20, 1e-20],
[Counter(), Counter([None])] * 2,
]
for A in tests:
print(' A =', A)
for func in funcs:
result = func(A)
if func is reference:
expect = result
correctness = ' '
else:
correctness = 'correct' if result == expect else 'wrong '
print(f'{func.__name__:19}', correctness, result)
print()
Solution 4:[4]
As suggest by the guys on the comments, this is the simplest and fastest solution:
A = [5, 9, 3, 8]
B = [x - y for x, y in zip(A, [0] + A)]
This outputs:
B
[5, 4, -6, 5]
Solution 5:[5]
You can also do it like this
A = [1,2,3,4]
B = [A[0]]+[A[i+1]-A[i] for i in range(len(A)-1)]
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | ShadowRanger |
| Solution 3 | |
| Solution 4 | |
| Solution 5 |
