hoe maak je een 2D-lijst plat naar 1D zonder numpy te gebruiken?

Ik heb een lijst die er als volgt uitziet:

[[1,2,3],[1,2],[1,4,5,6,7]]

en ik wil het afvlakken tot [1,2,3,1,2,1,4,5,6,7]

is er een lichtgewicht functie om dit te doen zonder numpy te gebruiken?


Antwoord 1, autoriteit 100%

Zonder numpy ( ndarray.flatten) een manier zou zijn om chain.from_iterablewat een alternatieve constructor is voor itertools.chain:

>>> list(chain.from_iterable([[1,2,3],[1,2],[1,4,5,6,7]]))
[1, 2, 3, 1, 2, 1, 4, 5, 6, 7]

Of als een andere, maar toch Pythonische benadering, kun je een lijstbegripgebruiken:

[j for sub in [[1,2,3],[1,2],[1,4,5,6,7]] for j in sub]

Een andere functionele benadering die zeer geschikt is voor korte lijsten zou ook kunnen zijn reducein Python2 en functools.reducein Python3 (gebruik dit niet voor lange lijsten):

In [4]: from functools import reduce # Python3
In [5]: reduce(lambda x,y :x+y ,[[1,2,3],[1,2],[1,4,5,6,7]])
Out[5]: [1, 2, 3, 1, 2, 1, 4, 5, 6, 7]

Om het iets sneller te maken, kunt u operator.addgebruiken, dat is ingebouwd, in plaats van lambda:

In [6]: from operator import add
In [7]: reduce(add ,[[1,2,3],[1,2],[1,4,5,6,7]])
Out[7]: [1, 2, 3, 1, 2, 1, 4, 5, 6, 7]
In [8]: %timeit reduce(lambda x,y :x+y ,[[1,2,3],[1,2],[1,4,5,6,7]])
789 ns ± 7.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [9]: %timeit reduce(add ,[[1,2,3],[1,2],[1,4,5,6,7]])
635 ns ± 4.38 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

benchmark:

:~$ python -m timeit "from itertools import chain;chain.from_iterable([[1,2,3],[1,2],[1,4,5,6,7]])"
1000000 loops, best of 3: 1.58 usec per loop
:~$ python -m timeit "reduce(lambda x,y :x+y ,[[1,2,3],[1,2],[1,4,5,6,7]])"
1000000 loops, best of 3: 0.791 usec per loop
:~$ python -m timeit "[j for i in [[1,2,3],[1,2],[1,4,5,6,7]] for j in i]"
1000000 loops, best of 3: 0.784 usec per loop

Een benchmark op het antwoord van @Will dat sumgebruikte (het is snel voor een korte lijst, maar niet voor een lange lijst):

:~$ python -m timeit "sum([[1,2,3],[4,5,6],[7,8,9]], [])"
1000000 loops, best of 3: 0.575 usec per loop
:~$ python -m timeit "sum([range(100),range(100)], [])"
100000 loops, best of 3: 2.27 usec per loop
:~$ python -m timeit "reduce(lambda x,y :x+y ,[range(100),range(100)])"
100000 loops, best of 3: 2.1 usec per loop

Antwoord 2, autoriteit 76%

Voor zo’n lijst als deze, is mijn favoriete handige truc om gewoon sumte gebruiken;

sumheeft een optioneel argument: sum(iterable [, start]), dus je kunt het volgende doen:

list_of_lists = [[1,2,3], [4,5,6], [7,8,9]]
print sum(list_of_lists, []) # [1,2,3,4,5,6,7,8,9]

dit werkt omdat de operator +toevallig de samenvoegingsoperator is voor lijsten, en je hebt verteld dat de startwaarde []is – een lege lijst.

maar de documentatie voor sumadviseert dat u itertools.chainin plaats daarvan, omdat het veel duidelijker is.


Antwoord 3, autoriteit 3%

Dit werkt in uw specifieke geval. Een recursieve functie zou het beste werken als je meerdere niveaus van geneste iterables hebt.

def flatten(input):
    new_list = []
    for i in input:
        for j in i:
            new_list.append(j)
    return new_list

Other episodes