Cómo crear un diccionario de dos columnas Pandas DataFrame

5 minutos de lectura

avatar de usuario
usuario1083734

¿Cuál es la forma más eficiente de organizar el siguiente marco de datos de pandas:

datos =

Position    Letter
1           a
2           b
3           c
4           d
5           e

en un diccionario como alphabet[1 : 'a', 2 : 'b', 3 : 'c', 4 : 'd', 5 : 'e']?

avatar de usuario
jeff

In [9]: pd.Series(df.Letter.values,index=df.Position).to_dict()
Out[9]: {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'}

Comparación de velocidad (usando el método de Wouter)

In [6]: df = pd.DataFrame(randint(0,10,10000).reshape(5000,2),columns=list('AB'))

In [7]: %timeit dict(zip(df.A,df.B))
1000 loops, best of 3: 1.27 ms per loop

In [8]: %timeit pd.Series(df.A.values,index=df.B).to_dict()
1000 loops, best of 3: 987 us per loop

  • Sin crear primero una Serie… dict(zip(df.Position, df.Letter))

    – Wouter Overmeire

    2 de julio de 2013 a las 14:05

  • FYI … mi método está muy cerca del capó en cuanto a lo que está haciendo Wouter, la diferencia es que se implementa usando izipmás bien que zip; el generador hace la diferencia supongo

    – Jeff

    2 de julio de 2013 a las 14:17

  • @WouterOvermeire esto funciona perfectamente en mi aplicación, gracias por su contribución

    – usuario1083734

    2 de julio de 2013 a las 14:35

  • @Jeff dict(zip…) el más rápido

    – Wouter Overmeire

    2 de julio de 2013 a las 17:44

  • En un DataFrame con forma=(100,2), el método de Wouter con dict(zip…) fue 3 veces más rápido que el de Jeff; usé %timeit

    – Quetzalcóatl

    30 de junio de 2015 a las 15:11

avatar de usuario
Kirell

Encontré una forma más rápida de resolver el problema, al menos en conjuntos de datos realmente grandes usando:
df.set_index(KEY).to_dict()[VALUE]

Prueba en 50.000 filas:

df = pd.DataFrame(np.random.randint(32, 120, 100000).reshape(50000,2),columns=list('AB'))
df['A'] = df['A'].apply(chr)

%timeit dict(zip(df.A,df.B))
%timeit pd.Series(df.A.values,index=df.B).to_dict()
%timeit df.set_index('A').to_dict()['B']

Producción:

100 loops, best of 3: 7.04 ms per loop  # WouterOvermeire
100 loops, best of 3: 9.83 ms per loop  # Jeff
100 loops, best of 3: 4.28 ms per loop  # Kikohs (me)

En Python 3.6, la forma más rápida sigue siendo WouterOvermeire. La propuesta de Kikohs es más lenta que las otras dos opciones.

import timeit

setup = '''
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randint(32, 120, 100000).reshape(50000,2),columns=list('AB'))
df['A'] = df['A'].apply(chr)
'''

timeit.Timer('dict(zip(df.A,df.B))', setup=setup).repeat(7,500)
timeit.Timer('pd.Series(df.A.values,index=df.B).to_dict()', setup=setup).repeat(7,500)
timeit.Timer('df.set_index("A").to_dict()["B"]', setup=setup).repeat(7,500)

Resultados:

1.1214002349999777 s  # WouterOvermeire
1.1922008498571748 s  # Jeff
1.7034366211428602 s  # Kikohs

dict (zip(data['position'], data['letter']))

esto te dará:

{1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'}

avatar de usuario
alvas

TL;DR

>>> import pandas as pd
>>> df = pd.DataFrame({'Position':[1,2,3,4,5], 'Letter':['a', 'b', 'c', 'd', 'e']})
>>> dict(sorted(df.values.tolist())) # Sort of sorted... 
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
>>> from collections import OrderedDict
>>> OrderedDict(df.values.tolist())
OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)])

en largo

Explicando la solución: dict(sorted(df.values.tolist()))

Dado:

df = pd.DataFrame({'Position':[1,2,3,4,5], 'Letter':['a', 'b', 'c', 'd', 'e']})

[out]:

 Letter Position
0   a   1
1   b   2
2   c   3
3   d   4
4   e   5

Probar:

# Get the values out to a 2-D numpy array, 
df.values

[out]:

array([['a', 1],
       ['b', 2],
       ['c', 3],
       ['d', 4],
       ['e', 5]], dtype=object)

Entonces opcionalmente:

# Dump it into a list so that you can sort it using `sorted()`
sorted(df.values.tolist()) # Sort by key

O:

# Sort by value:
from operator import itemgetter
sorted(df.values.tolist(), key=itemgetter(1))

[out]:

[['a', 1], ['b', 2], ['c', 3], ['d', 4], ['e', 5]]

Por último, convierta la lista de la lista de 2 elementos en un dictado.

dict(sorted(df.values.tolist())) 

[out]:

{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}

Relacionado

Respondiendo al comentario de @sbradbio:

Si hay varios valores para una clave específica y desea conservarlos todos, no es la forma más eficiente, pero sí la más intuitiva:

from collections import defaultdict
import pandas as pd

multivalue_dict = defaultdict(list)

df = pd.DataFrame({'Position':[1,2,4,4,4], 'Letter':['a', 'b', 'd', 'e', 'f']})

for idx,row in df.iterrows():
    multivalue_dict[row['Position']].append(row['Letter'])

[out]:

>>> print(multivalue_dict)
defaultdict(list, {1: ['a'], 2: ['b'], 4: ['d', 'e', 'f']})

  • ¿Hay alguna manera de agregar más de una columna como valor? {'key': [value1, value2]}

    – sbradbio

    21/10/2018 a las 15:35

  • Comprobar respuesta adjunta

    – alvas

    22 de octubre de 2018 a las 0:28

  • Creo que value1 y value2 son dos columnas separadas. ¿Podrías crear un diccionario con {‘id’: [‘long’,’lat]}? long y lat están en columnas separadas.

    – km

    24 de noviembre de 2019 a las 13:10


avatar de usuario
rug123

Aquí hay otras dos formas probadas con el siguiente df.

df = pd.DataFrame(np.random.randint(0,10,10000).reshape(5000,2),columns=list('AB'))

usando to_records()

dict(df.to_records(index=False))

usando MultiIndex.from_frame()

dict(pd.MultiIndex.from_frame(df))

Tiempo de cada uno.

24.6 ms ± 847 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
1.86 ms ± 11.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

  • ¿Hay alguna manera de agregar más de una columna como valor? {'key': [value1, value2]}

    – sbradbio

    21/10/2018 a las 15:35

  • Comprobar respuesta adjunta

    – alvas

    22 de octubre de 2018 a las 0:28

  • Creo que value1 y value2 son dos columnas separadas. ¿Podrías crear un diccionario con {‘id’: [‘long’,’lat]}? long y lat están en columnas separadas.

    – km

    24 de noviembre de 2019 a las 13:10


Me gusta el método Wouter, sin embargo, el comportamiento con valores duplicados puede no ser el esperado y, lamentablemente, el OP no discute este escenario de una forma u otra. Wouter, siempre elegirá el último valor para cada clave encontrada. En otras palabras, seguirá sobrescribiendo el valor de cada tecla.

El comportamiento esperado en mi mente sería más como Crear un dictado usando dos columnas del marco de datos con duplicados en una columna donde se guarda una lista para cada clave.

Entonces, para el caso de mantener duplicados, permítanme enviar df.groupby('Position')['Letter'].apply(list).to_dict() (O tal vez incluso un conjunto en lugar de una lista)

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad