Remove a column from a nested list in Python - python

I need help figuring how to work around removing a 'column' from a nested list to modify it.
Say I have
L = [[1,2,3,4],
[5,6,7,8],
[9,1,2,3]]
and I want to remove the second column (so values 2,6,1) to get:
L = [[1,3,4],
[5,7,8],
[9,2,3]]
I'm stuck with how to modify the list with just taking out a column. I've done something sort of like this before? Except we were printing it instead, and of course it wouldn't work in this case because I believe the break conflicts with the rest of the values I want in the list.
def L_break(L):
i = 0
while i < len(L):
k = 0
while k < len(L[i]):
print( L[i][k] , end = " ")
if k == 1:
break
k = k + 1
print()
i = i + 1
So, how would you go about modifying this nested list?
Is my mind in the right place comparing it to the code I have posted or does this require something different?

You can simply delete the appropriate element from each row using del:
L = [[1,2,3,4],
[5,6,7,8],
[9,1,2,3]]
for row in L:
del row[1] # 0 for column 1, 1 for column 2, etc.
print L
# outputs [[1, 3, 4], [5, 7, 8], [9, 2, 3]]

If you want to extract that column for later use, while removing it from the original list, use a list comprehension with pop:
>>> L = [[1,2,3,4],
... [5,6,7,8],
... [9,1,2,3]]
>>>
>>> [r.pop(1) for r in L]
[2, 6, 1]
>>> L
[[1, 3, 4], [5, 7, 8], [9, 2, 3]]
Otherwise, just loop over the list and delete the fields you no longer want, as in arshajii's answer

You can use operator.itemgetter, which is created for this very purpose.
from operator import itemgetter
getter = itemgetter(0, 2, 3) # Only indexes which are needed
print(list(map(list, map(getter, L))))
# [[1, 3, 4], [5, 7, 8], [9, 2, 3]]
You can use it in List comprehension like this
print([list(getter(item)) for item in L])
# [[1, 3, 4], [5, 7, 8], [9, 2, 3]]
You can also use nested List Comprehension, in which we skip the elements if the index is 1, like this
print([[item for index, item in enumerate(items) if index != 1] for items in L])
# [[1, 3, 4], [5, 7, 8], [9, 2, 3]]
Note: All these suggested in this answer will not affect the original list. They will generate new lists without the unwanted elements.

Use map-lambda:
print map(lambda x: x[:1]+x[2:], L)

Here is one way, updated to take in kojiro's advice.
>>> L[:] = [i[:1]+i[2:] for i in L]
>>> L
[[1, 3, 4], [5, 7, 8], [9, 2, 3]]
You can generalize this to remove any column:
def remove_column(matrix, column):
return [row[:column] + row[column+1:] for row in matrix]
# Remove 2nd column
copyofL = remove_column(L, 1) # Column is zero-base, so, 1=second column

when you do the del it will delete that index and reset the index, so you have to reduce that index. Here I use the count to reduce and reset the same from the index list we have. Hope this helps. Thanks
nested_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
remove_cols_index = [1,2]
count = 0
for i in remove_cols_index:
i = i-count
count = count+1
del nested_list[i]
print (nested_list)

[j.pop(1) for j in nested_list]
from https://www.geeksforgeeks.org/python-column-deletion-from-list-of-lists/

Related

Finding an element in nested python list and then replacing it

