2009-01-03 142 views
32

que tienen la siguiente estructura de datos (una lista de listas)ordenar y agrupar listas anidadas en Python

[ 
['4', '21', '1', '14', '2008-10-24 15:42:58'], 
['3', '22', '4', '2somename', '2008-10-24 15:22:03'], 
['5', '21', '3', '19', '2008-10-24 15:45:45'], 
['6', '21', '1', '1somename', '2008-10-24 15:45:49'], 
['7', '22', '3', '2somename', '2008-10-24 15:45:51'] 
] 

Me gustaría ser capaz de

  1. utilizar una función para reordenar la lista para que pueda agrupar por cada elemento en la lista. Por ejemplo, me gustaría poder agrupar en la segunda columna (para que todos los 21 estén juntos)

  2. Use una función para mostrar solo ciertos valores de cada lista interna. Por ejemplo me gustaría reducir esta lista para contener sólo el valor del campo 4 de '2somename'

lo que la lista se vería así

[ 
    ['3', '22', '4', '2somename', '2008-10-24 15:22:03'], 
    ['7', '22', '3', '2somename', '2008-10-24 15:45:51'] 
] 
+2

punto menor, pero que es mejor usar tuplas vez de las listas internas – hop

Respuesta

45

Para la primera pregunta, lo primero que se debe hacer es ordenar la lista por el segundo campo:

x = [ 
['4', '21', '1', '14', '2008-10-24 15:42:58'], 
['3', '22', '4', '2somename', '2008-10-24 15:22:03'], 
['5', '21', '3', '19', '2008-10-24 15:45:45'], 
['6', '21', '1', '1somename', '2008-10-24 15:45:49'], 
['7', '22', '3', '2somename', '2008-10-24 15:45:51'] 
] 

from operator import itemgetter 

x.sort(key=itemgetter(1)) 

continuación, puede utilizar la función GroupBy itertools':

from itertools import groupby 
y = groupby(x, itemgetter(1)) 

Ahora y es un iterador que contiene tuplas de (elemento, iterador de elemento).Es más confuso para explicar estas tuplas de lo que es para mostrar código:

for elt, items in groupby(x, itemgetter(1)): 
    print(elt, items) 
    for i in items: 
     print(i) 

que imprime:

21 <itertools._grouper object at 0x511a0> 
['4', '21', '1', '14', '2008-10-24 15:42:58'] 
['5', '21', '3', '19', '2008-10-24 15:45:45'] 
['6', '21', '1', '1somename', '2008-10-24 15:45:49'] 
22 <itertools._grouper object at 0x51170> 
['3', '22', '4', '2somename', '2008-10-24 15:22:03'] 
['7', '22', '3', '2somename', '2008-10-24 15:45:51'] 

Para la segunda parte, se debe utilizar listas por comprensión como ya se ha mencionado aquí:

from pprint import pprint as pp 
pp([y for y in x if y[3] == '2somename']) 

que imprime:

[['3', '22', '4', '2somename', '2008-10-24 15:22:03'], 
['7', '22', '3', '2somename', '2008-10-24 15:45:51']] 
+1

He agregado el ejemplo de comprensión de listas. – jfs

+0

Esta respuesta fue escrita hace mucho tiempo y debería usar una expresión de generador en lugar de una lista de comprensión: 'pp (y para y en x si y [3] == '2somename')' – llimllib

+0

no. Está mal. genexpr no es apropiado aquí. Intenta ejecutar el código. – jfs

3

Si entiendo bien su pregunta, la siguiente código debe hacer el trabajo:

l = [ 
['4', '21', '1', '14', '2008-10-24 15:42:58'], 
['3', '22', '4', '2somename', '2008-10-24 15:22:03'], 
['5', '21', '3', '19', '2008-10-24 15:45:45'], 
['6', '21', '1', '1somename', '2008-10-24 15:45:49'], 
['7', '22', '3', '2somename', '2008-10-24 15:45:51'] 
] 

def compareField(field): 
    def c(l1,l2): 
     return cmp(l1[field], l2[field]) 
    return c 

# Use compareField(1) as the ordering criterion, i.e. sort only with 
# respect to the 2nd field 
l.sort(compareField(1)) 
for row in l: print row 

print 
# Select only those sublists for which 4th field=='2somename' 
l2somename = [row for row in l if row[3]=='2somename'] 
for row in l2somename: print row 

salida:

['4', '21', '1', '14', '2008-10-24 15:42:58'] 
['5', '21', '3', '19', '2008-10-24 15:45:45'] 
['6', '21', '1', '1somename', '2008-10-24 15:45:49'] 
['3', '22', '4', '2somename', '2008-10-24 15:22:03'] 
['7', '22', '3', '2somename', '2008-10-24 15:45:51'] 

['3', '22', '4', '2somename', '2008-10-24 15:22:03'] 
['7', '22', '3', '2somename', '2008-10-24 15:45:51'] 
+0

El argumento 'cmp' para ordenar se elimina en 2.6/3.0, por lo tanto, es preferible usar el parámetro 'clave' que extrae una clave de clasificación, pero de lo contrario, +1. –

+0

eliminado 'cmp =', debería ser el primer argumento de todos modos. Por cierto, estoy usando Python 2.6.1 y todo funciona bien ... –

7

Si ha asignado a var "a" ...

# 1:

a.sort(lambda x,y: cmp(x[1], y[1])) 

# 2:

filter(lambda x: x[3]=="2somename", a) 
+0

Enfoque más simple y más limpio que itemgetter –

+0

lambda para la victoria. Me gustó mucho esta solución – alfredocambera

