Truncate sublists to the lowest length - python

I have:
l = [[1,2,3],[3,4],[1,6,8,3]]
I want:
[[1,2],[3,4],[1,6]]
Which is the list l with all sublists truncated to the lowest length found for the sublists in l.
I tried:
min = 1000
for x in l:
if len(x) < min: min = len(x)
r = []
for x in l:
s = []
for i in range(min):
s.append(x[i])
r.append(s.copy())
Which works but quite slow and long to write. I'd like to make this more efficient through list comprehension or similar.

Using del:
n = min(map(len, l))
for a in l:
del a[n:]

You can find the length of each item in the list and then pick min element from it. Later you can use this value to truncate all the items in the list
l = [[1,2,3],[3,4],[1,6,8,3]]
min_length = min(map(len,l)) # using map with len function to get the length of each item and then using min to find the min value.
l = [item[:min_length] for item in l] # list comprehension to truncate the list
One liner -
l = [item[:min(map(len,l))] for item in l]

One fun thing about zip is zip is the inverse itself, so list(zip(*zip(*x))) gives x in similar structure.
And zip stop iteration when any input is exhausted.
Though the results are tuples and the nested lists are not truncated in-place., one can make use of this to build the following output:
Output:
[(1, 2), (3, 4), (1, 6)]
l = [[1, 2, 3], [3, 4], [1, 6, 8, 3]]
print(list(zip(*zip(*l))))

With list comprehension, one-liner:
l = [[1,2,3],[3,4],[1,6,8,3]]
print ([[s[i] for i in range(min([len(x) for x in l]))] for s in l])
Or:
print ([s[:min([len(s) for s in l])] for s in l])
Output:
[[1, 2], [3, 4], [1, 6]]
We compute the minimal length of subslists in the 'range()' to iterate over sublists for that amount and to reconstruct a new subslist. The top-level list comprehension allows to reconstruct the nested sublist.
If you have a large nested list, you should use this version with two lines:
m = min([len(x) for x in l])
print ([[s[i] for i in range(m)] for s in l])
Or:
print ([s[:m] for s in l])
Using zip and preserving the list objects:
print (list([list(x) for x in zip(*zip(*l))]))
Output:
[[1, 2], [3, 4], [1, 6]]

Related

Filtering the duplicates in subset sum combinations

Given an array, I've found all the combinations of subsets that equal a targeted sum, that's because I want the largest array possible.
For instance, the array [1, 2, 2, 2] for the target sum of "4" returns [[2, 2], [2, 2], [2, 2]].
subsets = []
def subset_sum(numbers, target, partial=[]):
s = sum(partial)
if s == target:
subsets.append(partial)
if s >= target:
return
for i in range(len(numbers)):
n = numbers[i]
remaining = numbers[i + 1:]
subset_sum(remaining, target, partial + [n])
subsets.sort()
subsets.reversed()
How can I remove values that are once mentioned in the subsets' list?
In the example above, how can I hay only one [2,2].
And that, show the values of the initial array that are not in this final list?
In the example above [1].
You can use itertools.groupby to remove duplicate lists:
>>> import itertools
>>> lst = [[2, 2], [2, 2], [2, 2]]
>>> lst.sort()
>>> new_lst = list(k for k,_ in itertools.groupby(lst))
>>> print(new_lst)
[[2, 2]]
Then simply flatten new_lst with itertools.chain.from_iterable and check if any of the elements from the initial list do not exist in this flattened list:
>>> initial = [1,2,2,2]
>>> print([x for x in initial if x not in itertools.chain.from_iterable(new_lst)])
[1]
Note: You can probably make your subset_sum() return a list of non duplicate items also, but the above should also work fine.
This is not a direct answer to your question, but a better algorithm. If you're only looking for one example of a list of maximal length which satisfies your sum criterion, you should be looking at longer lists first. This code uses itertools for the combinatorial bits and will stop when the longest list is found.
numbers = [1, 2, 2, 2]
taget = 5
for size in reversed(range(1, 1 + len(numbers))):
for c in itertools.combinations(numbers, size):
if sum(c) == target:
break
else:
continue
break
c now contains the longest subset as a tuple (1, 2, 2)
You can do something like this:
Data is :
data=[1, 2, 2,2]
import itertools
your_target=4
One line solution:
print(set([k for k in itertools.combinations(data,r=2) if sum(k)==your_target]))
output:
{(2, 2)}
or better if you use a function:
def targeted_sum(data,your_target):
result=set([k for k in itertools.combinations(data,r=2) if sum(k)==your_target])
return result
print(targeted_sum(data,4))

