Advanced list comprehension: creating averages - python

I have a list, each element in the list in a list containing 2 numbers separated by a comma.
I have to treat the first items in each list in relation with each other, same with the second ones.
Given a list, I have to replace the first number of each nested list with the average of the number, the first number in the list to its right and the first number in the list to its left. same to the second number.
For the first nested list, I have to replace it the average or the number itself and its matching number in the list to its right.
For the last, the same with the list to its left.
For example:
[[1, 1], [7, 7], [20, 20], [9, 9], [-12, -12]]
would be:
[[(1+7)/2, (1+7)/2], [(7+1+20)/3, (7+1+20)/3], [(20+7+9)/3, (20+7+9)/3], [(9+20-12)/3, (9+20-12)/3], [(-12+9)/2, (-12+9)/2]]
and thus:
[[4, 4], [9, 9], [12, 12], [5, 5], [-2, -2]]
because we are returning ints.
I have a code but it only prints the average, which is kind of clunky. Please help me to point me to the right direction on how to calculate the elements, and create the new list.
Also, I would like to use only loops and basic list comprehensions, so I can understand the logic.
This is my code so far:
def lpf(lst):
for i in range(len(lst)):
for j in range(2):
if i == 0:
lst[i][j] = int((lst[i][j] + lst[i+1][j]) / 2)
elif 0 < i < (len(lst) - 1):
lst[i][j] = int((lst[i-1][j] + lst[i][j] + lst[i+1][j]) / 3)
elif i == len(lst) - 1:
lst[i][j] = int((lst[i-1][j] + lst[i][j]) / 2)
return lst
And we have to assume the items in the list won't always be the same.
I seem to understand my code's problem - Once I change the first element, the next iteration happens over the new element and not the original. Yet I cant think about how to solve this.

