How to remove members of a list in python - python

I have the following code to remove from the data list all sublists for which nums is a subset.and I dont understand why its not working:
data=[[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]
nums=[1,2]
for each in data:
if set(nums).issubset(each):
data.remove(each)
print(data)
>>[[1, 2, 4], [1, 3, 4], [2, 3, 4]]
Why isn't [1,2,4] being removed when nums is a subset of it, as seen below?
set(nums).issubset([1,2,4])
>>True

You're modifing the list you're iterating from.
This is a nicer solution:
data=[[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]
nums=[1,2]
data = [each for each in data if not set(nums).issubset(each)]
print(data)
For learning purposes, see this code which also works. The difference with your code is that here we're not modifying data list in the for loop.
data=[[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]
nums=[1,2]
new_data = []
for each in data:
if not set(nums).issubset(each):
new_data.append(each)
data = new_data
print(data)

Because the iterator is unaware that you removed an element. When it passes to the second element, it finds [1, 3, 4] meaning that you skipped [1, 2, 4].

For your information, there is also a filterfalse function in the very useful itertools module.
from itertools import filterfalse
data = list(filterfalse(set(nums).issubset, data))

Related

Mapping a list right in Python

I have a question on how to map correctly my list.
I have the following code:
class Bar():
def __init__(self, i, j):
self.i = i-1
self.j = j-1
For the following list:
bars = [Bar(1,2), Bar(2,3), Bar(3,4), Bar(4,5), Bar(5,1),Bar(1,4), Bar(2,4), Bar(4,6), Bar(6,5)]
But for my problem, I have an array like this:
elementsmat=[[1, 1, 2], [2, 2, 3], [3, 3, 4], [4, 4, 5], [5, 5, 1], [6, 1, 4], [7, 2, 4], [8, 4, 6], [9, 6, 5]]
I used the following code to obtain an array where I removed the first element of each list of the list and then transformed it into a list.
s= np.delete(elementsmat, 0, 1)
r = s.tolist()
Output: [[1, 2], [2, 3], [3, 4], [4, 5], [5, 1], [1, 4], [2, 4], [4, 6], [6, 5]]
So, how can I apply the Bar function to all the elements of my new array correctly? I did this but I got the following error.
bars = map(Bar,r)
__init__() missing 1 required positional argument: 'j'
I thought it could be because in the first one the list has () and in my list I have [], but I am not sure.
You can use itertools.starmap instead of map (after importing itertools). Your current way calls Bar([1, 2]). starmap unpacks the lists into arguments. A generator/list comprehension is also an option.
(Bar(*x) for x in r)
Now you see why it's called starmap.
You need to unpack the nested lists into the call to Bar():
l = list(map(lambda x: Bar(*x), r))
itertools.starmap does the same thing.
Or, you can use a list-comprehension:
l = [Bar(i, j) for i, j in r]
A built-in functional approach
lst = [[1, 2], [2, 3], [3, 4], [4, 5], [5, 1], [1, 4], [2, 4], [4, 6], [6, 5]]
map(Bar, *zip(*lst))

Trying to write a combinations function in Python on my own, not working

I'm trying to solve this problem here: https://codingbat.com/prob/p252079?parent=/home/peter#norvig.com
In math, a "combination" of a set of things is a subset of the things. We define the function combinations(things, k) to be a list of all the subsets of exactly k elements of things. Conceptually, that's all there is, but there are some questions to settle: (A) how do we represent a subset? (B) What order are the elements within each subset? (C) What order to we list the subsets? Here's what we will agree to: (A) a subset will be a list. (B) The order of elements within a list will be the same as the order within 'things'. So, for example, for combinations([1, 2, 3], 2) one of the subsets will be [1, 2]; whereas [2, 1] is not a subset. (C) The order of subsets will be lexicographical or sorted order -- that is, combinations([1, 2, 3], 2) returns [ [1, 2], [1, 3], 2, 3] ] because [1, 2] < [1, 3] < [2, 3]. You might want to use the function 'sorted' to make sure the results you return are properly ordered.
combinations([1, 2, 3, 4, 5], 2) → [[1, 2], [1, 3], [1, 4], [1, 5], [2, 3], [2, 4], [2, 5], [3, 4], [3, 5], [4, 5]]
combinations([1, 2, 3], 2) → [[1, 2], [1, 3], [2, 3]]
combinations([1, 2, 3, 4, 5, 6], 5) → [[1, 2, 3, 4, 5], [1, 2, 3, 4, 6], [1, 2, 3, 5, 6], [1, 2, 4, 5, 6], [1, 3, 4, 5, 6], [2, 3, 4, 5, 6]]
Here's my code:
def combinations(things, k):
if k == 0 or k == len(things):
return [things]
elif len(things) < k:
return
else:
finalcomb = []
subcomb1 = combinations(things[1:], k - 1)
subcomb2 = combinations(things[1:], k)
for i in range(len(combinations(things[1:], k - 1))):
firstelement = [things[0]]
firstelement += combinations(things[1:], k - 1)[i]
finalcomb.append(firstelement)
for j in range(len(combinations(things[1:], k))):
finalcomb.append(combinations(things[1:], k)[j])
return finalcomb
However, this is the output:
Haven't hit 10 reputation yet so it's a link to the error. I'm not sure what I did wrong, can anybody help me out? Thank you so much.
The problem is this. When k == 0 it shouldn't return [things]. It should return an empty array. Similar to when len(things) < k:. This is because, when k == 0, it means we that we have already found all the numbers for that specific combination.
But there's one more problem. We're returning an empty array. However, in the for loops, we're iterating over the returned array. So if the array is empty, nothing happens. So what we should really return is an empty 2D array. I won't go into too much detail about what the problem is since it's better for you to try and understand why it's not working. Try adding print statements inside and outside the for loops.
Anyway, the working code looks like this:
def combinations(things, k):
if k == len(things):
return [things[:]]
if len(things) < k or k == 0:
return [[]]
finalcomb = []
subcomb1 = combinations(things[1:], k - 1)
subcomb2 = combinations(things[1:], k)
for comb in subcomb1:
firstelement = [things[0]]
firstelement += comb
finalcomb.append(firstelement)
finalcomb += subcomb2
return finalcomb
Note a few things:
Use the variables you've already assigned (I'm assuming you forgot about them)
Lists can be concatenated using +, similar to strings. If you return within an if statement, you don't need an else for the next line since if the if statement is satisfied, it would definitely not go to the else.
You simply can try using itertools:
import itertools
output = []
for nums in itertools.combinations([1, 2, 3, 4, 5], 2):
output.append(list(nums))
print(output)
output:
[[1, 2], [1, 3], [1, 4], [1, 5], [2, 3], [2, 4], [2, 5], [3, 4], [3, 5], [4, 5]]
For 3 nums:
import itertools
output = []
for nums in itertools.combinations([1, 2, 3, 4, 5], 3):
output.append(list(nums))
print(output)
Output:
[[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5], [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5]]

Data structure to represent multiple equivalent keys in set in Python?

Currently, I want to find the correct data structure to meet the following requirement.
There are multiple arrays with disordered element, for example,
[1, 2], [2, 1], [3, 2, 2], [2], [2, 1, 3], [2, 2, 3]
After processing those data, the result is,
[1, 2], [2, 2, 3], [2], [1, 2, 3]
With sorted element in each array and filter the duplicate arrays.
Here are my thoughts:
Data structure Set(Arrays)? - Failed. It seems there is only one array in the build-in set
set([])
Data structure Array(Sets)? - Failed. However, there is no duplicate element in the build-in set. I want to know whether there is one data structure like multiset in C++ within Python?
Transform your list to tuple(thus can be a item of set), then back to list.
>>> [list(i) for i in set([tuple(sorted(i)) for i in a])]
[[1, 2], [2], [2, 2, 3], [1, 2, 3]]
lst = [[1, 2], [2, 1], [3, 2, 2], [2], [2, 1, 3], [2, 2, 3]]
map(list, set(map(tuple, map(sorted, lst)))
Output:
[[1, 2], [2], [2, 2, 3], [1, 2, 3]]
Try this:
[list(i) for i in set(map(tuple, a))]
EDIT:
Assuming that list is already sorted. Thanks to #PM2RING to remind me.
If not, then add this line above
a = [sorted(i) for i in a]
Thanks again to #PM2RING: one liner
[list(i) for i in set(map(tuple, (sorted(i) for i in a)))]
Demo
Some of the solutions currently here are destroying ordering. I'm not sure if that's important to you or not, but here is a version which preserves original ordering:
>>> from collections import OrderedDict
>>> A = [[1, 2], [2, 1], [3, 2, 2], [2], [2, 1, 3], [2, 2, 3]]
>>> [list(k) for k in OrderedDict.fromkeys(tuple(sorted(a)) for a in A)]
[[1, 2], [2, 2, 3], [2], [1, 2, 3]]
No Python, doesn't have a built-in multiset; the closest equivalent in the standard modules is collections.Counter, which is a type of dictionary. A Counter may be suitable for your needs, but it's hard to tell without more context.
Note that sets do not preserve order of addition. If you need to preserve the initial ordering of the lists, you can do what you want like this:
data = [[1, 2], [2, 1], [3, 2, 2], [2], [2, 1, 3], [2, 2, 3]]
a = set()
outlist = []
for s in data:
t = tuple(sorted(s))
if t not in a:
a.add(t)
outlist.append(list(t))
print(outlist)
output
[[1, 2], [2, 2, 3], [2], [1, 2, 3]]
If the number of input lists is fairly small you don't need the set (and the list<->tuple conversions), just test membership in outlist. However, that's not efficient for larger input lists since it performs a linear search on the list.

Python, slicing lists?

i really need your help with slicing a list.
lets say I've got a list of lists like this:
field=[[1, 2, 4, 4], [4, 1, 4, 2], [2, 1, 4, 3], [2, 4, 2, 3], [1, 2, 3, 4]]
and I have to write a function which will return a string that will look like this:
42334
44423
21142
14221
thus fat i managed to do this:
def out(field):
b=''
for list in field:
list1=list[::-1]
b+=''.join((str(sth) for sth in list1))+'\n'
return b
which returns this:
4421
2414
3412
3242
4321
You need to transpose your list of rows to a list of columns, as well as reverse the results of the transposition to get a proper rotation.
zip(*field) transposes the rows to columns, reversed() then reverses the results. Combined with a list comprehension you can do this all in one expression:
def out(field):
return '\n'.join([''.join(map(str, c)) for c in reversed(list(zip(*field)))])
or spelled out into an explicit loop:
def out(field):
b = []
for columns in reversed(list(zip(*field))):
b.append(''.join(map(str, columns)))
return '\n'.join(b)
The list() call around the zip() call allows us to reverse the iterator result in Python 3; it can be dropped in Python 2.
Demo:
>>> field=[[1, 2, 4, 4], [4, 1, 4, 2], [2, 1, 4, 3], [2, 4, 2, 3], [1, 2, 3, 4]]
>>> [''.join(map(str, c)) for c in reversed(list(zip(*field)))]
['42334', '44423', '21142', '14221']
>>> '\n'.join([''.join(map(str, c)) for c in reversed(list(zip(*field)))])
'42334\n44423\n21142\n14221'
>>> print('\n'.join([''.join(map(str, c)) for c in reversed(list(zip(*field)))]))
42334
44423
21142
14221
def out(field):
b=''
a=[]
while len(field[0])>0:
for list in field:
a.append(list.pop())
a.append("\n")
a="".join(str(x) for x in a)
return a
x=[[1, 2, 4, 4], [4, 1, 4, 2], [2, 1, 4, 3], [2, 4, 2, 3], [1, 2, 3, 4]]
print out(x)

Special type of combination using itertools

I am almost finished with a task someone gave me that at first involved easy use of the product() function from itertools.
However, the person asked that it should also do something a bit different like:
li =
[[1, 2, 3],
[4, 5, 6]]
A regular product() would give something like: [1, 4], [1, 5], [1, 6], [2, 4], [2, 5], [2, 6], [3, 4] ...
What it should do is:
Do a regular product(), then, add the next item from the first element in the list and so on. A complete set of example would be:
[[1, 4, 2]
[1, 4, 3],
[1, 5, 2],
[1, 5, 3],
[2, 4, 3],
[2, 5, 3],
[2, 6, 3]]
How should I use itertools in this circumstance?
EDIT:
It might help if I explain the goal of the program:
The user will enter, for example, a 5 row by 6 column list of numbers.
A normal product() will result in a 5-number combination. The person wants a 6-number combination. Where will this "6th" number come from? It would come from his choice of which row he wants.
I wondering what is the magical computations you performing, but it look's like that's your formula:
k = int(raw_input('From What row items should be appeared again at the end?'))
res = [l for l in product(*(li+[li[k]])) if l[k]<l[len(li)] ]
Generalized for more than two sublist (map function would be the other alternative)
from pprint import pprint
for li in ([[1, 2, 3],
[4, 5, 6]],
[[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]]
):
triples= []
prevlist=li[0]
for nextlist in li[1:]:
for spacing in range(1,len(prevlist)):
triples.extend([[first,other,second]
for first,second in zip(prevlist,prevlist[spacing:])
for other in nextlist])
pprint(sorted(triples))

Categories