simple dropping elements from the list in python - python

I'd like to achieve following effect
a=[11, -1, -1, -1]
msg=['one','two','tree','four']
msg[where a<0]
['two','tree','four']
In similar simple fashion (without nasty loops).
PS. For curious people this if statement is working natively in one of functional languages.
//EDIT
I know that below text is different that the requirements above, but I've found what I wonted to acheave.
I don't want to spam another answer in my own thread, so I've also find some nice solution,
and I want to present it to you.
filter(lambda x: not x.endswith('one'),msg)

You can use list comprehensions for this. You need to match the items from the two lists, for which the zip function is used. This will generate a list of tuples, where each tuple contains one item from each of the original lists (i.e., [(11, 'one'), ...]). Once you have this, you can iterate over the result, check if the first element is below 0 and return the second element. See the linked Python docs for more details about the syntax.
[y for (x, y) in zip(a, msg) if x < 0]
The actual problem seems to be about finding items in the msg list that don't contain the string "one". This can be done directly:
[m for m in msg if "one" not in m]

[m for m, i in zip(msg, a) if i < 0]

The answers already posted are good, but if you want an alternative you can look at numpy and at its arrays.
>>> import numpy as np
>>> a = np.array([11, -1, -1, -1])
>>> msg = np.array(['one','two','tree','four'])
>>> a < 0
array([False, True, True, True], dtype=bool)
>>> msg[a < 0]
array(['two', 'tree', 'four'], dtype='|S4')
I don't know how array indexing is implemented in numpy, but it is usually fast and problably rewritten in C. Compared to the other solutions, this should be more readable, but it requires numpy.

I think [msg[i] for i in range(len(a)) if a[i]<0] will work for you.

Related

Find minimum values of both "columns" of list of lists

Given a list like the next one:
foo_list = [[1,8],[2,7],[3,6]]
I've found in questions like Tuple pairs, finding minimum using python and
minimum of list of lists that the pair with the minimum value of a list of lists can be found using a generator like:
min(x for x in foo_list)
which returns
[1, 8]
But I was wondering if there is a similar way to return both minimum values of the "columns" of the list:
output = [1,6]
I know this can be achieved using numpy arrays:
output = np.min(np.array(foo_list), axis=0)
But I'm interested in finding such a way of doing so with generators (if possible).
Thanks in advance!
[min(l) for l in zip(*foo_list)]
returns [1, 6]
zip(*foo_list) gets the list transpose and then we find the minimum in both lists.
Thanks #mousetail for suggestion.
You can use two min() for this. Like -
min1 = min(a for a, _ in foo_list)
min2 = min(b for _, b in foo_list)
print([min1, min2])
Will this do? But I think if you don't want to use third party library, you can just use plain old loop which will be more efficient.

Popping the last element of a one-dimensional array

When it comes to lists, we all know and love good old pop, which removes the last item from the list and returns it:
>>> x = range(3)
>>> last_element = x.pop()
>>> last_element
2
>>> x
[0, 1]
But suppose I'm using a one-dimensional numpy array to hold my items, because I'm doing a lot of elementwise computations. What then is the most efficient way for me to achieve a pop?
Of course I can do
>>> import numpy as np
>>> x = np.arange(3)
>>> last_element = x[-1]
>>> x = np.delete(x, -1) # Or x = x[:-1]
>>> last_element
2
>>> x
array([0, 1])
And, really, when it comes down to it, this is fine. But is there a one-liner for arrays I'm missing that removes the last item and returns it at the same time?
And I'm not asking for
>>> last_element, x = x[-1], x[:-1]
I'm not counting this as a one-liner, because it's two distinct assignments achieved by two distinct operations. Syntactic sugar is what puts it all on one line. It's a sugary way to do what I've already done above. (Ha, I was sure someone would rush to give this as the answer, and, indeed, someone has. This answer is the equivalent of my asking, "What's a faster way to get to the store than walking?" and someone answering, "Walk, but walk faster." Uh . . . thanks. I already know how to walk.)
There is no such one liner for numpy (unless you write your own). numpy is meant to work on fixed sized objects (or objects that change less frequently). So by that metric a regular old python list is better for popping.
You are correct in that element-wise operations are better with numpy. You're going to have to profile out your code and see which performs better and make a design decision.

How can I skip creating tuples with certain values, when using the zip function in Python?