You wanted list-comprehension, I give you list-comprehension:
[[sum(s[p] for s in l[i-1 if i > 0 else 0:i+2])//(2 if i in (0,len(l)-1) else 3) for p in range(2)] for i in range(len(l))]
In all serious though, I would recommend breaking this down into a for-loop which contains the inner list-comprehension as it is just so unreadable.
Example of it working:
>>> l = [[1, 1], [7, 7], [20, 20], [9, 9], [-12, -12]]
>>> [[sum(s[p] for s in l[i-1 if i > 0 else 0:i+2])//(2 if i in (0,len(l)-1) else 3) for p in range(2)] for i in range(len(l))]
[[4, 4], [9, 9], [12, 12], [5, 5], [-2, -2]]
Exploded form without list-comprehensions:
output = []
for i in range(len(l)):
if i == 0:
group = l[i:i+2]
averaged = [(group[0][0] + group[1][0])//2,
(group[0][1] + group[1][1])//2]
output.append(averaged)
elif i == len(l)-1:
group = l[i-1:i+1]
averaged = [(group[0][0] + group[1][0])//2,
(group[0][1] + group[1][1])//2]
output.append(averaged)
else:
group = l[i-1:i+2]
averaged = [(group[0][0] + group[1][0] + group[2][0])//3,
(group[0][1] + group[1][1] + group[2][0])//3]
output.append(averaged)
which gives output as before:
[[4, 4], [9, 9], [12, 12], [5, 5], [-2, -2]]

Here's a way you can do it if all your sublist items are always the same:
mylist = [1, 7, 20, 9, -12]
def avg_list(l):
return sum(l)/int(len(l))
avgs = [avg_list(mylist[max(0,i-1):min(len(mylist),i+2)]) for i in range(len(mylist))]
output = [[j, j] for j in avgs]
>>> [[4, 4], [9, 9], [12, 12], [5, 5], [-2, -2]]

Related

Nested list in nested loop

like extremely new, so please bear with me.
Im trying to increment each element of a nested list by 1
a straight forward list works fine:
a = [1,2,3,4,5,6,7,8]
for i in range(len(a)):
a[i] += 1
but why doesn't it work with:
a = [[1, 2], [3, 4], [5, 6], [7, 8]]
what am i missing?
Let's unroll the loop so we can inspect:
a = [1, 2, 3, 4, 5, 6, 7, 8]
i = 0
assert a[i] == 1 # the zeroeth element of a
a[i] += 1 # increment 1, making it 2
assert a[i] == 2
i = 1
# ... etc, repeat
contrast with
a = [[1, 2], [3, 4], [5, 6], [7, 8]]
i = 0
assert a[i] == [1, 2] # the zeroeth element of a is now the list [1, 2]!
a[i] += 1 # TypeError! What's the logical equivalent of adding 1 to a list? There isn't one
It won't work as you have another list inside list a or nested list. Therefore, you need nested loop:
Following program would help:
a = [[1, 2], [3, 4], [5, 6], [7, 8]]
for i in range(len(a)):
for j in range(len(a[i])):
a[i][j] += 1
Hope it Helps!!!
In the first iteration your a[i] += 1 would effectively be a[0] = [1, 2] + 1. That doesn't exactly make sense. You need to have a second, inner loop.
Use nested for loops:
for i in range(len(a)):
for ii in range(len(a[i])):
a[i][ii] += 1
Because you have nested list then you have to iterate that nested one again
Here a cool way to check if there list inside with recursion
a = [1,[2, 4],3,[4, 5],5,6,7,8]
def increment_a_list(some_list):
for i in range(len(some_list)):
if type(some_list[i]) is list: # if the element is a list then execute the function again with that element
increment_a_list(some_list[i])
else:
some_list[i] += 1 # +1 to that element if it not a list
return some_list
print(increment_a_list(a))
a = [[1, 2], [3, 4], [5, 6], [7, 8]]
Each item in the list is again a list. You need to traverse and incerement each of it individually. So, nested for loop can solve your problem.
A more generic solution - Recursion
### Using recursion - The level of nesting doesn't matter
def incrementor(arr):
for i in range(len(arr)):
if type(arr[i]) is list:
incrementor(arr[i])
else:
arr[i] = arr[i] + 1
a = [[1, 2], [3, 4], [5, 6], [7, 8],9,10,11,12,[13,[14,15,16]],[[17,18],[19,[20,21]]]]
incrementor(a)
print(a)
Output
[[2, 3], [4, 5], [6, 7], [8, 9], 10, 11, 12, 13, [14, [15, 16, 17]], [[18, 19], [20, [21, 22]]]]

How to split a nested list into a smaller nested list

So I have a nested list as my input (and the nested list is always square. ie. same number of rows as columns). I want to break this list up into another nested list of which the elements are just 2x2 "parts" of the original list.
For example, if my input was
[[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,16]]
my output should be
[[1,2,5,6], [3,4,7,8], [9,10,13,14], [11,12,15,16]]
Another example:
input:
[[1,2,3],
[5,6,7],
[9,10,11],
output:
[[1,2,5,6],[3,7],[9,10],[11]]
I've tried making a nested for loop that goes through the first two columns and rows and makes that into a list and then appends that to another list and then repeats the process, but I get an index out of bounds exception error
This is what I've done so far
def get_2_by_2(map: List[List[int]]) -> int:
i = 0
j = 0
lst_2d = []
lst = []
for row in range(i, min(i+2, len(map))):
for column in range(j, min(j+2, len(map))):
print(row,column)
lst.append(map[row][column])
lst_2d.append(lst)
return lst_2d
basically this one only returns the first 2x2. I attempted using a while loop on the outside and incrementing the values of i and j and making my while loop dependent on one of them. that resulted in an index out of bounds.
You can iterate through the rows and columns in a step of 2, and slice the list of lists accordingly:
def get_2_by_2(matrix):
output = []
for row in range(0, len(matrix), 2):
for col in range(0, len(matrix[0]), 2):
output.append([i for r in matrix[row: row + 2] for i in r[col: col + 2]])
return output
or with a nested list comprehension:
def get_2_by_2(matrix):
return [
[i for r in matrix[row: row + 2]
for i in r[col: col + 2]] for col in range(0, len(matrix[0]), 2)
]
so that given:
m = [[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]]
get_2_by_2(m) returns:
[[1, 2, 5, 6], [3, 4, 7, 8], [9, 10, 13, 14], [11, 12, 15, 16]]
and that given:
m = [[1, 2, 3],
[5, 6, 7],
[9, 10, 11]]
get_2_by_2(m) returns:
[[1, 2, 5, 6], [3, 7], [9, 10], [11]]

Finding a minimum value in a list of lists and returning that list

This code is supposed to iterate over the list of lists and return the entire list that contains the smallest value. I have already identified that it keeps returning the list at index[0], but I cannot figure out why. Any help, or even hints, would be greatly appreciated.
def list_with_min(list_of_lists):
m = 0
for i in range(len(list_of_lists)-1):
list_i = list_of_lists[m]
min_list = min(list_of_lists[m])
if min_list < list_i[0]:
m = i
answer = list_of_lists[m]
return answer
print(list_with_min([[9, 10, 15], [1, 8, 4], [-3, 7, 8]]))
# [9, 10, 15]--------> should be [-3, 7, 8]
print(list_with_min([[5], [9], [6], [2], [7], [10], [72]]))
# [5]----------------> should be [2]
print(list_with_min([[-2, 6, 9], [-9, 6, 9], [4, 8, 2], [5, -2]]))
# [-2, 6, 9]---------> should be [[-2, 6, 9], [5, -2]] (I assume two lists with the same minimum value should both be returned?)
You can provide a key to the function min, that is a function used for comparison. It turns out that here you want key to be the function min itself.
list_of_lists = [[9, 10, 15], [1, 8, 4], [-3, 7, 8]]
min(list_of_lists, key=min) # [-3, 7, 8]
This does not return multiple minima, but can be improved to do so.
list_of_lists = [[9, 10, 15], [1, -3, 4], [-3, 7, 8]]
min_value = min(map(min, list_of_lists))
[lst for lst in list_of_lists if min(lst) == min_value] # [[1, -3, 4], [-3, 7, 8]]
you can get this in one line with a list comprehension (ive added three though to help you work through the logic), it deals with duplicates differently however:
#for each list return a list with the minimum and the list
mins_and_lists = [[min(_list), _list] for _list in lists]
#find the minimum one
min_and_list = min([[min(_list), _list] for _list in lists])
#parse the result of the minimum one list
minimum, min_list = min([[min(_list), _list] for _list in lists])
if you want to handle duplicate minimums by returning both then:
dup_mins = [_list for _min, _list in mins_and_lists if _min == minimum]
EDIT: A more python way could be this:
def list_with_min(l):
min_vals = [min(x) for x in l]
return l[min_vals.index(min(min_vals))]
A bit bulky but it works...
l=[[9, 10, 15], [1, 8, 4], [-3, 7, 8]]
def list_with_min(l):
m = min(l[0])
for i in l[1:]:
m = min(i) if min(i) < m else m
for i in l:
if m in i:
return i
print(list_with_min(l))
Output:
[-3, 7, 8]
You could also use this:
min([ (min(a),a) for a in list_of_lists ])[1]
Your condition just makes no sense. You're checking
if min_list < list_i[0]:
which means, if the smallest value of list_i is less than the first value of list_i.
I don't think you'd ever want to compare to just list_i[0]. You need to store min_list across loops, and compare to that.

how to merge two sublists sharing any number in common?(2)

Given that:
list=[[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11]]
I have asked a similar question before, I have tried the code on
how to merge two sublists sharing any number in common?
but I am stuck in my code now.
I want to merge the sublists that share a common number,
e.g. [1,2,3] and [3,4,5] can merge to give [1,2,3,4,5] as they share a common number, 3.
In [[1,2,3],[3,4,5],[5,6]], although [1,2,3] and [3,4,5] share a common number, 3,
[3,4,5] and [5,6] also share a common number, 5, so I want all three of them to merge then gives
[1,2,3,4,5,6].
So for list,
my expected result is:
[[1,2,3,4,5,6,7],[9,10,11]]
I have tried the following code but don't know what is wrong, can anyone help?
s = map(set, list)
for i, si in enumerate(s):
for j, sj in enumerate(s):
if i != j and si & sj:
s[i] = si | sj
s[j] = set()
list=[list(el) for el in s if el]
print list
>>>[[5, 6, 7], [9, 10, 11]]
def merge_containing(input_list):
merged_list = [set(input_list[0])]
i = 0
for sub_list in input_list:
if not set(sub_list).intersection(set(merged_list[i])): # 1
merged_list.append(set()) # 2
i += 1 # 2
merged_list[i].update(set(sub_list)) # 3
return [sorted(sub_list) for sub_list in merged_list] # 4
mylist=[[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11]]
print merge_containing(mylist)
Output:
[[1, 2, 3, 4, 5, 6, 7], [9, 10, 11]]
How does it work:
Check if the sub_list set shares any common member with the current
index set of the merged_list.
If it doesn't, add a new empty set to the merged_list and increment
the index.
Adds the sub_list set to the set at index in the merged_list.
Converts from set to list and return
def merge(list_of_lists):
number_set = set()
for l in list_of_lists:
for item in l:
number_set.add(item)
return sorted(number_set)
if __name__ == '__main__':
list_of_lists = [[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11]]
merged = merge(list_of_lists)
print merged
I'm posting this as a new answer since the OP already accepted my other.
But as pointed out by #Eithos,
the input:
[[3,4], [1,2], [1,3]]
should return
[[1,2,3,4]]
and the input
[[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11],[65,231,140], [13,14,51]]
should return
[[1, 2, 3, 4, 5, 6, 7], [9, 10, 11], [13, 14], [51], [65], [140], [231]]
Here's my attempt:
from itertools import chain
def merge_containing(input_list):
print " input:", input_list
chain_list = sorted(set(chain(*input_list))) # 1
print " chain:",chain_list
return_list = []
new_sub_list = []
for i, num in enumerate(chain_list):
try:
if chain_list[i + 1] == chain_list[i] + 1: # 2
new_sub_list.append(num)
else:
new_sub_list.append(num) # 3
return_list.append(new_sub_list)
new_sub_list = []
except IndexError:
new_sub_list.append(num) # 3
return_list.append(new_sub_list)
print 'result:', return_list
return return_list
mylist = [[3,4], [1,2], [1,3]]
merge_containing(mylist)
print
mylist = [[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11],[65,231,140], [13,14,51]]
merge_containing(mylist)
Output:
input: [[3, 4], [1, 2], [1, 3]]
chain: [1, 2, 3, 4]
result: [[1, 2, 3, 4]]
input: [[1, 2, 3], [3, 4, 5], [5, 6], [6, 7], [9, 10], [10, 11], [65, 231, 140], [13, 14, 51]]
chain: [1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13, 14, 51, 65, 140, 231]
result: [[1, 2, 3, 4, 5, 6, 7], [9, 10, 11], [13, 14], [51], [65], [140], [231]]
Explanation:
This one is a little hacky them the last one
I use itertool.chain to flat all the lists and them I sort it.
Then I check if the current number is within the range of 1 digit from the next
If it is I store it in the new_sub_list, if not I store in the new_sub_list, then store new_sub_list in the return_list, and empty the new_sub_list.
Note the try/except Index Error, it to avoid comparing the last item of the list with one that doesn't exist,
Well... I couldn't resist answering #f.rodrigues' last answer with one of my own.
I have to be honest though, this final version was heavily influenced by jonrsharpe's solution (the code went through various revisions, each one more efficient until I realized his method was the only way to press the most amount of juice) over here: Using sublists to create new lists where numbers don't repeat
Which made me wonder... why are we answering the same question over and over again (from the very same person)? This question, how to merge two sublists sharing any number in common? and the one with jonrsharpe's solution.
Anyway, this joins lists in the way outlined in his first question, but, like the solutions he already received over there, this one also works just as well for solving this problem.
sequence = [[1, 4, 9], [2, 3, 6], [4, 13, 50], [13, 23, 29], [2, 3, 7]]
def combineSequences(seq):
for index, y in enumerate(seq):
while True:
for x in seq[index + 1:]:
if any(i in x for i in seq[index]):
seq.remove(x)
y.extend(x)
break
else:
index += 1
break
return [sorted(set(l)) for l in seq]
sequence = [[1, 4, 9], [2, 3, 6], [4, 13, 50], [13, 23, 29], [2, 3, 7]]
print combineSequences(sequence)
>>> [[1, 4, 9, 13, 23, 29, 50], [2, 3, 6, 7]]
sequence = [[3, 4], [1, 2], [1, 3]]
print combineSequences(sequence)
>>> [[1, 2, 3, 4]]
This solution operates under a different assumption than the one I made earlier, just to clarify. This simply joins lists that have a common number. If the idea, however, was to only have them separated by intervals of 1, see my other answer.
That's it!
Okay. This solution may be difficult to grasp at first, but it's quite logical and succint, IMO.
The list comprehension basically has two layers. The outer layer will itself create separate lists
for each value of i (outer index) that satisfies the condition that the value it points to in the list is not equal to the value pointed to by the previous index + 1. So every numerical jump greater than one will create a new list within the outer list comprehension.
The math around the second (inner list comprehension) condition is a bit more complicated to explain, but essentially the condition seeks to make sure that the inner list only begins counting from the point where the outer index is at, stopping to where once again there is a numerical jump greater than one.
Assuming an even more complicated input:
listVar=[[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11],[65,231,140], [13,14,51]]
# Flattens the lists into one sorted list with no duplicates.
l = sorted(set([x for b in listVar for x in b]))
lGen = xrange(len(l))
print [
[l[i2] for i2 in lGen if l[i2] + i == l[i] + i2]
for i in lGen if l[i] != l[i-1] + 1
]
>>> [[1, 2, 3, 4, 5, 6, 7], [9, 10, 11], [13, 14], [51], [65], [140], [231]]

How can I merge lists that have common elements within a list of lists?

I have a list like [[1, 2, 4], [2, 5], [0, 3, 7, 8], [12, 3, 6], [18, 14]]. How can I get a list that contains lists of all the lists that contain overlapping elements added together? For the example input, the result should be [[1, 2, 4, 5], [0, 3, 6, 7, 8, 12], [14, 18]].
a = [[1, 2, 4], [2, 5], [0, 3, 7, 8], [12, 3, 6], [18, 14]]
result = []
for s in a:
s = set(s)
for t in result:
if t & s:
t.update(s)
break
else:
result.append(s)
This will go one-by-one through the list and create a set from the current sublist (s). Then it will check in the results, if there is another set t that has a non-empty intersection with it. If that’s the case, the items from s are added to that set t. If there is no t with a non-empty intersection, then s is a new independent result and can be appended to the result list.
A problem like this is also a good example for a fixed-point iteration. In this case, you would look at the list and continue to merge sublists as long as you could still find lists that overlap. You could implement this using itertools.combinations to look at pairs of sublists:
result = [set(x) for x in a] # start with the original list of sets
fixedPoint = False # whether we found a fixed point
while not fixedPoint:
fixedPoint = True
for x, y in combinations(result, 2): # search all pairs …
if x & y: # … for a non-empty intersection
x.update(y)
result.remove(y)
# since we have changed the result, we haven’t found the fixed point
fixedPoint = False
# abort this iteration
break
One way I can think of doing this is through recursion. Start with one item, then loop until you find every number it's connected to. For each of these numbers, you must do the same. Hence the recursion. To make it more efficient, store numbers you've visited in a list and check it at the beginning of each recursive sequence to make sure you don't repeat any explorations.
A two liner:
a_set = [set(x) for x in a]
result = [list(x.union(y)) for i,x in enumerate(a_set) for y in a_set[i:]
if x.intersection(y) and x != y]
I have left the last step for you:
a = [[1, 2, 4], [2, 5], [0, 3, 7, 8], [12, 3, 6], [18, 14]]
result = [[1, 2, 4, 5], [0, 3, 6, 7, 8, 12], [14, 18]]
# each sub list
result2 = []
count = 0
print a
for sub_list in a:
print count
print "sub_list: " + str(sub_list)
a.pop(count)
print "a: " + str(a)
#each int
sub_list_extend_flag = False
for int_in_sub_list in sub_list:
print "int_in_sub_list: " + str(int_in_sub_list)
for other_sub_list in a:
print "current_other_sub_list: " + str(other_sub_list)
if int_in_sub_list in other_sub_list:
sub_list_extend_flag = True
other_sub_list.extend(sub_list)
result2.append(list(set(other_sub_list)))
if not sub_list_extend_flag:
result2.append(sub_list)
count += 1
print result2
Simple answer:
a = [[1, 2, 4], [2, 5], [0, 3, 7, 8], [12, 3, 6], [18, 14]]
for x in a:
for y in x:
print y
its more simple than first one:
box=[]
a = [[1, 2, 4], [2, 5], [0, 3, 7, 8], [12, 3, 6], [18, 14]]
for x in a:
for y in x:
box.append(y)
print box
Result:[1, 2, 4, 2, 5, 0, 3, 7, 8, 12, 3, 6, 18, 14]
And with this, you can compare the numbers:
box=[]
box2=""
a = [[1, 2, 4], [2, 5], [0, 3, 7, 8], [12, 3, 6], [18, 14]]
for x in a:
for y in x:
box.append(y)
print box
for a in box:
box2+=str(a)
print box2
Result: 12425037812361814
Also you can make it more cute:
print " ".join(box2)
Result: 1 2 4 2 5 0 3 7 8 1 2 3 6 1 8 1 4

Categories

Resources