Python: Concatenating sublists between different lists - python

I have two lists of lists, of equal length, like this:
lstA = [[1,4,5,6],[4,5],[5,6],[],[],[],[],[]]
lstB = [[7,8],[4,5],[],[],[],[2,7,8],[7,8],[6,7]]
And I want to concatenate the sublists at each index position such that they make a single sublist, like this:
newlst = [[1,4,5,6,7,8],[4,5],[5,6],[],[],[2,7,8],[7,8],[6,7]]
Ideally, the new sublists will remove duplicates (like in newlst[1]). I converted the integers to strings, and attempted this:
for i in range(len(lstA)):
c = [item + item for item in strA[i], strB[i]]
but that adds each item from each list to itself before adding to the other list, resulting in something like this:
failedlst = [[["1","4","5","6","1","4","5","6"],["7","8","7","8"]],[["4","5","4","5"],["4","5","4","5"]]...etc]
And this still doesn't actually join the two sublists, just makes a new sublist of the two sublists. Any help would be greatly appeciated!

Making a list by concatenating items in parallel is very simple, using a list comprehension in combination with the zip function.
newlst = [x+y for x,y in zip(lstA, lstB)]
If you want to remove duplicates, you can use a set. If you then want to put the items back in order in a list, you can use sorted.
In combination, this:
newlst = [sorted(set(x+y)) for x,y in zip(lstA, lstB)]

You could use:
lstA = [[1,4,5,6],[4,5],[5,6],[],[],[],[],[]]
lstB = [[7,8],[4,5],[],[],[],[2,7,8],[7,8],[6,7]]
answer = []
for idx in range(len(lstA)):
answer.append(sorted(list(set(lstA[idx]+lstB[idx]))))
print(answer)
Output
[[1, 4, 5, 6, 7, 8], [4, 5], [5, 6], [], [], [2, 7, 8], [7, 8], [6, 7]]

Using zip and chain.from_iterable from the itertools module.
In [94]: from itertools import chain
In [95]: lstA = [[1,4,5,6],[4,5],[5,6],[],[],[],[],[]]
In [96]: lstB = [[7,8],[4,5],[],[],[],[2,7,8],[7,8],[6,7]]
In [97]: [list(set(chain.from_iterable(item))) for item in zip(lstA, lstB)]
Out[97]: [[1, 4, 5, 6, 7, 8], [4, 5], [5, 6], [], [], [8, 2, 7], [8, 7], [6, 7]]
If you want to sort the sublist then:
In [98]: [sorted(set(chain.from_iterable(item))) for item in zip(lstA, lstB)]
Out[98]: [[1, 4, 5, 6, 7, 8], [4, 4, 5, 5], [5, 6], [], [], [2, 7, 8], [7, 8], [6, 7]]

Related

Python - delete columns in 2D list

