Replace nested for loop with list comprehension in Python - python

Given a list of integers c, I want to perform the following:
Count how many elements of c are less than index i, where i is from 0 to len(c).
For example: c=[1, 0, 2, 1], then the output to the above will be b=[1, 3, 4, 4] because only the 1 element of c is less than or equal to i=0, then there are 3 elements of c less than or equal to i=1, then there are 4 elements of c less than or equal to i=2, and lastly there are 4 elements of c less than or equal to i=3.
Find a=[b[0] b[1]-1 b[2]-2 ... ]
Find math.prod(a)
I manage to do it using nested for loops, but I am learning the list comprehensions and want to transform them into nested for loops inside the list comprehensions.
#this code works
def solve(c):
a=1
for i in range(len(c)):
a=a*(sum(element<=i for element in c)-i)
return(a)
#input c=[1, 0, 2, 1]
#output 4
But this one fails:
$this code does not work
def solve(c):
a=1
return([(a=a*(sum(element<=i for element in c)-i)) for i in range(len(c))])

[[item for item in c if item <= ind] for ind in range(len(c))] give you the list of item that are < for the index.
[len([item for item in c if item <= ind]) - ind for ind in range(len(c))] will give you your second point.

Related

Comparing two lists and making new list

So lets say I have two lists a=[1,2,3,4,5,6] and b=[2,34,5,67,5,6] I want to create a third list which will have 1 where elements are different in a and b and 0 when they are same, so above would be like c=[1,1,1,1,0,0]
You can zip the lists and compare them in a list comprehension. This takes advantage of the fact that booleans are equivalent to 1 and 0 in python:
a=[1,2,3,4,5,6]
b=[2,34,5,67,5,6]
[int(m!=n) for m, n, in zip(a, b)]
# [1, 1, 1, 1, 0, 0]
Try a list comprehension over elements of each pair of items in the list with zip:
[ 0 if i == j else 1 for i,j in zip(a,b) ]
Iterating with a for loop is an option, though list comprehension may be more efficient.
a=[1,2,3,4,5,6]
b=[2,34,5,67,5,6]
c=[]
for i in range(len(a)):
if a[i] == b[i]:
c.append(0)
else:
c.append(1)
print(c)
prints
[1, 1, 1, 1, 0, 0]
If you will have multiple vector operations and they should be fast. Checkout numpy.
import numpy as np
a=[1,2,3,4,5,6]
b=[2,34,5,67,5,6]
a = np.array(a)
b = np.array(b)
c = (a != b).astype(int)
# array([1, 1, 1, 1, 0, 0])
idk if this is exactly what youre loocking for but this should work:
edidt: just found out that Joe Thor commented almost the exact same a few minutes earlier than me lmao
a = [1, 2, 3, 4, 5, 6]
b = [2, 34, 5, 67, 5, 6]
results = []
for f in range(0, len(a)):
if a[f] == b[f]:
results.append(0)
else:
results.append(1)
print(results)
This can be done fairly simply using a for loop. It does assume that both lists, a and b, are the same length. An example code would like something like this:
a = [1,2,3,4,5,6]
b = [2,34,5,67,5,6]
c = []
if len(a) == len(b):
for i in range(0,len(a)):
if(a[i] != b[i]):
c.append(1)
else:
c.append(0)
This can also be done using list comprehension:
a = [1,2,3,4,5,6]
b = [2,34,5,67,5,6]
c = []
if len(a) == len(b):
c = [int(i != j) for i,j in zip(a,b)]
The list comprehension code is from this thread: Comparing values in two lists in Python
a = [1, 2, 3, 4, 5, 6]
b = [2, 34, 5, 67, 5,6]
c = []
index = 0
x = 1
y = 0
for i in range(len(a)): # iterating loop from index 0 till the last
if a[index]!= b[index]: # comapring each index
c.append(x) # if not equal append c with '1'
index += 1 # increment index to move to next index in both lists
else:
c.append(y)
index += 1
print(c)
This should work for two lists of any type.
tstlist = ["w","s","u"]
lstseasons = ["s","u","a","w"]
lstbool_Seasons = [1 if ele in tstlist else 0 for ele in lstseasons]
Output: lstbool_Seasons = [1,1,0,1]
This is the first time I have posted anything, still figuring out how things work here, so please forgive faux pas...

