8

I am trying to remove items from a nested list in Python. I have a nested list as follows:

families = [[0, 1, 2],[0, 1, 2, 3],[0, 1, 2, 3, 4],[1, 2, 3, 4, 5],[2, 3, 4, 5, 6]]

I want to remove the entries in each sublist that coorespond to the indexed position of the sublist in the master list. So, for example, I need to remove 0 from the first sublist, 1 from second sublist, etc. I am trying to use a list comrehension do do this. This is what I have tried:

familiesNew = [ [ families[i][j] for j in families[i] if i !=j ] for i in range(len(families)) ] 

This works for range(len(families)) up to 3, however beyond that I get IndexError: list index out of range. I am not sure why. Can somebody give me an idea of how to do this. Preferably a one-liner (list comprehension).

Thanks.

4 Answers 4

10

You almost got it right. Just replace families[i][j] with j and it works:

>>> [ [ j for j in families[i] if i !=j ] for i in range(len(families)) ]
[[1, 2], [0, 2, 3], [0, 1, 3, 4], [1, 2, 4, 5], [2, 3, 5, 6]]

It can be written a bit cleaner using the enumerate function:

>>> [[f for f in family if f != i] for i, family in enumerate(families)]
[[1, 2], [0, 2, 3], [0, 1, 3, 4], [1, 2, 4, 5], [2, 3, 5, 6]]

Or even using remove if you don't mind changing the original list:

>>> for i, family in enumerate(families): family.remove(i)
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, I decided to go with the last option as it was in fact okay and desirable to change the list in place.
5

Edited question, removing my answer which was solving the wrong problem. Also, added additional answer by @Ashwini:

For comparison's sake:

root# python -m timeit 'families = [[0, 1, 2],[0, 1, 2, 3],[0, 1, 2, 3, 4],[1, 2, 3, 4, 5],[2, 3, 4, 5, 6]]' '[x.remove(ind) for ind,x in enumerate(families) ]'
100000 loops, best of 3: 3.42 usec per loop    

root# python -m timeit -s 'families = [[0, 1, 2],[0, 1, 2, 3],[0, 1, 2, 3, 4],[1, 2, 3, 4, 5],[2, 3, 4, 5, 6]]' '[[f for f in family if f != i] for i, family in enumerate(families)]'
100000 loops, best of 3: 4.87 usec per loop

root# python -m timeit -s 'families = [[0, 1, 2],[0, 1, 2, 3],[0, 1, 2, 3, 4],[1, 2, 3, 4, 5],[2, 3, 4, 5, 6]]' '[ filter(lambda x:x!=i,j) for i,j in enumerate(families) ]'
100000 loops, best of 3: 7.99 usec per loop

These are micro-second, so I think whatever you want to do is fine unless you are going to be doing this a lot of times.

3 Comments

I think the semantics of this is not quite right. It's not that the element at the index i should be removed from every sublist, but the item i itself.
This is a clever trick, but I think it doesn't quite do what it is intended to do. (It would work if the latter elements of the outer list were sequences starting at 0). +1 for timeit though.
My mistake for misreading the intent of the question (and subsequently for not checking the output to match)
3

Does this do what you want?

familiesNew=[ filter(lambda x:x!=i,j) for i,j in enumerate(families) ]

EDIT

Also note, the reason yours failed is because at the third element of the outer list ([1, 2, 3, 4, 5]) you're trying to get the fifth element in your for loop (for j in families[i] == for j in [1,2,3,4,5]), but families[i] has a length of 5, meaning the largest index is 4. Sorry if that explanation is a little unclear...perhaps the following will help clear it up a little:

families = [[0, 1, 2],[0, 1, 2, 3],[0, 1, 2, 3, 4],[1, 2, 3, 4, 5],[2, 3, 4, 5, 6]]

def f(i,j):
    print i,j,families[i]
    return families[i][j]
#THIS DOES NOT WORK -- but it will tell you where it failed.
familiesNew = [ [ f(i,j) for j in families[i] if i !=j ] for i in range(len(families)) ] 

Comments

2

If you want to modify the original list then try this:

>>>[x.remove(ind) for ind,x in enumerate(families) ]
>>>families
[[1, 2], [0, 2, 3], [0, 1, 3, 4], [1, 2, 4, 5], [2, 3, 5, 6]]

3 Comments

This answer is interesting because it does the calculation in place. Presumably it would return a list of None ... but that might be OK for some applications.
yes! this list comprehension will return a list of None of length == len(families) and will modify the original list.
good solution, though usually it's considered impolite to use a list comprehension for side effects.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.