2012-05-16 12 views
10

Tengo una lista de listas tal que la longitud de cada lista interna es 1 o n (supongo n> 1).Python list transpose y fill

>>> uneven = [[1], [47, 17, 2, 3], [3], [12, 5, 75, 33]] 

que desea transponer la lista, pero en lugar de truncar la lista más larga (como con zip) o llenando las listas más cortas con None, quiero llenar las listas más cortas con su propio valor singular. En otras palabras, me gustaría llegar:

>>> [(1, 47, 3, 12), (1, 17, 3, 5), (1, 2, 3, 75), (1, 3, 3, 33)] 

que pueda hacer esto con un par de iteraciones:

>>> maxlist = len(max(*uneven, key=len)) 
>>> maxlist 
4 
>>> from itertools import repeat 
>>> uneven2 = [x if len(x) == maxlist else repeat(x[0], maxlist) for x in uneven] 
>>> uneven2 
[[1, 1, 1, 1], [47, 17, 2, 3], [3, 3, 3, 3], [12, 5, 75, 33]] 
>>> zip(*uneven2) 
[(1, 47, 3, 12), (1, 17, 3, 5), (1, 2, 3, 75), (1, 3, 3, 33)] 

pero ¿hay un mejor enfoque? ¿De verdad necesito saber maxlist por adelantado para lograr esto?

+1

Se podía simular 'itertools.izip_longest', simplemente usando el último valor de la secuencia en lugar de un' fillvalue' cuando se agota la secuencia. –

+0

@ChrisMorgan tiene razón vio este enlace http://docs.python.org/library/itertools.html quizás también podría utilizar algunas otras funciones a partir de ahí. –

Respuesta

7

Puede repetir una lista de elementos para siempre:

uneven = [[1], [47, 17, 2, 3], [3], [12, 5, 75, 33]] 

from itertools import repeat 

print zip(*(repeat(*x) if len(x)==1 else x for x in uneven)) 
+0

Esto fue muy útil porque se fabricaron n = 1 listas internas. Ahora he refactorizado, así que la lista es '[repetir (1), [47, 17, 2, 3], repetir (3), [12, 5, 75, 33]]' y puedo hacer 'zip (* desigual) '. – kojiro

4

Usted podría utilizar en lugar itertools.cycle():

>>> from itertools import cycle 
>>> uneven3 = [x if len(x) != 1 else cycle(x) for x in uneven] 
>>> zip(*uneven3) 
[(1, 47, 3, 12), (1, 17, 3, 5), (1, 2, 3, 75), (1, 3, 3, 33)] 

Eso significa que usted no necesita saber maxlist antes de tiempo.

0

Me gustó mucho la idea de @ chris-morgan de simular itertools.izip_longest, así que cuando finalmente tuve la inspiración, escribí una función izip_cycle.

def izip_cycle(*iterables, **kwargs): 
    """Make an iterator that aggregates elements from each of the iterables. 
    If the iterables are of uneven length, missing values are filled-in by cycling the shorter iterables. 
    If an iterable is empty, missing values are fillvalue or None if not specified. 
    Iteration continues until the longest iterable is exhausted. 
    """ 
    fillvalue = kwargs.get('fillvalue') 
    counter = [len(iterables)] 
    def cyclemost(iterable): 
     """Cycle the given iterable like itertools.cycle, unless the counter has run out.""" 
     itb = iter(iterable) 
     saved = [] 
     try: 
      while True: 
       element = itb.next() 
       yield element 
       saved.append(element) 
     except StopIteration: 
      counter[0] -= 1 
      if counter[0] > 0: 
       saved = saved or [fillvalue] 
       while saved: 
        for element in saved: 
         yield element 
    iterators = [cyclemost(iterable) for iterable in iterables] 
    while iterators: 
     yield tuple([next(iterator) for iterator in iterators]) 

print list(izip_cycle([], range(3), range(6), fillvalue='@'))