I am using the zip() function in Python.
I am zipping two lists, however, some lists have the value 0 in them. I would like to avoid any tuples with a zero in them.
x = [1, 2, 3]
y = [0, 0, 6]
zipped = zip(x, y)
>>> zipped
[(3, 6)]
In the process of trying to figure this out, I have also come to realize that any possible solution is probably going to be slow, if it involves zipping then removing.
Is there any way to incorporate a condition on zipping? Or should I explore a different, faster method?
Any advice is gratefully appreciated.
You could izip and only pass through the proper values:
from itertools import izip
zipped = [(xx, yy) for xx, yy in izip(x, y) if yy]
The reason I choose izip over zip is because in python2.x, zip will generate a new list which really isn't necessary. izip avoids that overhead.
In python3.x, the builtin zip doesn't have this list materializing behavior so you should use that instead. (in fact, itertools.izip doesn't even exist in python3.x)
zip() by itself won't cover this particular requirement. Nevertheless you can use it along with list comprehensions to get the desired result:
>>>x = [1, 2, 3]
>>>y = [0, 0, 6]
>>>zipped = [(a,b) for a,b in zip(x,y) if a and b]
>>>zipped
[(3,6)]
Regarding to the speed of this method. It will be a lot faster than any new solution you can create yourself using loops, since the looping in this case is performed natively in C which is faster than Python's loops.

Remove the last N elements of a list

Is there a a better way to remove the last N elements of a list.
for i in range(0,n):
lst.pop( )
Works for n >= 1
>>> L = [1,2,3, 4, 5]
>>> n=2
>>> del L[-n:]
>>> L
[1, 2, 3]
if you wish to remove the last n elements, in other words, keep first len - n elements:
lst = lst[:len(lst)-n]
Note: This is not an in memory operation. It would create a shallow copy.
As Vincenzooo correctly says, the pythonic lst[:-n] does not work when n==0.
The following works for all n>=0:
lst = lst[:-n or None]
I like this solution because it is kind of readable in English too: "return a slice omitting the last n elements or none (if none needs to be omitted)".
This solution works because of the following:
x or y evaluates to x when x is logically true (e.g., when it is not 0, "", False, None, ...) and to y otherwise. So -n or None is -n when n!=0 and None when n==0.
When slicing, None is equivalent to omitting the value, so lst[:None] is the same as lst[:] (see here).
As noted by #swK, this solution creates a new list (but immediately discards the old one unless it's referenced elsewhere) rather than editing the original one. This is often not a problem in terms of performance as creating a new list in one go is often faster than removing one element at the time (unless n<<len(lst)). It is also often not a problem in terms of space as usually the members of the list take more space than the list itself (unless it's a list of small objects like bytes or the list has many duplicated entries). Please also note that this solution is not exactly equivalent to the OP's: if the original list is referenced by other variables, this solution will not modify (shorten) the other copies unlike in the OP's code.
A possible solution (in the same style as my original one) that works for n>=0 but: a) does not create a copy of the list; and b) also affects other references to the same list, could be the following:
lst[-n:n and None] = []
This is definitely not readable and should not be used. Actually, even my original solution requires too much understanding of the language to be quickly read and univocally understood by everyone. I wouldn't use either in any real code and I think the best solution is that by #wonder.mice: a[len(a)-n:] = [].
Just try to del it like this.
del list[-n:]
I see this was asked a long ago, but none of the answers did it for me; what if we want to get a list without the last N elements, but keep the original one: you just do list[:-n]. If you need to handle cases where n may equal 0, you do list[:-n or None].
>>> a = [1,2,3,4,5,6,7]
>>> b = a[:-4]
>>> b
[1, 2, 3]
>>> a
[1, 1, 2, 3, 4, 5, 7]
As simple as that.
Should be using this:
a[len(a)-n:] = []
or this:
del a[len(a)-n:]
It's much faster, since it really removes items from existing array. The opposite (a = a[:len(a)-1]) creates new list object and less efficient.
>>> timeit.timeit("a = a[:len(a)-1]\na.append(1)", setup="a=range(100)", number=10000000)
6.833014965057373
>>> timeit.timeit("a[len(a)-1:] = []\na.append(1)", setup="a=range(100)", number=10000000)
2.0737061500549316
>>> timeit.timeit("a[-1:] = []\na.append(1)", setup="a=range(100)", number=10000000)
1.507638931274414
>>> timeit.timeit("del a[-1:]\na.append(1)", setup="a=range(100)", number=10000000)
1.2029790878295898
If 0 < n you can use a[-n:] = [] or del a[-n:] which is even faster.
This is one of the cases in which being pythonic doesn't work for me and can give hidden bugs or mess.
None of the solutions above works for the case n=0.
Using l[:len(l)-n] works in the general case:
l=range(4)
for n in [2,1,0]: #test values for numbers of points to cut
print n,l[:len(l)-n]
This is useful for example inside a function to trim edges of a vector, where you want to leave the possibility not to cut anything.