I have a 2D list = [[1, 8, 3], [4, 5, 6], [0, 5, 7]], and I want to delete columns in a loop.
For example, columns with index: 0(first) and 2(last) - - the result after deletions should be: [8, 5, 5].
There is a problem, because when I delete the 0th column, the size of the list is decreased to (0,1), and the 2nd index is out of scope.
What is the fastest method to delete columns in a loop without the out-of-scope problem?
For a better picture:
[[1, 8, 3],
[4, 5, 6],
[0, 5, 7]]
There is no such shortcut in python except for iterating over all the list items and removing those index values.
However, you can use pandas which is meant for some other purpose but will do the task.
import pandas as pd
s = [[1, 8, 3], [4, 5, 6], [0, 5, 7]]
df = pd.DataFrame(s,columns=['val1','val2','val3'])
li = df.drop('val1',axis=1).values.tolist()
now li will look like this
[[8, 3], [5, 6], [5, 7]]
You can use numpy like this:
import numpy as np
my_list = np.array([[1, 8, 3], [4, 5, 6], [0, 5, 7]])
new_list = my_list[:, 1].copy()
print(new_list)
Output:
>>> [8, 5, 5]
Also numpy.delete(your_list, index, axis) is do the same job:
new_list = np.delete(my_list,(0, 2), axis=1)
(0, 2) is the indices of the columns 0 and 2
axis=1 says numpy that (0, 2) are columns indices not rows.
if you want to delete rows 0 and 2 you can change axis=1 to axis=0
Output is a little different:
>>> array([[8],
[5],
[5]])
For a pure python approach:
my_list = [[1, 8, 3], [4, 5, 6], [0, 5, 7]]
new_list = [value[1] for value in my_list]
print(new_list)
Output:
>>> [8, 5, 5]
L is 2D list:
print(map(lambda x: x[1:], L))
data= [[1, 8, 3], [4, 5, 6], [0, 5, 7]]
index_to_remove=[0,2]
[list(x) for x in zip(*[d for i,d in enumerate(zip(*data)) if i not in index_to_remove])]
If I understood your question correctly, you want to keep the middle element (index 1) of each list,in that case I would suggest creating a new list. There could be other better ways, for sure. But you could try this, if this works for you:
twoD_list = [[1, 8, 3], [4, 5, 6], [0, 5, 7]]
def keep_col( twoD_list ,index_to_keep = 1):
final_list = []
for x in twoD_list:
final_list.append(x[index_to_keep])
return final_list
final_list = keep_col( twoD_list , 1)
Final output:
[8,5,5]
Assuming you always want only the second element and the inner lists always have at least two elements.
Pure python with list comprehension:
lst = [
[1, 8, 3],
[4, 5, 6],
[0, 5, 7],
]
filtered_lst = [
inner_element
for inner_lst in lst
for i, inner_element in enumerate(inner_lst)
if i == 1
]
print(filtered_lst)
# [8, 5, 5]
If you want you can the reassign the new list to the old variable:
lst = filtered_lst
The advantages of this method are:
no need to worry about the list being altered while you iterate it,
no need to import other libraries
list comprehension is built-in
list comprehension is often the fastest way to filter a list (see for example this article)
easier to read and maintain that other solutions (in my opinion).
Via itemgetter to extract the value at index 1.
from operator import itemgetter
my_list = [[1, 8, 3], [4, 5, 6], [0, 5, 7]]
result = list(map(itemgetter(1), my_list))
try this
my_list = [[1, 8, 3], [4, 5, 6], [0, 5, 7]]
filter_col=[0,2]
col_length=3
my_list=[[x[i] for i in range(col_length) if i not in filter_col] for x in my_list]
u do not want to directly mutate the list that you are working on
this performs a list comprehension to create a new list from the existing list
edit:
just saw u wanted only a flat list
assuming u only want one element for the list u can use
my_list=[x[1] for x in my_list]

List comprehension in Python under specific circumstances

