Slice list based on values in another list - python

I have long lists, for example:
list1 = ["a","a","b","b","c"]
list2 = [1, 3, 5, 7, 9]`
How can I slice the second list based on the first list (list1 is sorted by its values)?
Like [[1,3],[5,7],[9]]
My lists are very long so I am looking for a fast way to precess this. Thanks!

With itertools.groupby() and a bit of effort.
>>> [[b[1] for b in r] for p,r in itertools.groupby(zip(list1, list2), operator.itemgetter(0))]
[[1, 3], [5, 7], [9]]
Use itertools.izip() instead of zip() if you're running 2.x.

You could also just use a dictionary:
from collections import defaultdict
list1 = ["a","a","b","b","c"]
list2 = [1, 3, 5, 7, 9]
d = defaultdict(list)
for k, v in zip(list1, list2):
d[k].append(v)
>>> print([lst for lst in sorted(d.values())])
[[1, 3], [5, 7], [9]]

Related

Comparing the 1st element of list of lists to another list then adding only the lists that match to a new list

My aim is to compare the 1st element of a list of lists with another list and have the lists added to a new list if the 1st elements of both lists match. So for instance,
list1 = [2,4,7,10]
list2 = [[2,5],[3,7],[1,6],[10,3]]
newlist = [[2,5],[10,3]]
What if the list1 items does NOT have exactly sequence matching list2?
Let's assume list1 has [2, 7, 10, 4], and list2 is the same as posted sample.
Using pure zip won't get what you expect!
Alternatively, you could try this approach: (it works w/o assuming there is a matching ordering relationship between the two lists)
L = [2, 7, 4, 10]
M = [[10, 1], [3, 5], [2, 8], [4, 6], [10, 10]]
result = []
for ll in M:
if ll[0] in set(L):
result.append(ll)
result.append(ll)
print(result)
# [[10, 1], [2, 8], [4, 6], [10, 10]]
You can use zip():
newlist = []
for a, b in zip(list1, list2):
if a == b[1]:
newlist.append(b)
Or with a generator expression:
newlist = [b for a, b in zip(list1, list2) if a == b[1]]
I would use a list comprehension:
[snd for fst, snd in zip(list1, list2) if fst == snd[0]]
This outputs:
[[2, 5], [10, 3]]

Unpack a single element from a list of lists python

I have zipped some data into as following:
list1 = [1,33,3]
list2 = [2,44,23]
list3 = [[3,4,5,6],[3,4,5,3],[4,5,3,1]]
list4 = [4,34,4]
data = [list(x) for x in zip(list1, list2, list3, list4)]
However, list3 is a list of lists. So the output ended up being
[[1,2,[3,4,5,6],4],
[33,44,[3,4,5,3],34],
[3,23,[4,5,3,1],4]]
I would like to unpack the list inside the lists like the following:
[[1,2,3,4,5,6,4],
[33,44,3,4,5,3,34],
[3,23,4,5,3,1,4]]
What is the best way to do that in python?
Cheers
If only two level-deep, you can try with first principles:
out = [
[e for x in grp for e in (x if isinstance(x, list) else [x])]
for grp in zip(list1, list2, list3, list4)
]
>>> out
[[1, 2, 3, 4, 5, 6, 4], [33, 44, 3, 4, 5, 3, 34], [3, 23, 4, 5, 3, 1, 4]]
Exploring concepts
First, we have to know what we should do.
The problem is only because list3 is a nested list and this is causing us problems.
To come up with a solution we have to think on how to turn list3 from a nested to a normal list.
To point out:
the shape of list3 is well-defined and it's always the same.
list3 is nested hence we have to think methods to flatten the list.
Finally I come up with 2 possible methods
Flattening list3
I would suggest to flatten list3, using itertools.chain.from_iterable.
chain.from_iterable(iterable)
Alternate constructor for chain(). Gets chained inputs from a single iterable argument that is evaluated lazily.
this can flatten a list of lists.
Here is a possible implementation:
import itertools
list1 = [1,33,3]
list2 = [2,44,23]
list3 = [[3,4,5,6],[3,4,5,3],[4,5,3,1]]
list4 = [4,34,4]
flat_list3 = itertools.chain.from_iterable(list3)
data = [list(x) for x in zip(list1, list2, list3, list4)]
>>> [[1,2,3,4,5,6,4],
[33,44,3,4,5,3,34],
[3,23,4,5,3,1,4]]
Deep nested list flatten
NOTE: This possibility is slower and applicable for nested lists that aren't of a particular shape.
You could use the deepflatten with the map builtin function.
Here is the equivalent to the defined function and arguments.
deepflatten(iterable, depth=None, types=None, ignore=None)
From the docs:
Flatten an iterable with given depth.
>>> from iteration_utilities import deepflatten
>>> data = [[1, 2, [3, 4, 5, 6], 4], [33, 44, [3, 4, 5, 3], 34], [3, 23, [4, 5, 3, 1], 4]]
>>> list(map(lambda x:list(deepflatten(x)),data))
[[1, 2, 3, 4, 5, 6, 4], [33, 44, 3, 4, 5, 3, 34], [3, 23, 4, 5, 3, 1, 4]]
Useful links:
SO(ans) how to find lenght of a list of lists
make a flat list out of lists

Quicker way to join multiple lists passed as args to a Python function?

So I have a function which takes a variable number of lists as an argument, then combines those lists into one single list:
def comb_lists(*lists):
sublist = []
for l in lists:
sublist.extend(l)
print(sublist)
>>> comb_lists([1, 2], [3, 4], [5, 6])
[1, 2, 3, 4, 5, 6]
And it works. But I was just wondering if there was a simpler solution? I tried a list comprehension using list unpacking, but that returned a SyntaxError:
def comb_lists(*lists):
sublist = [*l for l in lists]
>>> comb_lists([1, 2], [3, 4], [5, 6])
SyntaxError: iterable unpacking cannot be used in comprehension
Is there any neater or quicker way to do this?
EDIT: itertools looks really useful for this sort of thing. I'd be interested to know if there's any way of doing it that doesn't rely on imports though.
here is the simplest solution
result = sum(lists, [])
There's built-in function chain.form_iterable() in itertools module to do this:
>>> from itertools import chain
>>> my_list = [[1, 2], [3, 4], [5, 6]]
>>> list(chain.from_iterable(my_list))
[1, 2, 3, 4, 5, 6]
If you do not want to import any module, you can write nested list comprehension to achieve this as:
>>> my_list = [[1, 2], [3, 4], [5, 6]]
>>> [e for l in my_list for e in l]
[1, 2, 3, 4, 5, 6]

function of difference between value

Is there a function in Python to get the difference between two or more values in a list? So, in those two lists:
list1 = [1, 5, 3, 7]
list2 = [4, 2, 6, 4]
I need to calculate the difference between every value in list1 and list2.
for i in list1:
for ii in list2:
print i -ii
This gives negative values, but I want the subtraction between the values of the two lists only from highest value to lowest value for not getting negative values.
For the above lists, I expect the output to be [3, 3, 3, 3].
Thanks.
Assuming you expect [3, 3, 3, 3] as the answer in your question, you can use abs and zip:
[abs(i-j) for i,j in zip(list1, list2)]
Either zip the lists, or use numpy:
>>> list1 = [1, 5, 3, 7]
>>> list2 = [4, 2, 6, 4]
>>> [a-b for a,b in zip(list1, list2)]
[-3, 3, -3, 3]
>>> import numpy as np
>>> np.array(list1) - np.array(list2)
array([-3, 3, -3, 3])
Remember to cast the array back to a list as needed.
edit:
In response to the new requirement that the absolute values are needed: you can add abs in the list comprehension:
>>> [abs(a-b) for a,b in zip(list1, list2)]
[3, 3, 3, 3]
and the numpy solution would change to:
>>> map(abs, np.array(list1) - np.array(list2))
[3, 3, 3, 3]
You may also do if else condition inside list comprehension.
>>> [i-j if i>j else j-i for i,j in zip(list1, list2)]
[3, 3, 3, 3]
You can use zip method in order combine these two lists. See the tutorials for zip method https://docs.python.org/2/library/functions.html#zip
>>> list1 = [1, 5, 3, 7]
>>> list2 = [4, 2, 6, 4]
>>> [abs(x-y) for x, y in zip(list1, list2)]
[3, 3, 3, 3]
Avinash Raj's answer is correct, or alternatively, using map():
from operator import sub
C = map(sub, A, B)

Merge List of lists where sublists have common elements

I have a list of lists like this
list = [[1, 2], [1, 3], [4, 5]]
and as you see the first element of the first two sublists is repeated
So I want my output too be:
list = [[1, 2, 3], [4, 5]]
Thank you
The following code should solve your problem:
def merge_subs(lst_of_lsts):
res = []
for row in lst_of_lsts:
for i, resrow in enumerate(res):
if row[0]==resrow[0]:
res[i] += row[1:]
break
else:
res.append(row)
return res
Note that the elsebelongs to the inner for and is executed if the loop is exited without hitting the break.
I have a solution that builds a dict first with the 1st values, then creates a list from that, but the order may not be the same (i.e. [4, 5] may be before [1, 2, 3]):
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> map(lambda x: d[x[0]].append(x[1]), l)
[None, None, None]
>>> d
defaultdict(<type 'list'>, {1: [2, 3], 4: [5]})
>>> [[key] + list(val) for key, val in d.iteritems()]
[[1, 2, 3], [4, 5]]
You can use python sets, because you can compute intersection and union pretty easy. The code would be more clear, but the complexity would probably be comparable to the other solutions.
Although arguably unreadable:
# Note the _ after the list, otherwise you are redefining the list type in your scope
list_ = [[1, 2], [1, 3], [4, 5]]
from itertools import groupby
grouper = lambda l: [[k] + sum((v[1::] for v in vs), []) for k, vs in groupby(l, lambda x: x[0])]
print grouper(list_)
A more readable variant:
from collections import defaultdict
groups = defaultdict(list)
for vs in list_:
group[vs[0]] += vs[1:]
print group.items()
Note that these solve a more generic form of your problem, instead of [[1, 2], [1, 3], [4, 5]] you could also have something like this: [[1, 2, 3], [1, 4, 5], [2, 4, 5, 6], [3]]
Explanation about the _. This is why you don't want to overwrite list:
spam = list()
print spam
# returns []
list = spam
print list
# returns []
spam = list()
# TypeError: 'list' object is not callable
As you can see above, by setting list = spam we broke the default behaviour of list().

Categories

Resources