Найти в Dataframe повторяющиеся значения через каждые три строчки
Есть задача по комбинаторике:
В ящике 5 апельсинов и 4 яблока. Наудачу выбираются 3 фрукта. Какова вероятность, что все три фрукта – апельсины?
Необходимо проверить экспериментально в Python. (вероятность - 0.119)
import pandas as pd
import numpy as np
import random
U = ["orange"]*5 + ['apple']*4
# наполняем урну
n = 120000
# количество экспериментов
A = pd.DataFrame({"A":list(map(lambda a: random.choice([random.choice(U)]), range(n)))})
# формируем выборку
Есть ли способ посчитать значения Dataframe через каждые три элемента, которые будут содержать только 'orange' ? Планировал полученное число разделить на 40000, чтобы получить вероятность.
Ответы (5 шт):
Я не совсем уверен, что вы в принципе правильно пытаетесь проверить вероятность опытным путём, но что касается выборки, я бы советовал использовать не каждый третий ряд
(A.iloc[::3,0] == "orange").sum()
, а воспользоваться методом sample:
random.seed(42)
# количество экспериментов
A = pd.DataFrame({"A":list(map(lambda a: random.choice([random.choice(U)]), range(n)))})
res = A.sample(frac=0.33, replace=False, random_state=42).value_counts()["orange"]
22179
ПОДХОД К ЭКСПЕРИМЕНТУ У ВАС НЕПРАВИЛЬНЫЙ.
Можете посмотреть тут код, чтобы чему-то научиться. Но для правильного результата смотрите другой мой ответ.
Поменял несколько ваш код.
- чтобы упростить дальнейшие вычисления я сразу проверяю, что выпал именно
orangeи ставлю 1 если да - я делаю в датафрейме группы по 3 элемента с помощью
groupby - дальше я суммирую те единички по группам
- если в группе получилось три единички, то это то, что мы ищем
- результат варьируется, но не похож на тот, который вы предполагали
import pandas as pd
import random
# наполняем урну
data = ["orange"]*5 + ['apple']*4
# количество экспериментов
n = 120_000
# формируем выборку
df = pd.DataFrame({"data": [random.choice(data) == 'orange' for _ in range(n)], "group": [i // 3 for i in range(n)]})
# вычисляем
print((df.groupby("group").sum() == 3).sum()['data'] / 40_000)
Пример вывода:
0.1725
Тот же эксперимент без пандас. С таким же результатом:
import random
# наполняем урну
data = ["orange"]*5 + ['apple']*4
# количество экспериментов
n = 120_000
# выборка
k = 3
print(sum(sum(random.choice(data) == 'orange' for _ in range(k)) == 3 for _ in range(n)) / n)
Пример вывода:
0.17134166666666667
А, я понял. У вас подход к эксперименту неправильный, результат поэтому получается не тот, который ожидается. Фактически, вы не соблюдаете условие, что у вас есть ящик из которого вы вынимаете фрукты. У вас есть склад с ящиками и вы вынимаете 3 фрукта из любых ящиков, а не только из одного, из-за этого получается не верный результат. Если брать фрукты из одного ящика, то с каждым взятым апельсином вероятность следующего апельсина сильно уменьшается. А если брать со склада, то вероятность падает не настолько сильно.
Как посчитать верный результат:
- брать для эксперимента строго один ящик, а не множество ящиков
- перемешивать каждый раз фрукты в ящике
- брать три первых фрукта
- проверять, что все они апельсины
- разделить число получившихся случаев "3 апельсина" на число экспериментов
import random
# наполняем урну
data = ["orange"]*5 + ['apple']*4
# количество экспериментов
n = 120_000
# выборка
k = 3
positive = 0
for _ in range(n):
random.shuffle(data)
result = all(x == 'orange' for x in data[:k])
positive += result
print(positive/n)
Пример вывода:
0.118875
Вот это уже похоже на искомый результат.
Проверим цифры аналитически. Если мы берём апельсины из ящика, где их изначально 5 из 9, то вероятность взять 3 апельсина подряд будет:
print(5/9 * 4/8 * 3/7)
# 0.11904761904761905
А если мы берём апельсины из большого склада, то вероятность с каждым взятым апельсином будет падать очень незначительно, можно сказать вообще не будет падать:
print((5/9)**3)
# 0.1714677640603567
Именно эти цифры мы и наблюдаем в правильном и в неправильном экспериментах.
Не надо никаких датафреймов, у Питона самого есть модуль random:
import random
INBOX = 5 * ["O"] + 4 * ["A"]
WANTED = 3 * ["O"]
N = 100_000
wanted = 0
for __ in range(N):
if random.sample(INBOX, k=3) == WANTED:
wanted += 1
print(f"Вероятность, что все три фрукта – апельсины: {wanted/N}")
Вывод (например):
Вероятность, что все три фрукта – апельсины: 0.11821
по сути это реализация метода Монте-Карло, немного подправил ваш код:
from random import sample
U = [0]*5 + [1]*4
n = 120000
df = pd.DataFrame({"A":list(map(lambda a: sum(sample(U,3))==0, range(n)))})
print(df.head())
'''
A
0 False
1 False
2 True
3 True
4 False
'''
# вероятность
df['A'].mean()
0.11896666666666667