Python - Filter function with multiple conditions - python

I know this is a dummy question, but I didn't find the answer here the way I had in mind
I just want to know if I can apply multiple filters within a single filter function
A simple code to try it:
def check_odd(x):
return (x % 2) == 0
l1 = list(range(1,20))
list(filter((check_odd and lambda x:x>=5), l1))
Using the code above only one of the filters is applied (the 2nd).
I solved it using:
list(filter(check_odd, filter(lambda x:x>=5, l1)))
But I wonder if there is a way to group all filters in filter function.

Just transform the and inside the lamda, and combine your conditions there:
def check_odd(x):
return (x % 2) == 0
l1 = list(range(1,20))
l2 = list(filter((lambda x:x>=5 and check_odd(x)), l1))
print(l2)
Update
You can actually combine any number of conditions from other functions by wrapping them all inside the lambda. Here's an example:
def check_odd(x):
return (x % 2) == 0
def check_greater_than_five(x):
return x >= 5
def check_divisible_by_three(x):
return x % 3 == 0
l1 = list(range(1,20))
l2 = list(filter((lambda x: (check_odd(x) or check_divisible_by_three(x)) and check_greater_than_five(x)), l1))
print(l2) #prints all numbers that are greater than five, and are divisible by either 2 or 3

Related

How to extract strings from Python tuples or lists?

I don't get why it doesn't work but I'm new to programming so I might be making some simple mistakes.
def tuplasemstr(t):
tup1 = []
n = 0
while n <= 2 and type(t[n],) != str:
list.append(tup1, t[n])
n = n + 1
return tuple(tup1)
I expected something like:
t = ("a",3,2.1)
Output:
(3,2.1)
The solution given by #blhsing is the best one. But if you want to get your code working, you can do this:
def tuplasemstr(t):
tup1 = []
n = 0
while n < len(t):
if not isinstance(t[n], str):
list.append(tup1, t[n])
n = n + 1
return tuple(tup1)
t = ('a', 3, 2.1, 'c', 32)
print(tuplasemstr(t)) # (3, 2.1, 32)
To check for strings you should use isinstance inside the while loop. Also, don't hardcode the length with n <= 2, use n < len(t) instead.
If you mean to filter out string items in the tuple, you can use a generator expression with a condition that tests if the item is not an instance of string:
def tuplasemstr(t):
return tuple(i for i in t if not isinstance(i, str))

Largest even number in a list using fold

I want to accomplish this:
Construct a function that takes in a list as a parameter and returns the biggest even number in that list.
Do this by using the "fold" function in Python
I thought it might be something along the lines of:
def fold(f, v, l):
for x in l:
v = f(v, x)
return v
def biggest_even_number(xs):
l = [i for i in xs if i % 2 == 0]
return fold(l)
I know this is wrong but I just don't know how to set this up. How would I accomplish the above task using the "fold" function?
fold function looks good. You just need to call it with correct arguments:
def biggest_even_number(xs):
l = [i for i in xs if i % 2 == 0]
return fold(max, float("-inf"), l)
If it is not a homework, you can use builtin reduce() which basically does the same thing:
def biggest_even_number(xs):
l = [i for i in xs if i % 2 == 0]
return reduce(max, l, float("-inf"))
Thanks to #Steven Rumbalski, for anyone trying to find the maximum value of a sequence, you don't even need reduce:
def biggest_even_number(xs):
return max(i for i in xs if i % 2 == 0)
Do something like the following:
def fold(l):
biggest = float("-inf")
for i in l:
biggest = max(i, biggest)
return biggest
def biggest_even_number(xs):
l = [i for i in xs if i % 2 == 0]
return fold(l)

How can I sort tuples?

Im trying to write a program that has 2 variables (Integer) and that based on those variables print´s them joined and by order (Smaller number to Higher):
Like this:
together((0,39,100,210),(4,20))
printing the following:
(0,4,20,39,100,210)
The code:
def together(s,t):
y = s + t
z = 0
if sorted(y) == y:
print (y)
else:
for i in range(len(y)-1):
if y[z] > y[z+1]:
y[z+1] = y[z]
return (y)
print y
If variables are set like the following:
s=1,23,40 and t=9,90
I´m getting this:
(1, 23, 40, 9, 90)
which is out of order as you can see it should appear the following:
(1,9,23,40,90)
Why not just append both tuples and then sort them:
def together(s,t):
return tuple(sorted(s + t))
T = ((0,39,100,210),(4,20))
print tuple( sorted( reduce(tuple.__add__, T) ) )
This can combine and sort N number of tuples within a tuple, so it's not limited to two tuples

