tutorial lenguaje español descargar python

lenguaje - Python ordena la lista de diccionarios por múltiples claves



python wikipedia (7)

Tengo una lista de dicts:

b = [{u''TOT_PTS_Misc'': u''Utley, Alex'', u''Total_Points'': 96.0}, {u''TOT_PTS_Misc'': u''Russo, Brandon'', u''Total_Points'': 96.0}, {u''TOT_PTS_Misc'': u''Chappell, Justin'', u''Total_Points'': 96.0}, {u''TOT_PTS_Misc'': u''Foster, Toney'', u''Total_Points'': 80.0}, {u''TOT_PTS_Misc'': u''Lawson, Roman'', u''Total_Points'': 80.0}, {u''TOT_PTS_Misc'': u''Lempke, Sam'', u''Total_Points'': 80.0}, {u''TOT_PTS_Misc'': u''Gnezda, Alex'', u''Total_Points'': 78.0}, {u''TOT_PTS_Misc'': u''Kirks, Damien'', u''Total_Points'': 78.0}, {u''TOT_PTS_Misc'': u''Worden, Tom'', u''Total_Points'': 78.0}, {u''TOT_PTS_Misc'': u''Korecz, Mike'', u''Total_Points'': 78.0}, {u''TOT_PTS_Misc'': u''Swartz, Brian'', u''Total_Points'': 66.0}, {u''TOT_PTS_Misc'': u''Burgess, Randy'', u''Total_Points'': 66.0}, {u''TOT_PTS_Misc'': u''Smugala, Ryan'', u''Total_Points'': 66.0}, {u''TOT_PTS_Misc'': u''Harmon, Gary'', u''Total_Points'': 66.0}, {u''TOT_PTS_Misc'': u''Blasinsky, Scott'', u''Total_Points'': 60.0}, {u''TOT_PTS_Misc'': u''Carter III, Laymon'', u''Total_Points'': 60.0}, {u''TOT_PTS_Misc'': u''Coleman, Johnathan'', u''Total_Points'': 60.0}, {u''TOT_PTS_Misc'': u''Venditti, Nick'', u''Total_Points'': 60.0}, {u''TOT_PTS_Misc'': u''Blackwell, Devon'', u''Total_Points'': 60.0}, {u''TOT_PTS_Misc'': u''Kovach, Alex'', u''Total_Points'': 60.0}, {u''TOT_PTS_Misc'': u''Bolden, Antonio'', u''Total_Points'': 60.0}, {u''TOT_PTS_Misc'': u''Smith, Ryan'', u''Total_Points'': 60.0}]

y necesito usar un tipo de clave múltiple invertido por Total_Points, luego no invertido por TOT_PTS_Misc .

Esto se puede hacer en el símbolo del sistema como sigue:

a = sorted(b, key=lambda d: (-d[''Total_Points''], d[''TOT_PTS_Misc'']))

Pero tengo que ejecutar esto a través de una función, donde paso en la lista y las claves de clasificación. Por ejemplo, def multikeysort(dict_list, sortkeys):

¿Cómo se puede usar la línea lambda que ordenará la lista, para un número arbitrario de claves que se transfieren a la función de multicopia, y tener en cuenta que las claves pueden tener cualquier número de claves y las que necesitan géneros invertidos serán identificadas? con un ''-'' antes de eso?


Como ya te sientes cómodo con lambda, esta es una solución menos detallada.

>>> def itemgetter(*names): return lambda mapping: tuple(-mapping[name[1:]] if name.startswith(''-'') else mapping[name] for name in names) >>> itemgetter(''a'', ''-b'')({''a'': 1, ''b'': 2}) (1, -2)


Esta respuesta funciona para cualquier tipo de columna en el diccionario: la columna negada no necesita ser un número.

def multikeysort(items, columns): from operator import itemgetter comparers = [((itemgetter(col[1:].strip()), -1) if col.startswith(''-'') else (itemgetter(col.strip()), 1)) for col in columns] def comparer(left, right): for fn, mult in comparers: result = cmp(fn(left), fn(right)) if result: return mult * result else: return 0 return sorted(items, cmp=comparer)

