Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

377
Views
Contraer una lista de (diccionario de lista) a un solo (diccionario de lista)

Tengo datos que llegan como diccionarios de listas. De hecho, leí en una lista de ellos...

 data = [ { 'key1': [101, 102, 103], 'key2': [201, 202, 203], 'key3': [301, 302, 303], }, { 'key2': [204], 'key3': [304, 305], 'key4': [404, 405, 406], }, { 'key1': [107, 108], 'key4': [407], }, ]

Cada diccionario puede tener diferentes claves.

Cada tecla se asocia a una lista, de longitud variable.

Lo que me gustaría hacer es hacer un solo diccionario, concatenando las listas que comparten una clave...

 desired_result = { 'key1': [101, 102, 103, 107, 108], 'key2': [201, 202, 203, 204], 'key3': [301, 302, 303, 304, 305], 'key4': [404, 405, 406, 407], }

Notas:

  • El orden no importa
  • Hay cientos de diccionarios.
  • Hay docenas de claves por diccionario.
  • Totalizando cientos de claves en el conjunto de resultados
  • Cada lista de fuentes contiene docenas de elementos

Puedo hacer esto, con comprensiones, pero se siente muy torpe, y en realidad es muy lento (recorrer todas las teclas posibles para cada diccionario posible produce más 'errores' que 'aciertos')...

 { key: [ item for d in data if key in d for item in d[key] ] for key in set( key for d in data for key in d.keys() ) } # TimeIt gives 3.2, for this small data set

Una opción más corta y más fácil de leer/mantener es simplemente recorrer todo. Pero el rendimiento aún apesta (¿posiblemente debido a la gran cantidad de llamadas a extend() , lo que obliga a la reasignación frecuente de memoria a medida que se llenan las listas sobreaprovisionadas?) ...

 from collections import defaultdict result = defaultdict(list) for d in data: for key, val in d.items(): result[key].extend(val) # TimeIt gives 1.7, for this small data set

¿Hay una mejor manera?

  • más 'pitónico'?
  • más conciso?
  • más eficaz?

Alternativamente, ¿existe una estructura de datos más aplicable para este tipo de proceso?

  • Estoy haciendo una especie de mapa hash
  • Donde se garantiza que cada entrada tendrá múltiples colisiones y, por lo tanto, siempre será una lista

Editar: se agregó el tiempo para el conjunto de datos pequeños

  • No hay tiempos para los datos del mundo real, ya que no tengo acceso a ellos desde aquí (err, ooops/lo siento...)
over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

res = {} for d in data: for k, v in d.items(): # Adds `key1` to `res` with empty list, if `key1` is not there yet. # Extends list under `key1` with new portion of data contained in `v`. res.setdefault(k, []).extend(v)

Resultado:

 {'key1': [101, 102, 103, 107, 108], 'key2': [201, 202, 203, 204], 'key3': [301, 302, 303, 304, 305], 'key4': [404, 405, 406, 407]}

ACTUALIZACIÓN: Me gustaría evitar comparar el rendimiento de setdefault y defaultdict , pero hubo una discusión en los comentarios e hice algunas pruebas para esos datos en particular, usando python 3.10.

TL; DR: Setdefault más rápido defaultdict alrededor del 13% para ese caso en particular.

El resultado de la prueba:

 defaultldict [1.633565943, 1.590108738, 1.6549000220000005, 1.622328843, 1.6121867709999993] setdefault [1.4336988549999994, 1.4056579070000002, 1.4107502079999996, 1.408643755, 1.433823878]

El código de la prueba:

 import timeit from collections import defaultdict data = [ { 'key1': [101, 102, 103], 'key2': [201, 202, 203], 'key3': [301, 302, 303], }, { 'key2': [204], 'key3': [304, 305], 'key4': [404, 405, 406], }, { 'key1': [107, 108], 'key4': [407], }, ] def setdefault(): res = {} for d in data: for k, v in d.items(): res.setdefault(k, []).extend(v) def default(): res = defaultdict(list) for d in data: for k, v in d.items(): res[k].extend(v) if __name__ == '__main__': print('defaultldict', timeit.repeat(stmt=default, repeat=5, number=1000000, globals={'data': data})) print('setdefault', timeit.repeat(stmt=setdefault, repeat=5, number=1000000, globals={'data': data}))
over 4 years ago · Santiago Trujillo Report

0

Tal vez podría usar la comprensión de listas y pandas.

