Grouping Elements of Lists Within a List by Index - python

I am trying to take a list of lists, and return a list of lists which contain each element at an index of the original list of lists. I know that that's badly worded. Here's an example.
Say I have the following list of lists:
[[1,2,3], [4,5,6], [7,8,9]]
I want to get another list of lists, in which each list is a list of each elements at a specific index. For example:
[[1,2,3], [4,5,6], [7,8,9]] becomes [[1,4,7], [2,5,8], [3,6,9]]
So the first list in the returned list, contains all of the elements at the first index of each of the original list, and so on. I'm stuck, and have no idea how this could be done. Any help would be appreciated. Thanks.

>>> [list(t) for t in zip(*[[1,2,3], [4,5,6], [7,8,9]])]
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]

perhaps an easy way wiould be:
a=[[1,2,3], [4,5,6], [7,8,9]]
b=zip(*a)
b will be equal to [(1, 4, 7), (2, 5, 8), (3, 6, 9)].
hopes this helps

Dan D's answer is correct and will work in Python 2.x and Python 3.x.
If you're doing lots of matrix operations, not just transposition, it's worth considering Numpy at this juncture:
>>> import numpy as np
>>> x = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
>>> np.swapaxes(x, 0, 1)
array([[1, 4, 7],
[2, 5, 8],
[3, 6, 9]])
Or, more simply, as per the comments, use numpy.transpose():
>>> np.transpose(x)
array([[1, 4, 7],
[2, 5, 8],
[3, 6, 9]])

In addition to the zip(*lists) approach, you could also use a list comprehension:
[[l[i] for l in lists] for i in range(3)]

In case you want to simply transpose your matrix -e.g. to get new matrix where rows are cols from initial matrix while columns equal to the initial matrix rows values then you can use:
initMatr = [
[1,2,3],
[4,5,6],
[7,8,9]
]
map(list, zip(*initMatr))
>>> [
[1,4,7],
[2,5,8],
[3,6,9]
]
OR in case you want to rotate matrix left then:
map(list, zip(*map(lambda x: x[::-1], initMatr)
>>> [
[3,6,9],
[2,5,8],
[1,4,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]]

How can I turn two lists of equal lengths into a list of nested lists that represent ordered pairs?

For example, if these are my two lists:
a=[1,2,3,4,5]
b=[6,7,8,9,10]
Then what I'm trying to do is to figure out a way to get:
c=[[1,6],[2,7],[3,8],[4,9],[5,10]]
Sorry for the probably basic question. These are numpy arrays if that makes a difference.
If you want a numpy array as a result, you can build it using array.T:
In [15]: a=np.array([1,2,3,4,5])
In [16]: b=np.array([6,7,8,9,10])
In [17]: np.array([a,b]).T
Out[17]:
array([[ 1, 6],
[ 2, 7],
[ 3, 8],
[ 4, 9],
[ 5, 10]])
Reference: What is the equivalent of "zip()" in Python's numpy?
One approach is using list comprehension and zip:
>>> [[i, j] for i, j in zip(a,b)]
[[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]]
I don't use numpy, but maybe by using zip:
>>> a=[1,2,3,4,5]
>>> b=[6,7,8,9,10]
>>> list(zip(a,b))
[(1, 6), (2, 7), (3, 8), (4, 9), (5, 10)]
It returns a list of tuples though.
Just use np.transpose:
>>> np.transpose([a, b])
array([[ 1, 6],
[ 2, 7],
[ 3, 8],
[ 4, 9],
[ 5, 10]])
If you want the result as list just call tolist() afterwards:
>>> np.transpose([a, b]).tolist()
[[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]]
d = []
for i in range(0, 5):
d.append([a[i], b[i])
Is a simple way to create a new 2D list with element pairs. Using the zip() function as others have pointed out is also viable.
I'm pretty sure there is an easier or more pythonic way than this.
c = [list(x) for x in zip(a,b)]
This outputs a list of lists, instead of just doing list(zip(a,b) that outputs a list of tuples. This combines list comprehension and zip
Also avoids the tuple unpacking of [[i,j] for i,j in zip(a,b)]
not sure whats more efficient though
zip(*iterables) Make an iterator that aggregates elements from each of the iterables.
https://docs.python.org/3/library/functions.html#zip
There are multiple ways to do this.
a = [1, 2, 3, 4, 5]
b = [6, 7, 8, 9, 10]
If you just need to access the elements and not modify them, you can use the zip function:
zip(a, b)
>[(1, 6), (2, 7), (3, 8), (4, 9), (5, 10)]
If you actually need a list of lists, then you can use a list comprehension:
[[a[i], b[i]] for i in range(len(a))]
>[[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]]
And finally, if you need a numpy array as a result, use the Transpose function:
import numpy as np
np.concatenate([[a], [b]]).T
>array([[1, 6],
[2, 7],
[3, 8],
[4, 9],
[5, 10]])
This will give you what you want.
a = [1,2,3,4,5]
b = [6,7,8,9,10]
c = [list(x) for x in zip(a, b)]
with no libraries, you could use:
a = [1,2,3,4,5]
b = [6,7,8,9,10]
c = [[a[i],b[i]] for i in range(len(a))]

How to separate last column from 2D-array in Python?

I need to separate 2D array in order to have last column and the rest separately. Those must be arrays both.
Does this help?
>>> a = [[1,2,3], [4,5,6], [7,8,9]]
>>> l = [row[:-1] for row in a]
>>> r = [row[-1] for row in a]
>>> l
[[1, 2], [4, 5], [7, 8]]
>>> r
[3, 6, 9]

How to slice a list that is sliced in python?

I have a list of let's say [[1,2,3,4], [1,2,3,4], [1,2,3,4], [1,2,3,4]] and its name is somelist. I want to do somelist[:2][:2] and have that return [[1,2], [1,2]]. So it slices the list and then slices that list and returns that. Is it even possible to do this in one line in python?
You can do this using list comprehension. I changed your sample input to make it clearer what is being selected:
>>> l = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
>>> print [x[:2] for x in l[:2]]
[[1, 2], [5, 6]]
Multidimensional slicing is easy with numpy:
import numpy
a = numpy.array([[1,2,3,4], [1,2,3,4], [1,2,3,4], [1,2,3,4]])
print(a[:2,:2])
Are your lists always 16 elements in a 4x4 matrix? If you are simply trying to get the 4 lower right corner elements, try:
>>> l = [[1,2,3,4], [1,2,3,4], [1,2,3,4], [1,2,3,4]]
>>> print [l[2][:2], l[3][:2]]
[[1, 2], [1, 2]]
Otherwise, for a general solution the list comprehension mentioned by others is better.
It would be interesting to benchmark both and see which is faster for your use case. Though this sounds like premature optimization to me.

Categories

Resources