I'm trying to figure out how to make a function that takes two lists, then returns a list of all the possible combinations of those lists made by choosing one element from one of the lists per index. I don't think I'm describing it well, but what I'm looking for is:
input:
['a','b'], ['c','d']
output:
['ab', 'ad', 'cb', 'cd']
I've made a function that does this semi-successfully here:
def mix_list(lst1, lst2):
res = []
k = max(len(lst1), len(lst2))
ref = itertools.product(range(2), repeat=k)
for comb in list(ref):
temp = [None] * k
for i, e in enumerate(comb):
if e == 0:
try:
temp[i] = lst1[i]
except IndexError:
temp[i] = lst2[i]
elif e == 1:
try:
temp[i] = lst2[i]
except IndexError:
temp[i] = lst1[i]
res.append(temp)
return [''.join(i) for i in set(map(tuple, res))]
My first thought was that itertools would have some function that would accomplish this, but I couldn't find anything. Besides that, I did some googling and searching on here, but I haven't been able to find something that does what I'm looking for a bit faster or a bit more simply.
Is there a better way to accomplish this, or maybe a library that has a function that already does this?
You can zip() the two lists together to get a list of pairs of the values at corresponding indices, and then use product() to get all the combinations of picking one element at each index:
>>> lst1, lst2 = ['a','b'], ['c','d']
>>> list(product(*zip(lst1, lst2)))
[('a', 'b'), ('a', 'd'), ('c', 'b'), ('c', 'd')]
This can easily be extended to an arbitrary number and length of lists, but the size of the result will grow exponentially.
You have to convert
['a','b'], ['c','d']
to
['a','c'], ['b','d']
(first list has values for index 1, second list has values for index 2)
and then use itertools.product()
import itertools
a, b = zip(['a','b'], ['c','d'])
#print(a, b) # ['a','c'], ['b','d']
data = itertools.product(a, b)
data = ["".join(item) for item in data]
print(data)
Result
['ab', 'ad', 'cb', 'cd']
Related
I want to create a function that take a lsit as argument, for example:
list = ['a','b','a','d','e','f','a','b','g','b']
and returns a specific number of list elements ( i chose the number) such that no number occurs twice. For example if i chose 3:
new_list = ['a','b','d']
I tried the following:
def func(j, list):
new_list=[]
for i in list:
while(len(new_list)<j):
for k in new_list:
if i != k:
new_list.append(i)
return new_list
But the function went through infinite loop.
def func(j, mylist):
# dedup, preserving order (dict is insertion-ordered as a language guarantee as of 3.7):
deduped = list(dict.fromkeys(mylist))
# Slice off all but the part you care about:
return deduped[:j]
If performance for large inputs is a concern, that's suboptimal (it processes the whole input even if j unique elements are found in first j indices out of an input where j is much smaller than the input), so the more complicated solution can be used for maximum efficiency. First, copy the itertools unique_everseen recipe:
from itertools import filterfalse, islice # At top of file, filterfalse for recipe, islice for your function
def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in filterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element
now wrap it with islice to only pull off as many elements as required and exiting immediately once you have them (without processing the rest of the input at all):
def func(j, mylist): # Note: Renamed list argument to mylist to avoid shadowing built-in
return list(islice(unique_everseen(mylist), j))
Try this.
lst = ['a','b','a','d','e','f','a','b','g','b']
j = 3
def func(j,list_):
new_lst = []
for a in list_:
if a not in new_lst:
new_lst.append(a)
return new_lst[:j]
print(func(j,lst)) # ['a', 'b', 'd']
I don't know why someone does not post a numpy.unique solution
Here is memory efficient way(I think 😉).
import numpy as np
lst = ['a','b','a','d','e','f','a','b','g','b']
def func(j,list_):
return np.unique(list_).tolist()[:j]
print(func(3,lst)) # ['a', 'b', 'd']
list is a reserved word in python.
If order of the elements is not a concern then
def func(j, user_list):
return list(set(user_list))[:j]
it's bad practice to use "list" as variable name
you can solve the problem by just using the Counter lib in python
from collections import Counter
a=['a','b','a','d','e','f','a','b','g','b']
b = list(Counter(a))
print(b[:3])
so your function will be something like that
def unique_slice(list_in, elements):
new_list = list(Counter(list_in))
print("New list: {}".format(new_list))
if int(elements) <= len(new_list):
return new_list[:elements]
return new_list
hope it solves your question
As others have said you should not Shadow built-in name 'list'. Because that could lead to many issues. This is a simple problem where you should add to a new list and check if the element was already added.
The [:] operator in python lets you separate the list along an index.
>>>l = [1, 2, 3, 4]
>>>l[:1]
[1]
>>>l[1:]
[2, 3, 4]
lst = ['a', 'b', 'a', 'd', 'e', 'f', 'a', 'b', 'g', 'b']
def func(number, _list):
out = []
for a in _list:
if a not in out:
out.append(a)
return out[:number]
print(func(4, lst)) # ['a', 'b', 'd', 'e']
Let's say I have a list of n-tuples in Python, like so (using three-tuples in the example, but would want this to work for any tuple size):
myList = [('a','b','c'),
('a','a','a'),
('b','b','b'),
('d','e','f')
]
I want to remove any n-tuple where each element of the n-tuple is the same. In the example above, I would want to remove the tuples ('a','a','a') and ('b','b','b') because each of the elements in those tuples is the same.
I wrote a nested for loop that does this, but it seems really inefficient / not very Pythonic to do it this way. Any ideas on how to do this more simply and efficiently?
def tuple_removal(aList):
elements = len(aList) # number of elements in the list
tuple_size = len(aList[0]) # size of the tuple
for i in reversed(range(elements)):
same_element_count = 1 # initialize counter to 1
for j in range(tuple_size-1):
# add one to counter if the jth element is equal to the j+1 element
same_element_count += aList[i][j] == aList[i][j+1]
if same_element_count == tuple_size:
# remove the tuple at the ith index if the count of elements that are the same
# is equal to the size of the tuple
del aList[i]
return(aList)
myNewList = tuple_removal(myList)
myNewList
# Output
myNewList = [('a','b','c'),
('d','e','f')
]
You can simply use a list comprehension and check that the count of the first element in each matching tuple is not the same as the length of the tuple:
>>> r = [i for i in myList if i.count(i[0]) != len(i)]
>>> r
[('a', 'b', 'c'), ('d', 'e', 'f')]
You can use a list comprehension and test to see if all elements in a given tuple are equal using the builtin all() function.
>>> myList = [('a','b','c'),
('a','a','a'),
('b','b','b'),
('d','e','f')
]
>>>
>>> [el for el in myList if not all(x == el[0] for x in el)]
[('a', 'b', 'c'), ('d', 'e', 'f')]
>>>
Convert each tuple to a set; if the result has length 1, all elements were the same. Use this in a list comprehension as a filter, keeping all tuples which have more than one unique element:
def tuple_removal(lst):
return [t for t in lst if len(set(t)) > 1]
Demo:
>>> myList = [('a','b','c'),
... ('a','a','a'),
... ('b','b','b'),
... ('d','e','f')
... ]
>>> tuple_removal(myList)
[('a', 'b', 'c'), ('d', 'e', 'f')]
I'm trying to find all combinations of A,B repeated 3 times.
Once I've done this I would like to count how many A's there are in a row, by splitting the string and returning the len.max value. However this is going crazy on me. I must have misunderstood the len(max(tmp.split="A")
Can anyone explain what this really does (len returns the length of the string, and max returns the highest integer of that string, based on my split?) I expect it to return the number of A's in a row. "A,B,A" should return 1 even though there are two A's.
Suggestions and clarifications would be sincerely welcome
import itertools
list = list(itertools.product(["A", "B"], repeat=3))
count = 0;
for i in list:
count += 1;
tmp = str(i);
var = len(max(tmp.split("B")))
print(count, i, var)
You can use itertools.groupby to find groups of identical elements in an iterable. groupby generates a sequence of (key, group) tuples, where key is the value of the elements in the group, and group is an iterator of that group (which shares the underlying iterable with groupby. To get the length of the group we need to convert it to a list.
from itertools import product, groupby
for t in product("AB", repeat=3):
a = max([len(list(g)) for k, g in groupby(t) if k == "A"] or [0])
print(t, a)
output
('A', 'A', 'A') 3
('A', 'A', 'B') 2
('A', 'B', 'A') 1
('A', 'B', 'B') 1
('B', 'A', 'A') 2
('B', 'A', 'B') 1
('B', 'B', 'A') 1
('B', 'B', 'B') 0
We need to append or [0] to the list comprehension to cover the situation where no "A"s are found, otherwise max complains that we're trying to find the maximum of an empty sequence.
Update
Padraic Cunningham reminded me that the Python 3 version of max accepts a default arg to handle the situation when you pass it an empty iterable. He also shows another way to calculate the length of an iterable that is a bit nicer since it avoids capturing the iterable into a list, so it's a bit faster and consumes less RAM, which can be handy when working with large iterables. So we can rewrite the above code as
from itertools import product, groupby
for t in product("AB", repeat=3):
a = max((sum(1 for _ in g) for k, g in groupby(t) if k == "A"), default=0)
print(t, a)
I have two lists A and B of the same size. The first list is made of tuples. I reorder this first list A using the lambda function (so that I order according to the first element of the tuple). Is there a way to reorder the second list according to the same changes? That is, if the third element of list A is moved to place 7, then so does the third element in list B.
a = [(1,'a'), (3,'b'), (2,'c')]
b = [4,6,5]
sort_function = lambda x: x[0][0]
sort_target = list(zip(a,b))
sort_target.sort(key = sort_function)
resorted_a, resorted_b = zip(*sort_target)
print (resorted_a) # prints ((1, 'a'), (2, 'c'), (3, 'b'))
print (resorted_b) # prints (4, 5, 6)
You'll have to slightly modify you current sorting lambda function. In short, it should treat it's argument as tuple that have two items: item from first list and item from second list. So, to preserve sorting order you should first extract first element from tuple, than apply your current sorting lambda.
resorted_a and resorted_b come out as tuples, but I hope that's not a problem (otherwise explicitly turn them to list, e.g. list_a = list(resorted_a))
Refer to python documentation on zip for details. Note it's stated in official documentation:
zip() in conjunction with the * operator can be used to unzip a list:
Your code right now looks probably like this:
A.sort(key=lambda x: x[0])
Following #J0HN s suggestion:
A,B = zip(*sorted(zip(x,y),key=lambda x:x[0][0]))
will sort both lists according to the first element of the tupels in A.
Following the zip, sort, unpack idea:
lstA = ["a", "b", "z", "d"]
lstB = ["1a", "2b", "3z", "4d"]
print "lstA", lstA
print "lstB", lstB
# willing to have lstA sorted alphabetically and order of items in lstB follow the changes
sumlst = zip(lstA, lstB)
sumlst.sort(key=lambda itm: itm[0])
print "sumlst", sumlst
#unpack it to two separate lists
lstA = [a for a, b in sumlst]
lstB = [b for a, b in sumlst]
print "lstA", lstA
print "lstB", lstB
with output:
lstA ['a', 'b', 'z', 'd']
lstB ['1a', '2b', '3z', '4d']
sumlst [('a', '1a'), ('b', '2b'), ('d', '4d'), ('z', '3z')]
lstA ['a', 'b', 'd', 'z']
lstB ['1a', '2b', '4d', '3z']
Hey guys I need help on this past test question. Basically I am given two list of objects, and I am suppose to find the number of items that appear in the same position of the first list and the second. I have an example that was provided.
>>> commons(['a', 'b', 'c', 'd'], ['a', 'x', 'b', 'd'])
2
>>> commons(['a', 'b', 'c', 'd', 'e'], ['a', 'x', 'b', 'd'])
2
I am having trouble writing out the code. Our class is using python 3. I have no idea where to start writing this from. It is a first year programming course and I never did programming in my life.
I think a more straightforward solution would be:
def commons(L1,L2):
return len([x for x in zip(L1,L2) if x[0]==x[1]])
This is not a simple problem for a beginner. A more straightforward approach would use functions like sum and zip with a list comprehension like so:
def commons(L1, L2):
return sum(el1 == el2 * 1 for el1, el2 in zip(L1, L2))
A more typical but error prone approach taken by beginners is:
def commons(L1, L2):
count = 0
for i, elem in enumerate(L2):
if elem == L1[i]:
count += 1
return count
I say this is more error prone because there are more parts to get right.
Without using enumerate you could do:
def commons(L1, L2):
count = 0
for i, range(len(L2)):
if L1[i] == L2[i]:
count += 1
return count
but these previous two will work only if len(L2) <= len(L1). See what I mean by more error prone? To fix this you would need to do:
def commons(L1, L2):
count = 0
for i, range(min(len(L2), len(L1))):
if L1[i] == L2[i]:
count += 1
return count
Seems like this would work:
def commons(l1, l2):
return sum(1 for v1,v2 in map(None, l1,l2) if v1 == v2)
Note: The degenerate form of map used here results in None being returned for all values in the shorter list (so even if l1 and l2 are not the same length, it'll work.) It assumes that both lists all have values (i.e., L1 and L2 do not contain None - since that would end up in false positives if one list was shorter than the other.)