Escolha, amostra, e escolhas para seleccionar aleatoriamente elementos de uma lista em Python.

O negócio

As funções escolha(), amostra(), e escolhas() no módulo aleatório da biblioteca padrão Python podem ser usadas para seleccionar e recuperar aleatoriamente elementos de uma lista, tuple, string, ou outro objecto sequencial (amostragem aleatória).

escolha() obtém um único elemento, amostra() e escolhas() obtém uma lista de múltiplos elementos. amostra() é extracção não recuperável sem duplicados, escolhas() é extracção recuperável com duplicados.

A seguinte informação é fornecida aqui.

  • Seleccionar um elemento ao acaso.: random.choice()
  • Seleccionar aleatoriamente múltiplos elementos (sem duplicados): random.sample()
  • Seleccionar aleatoriamente múltiplos elementos (com duplicados): random.choices()
  • Fixar o número aleatório de sementes

Seleccionar um elemento ao acaso.: random.choice()

Com a função do módulo aleatório escolher(), um elemento é seleccionado aleatoriamente a partir da lista e pode ser recuperado.

import random

l = [0, 1, 2, 3, 4]

print(random.choice(l))
# 1

O mesmo se aplica aos tufos e cordas. No caso das cordas, é seleccionado um único carácter.

print(random.choice(('xxx', 'yyy', 'zzz')))
# yyy

print(random.choice('abcde'))
# b

Erro se uma lista vazia, tuple, ou string for especificada como argumento.

# print(random.choice([]))
# IndexError: Cannot choose from an empty sequence

Seleccionar aleatoriamente múltiplos elementos (sem duplicados): random.sample()

Com a função amostra() do módulo aleatório, é possível obter múltiplos elementos ao acaso a partir de uma lista. Não há duplicação de elementos (extracção não recuperável).

O primeiro argumento é uma lista, e o segundo argumento é o número de elementos a serem recuperados. A lista é devolvida.

import random

l = [0, 1, 2, 3, 4]

print(random.sample(l, 3))
# [2, 4, 0]

print(type(random.sample(l, 3)))
# <class 'list'>

Se o segundo argumento for fixado em 1, é também devolvida uma lista com um elemento; se for fixado em 0, a lista está vazia. Se o segundo argumento for 1, é devolvida uma lista com um elemento; se for 0, é devolvida uma lista vazia; se o primeiro argumento for mais do que o número de elementos da lista, ocorre um erro.

print(random.sample(l, 1))
# [3]

print(random.sample(l, 0))
# []

# print(random.sample(l, 10))
# ValueError: Sample larger than population or is negative

Se o primeiro argumento for um tuple ou um fio, o que é devolvido continua a ser uma lista.

print(random.sample(('xxx', 'yyy', 'zzz'), 2))
# ['xxx', 'yyy']

print(random.sample('abcde', 2))
# ['b', 'e']

Se quiser regressar a um tuple ou string, use tuple(),join().

print(tuple(random.sample(('xxx', 'yyy', 'zzz'), 2)))
# ('xxx', 'yyy')

print(''.join(random.sample('abcde', 2)))
# dc

Note-se que o valor não é julgado, portanto se a lista original ou tuple contém elementos com o mesmo valor, há a possibilidade de que o mesmo valor seja seleccionado.

l_dup = [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3]

print(random.sample(l_dup, 3))
# [3, 1, 1]

Se quiser evitar a duplicação de valores, pode usar set() para convertê-lo num conjunto (set type) e extrair apenas os elementos únicos, e depois usar sample().

print(set(l_dup))
# {0, 1, 2, 3}

print(random.sample(set(l_dup), 3))
# [1, 3, 2]

Seleccionar aleatoriamente múltiplos elementos (com duplicados): random.choices()

As escolhas de função do módulo aleatório() permite recuperar múltiplos elementos aleatoriamente de uma lista, e ao contrário da amostra(), permite seleccionar elementos duplicados.

escolhas() é uma função adicionada em Python 3.6. Não está disponível em versões anteriores.

O argumento k especifica o número de elementos a serem recuperados. A duplicação é permitida, pelo que o número de elementos a serem recuperados pode ser maior do que o número de elementos da lista original.

Uma vez que k é um argumento apenas de palavras-chave, é necessário especificar uma palavra-chave, tal como k=3.

import random

l = [0, 1, 2, 3, 4]

print(random.choices(l, k=3))
# [2, 1, 0]

print(random.choices(l, k=10))
# [3, 4, 1, 4, 4, 2, 0, 4, 2, 0]

O valor por defeito de k é 1; se for omitido, é devolvida uma lista com 1 elemento.

print(random.choices(l))
# [1]

Os pesos dos argumentos podem ser utilizados para especificar o peso (probabilidade) que cada elemento será seleccionado, e o tipo dos elementos da lista pode ser int ou float.

print(random.choices(l, k=3, weights=[1, 1, 1, 10, 1]))
# [0, 2, 3]

print(random.choices(l, k=3, weights=[1, 1, 0, 0, 0]))
# [0, 1, 1]

O argumento cum_weights também pode ser especificado como um peso cumulativo. O peso cum_weights no seguinte código de amostra é equivalente aos primeiros pesos acima.

print(random.choices(l, k=3, cum_weights=[1, 2, 3, 13, 14]))
# [3, 2, 3]

O padrão para ambos os pesos de argumentos e cum_weights é Nenhum, o que significa que cada elemento é seleccionado com a mesma probabilidade.

Se o comprimento (número de elementos) dos pesos dos argumentos ou cum_weights for diferente da lista original, ocorre um erro.

# print(random.choices(l, k=3, weights=[1, 1, 1, 10, 1, 1, 1]))
# ValueError: The number of weights does not match the population_

É também um erro especificar pesos e pesos cum_weights ao mesmo tempo.

# print(random.choices(l, k=3, weights=[1, 1, 1, 10, 1], cum_weights=[1, 2, 3, 13, 14]))
# TypeError: Cannot specify both weights and cumulative weights

Especificámos uma lista como primeiro argumento como exemplo no código da amostra até agora, mas o mesmo se aplica aos tuplos e cordas.

Fixar o número aleatório de sementes

Ao dar um inteiro arbitrário à função semente() do módulo aleatório, a semente do número aleatório pode ser fixada e o gerador de números aleatórios pode ser inicializado.

Após a inicialização com a mesma semente, os elementos são sempre seleccionados da mesma forma.

random.seed(0)
print(random.choice(l))
# 3

random.seed(0)
print(random.choice(l))
# 3