How to isolate specific rows of a Cartesian Product - Python - python

I've created a cartesian product and printed from the following code:
A = [0, 3, 5]
B = [5, 10, 15]
C = product(A, B)
for n in C:
print(n)
And we have a result of
(0, 5)
(0, 10)
(0, 15)
(3, 5)
(3, 10)
(3, 15)
(5, 5)
(5, 10)
(5, 15)
Is it possible to check the sum of each set within the cartesian product to a target value of 10? Can we then return or set aside the sets that match?
Result should be:
(0, 10)
(5, 5)
Finally, how can I count the frequency of numbers in my resulting subset to show that 0 appeared once, 10 appeared once, and 5 appeared twice? I would appreciate any feedback or guidance.

You can use a list comprehension like this:
C = [p for p in product(A, B) if sum(p) == 10]
And to count the frequency of the numbers in the tuples, as #Amadan suggests, you can use collections.Counter after using itertools.chain.from_iterable to chain the sub-lists into one sequence:
from collections import Counter
from itertools import chain
Counter(chain.from_iterable(C))
which returns, given your sample input:
Counter({5: 2, 0: 1, 10: 1})
which is an instance of a dict subclass, so you can iterate over its items for output:
for n, freq in Counter({5: 2, 0: 1, 10: 1}).items():
print('{}: {}'.format(n, freq))

Related

Combining arrays of diff sizes