2

utilizar una función para reordenar la lista para que yo puede agrupar por cada elemento en la lista. Por ejemplo, me gustaría poder agrupar en la segunda columna (para que todos los 21 estén juntos)

Las listas tienen un método de clasificación incorporado y puede proporcionar una función que extraiga la clave de clasificación.

>>> import pprint 
>>> l.sort(key = lambda ll: ll[1]) 
>>> pprint.pprint(l) 
[['4', '21', '1', '14', '2008-10-24 15:42:58'], 
['5', '21', '3', '19', '2008-10-24 15:45:45'], 
['6', '21', '1', '1somename', '2008-10-24 15:45:49'], 
['3', '22', '4', '2somename', '2008-10-24 15:22:03'], 
['7', '22', '3', '2somename', '2008-10-24 15:45:51']] 

utilizar una función para mostrar sólo ciertos valores de cada lista interior. Por ejemplo me gustaría reducir esta lista para contener sólo el valor del campo 4 de '2somename'

Esto se parece a un trabajo para list comprehensions

>>> [ll[3] for ll in l] 
['14', '2somename', '19', '1somename', '2somename'] 
+0

Reemplace '[ll [3] por ll en l]' por '[ll por ll en l si ll [3] == '2somename']' y corrija la salida. – jfs

2

Si va a hacer una gran cantidad de clasificación y filtrado, es posible que desee algunas funciones de ayuda.

m = [ 
['4', '21', '1', '14', '2008-10-24 15:42:58'], 
['3', '22', '4', '2somename', '2008-10-24 15:22:03'], 
['5', '21', '3', '19', '2008-10-24 15:45:45'], 
['6', '21', '1', '1somename', '2008-10-24 15:45:49'], 
['7', '22', '3', '2somename', '2008-10-24 15:45:51'] 
] 

# Sort and filter helpers. 
sort_on = lambda pos:  lambda x: x[pos] 
filter_on = lambda pos,val: lambda l: l[pos] == val 

# Sort by second column 
m = sorted(m, key=sort_on(1)) 

# Filter on 4th column, where value = '2somename' 
m = filter(filter_on(3,'2somename'),m) 
+0

sort_on == operator.itemgetter – jfs

+0

Utilice DEF's en lugar de lambdas. –

+0

@ s.lott - ¿por qué defs sobre lambdas aquí? – Triptych

1

Parece que está intentando utilizar una lista como base de datos.

Hoy en día, Python incluye enlaces sqlite en la distribución central. Si no necesita persistencia, es realmente fácil crear una base de datos sqlite en memoria (vea How do I create a sqllite3 in-memory database?).

Luego puede usar sentencias SQL para hacer todo este ordenamiento y filtrado sin tener que reinventar la rueda.

+0

Kamil, tienes razón. Sin embargo, estoy aprendiendo Python y quería hacer cosas usando listas para poder aprender un poco más sobre ellas. Voy a comprobar esto, aunque gracias – m3clov3n

2

Por parte (2), siendo x la matriz, creo que quiere,

[y for y in x if y[3] == '2somename'] 

que devolverá una lista de sólo sus listas de datos que tienen un cuarto valor de ser '2somename' ... Aunque parece que Kamil te está dando el mejor consejo con SQL ...

1

Simplemente estás creando índices en tu estructura, ¿verdad?

>>> from collections import defaultdict 
>>> def indexOn(things, pos): 
...  inx= defaultdict(list) 
...  for t in things: 
...    inx[t[pos]].append(t) 
...  return inx 
... 
>>> a=[ 
... ['4', '21', '1', '14', '2008-10-24 15:42:58'], 
... ['3', '22', '4', '2somename', '2008-10-24 15:22:03'], 
... ['5', '21', '3', '19', '2008-10-24 15:45:45'], 
... ['6', '21', '1', '1somename', '2008-10-24 15:45:49'], 
... ['7', '22', '3', '2somename', '2008-10-24 15:45:51'] 
... ] 

Aquí es su primera solicitud, agrupados por posición 1.

>>> import pprint 
>>> pprint.pprint(dict(indexOn(a,1))) 
{'21': [['4', '21', '1', '14', '2008-10-24 15:42:58'], 
     ['5', '21', '3', '19', '2008-10-24 15:45:45'], 
     ['6', '21', '1', '1somename', '2008-10-24 15:45:49']], 
'22': [['3', '22', '4', '2somename', '2008-10-24 15:22:03'], 
     ['7', '22', '3', '2somename', '2008-10-24 15:45:51']]} 

Esta es su segunda solicitud, agrupados por posición 3.

>>> dict(indexOn(a,3)) 
{'19': [['5', '21', '3', '19', '2008-10-24 15:45:45']], '14': [['4', '21', '1', '14', '2008-10-24 15:42:58']], '2somename': [['3', '22', '4', '2somename', '2008-10-24 15:22:03'], ['7', '22', '3', '2somename', '2008-10-24 15:45:51']], '1somename': [['6', '21', '1', '1somename', '2008-10-24 15:45:49']]} 
>>> pprint.pprint(_) 
{'14': [['4', '21', '1', '14', '2008-10-24 15:42:58']], 
'19': [['5', '21', '3', '19', '2008-10-24 15:45:45']], 
'1somename': [['6', '21', '1', '1somename', '2008-10-24 15:45:49']], 
'2somename': [['3', '22', '4', '2somename', '2008-10-24 15:22:03'], 
       ['7', '22', '3', '2somename', '2008-10-24 15:45:51']]} 
Cuestiones relacionadas