2010-11-06 18 views
9

Supongamos que tengo una lista de tuplas:Python: condicionalmente eliminar elementos de la lista

x = [(1,2), (3,4), (7,4), (5,4)] 

De todas las tuplas que comparten el segundo elemento, quiero preservar la tupla con el mayor primer elemento:

y = [(1,2), (7,4)] 

¿Cuál es la mejor manera de lograr esto en Python?


Gracias por las respuestas.

  • Las tuplas podrían ser listas de dos elementos en su lugar, si eso hace la diferencia.
  • Todos los elementos son enteros no negativos.
  • Me gustan las respuestas actuales. Realmente debería aprender más sobre lo que collections tiene para ofrecer.
+0

¿Necesita mantener el orden de las tuplas; es decir, si el original es '[(a, b), (x, y)]', entonces la salida debe tener '[(a, b), (x, y)]' como el orden, o es ' [(x, y), (a, b)] 'aceptable? ¿Necesitas retener el orden de los enteros dentro de las tuplas? es decir, es '[(b, a), (y, x)]' aceptable? – gotgenes

+0

Debe conservarse el orden dentro de tuplas. El orden entre las tuplas en la lista debe conservarse, pero pueden ordenarse fácilmente usando 'y.sort()' que operará en el primer elemento de cada tupla. –

+1

@Steve Creo que su afirmación de que el orden de aparición de las tuplas en la lista se conserva contradice al declarar que también pueden ordenarse usando 'sort()', a menos que exista una suposición no declarada en su pregunta de que la lista de entrada está ordenada por primer elemento de las tuplas. – gotgenes

Respuesta

5

Similar a la respuesta de Aaron

>>> from collections import defaultdict 
>>> x = [(1,2), (3,4), (7,4), (5,4)] 
>>> d = defaultdict(int) 
>>> for v,k in x: 
... d[k] = max(d[k],v) 
... 
>>> y=[(k,v) for v,k in d.items()] 
>>> y 
[(1, 2), (7, 4)] 

nota que la orden no se conserva con este método. Para preservar la orden use esto en su lugar

>>> y = [(k,v) for k,v in x if d[v]==k] 
>>> y 
[(1, 2), (7, 4)] 

aquí está otra manera. Se utiliza más capacidad de almacenamiento, pero tiene menos llamadas a un máximo, por lo que puede ser más rápido

>>> d = defaultdict(list) 
>>> for k,v in x: 
... d[v].append(k) 
... 
>>> y = [(max(k),v) for v,k in d.items()] 
>>> y 
[(1, 2), (7, 4)] 

Una vez más, una simple modificación conserva el orden

>>> y = [(k,v) for k,v in x if max(d[v])==k] 
>>> y 
[(1, 2), (7, 4)] 
+0

+1. Tu mejora de mi respuesta es muy buena. – aaronasterling

5

uso collections.defaultdict

import collections 

max_elements = collections.defaultdict(tuple) 

for item in x: 
    if item > max_elements[item[1]]: 
     max_elements[item[1]] = item 

y = max_elements.values() 
+0

Gracias por la respuesta. Ya estaba retocando con su respuesta anterior que funcionó para mi caso particular. ¿Puedo preguntar por qué lo cambiaste? –

+0

@Steve, esto solo se repite una vez y usará mucha menos memoria para una lista más grande. En general, es mucho mejor. – aaronasterling

+1

Gracias. Yo renunciaría, pero SO me está bloqueando porque subí, luego cancelé (antes de poder entender la respuesta), y luego pensé que podría volver a votar. –

0

mi propio intento, un poco inspirado en aaronsterling:

(oh sí, todos los elementos son no negativos)

def processtuples(x): 
    d = {} 
    for item in x: 
     if x[0] > d.get(x[1],-1): 
      d[x[1]] = x[0] 

    y = [] 
    for k in d: 
     y.append((d[k],k)) 
    y.sort() 
    return y 
0
>>> from collections import defaultdict 
>>> d = defaultdict(tuple) 
>>> x = [(1,2), (3,4), (7,4), (5,4)] 
>>> for a, b in x: 
...  d[b] = max(d[b], (a, b)) 
... 
>>> d.values() 
[(1, 2), (7, 4) 
2

Si se puede hacer la suposición de que tuplas con idéntica segundos elementos aparecen en orden contiguo en la lista original x, puede aprovechar itertools.groupby:

import itertools 
import operator 

def max_first_elem(x): 
    groups = itertools.groupby(x, operator.itemgetter(1)) 
    y = [max(g[1]) for g in groups] 
    return y 

Tenga en cuenta que esto garantizará la preservación del orden de los grupos (por el segundo elemento de tupla), si esa es una restricción deseada para la salida.

Cuestiones relacionadas