Me gustaría convertir un marco de datos de pandas en un diccionario de múltiples claves, usando 2 o más columnas como la clave del diccionario, y me gustaría que estas claves sean irrelevantes.
Aquí hay un ejemplo de cómo convertir un diccionario pandas a un diccionario regular de varias teclas, donde el orden es relevante.
import pandas as pd import numpy as np df = pd.DataFrame(np.random.randint(0,100,size=(5, 3)), columns=list('ABC')) df_dict = df.set_index(['B', 'C']).to_dict()['A'] print(df_dict) {(33, 21): 85, (61, 46): 88, (78, 12): 48, (89, 18): 65, (91, 19): 41} entonces df_dict[(33, 21)] obtendrá 85 , pero df_dict[(21, 33)] dará como resultado un error clave.
Soluciones potenciales
Esta es una pregunta SO que cubre formas de ordenar diccionarios irrelevantes, usando sorted, tuple, Counter y/o frozenset.
Diccionario de claves múltiples donde el orden de las claves no importa
Sin embargo, no me llama la atención ninguna solución aparente para usar estos tipos de datos y funciones con los métodos de conversión de Pandas.
La siguiente idea sería convertir las claves del diccionario después de que se haya convertido el marco de datos.
probé esto
new_d = {frozenset(key): value for key, value in df_dict}Pero obtuve este error
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-49-6a3244440ac2> in <module>() ----> 1 new_d = {frozenset(key): value for key, value in df_dict} 2 new_d <ipython-input-49-6a3244440ac2> in <dictcomp>(.0) ----> 1 new_d = {frozenset(key): value for key, value in df_dict} 2 new_d TypeError: 'int' object is not iterable¿Por qué no crear desde df?
d = dict(zip(df[['B', 'C']].apply(frozenset,1),df['A'])) d {frozenset({72, 12}): 34, frozenset({98, 76}): 82, frozenset({67, 7}): 35, frozenset({60, 70}): 18, frozenset({8, 53}): 81}Te estás olvidando de recorrer df_dict.items() en lugar de solo df_dict ;)
>>> new_d = {frozenset(key): value for key, value in df_dict.items()} >>> new_d {frozenset({10, 99}): 92, frozenset({60, 76}): 54, frozenset({6, 20}): 31, frozenset({36, 46}): 31, frozenset({3, 68}): 59} >>> new_d[frozenset({99, 10})] 92 Bonificación: dado que acceder a todo usando frozenset({...}) es espantoso, escribí una pequeña clase contenedora para que sea más fácil:
>>> class Test: ... def __init__(self, fs): ... self.fs = fs ... def __getitem__(self, key): ... return self.fs[frozenset(key)] ... def __setitem__(self, key, val): ... self.fs[frozenset(key)] = val ... def __repr__(self): ... import re ... return re.sub(r'frozenset\({(.+?)}\)', r'(\1)', self.fs.__repr__()) ... __str__ = __repr__ >>> new_d = Test(new_d) >>> new_d {(10, 99): 92, (76, 60): 54, (20, 6): 31, (36, 46): 31, (3, 68): 59} # Internally still just a dict of frozensets: >>> new_d.fs {frozenset({10, 99}): 92, frozenset({60, 76}): 54, frozenset({6, 20}): 31, frozenset({36, 46}): 31, frozenset({3, 68}): 59} >>> new_d[10, 99] 92 >>> new_d[99, 10] 92 >>> new_d[99, 10] = 123456789 >>> new_d[10, 99] 123456789