Compare elements of two list

I have been given two lists, say list1 and list2. I have to arrange the elements of the list1 in such a way that at particular index the element of list1 is greater than the element of list2. We have to find how many such elements of list1 are there.
For example:
list1=[20,30,50]
list2=[60,40,25]
Here only element index 2 is greater i.e. 50>25, but if we swap 50 and 30 in list1
So,
list1=[20,50,30]
list2=[60,40,25]
then 50 > 40 ( at index 1) and 30 > 25 (at index 2). So we got 2 elements 50 and 30 which are greater at their respective index.
Here is my approach
def swap(a,b):
a,b=b,a
return a,b
n=3
g=list(map(int,input().split()))
o=list(map(int,input().split()))
c=0
for i in range(n):
if o[i]>g[i]:
for j in range(i+1,n):
if g[j]>o[i]:
g[i],g[j]=swap(g[i],g[j])
c+=1
break
else:
c+=1
print(c)
But for
list1= [3,6,7,5,3,5,6,2,9,1]
list2= [2,7,0,9,3,6,0,6,2,6]
Its giving c=6 but expected output is c=7
You have to sort the two lists and then run through them to find "matches" where a value of list1 is greater than the next value of list2. This will pair up the values with the smallest possible difference and thus maximize the pairings.
For example:
list1=[20,30,50]
list2=[60,40,25]
iter1 = iter(sorted(list1)) # iterator to run through sorted list1
n1 = next(iter1,None) # n1 is current number in list1
count = 0 # count of paired elements (n1>n2)
for n2 in sorted(list2): # go through sorted list 2
while n1 is not None and n1 <= n2: # skip over ineligible items of list1
n1 = next(iter1,None)
if n1 is None: break # stop when list 1 is exhausted
count += 1 # count 1 pair and move on to next of list2
print(count) # 2
list1= [3,6,7,5,3,5,6,2,9,1]
list2= [2,7,0,9,3,6,0,6,2,6]
list1 = sorted(list1)
it = iter(enumerate(list1))
list2 = sorted(list2)
c = next(it)
good = []
for i, n in enumerate(list2 ):
try:
while c[1] < n:
c = next(it)
good.append([i, c[0]])
c = next(it)
except StopIteration:
break
for idx1, idx2 in good:
list1[idx1], list1[idx2] = list1[idx2], list1[idx1]
final_l1_l2 = sum(a > b for a, b in zip(list1, list2))# how many l1 are > l2
print(final_l1_l2)
output:
7
also, you can print list1 and list2 after the rearrange:
print(list1)
print(list2)
output:
[1, 2, 3, 3, 5, 6, 6, 7, 9, 5]
[0, 0, 2, 2, 3, 6, 6, 6, 7, 9]
the idea is to sort both lists and then to check what elements from list1 are greater than the elements from list2 if one element from list1 it is smaller then the current element from list2 just go to the next element from the list1 till there are no more elements in list1

Largest Subset whose sum is less than equal to a given sum

A list is defined as follows: [1, 2, 3]
and the sub-lists of this are:
[1], [2], [3],
[1,2]
[1,3]
[2,3]
[1,2,3]
Given K for example 3 the task is to find the largest length of sublist with sum of elements is less than equal to k.
I am aware of itertools in python but it will result in segmentation fault for larger lists. Is there any other efficient algorithm to achieve this? Any help would be appreciated.
My code is as allows:
from itertools import combinations
def maxLength(a, k):
#print a,k
l= []
i = len(a)
while(i>=0):
lst= list(combinations(sorted(a),i))
for j in lst:
#rint list(j)
lst = list(j)
#print sum(lst)
sum1=0
sum1 = sum(lst)
if sum1<=k:
return len(lst)
i=i-1
You can use the dynamic programming solution that #Apy linked to. Here's a Python example:
def largest_subset(items, k):
res = 0
# We can form subset with value 0 from empty set,
# items[0], items[0...1], items[0...2]
arr = [[True] * (len(items) + 1)]
for i in range(1, k + 1):
# Subset with value i can't be formed from empty set
cur = [False] * (len(items) + 1)
for j, val in enumerate(items, 1):
# cur[j] is True if we can form a set with value of i from
# items[0...j-1]
# There are two possibilities
# - Set can be formed already without even considering item[j-1]
# - There is a subset with value i - val formed from items[0...j-2]
cur[j] = cur[j-1] or ((i >= val) and arr[i-val][j-1])
if cur[-1]:
# If subset with value of i can be formed store
# it as current result
res = i
arr.append(cur)
return res
ITEMS = [5, 4, 1]
for i in range(sum(ITEMS) + 1):
print('{} -> {}'.format(i, largest_subset(ITEMS, i)))
Output:
0 -> 0
1 -> 1
2 -> 1
3 -> 1
4 -> 4
5 -> 5
6 -> 6
7 -> 6
8 -> 6
9 -> 9
10 -> 10
In above arr[i][j] is True if set with value of i can be chosen from items[0...j-1]. Naturally arr[0] contains only True values since empty set can be chosen. Similarly for all the successive rows the first cell is False since there can't be empty set with non-zero value.
For rest of the cells there are two options:
If there already is a subset with value of i even without considering item[j-1] the value is True
If there is a subset with value of i - items[j - 1] then we can add item to it and have a subset with value of i.
As far as I can see (since you treat sub array as any items of the initial array) you can use greedy algorithm with O(N*log(N)) complexity (you have to sort the array):
1. Assign entire array to the sub array
2. If sum(sub array) <= k then stop and return sub array
3. Remove maximim item from the sub array
4. goto 2
Example
[1, 2, 3, 5, 10, 25]
k = 12
Solution
sub array = [1, 2, 3, 5, 10, 25], sum = 46 > 12, remove 25
sub array = [1, 2, 3, 5, 10], sum = 21 > 12, remove 10
sub array = [1, 2, 3, 5], sum = 11 <= 12, stop and return
As an alternative you can start with an empty sub array and add up items from minimum to maximum while sum is less or equal then k:
sub array = [], sum = 0 <= 12, add 1
sub array = [1], sum = 1 <= 12, add 2
sub array = [1, 2], sum = 3 <= 12, add 3
sub array = [1, 2, 3], sum = 6 <= 12, add 5
sub array = [1, 2, 3, 5], sum = 11 <= 12, add 10
sub array = [1, 2, 3, 5, 10], sum = 21 > 12, stop,
return prior one: [1, 2, 3, 5]
Look, for generating the power-set it takes O(2^n) time. It's pretty bad. You can instead use the dynamic programming approach.
Check in here for the algorithm.
http://www.geeksforgeeks.org/dynamic-programming-subset-sum-problem/
And yes, https://www.youtube.com/watch?v=s6FhG--P7z0 (Tushar explains everything well) :D
Assume everything is positive. (Handling negatives is a simple extension of this and is left to the reader as an exercise). There exists an O(n) algorithm for the described problem. Using the O(n) median select, we partition the array based on the median. We find the sum of the left side. If that is greater than k, then we cannot take all elements, we must thus recur on the left half to try to take a smaller set. Otherwise, we subtract the sum of the left half from k, then we recur on the right half to see how many more elements we can take.
Partitioning the array based on median select and recurring on only 1 of the halves yields a runtime of n+n/2 +n/4 +n/8.. which geometrically sums up to O(n).

Compare 2 lists and print the element of the 2nd list if it is present in first list, but not by using 2 for loops

