2012-06-22 11 views
5

Digamos que tengo una lista:Python: Uniquefying una lista con un toque

L = [15,16,57,59,14] 

La lista contiene Mediciones, que no son muy precisos: que es el valor real de un elemento es de + -2 de la valor registrado Entonces 14,15 y 16 pueden tener el mismo valor. Lo que quiero hacer es seleccionar esa lista, teniendo en cuenta los errores de medición. La salida debe ser para ellos:

l_out = [15,57] 

o

l_out = [(14,15,16),(57,59)] 

no tengo problemas producir ya sea resultado con un bucle. Sin embargo, tengo curiosidad de si podría haber una solución más elegante. Ideas muy aprobadas

+5

¿Qué resultado espera para 'L = [1,2,3,4,5,6,7,8,10]'? – sloth

+0

¿Y cuál sería el resultado de [15,16,57,59,14,13]? – kosii

+0

Soy consciente del problema, pero los datos que tengo en mente se buscan a tientas para que la distancia entre grupos sea> 2 – root

Respuesta

5

Como lazyr señaló en los comentarios , se ha publicado un problema similar here. Con el módulo de clúster de la solución a mi problema sería:

>>> from cluster import * 
>>> L = [15,16,57,59,14] 
>>> cl = HierarchicalClustering(L, lambda x,y: abs(x-y)) 
>>> cl.getlevel(2) 
[[14, 15, 16], [57, 59]] 

o (para obtener la lista única con valores medios de cada grupo):

>>> [mean(cluster) for cluster in cl.getlevel(2)] 
[15, 58] 
-1

bucle for es la forma más sencilla, pero si realmente quieres un código de una sola línea:
l_out = list(set(tuple([tuple(filter(lambda i: abs(item - i) < 3, L)) for item in L])))
muy claro, sin embargo, yo preferiría que el de la versión :)

+2

Esto no funcionaría para el ejemplo '[15,16,57,59,14,13]' –

+0

dijo "Estoy al tanto del problema, pero los datos que Tenga en cuenta que se busca a tientas para que la distancia entre grupos sea> 2 " – lolopop

+1

esa condición se cumple aquí. aún su programa no particiona la lista correctamente. –

2

Si desea pitón lib estándar, itertool 's groupby es su amigo:

from itertools import groupby 

L = [15,16,57,59,14] 

# Stash state outside key function. (a little hacky). 
# Better way would be to create stateful class with a __call__ key fn. 
state = {'group': 0, 'prev': None} 
thresh = 2 

def _group(cur): 
    """Group if within threshold.""" 
    if state["prev"] is not None and abs(state["prev"] - cur) > thresh: 
     state["group"] += 1 # Advance group 
    state["prev"] = cur 
    return state["group"] 

# Group, then drop the group key and inflate the final tuples. 
l_out = [tuple(g) for _, g in groupby(sorted(L), key=_group)] 

print l_out 
# -> [(14, 15, 16), (57, 59)] 
+0

@ +1 Ryan no está mal :) – root

+0

Creo que se podría evitar el estado global agrupando los valores en pares primero a través de 'l = ordenado (L); zip (l, l [1:]) ' –

+0

@NiklasB. - sí! La otra forma sería un patrón más clásico decorar-ordenar-decodificar ... O supongo que ordenar-decorar-agrupar-decodificar;) –

2

Así es como me gustaría hacer esto en un enfoque puro en Python:

s = sorted(L) 
b = [i + 1 for i, (x, y) in enumerate(zip(s, s[1:])) if y > x + 2] 
result = [s[i:j] for i, j in zip([None] + b, b + [None])] 

Aquí b es la lista de "breaks", índices donde termina un clúster.

+0

@NiklasB. gracias, error de uno a uno; fijo. – ecatmur

Cuestiones relacionadas