Remove elements from several lists simultaneously - python

I have three lists with the same length and another list that stores indexes of elements that I need to remove from all three lists. This is an example of what I mean:
a = [3,4,5,12,6,8,78,5,6]
b = [6,4,1,2,8,784,43,6,2]
c = [8,4,32,6,1,7,2,9,23]
(all have len()=9)
The other list contains the indexes of those elements I need to remove from all three lists:
d = [8,5,3]
(note that it is already sorted)
I know I can remove one element at the time from the three lists with:
for indx in d:
del a[indx]
del b[indx]
del c[indx]
How could I do this in one single line?

Not one line, but concise, readable, and completely idiomatic Python:
for indx in d:
for x in a, b, c:
del x[indx]
However, the fact that you're doing this in the first place implies that maybe rather than 3 separate list variables, you should have a list of 3 lists, or a dict of three lists keyed by the names 'a', 'b', and 'c'.
If you really want it in one line:
for indx in d: a.pop(indx), b.pop(indx), c.pop(indx)
But that's really terrible. You're calling pop when you don't care about the values, and building up a tuple you don't need.
If you want to play code golf, you can save a few characters by using a list comprehension—which adds one more language abuse, and builds another, larger object you don't actually want—as in Ioan Alexandru Cucu's answer:
[x.pop(indx) for indx in d for x in a, b, c]
Of course the best way to write it in one line is to factor it out into a function:
def delemall(indices, *lists):
for index in indices:
for x in lists:
del x[indx]
And now, each of the 300 times you need to do this, it's just:
delemall(d, a, b, c)

Maybe numpy is useful for something like this, if your three lists were a 2D numpy.array deleting specified columns would be very easy.
a = [3,4,5,12,6,8,78,5,6]
b = [6,4,1,2,8,784,43,6,2]
c = [8,4,32,6,1,7,2,9,23]
big_array = np.array([a,b,c])
d = [8,5,3]
Result:
>>> big_array
array([[ 3, 4, 5, 12, 6, 8, 78, 5, 6],
[ 6, 4, 1, 2, 8, 784, 43, 6, 2],
[ 8, 4, 32, 6, 1, 7, 2, 9, 23]])
>>> np.delete(big_array, d, axis=1)
array([[ 3, 4, 5, 6, 78, 5],
[ 6, 4, 1, 8, 43, 6],
[ 8, 4, 32, 1, 2, 9]])

I think just your code is OK, to make it a single line:
In [234]: for i in d: del a[i], b[i], c[i]
In [235]: a,b,c
Out[235]: ([3, 4, 5, 6, 78, 5], [6, 4, 1, 8, 43, 6], [8, 4, 32, 1, 2, 9])
but I still like leaving that for loop two lines ;)

import operator
a = [3,4,5,12,6,8,78,5,6]
b = [6,4,1,2,8,784,43,6,2]
c = [8,4,32,6,1,7,2,9,23]
d = [8,5,3]
for _ in (operator.delitem(q,i) for q in (a,b,c) for i in d): pass
print(a,b,c)

Related

Find the lists that share common values

My question is closely related to the following topic.
I have a number of lists and I want to find the lists that share common values. All lists have the same size. The total number of lists is variable and can increase. Minimum number of lists is 2
a = [1, 2, 3, 4]
b = [5, 6, 7, 8]
c = [9, 10, 11, 1]
The expected output is:
[a, c]
Ideally, I also want the fastest method. Thanks in advance,
You could cast them to sets and use the intersection() function, if it does return a value, there are some values in common
If you want the ['a', 'c'] output for n named lists, you could save them in a dict and use any to check if they intersect while looping through them:
lists = {
"a" : [1, 2, 3, 4],
"b" : [5, 6, 7, 8],
"c" : [9, 10, 11, 1]
}
res = []
for l in lists:
for l2 in lists:
if l is not l2:
if any(i in lists[l] for i in lists[l2]):
res.append(l)
break;
print(res)
OUT: ['a', 'c']
lists = []
for i in a:
if i in b:
lists.append([a, b])
if i in c:
lists.append([a, c])
for i in b:
if i in c:
lists.append([b, c])
print(lists)
Using my limited knowledge on Python lists, I came up with this:
class countedList:
def __init__(self,listt):
self.listt = listt
self.sharedNum = 0
def mostCommon(*lists):
for item in lists:
for listItem in item.listt:
for item2 in lists:
item2.sharedNum+=item2.listt.count(listItem)
new = sorted(lists,key=lambda clist: clist.sharedNum,reverse=True)
return new[:2]
test = mostCommon(countedList([1, 2, 3, 4]),countedList([5, 6, 7, 8]),countedList([9, 10, 11, 1]))
Well yeah, I had to make up a custom class for it. In the test run it gave:
>>> test[0].listt
[1, 2, 3, 4]
>>> test[1].listt
[9, 10, 11, 1]
To simply check if two lists share at least one value you can use...
a = [1, 2, 3, 4]
b = [9, 10, 11, 1]
if any(a) == any(b):
print(True)

