Related
I wish to input an n*m array and the resulting output be an array containing the different combinations of the row elements.
Here's an example to clarify (albeit an extremely simple case):
I wish to input an array of the following shape:
[[1, 2, 3]
[2, 5, 6]]
And wish to receive the following output:
[[1,2], [1,5], [1,6], [2,5], [2,6], [3,2], [3,5], [3,6]]
As you can see [2,2] is not included because of repetition.
I can write quick and dirty code containing nested for loops when the input dimensions are know a priori:
A = [[1, 2, 3], [2, 5, 6]]
m = len(A[0])
for i in range(0, m):
for j in range(0, m):
if A[0][i]!=A[1][j]: #check and eliminate repetition
combined.append([A[0][i],A[1][j])
choice_num.append([i+1, j+1]) #See (**) below
I would really like to know how to implement this as a recursive function so given some input n-D array, A, one can simply call it as:
recursive_looper(A)
(**) Another feature that I would like is for the function to output the column number corresponding to the element used in the combination so we get two outputs:
element values: [[1,2], [1,5], [1,6], [2,5], [2,6], [3,2], [3,5], [3,6]]
element position: [[1,1], [1,2], [1,3], [2,2], [2,3], [3,1], [3,2], [3,3]]
Any tips or suggestions would be greatly appreciated!!
Edit: I am open to any solution that can achieve the desired output. Recursion was simply the first thing that came to mind.
Edit 2 (Extended capabilities): This code must not be restricted to a specific list input shape but be extensible to any array of shape (n,m).
I'll provide an example for where the code breaks down. The work-around was implementing n-1 conditional statements, which I would like to avoid because the array shape must be known a priori.
A = [[2, 4, 1, 11, 3], [3, 2, 1, 4, 11], [2, 3, 4, 17, 13]]
If I do not make any modifications to your indexing/filter I receive the following output for the 'filtered' list:
#[[2, 3, 2], [2, 3, 3], [2, 3, 4], [2, 3, 17], [2, 3, 13], [2, 1, 2], ..., [3, 11, 13]]
Immediately I notice that it only compared element position 0 with position 1 for 'likeness', hence why the first combination contains two 2's.
I can make a modification to the Index grabber and filter loop which looks like so:
for i in range(0, len(projects_master)-2):
indexes = [idx for idx, t in enumerate(prod) if t[i] == t[i+1] or t[i]==t[i+2] or t[i+1] == t[i+2] ]
res = []
for i in range(0, len(A)-2):
res.append(list(filter( lambda v: v[i] != v[i+1] and v[i] != v[i+2] and v[i+1] != v[i+2], prod)))
result = [list(t) for t in res[0]]
This does give the correct output, but like I said, I needed to write out n-1 t[i] and v[i] conditions. How can this be done automatically?
EDIT 3 - FINAL
Thanks a bunch to those who provided different approaches to help me achieve the same end goal. I took some insight from each and wrote something that makes sense to me and seems to function well for any input. The code which filters duplicates and removes them from the combinations is shown below:
ind_remove = []
for i in range(0, len(prod)):
if len(prod[i]) != len(set(prod[i])):
ind_remove.append(i)
adder=0
for i in ind_remove:
del prod[i-adder]
adder=adder+1 #takes into account change in indices after an element is deleted.
You can use itertools.product to generate the required combinations, which works like a cartesion product between two sets to generate the combinations.
So it the lists have been [[1, 2], [3, 4]], the cartesian product within the sublists will be
[[1, 3], [1, 4], [2, 3], [2, 4]]
from itertools import product
a = [[1, 2, 3], [2, 5, 6]]
# Generate all possible products, *a gives you two lists
prod = list(product(*a))
#[(1, 2), (1, 5), (1, 6), (2, 2), (2, 5), (2, 6), (3, 2), (3, 5), (3, 6)]
#Get the list of duplicate indexes
indexes = [idx for idx, t in enumerate(prod) if t[0] == t[1] ]
print(indexes)
#[3]
#Remove tuples who are duplicates
res = list(filter( lambda v: v[0] != v[1], prod))
print(res)
#[(1, 2), (1, 5), (1, 6), (2, 5), (2, 6), (3, 2), (3, 5), (3, 6)]
#Convert the tuples to list
result = [list(t) for t in res]
print(result)
#[[1, 2], [1, 5], [1, 6], [2, 5], [2, 6], [3, 2], [3, 5], [3, 6]]
You can use a function that iterates over the items of the first list of the given list of lists and merge each item with the combinations from the recursive calls:
def nonrepetitive_product(lists):
if not lists:
yield []
return
first, *rest = lists
combinations = list(nonrepetitive_product(rest))
for item in first:
for combination in combinations:
if item not in combination:
yield [item, *combination]
so that given:
l = [[1, 2, 3], [2, 5, 6]]
list(nonrepetitive_product(l)) returns:
[[1, 2], [1, 5], [1, 6], [2, 5], [2, 6], [3, 2], [3, 5], [3, 6]]
If you want the positions and values for any number of rows, you'd be better off using itertools.product and enumerate together. Filtering is a little tricky, but it can be done:
import itertools
A = [[1, 2, 3], [2, 5, 6], [7, 8, 3]]
prod = itertools.product(*map(enumerate, A)) # yields ((i,x),(j,y),(k,z),...) nested tuples
transposed = ([*zip(*pairs)] for pairs in prod) # yields ((i,j,k,...), (x,y,z,...)) 2-tuples
filtered = [(ijk, xyz) for ijk, xyz in transposed if len(xyz) == len(set(xyz))] # filter dupes
indexes, values = zip(*filtered) # you might find `filtered` more useful than separate lists
I am a beginner in Python.
I learn list comprehension but my code is bad, because list comprehensions are wrong or missing:
I tried a lot of things, but results are generators or errors. Could you give me advice...?
Thank you.
Acer
import itertools as it
aT=list(it.permutations([4,3,3,0],3))
uT=list(set(aT))
# convert uniqueTimes list of tuples to list of lists
uT=[list(el) for el in uT]
uTt=[el[0]+el[1]+el[2] for el in uT] #It is wrong, I want universal list comprehension :-( when I change members of permutation, e.g.it.permutations([5,4,3,2,1],4))
uTs=[] #It is wrong too, I want universal list comprehension :-(
a=""
for m in range(len(uT)):
for n in range(len(uT[m])):
a+=str(uT[m][n])
uTs.append(a)
a=""
uTcombine=list(it.zip_longest(uTs,uTt)) #result as I expected, but the algorithm is "non comperhesion"
print(uT)
print(uTcombine)
from this (uT - list of lists, where each inner list is unique)
[[0, 3, 3], [3, 4, 3], [0, 3, 4], [3, 0, 3], [3, 4, 0], [3, 0, 4], [4, 0, 3], [4, 3, 3], [3, 3, 4], [3, 3, 0], [0, 4, 3], [4, 3, 0]]
I need that (uTcombine - list of tuples, where first in tuple is [0,3,3] => '033' and second is sum of list's items [0,3,3] => 6
[('033', 6), ('343', 10), ('034', 7), ('303', 6), ('340', 7), ('304', 7), ('403', 7), ('433', 10), ('334', 10), ('330', 6), ('043', 7), ('430', 7)]
You can use existing functions from the standard library instead of writing your own where possible:
uT = [(''.join(map(str, el)), sum(el)) for el in uT]
In this case:
''.join(map(str, el)) converts the permutation to the string representation you wanted (0, 3, 3, ) -> '033' by first coverting each item to string (using map(str, el)), and then joining the strings.
sum(el) summarized the permutation (0, 3, 3, ) -> 6
For example, if these are my two lists:
a=[1,2,3,4,5]
b=[6,7,8,9,10]
Then what I'm trying to do is to figure out a way to get:
c=[[1,6],[2,7],[3,8],[4,9],[5,10]]
Sorry for the probably basic question. These are numpy arrays if that makes a difference.
If you want a numpy array as a result, you can build it using array.T:
In [15]: a=np.array([1,2,3,4,5])
In [16]: b=np.array([6,7,8,9,10])
In [17]: np.array([a,b]).T
Out[17]:
array([[ 1, 6],
[ 2, 7],
[ 3, 8],
[ 4, 9],
[ 5, 10]])
Reference: What is the equivalent of "zip()" in Python's numpy?
One approach is using list comprehension and zip:
>>> [[i, j] for i, j in zip(a,b)]
[[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]]
I don't use numpy, but maybe by using zip:
>>> a=[1,2,3,4,5]
>>> b=[6,7,8,9,10]
>>> list(zip(a,b))
[(1, 6), (2, 7), (3, 8), (4, 9), (5, 10)]
It returns a list of tuples though.
Just use np.transpose:
>>> np.transpose([a, b])
array([[ 1, 6],
[ 2, 7],
[ 3, 8],
[ 4, 9],
[ 5, 10]])
If you want the result as list just call tolist() afterwards:
>>> np.transpose([a, b]).tolist()
[[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]]
d = []
for i in range(0, 5):
d.append([a[i], b[i])
Is a simple way to create a new 2D list with element pairs. Using the zip() function as others have pointed out is also viable.
I'm pretty sure there is an easier or more pythonic way than this.
c = [list(x) for x in zip(a,b)]
This outputs a list of lists, instead of just doing list(zip(a,b) that outputs a list of tuples. This combines list comprehension and zip
Also avoids the tuple unpacking of [[i,j] for i,j in zip(a,b)]
not sure whats more efficient though
zip(*iterables) Make an iterator that aggregates elements from each of the iterables.
https://docs.python.org/3/library/functions.html#zip
There are multiple ways to do this.
a = [1, 2, 3, 4, 5]
b = [6, 7, 8, 9, 10]
If you just need to access the elements and not modify them, you can use the zip function:
zip(a, b)
>[(1, 6), (2, 7), (3, 8), (4, 9), (5, 10)]
If you actually need a list of lists, then you can use a list comprehension:
[[a[i], b[i]] for i in range(len(a))]
>[[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]]
And finally, if you need a numpy array as a result, use the Transpose function:
import numpy as np
np.concatenate([[a], [b]]).T
>array([[1, 6],
[2, 7],
[3, 8],
[4, 9],
[5, 10]])
This will give you what you want.
a = [1,2,3,4,5]
b = [6,7,8,9,10]
c = [list(x) for x in zip(a, b)]
with no libraries, you could use:
a = [1,2,3,4,5]
b = [6,7,8,9,10]
c = [[a[i],b[i]] for i in range(len(a))]
I used this way to make all combinations:
import itertools
lst = [[1, 2, 3], [1,2,2,4]]
combs = []
for i in xrange(1, len(lst)+1):
combs.append(i)
els = [list(x) for x in itertools.combinations(lst, i)]
combs.append(els)
But what I want is that each list includes every possible combinations of elements inside. With the solution above, each element pairs are scattered. How can I solve it?
Are you looking for something like this?
import itertools
lst = [1, 2, 3], [1, 2, 2, 4]
combs = []
for i in range(len(lst)):
els = [list(x) for x in itertools.combinations(lst[i], 2)]
combs.append(els)
print(combs)
Output
[[[1, 2], [1, 3], [2, 3]], [[1, 2], [1, 2], [1, 4], [2, 2], [2, 4], [2, 4]]]
Since you said, you want - each element in its own list make pairs and put back in that list, so i am assuming that [1, 2, 3] should be converted to [[1, 2], [1, 3], [2, 3]]. The above example does the same thing!
Update
If you want to generate all possible combinations (of length greater than 1) from those lists, then you can do something like this.
import itertools
lst = [1, 2, 3], [1, 2, 2, 4]
combs = []
for i in range(len(lst)):
temp_list = []
for j in range(len(lst[i])+1):
if j < 2: # skipping zero and one length combinations
continue
els = [x for x in itertools.combinations(lst[i], j)]
temp_list.extend(els)
# remove duplicate combinations
new_list = []
[new_list.append(i) for i in temp_list if not new_list.count(i)]
combs.append(new_list)
print(combs)
Output
[[(1, 2), (1, 3), (2, 3), (1, 2, 3)], [(1, 2), (1, 4), (2, 2), (2, 4),
(1, 2, 2), (1, 2, 4), (2, 2, 4), (1, 2, 2, 4)]]
I have a list whose nested list's size may vary with the multiple of 2. Currently, in this example, the nested list's length is 4.
a_list = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
According to length, I am trying to break the list to get the following result in the best possible pythonic way:
a = [[1,2], [5,6], [9,10]]
b = [[3,4], [7,8], [11,12]]
and if nested list's length is 6, then
c = [[..], [..], [..]]
Its kind of a transpose of a nested list but with sets of 2 values in a single row not to be transposed.
Using list comprehension:
>>> a_list = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
>>> a = [x[:2] for x in a_list]
>>> b = [x[2:] for x in a_list]
>>> a
[[1, 2], [5, 6], [9, 10]]
>>> b
[[3, 4], [7, 8], [11, 12]]
More general solution:
>>> [[x[i:i+2] for x in a_list] for i in range(0, len(a_list[0]), 2)]
[[[1, 2], [5, 6], [9, 10]],
[[3, 4], [7, 8], [11, 12]]]
I'd hesitate to call this "pythonic", since it's pretty much illegible, but:
>>> a, b = zip(*(zip(*[iter(s)]*2) for s in a_list))
>>> a
((1, 2), (5, 6), (9, 10))
>>> b
((3, 4), (7, 8), (11, 12))
Also works for 6-item lists:
>>> a_list = [[1,2,3,4,100,102],[5,6,7,8,103,104],[9,10,11,12,105,106]]
>>> a, b, c = zip(*(zip(*[iter(s)]*2) for s in a_list))
>>> a
((1, 2), (5, 6), (9, 10))
>>> b
((3, 4), (7, 8), (11, 12))
>>> c
((100, 102), (103, 104), (105, 106))
Almost the same as falsetru's answer, but first the nested lists are split into chunks of size 2 and then all of them are zipped together.
>>> a_list = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
>>> zip(*([j[i*2: i*2 + 2] for i in range(len(j) / 2)] for j in a_list))
[([1, 2], [5, 6], [9, 10]), ([3, 4], [7, 8], [11, 12])]
>>> a_list = [[1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]]
>>> zip(*([j[i*2: i*2 + 2] for i in range(len(j) / 2)] for j in a_list))
[([1, 2], [7, 8]), ([3, 4], [9, 10]), ([5, 6], [11, 12])]
>>> a_list = [[1,2,3,4,100,102],[5,6,7,8,103,104],[9,10,11,12,105,106]]
>>> zip(*([j[i*2: i*2 + 2] for i in range(len(j) / 2)] for j in a_list))
[([1, 2], [5, 6], [9, 10]), ([3, 4], [7, 8], [11, 12]), ([100, 102], [103, 104], [105, 106])]
A Fast way is using numpy.hsplit :
>>> import numpy
>>> numpy.hsplit(numpy.array(a_list),2)
[array([[ 1, 2],[ 5, 6],[ 9, 10]]),array([[ 3, 4],[ 7, 8],[11, 12]])]
Since readability is pythonic, here's a simpler iterator-based solution (without the neat tricks that #Zero used to turn it into a one-liner):
First, an iterator that turns a list [1,2,3,4,5,6] into [(1, 2), (3, 4), (5, 6)].
def pairs(lst):
it=iter(lst)
return list(zip(it, it)) # Return a list of pairs drawn from the same iterator
The list a_list can be transformed into a list of such pair lists as follows:
a_list = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
pair_list = [ pairs(row) for row in a_list ]
Finally, we need to effectively transpose this list, making a list of the first pair/element from each sublist, another list of the second one, etc. A nice idiom for transposing a list is zip(*some_list). Let's use it to make the transformation requested by the OP:
a, b = zip(*pair_list)
or to collect any number of generated lists in one list:
results = list( zip(*pair_list) )
Feel free to pack these into a one-liner (though I wouldn't):
results = list(zip( *(pairs(row) for row in a_list) ))