Shuffle un diccionario en Python

Este post fue migrado de un blog hecho con Wordpress. Si se ve mal, dejame un comentario y lo arreglo.

El módulo random de Python tiene varias funciones muy útiles.

>>> import random

Cómo choice, que permite elegir un elemento al asar de una secuencia:

>>> lista = [1, 2, 3, "hola", 'q', '$', 0]

>>> random.choice(lista)

1

>>> random.choice(lista)

0

>>> random.choice(lista)

3

>>> random.choice(lista)

'q'

>>> random.choice("Esta es una oración muy interesante")

'E'

>>> random.choice((1,2,3,4))

3

O shuffle, que desordena una lista (in place, es decir que no retorna una lista desordenada sino que la misma es desordenada):

>>> random.shuffle(lista)

>>> lista

[3, 1, 0, 'hola', '$', 'q', 2]

>>> texto = "Esto es una pruba con SHUFFLE"

>>> random.shuffle(texto)

Traceback (most recent call last):

File "<stdin>", line 1, in ?

File "/usr/lib/python2.4/random.py", line 262, in shuffle

x[i], x[j] = x[j], x[i]

TypeError: object does not support item assignment

Obviamente ni a tuplas ni a strings se puede aplicar esta función ya que esos dos tipos de datos son inmutables.

Me encontraba yo programando y se me ocurrió que podría desordenar (shuffle) un diccionario para lograr cierto efecto. ¿Cual sería la semántica de esto? Seguro se entenderá mejor con un ejemplo, dado el diccionario:

{0 : "cero", 1 : "uno", 2 : "dos"}

que tiene números como claves y strings como valores, luego de que se le aplique la función shuffle

random.shuffle(dicc)

Se obtendría por ejemplo:

{0 : "uno", 1 : "dos", 2 : "cero"}

Bien, probémoslo en el REPL de Python:

>>> from random import shuffle

>>> lista = [1,2,3,4,5,6,7,8]

>>> shuffle(lista)

>>> lista

[4, 1, 7, 8, 6, 2, 5, 3]

>>> dicc = {}

>>> for a in range(8):

dicc[a] = a

>>> dicc

{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7}

>>> shuffle(dicc)

>>> dicc

{0: 1, 1: 4, 2: 7, 3: 6, 4: 2, 5: 5, 6: 3, 7: 0}

Mmm, parece que funciona. ¿Qué dicen uds? El del último ejemplo no es en realidad el diccionario que uso en mi programa, sino que es más bien algo así:

>>> dicc = {(1,1): "primero", (1,2): "segundo", (2,2): "otro"}

Intentamos mezclarlo y..

>>> shuffle(dicc)

Traceback (most recent call last):

File "<stdin>", line 1, in ?

File "/usr/lib/python2.4/random.py", line 262, in shuffle

x[i], x[j] = x[j], x[i]

KeyError: 2

¿Qué pasó? La respuesta a este raro comportamiento está en el código mismo de Python, en el módulo random. En mi instalación de Python2.4 en Debian GNU/Linux lo encontramos en: /usr/lib/python2.4/random.py

def shuffle(self, x, random=None, int=int):

"""x, random=random.random -> shuffle list x in place; return None.



Optional arg random is a 0-argument function returning a random

float in [0.0, 1.0); by default, the standard random.random.

"""



    if random is None:

        random = self.random

    for i in reversed(xrange(1, len(x))):

    # pick an element in x[:i+1] with which to exchange x[i]

        j = int(random() * (i+1))

        x[i], x[j] = x[j], x[i]

Sólo de ver la implementación se nota que solo funcionará con listas. La siguiente es mi alternativa, una función shuffle_dict que sirve para mezclar los elementos de un diccionario:

from random import choice



def shuffle_dict(d):

"""Shuffle the d dict."""

    for i in d.keys():

        k = d.keys()

        k.remove(i)

        j = choice(k)

        d[i], d[j] = d[j], d[i]

Comentarios

Comments powered by Disqus