Puedes llamarlo así:

b = [{u''TOT_PTS_Misc'': u''Utley, Alex'', u''Total_Points'': 96.0}, {u''TOT_PTS_Misc'': u''Russo, Brandon'', u''Total_Points'': 96.0}, {u''TOT_PTS_Misc'': u''Chappell, Justin'', u''Total_Points'': 96.0}, {u''TOT_PTS_Misc'': u''Foster, Toney'', u''Total_Points'': 80.0}, {u''TOT_PTS_Misc'': u''Lawson, Roman'', u''Total_Points'': 80.0}, {u''TOT_PTS_Misc'': u''Lempke, Sam'', u''Total_Points'': 80.0}, {u''TOT_PTS_Misc'': u''Gnezda, Alex'', u''Total_Points'': 78.0}, {u''TOT_PTS_Misc'': u''Kirks, Damien'', u''Total_Points'': 78.0}, {u''TOT_PTS_Misc'': u''Worden, Tom'', u''Total_Points'': 78.0}, {u''TOT_PTS_Misc'': u''Korecz, Mike'', u''Total_Points'': 78.0}, {u''TOT_PTS_Misc'': u''Swartz, Brian'', u''Total_Points'': 66.0}, {u''TOT_PTS_Misc'': u''Burgess, Randy'', u''Total_Points'': 66.0}, {u''TOT_PTS_Misc'': u''Smugala, Ryan'', u''Total_Points'': 66.0}, {u''TOT_PTS_Misc'': u''Harmon, Gary'', u''Total_Points'': 66.0}, {u''TOT_PTS_Misc'': u''Blasinsky, Scott'', u''Total_Points'': 60.0}, {u''TOT_PTS_Misc'': u''Carter III, Laymon'', u''Total_Points'': 60.0}, {u''TOT_PTS_Misc'': u''Coleman, Johnathan'', u''Total_Points'': 60.0}, {u''TOT_PTS_Misc'': u''Venditti, Nick'', u''Total_Points'': 60.0}, {u''TOT_PTS_Misc'': u''Blackwell, Devon'', u''Total_Points'': 60.0}, {u''TOT_PTS_Misc'': u''Kovach, Alex'', u''Total_Points'': 60.0}, {u''TOT_PTS_Misc'': u''Bolden, Antonio'', u''Total_Points'': 60.0}, {u''TOT_PTS_Misc'': u''Smith, Ryan'', u''Total_Points'': 60.0}] a = multikeysort(b, [''-Total_Points'', ''TOT_PTS_Misc'']) for item in a: print item

Pruébalo con cualquiera de las columnas negadas. Verás que el orden de clasificación es inverso.

Siguiente: cámbialo para que no use clase extra ...

2016-01-17

Tomando mi inspiración de esta respuesta ¿Cuál es la mejor manera de obtener el primer artículo de una condición de coincidencia iterable? Acorté el código:

from operator import itemgetter as i def multikeysort(items, columns): comparers = [ ((i(col[1:].strip()), -1) if col.startswith(''-'') else (i(col.strip()), 1)) for col in columns ] def comparer(left, right): comparer_iter = ( cmp(fn(left), fn(right)) * mult for fn, mult in comparers ) return next((result for result in comparer_iter if result), 0) return sorted(items, cmp=comparer)

En caso de que le guste su código breve.

Más tarde 2016-01-17

Esto funciona con python3 (que eliminó el argumento cmp para sort ):

from operator import itemgetter as i from functools import cmp_to_key def multikeysort(items, columns): comparers = [ ((i(col[1:].strip()), -1) if col.startswith(''-'') else (i(col.strip()), 1)) for col in columns ] def comparer(left, right): comparer_iter = ( cmp(fn(left), fn(right)) * mult for fn, mult in comparers ) return next((result for result in comparer_iter if result), 0) return sorted(items, key=cmp_to_key(comparer))

Inspirado por esta respuesta ¿Cómo debo hacer una clasificación personalizada en Python 3?


Sé que esta es una pregunta bastante antigua, pero ninguna de las respuestas menciona que Python garantiza un orden de clasificación estable para sus rutinas de ordenación, como list.sort() y sorted() , lo que significa que los artículos que comparan iguales conservan su orden original.