Averaging over n elements

I have a numpy array like this [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Let's assume I want the average of 3 elements, my target array should then look like this:
[2, 2, 2, 5, 5, 5, 8, 8, 8, 10]
Notice that when there is no triplet available I want to calculate the average over the remaining elements
Is there a neat way to do that with array operations?
You could reshape the array to use the mean function, for example:
a = np.arange(1,11)
b = a[:a.size//3*3]
b.shape = (-1,3)
c = np.mean(b, axis=1)
# c == array([2., 5., 8.])
Then reassign the results in the original array:
c.shape = (-1,1) # i.e. (len(b), 1)
b[:] = c
print(a)
# array([ 2, 2, 2, 5, 5, 5, 8, 8, 8, 10])
Note that this works because b is a sub-view of a. Also the last element is not as you asked the average (I left it untouched), but it'll be easy to fix, with e.g.:
a[9:] = np.mean(a[9:])
I have done most of it in a one liner just for fun :D
*Notice I'm using sum() to flatten the list.. (that's some weird python trick)
def custom_avg(group: int, arr):
out = list()
[out.append(list(np.full( (1, group), np.sum(arr[i:i+group])/ (1 if i + group > len(arr) else group), dtype=int))) for i in range(0, len(arr), group) ]
return sum(out,[])
Enjoy! good luck.

Take elements from multiple lists

Given multiple lists like the ones shown:
a = [1, 2, 3]
b = [5, 6, 7, 8]
c = [9, 0, 1]
d = [2, 3, 4, 5, 6, 7]
...
I want to be able to combine them to take as many elements from the first list as I can before starting to take elements from the second list, so the result would be:
result = [1, 2, 3, 8, 6, 7]
Is there a particularly nice way to write this? I can't think of a really simple one without a for loop. Maybe a list comprehension with a clever zip.
Simple slicing and concatenation:
a + b[len(a):]
Or with more lists:
res = []
for lst in (a, b, c, d):
res += lst[len(res):]
# [1, 2, 3, 8, 6, 7]
With itertools.zip_longest() for Python 3, works on any number of input lists:
>>> from itertools import zip_longest
>>> [next(x for x in t if x is not None) for t in zip_longest(a,b,c,d)]
[1, 2, 3, 8, 6, 7]
The default fill value is None so take the first none None element in each tuple created with the zip_longest call (you can change the defaults and criteria if None is a valid data value)
With functools.reduce:
from functools import reduce
print(list(reduce(lambda a, b: a + b[len(a):], [a, b, c, d])))
This outputs:
[1, 2, 3, 8, 6, 7]

Sort list values to get a new order of its index, Python way

Sorry for the vague of my question's title.
My question is, I have a list a = [6, 9, 8, 10, 7, 5, 2, 3, 1, 4]
I need to get the new order b = [4, 2, 3, 5, 1, 6, 10, 8, 7, 9], where the first element of b is 4 because the 4th element of a 10 is the largest number in a. Similarly, the 2nd element in b is 2 because the second large number in a is its second number 9
So, hopefully you got my question: Sort the list a and get the new order b.
Currently, I get it done by using list.sort with some prepare.
tmp = zip(range(1,11), a)
tmp.sort(key=lambda x:(-x[1],x[0]))
b = [x[0] for x in tmp]
I wonder whether there are better python way to achieve my goal?
Thanks for any suggestions~
I would just use the key argument to sort range(1, len(a) + 1) by using a's values.
sorted(range(1, len(a) + 1), key=lambda i: a[i-1], reverse=True)
That's basically the idea, but you can do:
import operator
tmp = sorted(enumerate(a,1),key=itemgetter(1,0),reverse=True)
b = [x[0] for x in tmp]
#In python2.x, the following are equivalent to the list comprehension.
#b = zip(*tmp)[0]
#b = map(itemgetter(0),tmp)
I think that enumerate is a little cleaner than zip with range and itemgetter is a little cleaner than lambda.
You could use sorted and enumerate:
print [el[0] for el in sorted(enumerate(a, start=1), key=lambda L: L[1], reverse=True)]
# [4, 2, 3, 5, 1, 6, 10, 8, 7, 9]
For completeness an alternative using numpy (should you happen to use it any time in the near future):
np.argsort(a)[::-1] + 1
a = [6, 9, 8, 10, 7, 5, 2, 3, 1, 4]
b = [6, 9, 8, 10, 7, 5, 2, 3, 1, 4]
a.sort(reverse = True)
print(a)
print(b)
c = [b.index(y)+1 for y in a ]
print(c)
i have just got this stupid answers...

merge arrays in python based on a similar value

I want to merge two arrays in python based on the first element in each column of each array.
For example,
A = ([[1, 2, 3],
[4, 5, 6],
[4, 6, 7],
[5, 7, 8],
[5, 9, 1]])
B = ([[1, .002],
[4, .005],
[5, .006]])
So that I get an array
C = ([[1, 2, 3, .002],
[4, 5, 6, .005],
[4, 6, 7, .005],
[5, 7, 8, .006],
[5, 9, 1, .006]])
For more clarity:
First column in A is 1, 4, 4, 5, 5 and
First column of B is 1, 4, 5
So that 1 in A matches up with 1 in B and gets .002
How would I do this in python? Any suggestions would be great.
Is it Ok to modify A in place?:
d = dict((x[0],x[1:]) for x in B)
Now d is a dictionary where the first column are keys and the subsequent columns are values.
for lst in A:
if lst[0] in d: #Is the first value something that we can extend?
lst.extend(d[lst[0]])
print A
To do it out of place (inspired by the answer by Ashwini):
d = dict((x[0],x[1:]) for x in B)
C = [lst + d.get(lst[0],[]) for lst in A]
However, with this approach, you need to have lists in both A and B. If you have some lists and some tuples it'll fail (although it could be worked around if you needed to), but it will complicate the code slightly.
with either of these answers, B can have an arbitrary number of columns
As a side note on style: I would write the lists as:
A = [[1, 2, 3],
[4, 5, 6],
[4, 6, 7],
[5, 7, 8],
[5, 9, 1]]
Where I've dropped the parenthesis ... They make it look too much like you're putting a list in a tuple. Python's automatic line continuation happens with parenthesis (), square brackets [] or braces {}.
(This answer assumes these are just regular lists. If they’re NumPy arrays, you have more options.)
It looks like you want to use B as a lookup table to find values to add to each row of A.
I would start by making a dictionary out of the data in B. As it happens, B is already in just the right form to be passed to the dict() builtin:
B_dict = dict(B)
Then you just need to build C row by row.
For each row in A, row[0] is the first element, so B_dict[row[0]] is the value you want to add to the end of the row. Therefore row + [B_dict[row[0]] is the row you want to add to C.
Here is a list comprehension that builds C from A and B_dict.
C = [row + [B_dict[row[0]]] for row in A]
You can convert B to a dictionary first, with the first element of each sublist as key and second one as value.
Then simply iterate over A and append the related value fetched from the dict.
In [114]: A = ([1, 2, 3],
[4, 5, 6],
[4, 6, 7],
[5, 7, 8],
[6, 9, 1])
In [115]: B = ([1, .002],
[4, .005],
[5, .006])
In [116]: [x + [dic[x[0]]] if x[0] in dic else [] for x in A]
Out[116]:
[[1, 2, 3, 0.002],
[4, 5, 6, 0.005],
[4, 6, 7, 0.005],
[5, 7, 8, 0.006],
[6, 9, 1]]
Here is a solution using itertools.product() that prevents having to create a dictionary for B:
In [1]: from itertools import product
In [2]: [lst_a + lst_b[1:] for (lst_a, lst_b) in product(A, B) if lst_a[0] == lst_b[0]]
Out[2]:
[[1, 2, 3, 0.002],
[4, 5, 6, 0.005],
[4, 6, 7, 0.005],
[5, 7, 8, 0.006],
[5, 9, 1, 0.006]]
The naive, simple way:
for alist in A:
for blist in B:
if blist[0] == alist[0]:
alist.extend(blist[1:])
# alist.append(blist[1]) if B will only ever contain 2-tuples.
break # Remove this if you want to append more than one.
The downside here is that it's O(N^2) complexity. For most small data sets, that should be ok. If you're looking for something more comprehensive, you'll probably want to look at #mgilson's answer. Some comparison:
His response converts everything in B to a dict and performs list slicing on each element. If you have a lot of values in B, that could be expensive. This uses the existing lists (you're only looking at the first value, anyway).
Because he's using dicts, he gets O(1) lookup times (his answer also assumes that you're never going to append multiple values to the end of the values in A). That means overall, his algorithm will achieve O(N). You'll need to weigh whether the overhead of creating a dict is going to outweight the iteration of the values in B.

Categories