Comparing all elements of two tuples (with all() functionality)

So i know that comparisons on tuples work lexicographically:
Tuples and lists are compared lexicographically using comparison of corresponding elements. This means that to compare equal, each element must compare equal and the two sequences must be of the same type and have the same length.
If not equal, the sequences are ordered the same as their first differing elements. For example, cmp([1,2,x], [1,2,y]) returns the same as cmp(x,y). If the corresponding element does not exist, the shorter sequence is ordered first (for example, [1,2] < [1,2,3]).
So from this:
>>> a = (100, 0)
>>> b = (50, 50)
>>> a > b
True
But i want to compare all elements of 2 tuples in order, so functionally i want something akin to (using values from above):
>>> a > b
(True, False) #returned tuple containing each comparison
>>> all(a > b)
False
As an example in practice, for something like screen coordinates, if you wanted to check if something was 'inside' the screen at (0,0), but done a comparison like coord > (0,0), if the x coord was bigger than 0, but the y coord was smaller it would still return true, which isn't what is needed in this case.
As sort of a sub question/discussion:
I am not sure why comparing 2 tuples of different values is returned in such a way. You are not given any sort of index, so the only thing you get from comparing a tuple (that isn't testing equality) is that at some point in the tuple, one of the comparisons will throw a true or false value when they are not equal. How could you take advantage of that?
You can achieve this with a list comprehension and the zip built-in:
>>> a = (100, 0)
>>> b = (50, 50)
>>> [(a > b) for a, b in zip(a,b)]
[True, False]
You can use all() or any() on the returned list.
Replace a > b with tuple(i > j for i, j in zip(a,b)) in your second code sample.
>>> a = (100, 0)
>>> b = (50, 50)
>>> tuple(i > j for i, j in zip(a,b))
(True, False)
>>> all(i > j for i, j in zip(a,b))
False
You might consider using the following vectorized approach, which is usually more performant, and syntactically/semantically very clear:
>>> import numpy
>>>
>>> a = (100, 0)
>>> b = (50, 50)
>>> numpy.array(a) > b
array([ True, False], dtype=bool)
>>>
>>> (numpy.array(a) > b).any()
True
>>> (numpy.array(a) > b).all()
False
numpy is quite performant, and the resulting objects above also embed the any()/all() query methods you want. If you will be performing vector-like operations (as your screen coordinates example suggests), you may consider working with 'a' and 'b' as numpy arrays, instead of tuples. That results in the most efficient implementation of what you seek: no pre-conversion necessary, and Python-based loops are replaced with efficient numpy-based loops. This is worth highlighting because there are two and potentially three loops involved: (1) a preprocessing loop during conversion (which you can eliminate); (2) an item-by-item comparison loop; and (3) a query loop to answer the any/all question.
Note that I could've also created a numpy array from 'b', but not doing so eliminated one conversion step and pre-processing time. Since that approach results in one operand being a numpy array and the other a tuple, as the sequences grow, that may/may-not result in less speedy item-by-item comparisons (which strict numpy-to-numpy is good at). Try it. :)
I felt like the use of map and lambda functions was missing from the answers
>>> a = (100, 0)
>>> b = (50, 50)
>>> all(map(lambda x,y: x > y, a, b))
False
To get the described behavior, try:
[ai > bi for ai,bi in zip(a,b)]
The reason that comparisons of tuples are returned in that way is that you might want to write something like:
if a >= (0.,0.):
print "a has only positive values"
else:
print "a has at least one negative value"
If Python were to return the tuple that you describe, then the else would never happen. Try
if (False,False):
print "True!" # This is what is printed.
else:
print "False!"
I hope this helps.

Categories

Resources