Esto significa que el equivalente de ORDER BY name ASC, age DESC (usando notación SQL) para una lista de diccionarios se puede hacer así:

items.sort(key=operator.itemgetter(''age''), reverse=True) items.sort(key=operator.itemgetter(''name''))

La inversión / inversión funciona para todos los tipos que se pueden ordenar, no solo los números que puede negar al colocar un signo menos al frente.

Y debido al algoritmo Timsort utilizado en (al menos) CPython, esto es realmente bastante rápido en la práctica.


Uso lo siguiente para ordenar una matriz 2d en varias columnas

def k(a,b): def _k(item): return (item[a],item[b]) return _k

Esto podría ampliarse para trabajar en una cantidad arbitraria de elementos. Tiendo a pensar que es mejor encontrar un mejor patrón de acceso para sus claves ordenables que escribir un comparador elegante.

>>> data = [[0,1,2,3,4],[0,2,3,4,5],[1,0,2,3,4]] >>> sorted(data, key=k(0,1)) [[0, 1, 2, 3, 4], [0, 2, 3, 4, 5], [1, 0, 2, 3, 4]] >>> sorted(data, key=k(1,0)) [[1, 0, 2, 3, 4], [0, 1, 2, 3, 4], [0, 2, 3, 4, 5]] >>> sorted(a, key=k(2,0)) [[0, 1, 2, 3, 4], [1, 0, 2, 3, 4], [0, 2, 3, 4, 5]]


http://stygianvision.net/updates/python-sort-list-object-dictionary-multiple-key/ tiene un buen resumen de varias técnicas para hacer esto. Si sus requisitos son más simples que "multikey bidireccional completo", eche un vistazo. Está claro que la respuesta aceptada y la publicación del blog que acabo de mencionar influyeron mutuamente de alguna manera, aunque no sé en qué orden.

En caso de que el enlace falle, aquí hay una sinopsis muy rápida de ejemplos que no se han mencionado anteriormente:

mylist = sorted(mylist, key=itemgetter(''name'', ''age'')) mylist = sorted(mylist, key=lambda k: (k[''name''].lower(), k[''age''])) mylist = sorted(mylist, key=lambda k: (k[''name''].lower(), -k[''age'']))


def sortkeypicker(keynames): negate = set() for i, k in enumerate(keynames): if k[:1] == ''-'': keynames[i] = k[1:] negate.add(k[1:]) def getit(adict): composite = [adict[k] for k in keynames] for i, (k, v) in enumerate(zip(keynames, composite)): if k in negate: composite[i] = -v return composite return getit a = sorted(b, key=sortkeypicker([''-Total_Points'', ''TOT_PTS_Misc'']))


from operator import itemgetter from functools import partial def _neg_itemgetter(key, d): return -d[key] def key_getter(key_expr): keys = key_expr.split(",") getters = [] for k in keys: k = k.strip() if k.startswith("-"): getters.append(partial(_neg_itemgetter, k[1:])) else: getters.append(itemgetter(k)) def keyfunc(dct): return [kg(dct) for kg in getters] return keyfunc def multikeysort(dict_list, sortkeys): return sorted(dict_list, key = key_getter(sortkeys)

Demostración:

>>> multikeysort([{u''TOT_PTS_Misc'': u''Utley, Alex'', u''Total_Points'': 60.0}, {u''TOT_PTS_Misc'': u''Russo, Brandon'', u''Total_Points'': 96.0}, {u''TOT_PTS_Misc'': u''Chappell, Justin'', u''Total_Points'': 96.0}], "-Total_Points,TOT_PTS_Misc") [{u''Total_Points'': 96.0, u''TOT_PTS_Misc'': u''Chappell, Justin''}, {u''Total_Points'': 96.0, u''TOT_PTS_Misc'': u''Russo, Brandon''}, {u''Total_Points'': 60.0, u''TOT_PTS_Misc'': u''Utley, Alex''}]

El análisis es un poco frágil, pero al menos permite un número variable de espacios entre las teclas.