I have to compare 2 lists, if element of list a is present in list b, then the element of list b is to print.
a = [1, 3, 2, 1, 3]
b = [2, 2, 1, 1, 1, 4, 2, 3]
ans = [1, 1, 1, 3, 2, 2, 2, 1, 1, 1, 3]
I may get the answer by using 2 for loops like:
for a_ in a:
for b_ in b:
if a_ == b_:
print b_
op: 1 1 1 3 2 2 2 1 1 1 3
But I don't want to use 2 for loops. How can I do that with a single loop?
Use collections.Counter to count for you:
from collections import Counter
c = Counter(b)
ans = []
for x in a:
ans += [x]*c.get(x,0)
This is one potential way (a bit messy), just posting it since it turns out Fabricator didn't end up with the correct result.
[item for sublist in ([i] * b.count(i) for i in a) for item in sublist]
Basically, the ([i] * b.count(i) for i in a) part builds the list, but it ends up as a list of lists, so then I did the [item for sublist in list for item in sublist] thing to flatten the list.
It's probably a bit similar to the answer by zondo but this keeps it as a list of numbers instead of a string.
print(" ".join(str(x) for x in a for _ in range(b.count(x))))
This, should be work:
for a_ in a:
if b.count(a_) :
ans+=((str(a_)+' ')*b.count(a_)).strip().split(' ')
list.count(x) count the number of occourrences of x in list.
you can print n times a string simply: *'mystring'times
Hope I helped you!

Remove elements that appear more often than once from numpy array