I have this code that creates permutations of a given number. It also give the permuatations based on the number of specified digits, so 2 would give the permutations of all possible 2 digit values. I have this looped so for a 4 digit number, it would loop giving all permutations scenarios, like 4,3,2 and 1 digit permutations scenarios. The problem im having is how to store the perm variable which stores the permutations. I tried making a multi array perm, then as the loop iterates it adds the new array to the perm. Didn't work because the arrays are different sizes. How can I continue?
def fp(number):
# A Python program to print all
# permutations using library function
from itertools import permutations
# Get all permutations of [1, 2, 3]
c= list(map(int,str(number)))
print(c, len(c))
i=1
while i <= len(c):
perm= permutations(c,i) #permuate the number c to the number of specified digits i
i+=1
# Print the obtained permutations
for i in list(perm):
print (i)
You are searching for a powerset, search in these functions by itertools for powerset. I just changed the combinations to permutations.
Then loop through all permutations and append them to a list (you could also use a dictionary)
import itertools
def powerset(iterable):
s = list(iterable)
return itertools.chain.from_iterable(itertools.permutations(s, r) for r in range(1,len(s)+1))
lst_of_numbers = [1, 2, 3]
out = []
for perm in powerset(lst_of_numbers):
out.append(perm)
print(out)
[(1,), (2,), (3,), (1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2), (1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]
Use another variable to hold all the permutations. Extend it with the list returned by each call to permutations().
def fp(number):
# A Python program to print all
# permutations using library function
from itertools import permutations
all_perms = []
c= list(map(int,str(number)))
print(c, len(c))
i=1
while i <= len(c):
perm= permutations(c,i) #permuate the number c to the number of specified digits i
all_perms.extend(perm)
i+=1
# Print the obtained permutations
for i in all_perms:
print (i)

Sum element of numbers to get desired result

By using the snippet
import itertools
numbers = [1,2,3,4,5]
results = [7,8]
allcombs = [seq for i in range(len(numbers), 0, -1) for seq in itertools.combinations(numbers, i) if sum(seq) in results]
print(allcombs)
I'm able to get all combinations which give me the desire results. Major problem here is that the number must not repeat. So, instead of result
[(1, 2, 4), (1, 2, 5), (1, 3, 4), (2, 5), (3, 4), (3, 5)]
I need to get
[(1, 2, 4),(3, 5)]
All elements of results doesnt needs to be contained in combination of numbers.
Edit:
1 Solution
usednumbers = []
newresult = []
for comb in allcombs:
if not any(a in usednumbers for a in comb):
newresult.append(comb)
for n in comb:
usednumbers.append(n)
print(newresult)
I would suggest a recursive function that will find the largest combination first and then call itself with the remaining numbers to add other combinations that match the results.
Also, the combinations should be generated with indexes in addition to the numbers themselves to make it easier to determine the remaining values when the list contains duplicates numbers.
from itertools import combinations
def comboToSums(numbers,results,size=None):
if size is None: size = len(numbers)
if size == 0: return []
for combo in combinations(enumerate(numbers),size):
indexes,values = zip(*combo)
if sum(values) not in results: continue
remaining = [n for i,n in enumerate(numbers) if i not in indexes]
return [values] + comboToSums(remaining,results)
return comboToSums(numbers,results,size-1)
output:
numbers = [1,2,3,4,5]
results = [7,8]
c = comboToSums(numbers,results)
print(c) # [(1, 2, 4), (3, 5)]

All possible exact sums from multiple arrays

I've been given the following problem and my code has become a horrible mess and I'm quite stuck.
You are given an allowance to spend at some shops (something like $15, just some positive integer)
You may only spend this money under two conditions:
1) You buy exactly one item from each shop.
2) You spend all of your money (nothing left over, and no debt)
Find all possible ways you could have purchased items to satisfy the above.
In actuality, you're given a budget int and some array like
9 and [[1,2,3],[0,5],[2,3,8]]
where each inner array lists the prices of items in the a shop. There can be arbitrarily many shops with arbitrarily many items. Costs cannot be negative, but they can be free!
The expected solution here would be:
[[1,5,3],[2,5,2],[1,0,8]]
As each array contains one item from each shop, each totals 9, and all possibilities are present.
Just to make it more difficult, speed is paramount.
The following is my code that has descended into madness and a near complete lack of functionality:
def Stepper(bud,LOL):
n=len(LOL)
lasts=[]
indices=[0 for j in range(n)]
focus=0
funds=[0 for j in range(n+1)]
funds[0]=bud
sols=[]
moveable=[]
for j in range(n):
length=len(LOL[j])
if(length==0):
return []
lasts.append(length-1)
if(moveable==[]):
if(length==1):
funds[j+1]=funds[j]-LOL[j][0]
else:
moveable.append(j)
focus=j
while(moveable!=[]):
while(LOL[focus][indices[focus]] > funds[focus]):
indices[focus]+=1
if(indices[focus]==lasts[focus]):
if(moveable[-1]==focus):
moveable.remove(focus)
if(moveable==[]):
if(focus<n-1):
moveable.append(focus+1)
funds[focus+1]=funds[focus]-LOL[focus][indices[focus]]
#print(n,lasts,indices,focus,moveable,funds,sols)
if((funds[focus+1]!=0) and (focus<n-1)):
focus+=1
indices[focus]=0
else:
if(funds[focus+1]==0):
for j in range(focus+1,n):
indices[j]=lasts[j]
sols.append(list(indices))
if(moveable[-1]==n-1):
moveable.remove(n-1)
if(moveable!=[]):
focus=moveable[-1]
indices[focus]+=1
if(indices[focus]==lasts[focus]):
if(moveable[-1]==focus):
moveable.remove(focus)
if(moveable==[]):
if(focus<n-1):
moveable.append(focus+1)
funds[focus+1]=funds[focus]-LOL[focus][indices[focus]]
focus+=1
indices[focus]=0
return(sols)
where bud is the budget and LOL is the list of lists (the shops and prices)
This is a combinatorics problem. Which item from shop 1 combines with which item from shop 2 and which single items from shops 3...n to add up to some number?
Python's standard library has a function which can generate all these combinations for you, saving you the nested for loops. It's the handy itertools.product:
>>> import itertools
>>> budget = 9
>>> shops = [[1, 2, 3], [0, 5], [2, 3, 8]]
>>> list(itertools.product(*shops))
[(1, 0, 2),
(1, 0, 3),
(1, 0, 8),
(1, 5, 2),
(1, 5, 3),
(1, 5, 8),
(2, 0, 2),
(2, 0, 3),
(2, 0, 8),
(2, 5, 2),
(2, 5, 3),
(2, 5, 8),
(3, 0, 2),
(3, 0, 3),
(3, 0, 8),
(3, 5, 2),
(3, 5, 3),
(3, 5, 8)]
Next we want to get rid of all the combinations which do not satisfy our condition (that the prices exactly add up to the total budget). Let's use the built-in filter function to get our solution:
>>> list(filter(lambda prices: sum(prices) == budget, itertools.product(*shops))
[(1, 0, 8), (1, 5, 3), (2, 5, 2)]
If you are allowed to use itertools
import itertools
x = [[1,2,3],[0,5],[2,3,8]]
combinations = list(itertools.product(*x))
for o in combinations:
if sum(o) == 9:
print(o)
Output:
(1, 0, 8)
(1, 5, 3)
(2, 5, 2)
Depends on this question.
If you're still interested in an algorithm this is my solution:
def Stepper(bud, shops, combinations = None):
if combinations is None:
combinations = [[item] for item in shops[0]]
#if empty, populate the list of combinations with list containing every item of the first shop
shops = shops[1:] #remove the shop from the list
if len(shops) == 1: #if only one shop remains
counter = 0
while counter < len(combinations):
comb = combinations[counter] # take a combination
diff = bud - sum(comb) # check how much you have to spend
if diff in shops[0]:
#if the shop have what you're looking for you keep the combination
comb.append(diff)
counter += 1
else:
# there is no way to spend all the money
combinations.remove(comb)
return combinations
new_combinations = list()
for old_combination in combinations:
for item in shops[0]:
comb = old_combination + [item] #create every possible combination mixing the old combinations with the items of the next stop
if sum(comb) < bud: new_combinations.append(comb) # the combination is valid only if you have 0 or more money left
return Stepper(bud,shops[1:],new_combinations) # calculate combinations with the remaining shops
To test it simply call
Stepper(9, [[1,2,3],[0,5],[2,3,8]])
Here is a simple, and probably inefficient, way to solve it.
In [70]: s = [[1,2,3],[0,5],[2,3,8]]
In [71]: n = 9
In [72]: for x in s[0]:
...: for y in s[1]:
...: for z in s[2]:
...: if x+y+z == n:
...: print(x,y,z)
...:
1 0 8
1 5 3
2 5 2
Starting from the first shop, for any option, iterate over the next store, then again, for any option of the second store, iterate over the options of the next store. Sum up all the prices and check if the sum is equal to 9.

Find overlapping elements in a list of tuples?

From my understanding of the intersection function, it finds complete overlap between elements in a list. For example:
tup_1 = [(1,2,3),(4,5,6)]
tup_2 = [(4,5,6)]
ol_tup = set(tup_1).intersection(tup_2)
print ol_tup
would yield:
set([(4, 5, 6)])
However, suppose my list of tuples are set up as this:
tup_1 = [(1,2,3),(4,5,5)]
tup_2 = [(4,5,6)]
Where there's an overlap in 2 elements of the 2nd tuple in tup_1 and 1st tuple in tup_2. If I want to python to return these 2 tuples: (4,5,5) and (4,5,6), is there an easier way than this nested for loop (below)?
for single_tuple_1 in tup_1:
for single_tuple_2 in tup_2:
if single_tuple_1[0] == single_tuple_2[0] and single_tuple_1[1] == single_tuple_2[1]:
print single_tuple_1,single_tuple_2
EDIT:
For this case, suppose order matters and suppose the tuples contain 5 elements:
tup_1 = [(1,2,3,4,5),(4,5,6,7,8),(11,12,13,14,15)]
tup_2 = [(1,2,3,4,8),(4,5,1,7,8),(11,12,13,14,-5)]
And I would like to find the tuples that intersect with each other in their respective first 4 elements. So the result should be:
[(1,2,3,4,5),(1,2,3,4,8),(11,12,13,14,15),(11,12,13,14,-5)]
How would the code change to accommodate this?
If you want to return all the pairs of "overlapping" tuples there's no way around comparing all the pairs, i.e. a quadratic algorithm. But you could make the code a bit more elegant using a list comprehension, product for the combinations and zip and sum for the comparison:
>>> tup_1 = [(1,2,3),(4,5,5),(7,8,9)]
>>> tup_2 = [(4,5,6),(0,5,5),(9,8,7)]
>>> [(a, b) for (a, b) in itertools.product(tup_1, tup_2)
... if sum(1 for ai, bi in zip(a, b) if ai == bi) >= 2]
[((4, 5, 5), (4, 5, 6)), ((4, 5, 5), (0, 5, 5))]
Note: This checks whether two tuples have the same element in at least two positions, i.e. order matters. If order should not matter, you can convert a and b to set instead and check the size of their intersection, but that might fail for repeated numbers, i.e. the intersection of (1,1,2) and (1,1,3) would just be 1 instead of 2.
If you only want to match the first two, or first two and last two elements, you can compare slices of the tuples in an accordant disjunction:
>>> [(a, b) for (a, b) in itertools.product(tup_1, tup_2)
... if a[:2] == b[:2]]
[((4, 5, 5), (4, 5, 6))]
>>> [(a, b) for (a, b) in itertools.product(tup_1, tup_2)
... if a[:2] == b[:2] or a[-2:] == b[-2:]]
[((4, 5, 5), (4, 5, 6)), ((4, 5, 5), (0, 5, 5))]
This is one way using a list comprehension. The logic as written checks for an overlap of at least 2 elements.
Note that if there is no overlap you will be left with the one element of tup_2, but that can be trivially identified.
from itertools import chain
tup_1 = [(1,2,3),(4,5,5)]
tup_2 = [(4,5,6)]
y = sorted(tup_2[0])
res = [i for i in chain(tup_1, tup_2) if
sum(i==j for i, j in zip(sorted(i), y)) > 1]
print res
[(4, 5, 5), (4, 5, 6)]

How do i find the percentage of the most common element in a list?

I have been recently using Counter().most_common but the problem is that I need to turn the bit where it shows how much it came up into a percentage, for example:
[(2, 5), (10, 5)]
to:
[(2, 50%), (10, 50%)]
Is there any way of doing this using Counter().most_common, or any other method?
Here is part of my code:
while count < int(DR):
count = count + int(1)
DV.append(random.randint(1, int(DI)))
if count == int(DR):
print ('\n(The Number that was rolled , the amount of times it came up):')
global x
print (Counter(DV).most_common(int((DI))))
from collections import Counter
l = [1, 1, 2, 2, 2, 2, 2, 3, 4, 10, 10, 10, 10, 10]
c = Counter(l)
[(i, c[i] / len(l) * 100.0) for i in c]
Output, in the form (element, % of total)
[(1, 14.285714285714285),
(2, 35.714285714285715),
(3, 7.142857142857142),
(4, 7.142857142857142),
(10, 35.714285714285715)]
To list them in order you can use collections.Counter.most_common
>>> [(i, c[i] / len(l) * 100.0) for i, count in c.most_common()]
[(2, 35.714285714285715),
(10, 35.714285714285715),
(1, 14.285714285714285),
(3, 7.142857142857142),
(4, 7.142857142857142)]
If you don't have the original data, you can still accomplish this just with the Counter.
OrderedDict([(i, str(round(count / sum(c.values()) * 100.0, 3)) + '%') for i, count in c.most_common()])
Where:
i is the item that was counted;
count is the count of that item;
c is the Counter object
3 is the precision of the percentage
Performance can be improved if sum(c.values()) is moved outside of the list compression.

Categories