Related
When given a list of lists like: [[5, 2] [2, 5]]
I want to reduce this list to this: [[5, 2]] or [[2, 5]]
I've already tried the following: list(map(list, set(map(frozenset, parts_to_add))))
But the problem with this method is that it turns my lists in sets and while this works - it filters out all the equivalent lists - it also eats all of my duplicate values; When given [[3, 3]] it returns [[3]], due to the nature of sets in python. Is there another way of doing this?
Any help would be grately appreciated.
You could do :
data = [[5, 2], [2, 5], [3, 3]]
result = set(tuple(e) for e in map(sorted, data))
print([list(e) for e in result])
Output
[[2, 5], [3, 3]]
As #soon mentioned you could use a frozen Counter, for example:
from collections import Counter
data = [[5, 2], [2, 5], [3, 3]]
result = [e for e in {frozenset(Counter(e)): e for e in data}.values()]
print(result)
Output
[[2, 5], [3, 3]]
The above approach also uses a dict for removing duplicates instead of a set.
A more straightforward approach would be,
data = [[2, 5], [5, 2], [3, 6], [2, 5], [6, 3]]
result = []
for i in range(len(data)):
data[i].sort()
if data[i] not in result:
result.append(data[i])
In [49]: result
Out[49]: [[2, 5], [3, 6]]
I'm very new to python (using python3) and I'm trying to add numbers from one list to another list. The only problem is that the second list is a list of lists. For example:
[[1, 2, 3], [4, 5, 6]]
What I want is to, say, add 1 to each item in the first list and 2 to each item in the second, returning something like this:
[[2, 3, 4], [6, 7, 8]]
I tried this:
original_lst = [[1, 2, 3], [4, 5, 6]]
trasposition_lst = [1, 2]
new_lst = [x+y for x,y in zip(original_lst, transposition_ls)]
print(new_lst)
When I do this, I get an error
can only concatenate list (not "int") to list
This leads me to believe that I can't operate in this way on the lists as long as they are nested within another list. I want to do this operation without flattening the nested list. Is there a solution?
One approach using enumerate
Demo:
l = [[1, 2, 3], [4, 5, 6]]
print( [[j+i for j in v] for i,v in enumerate(l, 1)] )
Output:
[[2, 3, 4], [6, 7, 8]]
You can use enumerate:
l = [[1, 2, 3], [4, 5, 6]]
new_l = [[c+i for c in a] for i, a in enumerate(l, 1)]
Output:
[[2, 3, 4], [6, 7, 8]]
Why don't use numpy instead?
import numpy as np
mat = np.array([[1, 2, 3], [4, 5, 6]])
mul = np.array([1,2])
m = np.ones(mat.shape)
res = (m.T *mul).T + mat
You were very close with you original method. Just fell one step short.
Small addition
original_lst = [[1, 2, 3], [4, 5, 6]]
transposition_lst = [1, 2]
new_lst = [[xx + y for xx in x] for x, y in zip(original_lst, transposition_lst)]
print(new_lst)
Output
[[2, 3, 4], [6, 7, 8]]
Reasoning
If you print your original zip it is easy to see the issue. Your original zip yielded this:
In:
original_lst = [[1, 2, 3], [4, 5, 6]]
transposition_lst = [1, 2]
for x,y in zip(original_lst, transposition_lst):
print(x, y)
Output
[1, 2, 3] 1
[4, 5, 6] 2
Now it is easy to see that you are trying to add an integer to a list (hence the error). Which python doesn't understand. if they were both integers it would add them or if they were both lists it would combine them.
To fix this you need to do one extra step with your code to add the integer to each value in the list. Hence the addition of the extra list comprehension in the solution above.
A different approach than numpy that could work even for lists of different lengths is
lst = [[1, 2, 3], [4, 5, 6, 7]]
c = [1, 2]
res = [[l + c[i] for l in lst[i]] for i in range(len(c))]
I have a table containing:
table = [[5, 7],[4, 3],[3, 3],[2, 3],[1, 3]]
and the first values represented in each list, (5,4,3,2,1) can be said to be an ID of a person. the second values represented (7,3,3,3,3) would be a score. What I'm trying to do is to detect duplicates values in the second column which is in this case is the 3s in the list. Because the 4 lists has 3 as the second value, i now want to sort them based on the first value.
In the table, notice that [1,3] has one as the first value hence, it should replace [4,3] position in the table. [2,3] should replace [3,3] in return.
Expected output: [[5,7],[1,3],[2,3],[3,3],[4,3]]
I attempted:
def checkDuplicate(arr):
i = 0
while (i<len(arr)-1):
if arr[i][1] == arr[i+1][1] and arr[i][0] > arr[i+1][0]:
arr[i],arr[i+1] = arr[i+1],arr[i]
i+=1
return arr
checkDuplicate(table)
The code doesn't fulfil the output i wanted and i would appreciate some help on this matter.
You can use sorted with a key.
table = [[5, 7], [4, 3], [3, 3], [2, 3], [1, 3]]
# Sorts by second index in decreasing order and then by first index in increasing order
sorted_table = sorted(table, key=lambda x: (-x[1], x[0]))
# sorted_table: [[5, 7], [1, 3], [2, 3], [3, 3], [4, 3]]
You should sort the entire list by the second column, using the first to break ties. This has the advantage of correctly grouping the threes even when the seven is interpersed among them, e.g. something like
table = [[4, 3],[3, 3],[5, 7],[2, 3],[1, 3]]
In Python, you can do it with a one-liner:
result = sorted(table, key=lambda x: (-x[1], x[0]))
If you want an in-place sort, do
table.sort(key=lambda x: (-x[1], x[0]))
Another neat thing you can do in this situation is to rely on the stability of Python's sorting algorithm. The docs actually suggest doing multiple sorts in complex cases like this, in the reverse order of the keys. Using the functions from operator supposedly speeds up the code as well:
from opetator import itemgetter
result = sorted(table, key=itemgetter(0))
result.sort(key=itemgetter(1), reversed=True)
The first sort will arrange the IDs in the correct order. The second will sort by score, in descending order, leaving the IDs undisturbed for identical scores since the sort is stable.
If you want to leave the list items with non-duplicate second elements untouched, and the ability to deal with the cases where multiple second items can be duplicate, I think you'll need more than the built-in sort.
What my function achieves:
Say your list is: table = [[5, 7], [6, 1], [8, 9], [3, 1], [4, 3], [3, 3], [2, 3], [1, 3]]
It will not touch the items [5, 7] and [8, 9], but will sort the remaining items by swapping them based on their second elements. The result will be:
[[5, 7], [3, 1], [8, 9], [6, 1], [1, 3], [2, 3], [3, 3], [4, 3]]
Here is the code:
def secondItemSort(table):
# First get your second values
secondVals = [e[1] for e in table]
# The second values that are duplicate
dups = [k for k,v in Counter(secondVals).items() if v>1]
# The indices of those duplicate second values
indices = dict()
for d in dups:
for i, e in enumerate(table):
if e[1]==d:
indices.setdefault(d, []).append(i)
# Now do the sort by swapping the items intelligently
for dupVal, indexList in indices.items():
sortedItems = sorted([table[i] for i in indexList])
c = 0
for i in range(len(table)):
if table[i][1] == dupVal:
table[i] = sortedItems[c]
c += 1
# And return the intelligently sorted list
return table
Test
Let's test on a little bit more complicated table:
table = [[5, 7], [6, 1], [8, 9], [3, 1], [4, 3], [3, 9], [3, 3], [2, 2], [2, 3], [1, 3]]
Items that should stay in their places: [5, 7] and [2, 2].
Items that should be swapped:
[6, 1] and [3, 1].
[8, 9] and [3, 9]
[4, 3], [3, 3], [2, 3], [1, 3]
Drumroll...
In [127]: secondItemSort(table)
Out[127]:
[[5, 7],
[3, 1],
[3, 9],
[6, 1],
[1, 3],
[8, 9],
[2, 3],
[2, 2],
[3, 3],
[4, 3]]
Sorry if I get terminology wrong - I've only just started learning Python, and I'm receiving instruction from friends instead of being on an actual course.
I want to search a list containing lots of arrays containing multiple elements, and find arrays with some elements matching, but some different.
In less confusing terms e.g. I have a list of arrays that each contain 2 elements (I think this is called a 2D array?) so:
list = [[1, 2], [2, 2], [3, 5], [4, 1], [5, 2], ...]
In my specific example, the first elements in each sub array just ascend linearly, but the second elements are almost random. I want to find or sort the arrays only by the second number. I could just remove the first number from each array:
list = [2, 2, 5, 1, 2 ...]
And then use something like "if list[x] == 1" to find '1' etc.
(side note: I'm not sure how to find ALL the values if one value is repeated - I can't remember quite what I wrote but it would only ever find the first instance where the value matched, so e.g. it would detect the first '2' but not the second or third)
But I want to keep the first values in each array. My friend told me that you could use a dictionary with values and keys, which would work for my example, but I want to know what the more general method would be.
So in my example, I hoped that if I wrote this:
if list[[?, x]] == [?, 1]
Then it would find the array where the second value of the array was 1, (i.e. [4, 1] in my example) and not care about the first value. Obviously it didn't work because '?' isn't Python syntax as far as I'm aware, but hopefully you can see what I'm trying to do?
So for a more general case, if I had a list of 5 dimensional arrays and I wanted to find the second and fourth values of each array, I would write:
if list[[?, x, ?, y, ?]] == [?, a, ?, b, ?]
And it would match any array where the value of the second element was 'a', and the value of the fourth was 'b'.
e.g. [3, a, 4, b, 7], [20, a, 1, b, 9], ['cat', a, 'dog', b, 'fish'] etc. would all be possible results found by the command.
So I want to know if there's any similar way to my method of using a question mark (but that actually works) to denote that an element in an array can have any value.
To sort on the second element for a list containg lists (or tuples):
from operator import itemgetter
mylist = [[1, 2], [2, 2], [3, 5], [4, 1], [5, 2]]
sortedlist = sorted(mylist, key=itemgetter(1))
See the Python sorting howto.
Use sorted if you want to keep original list unaffected
lst = [[1, 2], [2, 2], [3, 5], [4, 1], [5, 2]]
In [103]: sorted(lst, key=lambda x: x[1])
Out[103]: [[4, 1], [1, 2], [2, 2], [5, 2], [3, 5]]
else use list.sort to sort current list and keep sorted list
In [106]: lst.sort(key=lambda x: x[1])
In [107]: lst
Out[107]: [[4, 1], [1, 2], [2, 2], [5, 2], [3, 5]]
or use operator.itemgetter
from operator import itemgetter
In [108]: sorted(lst, key=itemgetter(1))
Out[108]: [[4, 1], [1, 2], [2, 2], [5, 2], [3, 5]]
You could use a list comprehension to build a list of all the desired items:
In [16]: seq = [[1, 2], [2, 2], [3, 5], [4, 1], [5, 2]]
To find all items where the second element is 1:
In [17]: [pair for pair in seq if pair[1] == 1]
Out[17]: [[4, 1]]
This finds all items where the second element is 2:
In [18]: [pair for pair in seq if pair[1] == 2]
Out[18]: [[1, 2], [2, 2], [5, 2]]
Instead of
if list[[?, x, ?, y, ?]] == [?, a, ?, b, ?]
you could use
[item for item in seq if item[1] == 'a' and item[3] == 'b']
Note, however, that each time you use a list comprehension, Python has to loop
through all the elements of seq. If you are doing this search multiple times,
you might be better off building a dict:
import collections
seq = [[1, 2], [2, 2], [3, 5], [4, 1], [5, 2]]
dct = collections.defaultdict(list)
for item in seq:
key = item[1]
dct[key].append(item)
And then you could access the items like this:
In [22]: dct[1]
Out[22]: [[4, 1]]
In [23]: dct[2]
Out[23]: [[1, 2], [2, 2], [5, 2]]
The list comprehension
[pair for pair in seq if pair[1] == 1]
is roughly equivalent to
result = list()
for pair in seq:
if pair[1] == 1:
result.append(pair)
in the sense that result would then equal the list comprehension.
The list comprehension is just a syntactically prettier way to express the same
thing.
The list comprehension above has three parts:
[expression for-loop conditional]
The expression is pair, the for-loop is for pair in seq, and the conditional is if pair[1] == 1.
Most, but not all list comprehensions share this syntax. The full list comprehension grammar is given here.
to get right down to it, I'm trying to iterate through a list of coordinate pairs in python and delete all cases where one of the coordinates is negative. For example:
in the array:
map = [[-1, 2], [5, -3], [2, 3], [1, -1], [7, 1]]
I want to remove all the pairs in which either coordinate is < 0, leaving:
map = [[2, 3], [7, 1]]
My problem is that python lists cannot have any gaps, so if I loop like this:
i = 0
for pair in map:
for coord in pair:
if coord < 0:
del map[i]
i += 1
All the indices shift when the element is deleted, messing up the iteration and causing all sorts of problems. I've tried storing the indices of the bad elements in another list and then looping through and deleting those elements, but I have the same problem: once one is gone, the whole list shifts and indices are no longer accurate.
Is there something I'm missing?
Thanks.
If the list is not large, then the easiest way is to create a new list:
In [7]: old_map = [[-1, 2], [5, -3], [2, 3], [1, -1], [7, 1]]
In [8]: new_map=[[x,y] for x,y in a_map if not (x<0 or y<0)]
In [9]: new_map
Out[9]: [[2, 3], [7, 1]]
You can follow this up with old_map = new_map if you want to discard the other pairs.
If the list is so large creating a new list of comparable size is a problem, then you can delete elements from a list in-place -- the trick is to delete them from the tail-end first:
the_map = [[-1, 2], [5, -3], [2, 3], [1, -1], [7, 1]]
for i in range(len(the_map)-1,-1,-1):
pair=the_map[i]
for coord in pair:
if coord < 0:
del the_map[i]
print(the_map)
yields
[[2, 3], [7, 1]]
PS. map is such a useful built-in Python function. It is best not to name a variable map since this overrides the built-in.
You can use list comprehension for this:
>>> mymap = [[-1, 2], [5, -3], [2, 3], [1, -1], [7, 1]]
>>> mymap = [m for m in mymap if m[0] > 0 and m[1] > 0]
>>> mymap
[[2, 3], [7, 1]]
If you do not have any other references to the map list, a list comprehension works best:
map = [[a,b] for (a,b) in map if a > 0 and b > 0]
If you do have other references and need to actually remove elements from the list referenced by map, you have to iterate over a copy of map:
for coord in map[:]:
if coord[0] < 0 or coord[1] < 0:
map.remove(coord)
Personally, I prefer in-place modification:
li = [[-1, 2], [5, -3], [2, 3], [1, -1], [7, 1]]
print li,'\n'
N = len(li)
for i,(a,b) in enumerate(li[::-1], start=1):
if a<0 or b<0:
del li[N-i]
print li
->
[[-1, 2], [5, -3], [2, 3], [1, -1], [7, 1]]
[[2, 3], [7, 1]]
If you wish to do this in place, without creating a new list, simply use a for loop with index running from len(map)-1 down to 0.
for index in range(len(map)-1,-1,-1):
if hasNegativeCoord(map[index]):
del(map[index])
Not very Pythonic, I admit.
If the list is small enough, it's more efficient to make a copy containing just the elements you need, as detailed in the other answers.
However, if the list is too large, or for some other reason you need to remove the elements from the list object in place, I've found the following little helper function quite useful:
def filter_in_place(func, target, invert=False):
"remove all elements of target where func(elem) is false"
pos = len(target)-1
while pos >= 0:
if (not func(target[pos])) ^ invert:
del target[pos]
pos -= 1
In your example, this could be applied as follows:
>>> data = [[-1, 2], [5, -3], [2, 3], [1, -1], [7, 1]]
>>> def is_good(elem):
return elem[0] >= 0 and elem[1] >= 0
>>> filter_in_place(is_good, data)
>>> data
[[2, 3], [7, 1]]
(This is just a list-oriented version of filter_in_place, one which supports all base Python datatypes is a bit more complex).
itertools.ifilter()/ifilterfalse() exist to do exactly this: filter an iterable by a predicate (not in-place, obviously).
Better still, avoid creating and allocating the entire filtered list object if at all possible, just iterate over it:
import itertools
l = [(4,-5), (-8,2), (-2,-3), (4,7)]
# Option 1: create a new filtered list
l_filtered = list( itertools.ifilter(lambda p: p[0]>0 and p[1]>0, l) )
# Option 2:
for p in itertools.ifilter(lambda p: p[0]>0 and p[1]>0, l):
... <subsequent code on your filtered list>
You probably want del pair instead.