The question is, how can I remove elements that appear more often than once in an array completely. Below you see an approach that is very slow when it comes to bigger arrays.
Any idea of doing this the numpy-way? Thanks in advance.
import numpy as np
count = 0
result = []
input = np.array([[1,1], [1,1], [2,3], [4,5], [1,1]]) # array with points [x, y]
# count appearance of elements with same x and y coordinate
# append to result if element appears just once
for i in input:
for j in input:
if (j[0] == i [0]) and (j[1] == i[1]):
count += 1
if count == 1:
result.append(i)
count = 0
print np.array(result)
UPDATE: BECAUSE OF FORMER OVERSIMPLIFICATION
Again to be clear: How can I remove elements appearing more than once concerning a certain attribute from an array/list ?? Here: list with elements of length 6, if first and second entry of every elements both appears more than once in the list, remove all concerning elements from list. Hope I'm not to confusing. Eumiro helped me a lot on this, but I don't manage to flatten the output list as it should be :(
import numpy as np
import collections
input = [[1,1,3,5,6,6],[1,1,4,4,5,6],[1,3,4,5,6,7],[3,4,6,7,7,6],[1,1,4,6,88,7],[3,3,3,3,3,3],[456,6,5,343,435,5]]
# here, from input there should be removed input[0], input[1] and input[4] because
# first and second entry appears more than once in the list, got it? :)
d = {}
for a in input:
d.setdefault(tuple(a[:2]), []).append(a[2:])
outputDict = [list(k)+list(v) for k,v in d.iteritems() if len(v) == 1 ]
result = []
def flatten(x):
if isinstance(x, collections.Iterable):
return [a for i in x for a in flatten(i)]
else:
return [x]
# I took flatten(x) from http://stackoverflow.com/a/2158522/1132378
# And I need it, because output is a nested list :(
for i in outputDict:
result.append(flatten(i))
print np.array(result)
So, this works, but it's impracticable with big lists.
First I got
RuntimeError: maximum recursion depth exceeded in cmp
and after applying
sys.setrecursionlimit(10000)
I got
Segmentation fault
how could I implement Eumiros solution for big lists > 100000 elements?
np.array(list(set(map(tuple, input))))
returns
array([[4, 5],
[2, 3],
[1, 1]])
UPDATE 1: If you want to remove the [1, 1] too (because it appears more than once), you can do:
from collections import Counter
np.array([k for k, v in Counter(map(tuple, input)).iteritems() if v == 1])
returns
array([[4, 5],
[2, 3]])
UPDATE 2: with input=[[1,1,2], [1,1,3], [2,3,4], [4,5,5], [1,1,7]]:
input=[[1,1,2], [1,1,3], [2,3,4], [4,5,5], [1,1,7]]
d = {}
for a in input:
d.setdefault(tuple(a[:2]), []).append(a[2])
d is now:
{(1, 1): [2, 3, 7],
(2, 3): [4],
(4, 5): [5]}
so we want to take all key-value pairs, that have single values and re-create the arrays:
np.array([k+tuple(v) for k,v in d.iteritems() if len(v) == 1])
returns:
array([[4, 5, 5],
[2, 3, 4]])
UPDATE 3: For larger arrays, you can adapt my previous solution to:
import numpy as np
input = [[1,1,3,5,6,6],[1,1,4,4,5,6],[1,3,4,5,6,7],[3,4,6,7,7,6],[1,1,4,6,88,7],[3,3,3,3,3,3],[456,6,5,343,435,5]]
d = {}
for a in input:
d.setdefault(tuple(a[:2]), []).append(a)
np.array([v for v in d.itervalues() if len(v) == 1])
returns:
array([[[456, 6, 5, 343, 435, 5]],
[[ 1, 3, 4, 5, 6, 7]],
[[ 3, 4, 6, 7, 7, 6]],
[[ 3, 3, 3, 3, 3, 3]]])
This is a corrected, faster version of Hooked's answer. count_unique counts the number of the number of occurrences for each unique key in keys.
import numpy as np
input = np.array([[1,1,3,5,6,6],
[1,1,4,4,5,6],
[1,3,4,5,6,7],
[3,4,6,7,7,6],
[1,1,4,6,88,7],
[3,3,3,3,3,3],
[456,6,5,343,435,5]])
def count_unique(keys):
"""Finds an index to each unique key (row) in keys and counts the number of
occurrences for each key"""
order = np.lexsort(keys.T)
keys = keys[order]
diff = np.ones(len(keys)+1, 'bool')
diff[1:-1] = (keys[1:] != keys[:-1]).any(-1)
count = np.where(diff)[0]
count = count[1:] - count[:-1]
ind = order[diff[1:]]
return ind, count
key = input[:, :2]
ind, count = count_unique(key)
print key[ind]
#[[ 1 1]
# [ 1 3]
# [ 3 3]
# [ 3 4]
# [456 6]]
print count
[3 1 1 1 1]
ind = ind[count == 1]
output = input[ind]
print output
#[[ 1 3 4 5 6 7]
# [ 3 3 3 3 3 3]
# [ 3 4 6 7 7 6]
# [456 6 5 343 435 5]]
Updated Solution:
From the comments below, the new solution is:
idx = argsort(A[:, 0:2], axis=0)[:,1]
kidx = where(sum(A[idx,:][:-1,0:2]!=A[idx,:][1:,0:2], axis=1)==0)[0]
kidx = unique(concatenate((kidx,kidx+1)))
for n in arange(0,A.shape[0],1):
if n not in kidx:
print A[idx,:][n]
> [1 3 4 5 6 7]
[3 3 3 3 3 3]
[3 4 6 7 7 6]
[456 6 5 343 435 5]
kidx is a index list of the elements you don't want. This preserves rows where the first two inner elements do not match any other inner element. Since everything is done with indexing, it should be fast(ish), though it requires a sort on the first two elements. Note that original row order is not preserved, though I don't think this is a problem.
Old Solution:
If I understand it correctly, you simply want to filter out the results of a list of lists where the first element of each inner list is equal to the second element.
With your input from your update A=[[1,1,3,5,6,6],[1,1,4,4,5,6],[1,3,4,5,6,7],[3,4,6,7,7,6],[1,1,4,6,88,7],[3,3,3,3,3,3],[456,6,5,343,435,5]], the following line removes A[0],A[1] and A[4]. A[5] is also removed since that seems to match your criteria.
[x for x in A if x[0]!=x[1]]
If you can use numpy, there is a really slick way of doing the above. Assume that A is an array, then
A[A[0,:] == A[1,:]]
Will pull out the same values. This is probably faster than the solution listed above if you want to loop over it.
Why not create another array to hold the output?
Iterate through your main list and for each i check if i is in your other array and if not append it.
This way, your new array will not contain more than one of each element

Categories