I have a nested list and I am trying to replace a certain element of the list with something else.
NL = [[1,2,3],
[4,5,6],
[7,8,9]];
Now, I need to update the list, let's say the user wants to change element at NL[1][1] (i.e. 5) to 'X'.
NL will be updated as
NL = [[1,2,3],
[4,'X',6],
[7,8,9]];`
I am having trouble trying to find the position of the element and then changing it. Any help is much appreciated.
Thanks
Using numpy:
NL = np.array(NL)
mask = np.where(NL == 5)
NL[mask] = 10
array([[ 1, 2, 3],
[ 4, 10, 6],
[ 7, 8, 9]])
Solution2:
def get_index(num, List):
for row, i in enumerate(List):
if num in i:
return row, i.index(num)
return -1
idx = get_index(5,NL)
if idx>0:
NL[idx[0]][idx[1]] = 7
[[1, 2, 3], [4, 7, 6], [7, 8, 9]]
Use 2 indexes, 1 for the what nested list you want and one for what element of the nested list you want.
So in this case you want the 2nd list's 2nd element:
NL[1][1]='X'
Output:
[[1, 2, 3], [4, 'X', 6], [7, 8, 9]]
Let's say you need to find the element 5 and want to replace it with 10.
We iterate through the outer list and then each inner-list's elements. Once we find the element we look for, we can replace it by the indexes. We use enumerate to have the indexes once we find a matching element.
The following code replaces ALL matching elements (all occurences of 5).
NL = [[1,2,3], [4,5,6], [7,8,9]]
print(NL) # prints: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for i, sublist in enumerate(NL):
for y, element in enumerate(sublist):
if element == 5:
NL[i][y] = 10
print(NL) # prints: [[1, 2, 3], [4, 10, 6], [7, 8, 9]]
This will replace only the first occurrence of item_to_replace. If you want it to replace in all sublist then remove the break statement from try block.
item_to_replace = 5
for lst in NL:
try:
index = lst.index(item_to_replace)
lst[index] = # your replacement for item_to_replace
break
except ValueError:
continue
You should access to element by indexes. You have 2D list (array) so you should use 2 indexes: NL[1][1] = "X".
Complete code:
NL = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
print("Original: {}".format(NL))
NL[1][1] = "X"
print("New: {}".format(NL))
Output:
>>> python3 test.py
Original: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
New: [[1, 2, 3], [4, 'X', 6], [7, 8, 9]]
just use NL[1][1] = 'X'
then print(NL)
I am having trouble trying to find the position of the element and then changing it.
Most of the answers here seem to have missed that part, and assumed you had the position.
You can use a nested list comprehension:
NL = [[1,2,3],
[4,5,6],
[7,8,9]]
NL = [['X' if i==5 else i for i in j] for j in NL]
print(NL)
Output:
[[1, 2, 3],
[4,'X',6],
[7, 8, 9]]

Sorting elements in a list of lists

Hey I have a list that goes like:
List = [[[1,2],[4,5,3]],[[5,2],[3,4,7]],...]
and I want to sort the elements of each sublist, that the output looks like:
List = [[[2,1],[5,4,3]],[[5,2],[7,4,3]],...]
What I have already tried is creating a loop like this.
for i in List:
for u in [i]:
List[u].sort(reverse = True)
print(List)
but it didnt worked. In other Blogposts I just found ways how to sort the lists, but not the elements in them.
Thanks for your help
You are close, just a few things wrong:
for u in [i]: Do not wrap i in brackets. You are just creating a new list with i as its only element, and looping over that which is not what you want.
Second:
List[u].sort(reverse = True) You do not need to do List[u], rather just u, because inside of this loop, u is equal to the sublist that you want to sort
Also a side note, variables should be lowercase
myList = [[[1,2],[4,5,3]],[[5,2],[3,4,7]]]
for i in myList:
for u in i:
u.sort(reverse=True)
print(myList)
You may use a list comprehension, to easily retrieve the values, iterate over each sublist and sort the subsublist
values = [[[1, 2], [4, 5, 3]], [[5, 2], [3, 4, 7]]]
values = [[sorted(subsub, reverse=True) for subsub in sub] for sub in values]
print(values) # [[[2, 1], [5, 4, 3]], [[5, 2], [7, 4, 3]]]
Or with loops, you can just access the inner lists with the second loop and inline sort them
for sub in values:
for subsub in sub:
subsub.sort(reverse=True)
Note: use meaningful names to your variables
You are close with your approach although in your second loop you’re enclosing i in a list which won’t loop through each sub list and you only need to sort u as you’re not going to slice a list with a list.
for i in List:
for u in i:
u.sort(reverse = True)
print(List)
Result:
[[[2, 1], [5, 4, 3]], [[5, 2], [7, 4, 3]]]
PS If there could be only one layer of nesting then I'd suggest using isinstance to check before looping through and error-ing out due to attempting to sort an int
for i in List:
if isinstance(i[0], list):
for u in i:
u.sort(reverse = True)
else:
i.sort(reverse = True)
Another approach using list comprehension (one liner):
li = [[[1,2],[4,5,3]],[[5,2],[3,4,7]]]
li = [[sorted(i, reverse=True) for i in j] for j in li]
print(li)
Output:
[[[2, 1], [5, 4, 3]], [[5, 2], [7, 4, 3]]]
Is this what you are looking for?
for i in List:
for u in i:
u.sort(reverse = True)
You can use Pandas to sort your data.
import pandas as pd
ist = [[[1,2],[4,5,3]],[[5,2],[3,4,7]]]
df = pd.DataFrame(ist)
I named the columns to read the columns easier.
df.columns = ['a', 'b']
Now we can sort the elements of the lists in each,
df['a'] = df.a.apply(lambda x: sorted(x,reverse=True))
df['b'] = df.b.apply(lambda x: sorted(x,reverse=True))
which yields,
print(df.head())
a b
0 [2, 1] [5, 4, 3]
1 [5, 2] [7, 4, 3]

Merge two lists based on condition

I am trying to merge two lists based on position of index, so sort of a proximity intersection.
A set doesn't work in this case. What i am trying to do is match index in each list then if the element is one less than that of the element in other list, only then i collect it.
An example will explain my scenario better.
Sample Input:
print merge_list([[0, 1, 3], [1, 2], [4, 1, 3, 5]],
[[0, 2, 6], [1, 4], [2, 2], [4, 1, 6]])
Sample Output:
[[0,2],[4,6]]
so on position 0 in list1 we have 1, 3 and in list2 we have 2, 6. Since 1 is one less than 2, so we collect that and move on, now 3 is less than 6 but it's not one less than i.e. not 5 so we ignore that. Next we have [1, 2][1, 4], so both index/position 1, but 2 is not one less than 4 so we ignore that. Next we have [2, 2] in list2 both index 2 doesn't match any index in first list so no comparison. Finally we have [4, 1, 3, 5] [4, 1, 6] comparison. Both index match and only 5 in list one is one less than list two so we collect six hence we collect [4,6] meaning index 4 and match etc.
I have tried to make it work, but i don't seem to make it work.
This is my code so far.
def merge_list(my_list1, my_list2):
merged_list = []
bigger_list = []
smaller_list = []
temp_outer_index = 0
temp_inner_index = 0
if(len(my_list1) > len(my_list2)):
bigger_list = my_list1
smaller_list = my_list2
elif(len(my_list2) > len(my_list1)):
bigger_list = my_list2
smaller_list = my_list1
else:
bigger_list = my_list1
smaller_list = my_list2
for i, sublist in enumerate(bigger_list):
for index1 , val in enumerate(sublist):
for k, sublist2 in enumerate(smaller_list):
for index2, val2 in enumerate(sublist2):
temp_outer_index = index1 + 1
temp_inner_index = index2 + 1
if(temp_inner_index < len(sublist2) and temp_outer_index < len(sublist)):
# print "temp_outer:%s , temp_inner:%s, sublist[temp_outer]:%s, sublist2[temp_inner_index]:%s" % (temp_outer_index, temp_inner_index, sublist[temp_outer_index], sublist2[temp_inner_index])
if(sublist2[temp_inner_index] < sublist[temp_outer_index]):
merged_list.append(sublist[temp_outer_index])
break
return merged_list
No clue what you are doing, but this should work.
First, convert the list of lists to a mapping of indices to set of digits contained in that list:
def convert_list(l):
return dict((sublist[0], set(sublist[1:])) for sublist in l)
This will make the lists a lot easier to work with:
>>> convert_list([[0, 1, 3], [1, 2], [4, 1, 3, 5]])
{0: set([1, 3]), 1: set([2]), 4: set([1, 3, 5])}
>>> convert_list([[0, 2, 6], [1, 4], [2, 2], [4, 1, 6]])
{0: set([2, 6]), 1: set([4]), 2: set([2]), 4: set([1, 6])}
Now the merge_lists function can be written as such:
def merge_lists(l1, l2):
result = []
d1 = convert_list(l1)
d2 = convert_list(l2)
for index, l2_nums in d2.items():
if index not in d1:
#no matching index
continue
l1_nums = d1[index]
sub_nums = [l2_num for l2_num in l2_nums if l2_num - 1 in l1_nums]
if sub_nums:
result.append([index] + sorted(list(sub_nums)))
return result
Works for your test case:
>>> print merge_lists([[0, 1, 3], [1, 2], [4, 1, 3, 5]],
[[0, 2, 6], [1, 4], [2, 2], [4, 1, 6]])
[[0, 2], [4, 6]]
I believe this does what you want it to do:
import itertools
def to_dict(lst):
dct = {sub[0]: sub[1:] for sub in lst}
return dct
def merge_dicts(a, b):
result = []
overlapping_keys = set.intersection(set(a.keys()), set(b.keys()))
for key in overlapping_keys:
temp = [key] # initialize sublist with index
for i, j in itertools.product(a[key], b[key]):
if i == j - 1:
temp.append(j)
if len(temp) > 1: # if the sublist has anything besides the index
result.append(temp)
return result
dict1 = to_dict([[0, 1, 3], [1, 2], [4, 1, 3, 5]])
dict2 = to_dict([[0, 2, 6], [1, 4], [2, 2], [4, 1, 6]])
result = merge_dicts(dict1, dict2)
print(result)
Result:
[[0, 2], [4, 6]]
First, we convert your lists to dicts because they're easier to work with (this separates the key out from the other values). Then, we look for the keys that exist in both dicts (in the example, this is 0, 1, 4) and look at all pairs of values between the two dicts for each key (in the example, 1,2; 1,6; 3,2; 3,6; 2,4; 1,1; 1,6; 3,1; 3,6; 5,1; 5,6). Whenever the first element of a pair is one less than the second element, we add the second element to our temp list. If the temp list ends up containing anything besides the key (i.e. is longer than 1), we add it to the result list, which we eventually return.
(It just occurred to me that this has pretty bad performance characteristics - quadratic in the length of the sublists - so you might want to use Claudiu's answer instead if your sublists are going to be long. If they're going to be short, though, I think the cost of initializing a set is large enough that my solution might be faster.)
def merge_list(a, b):
d = dict((val[0], set(val[1:])) for val in a)
result = []
for val in b:
k = val[0]
if k in d:
match = [x for x in val[1:] if x - 1 in d[k]]
if match:
result.append([k] + match)
return result
Similar to the other answers, this will first convert one of the lists to a dictionary with the first element of each inner list as the key and the remainder of the list as the value. Then we walk through the other list and if the first element exists as a key in the dictionary, we find all values that meet your criteria using the list comprehension and if there were any, add an entry to the result list which is returned at the end.

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.

Python: One-liner to perform an operation upon elements in a 2d array (list of lists)?

I have a list of lists, each containing a different number of strings. I'd like to (efficiently) convert these all to ints, but am feeling kind of dense, since I can't get it to work out for the life of me. I've been trying:
newVals = [int(x) for x in [row for rows in values]]
Where 'values' is the list of lists. It keeps saying that x is a list and can therefore not be the argument if int(). Obviously I'm doing something stupid here, what is it? Is there an accepted idiom for this sort of thing?
This leaves the ints nested
[map(int, x) for x in values]
If you want them flattened, that's not hard either
for Python3 map() returns an iterator. You could use
[list(map(int, x)) for x in values]
but you may prefer to use the nested LC's in that case
[[int(y) for y in x] for x in values]
How about:
>>> a = [['1','2','3'],['4','5','6'],['7','8','9']]
>>> [[int(j) for j in i] for i in a]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Another workaround
a = [[1, 2, 3], [7, 8, 6]]
list(map(lambda i: list(map(lambda j: j - 1, i)), a))
[[0, 1, 2], [6, 7, 5]] #output
You simply use incorrect order and parenthesis - should be:
inputVals = [['1','2','3'], ['3','3','2','2']]
[int(x) for row in inputVals for x in row]
Or if you need list of list at the output then:
map(lambda row: map(int, row), inputVals)
an ugly way is to use evalf:
>>> eval(str(a).replace("'",""))
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
if you don't mind all your numbers in one array you could go:
>>> a = [['1','2','3'],['4','5','6'],['7','8','9']]
>>> map(int,sum(a,[]))
[1, 2, 3, 4, 5, 6, 7, 8, 9]
In order to map list with any number of dimensions you could use numpy.apply_over_axes
import numpy as np
np.apply_over_axes(lambda x,_:x*2, np.array([[1,2,3],[5,2,1]]),[0])
--------------------
array([[ 2, 4, 6],
[10, 4, 2]])
Unfortunately that doesn't work if you also need to change variable type. Didn't find any library solution for this, so here is the code to do that:
def map_multi_dimensional_list(l, transform):
if type(l) == list and len(l) > 0:
if type(l[0]) != list:
return [transform(v) for v in l]
else:
return [map_multi_dimensional_list(v, transform) for v in l]
else:
return []
map_multi_dimensional_list([[[1,2,3],[5,2,1]],[[10,20,30],[50,20,10]]], lambda x:x*2)
------------------
[[[2, 4, 6], [10, 4, 2]], [[20, 40, 60], [100, 40, 20]]]

Categories