Related
My very first post and question here...
So, let list_a be the list of lists:
list_a = [[2,7,8], [3,4,2], [5,10], [4], [2,3,5]...]
Let list_b be another list of integers: list_b = [5,7]
I need to exclude all lists in list_a, whose items include at least one item from list_b. The result from example above schould look like list_c = [[3,4,2], [4]...]
If list_b was not a list but a single number b, then one could define list_c in one line as:
list_c = [x for x in list_a if not b in x]
I am wondering, if it is possible to write an elegant one-liner also for the list list_b with several values in it. Of course, I can just loop through all list_b's values, but may be there exists a faster option?
Let's first consider the task of checking an individual element of list_a - such as [2,7,8] - because no matter what, we're conceptually doing to need a way to do that, and then we're going to apply that to the list with a list comprehension. I'll use a as the name for such a list, and b for an element of list_b.
The straightforward way to write this is using the any builtin, which works elegantly in combination with generator expressions: any(b in a for b in list_b).
The logic is simple: we create a generator expression (like a lazily-evaluated list comprehension) to represent the result of the b in a check applied to each b in list_b. We create those by replacing the [] with (); but due to a special syntax rule we may drop these when using it as the sole argument to a function. Then any does exactly what it sounds like: it checks (with early bail-out) whether any of the elements in the iterable (which includes generator expressions) is truthy.
However, we can likely do better by taking advantage of set intersection. The key insight is that the test we are trying to do is symmetric; considering the test between a and list_b (and coming up with another name for elements of a), we could equally have written any(x in list_b for x in a), except that it's harder to understand that.
Now, it doesn't help to make a set from a, because we have to iterate over a anyway in order to do that. (The generator expression does that implicitly; in used for list membership requires iteration.) However, if we make a set from list_b, then we can do that once, ahead of time, and just have any(x in set_b for x in a).
But that, in turn, is a) as described above, hard to understand; and b) overlooking the built-in machinery of sets. The operator & normally used for set intersection requires a set on both sides, but the named method .intersection does not. Thus, set_b.intersection(a) does the trick.
Putting it all together, we get:
set_b = set(list_b)
list_c = [a for a in list_a if not set_b.intersection(a)]
You can write the logic all sublists in A where none of the elements of B are in the sublist with a list comprehension like:
A = [[2,7,8], [3,4,2], [5,10], [4], [2,3,5]]
B = [5,7]
[l for l in A if not any(n in l for n in B)]
# [[3, 4, 2], [4]]
The condition any(n in l for n in B) will be true if any element, n, of B is in the sublist, l, from A. Using not we can take the opposite of that.
Mark's answer is good but hard to read.
FYI, you can also leverage sets:
>>> set_b = set(list_b)
>>> [l for l in list_a if not set_b.intersection(l)]
[[3, 4, 2], [4]]
I want to select elements of an array according to multiple conditions but dynamically.
I'd define them as follows:
L = [1,2,5]
X = np.random.choice(10, size=(15,))
X[X in L]
I know I can do it as X[(X==1)|(X==2)|(X==5)] but my question goes for dynamically changing L, suppose it is an arbitrary list of integers.
index = np.zeros_like(X, dtype=np.bool)
for i in L:
index[i] = 1
X[index]
Is there a better way to perform this?
You want to get all elements from the input list X, which also belong to L.
You can use numpy.isin(...):
X[np.isin(X, L)]
np.isin(X, L) will in essence return array of Booleans for each element of X having True if it belongs to L and False otherwise.
https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.isin.html
I've had a look through the forums and can't find anything to do with multiplying all elements in an array recursively.
I've created the following code that almost does what I want. The goal is to use no loops and only recursion.
Here's the code:
def multAll(k,A):
multAllAux(k,A)
return A[:]
def multAllAux(k,A):
B = [0]
if A == []:
return 0
else:
B[0] = (A[0] * k)
B.append(multAllAux(k,A[1:]))
return B
print(multAllAux(10, [5,12,31,7,25] ))
The current output is:
[50, [120, [310, [70, [250, 0]]]]]
However, it should be:
[50,120,310,70,250]
I know I am close, but I am at a complete loss at this point. The restrictions of no loops and solely recursion has left me boggled!
Your multAllAux function returns a list. If you append a list to another list, you get this nested list kind of structure that you are getting right now.
If you instead use the "extend" function; it will work as expected.
>>> a = [1, 2, 3]
>>> a.extend([4, 5])
>>> a
[1, 2, 3, 4, 5]
extend takes the elements from a second list and adds them to the first list, instead of adding the second list itself which is what append does!
Your function also returns a zero at the end of the list, which you don't need. You can try this:
def mult(k, A: list) -> list:
return [k * A[0]] + mult(k, A[1:]) if A else []
The problem is here:
B.append(multAllAux(k,A[1:])))
What .append(..) does is it takes the argument, considers it as a single element and adds that element to the end of the list. What you want is to concatenate to the list (ie the item being added should be seen as a list of elements rather than one single element).
You can say something like: B = B + multAllAux(..) or just use +=
B += multAllAux(...)
BTW, if you wanted to multiply a single number to a list, there is a very similar construct: map(..). Note that this behaves slightly differently depending on whether you're using Py2 or Py3.
print(map(lambda x: x * 10, [5,12,31,7,25]))
I am trying to perform in-place modification of a list of list on the level of the primary list. However, when I try to modify the iterating variable (row in the example below), it appears to create a new pointer to it rather than modifying it.
Smallest example of my problem.
c = [1,2,3]
for x in c:
x = x + 3
print(c) #returns [1,2,3], expected [4,5,6]
The above example is a trivial example of my problem. Is there a way to modify x elementwise, in-place and have the changes appear in C?
Less trivial example of my problem. I am switching all 0's to 1's and vice-versa.
A = [[1,1,0],
[1,0,1],
[0,0,0]]
for row in A:
row = list(map(lambda val: 1 - val, row))
print(A)
Expected
A = [[0,0,1],
[0,1,0],
[1,1,1]]
Returned
A = [[1,1,0],
[1,0,1],
[0,0,0]]
update:
Great answers so far. I am interested how the iterating variable (row in the second example) is linked to the iterable variable (A in the second example).
If I do the following, which reverses each sublist of A, it works perfectly.
Why does the following example, where I modify the iterating variable works but the above examples do not?
A = [[1,1,0],
[1,0,1],
[0,0,0]]
for row in A:
row.reverse()
print(A)
#returns, as expected
A = [[0, 1, 1],
[1, 0, 1],
[0, 0, 0]]
I found this in the docs: https://docs.python.org/3/tutorial/controlflow.html#for
Python’s for statement iterates over the items of any sequence (a list
or a string), in the order that they appear in the sequence.
If you need to modify the sequence you are iterating over while inside
the loop (for example to duplicate selected items), it is recommended
that you first make a copy. Iterating over a sequence does not
implicitly make a copy.
I was wrong in my first response, when iterating through a list it returns the actual items in that list. However, it seems they cannot be edited directly while they are being iterated through. This is why iterating through the integers the length of the list works.
As for why the .reverse() function works, I think it's because it is affecting a list instead of a value. I tried to use similar built in functions on nonlist datatypes like .replace() on strings and it had no effect.
All of the other list functions I tried worked: .append(), .remove(), and .reverse() as you showed. I'm not sure why this is, but I hope it clears up what you can do in for loops a bit more.
Answer to old question below:
The way you are using the for loops doesn't affect the actual list, just the temporary variable that is iterating through the list. There are a few ways you can fix this. Instead of iterating through each element you can can count up to the length of the list and modify the list directly.
c = [1,2,3]
for n in range(len(c)):
c[n] += 3
print(c)
You can also use the enumerate() function to iterate through both a counter and list items.
c = [1,2,3]
for n, x in enumerate(c):
c[n] = x + 3
print(c)
In this case, n is a counter and x is the item in the list.
Finally, you can use list comprehension to generate a new list with desired differences in one line.
c = [1, 2, 3]
d = [x + 3 for x in c]
print(d)
The usual way to poke values into an existing list in Python is to use enumerate which lets you iterate over both the indices and the values at once -- then use the indices to manipulate the list:
c = [1,2,3]
for index, value in enumerate(c):
c[index] = value + 3
For your second example you'd do almost the same:
A = [[1,1,0],
[1,0,1],
[0,0,0]]
for row in A:
for index, val in row:
row[index] = 0 if val > 0 else 1
In the second example the list objects in A become the loop variable row -- and since you're only mutating them (not assigning to them) you don't need enumerate and the index
If you want to keep it consice without creating an additional variable, you could also do:
c = [1,2,3]
print(id(c))
c[:] = [i+3 for i in c]
print(c, id(c))
Output:
2881750110600
[4, 5, 6] 2881750110600
Using list comprehension here also will work:
A = [[1,1,0],
[1,0,1],
[0,0,0]]
A = [[0 if x > 0 else 1 for x in row] for row in A]
print(A)
Output:
[[0, 0, 1],
[0, 1, 0],
[1, 1, 1]]
I was asked if it was possible to compare two (say) lists without invoking operators, to determine if they were the same (or rather, contained the same elements).
I first entertained using
x in y
before I realised that it would not care for order, but for mere presence. Of course, if the lists were to contain purely numbers it would be trivial to do a modulus test or so, but lists can contain strings. (is didn't work either, but I didn't really expect it to, considering it tests identity...)
So I was wondering if it's (even) possible to pull off equality tests without using operators (==, !=)?
It was a mere rhetorical question, but it's been gnawing at me for some time and I've rather given up trying to solve it myself with my not-very extensive python knowledge.
Sure it is, just bypass the operators and go straight for the __eq__ special method:
>>> x = [1, 2, 3]
>>> y = [1, 2, 3]
>>> x.__eq__(y)
True
>>> z = [42]
>>> x.__eq__(z)
False
You can also use the operator module:
>>> import operator
>>> operator.eq(x, y)
True
>>> operator.eq(x, z)
False
In Python 2, you could use looping with any() and cmp(), with itertools.izip_longest() to make sure we don't ignore uneven lengths:
>>> from itertools import izip_longest
>>> not any(cmp(i, j) for i, j in izip_longest(x, y, fillvalue=object()))
True
>>> not any(cmp(i, j) for i, j in izip_longest(x, z, fillvalue=object()))
False
This works because cmp() returns 0 for values that are equal. any() returns False only if all results are false (e.g. 0).
Hell, go straight for cmp() without looping:
>>> not cmp(x, y)
True
>>> not cmp(x, z)
False
For Python 3 you'd have to create your own cmp() function, perhaps using .__lt__ and .__gt__ if you want to avoid the < and > operators too.
For lists with only integers, you can forgo the cmp() function and go straight to subtraction; let's use map() here and include the list lengths:
>>> not (len(x) - len(y)) and not any(map(lambda i, j: i - j, x, y))
True
>>> not (len(x) - len(z)) and not any(map(lambda i, j: i - j, x, z))
False
This works because map() zips up the values in the lists and passes these pairs to the first argument, a callable. That subtracts the values and only if the integers are equal do we get all 0 values and any() returns False.
Apart from Martijn Pieters's answer, i could think of following options:
using XOR:
x = [1, 2, 3]
y = [1, 2, 3]
result = "list equal"
if len(x)-len(y):
result = "list not equal"
else:
for i,j in zip(x,y):
if i ^ j:
result = "list is not equal"
break
print result
Using set:
if set(x).difference(set(y)):
print "list not equal"
else:
print "list equal"