Python remove elements from two dimensional list

Trying to remove min and max values from two dimensional list in array.
My code:
myList = [[1, 3, 4], [2, 4, 4], [3, 4, 5]]
maxV = 0
minV = myList[0]0]
for list in myList:
for innerlist in list:
if innerlist > maxV:
maxV = innerlist
if innerlist < minV:
minV = innerlist
innerlist.remove(maxV)
innerlist.remove(minV)
print(myList)
This causes me some erros, which i not particulary understand. I'm quite sure that innerlist is not array but ordinary variable. But still i think it should be somehow possible to remove min and max elements from two dimensional list.
I mean I need to remove in every innerlist in my list highest and lowest values.
LF help!
Regards.
Just for the sake of showing a much simpler way of doing this using list comprehensions, the sorted method and slicing:
d = [[1, 3, 4], [2, 4, 4], [3, 4, 5]]
n = [sorted(l)[1:-1] for l in d]
print(n)
# [[3], [4], [4]]
Some reading material on each of the items used to solve this problem:
list
comprehension
sorted
slicing
To take care of duplicates, this answer by Padraic is very well done.
If you want to remove all occurrences, you will have to find the min and max and remove all occurrence from each sublist:
def remove(l):
for sub in l:
t = {min(sub), max(sub)}
sub[:] = (ele for ele in sub if ele not in t)
l = [[1, 3, 4], [1, 2, 4, 4], [3, 4, 5]]
remove(l)
Which will give you:
[[3], [2], [4]]
To find the min and max in a single pass you can use a helper function:
def min_max(sub):
# all numbers are > float("-inf") and < float("inf")
mx, mn = float("-inf"), float("inf")
for ele in sub:
if ele < mn:
mn = ele
if ele > mx:
mx = ele
return {mn, mx}
def remove(l):
for sub in l:
# find min and max
mn_mx = min_max(sub)
# update sublist so all occurrences of either are removed
sub[:] = (ele for ele in sub if ele not in mn_mx)
Even if your own logic worked and you wanted to remove all the elements equal to the max, it would not work using remove as it will only remove the first occurrence each time.
In [8]: l = [1,2,3,4,4]
In [9]: l.remove(4)
In [10]: l
Out[10]: [1, 2, 3, 4]
Based on one of your comments you seem to have strings in your sublists which will error when compared to an int, if the string is always the first element you can slice it off:
from itertools import islice
def remove(l):
for sub in l:
sub = sub[1:]
mn_mx = min_max(sub)
sub[:] = (ele for ele in sub if ele not in mn_mx)
try this approach:
foreach innerlist:
sort the array
remove the first element
remove the last element
It should work like this:
myList = [[1, 3, 4], [2, 4, 4], [3, 4, 5]]
for list in myList:
maxV = list[0] #initialise them here
minV = list[0] #because you look for max in list
for value in list:
if value> maxV:
maxV = innerlist
if value< minV:
minV = innerlist
list.remove(maxV) #remove from list
list.remove(minV)
print(myList)
Your errors where:
minV = myList[0]0] a [ to little
maxV = 0 works only if the list is always positive
maxV and minV should be inside the first loop
innerlist.remove(maxV) should be list.remove(maxV)
I also renamed innerList to value
Unindent your removes, to take them out of the loop and:
maxV = -10000000
minV = 10000000 # !!

Remove arrays from nested arrays based on first element of each array

I have two nested arrays say
a=[[1,2,3],[2,4,7],[4,2,8],[3,5,7],[6,1,2]]
b=[[1,6,7],[2,4,9],[4,3,5],[3,10,2],[5,3,2],[7,2,1]]
I want to only keep those arrays in b whose first element is not common to the first elements of the arrays in a, so for these two we should get
c=[[5,3,2],[7,2,1]]
Is there a way to do this in python?
You may do like this,
>>> a=[[1,2,3],[2,4,7],[4,2,8],[3,5,7],[6,1,2]]
>>> b=[[1,6,7],[2,4,9],[4,3,5],[3,10,2],[5,3,2],[7,2,1]]
>>> [i for i in b if i[0] not in [j[0] for j in a]]
[[5, 3, 2], [7, 2, 1]]
>>>
or
>>> k = [j[0] for j in a]
>>> [i for i in b if i[0] not in k]
[[5, 3, 2], [7, 2, 1]]
To make this a little faster and efficient using set
Code:
list1 = [[1,2,3], [2,4,7], [4,2,8], [3,5,7], [6,1,2]]
list2 = [[1,6,7], [2,4,9], [4,3,5], [3,10,2], [5,3,2], [7,2,1]]
check_set=set(val[0] for val in list1 )
print [val for val in list2 if val[0] not in check_set]
Output:
[[5, 3, 2], [7, 2, 1]]
Notes:
First we are creating a set to store all the unique first value of list1
The set is used to remove duplicate values at the same time member check in set is almost O(1) i.e.) 0 in set([0,1,2]) is O(1) member checking in list can go to a worst case of O(n)
Finally creating a list by iterating over list2 and checking if the first element is not present in set
This sounds like a homework problem, but I'm going to trust that this isn't one.
You can do this easily in two steps:
Store all first elements from a in a set.
Filter our lists in b whose first elements do not exist in the set.
def remove_common(a, b):
"""remove lists from b whose first element is the first element of a list in a"""
first_elements = set(sublist[0] for sublist in a)
return list(filter(lambda sublist: sublist[0] not in first_elements, b))
With dictionaries to enhance the data structure and garantees a linear complexity, assuming all l[0] are differents :
source,target={l[0]:l for l in a},{l[0]:l for l in b}
[target[key] for key in target.keys()-source.keys()]
You could assign c with a nested list comprehension.
c = [each_b for each_b in b if each_b[0] not in [each_a[0] for each_a in a]]
print(c)
>>> [[5, 3, 2], [7, 2, 1]]

