Python random.choice・sample・choices完全ガイド – ランダム選択の使い分けを徹底解説
Pythonのrandomモジュールには、リストや配列からランダムに要素を選択するための3つの主要な関数があります:choice()、sample()、choices()。それぞれ異なる特徴と用途を持ち、適切に使い分けることで効率的なランダム処理が可能になります。この記事では、これら3つの関数の違いから実用的な応用例まで、詳しく解説します。
目次
1. 3つの関数の基本的な違い
| 関数 | 用途 | 重複 | 復元抽出 | 重み付け |
|---|---|---|---|---|
choice() |
1つ選択 | なし | なし | なし |
sample() |
複数選択 | なし | なし | なし |
choices() |
複数選択 | あり | あり | あり |
2. random.choice() – 1つの要素をランダム選択
基本的な使い方
import random
fruits = ["apple", "banana", "orange", "grape"]
selected = random.choice(fruits)
print(selected) # "banana"(ランダムに1つ選択)
数値リストからの選択
import random
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
lucky_number = random.choice(numbers)
print(f"ラッキーナンバー: {lucky_number}")
文字列からの文字選択
import random
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
random_char = random.choice(alphabet)
print(f"ランダム文字: {random_char}")
3. random.sample() – 重複なしで複数選択
基本的な使い方
import random
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
selected = random.sample(numbers, 3)
print(selected) # [7, 2, 9](重複なしで3つ選択)
パスワード生成への応用
import random
import string
def generate_password(length=8):
chars = string.ascii_letters + string.digits + "!@#$%&"
return ''.join(random.sample(chars, length))
password = generate_password(12)
print(f"生成されたパスワード: {password}")
リスト全体のシャッフル
import random
deck = ["♠A", "♠K", "♠Q", "♠J", "♠10", "♥A", "♥K", "♥Q"]
shuffled = random.sample(deck, len(deck))
print(f"シャッフル後: {shuffled}")
# random.shuffle()と同等だが新しいリストを返す
4. random.choices() – 重複ありで複数選択(重み付け可能)
基本的な使い方
import random
colors = ["red", "blue", "green", "yellow"]
selected = random.choices(colors, k=5)
print(selected) # ['blue', 'red', 'blue', 'green', 'red'](重複あり)
重み付けを使った選択
import random
items = ["Common", "Rare", "Epic", "Legendary"]
weights = [70, 20, 8, 2] # 確率の重み
result = random.choices(items, weights=weights, k=10)
print(result)
# Commonが多く選ばれ、Legendaryは稀に選ばれる
確率分布による選択
import random
# サイコロの出目(重み付け)
dice_faces = [1, 2, 3, 4, 5, 6]
# 通常のサイコロ(均等確率)
normal_dice = random.choices(dice_faces, k=100)
# いかさまサイコロ(6が出やすい)
loaded_weights = [1, 1, 1, 1, 1, 5]
loaded_dice = random.choices(dice_faces, weights=loaded_weights, k=100)
print(f"6の出現回数(通常): {normal_dice.count(6)}")
print(f"6の出現回数(いかさま): {loaded_dice.count(6)}")
5. 実用的な応用例
クイズアプリケーション
import random
class QuizApp:
def __init__(self):
self.questions = [
{"q": "Pythonの作者は?", "a": "Guido van Rossum"},
{"q": "1 + 1 = ?", "a": "2"},
{"q": "日本の首都は?", "a": "東京"},
{"q": "2の3乗は?", "a": "8"}
]
def get_random_question(self):
return random.choice(self.questions)
def get_quiz_set(self, num_questions):
return random.sample(self.questions, min(num_questions, len(self.questions)))
quiz = QuizApp()
question = quiz.get_random_question()
print(f"問題: {question['q']}")
quiz_set = quiz.get_quiz_set(2)
for i, q in enumerate(quiz_set, 1):
print(f"{i}. {q['q']}")
ゲームのアイテムドロップシステム
import random
class ItemDrop:
def __init__(self):
self.items = {
"ポーション": {"weight": 50, "rarity": "Common"},
"マジックソード": {"weight": 20, "rarity": "Rare"},
"ドラゴンアーマー": {"weight": 5, "rarity": "Epic"},
"伝説の指輪": {"weight": 1, "rarity": "Legendary"}
}
def drop_items(self, num_drops=3):
item_names = list(self.items.keys())
weights = [self.items[item]["weight"] for item in item_names]
return random.choices(item_names, weights=weights, k=num_drops)
drop_system = ItemDrop()
dropped = drop_system.drop_items(5)
print(f"ドロップアイテム: {dropped}")
A/Bテストのユーザー振り分け
import random
class ABTestAssigner:
def __init__(self, variant_weights=None):
self.variants = ["A", "B"]
self.weights = variant_weights or [50, 50] # デフォルトは50:50
def assign_user(self, user_id):
# ユーザーIDをシードにして再現可能な振り分け
random.seed(user_id)
variant = random.choices(self.variants, weights=self.weights)[0]
random.seed() # シードをリセット
return variant
def assign_multiple_users(self, user_ids):
return [self.assign_user(uid) for uid in user_ids]
# 70:30の比率でテスト
ab_test = ABTestAssigner([70, 30])
user_ids = range(1, 101)
assignments = ab_test.assign_multiple_users(user_ids)
print(f"グループAの人数: {assignments.count('A')}")
print(f"グループBの人数: {assignments.count('B')}")
6. データサンプリングと統計処理
データセットからのランダムサンプリング
import random
def create_sample_dataset():
"""サンプルデータセットを作成"""
data = []
for i in range(1000):
data.append({
"id": i,
"age": random.randint(18, 80),
"income": random.randint(200, 1000) * 1000,
"category": random.choice(["A", "B", "C"])
})
return data
def analyze_sample(dataset, sample_size=100):
"""ランダムサンプルを分析"""
sample = random.sample(dataset, sample_size)
avg_age = sum(person["age"] for person in sample) / len(sample)
avg_income = sum(person["income"] for person in sample) / len(sample)
return {
"sample_size": len(sample),
"average_age": round(avg_age, 1),
"average_income": round(avg_income, 0)
}
dataset = create_sample_dataset()
analysis = analyze_sample(dataset, 50)
print(f"サンプル分析結果: {analysis}")
重み付きランダムサンプリング
import random
def weighted_sampling(population, weights, k):
"""重み付きサンプリング(復元抽出)"""
return random.choices(population, weights=weights, k=k)
def weighted_sampling_no_replacement(population, weights, k):
"""重み付きサンプリング(非復元抽出)"""
selected = []
pop_copy = population.copy()
weights_copy = weights.copy()
for _ in range(min(k, len(pop_copy))):
chosen = random.choices(pop_copy, weights=weights_copy)[0]
idx = pop_copy.index(chosen)
selected.append(pop_copy.pop(idx))
weights_copy.pop(idx)
return selected
# 使用例
cities = ["東京", "大阪", "名古屋", "福岡", "札幌"]
populations = [1400, 880, 230, 160, 190] # 人口(万人)
# 人口に比例した重み付きサンプリング
sample1 = weighted_sampling(cities, populations, 10)
sample2 = weighted_sampling_no_replacement(cities, populations, 3)
print(f"復元抽出: {sample1}")
print(f"非復元抽出: {sample2}")
7. シミュレーション応用
モンテカルロシミュレーション
import random
import math
def estimate_pi(num_points=100000):
"""モンテカルロ法でπを推定"""
inside_circle = 0
for _ in range(num_points):
x = random.uniform(-1, 1)
y = random.uniform(-1, 1)
if x**2 + y**2 <= 1:
inside_circle += 1
return 4 * inside_circle / num_points
estimated_pi = estimate_pi(1000000)
print(f"推定π値: {estimated_pi}")
print(f"実際のπ値: {math.pi}")
print(f"誤差: {abs(estimated_pi - math.pi):.6f}")
ランダムウォーク
import random
def random_walk_2d(steps=1000):
"""2次元ランダムウォーク"""
x, y = 0, 0
path = [(x, y)]
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] # 上、右、下、左
for _ in range(steps):
dx, dy = random.choice(directions)
x += dx
y += dy
path.append((x, y))
return path
def analyze_random_walk(path):
"""ランダムウォークの分析"""
start = path[0]
end = path[-1]
max_distance = max(abs(x) + abs(y) for x, y in path)
return {
"start": start,
"end": end,
"final_distance": abs(end[0]) + abs(end[1]),
"max_distance": max_distance
}
path = random_walk_2d(10000)
analysis = analyze_random_walk(path)
print(f"ランダムウォーク分析: {analysis}")
8. パフォーマンス比較と最適化
実行速度の比較
import random
import time
def performance_test():
data = list(range(10000))
iterations = 10000
# choice()のテスト
start = time.time()
for _ in range(iterations):
random.choice(data)
choice_time = time.time() - start
# sample()のテスト(1個選択)
start = time.time()
for _ in range(iterations):
random.sample(data, 1)
sample_time = time.time() - start
# choices()のテスト(1個選択)
start = time.time()
for _ in range(iterations):
random.choices(data, k=1)
choices_time = time.time() - start
print(f"choice(): {choice_time:.4f}秒")
print(f"sample(): {sample_time:.4f}秒")
print(f"choices(): {choices_time:.4f}秒")
performance_test()
メモリ効率的な大規模サンプリング
import random
def efficient_large_sampling(data_generator, sample_size):
"""大規模データからの効率的サンプリング(リザーバーサンプリング)"""
reservoir = []
for i, item in enumerate(data_generator()):
if len(reservoir) < sample_size:
reservoir.append(item)
else:
# ランダムに置き換え
j = random.randint(0, i)
if j < sample_size:
reservoir[j] = item
return reservoir
def large_data_generator():
"""大量データのジェネレータ"""
for i in range(1000000):
yield f"data_{i}"
# 100万件のデータから1000件をサンプリング
sample = efficient_large_sampling(large_data_generator, 1000)
print(f"サンプルサイズ: {len(sample)}")
print(f"最初の5件: {sample[:5]}")
9. エラーハンドリングとベストプラクティス
安全な選択関数
import random
def safe_choice(sequence, default=None):
"""空のシーケンスでもエラーにならないchoice"""
try:
return random.choice(sequence)
except IndexError:
return default
def safe_sample(sequence, k, default=None):
"""要求数がシーケンス長を超えてもエラーにならないsample"""
try:
return random.sample(sequence, k)
except ValueError:
return default or sequence.copy() if sequence else []
def safe_choices(sequence, k=1, weights=None, default=None):
"""空のシーケンスでもエラーにならないchoices"""
if not sequence:
return default or []
try:
return random.choices(sequence, weights=weights, k=k)
except (ValueError, TypeError):
return default or []
# 使用例
empty_list = []
short_list = [1, 2]
print(safe_choice(empty_list, "デフォルト値")) # デフォルト値
print(safe_sample(short_list, 5)) # [1, 2](全要素返却)
print(safe_choices(empty_list, k=3, default=["なし"])) # ["なし"]
10. 実践的なユースケース
機械学習のデータ分割
import random
def train_test_split(data, test_ratio=0.2, random_state=None):
"""データを訓練用とテスト用に分割"""
if random_state:
random.seed(random_state)
data_copy = data.copy()
random.shuffle(data_copy)
split_point = int(len(data_copy) * (1 - test_ratio))
train_data = data_copy[:split_point]
test_data = data_copy[split_point:]
return train_data, test_data
# サンプルデータ
dataset = [{"features": [i, i*2], "label": i % 2} for i in range(100)]
train, test = train_test_split(dataset, test_ratio=0.3, random_state=42)
print(f"訓練データ: {len(train)}件")
print(f"テストデータ: {len(test)}件")
ロードバランサーの実装
import random
class LoadBalancer:
def __init__(self, servers, weights=None):
self.servers = servers
self.weights = weights or [1] * len(servers)
def get_server_random(self):
"""ランダム選択"""
return random.choice(self.servers)
def get_server_weighted(self):
"""重み付き選択"""
return random.choices(self.servers, weights=self.weights)[0]
def get_servers_batch(self, count):
"""バッチ処理用の複数サーバー選択"""
return random.choices(self.servers, weights=self.weights, k=count)
# 使用例
servers = ["server1", "server2", "server3", "server4"]
weights = [4, 3, 2, 1] # server1が最も高性能
lb = LoadBalancer(servers, weights)
print("ランダム選択:")
for _ in range(5):
print(f" -> {lb.get_server_random()}")
print("\n重み付き選択:")
for _ in range(5):
print(f" -> {lb.get_server_weighted()}")
まとめ
Python の random.choice()、random.sample()、random.choices() は、それぞれ異なる特徴を持つ強力な関数です:
- choice(): 1つの要素をシンプルに選択したい場合
- sample(): 重複なしで複数の要素を選択したい場合
- choices(): 重複ありで複数選択、または重み付け選択が必要な場合
適切な関数を選択することで、効率的で読みやすいコードを書くことができます。また、エラーハンドリングやパフォーマンスも考慮して、堅牢なアプリケーションを構築しましょう。
これらの関数をマスターすることで、シミュレーション、ゲーム開発、データ分析、機械学習など、様々な分野でランダム処理を効果的に活用できるようになります。
■らくらくPython塾 – 読むだけでマスター
■プロンプトだけでオリジナルアプリを開発・公開してみた!!
■AI時代の第一歩!「AI駆動開発コース」はじめました!
テックジム東京本校で先行開始。
■テックジム東京本校
「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。
<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。
<月1開催>放送作家による映像ディレクター養成講座
<オンライン無料>ゼロから始めるPython爆速講座

