I'm trying to sum a list of lists of lists in Python but I'm getting the wrong output. I want the number 36 as my answer but I'm getting the sum of each bracket.
>>> list = [[[1,2],[3,4]],[[5,6],[7,8]]]
>>> for xs in list[0::1]:
... for x in xs[0::1]:
... sum(x)
...
3
7
11
15
You could probably do this recursively, which would work for nested lists of arbitrary depth:
def add_all(l):
try:
return sum(add_all(i) for i in l)
except TypeError:
return l
print add_all([[[1,2],[3,4]],[[5,6],[7,8]]]) # 36
Here's a little more fancy way to do it:
You can use itertools.chain to remove one level of nesting from your list of lists:
>>> lst = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
>>> from itertools import chain
>>> list(chain(*lst)) # note: list != lst
[[1, 2], [3, 4], [5, 6], [7, 8]]
Now apply it twice and sum all items:
>>> sum(chain(*chain(*lst)))
36
You can also use list comprehension like this -
>>> lst = [[[1,2],[3,4]],[[5,6],[7,8]]]
>>> sum([x for i in lst for y in i for x in y])
36
Or
>>> sum(sum(y) for x in lst for y in x)
36
Also, just FYI list is a bad name for a variable, since it overwrites the built-in list function.
If there are n nested lists (arbitrary number) I cannot think of a way to achieve the sum through list comprehension , but a simple recursive algorithm that would do the trick is -
>>> def sumOfList(element):
... if isinstance(element, list):
... return sum([sumOfList(x) for x in element])
... elif isinstance(element, int):
... return element
...
>>> sumOfList([[[1,2,3,4],[1,2,3,4]],[1,2,3]])
26
You can create a recursive function like this:
def rsum(lst):
if type(lst) != list:
return lst
if len(lst)==1:
return rsum(lst[0])
return rsum(lst.pop(0))+rsum(lst)
The difference is that it works for a nested list of any depth
Assign sublists sums to a variable:
total = 0
for x in list: # x is list of lists
for y in x: # y is a list
total = total + sum(y)
print total
Related
I would like to retrieve specific elements within a list of lists without using list comprehension, loops, or any iterative approach in Python.
For example, given this list:
[[1,2,3,4],[3,4],[5,6,7]]
and this vector:
[0,0,1]
I would like to retrieve the 0th element on the 0th list, the 0th element of the 1st list, and the 1st element of the 2nd list:
[1,2,3,4][0] -> 1
[3,4][0] -> 3
[5,6,7][1] -> 6
Which should give this result:
[1,3,6]
Is this possible in python?
This is an alternate:
x = [[1,2,3,4],[3,4],[5,6,7]]
y = [0,0,1]
res = []
for i in range(len(x)):
res.append(x[i][y[i]])
Using a list comprehension with zip() is one of the most Pythonic way to achieve this:
>>> my_list = [[1,2,3,4],[3,4],[5,6,7]]
>>> my_vector = [0,0,1]
>>> [x[i] for x, i in zip(my_list, my_vector)]
[1, 3, 6]
However, since OP can not use list comprehension, here's an alternative using map() with lambda expression as:
>>> list(map(lambda x, y: x[y], my_list, my_vector))
[1, 3, 6]
In the above solution, I am explicitly type-casting the object returned by map() to list as they return the iterator. If you are fine with using iterator, there's no need to type-cast.
you can use zip
l = [[1,2,3,4],[3,4],[5,6,7]]
i = [0,0,1]
op = []
for index, element in zip(i, l):
op.append(element[index])
output
[1, 3, 6]
using map you can do this way
a = [[1,2,3,4],[3,4],[5,6,7]]
b = [0, 0, 1]
result = list(map(lambda x,y: x[y], a,b))
print(result)
output
[1, 3, 6]
So for example, I have the list = [1, [2, [3, [5, [5]]]]] the resulting list would be [1,3].
I currently have this,
def avg(mylist):
if mylist == []:
return mylist
elif type(mylist[0]) == list:
mylist[0] = 0 # average of mylist[0]
return mylist[:1]+avg(mylist[1:])
elif type(mylist[0]) == float:
return mylist[:1]+avg(mylist[1:])
Which works the way I want it too but I cannot find a way to set mylist[0] = the average of mylist[0]. I have also tried it a lot of different ways but I cannot find one that works.
EDIT: Another example of something I tried.
total = 0
nosublist=True
if mylist == []:
return mylist
for x in mylist:
if type(x) == list:
nosublist=False
if nosublist:
return mylist[:1]+average(mylist[1:])
elif not nosublist:
for x in mylist:
if type(x) == list:
total += average(x)
else:
total += x
mylist[0] = total/len(mylist)
return average(mylist[:1])+average(mylist[1:])
def isiter(x):
try:
iter(x)
return True
except TypeError:
return False
def _flatten(x, reduce=iter):
for i in x:
if isiter(i):
r = reduce((j for j in _flatten(i, reduce=reduce)))
if isiter(r):
yield from r
else:
yield r
else:
yield i
Now you can plugin mean
def mean(x):
l = list(x)
return sum(l)/len(l)
l = [1, [2, [3, [5, [5]]]]]
list(_flatten(l, reduce=mean))
>>> [1, 3.0]
or
mean(flatten(l, reduce=mean))
>>> 2.0
EDIT:
If you really need only a single function:
def flatten(x, reduce=iter):
return reduce(_flatten(x, reduce=reduce))
This isn't probably the best solution but you could use it help make yours even better! I created two lists on each recursive call one with only elements that aren't lists and one with elements that were all lists (just in case you had a format like [1, 3, [2], [2], [3, [5, [5, 5]]]]) and created a call stack to take the sum of the elements in each inner array and one to take the length of each inner array and then took the average. Of course since there might be multiple arrays in the list of arrays you can map this same functionality for each array and accumulate their avg's.
code
list1 = [1, [2, [3, [5, [5]]]]]
list2 = [1, [2, 3], [4, 5], [5, [3, 4]]]
def avg(mylist):
"""flattens an array where the sublists to flatten are the average of that sublist"""
subarrays = filter(lambda x: type(x) == type([]), mylist)
rootelems = filter(lambda x: type(x) != type([]), mylist)
avg_all = lambda elem: sum((avg(elem))) / len(avg(elem))
if subarrays == []:
return mylist
return rootelems + map(avg_all, subarrays)
print avg(list1)
print avg(list2)
result
[1, 3]
[1, 2, 4, 4]
Im trying to write a function that takes a list and can print the lowest integer that is within that list. Now i'm trying to figure out what to do where this works with nested lists that if the lowest number is within one of those nested lists then overall it will print that number. My code is here:
def listMin():
list2 = [3,4,[2,99,8],7]
for i in range (len(list2)):
if type(list2[i]) == type([]):
y=min(i)
list2.append(y)
print "hello"
if len(list2)== 0:
return None
else:
x= min(list2)
print x
listMin()
while this seems like it should print the number 2 it doesnt and just gives me an error once it reaches the nested list saying:
TypeError: 'int' object is not iterable
ive tried multiple things but i'm having a hard time as to why this sort of thing isn't working.
Nesting One Deep
In your example, the list is nested only one deep. If this is the case in general, then try:
>>> list2 = [3,4,[2,99,8],7]
>>> min(x if isinstance(x, int) else min(x) for x in list2)
2
Nesting of Arbitrary Depth
If deeper nesting is allowed, define this recursive function:
>>> def rmin(lst): return min(x if isinstance(x, int) else rmin(x) for x in lst)
...
In operation:
>>> rmin(list2)
2
Or, with deeper nesting:
>>> list3 = [3,4,[[2,99],8],7]
>>> rmin(list3)
2
>>> list4 = [3, 4, [[2, [99, 1]], 8], 7]
>>> rmin(list4)
1
How it works
The function rmin consists of the single line:
return min(x if isinstance(x, int) else rmin(x) for x in lst)
As you can see, this is a list comprehension that looks at every value x of the list lst.
Let's divide the argument of min into two parts. The first is:
x if isinstance(x, int) else rmin(x)
This returns x if x is an integer. Otherwise, it calls rmin on x. In the latter case, rmin recursively looks at every value in x and returns the minimum.
The second part of the argument of min is:
for x in lst
This is just the usual for a list comprehension. It extracts each value in lst in turn and assigns it to x.
In general, you could flatten your list of lists and search for min in the flattened list. There are many recipes for flattening. Here is one that I took from here.
import collections
def flatten(iterable):
for el in iterable:
if isinstance(el, collections.Iterable) and not isinstance(el, str):
yield from flatten(el)
else:
yield el
list2 = [3,4,[2,99,8],7]
print(list(flatten(list2)))
# [3, 4, 2, 99, 8, 7]
print(min(flatten(list2)))
# 2
This will work on multiple nested list as well, e.g.:
list2 = [3,4,[2,99,8,[-1,-2]],7]
print(list(flatten(list2)))
# [3, 4, 2, 99, 8, -1, -2, 7]
print(min(flatten(list2)))
# -2
The problem is caused by the line
y=min(i)
where i is an integer but not a list. You probably want y = min(list2[i]).
Note that while you have appended this y back to the original list, the loop would not reach the newly added element, since i will only range up to the original length of the list.
With some simple Python idioms, your original idea can be expressed in a more readable way as follows:
def listMin():
lst = [3,4,[2,99,8],7]
for x in lst:
if type(x) == list:
lst.append(min(x))
print min(lst)
listMin()
When I needed to do something similar, I wrote following:
import copy
def nestedMin(list_):
target_list = copy.deepcopy(list_) # to keep original list unchanged
for index in range (len(target_list)):
if type (target_list[index]) is list:
target_list[index] = nestedMin(target_list[index])
return min(target_list)
I know that it is not very efficient, keeps doing deepcopy; but it's readable and it does the job :)
Example:
list1 = [2,3,[4, -5, [7, -20]]]
print nestedMin(list1) # prints -20
print list1 # prints [2, 3, [4, -5, [7, -20]]]
I'm not allowed to use the .remove() function to delete even numbers in a list and the specs say: You will be modifying the original list, so its id() must not change.This is what I have:
def remove_evens(xs):
for i in range(len(xs)):
for x in xs:
if x%2==0:
del xs[i]
return xs
If for instance I test the function and I input xs=[1,2,3,4,5] it returns [3,5]. I don't know why the 1 doesn't return
That's because you're iterating over the list and modifying it at the same time. This means the index i isn't always correct because you've just removed an element from the list. This leads to skipping elements and that's why the 1 isn't in there.
You can do:
def remove_evens(xs):
return [x for x in xs if x % 2 != 0]
This uses a list comprehension to create a list without the even numbers. If you need to modify the existing list you can do:
def remove_evens(xs):
to_remove = []
for i, x in enumerate(xs):
if x % 2 == 0:
to_remove.append(i)
for j in to_remove:
del xs[j]
return xs
This creates a list to_remove which keeps track of the positions of the elements that need to be removed.
Maybe something like this will work for you.
>>> xs = [1,5,123,6,2,34]
>>> id(xs)
35519496L
>>> lastidx = len(xs) - 1
>>> for i, x in enumerate(reversed(xs)):
... if x%2==0:
... del xs[lastidx-i]
...
>>> xs
[1, 5, 123]
>>> id(xs)
35519496L
It's linear time because it iterates over the list only once. When you iterate over a list backwards, you can freely delete elements without modifying the indexes of future elements that you haven't looped over yet. Also the id of the list does not change with this method, as you can see. :)
Here is another way to do the same thing with only range/xrange and len.
>>> xs = [1,5,123,6,2,34]
>>> for i in range(len(xs)-1,-1,-1):
... if xs[i]%2==0:
... del xs[i]
...
>>> xs
[1, 5, 123]
Both ways do the same thing so whichever way you decide to use is a matter of preference/personal style.
It’s a bad idea to delete items from a list as you are iterating over the list.
Here’s a few ways in which you can achieve your goal:
>>> def deleteEvens(L):
... dels = []
... for i,x in enumerate(L):
... if not x%2:
... dels.append(i)
... for d in dels[::-1]:
... L.pop(d)
...
>>> L
[1, 2, 3, 4, 5]
>>> deleteEvens(L)
>>> L
[1, 3, 5]
OR
>>> L = [1,2,3,4,5]
>>> odds = [i for i in L if i%2]
>>> odds
[1, 3, 5]
OR
>>> def delEvens(L):
... dels = []
... for i,x in enumerate(L):
... if not x%2:
... dels.append(i)
... for d in dels[::-1]:
... del L[d]
...
>>> L = [1,2,3,4,5]
>>> delEvens(L)
>>> L
[1, 3, 5]
I have a number of lists that I'm going to use in my program, but I need to be sure that they are all the same length, or I'm going to get problems later on in my code.
What's the best way to do this in Python?
For example, if I have three lists:
a = [1, 2, 3]
b = ['a', 'b']
c = [5, 6, 7]
I could do something like:
l = [len(a), len(b), len(c)]
if max(l) == min(l):
# They're the same
Is there a better or more Pythonic way to do this?
Assuming you have a non-empty list of lists, e.g.
my_list = [[1, 2, 3], ['a', 'b'], [5, 6, 7]]
you could use
n = len(my_list[0])
if all(len(x) == n for x in my_list):
# whatever
This will short-circuit, so it will stop checking when the first list with a wrong length is encountered.
len(set(len(x) for x in l)) <= 1
Latter I ended up writing:
def some(x):
"""Replacement for len(set(x)) > 1"""
if isinstance(x, (set, frozenset)):
return len(x) > 1
s = set()
for e in x:
s.add(e)
if len(s) > 1:
return True
return False
def lone(x):
"""Replacement for len(set(x)) <= 1"""
return not some(x)
Which allows the above to be written as:
lone(len(x) for x in l)
This will stop taking the lengths of the lists as soon as it finds a list with a different length.
A bit of functional Python:
>>> len(set(map(len, (a, b, c)))) == 1
False
Each call to max and min will traverse the whole list, but you don't really need to do that; you can check for the desired property with one traversal:
def allsamelength(lst_of_lsts):
if len(lst_of_lsts) in (0,1): return True
lfst = len(lst_of_lsts[0])
return all(len(lst) == lfst for lst in lst_of_lsts[1:])
This will also short-circuit if one of the lists has a different length from the first.
If l is list of lengths:
l = [len(a), len(b), len(c)]
if len(set(l))==1:
print 'Yay. List lengths are same.'
Otherwise, using the original lists, one could create a list of lists:
d=[a,b,c]
if len(set(len(x) for x in d)) ==1:
print 'Yay. List lengths are same.'