Picking up an item from a list of lists

I'm pretty new in python and have am difficulty doing the next assignment.
I am receiving a list of lists with numbers and the word none, for example:
[[1,None],[2,4],[1.5,2]]
My problem is when I go through None (I need to sum up the lists) I need to replace it by the max number in the same place in the other lists.
So my result should be None = max(4,2) and receive :
[[1,4],[2,4],[1.5,2]]
If I go through a for loop I don't understand how can I go to the other sub lists and check them out (especially when I don't know how many subs lists I have)
Use a nested list comprehension with conditional
>>> l = [[1,None],[2,4],[1.5,2]]
>>> def findMax(j):
... return max(i[j] for i in l)
...
>>> [[j if j is not None else findMax(k) for k,j in enumerate(i)] for i in l]
[[1, 4], [2, 4], [1.5, 2]]
Here the list comprehension checks if each element is None or not. If not it will print the number, else it will fnd the maximum and print that element.
Another way using map is
>>> l = [[1,None],[2,4],[1.5,2]]
>>> maxVal = max(map(max, l))
>>> [[j if j is not None else maxVal for k,j in enumerate(i)] for i in l]
[[1, 4], [2, 4], [1.5, 2]]
Here is a hint: In Python, a for in loop iterates through all the elements in some iterable. If you have a list of lists, that means each element in the list can also have a for loop applied to it, as in a for loop inside a for loop. You could use this if and only if the maximum depth of a list is 2:
def get_deep_max(deeplst):
new = []
for elem in deeplst:
for num in elem:
new.append(num)
return max(new)
Try writing the code for replacing the none value yourself for practice.
Code:
for idx1, sublist in enumerate(list1):
for idx2, element in enumerate(sublist):
if element is None:
try:
sublist[idx2] = max(list1[idx1+1])
except IndexError:
pass
The problem is that if there is a None in the last list you didn’t specify what the code should do. I just added a try and except. You can replace pass with what you want the code to do.
My suggestion:
x = [[1,None],[2,4],[1.5,2]] #your list
highest_num = None # assume that 0 can be the highest number in a different situation
for each in x:`# find the highest number
for another in each:
if another > highest_num:
highest_num = another
for each in xrange(len(x)): # find the None and replace it with the highest number
for another in xrange(len(x[each])):
if x[each][another] is None:
x[each][another] = highest_num
from contextlib import suppress
l = [[1,None],[2,4],[1.5,2]]
for sub in l:
with suppress(ValueError):
i = sub.index(None) # find index of None in sublist (or ValueError)
sub[i] = max(s[i] for s in l if s[i] is not None) # replace sublist item with max of sublists in same index
break
print(l)
# [[1, 4], [2, 4], [1.5, 2]]
This one is a bit cleaner to read than the others IMO
l = [[1,None],[2,4],[1.5,2]]
maxVal = max(map(max, l)) # maps the function 'max' over all the sub-lists
for subl in l:
for idx,elem in enumerate(subl): # use enumerate to get the index and element
if elem is None:
subl[idx] = maxVal
print l
# [[1, 4], [2, 4], [1.5, 2]]
Here is my suggestion:
l = [[1, None], [2, 4], [1.5, 2]]
# l = [[1, None], [2, 4], [1.5, 2]]
l1 = zip(*l)
# l1 = [(1, 2, 1.5), (None, 4, 2)]
m = map(max, l1)
# m = [2, 4]
l2 = [map(lambda y: m[i] if y is None else y, x) for i,x in enumerate(l1)]
# l2 = [[1, 2, 1.5], [4, 4, 2]]
ret = zip(*l2)
# ret = [(1, 4), (2, 4), (1.5, 2)]

How do I remove duplicate arrays in a list in Python

I have a list in Python filled with arrays.
([4,1,2],[1,2,3],[4,1,2])
How do I remove the duplicate array?
Very simple way to remove duplicates (if you're okay with converting to tuples/other hashable item) is to use a set as an intermediate element.
lst = ([4,1,2],[1,2,3],[4,1,2])
# convert to tuples
tupled_lst = set(map(tuple, lst))
lst = map(list, tupled_lst)
If you have to preserve order or don't want to convert to tuple, you can use a set to check if you've seen the item before and then iterate through, i.e.,
seen = set()
def unique_generator(lst)
for item in lst:
tupled = tuple(item)
if tupled not in seen:
seen.add(tupled)
yield item
lst = list(unique_generator(lst))
This isn't great python, but you can write this as a crazy list comprehension too :)
seen = set()
lst = [item for item in lst if not(tuple(item) in seen or seen.add(tuple(item)))]
If order matters:
>>> from collections import OrderedDict
>>> items = ([4,1,2],[1,2,3],[4,1,2])
>>> OrderedDict((tuple(x), x) for x in items).values()
[[4, 1, 2], [1, 2, 3]]
Else it is much simpler:
>>> set(map(tuple, items))
set([(4, 1, 2), (1, 2, 3)])
l = ([4,1,2],[1,2,3],[4,1,2])
uniq = []
for i in l:
if not i in uniq:
uniq.append(i)
print('l=%s' % str(l))
print('uniq=%s' % str(uniq))
which produces:
l=([4, 1, 2], [1, 2, 3], [4, 1, 2])
uniq=[[4, 1, 2], [1, 2, 3]]
Use sets to keep track of seen items, but as sets can only contain hashable items so you may have to convert the items of your tuple to some hashable value first( tuple in this case) .
Sets provide O(1) lookup, so overall complexity is going to be O(N)
This generator function will preserve the order:
def solve(lis):
seen = set()
for x in lis:
if tuple(x) not in seen:
yield x
seen.add(tuple(x))
>>> tuple( solve(([4,1,2],[1,2,3],[4,1,2])) )
([4, 1, 2], [1, 2, 3])
If the order doesn't matter then you can simply use set() here:
>>> lis = ([4,1,2],[1,2,3],[4,1,2]) # this contains mutable/unhashable items
>>> set( tuple(x) for x in lis) # apply tuple() to each item, to make them hashable
set([(4, 1, 2), (1, 2, 3)]) # sets don't preserve order
>>> lis = [1, 2, 2, 4, 1] #list with immutable/hashable items
>>> set(lis)
set([1, 2, 4])

Categories

Resources