lst_a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
lst_b = [[1, 4, 7], [6, 5, 4], [9, 8, 7]]
My goal is to check all nested lists in lst_a if the first entry == first entry of any element in lst_b. If it's not than copy ONLY THAT sublist. In this example he wouldn't copy lst_a[0] but 1 and 2.
I tried to achieve my goal with list comprehension but it won't work.
zero = [x[0] for x in lst_a]
if zero not in lst_b:
# I don't know what to do here.
Creating a tuple or a dictionary isn't possible because the whole process is in a loop in which every second new data come in and I try to avoid copying duplicates to the list.
EDIT: lst_b should look like that after the whole process:
lst_b = [[1, 4, 7], [6, 5, 4], [9, 8, 7], [4, 5, 6], [7, 8, 9]]
Extract all the first elements from lst_b into a set so you can check membership efficiently. Then use a list comprehension to copy all the sublists in lst_a that match your criteria.
first_elements = {x[0] for x in lst_b}
result = [x for x in lst_a if x[0] not in first_elements]
It's a bit of a mouthful, but not too bad:
lst_b.extend(x for x in lst_a if not any(x[0] == y[0] for y in lst_b)
If you want a new list rather than modifying lst_b in place, then
lst_c = lst_b + [x for x in lst_a if not any(x[0] == y[0] for y in lst_b)]
In either case, we examine each sublist x in lst_a. any(x[0] == y[0] for y in lst_b) is True if the first element of the sublist is equal to the first element of any sublist in lst_b. If that's not true, then we'll include x in our final result.
Using any allows us to avoid checking against every sublist in lst_b when finding a single match is sufficient. (There are cases where this could be more efficient than first creating an entire set of first elements, as in #barmar's answer, but on average that approach is probably more efficient.)
Another way:
exclude=set(next(zip(*lst_b)))
lst_b+=[sl for sl in lst_a if sl[0] not in exclude]
>>> lst_b
[[1, 4, 7], [6, 5, 4], [9, 8, 7], [4, 5, 6], [7, 8, 9]]
Explanation:
zip(*lst_b) is a generator of the inverse of the matrix lst_b, The * expands the sub lists and this creates a generator that yields [(1, 6, 9), (4, 5, 8), (7, 4, 7)] in turn.
next(zip(*lst_b) we only need the first element of that inverse: (1,6,9)
set(next(zip(*lst_b))) only need the uniq elements of that so turn into a set. You get {1, 6, 9} (order does not matter)
[sl for sl in lst_a if sl[0] not in exclude] filter on that condition.
lst_b+= extend lst_b with the filtered elements.
Profit!
There may be more efficient ways of doing this, but this accomplishes the goal.
>>> [a for a in lst_a if a[0] not in [b[0] for b in lst_b]]
[[4, 5, 6], [7, 8, 9]]

Split a list into multiple ones

I am trying to split a list into multiple ones. The original list lst should be split on every element that is present in a second list, split_on.
Example:
lst = [1, 2, 3, 4, 5, 6, 7, 8]
split_on = [3, 4, 7]
should yield:
[[1,2,3],[3,4],[4,5,6,7],[7,8]]
Note that both lst and split_on do not contain duplicate elements and that any item of split_on is also an item of lst. Finally, the order of the elements of split_on can be random.
How about the following:
a = [1,2,3,4,5,6,7,8]
b = [4,3,7]
res = [[]]
for i in a:
res[-1].append(i)
if i in b:
res.append([i])
print(res) # [[1, 2, 3], [3, 4], [4, 5, 6, 7], [7, 8]]
Note that since b is only used for membership tests and the order does not matter, you could consider converting it to a set so that the approach scales better.

How to prepend an element to each list in a list

I have a list of lists:
x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
I want to prepend a string 'a' to each list in the list such that x becomes:
[['a', 1, 2, 3], ['a', 4, 5, 6], ['a', 7, 8, 9]]
What is the most pythonic way of achieving this?
What I've got so far:
[l.insert(0, 'a') for l in x]
Just concat what you want to prepend:
[['a'] + l for l in x]
# [['a', 1, 2, 3], ['a', 4, 5, 6], ['a', 7, 8, 9]]
Prepending to a list is expensive (O(n)), because you need to move every element of the list to make room for the new item. (Appending, by contrast, is cheap.)
If this is something you need to do often for these lists, consider using a deque instead, which is optimized to support appending and prepending efficiently.
from collections import deque
x = [deque([1,2,3]), deque([4,5,6]), deque([7,8,9])]
for d in x:
d.appendleft('a')

Generator to Yield a List

I have three lists of lists, and I'm trying to write a generator function to help me package up values in the same index.
So my lists:
list1 = [[1, 2, 3], [2, 3, 4],...]
list2 = [[4, 5, 6], [5, 6, 7],...]
list3 = [[8, 9, 10], [9, 10, 11],...]
My desired output:
result1 = [[1, 4, 8], [2, 5, 9],...]
result2 = [[2, 5, 9], [3, 6, 10],...]
result3 = [[3, 6, 10], [4, 7, 11],...]
My attempt:
def bundle(x, y, z, index):
for row in x, y, z:
for item in row[index]:
yield list(item)
I keep getting float is not iterable errors. If i modify it slightly:
def bundle(x, y, z, index):
for row in x, y, z:
for item in row:
yield item[index]
I get the values I want as one large sequence, but I would prefer to keep them grouped in a nested style.
If you're dealing with large lists, a custom, fully lazy approach would be the following:
import itertools as it
def bundle(lists, index):
return ([b[index] for b in blocks] for blocks in it.izip(*lists))
print list(bundle([[[1, 2, 3], [2, 3, 4]],
[[4, 5, 6], [5, 6, 7]],
[[8, 9, 10], [9, 10, 11]]],
0))
# => [[1, 4, 8], [2, 5, 9]]
One way to do this is by a repeated application of zip():
res1, res2, res3 = zip(*(zip(*x) for x in zip(list1, list2, list3)))
This uses zip(list1, list2, list3) to create a sequence of matrices, zip(*x) to transpose each of this matrices, and a final zip() to unpack to the three resulting sequences. (I don't think this approach is very efficient.)
If you're dealing with numeric values then you could use numpy's transposition method to achieve what you want:
import numpy
numpy.array([list1,list2, list3]).T

Categories

Resources