type(Table)
pandas.core.frame.DataFrame

Table
======= ======= =======
Column1 Column2 Column3
0       23      1
1       5       2
1       2       3
1       19      5
2       56      1
2       22      2
3       2       4
3       14      5
4       59      1
5       44      1
5       1       2
5       87      3

Para cualquiera que esté familiarizado con los pandas, ¿cómo construiría un diccionario de valores múltiples con el método .groupby()?

Me gustaría que una salida se parezca a este formato:

{
    0: [(23,1)]
    1: [(5,  2), (2, 3), (19, 5)]
    # etc...
    }

Donde los valores Col1 se representan como claves y los Col2 y Col3 correspondientes son tuplas empaquetadas en una matriz para cada clave Col1.

Mi sintaxis funciona para agrupar solo una columna en .groupby():

Table.groupby('Column1')['Column2'].apply(list).to_dict()
# Result as expected
{
    0: [23], 
    1: [5, 2, 19], 
    2: [56, 22], 
    3: [2, 14], 
    4: [59], 
    5: [44, 1, 87]
}

Sin embargo, al especificar varios valores para los índices, se devuelven nombres de columna para el valor:

Table.groupby('Column1')[('Column2', 'Column3')].apply(list).to_dict()
# Result has column namespace as array value
{
    0: ['Column2', 'Column3'],
    1: ['Column2', 'Column3'],
    2: ['Column2', 'Column3'],
    3: ['Column2', 'Column3'],
    4: ['Column2', 'Column3'],
    5: ['Column2', 'Column3']
 }

¿Cómo devolvería una lista de tuplas en la matriz de valores?

10
Micks Ketches 27 feb. 2018 a las 23:13

3 respuestas

La mejor respuesta

Personalice la función que usa en apply para que devuelva una lista de listas para cada grupo:

df.groupby('Column1')[['Column2', 'Column3']].apply(lambda g: g.values.tolist()).to_dict()
# {0: [[23, 1]], 
#  1: [[5, 2], [2, 3], [19, 5]], 
#  2: [[56, 1], [22, 2]], 
#  3: [[2, 4], [14, 5]], 
#  4: [[59, 1]], 
#  5: [[44, 1], [1, 2], [87, 3]]}

Si necesita una lista de tuplas explícitamente, use list(map(tuple, ...)) para convertir:

df.groupby('Column1')[['Column2', 'Column3']].apply(lambda g: list(map(tuple, g.values.tolist()))).to_dict()
# {0: [(23, 1)], 
#  1: [(5, 2), (2, 3), (19, 5)], 
#  2: [(56, 1), (22, 2)], 
#  3: [(2, 4), (14, 5)], 
#  4: [(59, 1)], 
#  5: [(44, 1), (1, 2), (87, 3)]}
10
Psidom 27 feb. 2018 a las 20:23

Una forma es crear una nueva columna tup y luego crear el diccionario.

df['tup'] = list(zip(df['Column2'], df['Column3']))
df.groupby('Column1')['tup'].apply(list).to_dict()

# {0: [(23, 1)],
#  1: [(5, 2), (2, 3), (19, 5)],
#  2: [(56, 1), (22, 2)],
#  3: [(2, 4), (14, 5)],
#  4: [(59, 1)],
#  5: [(44, 1), (1, 2), (87, 3)]}

La solución de @ Psidom es más eficiente, pero si el rendimiento no es un problema, utilice lo que tenga más sentido para usted:

df = pd.concat([df]*10000)

def jp(df):
    df['tup'] = list(zip(df['Column2'], df['Column3']))
    return df.groupby('Column1')['tup'].apply(list).to_dict()

def psi(df):
    return df.groupby('Column1')[['Column2', 'Column3']].apply(lambda g: list(map(tuple, g.values.tolist()))).to_dict()

%timeit jp(df)   # 110ms
%timeit psi(df)  # 80ms
2
jpp 27 feb. 2018 a las 20:35

Prefiero usar defaultdict

from collections import defaultdict

d = defaultdict(list)

for row in df.values.tolist():
    d[row[0]].append(tuple(row[1:]))

dict(d)

{0: [(23, 1)],
 1: [(5, 2), (2, 3), (19, 5)],
 2: [(56, 1), (22, 2)],
 3: [(2, 4), (14, 5)],
 4: [(59, 1)],
 5: [(44, 1), (1, 2), (87, 3)]}
2
piRSquared 27 feb. 2018 a las 20:39