No tengo idea si esta es una respuesta válida, o cómo funciona, pero funciona, para el pequeño conjunto de datos de ejemplo de todos modos.

 import pandas as pd data = [ { "key1": [101, 102, 103], "key2": [201, 202, 203], "key3": [301, 302, 303], }, { "key2": [204], "key3": [304, 305], "key4": [404, 405, 406], }, { "key1": [107, 108], "key4": [407], }, ] dics = [pd.DataFrame.from_dict(el, orient="index") for el in data] dics_concat = pd.concat(dics).fillna("Empty") dics_concat["key"] = dics_concat.index dics_concat = dics_concat.groupby("key").agg(list) combined = dics_concat.apply( lambda row: sorted( [item for sublist in row for item in sublist if item != "Empty"] ), axis=1, ) print(combined.to_dict())
 {'key1': [101, 102.0, 103.0, 107, 108.0], 'key2': [201, 202.0, 203.0, 204], 'key3': [301, 302.0, 303.0, 304, 305.0], 'key4': [404, 405.0, 406.0, 407]}

Sin pd.concat.

 df = pd.DataFrame(data).fillna("") combined = df.apply( lambda row: sorted( [item for sublist in row for item in sublist if item != "Empty"] ), axis=0, ) print(combined.to_dict())
over 4 years ago · Santiago Trujillo Report

0

Tengo una solución que parece lograr buenos resultados cuando las listas en sus diccionarios son largas. Aunque no es el caso en su situación, todavía lo menciono.

La idea, como se menciona en mis comentarios, es usar append en lugar de extend en un primer ciclo y luego concatenar todas las listas en el diccionario resultante usando itertools.chain . Esta es la chain de funciones definida a continuación. También agregué el código de @ mrvol en mi respuesta para comparar.

Aquí está el código:

 import timeit import itertools from collections import defaultdict REPEAT = 5 # parameter repeat of timeit NUMBER = 100 # parameter number of timeit DICTS = 100 # number of dictionaries in our data KEYS = 12 # size of the dictionaries in our data LEN = 1_000 # size of the lists in our dictionaries data = [ { f'key{x}': [x * 100 + y for y in range(LEN)] for x in range(KEYS) } for _ in range(DICTS) ] def setdefault(): res = {} for d in data: for k, v in d.items(): res.setdefault(k, []).extend(v) return res def default(): res = defaultdict(list) for d in data: for k, v in d.items(): res[k].extend(v) return res def chain(): res = dict() for d in data: for k, v in d.items(): res.setdefault(k, []).append(v) for key in res: res[key] = list(itertools.chain.from_iterable(res[key])) return res # check that all produce the same result assert chain() == default() assert setdefault() == default() if __name__ == '__main__': for name, fun in [ ('default', default), ('setdefault', setdefault), ('chain', chain) ]: print(name, timeit.repeat( stmt=fun, repeat=REPEAT, number=NUMBER, globals={'data': data} ))

Todas las pruebas a continuación se han realizado con python 3.10.

Aquí están los resultados:

 default [3.0608591459999843, 2.9533347530000356, 3.204700414999934, 2.934139603999938, 2.854463246000023] setdefault [2.7814459759999863, 2.801596405000055, 2.796927817000096, 2.797430740999971, 2.795393482999998] chain [2.336767712999972, 2.33148793700002, 2.3378432869999415, 2.3322470529999464, 2.3312841169999956]

Si aumentamos LEN a 10_000 la diferencia es más impresionante:

 default [33.63351462200012, 33.598145768999984, 33.83524595699987, 33.732721158000004, 33.785992579999856] setdefault [33.658237180000015, 33.51113319399997, 33.25321677000011, 33.23780467200004, 33.467723277999994] chain [23.47564513400016, 23.542697918999693, 23.520614959999875, 23.498439506000068, 23.582990831999723]

Pero con LEN=100 , la función de chain es un poco más lenta:

 default [0.20926385200004916, 0.23037391399998342, 0.21281876400007604, 0.21195233899993582, 0.21580142600009822] setdefault [0.22843905199988512, 0.2232434430000012, 0.2187928880000527, 0.22453147500004889, 0.21708852799997658] chain [0.24585279899997659, 0.23280389700016713, 0.2262972040000477, 0.24113659099998586, 0.2370573980001609]

Entonces, una vez más, chain no debería ajustarse a sus necesidades, ya que sus listas tienden a ser pequeñas con docenas de elementos, pero menciono esta solución en aras de la exhaustividad.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!