Filtering another filter object

I am trying to generate prime endlessly,by filtering out composite numbers. Using list to store and test for all primes makes the whole thing slow, so i tried to use generators.
from itertools import count
def chk(it,num):
for i in it:
if i%num:
yield(i)
genStore = [count(2)]
primeStore = []
while 1:
prime = next(genStore[-1])
primeStore.append(prime)
genStore.append(chk(genStore[-1],num))
It works quite well, generating primes, until it hit maximum recursion depth.
So I found ifilter (or filter in python 3).
From documentation of python standard library:
Make an iterator that filters elements from iterable returning only those for which the predicate is True. If predicate is None, return the items that are true. Equivalent to:
def ifilter(predicate, iterable):
# ifilter(lambda x: x%2, range(10)) --> 1 3 5 7 9
if predicate is None:
predicate = bool
for x in iterable:
if predicate(x):
yield x
So I get the following:
from itertools import count
genStore = [count(2)]
primeStore = []
while 1:
prime = next(genStore[-1])
primeStore.append(prime)
genStore.append(filter(lambda x:x%num,genStore[-1]))
I expected to get:
2
3
5
7
11
13
17
...
What I get is:
2
3
4
5
6
7
...
It seems next() only iterate through count(), not the filter. Object in list should point to the object, so I expected it works like filter(lambda x: x%n,(.... (filter(lambda x:x%3,filter(lambda x:x%2,count(2)))). I do some experiment and noticed the following characteristic:
filter(lambda x:x%2,filter(lambda x:x%3,count(0))) #works, filter all 2*n and 3*n
genStore = [count(2)]; genStore.append(filter(lambda x:x%2,genStore[-1])); genStore.append (filter(lambda x:x%2,genStore[-1])) - works, also filter all 2*n and 3*n
next(filter(lambda x:x%2,filter(lambda x:x%3,count(2)))) - works, printing out 5
On contrast:
from itertools import count
genStore = [count(2)]
primeStore = []
while 1:
prime = next(genStore[-1])
print(prime)
primeStore.append(prime)
genStore.append(filter(lambda x:x%prime,genStore[-1]))
if len(genStore) == 3:
for i in genStore[-1]:
print(i)
#It doesn't work, only filtering out 4*n.
Questions:
Why doesn't it work?
Is it a feature of python, or I made mistakes somewhere?
Is there any way to fix it?
I think your problem stems from the fact that lambdas are not evaluated will it's 'too late' and then you will get prime be same for all of them as all of them point at the same variable.
you can try to add custom filter and use normal function instead of lambda:
def myfilt(f, i, p):
for n in i:
print("gen:", n, i)
if f(n, p):
yield n
def byprime(x, p):
if x % p:
print("pri:", x, p)
return True
f = myfilt(byprime, genStore[-1], prime)
this way you avoid the problems of lambdas being all the same

Python set intersection question

I have three sets:
s0 = [set([16,9,2,10]), set([16,14,22,15]), set([14,7])] # true, 16 and 14
s1 = [set([16,9,2,10]), set([16,14,22,15]), set([7,8])] # false
I want a function that will return True if every set in the list intersects with at least one other set in the list. Is there a built-in for this or a simple list comprehension?
all(any(a & b for a in s if a is not b) for b in s)
Here's a very simple solution that's very efficient for large inputs:
def g(s):
import collections
count = collections.defaultdict(int)
for a in s:
for x in a:
count[x] += 1
return all(any(count[x] > 1 for x in a) for a in s)
It's a little verbose but I think it's a pretty efficient solution. It takes advantage of the fact that when two sets intersect, we can mark them both as connected. It does this by keeping a list of flags as long as the list of sets. when set i and set j intersect, it sets the flag for both of them. It then loops over the list of sets and only tries to find a intersection for sets that haven't already been intersected. After reading the comments, I think this is what #Victor was talking about.
s0 = [set([16,9,2,10]), set([16,14,22,15]), set([14,7])] # true, 16 and 14
s1 = [set([16,9,2,10]), set([16,14,22,15]), set([7,8])] # false
def connected(sets):
L = len(sets)
if not L: return True
if L == 1: return False
passed = [False] * L
i = 0
while True:
while passed[i]:
i += 1
if i == L:
return True
for j, s in enumerate(sets):
if j == i: continue
if sets[i] & s:
passed[i] = passed[j] = True
break
else:
return False
print connected(s0)
print connected(s1)
I decided that an empty list of sets is connected (If you produce an element of the list, I can produce an element that it intersects ;). A list with only one element is dis-connected trivially. It's one line to change in either case if you disagree.
Here's a more efficient (if much more complicated) solution, that performs a linear number of intersections and a number of unions of order O( n*log(n) ), where n is the length of s:
def f(s):
import math
j = int(math.log(len(s) - 1, 2)) + 1
unions = [set()] * (j + 1)
for i, a in enumerate(s):
unions[:j] = [set.union(set(), *s[i+2**k:i+2**(k+1)]) for k in range(j)]
if not (a & set.union(*unions)):
return False
j = int(math.log(i ^ (i + 1), 2))
unions[j] = set.union(a, *unions[:j])
return True
Note that this solution only works on Python >= 2.6.
As usual I'd like to give the inevitable itertools solution ;-)
from itertools import combinations, groupby
from operator import itemgetter
def any_intersects( sets ):
# we are doing stuff with combinations of sets
combined = combinations(sets,2)
# group these combinations by their first set
grouped = (g for k,g in groupby( combined, key=itemgetter(0)))
# are any intersections in each group
intersected = (any((a&b) for a,b in group) for group in grouped)
return all( intersected )
s0 = [set([16,9,2,10]), set([16,14,22,15]), set([14,7])]
s1 = [set([16,9,2,10]), set([16,14,22,15]), set([7,8])]
print any_intersects( s0 ) # True
print any_intersects( s1 ) # False
This is really lazy and will only do the intersections that are required. It can also be a very confusing and unreadable oneliner ;-)
To answer your question, no, there isn't a built-in or simple list comprehension that does what you want. Here's another itertools based solution that is very efficient -- surprisingly about twice as fast as #THC4k's itertools answer using groupby() in timing tests using your sample input. It could probably be optimized a bit further, but is very readable as presented. Like #AaronMcSmooth, I arbitrarily decided what to return when there are no or is only one set in the input list.
from itertools import combinations
def all_intersect(sets):
N = len(sets)
if not N: return True
if N == 1: return False
intersected = [False] * N
for i,j in combinations(xrange(N), 2):
if not intersected[i] or not intersected[j]:
if sets[i] & sets[j]:
intersected[i] = intersected[j] = True
return all(intersected)
This strategy isn't likely to be as efficient as #Victor's suggestion, but might be more efficient than jchl's answer due to increased use of set arithmetic (union).
s0 = [set([16,9,2,10]), set([16,14,22,15]), set([14,7])]
s1 = [set([16,9,2,10]), set([16,14,22,15]), set([7,8])]
def freeze(list_of_sets):
"""Transform a list of sets into a frozenset of frozensets."""
return frozenset(frozenset(set_) for set_ in list_of_sets)
def all_sets_have_relatives(set_of_sets):
"""Check if all sets have another set that they intersect with.
>>> all_sets_have_relatives(s0) # true, 16 and 14
True
>>> all_sets_have_relatives(s1) # false
False
"""
set_of_sets = freeze(set_of_sets)
def has_relative(set_):
return set_ & frozenset.union(*(set_of_sets - set((set_,))))
return all(has_relative(set) for set in set_of_sets)
This may give better performance depending on the distribution of the sets.
def all_intersect(s):
count = 0
for x, a in enumerate(s):
for y, b in enumerate(s):
if a & b and x!=y:
count += 1
break
return count == len(s)

Categories

Resources