I have an array with numbers like this, in this case 5 numbers:
a = array([2.88581812, 2.8930633 , 2.85603976, 2.86739916, 2.85736707])
I would like to create an array of 10 elements in which the pairwise difference of all the numbers of the array is present.
For now i used nested loop for like this:
diffmean = []
for i in range(len(a)-1):
for j in range(i+1, len(a)):
diffmean.append(a[i]-a[j])
Obtaining a list with 10 elements of pairwise difference
[-0.007245185215707384,
0.029778354907735505,
0.018418952746142025,
0.0284510446999775,
0.03702354012344289,
0.02566413796184941,
0.035696229915684885,
-0.01135940216159348,
-0.0013273102077580035,
0.010032091953835476]
there is a "pythonic" way to perform this? without loopfor or nested loop for?
You can use combinations in build-in itertools library. Like:
from itertools import combinations
a = [2.88581812, 2.8930633 , 2.85603976, 2.86739916, 2.85736707]
diffmean = []
for b,c in combinations(a, 2):
diffmean.append(b - c)
The second argument of the function is the number of elements you want to combine. The function is order-free and the order-based version is permutations which returns 20 values in this case.
Assuming a is is numpy array. The below should work, but perhaps a more efficient solution exists as this calculates differences twices
np.expand_dims(a, 1) - a
d[np.tril_indices(a.size, k=-1)]
Related
I would like to have all possible combinations of lists of a given size.
Lets say for example, I have a given list K, such as K = [3, 5, 2]. With the following code I get what I desire. But how do I generalize this for any list of a given size, without just adding for loops?
assignment= []
for l in range(K[0]):
for m in range(K[1]):
for n in range(K[2]):
a = [l,m,n]
assignment.append(a)
The itertools library can often help with things like this. itertools.product() in particular, using * to pass it a list or iterator of ranges:
list(itertools.product(*(range(n) for n in K))
or
list(itertools.product(*map(range, K)))
I need to do this: I have list of integers, i make all possible combinations containing 3 of these numbers, and output is list containing sums of these combinations. (not sum of all combinations together, but for each combination one sum)
My algorithm do it in this way: make list of all possible combinations, then count sum of each combination and save it into list, what is too uneffective if number of integers is large. Here is little sample in python:
import itertools
array=[1,2,3,4]
combs=[]
els = [list(x) for x in itertools.combinations(array, 3)]
combs.extend(els)
result=list(map(sum, combs))
Could you please come up with some more effective solution? Maybe if there is some way of making it a loop where there isn't made a list of combinations at first and counted sum of each combination at second but rather it straight counts sum of just created combination, saves it into list and then continues to another one until all combinations and sums are done.
sum() works with any iterable, not just lists. You could make the code more efficient by applying it directly to the combinations' tuples:
result = [sum(c) for c in itertools.combinations(array, 3)]
If you want to process the result lazily, you can use a generator expression as well:
result = (sum(c) for c in itertools.combinations(array, 3))
Say I have 3 integer arrays: {1,2,3}, {2,3}, {1}
I must take exactly one element from each array, to form a new array where all numbers are unique. In this example, the correct answers are: {2,3,1} and {3,2,1}. (Since I must take one element from the 3rd array, and I want all numbers to be unique, I must never take the number 1 from the first array.)
What I have done:
for a in array1:
for b in array2:
for c in array3:
if a != b and a != c and b != c:
AddAnswer(a,b,c)
This is brute force, which works, but it doesn't scale well. What if now we are dealing with 20 arrays instead of just 3. I don't think it's good to write a 20 nested for-loops. Is there a clever way to do this?
What about:
import itertools
arrs = [[1,2,3], [2,3], [1]]
for x in itertools.product(*arrs):
if len(set(x)) < len(arrs): continue
AddAnswer(x)
AddAnswer(x) is called twice, with the tuples:
(2, 3, 1)
(3, 2, 1)
You can think of this as finding a matching in a bipartite graph.
You are trying to select one element from each set, but are not allowed to select the same element twice, so you are trying to match sets to numbers.
You can use the matching function in the graph library NetworkX to do this efficiently.
Python example code:
import networkx as nx
A=[ [1,2,3], [2,3], [1] ]
numbers = set()
for s in A:
for n in s:
numbers.add(n)
B = nx.Graph()
for n in numbers:
B.add_node('%d'%n,bipartite=1)
for i,s in enumerate(A):
set_name = 's%d'%i
B.add_node(set_name,bipartite=0)
for n in s:
B.add_edge(set_name,n)
matching = nx.maximal_matching(B)
if len(matching) != len(A):
print 'No complete matching'
else:
for number,set_name in matching:
print 'choose',number,'from',set_name
This is a simple, efficient method for finding a single matching.
If you want to enumerate through all matchings you may wish to read:
Algorithms for Enumerating All Perfect, Maximum and
Maximal Matchings in Bipartite Graphs by Takeaki UNO which gives O(V) complexity per matching.
A recursive solution (not tested):
def max_sets(list_of_sets, excluded=[]):
if not list_of_sets:
return [set()]
else:
res = []
for x in list_of_sets[0]:
if x not in excluded:
for candidate in max_sets(list_of_sets[1:], exclude+[x]):
candidate.add(x)
res.append(candidate)
return res
(You could probably dispense with the set but it's not clear if it was in the question or not...)
Using Python 3.3, I have a list as such:
>>> ls
[250313, 242517, 243788, 1606025, 1566365, 1573483]
I need a for loop or built-in function to iterate over this, summing three integers together at a time.
I would like the end result to look like this:
>>> newls
[736618, 4745873]
It seems like itertools or sum would be helpful for this, but I can't seem to figure it out yet.
Take the sum of each consecutive three-element sublist of ls as so:
newls = [sum(ls[i:i+3]) for i in range(0, len(ls), 3)]
Note that, if len(ls) is not a multiple of three, the last one or two elements of ls will be summed together to make the last element of newls. To ignore any trailing elements that aren't part of a group of three, do:
newls = [sum(ls[i:i+3]) for i in range(0, len(ls) - len(ls) % 3, 3)]
Reshape your array into a matrix of dimensionality len(ls)/3 by 3, and then sum elements in each row.
import numpy as np
result = np.array(ls).reshape((-1,3)).sum(axis = 1)
I have used the following script to generate all combinations:
import itertools
x = 7
my_list = range(1, 11)
for k in [x]:
for sublist in itertools.combinations(my_list, k):
print sublist
For the second part I will take 6 random elements from range(1, 11). Let's call them my_second_list.
I need to generate the minimum number of combinations of my_list in order to obtain at least one combination to include let's say 5 elements from my_second_list.
Any ideas on how to do that?
import itertools
x = 7
my_list = range(1,11)
for k in [x]: # isn't this just 7?
your_output = (combination for combination in itertools.combinations(my_list,k) if all(element in combination for element in [1,2,3,4,5]))
It's ugly as heck, but that's how I'd do it (if I'm understanding your question correctly. You're trying to get only those combinations that contain a certain subset of items, right? If you want all combinations BEFORE and INCLUDING that first combination that contains the subset of items, I'd do:
accumulator = list()
subset = [1,2,3,4,5] # or whatever
for k in [x]:
for combination in itertools.combinations(my_list,k):
accumulator.append(combination)
if all(el in combination for el in subset):
break
Depending on your exact use case you may want to consider defining subset as a set (e.g. {1,2,3,4,5}) and do subset.issubset(set(combination)) but it's hard to tell if that's better or not without doing some profiling.