This is what you are looking for:
array[-1] * sum(array[::2])
array[::2] traverses the array from first index to last index in steps of two, i.e., every alternate number. sum(array[::2]) gets the sum of alternate numbers from the original list.
Using index will work as expected only when you are sure the list does not have duplicates, which is why your code fails to give the correct result.
This is what you are looking for:
array[-1] * sum(array[::2])
array[::2] traverses the array from first index to last index in steps of two, i.e., every alternate number. sum(array[::2]) gets the sum of alternate numbers from the original list.
Using index will work as expected only when you are sure the list does not have duplicates, which is why your code fails to give the correct result.
There is a repeated element 84 in the list, thus array.index does not work as you expected it to be. Also, your code has a quadratic complexity which is not required.
To fix your code with a minimum amount of edit, it would look something like this:
array = [-37,-36,-19,-99,29,20,3,-7,-64,84,36,62,26,-76,55,-24,84,49,-65,41]
print sum(array[i] for i in range(len(array)) if i % 2 == 0)*array[-1] if array != [] else 0
How to get sum of elements lists with condition in python? - Stack Overflow
bam - sum elements in python list if match condition - Stack Overflow
python - Find Sum of list with condition - Stack Overflow
python - Sum of values in list when condition is met - Stack Overflow
How can I add a condition to the list in the sum function.
You're nearly there, except that the if comes at the end:
sum(int(i.replace(',','')) for i in list if re.search(r'\d', i))
Having said this, the overall approach is not bullet-proof. It would choke on inputs that mix digits with other characters (e.g. 'a1').
Also, the use of the comma as the thousands separator is not universal. Some locales use it to mark the radix point. In those locales, your code would produce incorrect values for numbers with commas in them.
maybe something like this
def safeIntToSum(x):
try: return int(x.replace(",",""))
except: return 0
print sum([safeIntToSum(x) for x in list])
You can slice Python lists using a step (sometimes called a stride), you can use this to get every second element, starting at index 1 (for the first letter):
>>> example = ['30', 'M', '1', 'D', '120', 'M']
>>> example[1::2]
['M', 'D', 'M']
The [1::2] syntax means: start at index 1, go on until you run out of elements (nothing entered between the : delimiters), and step over the list to return every second value.
You can do the same thing for the numbers, using [::2], so begin with the value right at the start and take every other value.
If you then combine this with the zip() function you can pair up your numbers and letters to figure out what to sum:
def sum_m_values(values):
summed = []
m_sum = 0
for number, letter in zip(values[::2], values[1::2]):
if letter != "M":
if m_sum:
summed += (str(m_sum), "M")
m_sum = 0
summed += (number, letter)
else:
m_sum += int(number)
if m_sum:
summed += (str(m_sum), "M")
return summed
The above function takes your list of numbers and letters and:
- creates a list for the results
- tracks a running sum of
"M"values - pairs up the numbers and letters
- for each pair:
- if it is a number and
"M", add that value (as an integer) to the running sum. - otherwise, adds the running sum (if any) to the list with the letter
"M", then adds the current number and letter too.
- if it is a number and
- after all pairs are processed, adds the running sum and the letter
"M", if there is any.
This covers all your example inputs:
>>> def sum_m_values(values):
... summed = []
... m_sum = 0
... for number, letter in zip(values[::2], values[1::2]):
... if letter != "M":
... if m_sum:
... summed += (str(m_sum), "M")
... m_sum = 0
... summed += (number, letter)
... else:
... m_sum += int(number)
... if m_sum:
... summed += (str(m_sum), "M")
... return summed
...
>>> examples = [
... ['20', 'M', '10', 'M', '1', 'D', '14', 'M', '106', 'M'],
... ['124', 'M', '19', 'M', '7', 'M'],
... ['19', 'M', '131', 'M'],
... ['3', 'M', '19', 'M', '128', 'M'],
... ['12', 'M', '138', 'M'],
... ]
>>> for example in examples:
... print(example, "->", sum_m_values(example))
...
['20', 'M', '10', 'M', '1', 'D', '14', 'M', '106', 'M'] -> ['30', 'M', '1', 'D', '120', 'M']
['124', 'M', '19', 'M', '7', 'M'] -> ['150', 'M']
['19', 'M', '131', 'M'] -> ['150', 'M']
['3', 'M', '19', 'M', '128', 'M'] -> ['150', 'M']
['12', 'M', '138', 'M'] -> ['150', 'M']
There are other methods of looping over a list in fixed-sized groups; you can also create an iterator for the list with iter()and then use zip() to pull in consecutive elements into pairs:
it = iter(inputlist)
for number, letter in zip(it, it):
# ...
This works because zip() gets the next element for each value in the pair from the same iterator, so "30" first, then "M", etc.:
>>> example = ['124', 'M', '19', 'M', '7', 'M']
>>> it = iter(example)
>>> for number, letter in zip(it, it):
... print(number, letter)
...
124 M
19 M
7 M
However, for short lists it is perfectly fine to use slicing, as it can be understood more easily.
Next, you can make the summing a little easier by using the itertools.groupby() function to give you your number + letter pairs as separate groups. That function takes an input sequence, and a function to produce the group identifier. When you then loop over its output you are given that group identifier and an iterator to access the group members (those elements that have the same group value).
Just pass it the zip() iterator build before, and either lambda pair: pair[1] or operator.itemgetter(1); the latter is a little faster but does the same thing as the lambda, get the letter from the number + letter pair.
With separate groups, the logic starts to look a lot simpler:
from itertools import groupby
from operator import itemgetter
def sum_m_values(values):
summed = []
it = iter(values)
paired = zip(it, it)
for letter, grouped in groupby(paired, itemgetter(1)):
if letter == "M":
total = sum(int(number) for number, _ in grouped)
summed += (str(total), letter)
else:
# add the (number, "D") as separate elements
for number, letter in grouped:
summed += (number, letter)
return summed
The output of the function hasn't changed, only the implementation.
Finally, we could turn the function into a generator function, by replacing the summed += ... statements with yield from ..., so it'll still generate a sequence of numeric strings and letters:
from itertools import groupby
from operator import itemgetter
def sum_m_values(values):
it = iter(values)
paired = zip(it, it)
for letter, grouped in groupby(paired, itemgetter(1)):
if letter == "M":
total = sum(int(number) for number, _ in grouped)
yield from (str(total), letter)
else:
# add the (number, "D") as separate elements
for number, letter in grouped:
yield from (number, letter)
You can then use list(sum_m_values(...)) to get a list again, or just use the generator as-is. For long inputs, that could be the preferred option as that means you never need to keep everything in memory all at once.
If you can guarantee that only numbers with M repeat (so a D pair is always followed by an M pair or is the last pair in the sequence), you can even just drop the if test and just always sum:
from itertools import groupby
from operator import itemgetter
def sum_m_values(values):
it = iter(values)
paired = zip(it, it)
for letter, grouped in groupby(paired, itemgetter(1)):
yield str(sum(int(number) for number, _ in grouped))
yield letter
This works because there will only ever be one number value per D group, summing won’t make that into a different number.
solution using itertools package:
>>> from itertools import groupby, chain
>>> records = [
... ['20', 'M', '10', 'M', '1', 'D', '14', 'M', '106', 'M'],
... ['124', 'M', '19', 'M', '7', 'M'],
... ['19', 'M', '131', 'M'],
... ['3', 'M', '19', 'M', '128', 'M'],
... ['12', 'M', '138', 'M'],
... ]
>>> res = []
>>> for rec in records:
... res.append(list(
... chain.from_iterable(
... map(
... lambda x: (
... str(sum(map(lambda y: y[0], x[1]))),
... x[0],
... ),
... groupby(
... zip(map(int, rec[::2]), rec[1::2]),
... lambda k: k[1]
... )
... )
... )
... ))
...
>>> res
[['30', 'M', '1', 'D', '120', 'M'], ['150', 'M'], ['150', 'M'], ['150', 'M'], ['150', 'M']]
I think what bothers you is the summ-=val part, not the if. And there is a one line solution with reduce.
>>> from functools import reduce
>>> L = [1,2,3,4,5,6,7,8,9]
>>> reduce(lambda x, y: x + y if x + y <= 25 else x, L)
21
- https://docs.python.org/3/library/functools.html#functools.reduce
You can use a while loop:
L = [1,2,3,4,5,6,7,8,9]
i = 0
s = 0
while i < len(L) and s + L[i] < 25:
s += L[i]
i += 1
There is (almost?) nothing that itertools cannot do. Take a look at groupby:
from itertools import groupby
from operator import attrgetter
class Element:
def __init__(self, id, value):
self.id = id
self.value = value
def __repr__(self): # kudos @mesejo
return "Element({}, {})".format(self.id, self.value)
l = [Element(1, 100), Element(1, 200), Element(2, 1), Element(3, 4), Element(3, 4)]
l.sort(key=attrgetter('id')) # if it is already sorted by 'id', comment-out
res = [Element(g, sum(sub.value for sub in k)) for g, k in groupby(l, key=attrgetter('id'))]
which results in:
print(res) # [Element(1, 300), Element(2, 1), Element(3, 8)]
One way would be to create a defaultdict that maps ids to sums of values. Then we can take those results and use them to build a new list of Elements. One way to do that is to use starmap to map the items of that dictionary to the arguments to Element
from collections import defaultdict
from itertools import starmap
class Element:
def __init__(self, id, value):
self.id = id
self.value = value
def __repr__(self):
return "Element({}, {})".format(self.id, self.value)
l = [Element(1, 100), Element(1, 200), Element(2, 1), Element(3, 4), Element(3, 4)]
d = defaultdict(int)
for e in l:
d[e.id] += e.value
print(list(starmap(Element, d.items())))
# [Element(1, 300), Element(2, 1), Element(3, 8)]
Here's a basic, naive, FORTRAN like solution with your first data type:
int_and_floats = ['2', '4.384508781', '2', '1.38586366', '2', '25.4309252', '1', '9.969634146', '1', '10.3821918', '2', '70.02500521', '1', '12.21172958', '1', '13.53189471', '1', '6.166945117', '1', '16.28642897']
last_int = None
n = len(int_and_floats)
total = 0
for i in range(0, n, 2):
a, b = int(int_and_floats[i]), float(int_and_floats[i + 1])
total += b
if a != last_int:
total += a
last_int = a
print(total)
# 175.775127174
With your second data format, you could just use groupby to chunk the ints together before summing them:
from itertools import groupby
ints = ['2', '2', '2', '1', '1', '2', '1', '1', '1', '1']
floats = ['4.384508781', '1.38586366', '25.4309252', '9.969634146', '10.3821918',
'70.02500521', '12.21172958', '13.53189471', '6.166945117', '16.28642897']
print(sum(map(float, floats)) + sum(int(i) for i, _ in groupby(ints)))
And with numbers instead of strings, your code could be:
from itertools import groupby
ints = [2, 2, 2, 1, 1, 2, 1, 1, 1, 1]
floats = [4.384508781, 1.38586366, 25.4309252, 9.969634146, 10.3821918, 70.02500521, 12.21172958, 13.53189471, 6.166945117, 16.28642897]
print(sum(floats) + sum(i for i, _ in groupby(ints)))
# 175.775127174
a = ['2', '4.384508781', '2', '1.38586366', '2', '25.4309252', '1',
'9.969634146', '1', '10.3821918', '2', '70.02500521', '1',
'12.21172958', '1', '13.53189471', '1']
prev = 0
sm = 0
for each in a:
if each.isdigit() and int(each)!=prev:
sm += int(each)
prev = int(each)
elif '.' in each:
sm += float(each)
print(sm)
should do the trick
You can use itertools.compress with sum:
>>> import itertools
>>> list1 = [3, 1, 1, 6, 8, 3, 7, 4, 8, 4]
>>> list2 = [0, 0, 0, 1, 1, 1, 0, 0, 0, 0]
>>> list(itertools.compress(list1, list2))
[6, 8, 3]
>>> sum(itertools.compress(list1, list2))
17
Yes, using sum and itertools.compress:
>>> from itertools import compress
>>> sum(compress(list1, list2))
17
Explanation: compress() takes two iterables, and yields elements from the first if the corresponding element of the second is truthy. sum() ... well, you can probably guess.
Here's a solution using collections.Counter:
from collections import Counter
c = Counter()
for name, value in inputs:
c[name] += float(value)
then you can convert the accumulated values for the name to string again. Counter works just like the built-in dict. I'm not sure if your task can be accomplished in a more performant way.
Using an dictionary to group the names should work for this:
L = [['savoielibrercgcm', 0.25], ['MeriFer', 0.25], ['XlassII', 0.25],
['Tomixt_31', 0.25], ['Tomixt_31', 0.25], ['Tomixt_31', 0.25]]
grouped = dict()
grouped.update((name,grouped.get(name,0)+value) for name,value in L)
L = [*map(list,grouped.items())]
print(L)
[['savoielibrercgcm', 0.25], ['MeriFer', 0.25], ['XlassII', 0.25],
['Tomixt_31', 0.75]]
Note: I assumed you don't really want your numerical values to be stored as strings
In this exercise your function will receive three parameters,
a list of integers, and two integers. It will add up all values
in the list that do not equal either of the two integers.
This is what I have:
def sumniout(nums, a, b):
for num in nums:
if num == a or num == b:
total = sum(nums) - a - b
else:
total = sum(nums)
return total
print(suminout([1,2,3,4], 1,2)) Example of it working:
suminout([1, 2, 3, 4], 1, 2) -> 7
My code gives me the right results sometimes. For example, if I run a test on print(suminout([1,2,3,4,5], 1,1)) I get 13 when I should get 14. Any ideas to get me on the right track?
You could construct a result dict where key is tuple of first two items in the original lists and value is list of numbers. Every time you add value to dict you could use get to either return existing element or given default value, in this case empty list.
Once you have the existing list and list to add you can use zip_longest with fillvalue to get numbers to sum from both lists. zip_longest returns tuples of length 2 containing one number from each list. In case one list is longer than other fillvalue is used as default so this will also work in case lists have different lengths. Finally list comprehension could used to sum each item for a new value:
from itertools import zip_longest
l = [
['Vienna','2012', 890,503,70],['London','2014', 5400, 879,78],
['London','2014',4800,70,90],['Bern','2013',300,450,678],
['Vienna','2013', 700,850,90], ['Bern','2013',500,700,90]
]
res = {}
for x in l:
key = tuple(x[:2])
res[key] = [i + j for i, j in zip_longest(res.get(key, []), x[2:], fillvalue=0)]
print(res)
Output:
{('Vienna', '2013'): [700, 850, 90], ('London', '2014'): [10200, 949, 168],
('Vienna', '2012'): [890, 503, 70], ('Bern', '2013'): [800, 1150, 768]}
If you want to sort the cities alphabetically and years latest first you could pass custom key to sorted:
for item in sorted(res.items(), key=lambda x: (x[0][0], -int(x[0][1]))):
print(item)
Output:
(('Bern', '2013'), [800, 1150, 768])
(('London', '2014'), [10200, 949, 168])
(('Vienna', '2013'), [700, 850, 90])
(('Vienna', '2012'), [890, 503, 70])
You can achieve the result you want by simply using a dictionary store all the country names and years as one value. Each key in the dictionary is a tuple of the country name and the corresponding year.
Ex: key = (country,year).
This allows us to have the unique values that we need to group them by.
L = [
['Vienna','2012', 890,503,70],['London','2014', 5400, 879,78],
['London','2014',4800,70,90],['Bern','2013',300,450,678],
['Vienna','2013', 700,850,90], ['Bern','2013',500,700,90]
]
countries = {}
for list in L:
key = tuple(list[0:2])
values = list[2:]
if key in countries:
countries[key] = [sum(v) for v in zip(countries[key],values)]
else:
countries[key] = values
print(countries)
out:
{
('Vienna', '2012'): [890, 503, 70],
('London', '2014'): [10200, 949, 168],
('Bern', '2013'): [800, 1150, 768],
('Vienna', '2013'): [700, 850, 90]
}
You can try itertools.groupby:
from itertools import groupby
out = []
for v, g in groupby(oldlist, lambda x: x > 215):
if v:
out.append(sum(g))
else:
out.extend(g)
print(out)
Note: your code doesnt work, because list.remove removes first occurence of the value. This is probably not what you want.
You could also iterate through the list like you have done but without having to worry about the index of every item:
oldlist = [216, 216, 199, 253, 271, 217, 183, 225, 199, 217, 217, 235, 254, 217, 235, 235, 234, 234, 235, 231, 183, 263, 298, 190, 248, 200, 223, 199, 225, 195, 240]
def get_nums_more_than_250():
temp_nums_to_add = 0
new_list = []
for i in oldlist:
if i >215:
temp_nums_to_add += i #add numbers togther
else:
if temp_nums_to_add !=0:
new_list.append(temp_nums_to_add)
temp_nums_to_add = 0
new_list.append(i)
#for final iteration (if values are stored in temp_nums_to_add
if temp_nums_to_add !=0:
new_list.append(temp_nums_to_add)
return new_list
print(get_nums_more_than_250())
Although this isn't as sleek as the other solutions I thought I should still show mine.
Let's make a test case:
In [59]: x = np.random.randint(0,10,10000)
In [60]: x.shape
Out[60]: (10000,)
(I thought test cases like this were required on Code Review. We like to have then on SO, and CR is supposed to be stricter about code completeness.)
Your code as a function:
def foo(pntl, adj_wgt, wgt_dif):
sum_4s = 0
for i in range(len(pntl)):
if pntl[i] == 4 and adj_wgt[i] != 10:
sum_4s += wgt_dif[i]
return sum_4s
Test it with lists:
In [61]: pntl = adj_wgt = wgt_dif = x.tolist() # test list versions
In [63]: foo(pntl, adj_wgt, wgt_dif)
Out[63]: 4104
In [64]: timeit foo(pntl, adj_wgt, wgt_dif)
1000 loops, best of 3: 1.45 ms per loop
Same test with array inputs is slower (lesson - if you must loop, lists are usually better):
In [65]: timeit foo(x,x,x)
The slowest run took 5.44 times longer than the fastest. This could mean that an intermediate result is being cached.
100 loops, best of 3: 3.97 ms per loop
The suggested list comprehension is modestly faster
In [66]: sum([w for w, p, a in zip(wgt_dif, pntl, adj_wgt) if p == 4 and a != 10])
Out[66]: 4104
In [67]: timeit sum([w for w, p, a in zip(wgt_dif, pntl, adj_wgt) if p == 4 and a != 10])
1000 loops, best of 3: 1.14 ms per loop
foo could have been written with zip instead of the indexed iteration. (todo - time that).
But since you say these are arrays, let's try a numpy version:
def foon(pntl, adj_wgt, wgt_dif):
# array version
mask = (pntl==4) & (adj_wgt != 10)
return wgt_dif[mask].sum()
In [69]: foon(x,x,x)
Out[69]: 4104
In [70]: timeit foon(x,x,x)
10000 loops, best of 3: 105 µs per loop
This is an order of magnitude faster. So if you already have arrays, try to work with them directly, without iteration.
def foo2(pntl, adj_wgt, wgt_dif):
sum_4s = 0
for w, p, a in zip(wgt_dif, pntl, adj_wgt):
if p == 4 and a != 10:
sum_4s += w
return sum_4s
In [77]: foo2(pntl, adj_wgt, wgt_dif)
Out[77]: 4104
In [78]: timeit foo2(pntl, adj_wgt, wgt_dif)
1000 loops, best of 3: 1.17 ms per loop
So it's the zip that speeds up your original code, not the list comprehension.
sum([w for w, p, a in zip(wgt_dif, pntl, adj_wgt) if p == 4 and a != max_wgt])
Explanation:
zip(a, b, c)
creates the list of triplets of corresponding values from the lists a, b, c - something as
[(a[0], b[0], c[0]), (a[1], b[1], c[1]), (a[2], b[2], c[2]), ...]
so the part
for w, p, a in zip(wgt_dif, pntl, adj_wgt)
loops over this triples, associating th 1st item to w, 2nd to p, and 3rd to a.